Skip to content

Commit 0af0924

Browse files
committed
Update CLI instructions
1 parent 5b05b7f commit 0af0924

File tree

4 files changed

+144
-91
lines changed

4 files changed

+144
-91
lines changed

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,14 @@ In this deployment, Open5GS is configured to support two network slices. Each sl
165165
### Deploy Open5GS components
166166
To deploy Open5GS, apply the Kubernetes manifest files in the open5gs/ directory. These manifest files define each Open5GS NF as a separate pod, allowing the platform to operate in a distributed fashion.
167167

168-
Select one of the following deployment options based on your monitoring needs:
168+
Select one of the following deployment options based on your monitoring needs - **Standard Deployment** or **Deployment with Monarch**.
169+
170+
> [!WARNING]
171+
> Deploy only one option at a time.
172+
173+
> [!NOTE]
174+
> To switch deployments (e.g., from Standard to Monarch), first delete the current deployment with
175+
> `kubectl delete -k open5gs -n open5gs`. Then proceed with the other deployment option.
169176
170177
#### 1. Standard Deployment:
171178
Deploys Open5GS network functions as separate pods for a distributed setup.

add-subscribers-using-cli.md

+23-18
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
# Add subscribers using CLI
22

3-
Using the CLI to add subscribers and slices is particularly efficient when dealing with multiple slices (e.g., 10 or more). This method enables batch additions and can be adjusted via the config.yaml file in the data directory.
3+
Using the CLI to add subscribers and slices is particularly efficient when dealing with multiple slices (e.g., 10 or more). This method enables batch additions and can be adjusted via the configuration options described below.
44

5-
## Configuration File (config.yaml)
6-
The default configuration (data/config.yaml) is shown below:
5+
## Configuration File (mongo-tools/generate-data.py)
6+
The default configuration (mongo-tools/generate-data.py) is shown below:
77

8-
```yaml
9-
NUM_SLICES: 2
10-
NUM_SUBSCRIBERS: 0 # simulated subscribers, one per slice e.g., ue_101, ue_201 etc.
11-
COTS_UE: false # Test SIMs used in COTS UE (Pixel 7 Pro)
12-
SIM_UE: true # 3 simulated UEs (ue1, ue2, ue3) for testing with UERANSIM
8+
```python
9+
DEFAULT_CONFIG = {
10+
"NUM_SLICES": 2, # Number of network slices to create
11+
"NUM_COTS_SUBSCRIBERS": 0, # Number of COTS subscribers
12+
"NUM_SAMPLE_SUBSCRIBERS": 2, # Number of simulated subscribers
13+
"NUM_AUTO_GENERATED_SUBSCRIBERS": 0, # Number of auto-generated subscribers (for MSD deployment only)
14+
}
1315
```
1416

1517
### Types of Subscribers
1618

1719
Our setup supports three types of subscribers:
1820

19-
- **COTS_UE**: Refers to Commercial Off-The-Shelf UEs configured with programmable SIM cards, used with USRP-based gNBs. Currently, these are configured for the UWaterloo lab.
20-
- **SIM_UE**: Denotes simulated UEs, such as subscriber_1, subscriber_2, and subscriber_3, for testing with UERANSIM. By default:
21+
- **NUM_COTS_SUBSCRIBERS**: Refers to Commercial Off-The-Shelf UEs configured with programmable SIM cards, used with USRP-based gNBs. Currently, these are configured for the UWaterloo lab.
22+
- **NUM_SAMPLE_SUBSCRIBERS**: Denotes simulated UEs, such as subscriber_1, subscriber_2, and subscriber_3, for testing with UERANSIM. By default:
2123
- subscriber_1 and subscriber_3 connect to slice 1.
2224
- subscriber_2 connects to slice 2.
23-
- **NUM_SUBSCRIBERS**: Specifies the number of programmatically added simulated UEs. Each UE is assigned to slices in a round-robin fashion: For instance, if NUM_SUBSCRIBERS is 3 and NUM_SLICES is 2, the subscribers will be added as follows:
25+
- **NUM_AUTO_GENERATED_SUBSCRIBERS**: Specifies the number of automatically generated simulated UEs. Each UE is assigned to slices in a round-robin fashion: For instance, if NUM_SUBSCRIBERS is 3 and NUM_SLICES is 2, the subscribers will be added as follows:
2426
- Slice 1: subscriber_101
2527
- Slice 2: subscriber_201
2628
- Slice 1: subscriber_102
@@ -33,16 +35,20 @@ Our setup supports three types of subscribers:
3335
## Adding subscribers
3436
Ensure your virtual environment is active as described in as described in [Set up a virtual environment](README.md#1-set-up-a-virtual-environment).
3537

36-
1. Run the generate-data.py script, which reads data/config.yaml and generates two files: data/slices.yaml and data/subscribers.yaml.
38+
1. Run the generate-data.py script, generates two files: `data/slices.yaml` and `data/subscribers.yaml`.
3739

3840
```bash
3941
(venv) dev@workshop-vm:~/open5gs-k8s$ python mongo-tools/generate-data.py
4042
```
4143
You should see output similar to the one below.
4244
```bash
43-
2024-10-31 11:38:29 | INFO | Creating 2 slices and 0 subscribers
44-
2024-10-31 11:38:29 | INFO | Creating slices and saving to data/slices.yaml
45-
2024-10-31 11:38:29 | INFO | Creating subscribers and saving to data/subscribers.yaml
45+
2024-11-06 16:36:02 | INFO | Loading existing data...
46+
2024-11-06 16:36:02 | INFO | Creating 2 slices ...
47+
2024-11-06 16:36:02 | INFO | Generating 0 new slices...
48+
2024-11-06 16:36:02 | INFO | Saving slices to data/slices.yaml
49+
2024-11-06 16:36:02 | INFO | Adding 2 sample subscribers ...
50+
2024-11-06 16:36:02 | INFO | Saving subscribers to data/subscribers.yaml
51+
2024-11-06 16:36:02 | INFO | Slice and subscriber creation complete.
4652
```
4753
2. Run add-subscribers.py to add the generated subscribers.
4854

@@ -54,9 +60,10 @@ Ensure your virtual environment is active as described in as described in [Set u
5460
```bash
5561
2024-10-31 11:54:22 | INFO | Added subscriber_1
5662
2024-10-31 11:54:22 | INFO | Added subscriber_2
57-
2024-10-31 11:54:22 | INFO | Added subscriber_3
5863
```
5964

65+
> [!NOTE]
66+
> The default configuration will add 2 sample subscribers. To add a third sample subsciber, edit the configuration variable `NUM_SAMPLE_SUBSCRIBERS` to 3, then re-run `generate.py` and `add-subscribers.py`
6067

6168
3. You can verify if the subscribers were added with
6269
```bash
@@ -66,7 +73,6 @@ Ensure your virtual environment is active as described in as described in [Set u
6673
```
6774
2024-10-31 11:55:36 | INFO | subscriber_1
6875
2024-10-31 11:55:36 | INFO | subscriber_2
69-
2024-10-31 11:55:36 | INFO | subscriber_3
7076
```
7177
4. To delete all subscribers, use delete-subscribers.py.
7278
```bash
@@ -76,5 +82,4 @@ Example output:
7682
```bash
7783
2024-10-31 11:56:55 | INFO | Deleted 001010000000001
7884
2024-10-31 11:56:55 | INFO | Deleted 001010000000002
79-
2024-10-31 11:56:55 | INFO | Deleted 001010000000003
8085
```

data/config.yaml

-4
This file was deleted.

mongo-tools/generate-data.py

+113-68
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,29 @@
55

66
yaml = YAML()
77

8-
DEFAULT_UPLINK_SPEED = {"value": 1, "unit": Open5GS.Unit.Gbps}
9-
DEFAULT_DOWNLINK_SPEED = {"value": 1, "unit": Open5GS.Unit.Gbps}
10-
DEFAULT_QOS_INDEX = 9
11-
DEFAULT_ARP_VALUES = {
12-
"priority_level": 8,
13-
"pre_emption_capability": Open5GS.Status.DISABLED,
14-
"pre_emption_vulnerability": Open5GS.Status.DISABLED,
8+
######################### CONFIGURATION ###################################
9+
DEFAULT_CONFIG = {
10+
"NUM_SLICES": 2, # Number of network slices to create
11+
"NUM_COTS_SUBSCRIBERS": 0, # Number of COTS subscribers
12+
"NUM_SAMPLE_SUBSCRIBERS": 2, # Number of simulated subscribers
13+
"NUM_AUTO_GENERATED_SUBSCRIBERS": 0, # Number of auto-generated subscribers (for MSD deployment only)
14+
"DEFAULT_UPLINK_SPEED": {"value": 1, "unit": Open5GS.Unit.Gbps},
15+
"DEFAULT_DOWNLINK_SPEED": {"value": 1, "unit": Open5GS.Unit.Gbps},
16+
"DEFAULT_QOS_INDEX": 9,
17+
"DEFAULT_ARP_VALUES": {
18+
"priority_level": 8,
19+
"pre_emption_capability": Open5GS.Status.DISABLED,
20+
"pre_emption_vulnerability": Open5GS.Status.DISABLED,
21+
},
22+
"DEFAULT_KEY": "465B5CE8B199B49FAA5F0A2EE238A6BC",
23+
"DEFAULT_OPC": "E8ED289DEBA952E4283B54E88E6183CA",
24+
"DATA_DIR": "data",
25+
"SLICE_FILE_PATH": "data/slices.yaml",
26+
"SUBSCRIBER_FILE_PATH": "data/subscribers.yaml"
1527
}
1628

17-
18-
DEFAULT_KEY = "465B5CE8B199B49FAA5F0A2EE238A6BC"
19-
DEFAULT_OPC = "E8ED289DEBA952E4283B54E88E6183CA"
20-
DATA_DIR = "data"
21-
22-
SLICE_FILE_PATH = f"{DATA_DIR}/slices.yaml"
23-
SUBSCRIBER_FILE_PATH = f"{DATA_DIR}/subscribers.yaml"
24-
29+
######################### DATA STRUCTURES #################################
30+
# Example slice and subscriber data for testing.
2531
slice_data = {
2632
"slice_1": {
2733
"sst": 1,
@@ -72,8 +78,7 @@
7278
],
7379
},
7480
}
75-
76-
subscriber_data = {
81+
simulated_subscriber_data = {
7782
"subscriber_1": {
7883
"_id": "",
7984
"imsi": "001010000000001",
@@ -138,8 +143,7 @@
138143
"__v": 0,
139144
},
140145
}
141-
142-
cots_ue_data = {
146+
cots_subscriber_data = {
143147
"pixel_1": {
144148
"_id": "",
145149
"imsi": "001010000060592",
@@ -184,8 +188,33 @@
184188
},
185189
}
186190

191+
######################### HELPER FUNCTIONS #################################
192+
193+
def convert_defaultdict_to_dict(obj):
194+
"""Recursively converts defaultdict to dict."""
195+
if isinstance(obj, defaultdict):
196+
obj = dict(obj) # Convert the defaultdict to a regular dict
197+
if isinstance(obj, dict):
198+
for key, value in obj.items():
199+
obj[key] = convert_defaultdict_to_dict(value) # Recursively convert nested defaultdicts
200+
return obj
201+
202+
def load_existing_data():
203+
"""Load existing slice and subscriber data if available."""
204+
global slice_data, simulated_subscriber_data, cots_subscriber_data
205+
try:
206+
with open(DEFAULT_CONFIG["SLICE_FILE_PATH"], "r") as file:
207+
slice_data = yaml.load(file)
208+
except FileNotFoundError:
209+
log.warning(f"No existing slice data found at {DEFAULT_CONFIG['SLICE_FILE_PATH']}")
187210

188-
def generate_slice_data(slice_number, qos_index):
211+
try:
212+
with open(DEFAULT_CONFIG["SUBSCRIBER_FILE_PATH"], "r") as file:
213+
subscribers = yaml.load(file)
214+
except FileNotFoundError:
215+
log.warning(f"No existing subscriber data found at {DEFAULT_CONFIG['SUBSCRIBER_FILE_PATH']}")
216+
217+
def generate_slice_data(slice_number, qos_index=DEFAULT_CONFIG["DEFAULT_QOS_INDEX"]):
189218
slice_name = f"slice_{slice_number}"
190219
sd = f"{slice_number:06x}"
191220
session_name = f"dnn{slice_number}"
@@ -201,34 +230,30 @@ def generate_slice_data(slice_number, qos_index):
201230
"type": Open5GS.Type.IPv4,
202231
"pcc_rule": [],
203232
"ambr": {
204-
"uplink": DEFAULT_UPLINK_SPEED,
205-
"downlink": DEFAULT_DOWNLINK_SPEED,
233+
"uplink": DEFAULT_CONFIG["DEFAULT_UPLINK_SPEED"],
234+
"downlink": DEFAULT_CONFIG["DEFAULT_DOWNLINK_SPEED"],
206235
},
207-
"qos": {"index": qos_index, "arp": DEFAULT_ARP_VALUES},
236+
"qos": {"index": qos_index, "arp": DEFAULT_CONFIG["DEFAULT_ARP_VALUES"]},
208237
}
209238
],
210239
}
211240
}
212241

213-
214242
def create_slices(num_slices):
215-
last_slice_number = max(slice_data.keys(), key=lambda x: int(x.split("_")[1]))
243+
"""Create slices based on the number specified and add to slice_data."""
244+
last_slice_number = max(slice_data.keys(), key=lambda x: int(x.split("_")[1]), default="slice_0")
216245
starting_slice_number = int(last_slice_number.split("_")[1]) + 1
217246

218247
for i in range(starting_slice_number, starting_slice_number + num_slices):
219-
qos_index = DEFAULT_QOS_INDEX
220-
slice_data.update(generate_slice_data(i, qos_index))
221-
248+
slice_data.update(generate_slice_data(i))
222249

223250
def generate_subscriber_data(slice_name, subscriber_index):
224251
slice_info = slice_data[slice_name]
225252
slice_number = int(slice_name.split("_")[1])
226253
subscriber_name = f"subscriber_{slice_number}{subscriber_index:02d}"
227254

228255
padding_length = 15 - len(f"00101{slice_name.split('_')[1]}{subscriber_index:02d}")
229-
imsi = (
230-
f"00101{'0' * padding_length}{slice_name.split('_')[1]}{subscriber_index:02d}"
231-
)
256+
imsi = f"00101{'0' * padding_length}{slice_name.split('_')[1]}{subscriber_index:02d}"
232257

233258
return {
234259
subscriber_name: {
@@ -240,65 +265,85 @@ def generate_subscriber_data(slice_name, subscriber_index):
240265
"access_restriction_data": 32,
241266
"slice": [slice_info],
242267
"ambr": {
243-
"uplink": DEFAULT_UPLINK_SPEED,
244-
"downlink": DEFAULT_DOWNLINK_SPEED,
268+
"uplink": DEFAULT_CONFIG["DEFAULT_UPLINK_SPEED"],
269+
"downlink": DEFAULT_CONFIG["DEFAULT_DOWNLINK_SPEED"],
245270
},
246271
"security": {
247-
"k": DEFAULT_KEY,
272+
"k": DEFAULT_CONFIG["DEFAULT_KEY"],
248273
"amf": "8000",
249274
"op": None,
250-
"opc": DEFAULT_OPC,
275+
"opc": DEFAULT_CONFIG["DEFAULT_OPC"],
251276
},
252277
"schema_version": 1,
253278
"__v": 0,
254279
}
255280
}
256281

257-
258-
def create_subscribers(num_subscribers):
282+
def create_auto_generated_subscribers():
283+
"""Create subscribers and assign them to slices in a round-robin fashion."""
259284
subscribers = {}
260285

261-
if config["COTS_UE"]:
262-
subscribers.update(cots_ue_data)
286+
num_auto_generated_subscribers = DEFAULT_CONFIG["NUM_AUTO_GENERATED_SUBSCRIBERS"]
263287

264-
if config["SIM_UE"]:
265-
subscribers.update(subscriber_data)
266-
288+
if num_auto_generated_subscribers > 0:
289+
log.info(f"Generating {num_auto_generated_subscribers} auto generated subscribers ...")
267290

268-
num_slices = len(slice_data)
269-
slice_counter = 0
270-
subscriber_assignments = defaultdict(list)
271-
for _ in range(1, num_subscribers + 1):
272-
slice_index = slice_counter % num_slices + 1
273-
slice_name = f"slice_{slice_index}"
274-
subscriber_index = len(subscriber_assignments[slice_name]) + 1
275-
subscriber_assignments[slice_name].append(subscriber_index)
276-
slice_counter += 1
277-
subscriber = generate_subscriber_data(slice_name, subscriber_index)
278-
subscribers.update(subscriber)
291+
num_slices = DEFAULT_CONFIG["NUM_SLICES"]
292+
slice_counter = 0
293+
subscriber_assignments = defaultdict(list)
294+
for i in range(1, num_auto_generated_subscribers + 1):
295+
slice_index = slice_counter % num_slices + 1
296+
slice_name = f"slice_{slice_index}"
297+
subscriber_index = len(subscriber_assignments[slice_name]) + 1
298+
subscriber_assignments[slice_name].append(subscriber_index)
299+
subscriber = generate_subscriber_data(slice_name, subscriber_index)
300+
subscribers.update(subscriber)
301+
slice_counter += 1
279302

280303
return subscribers
281304

305+
def create_cots_subscribers(subscribers):
306+
num_cots_subscribers = DEFAULT_CONFIG["NUM_COTS_SUBSCRIBERS"]
307+
if num_cots_subscribers > 0:
308+
log.info(f"Adding {num_cots_subscribers} COTS subscribers ...")
309+
from itertools import islice
310+
for key, value in islice(cots_subscriber_data.items(), num_cots_subscribers):
311+
subscribers[key] = value
312+
313+
def create_simulated_subscribers(subscribers):
314+
num_simulated_subscribers = DEFAULT_CONFIG["NUM_SAMPLE_SUBSCRIBERS"]
315+
if num_simulated_subscribers > 0:
316+
log.info(f"Adding {num_simulated_subscribers} sample subscribers ...")
317+
from itertools import islice
318+
for key, value in islice(simulated_subscriber_data.items(), num_simulated_subscribers):
319+
subscribers[key] = value
320+
282321

283-
if __name__ == "__main__":
284322

285-
with open(DATA_DIR + "/config.yaml", "r") as file:
286-
config_file = file.read()
287-
config = yaml.load(config_file)
323+
######################### MAIN SCRIPT ###################################
288324

289-
num_slices = config["NUM_SLICES"]
290-
num_subscribers = config["NUM_SUBSCRIBERS"]
291-
existing_slices = len(slice_data)
325+
def main():
326+
log.info("Loading existing data...")
327+
load_existing_data()
292328

293-
log.info(f"Creating {num_slices} slices and {num_subscribers} subscribers")
329+
log.info(f"Creating {DEFAULT_CONFIG['NUM_SLICES']} slices ...")
330+
slices_to_generate = DEFAULT_CONFIG["NUM_SLICES"] - len(slice_data)
331+
log.info(f"Generating {slices_to_generate} new slices...")
332+
create_slices(slices_to_generate)
294333

295-
log.info(f"Creating slices and saving to {SLICE_FILE_PATH}")
296-
create_slices(num_slices - existing_slices)
297-
with open(SLICE_FILE_PATH, "w") as file:
334+
log.info(f"Saving slices to {DEFAULT_CONFIG['SLICE_FILE_PATH']}")
335+
with open(DEFAULT_CONFIG["SLICE_FILE_PATH"], "w") as file:
298336
yaml.dump(slice_data, file)
299337

300-
log.info(f"Creating subscribers and saving to {SUBSCRIBER_FILE_PATH}")
301-
num_subscribers = config["NUM_SUBSCRIBERS"]
302-
subscribers = create_subscribers(num_subscribers)
303-
with open(SUBSCRIBER_FILE_PATH, "w") as file:
338+
subscribers = create_auto_generated_subscribers()
339+
create_cots_subscribers(subscribers)
340+
create_simulated_subscribers(subscribers)
341+
342+
log.info(f"Saving subscribers to {DEFAULT_CONFIG['SUBSCRIBER_FILE_PATH']}")
343+
with open(DEFAULT_CONFIG["SUBSCRIBER_FILE_PATH"], "w") as file:
304344
yaml.dump(subscribers, file)
345+
346+
log.info("Slice and subscriber creation complete.")
347+
348+
if __name__ == "__main__":
349+
main()

0 commit comments

Comments
 (0)