diff --git a/Makefile b/Makefile index be48c16..93aeda0 100644 --- a/Makefile +++ b/Makefile @@ -202,9 +202,35 @@ ue/switch-edge/%: @# swich edge for ue @UE_IP=$(shell docker exec ue$(@F)-debug bash -c "ip --brief address show uesimtun0|awk '{print \$$3; exit}'|cut -d"/" -f 1");\ scripts/switch.py $(BCONFIG) $$UE_IP +.PHONY: plot/policy-diff +plot/policy-diff: + @echo "[1/2] [1/6] Configuring testbed with NextMN-SRv6" + @$(MAKE) set/dataplane/nextmn-srv6 + @$(MAKE) set/nb-ue/2 + @$(MAKE) set/nb-edges/2 + @$(MAKE) set/full-debug/false + @$(MAKE) set/log-level/info + @echo "[1/2] [2/6] Starting containers" + @$(MAKE) up + @echo "[1/2] [3/6] Adding latency on instance s0" + @docker exec s0-debug bash -c "tc qdisc add dev edge-0 root netem delay 5ms" + @sleep 2 + @docker exec ue1-debug bash -c "ping -c 1 10.4.0.1 > /dev/null" # check instance is reachable + @docker exec ue2-debug bash -c "ping -c 1 10.4.0.1 > /dev/null" # check instance is reachable + @echo "[1/2] [4/6] Setting UE2 on edge 1" + @$(MAKE) ue/switch-edge/2 + @echo "[1/2] [5/6] [$$(date --rfc-3339=seconds)] Starting ping from ue1 and ue2 (60s + 5s margin)" + @bash -c 'docker exec ue1-debug bash -c "ping -D -w 60 10.4.0.1 -i 0.1 > /volume/ping-policy-diff-sliceA.txt"' & + @bash -c 'docker exec ue2-debug bash -c "ping -D -w 60 10.4.0.1 -i 0.1 > /volume/ping-policy-diff-sliceB.txt"' & + @sleep 65 + @echo "[1/2] [6/6] Stopping containers" + @$(MAKE) down + @echo "[2/2] Plotting data" + @scripts/plots/policy_diff.py $(BUILD_DIR)/volumes/ue1/ping-policy-diff-sliceA.txt $(BUILD_DIR)/volumes/ue2/ping-policy-diff-sliceB.txt $(BUILD_DIR)/volumes/ue1/plot-policy-diff.pdf -.PHONY: graph/latency-switch -graph/latency-switch: + +.PHONY: plot/latency-switch +plot/latency-switch: @echo "[1/3] [1/6] Configuring testbed with NextMN-SRv6" @$(MAKE) set/dataplane/nextmn-srv6 @$(MAKE) set/nb-ue/1 @@ -239,49 +265,5 @@ graph/latency-switch: @docker exec ue1-debug bash -c "ping -D -w 60 10.4.0.1 -i 0.1 > /volume/ping-ulcl.txt" @echo "[2/3] [5/5] Stopping containers" @$(MAKE) down - @echo "[3/3] Creating graph" - @scripts/graphs/latency_switch.py $(BUILD_DIR)/volumes/ue1/ping-sr4mec.txt $(BUILD_DIR)/volumes/ue1/ping-ulcl.txt $(BUILD_DIR)/volumes/ue1/plot.pdf - -.PHONY: graph/cp-delay -graph/cp-delay: - @$(MAKE) graph/cp-delay/iter/100 - -.PHONY: graph/cp-delay/iter -graph/cp-delay/iter/%: - @echo "[1/4] Configuring testbed" - @$(MAKE) set/nb-ue/1 - @$(MAKE) set/nb-edges/2 - @$(MAKE) set/full-debug/false - @$(MAKE) set/log-level/info - @echo "[2/4] [$$(date --rfc-3339=seconds)] Setting dataplane to Free5GC" - @$(MAKE) set/dataplane/free5gc - @$(MAKE) build - @iter=1 ; while [ $$iter -le $(@F) ] ; do \ - echo "[2/4] [$$iter/$(@F)] [$$(date --rfc-3339=seconds)] New Free5GC capture" ; \ - success=false; while [ $$success = false ]; do \ - { $(MAKE) graph/cp-delay/f5gc-$$iter && success=true || { $(MAKE) down ; kill -s TERM "$$(cat $(BUILD_DIR)/results/pid)" && rm $(BUILD_DIR)/results/pid && rm -f $(BUILD_DIR)/results/cp-delay-f5gc-$$iter.pcapng ; } ; }; \ - done ; \ - iter=$$(( iter + 1)) ; \ - done - @echo "[3/4] [$$(date --rfc-3339=seconds)] Setting dataplane to NextMN-SRv6" - @$(MAKE) set/dataplane/nextmn-srv6 - @$(MAKE) build - @iter=1 ; while [ $$iter -le $(@F) ] ; do \ - echo "[3/4] [$$iter/$(@F)] [$$(date --rfc-3339=seconds)] New NextMN-SRv6 capture" ; \ - success=false; while [ $$success = false ]; do \ - { $(MAKE) graph/cp-delay/srv6-$$iter && success=true || { $(MAKE) down ; kill -s TERM "$$(cat $(BUILD_DIR)/results/pid)" && rm $(BUILD_DIR)/results/pid && rm -f $(BUILD_DIR)/results/cp-delay-srv6-$$iter.pcapng ; } ; }; \ - done ; \ - iter=$$(( iter + 1)) ; \ - done - @echo "[4/4] Creating graph" - @scripts/graphs/cp_delay.py text $(BUILD_DIR)/results - @scripts/graphs/cp_delay.py plot $(BUILD_DIR)/results $(@F) - -graph/cp-delay/%: - @mkdir build/results -p - @tshark -i any -f sctp -w $(BUILD_DIR)/results/cp-delay-$(@F).pcapng & echo "$$!" > $(BUILD_DIR)/results/pid - @$(MAKE) up - @sleep 5 - @docker exec ue1-debug bash -c "ping 10.4.0.1 -c1" - @$(MAKE) down - @kill -s TERM "$$(cat $(BUILD_DIR)/results/pid)" && rm $(BUILD_DIR)/results/pid + @echo "[3/3] Plotting data" + @scripts/plots/latency_switch.py $(BUILD_DIR)/volumes/ue1/ping-sr4mec.txt $(BUILD_DIR)/volumes/ue1/ping-ulcl.txt $(BUILD_DIR)/volumes/ue1/plot-latency-switch.pdf diff --git a/README.md b/README.md index 19d2615..1fa514d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The following Debian's packages are required (or their equivalent on your distri - `make` - `acl` - `openssl` +- `python3-matplotlib` >[!NOTE] > If you intend to use Free5GC’s UPF (with `make set/dataplane/free5gc`), @@ -22,10 +23,6 @@ The following Debian's packages are required (or their equivalent on your distri > Please note that you need to have Linux headers installed on the host to be able to install the module > (for example, the package `linux-headers-amd64` on Debian if you are on an amd64 architecture). -The following Debian's packages are required for `make graph` subcommands only: -- `python3-matplotlib` -- `tshark` - ### Usage > [!IMPORTANT] > Make sure to enable IPv6. diff --git a/scripts/graphs/cp_delay.py b/scripts/graphs/cp_delay.py deleted file mode 100755 index f7589da..0000000 --- a/scripts/graphs/cp_delay.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 -'''Check control plane establishment delay''' -# Copyright 2024 Louis Royer. All rights reserved. -# Use of this source code is governed by a MIT-style license that can be -# found in the LICENSE file. -# SPDX-License-Identifier: MIT - -from os import path -import argparse -import os -import pathlib -import subprocess -import matplotlib.pyplot as plt - -class DataError(Exception): - '''Issue with data''' - -def convert(arguments: argparse.Namespace): - '''Convert .pcapng to .pcapng.txt''' - for file in os.listdir(arguments.dir): - filename = os.fsdecode(file) - if filename.endswith('.pcapng'): - with open(path.join(arguments.dir, f'{filename}.txt'), 'w', encoding='utf-8') as out: - subprocess.run(['tshark', '-r', - path.join(arguments.dir, filename), - '-Y', 'ngap' - ], - check=True, - stdout=out, - stderr=subprocess.DEVNULL - ) - -def plot(arguments: argparse.Namespace): - '''Write plot''' - data = [] - for data_i, dataplane in enumerate(['f5gc', 'srv6']): - data.append([]) - for i in range(1, arguments.num+1): - with open(path.join(arguments.dir, f'cp-delay-{dataplane}-{i}.pcapng.txt'), - 'r', encoding='utf-8') as input_file: - tmp = [] - for line in input_file: - if 'InitialUEMessage' in line: - tmp.append(float(line.strip().split(' ')[1])) - elif 'PDUSessionResourceSetupResponse' in line: - tmp.append(float(line.strip().split(' ')[1])) - if len(tmp) != 2: - print(f'skip {dataplane}-{i}: {len(tmp)} messages instead of 2.') - #raise DataError(f'{dataplane}-{i}: too many messages') - else: - if (tmp[1] - tmp[0]) * 1000 > 500: - print(f'more than 500ms: {dataplane}-{i}') - data[data_i].append((tmp[1] - tmp[0])*1000) - _, axplt = plt.subplots() - axplt.boxplot(data, labels=['UL-CL', 'SR4MEC'], - patch_artist=True, boxprops={'facecolor': 'bisque'}) - axplt.set_ylabel('Time (ms)') - axplt.autoscale_view() - plt.title('Comparison of PDU Session Establishment time') - plt.savefig(path.join(arguments.dir, 'cp-delay.pdf')) - print(f'plot saved in {path.join(arguments.dir, "cp-delay.pdf")}') - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - prog='cp_delay', - description='Convert pcap of control plane delay into graph' - ) - subparser = parser.add_subparsers(required=True) - parser_text = subparser.add_parser('text', - help='convert .pcapng files from the directory to .pcapng.txt files') - parser_text.set_defaults(func=convert) - parser_text.add_argument('dir', type=pathlib.Path, - help='directory containing .pcapng files to convert') - - parser_plot = subparser.add_parser('plot', help='write plot from .pcapng.txt files') - parser_plot.set_defaults(func=plot) - parser_plot.add_argument('dir', type=pathlib.Path, - help='directory containing .pcapng.txt files') - parser_plot.add_argument('num', type=int, - help='number of .pcapng.txt files to use') - - args = parser.parse_args() - args.func(args) diff --git a/scripts/graphs/latency_switch.py b/scripts/plots/latency_switch.py similarity index 100% rename from scripts/graphs/latency_switch.py rename to scripts/plots/latency_switch.py diff --git a/scripts/plots/policy_diff.py b/scripts/plots/policy_diff.py new file mode 100755 index 0000000..9bb720d --- /dev/null +++ b/scripts/plots/policy_diff.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +'''Create plot for policy diff scenario''' +# Copyright 2024 Louis Royer. All rights reserved. +# Use of this source code is governed by a MIT-style license that can be +# found in the LICENSE file. +# SPDX-License-Identifier: MIT + +import argparse +import pathlib +import matplotlib.pyplot as plt + +def plot(arguments: argparse.Namespace): + '''Write plot''' + res = [] + with open(arguments.slicea, 'r', encoding='utf8') as ping: + res.append({'tsp': [], 'pqt': []}) + for i, line in enumerate(ping): + if 'time=' in line: + res[0]['tsp'].append(float (line.split('[')[1].split('] ')[0])) + res[0]['pqt'].append(float(line.split('time=' )[1].split(' ms' )[0])) + with open(arguments.sliceb, 'r', encoding='utf8') as ping: + res.append({'tsp': [], 'pqt': []}) + for i, line in enumerate(ping): + if 'time=' in line: + res[1]['tsp'].append(float (line.split('[')[1].split('] ')[0])) + res[1]['pqt'].append(float(line.split('time=' )[1].split(' ms' )[0])) + first = min(res[0]['tsp'][0], res[1]['tsp'][0]) + for i, timestamp in enumerate(res[0]['tsp']): + res[0]['tsp'][i] = timestamp - first + for i, timestamp in enumerate(res[1]['tsp']): + res[1]['tsp'][i] = timestamp - first + _, axplt = plt.subplots() + axplt.set_xlabel('Time (s)') + axplt.set_ylabel('RTT (ms)') + axplt.plot(res[0]['tsp'], res[0]['pqt'], color='tab:red', label='Slice A') + axplt.plot(res[1]['tsp'], res[1]['pqt'], color='tab:blue', label='Slice B') + axplt.autoscale_view() + axplt.legend() + plt.savefig(arguments.output) + print(f'plot saved in {arguments.output}') + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + prog='policy_diff', + description='Convert ping result into plot' + ) + parser.set_defaults(func=plot) + parser.add_argument('slicea', type=pathlib.Path, + help='ping result file') + parser.add_argument('sliceb', type=pathlib.Path, + help='ping result file') + parser.add_argument('output', type=pathlib.Path, + help='output file') + + args = parser.parse_args() + args.func(args) diff --git a/templates/compose.yaml.j2 b/templates/compose.yaml.j2 index 41a03a0..23940b3 100644 --- a/templates/compose.yaml.j2 +++ b/templates/compose.yaml.j2 @@ -59,7 +59,7 @@ services: ran: # automatic allocation by docker compose #~ if config["topology"]["nb_ue"] > 1 - {{ container_s(name='ue2', image='louisroyer/ueransim-ue', enable_ipv6=True, debug='always', iface_tun=True, cap_net_admin=True, init=True) }} + {{ container_s(name='ue2', image='louisroyer/ueransim-ue', enable_ipv6=True, debug='always', debug_volume=True, iface_tun=True, cap_net_admin=True, init=True) }} depends_on: amf: condition: service_started