This commit is contained in:
2025-11-18 18:36:26 +09:00
parent b30f738b01
commit 7a18e84f1b
9 changed files with 253 additions and 437 deletions
@@ -94,7 +94,7 @@ services:
retries: 5
oai-gnb:
image: nakaolab:oct1
image: nakaolab:oct29
container_name: oai-gnb
cap_drop:
- ALL
@@ -105,6 +105,7 @@ services:
public_net:
ipv4_address: 192.168.71.140
volumes:
# - ../../conf_files/gnb.sa.band78.273prb.rfsim.2x2.conf:/opt/oai-gnb/etc/gnb.conf
- ../../conf_files/gnb.sa.band78.106prb.rfsim.yaml:/opt/oai-gnb/etc/gnb.yaml
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
@@ -115,7 +116,7 @@ services:
retries: 5
oai-flexric:
image: nakaolab:oct1
image: nakaolab:oct29
container_name: oai-flexric
command: ["-c", "stdbuf -o0 /oai-ran/openair2/E2AP/flexric/build/examples/ric/nearRT-RIC"]
networks:
@@ -130,8 +131,14 @@ services:
timeout: 5s
xapp-chatgpt:
image: nakaolab:oct1
command: ["-c", "XAPP_DURATION=20 stdbuf -oL /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
image: nakaolab:oct29
command: ["-c", "XAPP_DURATION=3600 /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
# command: ["-lc","ulimit -c unlimited; export XAPP_DURATION=3600; gdb -q -batch \
# -ex 'set pagination off' \
# -ex run \
# -ex 'bt full' \
# -ex 'thread apply all bt full' \
# --args /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
container_name: xapp-chatgpt
environment:
- TZ=Asia/Tokyo
@@ -147,7 +154,7 @@ services:
timeout: 5s
oai-nr-ue1:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue1
cap_drop:
@@ -155,8 +162,6 @@ services:
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
# environment:
# USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
depends_on:
- oai-gnb
networks:
@@ -175,7 +180,7 @@ services:
retries: 5
oai-nr-ue2:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001101 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue2
cap_drop:
@@ -201,7 +206,7 @@ services:
retries: 5
oai-nr-ue3:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001102 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue3
cap_drop:
@@ -227,7 +232,7 @@ services:
retries: 5
oai-nr-ue4:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001103 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue4
cap_drop:
@@ -253,7 +258,7 @@ services:
retries: 5
oai-nr-ue5:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001104 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue5
cap_drop:
@@ -279,7 +284,7 @@ services:
retries: 5
oai-nr-ue6:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001105 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue6
cap_drop:
@@ -305,7 +310,7 @@ services:
retries: 5
oai-nr-ue7:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001106 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue7
cap_drop:
@@ -331,7 +336,7 @@ services:
retries: 5
oai-nr-ue8:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001107 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue8
cap_drop:
@@ -357,7 +362,7 @@ services:
retries: 5
oai-nr-ue9:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001108 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue9
cap_drop:
@@ -365,12 +370,6 @@ services:
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
<<<<<<< HEAD
=======
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
>>>>>>> 2025.w42
depends_on:
- oai-gnb
networks:
@@ -389,7 +388,7 @@ services:
retries: 5
oai-nr-ue10:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue10
cap_drop:
@@ -411,25 +410,36 @@ services:
interval: 10s
timeout: 5s
retries: 5
# iperf:
# image: phlak/iperf
# container_name: iperf
# iperf3:
# image: networkstatic/iperf3:latest
# container_name: iperf3
# command: ["-c 'for p in $(seq 5201 5210); do iperf3 -s -p $$p & done;'"]
# ports:
# - "5201:5201/tcp"
# - "5201:5201/udp"
# - "5201-5210:5201-5210/tcp"
# - "5201-5210:5201-5210/udp"
# networks:
# traffic_net:
# ipv4_address: 192.168.72.130
# public_net:
# ipv4_address: 192.168.71.130
# restart: unless-stopped
iperf:
iperf_udp:
image: mlabbe/iperf
container_name: iperf
command: ["-s", "-p 5201"]
container_name: iperf_udp
command: ["-u -s -p 5201"]
ports:
- "5201:5201/tcp"
- "5201:5201/udp"
networks:
public_net:
ipv4_address: 192.168.71.129
restart: unless-stopped
iperf_tcp:
image: mlabbe/iperf
container_name: iperf_tcp
command: ["-s -p 5202"]
ports:
- "5202:5202/tcp"
networks:
public_net:
ipv4_address: 192.168.71.130
+133 -133
View File
@@ -1,28 +1,21 @@
#!/bin/bash
# Examples:
# ./exp-35.sh
# UE_MIN=2 UE_MAX=12 UE_PEAK_HOUR=14 UE_NOISE_FRAC=0.15 ./exp-35.sh
# TEST_UE=oai-nr-ue1 TEST_PROFILE=browsing TEST_DURATION_SEC=120 ./exp-35.sh
set -euo pipefail
# --- UE profile map (index 0..10). 0-index kept for convenience ---
# Initialize with "0" to denote no profile chosen.
declare -a UE_PROFILE_MAP
for i in {0..10}; do UE_PROFILE_MAP[$i]=0; done
# ---------------------------[ Random Burst Mode ]----------------------------
# ---------------------------[ Random / diurnal Mode ]----------------------------
# UE scaler mode: 'random' (rapid random targets) or 'diurnal' (legacy cosine)
UE_SCALER_MODE="${UE_SCALER_MODE:-random}"
# Interval (sec) for scaler ticks
UE_SCALER_INTERVAL_SEC="${UE_SCALER_INTERVAL_SEC:-30}"
# Rapid/random system-level bursts: every few minutes trigger a short, global burst
# that nudges all UEs to start/extend a session at once. UE-side script watches
# /tmp/system_burst and reacts (see traffic_runner --profile random).
RANDOM_BURST_MODE="${RANDOM_BURST_MODE:-1}" # 1=enabled, 0=disabled
BURST_MIN_GAP_SEC="${BURST_MIN_GAP_SEC:-180}" # 3 minutes
BURST_MAX_GAP_SEC="${BURST_MAX_GAP_SEC:-600}" # 10 minutes
BURST_MARK_FILE="${BURST_MARK_FILE:-/tmp/system_burst}"
# -------------------------------[ Config ]------------------------------------
ARTIFACT_DIR="/home/nakaolab-go/OAI/research-main/artifacts/"
ARTIFACT_DIR="/media/nakaolab-go/2nd/artifacts"
mkdir -p "$ARTIFACT_DIR"
POWER_LOG_FILE="${POWER_LOG_FILE:-$ARTIFACT_DIR/power_usage.csv}"
@@ -30,19 +23,19 @@ CPU_PROXY_LOG_FILE="${CPU_PROXY_LOG_FILE:-$ARTIFACT_DIR/cpu_proxy.csv}"
RRC_SINCE_STATE="${RRC_SINCE_STATE:-$ARTIFACT_DIR/.rrc_since}"
# xApp (mac stats) container + path inside container
MAC_STATS_CONTAINER="${MAC_STATS_CONTAINER:-xapp-chatgpt}"
STATS_CONTAINER="${STATS_CONTAINER:-xapp-chatgpt}"
MAC_STATS_FILE_IN_CONTAINER="${MAC_STATS_FILE_IN_CONTAINER:-/tmp/mac_stats.log}"
PDCP_STATS_FILE_IN_CONTAINER="${PDCP_STATS_FILE_IN_CONTAINER:-/tmp/pdcp_stats.log}"
# Intervals (seconds)
RRC_LOG_INTERVAL_SEC="${RRC_LOG_INTERVAL_SEC:-10}"
POWER_LOG_INTERVAL_SEC="${POWER_LOG_INTERVAL_SEC:-5}"
CPU_PROXY_INTERVAL_SEC="${CPU_PROXY_INTERVAL_SEC:-5}"
UE_SCALER_INTERVAL_SEC="${UE_SCALER_INTERVAL_SEC:-60}"
# Docker compose helper
DC_BIN=(docker compose)
dc() { command "${DC_BIN[@]}" "$@"; }
dc down -v
dc down -v --remove-orphans
# Service names (override if your compose uses different ones)
AMF_SERVICE="${AMF_SERVICE:-oai-amf}"
@@ -54,14 +47,14 @@ XAPP_SERVICE="${XAPP_SERVICE:-xapp-chatgpt}"
UE_NAME_REGEX="${UE_NAME_REGEX:-^oai-nr-ue}"
# Test controls
TEST_UE="${TEST_UE:-}" # e.g. oai-nr-ue1
TEST_MODE="${TEST_MODE:-0}" # 1=manual profiles allowed (TEST_* honored); 0=auto
TEST_UES="${TEST_UES:-}"
TEST_PROFILES="${TEST_PROFILES:-}" # profile name
TEST_DURATION_SEC="${TEST_DURATION_SEC:-0}" # 0 = run indefinitely
# Diurnal scaling controls (normal mode only; ignored if TEST_UE is set)
UE_MIN="${UE_MIN:-1}" # Minimum number of UEs to keep connected
# UE_MAX="${UE_MAX:-}" # Max UEs; default = total UEs in compose
UE_MAX=10
UE_MAX="${UE_MAX:-10}"
UE_PEAK_HOUR="${UE_PEAK_HOUR:-17}" # Local hour with peak load (0..23)
UE_NOISE_FRAC="${UE_NOISE_FRAC:-0.15}" # ±fractional noise applied to target
@@ -74,6 +67,7 @@ declare -A PWR_MW_PROFILE=(
[voip]=520 # steady uplink/downlink small packets
[video]=850 # continuous DL; 720–1080p
[gaming]=950 # bursty DL + UL keepalives
[boring]=500 # for single PRB testing
[bulk_download]=1100 # sustained UL/DL at MCS ceiling
)
@@ -101,14 +95,8 @@ get_core_services() {
}
get_running_ues() {
# Running UEs by service name (include TEST_UE name if custom)
if [[ -n "${TEST_UE:-}" ]]; then
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}|^${TEST_UE}$" || true
else
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}" || true
fi
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}" || true
}
# -------------------------[ Readiness helpers ]-------------------------------
@@ -153,7 +141,6 @@ wait_log() {
local start; start="$(ts_now)"
while :; do
if dc logs "$svc" --since=2m 2>&1 | grep -Eiq -- "$pat"; then
log "wait_log: detected '$pat' in '$svc'."
return 0
fi
if (( $(ts_now) - start > timeout )); then
@@ -174,15 +161,14 @@ get_container_ip() {
start_services_list() {
[[ $# -eq 0 ]] && return 0
dc up -d "$@"
dc up -d "$@" >/dev/null 2>&1
}
start_ue_list() {
[[ $# -eq 0 ]] && return 0
for svc in "$@"; do
log "Starting: $svc"
dc up -d "$svc"
sleep 10
dc up -d "$svc" >/dev/null 2>&1
done
}
@@ -200,11 +186,28 @@ start_in_stages() {
wait_healthy "${AMF_SERVICE}" 15
wait_healthy "${UPF_SERVICE}" 15
exec_safe "${UPF_SERVICE}" 'bash -lc "
GW=192.168.72.129
DEV=\$(ip -4 route get \$GW 2>/dev/null | awk '\''/ dev /{for(i=1;i<=NF;i++) if(\$i==\"dev\"){print \$(i+1); exit}}'\''); DEV=\${DEV:-eth0}
ip -4 route replace \$GW/32 dev \$DEV
ip -4 route replace default via \$GW dev \$DEV onlink
"'
set -e
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.eth0.rp_filter=2
sysctl -w net.ipv4.conf.eth1.rp_filter=2
sysctl -w net.ipv4.conf.tun0.rp_filter=2
ip rule add pref 100 iif tun0 to 192.168.71.0/24 lookup 201 || true
ip route replace 192.168.71.0/24 dev eth0 table 201
ip rule add pref 200 iif tun0 lookup 200 || true
ip route replace 12.1.1.0/24 dev tun0 table 200
ip route replace default via 192.168.72.129 dev eth1 table 200
# UE -> DN (192.168.72.128/26) via eth1
iptables -t nat -C POSTROUTING -s 12.1.1.0/24 -d 192.168.72.128/26 -o eth1 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 12.1.1.0/24 -d 192.168.72.128/26 -o eth1 -j MASQUERADE
# UE -> 192.168.71.0/24 via eth0
iptables -t nat -C POSTROUTING -s 12.1.1.0/24 -d 192.168.71.0/24 -o eth0 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 12.1.1.0/24 -d 192.168.71.0/24 -o eth0 -j MASQUERADE
"'
wait_healthy oai-flexric 15
# 2) gNB
@@ -272,17 +275,6 @@ calc_target_ue_count() {
}'
}
wait_oaitun() {
local ue="$1" timeout="${2:-20}"
for ((i=1; i<=timeout; i++)); do
if exec_safe "$ue" 'ip link show oaitun_ue1 >/dev/null 2>&1'; then
return 0
fi
sleep 1
done
return 1
}
ensure_ue_count() {
local target="$1"
@@ -312,7 +304,12 @@ ensure_ue_count() {
if (( cur < target )); then
local need=$(( target - cur ))
log "UE scaler: need +${need} UEs (cur=${cur}, target=${target})."
# Choose candidates (prefer random if shuf available)
if (( ${#not_running[@]} == 0 )); then
log "UE scaler: no available UEs to start."
return 0
fi
local to_start=()
if command -v shuf >/dev/null 2>&1; then
mapfile -t to_start < <(printf '%s\n' "${not_running[@]}" | shuf -n "$need" || true)
@@ -321,16 +318,15 @@ ensure_ue_count() {
fi
if (( ${#to_start[@]} > 0 )); then
start_ue_list "${to_start[@]}"
# kick traffic + (best-effort) readiness
for ue in "${to_start[@]}"; do
if wait_oaitun "$ue" 20; then
if wait_log "$ue" "oaitun_ue1 successfully configured"; then
exec_safe "$ue" 'ip route replace default dev oaitun_ue1'
run_traffic_profile "$ue" "random" "0"
# run_traffic_profile "$ue" "$(choose_random_profile)" "0"
run_traffic_profile "$ue" "boring"
else
dc restart "$ue"
to_start+="$ue"
dc rm -fv "$ue" >/dev/null 2>&1
fi
done
done
fi
return 0
fi
@@ -346,46 +342,57 @@ ensure_ue_count() {
fi
for ue in "${to_stop[@]}"; do
log "Stopping UE ${ue}"
dc stop "$ue" || true
dc rm -f "$ue" || true
dc stop "$ue" >/dev/null 2>&1 &
done
wait
for ue in "${to_stop[@]}"; do
dc rm -fv "$ue" >/dev/null 2>&1 &
done
wait
}
# --------------------------[ UE traffic helpers ]-----------------------------
pick_profile() {
local r=$((RANDOM % 100))
if (( r < 30 )); then echo browsing # 45%
elif (( r < 70 )); then echo video # 35%
elif (( r < 90 )); then echo bulk_download # 12%
else echo iot_chatty # 8%
fi
PROFILES=(boring browsing video bulk_download voip iot_chatty gaming)
choose_random_profile() {
if command -v shuf >/dev/null 2>&1; then
shuf -e "${PROFILES[@]}" -n1
else
idx=$(( RANDOM % ${#PROFILES[@]} ))
echo "${PROFILES[$idx]}"
fi
}
run_traffic_profile() {
local ue="$1" profile="$2" duration="${3:-0}"
exec_safe "$ue" "echo 'traffic runner ready for $ue. profile: $profile'"
local ue="$1" profile="$2"
# Record selected profile for this UE into UE_PROFILE_MAP
if [[ "$ue" =~ ([0-9]+)$ ]]; then
__idx="${BASH_REMATCH[1]}"
UE_PROFILE_MAP[$__idx]="$profile"
fi
dc cp /home/nakaolab-go/OAI/openairinterface5g/tools/scripts/traffic_runner.sh "$ue":/tmp/traffic_runner.sh >/dev/null 2>&1
exec_safe "$ue" "bash -lc '
if [ ! -f /tmp/traffic_profile.lock ]; then
prof=\$1; dur=\$2
/usr/local/bin/traffic_runner --profile \$prof --duration \$dur >/tmp/traffic_runner.log 2>&1 &
printf \"%s %s\n\" \"\$prof\" \"\$dur\" > /tmp/traffic_profile.lock
fi
' -- $profile $duration"
prof=\$1;
/tmp/traffic_runner.sh --profile \$prof &
' -- $profile"
}
# --------------------------[ RRC / MAC scraping ]-----------------------------
rotate_mac_capture() {
rotate_stats_capture() {
local tag="${1:-}"
[[ -z "$tag" ]] && tag="$(ts_tag)"
if dc ps --services --filter "status=running" | grep -q "^${MAC_STATS_CONTAINER}$"; then
log "Rotating mac_stats from ${MAC_STATS_CONTAINER}"
dc cp "${MAC_STATS_CONTAINER}:${MAC_STATS_FILE_IN_CONTAINER}" \
if dc ps --services --filter "status=running" | grep -q "^${STATS_CONTAINER}$"; then
log "Rotating stats from ${STATS_CONTAINER}"
dc cp "${STATS_CONTAINER}:${MAC_STATS_FILE_IN_CONTAINER}" \
"${ARTIFACT_DIR}/mac_stats_${tag}.log" || true
exec_safe "$MAC_STATS_CONTAINER" "> '${MAC_STATS_FILE_IN_CONTAINER}'"
dc cp "${STATS_CONTAINER}:${PDCP_STATS_FILE_IN_CONTAINER}" \
"${ARTIFACT_DIR}/pdcp_stats_${tag}.log" || true
# exec_safe "$STATS_CONTAINER" "> '${MAC_STATS_FILE_IN_CONTAINER}'"
# exec_safe "$STATS_CONTAINER" "> '${PDCP_STATS_FILE_IN_CONTAINER}'"
else
log "mac_stats container ${MAC_STATS_CONTAINER} not running; skipping rotate"
log "stats container ${STATS_CONTAINER} not running; skipping rotate"
fi
}
@@ -400,10 +407,10 @@ _since_arg_from_file() {
fi
}
dump_gnb_rrc_logs() {
dump_gnb_logs() {
local tag="$1"
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "NR_RRC" \
> "${ARTIFACT_DIR}/gnb_rrc_${tag}.log" || true
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "NR_RRC" > "${ARTIFACT_DIR}/gnb_rrc_${tag}.log"
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "\[NR_MAC\] I Frame\.Slot|MAC:" > "${ARTIFACT_DIR}/gnb_${tag}.log"
}
# -----------------------------[ Power / CPU ]---------------------------------
@@ -428,14 +435,16 @@ sample_power_once() {
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
local lock
lock="$(exec_safe "$ue" 'bash -lc "cat /tmp/traffic_profile.lock 2>/dev/null || true"')"
# Determine profile without container lock by consulting UE_PROFILE_MAP
local picked_profile picked_duration
picked_profile="$(awk "{print \$1}" <<<"$lock")"
picked_duration="$(awk "{print \$2}" <<<"$lock")"
if [[ "$ue" =~ ([0-9]+)$ ]]; then
__idx="${BASH_REMATCH[1]}"
picked_profile="${UE_PROFILE_MAP[$__idx]}"
fi
picked_duration=""
if [[ -z "$picked_profile" ]]; then
picked_profile="${TEST_PROFILE:-idle}"
if [[ "${TEST_MODE}" -eq 1 ]]; then picked_profile="${TEST_PROFILE}"; else picked_profile="idle"; fi
fi
while [[ "$picked_profile" == -* ]]; do
@@ -499,8 +508,8 @@ rotate_csv_if_any() {
finalize_and_rotate_artifacts() {
local tag; tag="$(ts_tag)"
rotate_mac_capture "$tag" || true
dump_gnb_rrc_logs "$tag" || true
rotate_stats_capture "$tag" || true
dump_gnb_logs "$tag" || true
rotate_csv_if_any "$POWER_LOG_FILE" "power_usage" "$tag"
rotate_csv_if_any "$CPU_PROXY_LOG_FILE" "cpu_proxy" "$tag"
}
@@ -530,40 +539,40 @@ cleanup() {
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
dc stop "$ue" || true
dc rm -f "$ue" || true
dc rm -fv "$ue" >/dev/null 2>&1 &
done < <(dc ps --services | grep -E "${UE_NAME_REGEX}" || true)
}
# ---------------------------------[ Main ]------------------------------------
trigger_system_burst() {
mapfile -t running < <(get_running_ues)
if (( ${#running[@]} == 0 )); then
return 0
fi
log "Triggering system-wide burst for ${#running[@]} UE(s)…"
for ue in "${running[@]}"; do
exec_safe "$ue" "bash -lc 'date +%s > ${BURST_MARK_FILE}'" || true
done
}
random_target_ue_count() {
local total_ues="${UE_MAX:-}"
if [[ -z "$total_ues" ]]; then
total_ues="$(get_all_ue_services | wc -l | tr -d ' ')"
local cur="$(get_running_ues | wc -l | tr -d ' ')"
bias=$((RANDOM % 100)) # 0–99
if (( cur <= UE_MIN )); then
if (( bias < 70 )); then
delta=$(( (RANDOM % 3) + 1 )) # +1..+3
else
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
elif (( cur >= UE_MAX )); then
if (( bias < 70 )); then
delta=$(( -((RANDOM % 3) + 1) )) # -1..-3
else
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
else # normal
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
local min="${UE_MIN:-1}"
if (( total_ues < min )); then total_ues="$min"; fi
local val
if command -v shuf >/dev/null 2>&1; then
val="$(shuf -i "${min}-${total_ues}" -n 1)"
else
local span=$(( total_ues - min + 1 ))
val=$(( min + (RANDOM % span) ))
local target=$((cur + delta))
if (( target < UE_MIN )); then
target="${UE_MIN}"
elif (( target > UE_MAX )); then
target="${UE_MAX}"
fi
echo "$val"
echo "$target"
}
main() {
trap on_interrupt INT TERM
@@ -572,24 +581,18 @@ main() {
# Staged bring-up with readiness checks for both normal and TEST modes
start_in_stages
# Initialize next system-burst time
if (( RANDOM_BURST_MODE )); then
local next_burst_ts
next_burst_ts="$(($(ts_now) + BURST_MIN_GAP_SEC + RANDOM % (BURST_MAX_GAP_SEC - BURST_MIN_GAP_SEC + 1)))"
fi
# Kick off traffic workload(s)
if [[ -n "${TEST_UE:-}" ]]; then
if [[ "${TEST_MODE}" -eq 1 ]]; then
log "TEST UE(S) ENABLED"
# Support multiple UEs (comma/space/newline separated)
read -r -a _ue_list <<< "$(tr ',\n' ' ' <<< "${TEST_UE}")"
read -r -a _ue_list <<< "$(tr ',\n' ' ' <<< "${TEST_UES}")"
read -r -a _ue_profiles <<< "$(tr ',\n' ' ' <<< "${TEST_PROFILES}")"
start_ue_list "${_ue_list[@]}"
for i in "${!_ue_list[@]}"; do
ue="${_ue_list[$i]}"
prof="${_ue_profiles[$i]}"
[[ -z "$ue" ]] && continue
if wait_oaitun "$ue" 20; then
if wait_log "$ue" "oaitun_ue1 successfully configured"; then
exec_safe "$ue" "ip route replace default dev oaitun_ue1"
run_traffic_profile "$ue" "$prof"
else
@@ -599,7 +602,7 @@ main() {
fi
# Optional finite duration for TEST mode
if [[ -n "${TEST_UE:-}" && "${TEST_DURATION_SEC:-0}" -gt 0 ]]; then
if [[ "${TEST_MODE}" -eq 1 && -n "${TEST_UE:-}" && "${TEST_DURATION_SEC:-0}" -gt 0 ]]; then
RUN_STOP_TS=$(( $(ts_now) + TEST_DURATION_SEC ))
fi
@@ -612,14 +615,6 @@ main() {
log "Entering main loop (sampling + diurnal scaler)…"
while :; do
local now; now="$(ts_now)"
# System-wide random burst scheduler
if (( RANDOM_BURST_MODE )); then
if (( now >= next_burst_ts )); then
trigger_system_burst || true
next_burst_ts="$(( now + BURST_MIN_GAP_SEC + RANDOM % (BURST_MAX_GAP_SEC - BURST_MIN_GAP_SEC + 1) ))"
fi
fi
if (( now - last_power >= POWER_LOG_INTERVAL_SEC )); then
sample_power_once || true
@@ -632,9 +627,14 @@ main() {
fi
# Re-evaluate diurnal target (normal mode only)
if [[ -z "${TEST_UE:-}" ]] && (( now - last_scale >= UE_SCALER_INTERVAL_SEC )); then
local target; if [[ "${UE_SCALER_MODE}" == "diurnal" ]]; then target="$(calc_target_ue_count)"; else target="$(random_target_ue_count)"; fi
log "Diurnal scaler tick: target = ${target}"
if [[ "${TEST_MODE}" -eq 0 ]] && (( now - last_scale >= UE_SCALER_INTERVAL_SEC )); then
local target;
if [[ "${UE_SCALER_MODE}" == "diurnal" ]]; then
target="$(calc_target_ue_count)";
else
target="$(random_target_ue_count)";
fi
log "Scaler tick: target = ${target}"
ensure_ue_count "$target"
last_scale="$now"
fi