# VNF Onboarding Walkthrough **NOTE: this section uses pre-SOL006 descriptors which need to be converted before trying in Release 9+** ## Introduction This section uses NextEPC (an open-source implementation of a 4G/5G packet core) to go through most of the steps described in the onboarding guidelines, in order to provide a concrete example on how to build a complete VNF Package from scratch. The example is meant to be used for educational purposes and not for a real-life implementation of an EPC. It may change over time to cover more use cases. A Linux machine is required to follow the complete procedure. In addition to the procedure, here you can find some resources related to it: - [Resulting packages](https://osm-download.etsi.org/ftp/Packages/vnf-onboarding-tf/) - [Images](https://osm-download.etsi.org/ftp/images/vnf-onboarding-tf/) - [Video presentation](https://youtu.be/hZzwwy9wNRE) ## VNF Requirements ### Day-0 Requirements The following table describes the components description and associated images. | VDU | Description | Image name | | :-----: | :------------------------------------- | :-----------------------------------------------------------------------------------------------------------: | | spgwmme | Single VDU containing SGW, PGW and MME | [nextepc-spgwmme-base](https://osm-download.etsi.org/ftp/images/vnf-onboarding-tf/nextepc-spgwmme-base.qcow2) | | hss | HSS VDU | [nextepc-hss-base](https://osm-download.etsi.org/ftp/images/vnf-onboarding-tf/nextepc-hss-base.qcow2) | And here the NFVI requirements (for this example, as the VNF has no strict requirements) | VDU | vCPU | RAM (GB) | Storage (GB) | EPA Features | | :-----: | :--: | :------: | :----------: | :---------------------------------------- | | spgwmme | 2 | 4 | 10 | CPU Pinning, Huge Pages & SRIOV in S1/SGI | | hss | 1 | 2 | 10 | - | The topology of the VNF would be as follows: ![](assets/vnfonbtf_samplevnf_1.png) Finally, we will use cloud-init files to set a hostname and password (just as example, since OSM VCA supports SSH key authentication). The password will be kept as a variable, so it can be passed at instantiation time. ``` #cloud-config hostname: nextepc-hss password: {{ password }} chpasswd: { expire: False } ssh_pwauth: True ``` ``` #cloud-config hostname: nextepc-spgw password: {{ password }} chpasswd: { expire: False } ssh_pwauth: True ``` ### Day-1 Requirements For this sample VNF, we will assume that establishing the session between the MME and HSS, and starting the main services, is enough to consider that the EPC is ready to start serving subscribers. This VNF does not expose an API, so we will configure it with Linux commands through SSH. SPGW-MME Day-1 operations include: - Enabling its 3 additional interfaces: ``` sudo ip link set ens4 up && sudo dhclient ens4 sudo ip link set ens5 up && sudo dhclient ens5 sudo ip link set ens6 up && sudo dhclient ens6 ``` - Replacing the HSS and SPGW IP addresses in the configuration file of the MME component. We will need to pass both IP addresses as instantiation parameters (as of OSM Release 6.0.2, there is no automatic detection of non-management IP addressing) ``` sudo sed -i 's/$hss_ip/HSS_IP/g' /etc/nextepc/freeDiameter/mme.conf sudo sed -i 's/$spgw_ip/SPGW_IP/g' /etc/nextepc/freeDiameter/mme.conf ``` - Restarting the MME daemon ``` sudo systemctl restart nextepc-mmed ``` HSS Day-1 operations include: - Enabling its additional interface: ``` sudo ip link set ens4 up && sudo dhclient ens4 ``` - Replacing the HSS and SPGW IP addresses in the configuration file of the HSS component. We will need to pass both IP addresses as instantiation parameters as well. ``` sudo sed -i 's/$hss_ip/HSS_IP/g' /etc/nextepc/freeDiameter/hss.conf sudo sed -i 's/$spgw_ip/SPGW_IP/g' /etc/nextepc/freeDiameter/hss.conf ``` - Restarting the HSS daemon ``` sudo systemctl restart nextepc-hssd ``` ### Day-2 Requirements For day-2 operations, we will enable the possibility of reconfiguring and monitoring the SPGW. - SPGW reconfiguration includes adding static routes. We will need to pass variables for prefix and next-hop. ``` sudo route add -net PREFIX gw NEXTHOP ``` - SPGW KPIs will include CPU and Memory metrics collection from the NFVI, through the VIM. ## Building the VNF Package for Day-0 - Generate base VNF and NS packages. Our VNF includes 2 VDUs, but we will specify just one and add the other one later. ``` osm package-create --base-directory ~/vEPC --image nextepc-spgwmme-base --vcpu 2 --memory 4096 --storage 10 --interfaces 3 --vendor OSM_VNFONB_TF vnf vEPC ``` - Modify the VNFD (at ~/vEPC/vEPC_vnf/vEPC_vnfd.yaml), to replace the names of our first VDU and its connection points. ``` ... vdu: - id: spgwmme name: spgwmme description: spgwmme ... interface: - name: eth0 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: spgwmme-mgmt - name: eth1 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: spgwmme-s1 - name: eth2 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: spgwmme-sgi - name: eth3 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: spgwmme-s6a ... ``` - Add the second VDU, you can copy and paste the first one, adapting the number of interfaces and VM Flavor. ``` ... vdu: ... - id: hss name: hss description: hss count: 1 vm-flavor: vcpu-count: 1 memory-mb: 2048 storage-gb: 10 image: 'nextepc-hss-base' interface: - name: eth0 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: hss-mgmt - name: eth1 type: EXTERNAL virtual-interface: type: PARAVIRT external-connection-point-ref: hss-s6a ... ``` - Modify the s6a connection point to be 'internal' one, mapped to internal CPs and VLD to interconnect both VDUs. ``` ... vdu: - id: spgwmme ... interface: ... - name: eth3 type: INTERNAL virtual-interface: type: PARAVIRT internal-connection-point-ref: spgwmme-s6a internal-connection-point: - id: spgwmme-s6a name: spgwmme-s6a type: VPORT ... vdu: - id: hss ... interface: ... - name: eth1 type: INTERNAL virtual-interface: type: PARAVIRT internal-connection-point-ref: hss-s6a internal-connection-point: - id: hss-s6a name: hss-s6a type: VPORT ... internal-vld: - id: s6a internal-connection-point: - id-ref: spgwmme-s6a - id-ref: hss-s6a name: s6a ... ``` - Modify the external connection points that will be exposed to the Network Service level, and set the management one. ``` ... connection-point: - name: spgwmme-mgmt - name: spgwmme-s1 - name: spgwmme-sgi - name: hss-mgmt mgmt-interface: cp: spgwmme-mgmt ... ``` - We will set a particular subnet prefix for our internal VLD, to be able to set our own IP addresses at instantiation time. ``` ... ip-profiles: - name: s6a description: s6a network ip-profile-params: ip-version: ipv4 subnet-address: 10.0.6.0/24 dhcp-params: enabled: true ... internal-vld: - id: s6a ip-profile-ref: s6a ... ``` - Now, let's add the EPA requirements to the SPGW VDU. ``` ... vdu: - id: spgwmme guest-epa: cpu-pinning-policy: DEDICATED mempage-size: LARGE ... interface: ... - name: eth1 type: EXTERNAL virtual-interface: type: SRIOV external-connection-point-ref: spgwmme-s1 - name: eth2 type: EXTERNAL virtual-interface: type: SRIOV external-connection-point-ref: spgwmme-sgi ... ``` - Finally, let's specify the cloud-init file names in the descriptor, and copy them to the corresponding folder (~/vEPC/vEPC_vnf/cloud_init/) ``` ... vdu: - id: spgwmme cloud-init-file: spgwmme-init ... vdu: - id: hss cloud-init-file: hss-init ... ``` - At this point, it is ideal to validate if your package has the correct format: ``` # Validating the VNFD osm package-validate vEPC/vEPC_vnf/ ``` ## Building the VNF Package for Day-1 - Let's start with the SPGW day-1 operations by populating the descriptor with 'initial config primitives'. The management interface is already set at the "spgw_mgmt" CP, so OSM will connect to this machine when defining a configuration at the VNF level. A 'config' primitive is required to pass the parameters for SSH connection. The only parameter that is auto-populated is 'rw_mgmt_ip' (the management IP address), the other ones will need to be provided at instantiation time. Note that we are setting names for primitives (operations), order of execution, parameters, and the juju charm that will implement all of them. ``` ... vnf-configuration: initial-config-primitive: - seq: '1' name: config parameter: - name: ssh-hostname value: - name: ssh-username value: ubuntu - name: ssh-password value: - seq: '2' name: configure-spgw parameter: - name: spgw-ip data-type: STRING value: - name: hss-ip data-type: STRING value: - seq: '3' name: restart-spgw juju: charm: spgwcharm ... ``` - Now, let's modify the descriptor to include operations for the HSS VDU. For this, we will use a configuration block at the VDU level, and specify the management interface for this particular VDU. ``` ... vdu: ... - id: hss ... interface: - name: eth0 type: EXTERNAL mgmt-interface: true ... vdu-configuration: initial-config-primitive: - seq: '1' name: config parameter: - name: ssh-hostname value: - name: ssh-username value: ubuntu - name: ssh-password value: - seq: '2' name: configure-hss parameter: - name: spgw-ip data-type: STRING value: - name: hss-ip data-type: STRING value: - seq: '3' name: restart-hss juju: charm: hsscharm ... ``` - Validate your package again, just in case: ``` # Validating the VNFD osm package-validate vEPC/vEPC_vnf/ ``` - Finally, we need to build the 'spgwcharm' and 'hsscharm' that will implement these operations. Let's start with the SPGW. ``` # Prepare the environment snap install charm --classic mkdir -p ~/charms/layers export JUJU_REPOSITORY=~/charms export LAYER_PATH=$JUJU_REPOSITORY/layers cd $LAYER_PATH charm create spgwcharm cd spgwcharm ``` ``` # Modify the layer.yaml file includes: - layer:basic - layer:vnfproxy ``` ``` # Modify the metadata.yaml file name: spgwcharm summary: NextEPC SPGW Charm maintainer: Your name description: | This is an example of a proxy charm deployed by Open Source Mano. tags: - nfv subordinate: false series: - trusty - xenial ``` ``` # Create and modify the actions.yaml file configure-spgw: description: "Configures the SPGW" params: hss-ip: description: "HSS IP" type: string default: "0.0.0.0" spgw-ip: description: "SPGW IP" type: string default: "0.0.0.0" restart-spgw: description: "Restarts the service of the VNF" ``` ``` # Create the 'actions' folder and populate executable files for each action, with the same content: mkdir actions cat <<'EOF' >> actions/configure-spgw #!/usr/bin/env python3 import sys sys.path.append('lib') from charms.reactive import main from charms.reactive import set_state from charmhelpers.core.hookenv import action_fail, action_name """ `set_state` only works here because it's flushed to disk inside the `main()` loop. remove_state will need to be called inside the action method. """ set_state('actions.{}'.format(action_name())) try: main() except Exception as e: action_fail(repr(e)) EOF chmod +x actions/configure-spgw ``` ``` # Same procedure for the 'restart-spgw' action cat <<'EOF' >> actions/restart-spgw #!/usr/bin/env python3 import sys sys.path.append('lib') from charms.reactive import main from charms.reactive import set_state from charmhelpers.core.hookenv import action_fail, action_name """ `set_state` only works here because it's flushed to disk inside the `main()` loop. remove_state will need to be called inside the action method. """ set_state('actions.{}'.format(action_name())) try: main() except Exception as e: action_fail(repr(e)) EOF chmod +x actions/restart-spgw ``` ``` # Fill in the contents of the 'reactive' file (reactive/spgwcharm.py) from charms.reactive import when, when_not, set_flag from charmhelpers.core.hookenv import ( action_get, action_fail, action_set, config, status_set, ) from charms.reactive import ( remove_state as remove_flag, set_state as set_flag, when, when_not ) import charms.sshproxy @when('actions.configure-spgw') def configure_spgw(): hss_ip = action_get('hss-ip') spgw_ip = action_get('spgw-ip') cmd1 = "sudo ip link set ens4 up && sudo dhclient ens4" charms.sshproxy._run(cmd1) cmd2 = "sudo ip link set ens5 up && sudo dhclient ens5" charms.sshproxy._run(cmd2) cmd3 = "sudo ip link set ens6 up && sudo dhclient ens6" charms.sshproxy._run(cmd3) cmd3='sudo sed -i "\'s/$hss_ip/{}/g\'" /etc/nextepc/freeDiameter/mme.conf'.format(hss_ip) charms.sshproxy._run(cmd3) cmd4='sudo sed -i "\'s/$spgw_ip/{}/g\'" /etc/nextepc/freeDiameter/mme.conf'.format(spgw_ip) charms.sshproxy._run(cmd4) remove_flag('actions.configure-spgw') @when('actions.restart-spgw') def restart_spgw(): cmd = "sudo systemctl restart nextepc-mmed" charms.sshproxy._run(cmd) remove_flag('actions.restart-spgw') ``` - Proceed in a similar way for the HSS, and provide the following contents to the reactive file: ``` cd $LAYER_PATH charm create hsscharm cd hsscharm ``` ``` # Modify the layer.yaml file includes: - layer:basic - layer:vnfproxy ``` ``` # Modify the metadata.yaml file name: hsscharm summary: NextEPC HSS Charm maintainer: Your name description: | This is an example of a proxy charm deployed by Open Source Mano. tags: - nfv subordinate: false series: - trusty - xenial ``` ``` # Create and modify the actions.yaml file configure-hss: description: "Configures the HSS" params: hss-ip: description: "HSS IP" type: string default: "0.0.0.0" spgw-ip: description: "SPGW IP" type: string default: "0.0.0.0" restart-hss: description: "Restarts the service of the VNF" ``` ``` # Create the 'actions' folder and populate executable files for each action, with the same content: mkdir actions cat <<'EOF' >> actions/configure-hss #!/usr/bin/env python3 import sys sys.path.append('lib') from charms.reactive import main from charms.reactive import set_state from charmhelpers.core.hookenv import action_fail, action_name """ `set_state` only works here because it's flushed to disk inside the `main()` loop. remove_state will need to be called inside the action method. """ set_state('actions.{}'.format(action_name())) try: main() except Exception as e: action_fail(repr(e)) EOF chmod +x actions/configure-hss ``` ``` # Same procedure for the 'restart-hss' action cat <<'EOF' >> actions/restart-hss #!/usr/bin/env python3 import sys sys.path.append('lib') from charms.reactive import main from charms.reactive import set_state from charmhelpers.core.hookenv import action_fail, action_name """ `set_state` only works here because it's flushed to disk inside the `main()` loop. remove_state will need to be called inside the action method. """ set_state('actions.{}'.format(action_name())) try: main() except Exception as e: action_fail(repr(e)) EOF chmod +x actions/restart-hss ``` ``` # Fill in the contents of the 'reactive' file (reactive/hsscharm.py) from charms.reactive import when, when_not, set_flag from charmhelpers.core.hookenv import ( action_get, action_fail, action_set, config, status_set, ) from charms.reactive import ( remove_state as remove_flag, set_state as set_flag, when, when_not ) import charms.sshproxy @when('actions.configure-hss') def configure_hss(): spgw_ip = action_get('spgw-ip') hss_ip = action_get('hss-ip') cmd1 = "sudo ip link set ens4 up && sudo dhclient ens4" charms.sshproxy._run(cmd1) cmd2= 'sudo sed -i "\'s/$hss_ip/{}/g\'" /etc/nextepc/freeDiameter/hss.conf'.format(hss_ip) charms.sshproxy._run(cmd2) cmd3= 'sudo sed -i "\'s/$spgw_ip/{}/g\'" /etc/nextepc/freeDiameter/hss.conf'.format(spgw_ip) charms.sshproxy._run(cmd3) remove_flag('actions.configure-hss') @when('actions.restart-hss') def restart_hss(): cmd = "sudo systemctl restart nextepc-hssd" charms.sshproxy._run(cmd) remove_flag('actions.restart-hss') ``` - Charms need to be built and copied into the package, but we can do this in a later stage after Day-2 operations have been defined. ## Building the VNF Package for Day-2 - As defined earlier, the only VDU containing reconfiguration primitives is the SPGW. Let's add it to the descriptor first. We can define default values in this kind of primitive. ``` ... vnf-configuration: ... config-primitive: - name: add-route parameter: - name: external-prefix data-type: STRING default-value: '8.8.8.8/32' - name: next-hop data-type: STRING default-value: '192.168.2.1' ``` - Back to the SPGW charm folder, modify the actions.yaml file to include this new primitive, ading the respective executable file at the 'actions' folder, and finally adding the primitive at the 'reactive' file. ``` # Modify the actions.yaml file ... add-route: description: "Adds a route to the SPGW" params: external-prefix: description: "Destinaton prefix IP" type: string default: "8.8.8.8/32" next-hop: description: "SGI next hop" type: string default: "192.168.2.1" ``` ``` # Populate the executable file for the new action, in the 'actions' folder: cat <<'EOF' >> actions/add-route #!/usr/bin/env python3 import sys sys.path.append('lib') from charms.reactive import main from charms.reactive import set_state from charmhelpers.core.hookenv import action_fail, action_name """ `set_state` only works here because it's flushed to disk inside the `main()` loop. remove_state will need to be called inside the action method. """ set_state('actions.{}'.format(action_name())) try: main() except Exception as e: action_fail(repr(e)) EOF chmod +x actions/add-route ``` ``` # Fill in the contents of the 'reactive' file (reactive/spgwcharm.py) ... @when('actions.add-route') def add_route(): prefix = action_get('external-prefix') next_hop = action_get('next-hop') cmd = "sudo route add -net " + prefix + " gw " + next_hop charms.sshproxy._run(cmd) remove_flag('actions.add-route') ``` - Build both charms and copy them to the VNF folder ``` cd ~/charms/layers/spgwcharm sudo -E charm build cp -r ~/charms/builds/spgwcharm ~/vEPC/vEPC_vnf/charms/ cd ~/charms/layers/hsscharm sudo -E charm build cp -r ~/charms/builds/hsscharm ~/vEPC/vEPC_vnf/charms/ ``` - We also defined monitoring as part of our day-2 operations. Modify the descriptor to monitor CPU and Memory metrics of the SPGW. ``` vdu: ... - id: spgwmme ... monitoring-param: - id: "spgw_cpu_util" nfvi-metric: "cpu_utilization" - id: "spgw_memory_util" nfvi-metric: "average_memory_utilization" ... monitoring-param: - id: "spgw_cpu_util" name: "spgw_cpu_util" aggregation-type: AVERAGE vdu-monitoring-param: vdu-ref: "spgwmme" vdu-monitoring-param-ref: "spgw_cpu_util" - id: "spgw_memory_util" name: "spgw_memory_util" aggregation-type: AVERAGE vdu-monitoring-param: vdu-ref: "spgwmme" vdu-monitoring-param-ref: "spgw_memory_util" ``` - Run a final validation of your package: ``` # Validating the VNFD osm package-validate vEPC/vEPC_vnf/ ``` ## Testing the VNF Package - To test the VNF package, you need to first include it into a NS Package, let's create one. ``` osm package-create --base-directory ~/vEPC --vendor OSM_VNFONB_TF ns vEPC ``` - Its content should be similar to the following one, where VLDs are mapped to the external connection points of the VNF. Management external network is expected to be already present at the VIM ('vim-network-name' attribute). Note that, for this example, we are setting some subnets and IP address values, to be requested to the VIM's IPAM to match some pre-existing configurations inside the VNF. ``` nsd:nsd-catalog: nsd: - id: vEPC_nsd name: vEPC_nsd short-name: vEPC_nsd description: Generated by OSM package generator vendor: OSM_VNFONB_TF version: '1.0' constituent-vnfd: - member-vnf-index: 1 vnfd-id-ref: vEPC_vnfd ip-profiles: - name: s1 description: s1 network ip-profile-params: ip-version: ipv4 subnet-address: 192.168.0.0/24 dhcp-params: enabled: true - name: sgi description: s1 network ip-profile-params: ip-version: ipv4 subnet-address: 192.168.2.0/24 dhcp-params: enabled: true vld: - id: management name: management short-name: management type: ELAN mgmt-network: 'true' vim-network-name: osm-ext vnfd-connection-point-ref: - member-vnf-index-ref: 1 vnfd-id-ref: vEPC_vnfd vnfd-connection-point-ref: spgwmme-mgmt - member-vnf-index-ref: 1 vnfd-id-ref: vEPC_vnfd vnfd-connection-point-ref: hss-mgmt - id: s1 name: s1 short-name: s1 type: ELAN ip-profile-ref: s1 vnfd-connection-point-ref: - member-vnf-index-ref: 1 vnfd-id-ref: vEPC_vnfd vnfd-connection-point-ref: spgwmme-s1 ip-address: 192.168.0.11 - id: sgi name: sgi short-name: sgi type: ELAN ip-profile-ref: sgi vnfd-connection-point-ref: - member-vnf-index-ref: 1 vnfd-id-ref: vEPC_vnfd vnfd-connection-point-ref: spgwmme-sgi ip-address: 192.168.2.11 ``` - Now, validate your descriptors against the OSM's Information Model, using the tool in the devops repository. For example: ``` # Validating the NSD osm package-validate vEPC/vEPC_ns/ ``` - Now that you now your descriptors are valid, compress both packages and upload them to OSM. ``` cd ~/vEPC tar -cvzf vEPC_vnfd.tar.gz vEPC_vnf/ tar -cvzf vEPC_nsd.tar.gz vEPC_ns/ osm vnfd-create vEPC_vnfd.tar.gz osm nsd-create vEPC_nsd.tar.gz ``` - Prepare a file, which you could call 'params.yaml', with the instantiaton parameters. Note that it also includes a 'vnf' block that modifies the internal VLD at instantiation time to set its IP addresses (at VIM IPAM level) so they match with the ones we are providing. ``` additionalParamsForVnf: - member-vnf-index: '1' additionalParams: spgw_ip: '10.0.6.15' hss_ip: '10.0.6.20' password: 'osm4u' vnf: - member-vnf-index: '1' internal-vld: - name: s6a internal-connection-point: - id-ref: spgwmme-s6a ip-address: 10.0.6.15 - id-ref: hss-s6a ip-address: 10.0.6.20 ``` - Instantiate the VNF! Make sure you have registered a VIM with the compute and EPA requirements you need for the VNF. ``` osm ns-create --ns_name EPC1 --nsd_name vEPC_nsd --ssh_keys ~/.ssh/id_rsa.pub --config_file params.yaml --vim_account [VIM_ACCOUNT] ``` - Monitor the primitives execution status and NS status with the following commands. Remember that the instantiation flow from Release 7 onwards is as follows: (1) Charm execution enviroment preparation and VM Instantiation (in parallel), (2) Primitives execution ``` osm ns-list juju switch [NS_ID] juju status juju debug-log ``` - When the NS reaches the 'configured' state, ssh to the HSS or SPGW/MME, and check if the HSS - MME session is established. This means that Day-1 operations worked. ``` ubuntu@nextepc-spgw:~$ sudo netstat -anlp | grep 10.0.6 ... sctp 0 0 10.0.6.15:3868 10.0.6.20:55662 ESTABLISHED ``` - We can also test Day-2 reconfiguration operations by running the primitive that adds routes to the SPGW (no host routes allowed in this example). This can be done through the OSM UI or the CLI, for example: ``` osm ns-action --vnf_name 1 --action_name add-route --params '{external-prefix: "8.8.8.0/24", next-hop: "192.168.2.1"}' epc1 ``` - Finally, visit the Prometheus GUI at OSM IP (port 9091), or Grafana Dashboard at port 3000 and look for the 'osm_cpu_utilization' and 'osm_average_memory_utilization' metrics. ![](assets/vnfonbtf_samplevnf_1_prometheus.png)