Commit 0252a426 authored by sergio-tarazona's avatar sergio-tarazona
Browse files

new oai-helm procedure created

parent 537efb33
Pipeline #6135 passed with stage
in 2 minutes and 20 seconds
ssh_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrcvnKVl3o2cN7YFmajQ2RqGGRVgL0OhdkQCXpehJSL/7rxD0rURlUCipqAvqzDQ0UvfPDxWKOJSxhZRY0SCOlb4W6WNBW7hTE31AjS0YtH/JUimc99567y3F1937sy3nzt4yAlIAoI3y7ZZKJI4EP29A47TZ2WbAuQhI/TsDbJmNpXLB0XtBvzTMJuPffwJep0nWp2nYyv21NBBk5rQF6yTi2dDJ3DwcatfaIv5o0UeI07mbyEmxV2hVrIxPKv3KG65dBvbvF+sjdFMp0tJUfqDn/FJso+jqlwmmvrtk+8bmhL7vzzMyUXyfCjUV2G+u0GOPjl3Q+rnxjCsl2yEib
k8s-namespace: oai
vnf:
- member-vnf-index: gnbsim
vimAccountId: b0fe741c-37d9-4f42-bce7-b163122a9790
- member-vnf-index: ext-server
vimAccountId: 74c9e008-28ce-45c4-9634-9aec3177fb76
nsd:
nsd:
- description: NS with 1 VNF for gnbsim, 1 VNF for ext server, 1 PNF for VyOS router and 1 KNF for 5G OAI Core
df:
- id: default-df
vnf-profile:
- id: gnbsim
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: gnbsim
constituent-cpd-id: vnf-mgmt-ext
virtual-link-profile-id: mgmtnet
- constituent-cpd-id:
- constituent-base-element-id: gnbsim
constituent-cpd-id: vnf-internal-ext
ip-address: 192.168.1.5
virtual-link-profile-id: datanet
vnfd-id: gnbsim-vnf
- id: ext-server
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: ext-server
constituent-cpd-id: vnf-mgmt-ext
virtual-link-profile-id: mgmtnet
- constituent-cpd-id:
- constituent-base-element-id: ext-server
constituent-cpd-id: vnf-internal-ext
ip-address: 192.168.1.6
virtual-link-profile-id: datanet
vnfd-id: ext-dn-vnf
- id: vyos-pnf
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: vyos-pnf
constituent-cpd-id: gateway_public-ext
virtual-link-profile-id: mgmtnet
vnfd-id: vyos_pnf
- id: 5gcore_knf
virtual-link-connectivity:
- constituent-cpd-id:
- constituent-base-element-id: 5gcore_knf
constituent-cpd-id: mgmt-ext
virtual-link-profile-id: mgmtnet
vnfd-id: oai_knf
id: 5gcore-ns
name: 5gcore-ns
version: 1.0
virtual-link-desc:
- id: mgmtnet
mgmt-network: true
vim-network-name: osm-ext
- id: datanet
vim-network-name: 5g-inter-vm
vnfd-id:
- gnbsim-vnf
- oai_knf
- ext-dn-vnf
- vyos_pnf
# OAI 5G Core deployment using helm-based EE
This guide is intended to give all the considerations and descriptors needed to deploy a 5G Core, a gnbsim to emulate access (gNB and UE) and a external network to test. This guide also includes a PNF VyOS Router and its configuration to ensure the connectivity between elements in this architecture.
## Architecture
This architecture considers:
![architecture](img/architecture.png)
- gnbsim: the open source software to emulate gNB and UE, it's used to connect a gNB and register a UE to the 5G Core. In this case we're deploying it as a VNF.
- 5gcore: The open source project created by OAI (Open Air Interface), it deploys the 5G SA core including NF like NRF, AMF, SMF, UDR, UDM, UPF. In this case we're going to deploy it as a CNF.
- ext-dn: this is the external network we're implementing to test the 5G core, it implements an iperf server to test from the UE. In this case we're deploying it as a VNF.
## Preparation
### Infrastructure preparation
#### Openstack
Two networks should be defined, one for 5gcore and other for communication between access and external-network:
- 5g-core-cp: 192.168.18.0/24 (could be shorter), gateway and port-security in the VMs must be disabled
- 5g-inter-vm: 192.168.1.0/29, gateway and port-security in the VMs must be disabled
#### Kubernetes cluster
Since 5G Core is going to be deployed on a K8s cluster, take note of what you need in your K8s Cluster first:
1. K8s cluster must have multus and macvlan installed since 5G core needs PODs with two interfaces.
2. Worker nodes must have one additional interface (ens8) in the network 192.168.18.0/24 (could be shorter), for example worker01 could have 192.168.18.3, worker02 could have 192.168.18.4 and so on.
#### PNF
VyOS Router will provide connectivity. It must have 3 interfaces:
- One should be the default for 5g-core-cp network: 192.168.18.129
- One should be the default for 5g-inter-vm network: 192.168.1.1
- One for management, this depends on the infra and will be used to connect from OSM.
You could use the infra.sh script to create PDU in OSM. You also have the pdu.yaml file, make sure to put there the management IP you have for your VyOS VM. Specific configuration for this deployment will be performed from OSM later.
## Launching the network service (NS)
Make sure that VNF/KNF packages are already onboarded in OSM (osm nfpkg-list), if not, you can create them:
```
osm nfpkg-create gnbsim_vnf
osm nfpkg-create ext-dn_vnf
osm nfpkg-create oai_knf
osm nfpkg-create vyos_ns
```
Make sure that NS package is already onboarded in OSM (osm nspkg-list), if not, you can create it:
```
osm nspkg-create 5gcore_ns
```
Then you have the 5g_core.yaml file which passes additional configuration to the NS, in this case:
- ssh_keys: this ssh key is of the server we'll be using to ssh to the VNF gnbsim and ext-dn. Another option is using cloud-init to set a password.
- k8s-namespace: a specific namespace is used to deploy the 5G Core in K8s cluster.
- vimAccountId: a different VIM is used to deploy gnbsim and ext-server.
```
ssh_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDrcvnKVl3o2cN7YFmajQ2RqGGRVgL0OhdkQCXpehJSL/7rxD0rURlUCipqAvqzDQ0UvfPDxWKOJSxhZRY0SCOlb4W6WNBW7hTE31AjS0YtH/JUimc99567y3F1937sy3nzt4yAlIAoI3y7ZZKJI4EP29A47TZ2WbAuQhI/TsDbJmNpXLB0XtBvzTMJuPffwJep0nWp2nYyv21NBBk5rQF6yTi2dDJ3DwcatfaIv5o0UeI07mbyEmxV2hVrIxPKv3KG65dBvbvF+sjdFMp0tJUfqDn/FJso+jqlwmmvrtk+8bmhL7vzzMyUXyfCjUV2G+u0GOPjl3Q+rnxjCsl2yEib
k8s-namespace: oai
vnf:
- member-vnf-index: gnbsim
vimAccountId: b0fe741c-37d9-4f42-bce7-b163122a9790
- member-vnf-index: ext-server
vimAccountId: 74c9e008-28ce-45c4-9634-9aec3177fb76
```
The command to instantiate this NS is:
```
osm ns-create --ns_name 5g --nsd_name 5gcore-ns --vim_account whitecloud --config_file 5g_params.yaml
```
## Starting services from OSM
Since Helm-based EE is used to create primitives for day-1 and day-2, we're going to use the GUI to start external server and gnbsim services:
### Starting external server service
First log in OSM and find the NS created, then click in "Action" and click in "Exec Primitive"
![architecture](img/login.png)
In the opened window select "VNF Level Primitive", "ext-server", "ext_dn_up", "file" and type "ext-dn-oai", then click in Execute:
![architecture](img/ext-server.png)
### Starting gnbsim service
First log in OSM and find the NS created, then click in "Action" and click in "Exec Primitive"
![architecture](img/login.png)
In the opened window select "VNF Level Primitive", "gnbsim", "gnbsim_up", "file" and type "gnbsim", then click in Execute:
![architecture](img/gnbsim.png)
With these two primitives executed we're ready to test traffic.
## Testing traffic
1. Using management IP assigned to the external server proceed to ssh to the instance (remember to do it from the server you have copied the ssh keys)
2. Execute the iperf command:
```
docker exec -ti oai-ext-dn iperf3 -s
```
3. Using the management IP assigned to gnbsim proceed to ssh to the instance (remember to do it from the server you have copied the ssh keys)
```
docker exec -ti gnbsim iperf3 -c 192.168.70.135 -B 12.1.1.2
```
4. You should be able to see traffic going from gnbsim to external server passing through 5G Core.
\ No newline at end of file
vnfd:
description: ext-dn VNF with 2 networks attached
df:
- id: default-df
instantiation-level:
- id: default-instantiation-level
vdu-level:
- number-of-instances: 1
vdu-id: ext-dn-VM
vdu-profile:
- id: ext-dn-VM
min-number-of-instances: 1
lcm-operations-configuration:
operate-vnf-op-config:
day1-2:
- config-primitive:
- execution-environment-primitive: ext_dn_up
execution-environment-ref: ext-dn
name: ext_dn_up
parameter:
- data-type: STRING
name: file
- execution-environment-primitive: ext_dn_down
execution-environment-ref: ext-dn
name: ext_dn_down
parameter:
- data-type: STRING
name: file
- execution-environment-primitive: send_command
execution-environment-ref: ext-dn
name: send_command
parameter:
- data-type: STRING
name: file
config-access:
ssh-access:
default-user: ubuntu
required: true
execution-environment-list:
- external-connection-point-ref: vnf-mgmt-ext
helm-chart: eechart
id: ext-dn
id: ext-dn-vnf
initial-config-primitive:
- execution-environment-ref: ext-dn
name: config
parameter:
- name: ssh-hostname
value: <rw_mgmt_ip>
- name: ssh-username
value: ubuntu
seq: 1
- execution-environment-ref: ext-dn
name: run_script
parameter:
- name: file
value: install_docker.sh
seq: 2
- execution-environment-ref: ext-dn
name: copy_files
parameter:
- name: file
value: docker-compose.yaml
seq: 3
ext-cpd:
- id: vnf-mgmt-ext
int-cpd:
cpd: vdu-eth0-int
vdu-id: ext-dn-VM
- id: vnf-internal-ext
int-cpd:
cpd: vdu-eth1-int
vdu-id: ext-dn-VM
id: ext-dn-vnf
mgmt-cp: vnf-mgmt-ext
product-name: ext-dn-vnf
sw-image-desc:
- id: ubuntu20.04
image: ubuntu20.04
name: ubuntu20.04
- id: ubuntu20.04-azure
name: ubuntu20.04-azure
image: Canonical:0001-com-ubuntu-server-focal:20_04-lts:latest
vim-type: azure
- id: ubuntu20.04-gcp
name: ubuntu20.04-gcp
image: ubuntu-os-cloud:image-family:ubuntu-2004-lts
vim-type: gcp
vdu:
- id: ext-dn-VM
int-cpd:
- id: vdu-eth0-int
virtual-network-interface-requirement:
- name: vdu-eth0
position: 0
virtual-interface:
type: PARAVIRT
- id: vdu-eth1-int
port-security-enabled: false
virtual-network-interface-requirement:
- name: vdu-eth1
position: 1
virtual-interface:
type: PARAVIRT
- id: vdu-eth2-int
virtual-network-interface-requirement:
- name: vdu-eth2
position: 2
virtual-interface:
type: PARAVIRT
name: ext-dn-VM
supplemental-boot-data:
boot-data-drive: true
sw-image-desc: ubuntu20.04
alternative-sw-image-desc:
- ubuntu20.04-azure
- ubuntu20.04-gcp
virtual-compute-desc: ext-dn-VM-compute
virtual-storage-desc:
- ext-dn-VM-storage
version: 1.0
virtual-compute-desc:
- id: ext-dn-VM-compute
virtual-cpu:
num-virtual-cpu: 1
virtual-memory:
size: 4.0
virtual-storage-desc:
- id: ext-dn-VM-storage
size-of-storage: 20
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
apiVersion: v1
appVersion: "1.0"
description: OSM EE helm chart
name: eechart
version: 0.1.0
version: '3.8'
services:
oai-nrf:
container_name: "oai-nrf"
image: oai-nrf:latest
environment:
- NRF_INTERFACE_NAME_FOR_SBI=eth0
- NRF_INTERFACE_PORT_FOR_SBI=80
- NRF_INTERFACE_HTTP2_PORT_FOR_SBI=9090
- NRF_API_VERSION=v1
- INSTANCE=0
- PID_DIRECTORY=/var/run
networks:
public_net:
ipv4_address: 192.168.70.130
volumes:
- ./nrf-healthcheck.sh:/openair-nrf/bin/nrf-healthcheck.sh
healthcheck:
test: /bin/bash -c "/openair-nrf/bin/nrf-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 5
mysql:
container_name: "mysql"
image: mysql:5.7
volumes:
- ./oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ./mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
ipv4_address: 192.168.70.131
oai-amf:
container_name: "oai-amf"
image: oai-amf:latest
environment:
- TZ=Europe/paris
- INSTANCE=0
- PID_DIRECTORY=/var/run
- MCC=208
- MNC=95
- REGION_ID=128
- AMF_SET_ID=1
- SERVED_GUAMI_MCC_0=208
- SERVED_GUAMI_MNC_0=95
- SERVED_GUAMI_REGION_ID_0=128
- SERVED_GUAMI_AMF_SET_ID_0=1
- SERVED_GUAMI_MCC_1=460
- SERVED_GUAMI_MNC_1=11
- SERVED_GUAMI_REGION_ID_1=10
- SERVED_GUAMI_AMF_SET_ID_1=1
- PLMN_SUPPORT_MCC=208
- PLMN_SUPPORT_MNC=95
- PLMN_SUPPORT_TAC=0xa000
- SST_0=222
- SD_0=123
- SST_1=1
- SD_1=12
- AMF_INTERFACE_NAME_FOR_NGAP=eth0
- AMF_INTERFACE_NAME_FOR_N11=eth0
- SMF_INSTANCE_ID_0=1
- SMF_FQDN_0=oai-smf
- SMF_IPV4_ADDR_0=0.0.0.0
- SMF_HTTP_VERSION_0=v1
- SELECTED_0=true
- SMF_INSTANCE_ID_1=2
- SMF_FQDN_1=oai-smf
- SMF_IPV4_ADDR_1=0.0.0.0
- SMF_HTTP_VERSION_1=v1
- SELECTED_1=false
- MYSQL_SERVER=192.168.70.131
- MYSQL_USER=root
- MYSQL_PASS=linux
- MYSQL_DB=oai_db
- OPERATOR_KEY=63bfa50ee6523365ff14c1f45f88737d
- NRF_IPV4_ADDRESS=192.168.70.130
- NRF_PORT=80
- NF_REGISTRATION=yes
- SMF_SELECTION=yes
- USE_FQDN_DNS=yes
- NRF_API_VERSION=v1
- NRF_FQDN=oai-nrf
- AUSF_IPV4_ADDRESS=127.0.0.1
- AUSF_PORT=80
- AUSF_API_VERSION=v1
- AUSF_FQDN=oai-ausf
depends_on:
- mysql
volumes:
- ./amf-healthcheck.sh:/openair-amf/bin/amf-healthcheck.sh
healthcheck:
test: /bin/bash -c "/openair-amf/bin/amf-healthcheck.sh"
interval: 10s
timeout: 15s
retries: 5
networks:
public_net:
ipv4_address: 192.168.70.132
oai-smf:
container_name: "oai-smf"
image: oai-smf:latest
environment:
- TZ=Europe/Paris
- INSTANCE=0
- PID_DIRECTORY=/var/run
- SMF_INTERFACE_NAME_FOR_N4=eth0
- SMF_INTERFACE_NAME_FOR_SBI=eth0
- SMF_INTERFACE_PORT_FOR_SBI=80
- SMF_INTERFACE_HTTP2_PORT_FOR_SBI=9090
- SMF_API_VERSION=v1
- DEFAULT_DNS_IPV4_ADDRESS=192.168.18.129
- DEFAULT_DNS_SEC_IPV4_ADDRESS=192.168.18.129
- AMF_IPV4_ADDRESS=0.0.0.0
- AMF_PORT=80
- AMF_API_VERSION=v1
- AMF_FQDN=oai-amf
- UDM_IPV4_ADDRESS=127.0.0.1
- UDM_PORT=80
- UDM_API_VERSION=v1
- UDM_FQDN=localhost
- UPF_IPV4_ADDRESS=192.168.70.134
- UPF_FQDN_0=oai-spgwu
- NRF_IPV4_ADDRESS=192.168.70.130
- NRF_PORT=80
- NRF_API_VERSION=v1
- NRF_FQDN=oai-nrf
- REGISTER_NRF=yes
- DISCOVER_UPF=yes
- USE_FQDN_DNS=yes
depends_on:
- oai-nrf
volumes:
- ./smf-healthcheck.sh:/openair-smf/bin/smf-healthcheck.sh
healthcheck:
test: /bin/bash -c "/openair-smf/bin/smf-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
ipv4_address: 192.168.70.133
oai-spgwu:
container_name: "oai-spgwu"
image: oai-spgwu-tiny:latest
environment:
- TZ=Europe/Paris
- PID_DIRECTORY=/var/run
- SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP=eth0
- SGW_INTERFACE_NAME_FOR_SX=eth0
- PGW_INTERFACE_NAME_FOR_SGI=eth0
- NETWORK_UE_NAT_OPTION=yes
- NETWORK_UE_IP=12.1.1.0/24
- SPGWC0_IP_ADDRESS=192.168.70.133
- BYPASS_UL_PFCP_RULES=no
- MCC=208
- MNC=95
- MNC03=095
- TAC=40960
- GW_ID=1
- REALM=openairinterface.org
- ENABLE_5G_FEATURES=yes
- REGISTER_NRF=yes
- USE_FQDN_NRF=yes
- UPF_FQDN_5G=oai-spgwu
- NRF_IPV4_ADDRESS=192.168.70.130
- NRF_PORT=80
- NRF_API_VERSION=v1
- NRF_FQDN=oai-nrf
- NSSAI_SST_0=222
- NSSAI_SD_0=123
- DNN_0=default
depends_on:
- oai-nrf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
volumes:
- ./spgwu-healthcheck.sh:/openair-spgwu-tiny/bin/spgwu-healthcheck.sh
healthcheck:
test: /bin/bash -c "/openair-spgwu-tiny/bin/spgwu-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
ipv4_address: 192.168.70.134
oai-ext-dn:
image: ubuntu:bionic
privileged: true
container_name: oai-ext-dn
entrypoint: /bin/bash -c \
"apt update; apt install -y iptables iproute2 iperf3 iputils-ping;"\
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.70.129 dev eth0; sleep infinity"
#depends_on:
# - oai-spgwu
networks:
public_net:
ipv4_address: 192.168.70.135
networks:
# public_net:
# external:
# name: demo-oai-public-net
public_net:
driver: bridge
name: demo-oai-public-net
ipam:
config:
- subnet: 192.168.70.128/26
driver_opts:
com.docker.network.bridge.name: "demo-oai"
#!/bin/bash
##
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
##
echo "Updating operating system"
apt-get update
# Install ansible libraries
echo "Installing ansible"
apt-get install -y software-properties-common
apt-add-repository --yes --update ppa:ansible/ansible
apt install -y ansible
# Install library to execute command remotely by ssh
echo "Installing asynssh"
python3 -m pip install asyncssh
# Install ping system command
apt install -y iputils-ping
# Install HTTP python library
python3 -m pip install requests
# Install MySQL library
python3 -m pip install mysql-connector-python
#!/usr/bin/env bash
set -eux
sudo -s <<EOF
apt-get -y update
apt-get -y install \
ca-certificates \
curl \
gnupg \
lsb-release
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get -y update
apt-get -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin
groupadd docker
usermod -aG docker ubuntu
newgrp docker
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
sysctl net.ipv4.conf.all.forwarding=1
iptables -P FORWARD ACCEPT
ip route add 192.168.18.0/24 via 192.168.1.1 dev ens4
ip route add 12.1.1.0/24 via 192.168.1.1 dev ens4
EOF
##
# Copyright 2022 Telefonica Investigacion y Desarrollo, S.A.U.
# This file is part of OSM
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact with: nfvlabs@tid.es
##
import logging
import asyncio
import asyncssh
import time
from mysql.connector import connect, Error
logger = logging.getLogger("osm_ee.vnf")
async def ssh_exec(host: str, user: str, command: str
) -> (int, str):
"""
Execute a remote command via SSH.
"""
try:
async with asyncssh.connect(host,
username=user,
known_hosts=None) as conn:
logger.debug("Executing command '{}'".format(command))
result = await conn.run(command)
logger.debug("Result: {}".format(result))
return result.exit_status, result.stderr
except Exception as e:
logger.error("Error: {}".format(repr(e)))
return -1, str(e)
def mysql_query(host: str, user: str, password: str, retries: int, query: str
) -> (int, str):
"""
Execute a query to a MySQL database.
"""
text = ""
logger.debug("Host: '{}', user: '{}', password: '{}', query: '{}'".format(host, user, password, query))
for i in range(0, retries):
try:
with connect(
host=host,
user=user,
password=password,
) as connection:
with connection.cursor() as cursor:
cursor.execute(query)
for (db) in cursor:
logger.debug(db)
text = text + str(db[0]) + ", "
return 0, text[0:len(text)-2]
except Error as e:
text = str(e)
logger.debug("Error: {}".format(e))
time.sleep(3)
continue
return -1, text
---
- hosts: all
become: yes
tasks:
- name: Wait 120 seconds, but only start checking after 10 seconds
wait_for_connection:
delay: 20
timeout: 120
- name: Install packages
apt:
name:
- "{{ app }}"
state: latest
cache_valid_time: 3600
#!/usr/bin/env bash
date "+%H:%M:%S Starting $0..."
IP=$1
USERNAME=$2
SCRIPT=$3
PARAMS=$4
DIR=$(dirname $0)
date "+%H:%M:%S Waiting for $IP to be ready..."
i=5
while ! ssh -T -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o LogLevel=ERROR "$USERNAME"@"$IP" 'exit' ; do
date "+%H:%M:%S Error accessing $IP, retrying..."
sleep 5
i=$(( $i - 1 ))
[ $i -ge 0 ] || exit 1
done
date "+%H:%M:%S SSH server is up, sending script '${DIR}/${SCRIPT}'..."
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${DIR}/${SCRIPT} "$USERNAME"@"$IP":
if [ $? -ne 0 ]; then
date "+%H:%M:%S scp error"
exit 1
fi
date "+%H:%M:%S OK. Setting file permissions"
ssh -T -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o LogLevel=ERROR "$USERNAME"@"$IP" "chmod a+x $SCRIPT"
if [ $? -ne 0 ]; then
date "+%H:%M:%S ssh error"
exit 1
fi
COMMAND="./$SCRIPT"
[ ${#PARAMS} -ge 0 ] || COMMAND="${COMMAND=} $PARAMS"
date "+%H:%M:%S Running '$COMMAND' on $IP..."
ssh -T -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o LogLevel=ERROR "$USERNAME"@"$IP" "$COMMAND"
if [ $? -ne 0 ]; then
date "+%H:%M:%S ssh error"
exit 1
fi
date "+%H:%M:%S End"
exit 0
##
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import asyncio
import asyncssh
import requests
import logging
import os
from osm_ee.exceptions import VnfException
import osm_ee.util.util_ee as util_ee
import osm_ee.util.util_ansible as util_ansible
import osm_ee.vnf.mylib as mylib
class VnfEE:
PLAYBOOK_PATH = "/app/EE/osm_ee/vnf"
SSH_SCRIPT = "/app/EE/osm_ee/vnf/run_ssh.sh"
def __init__(self, config_params):
self.logger = logging.getLogger('osm_ee.vnf')
self.config_params = config_params
# config method saves SSH access parameters (host and username) for future use by other methods.
# It is mandatory in any case.
async def config(self, id, params):
self.logger.debug("Execute action config, params: {}".format(params))
# Config action is special, params are merged with previous config calls
self.config_params.update(params)
required_params = ["ssh-hostname"]
self._check_required_params(self.config_params, required_params)
yield "OK", "Configured"
# This method implements the "run_script" primitive. Uncomment and modify it if a primitive requires executing a user
# script in the VDU. It needs "file" parameter (script's file to run, must be in "source" directory)
# and optionally "parameters" (command-line arguments for script).
async def run_script(self, id, params):
self.logger.debug("Execute action run_script, params: '{}'".format(params))
self._check_required_params(params, ["file"])
command = "bash " + self.SSH_SCRIPT + " " + self.config_params["ssh-hostname"] + " " + self.config_params["ssh-username"] + " " + params["file"]
if "parameters" in params:
command += " \"" + params.get("parameters", "") + "\""
self.logger.debug("Command: '{}'".format(command))
return_code, stdout, stderr = await util_ee.local_async_exec(command)
if return_code != 0:
yield "ERROR", "return code {}: {}".format(return_code, stderr.decode())
else:
yield "OK", stdout.decode()
# This method implements the "copy_files" primitive. Uncomment and modify it if a primitive requires
# to copy files to a VNF. It needs file name parameter (file must be in "source" directory).
async def copy_files(self, id, params):
self.logger.debug("Execute action copy_files, params: '{}'".format(params))
self._check_required_params(params, ["file"])
command= "scp" + " -o StrictHostKeyChecking=no /app/EE/osm_ee/vnf/" + params["file"] + " " + self.config_params["ssh-username"] + "@" + self.config_params["ssh-hostname"] + ":" + params["file"]
self.logger.debug("Command: '{}'".format(command))
return_code, stdout, stderr = await util_ee.local_async_exec(command)
if return_code != 0:
yield "ERROR", "return code {}: {}".format(return_code, stderr.decode())
else:
yield "OK", stdout.decode()
# This method implements the "ansible_playbook" primitive. Uncomment and modify it if a primitive requires
# executing an Ansible Playbook. It needs "playbook-name" parameter (playbook file, must be in "source" directory).
# async def ansible_playbook(self, id, params):
# self.logger.debug("Execute action ansible_playbook, params: '{}'".format(params))
# try:
# self._check_required_params(params, ["playbook-name"])
# params["ansible_user"] = self.config_params["ssh-username"]
# inventory = self.config_params["ssh-hostname"] + ","
# playbook = self.PLAYBOOK_PATH + "/" + params["playbook-name"]
# os.environ["ANSIBLE_HOST_KEY_CHECKING"] = "False"
# return_code, stdout, stderr = await util_ansible.execute_playbook(playbook, inventory, params)
# status = "OK" if return_code == 0 else "ERROR"
# yield status, stdout + stderr
# except Exception as e:
# self.logger.debug("Error executing ansible playbook: {}".format(repr(e)))
# yield "ERROR", str(e)
# This method implements the "ping" primitive. Uncomment and modify it if a primitive requires
# executing a local command (such as ping) on EE. It uses "ssh-hostname" as ping destination.
# async def ping(self, id, params):
# self.logger.debug("Execute action ping, params: '{}'".format(params))
# command = "ping -c 3 " + self.config_params["ssh-hostname"]
# return_code, stdout, stderr = await util_ee.local_async_exec(command)
# if return_code != 0:
# yield "ERROR", "return code {}: {}".format(return_code, stderr.decode())
# else:
# yield "OK", stdout.decode()
# This method implements the "http_check" primitive. Uncomment and modify it if a primitive requires
# executing embedded python code on VfnEE class. It requests "http://<ssh-hostname>/" using a HTTP library.
# async def http_check(self, id, params):
# self.logger.debug("Execute action http_check, params: '{}'".format(params))
# try:
# session = requests.Session()
# url = 'http://' + self.config_params["ssh-hostname"]
# self.logger.debug("HTTP GET {}...".format(url))
# req = session.get(url)
# self.logger.debug("{}".format(req.text))
# if req.status_code == 200:
# yield "OK", req.text
# else:
# yield "ERROR", req.text
# except Exception as e:
# self.logger.error("HTTP error: {}".format(repr(e)))
# yield "ERROR", str(e)
# This method implements the "touch" primitive. Uncomment and modify it if you need a primitive that
# imports a user-defined python library. It only needs "file" parameter for creating it in VDU via SSH.
# async def touch(self, id, params):
# self.logger.debug("Execute action touch, params: '{}'".format(params))
#
# self._check_required_params(params, ["file"])
# command = "touch" + " " + params["file"]
# return_code, description = await mylib.ssh_exec(self.config_params["ssh-hostname"], self.config_params["ssh-username"], command)
# if return_code != 0:
# yield "ERROR", description
# else:
# yield "OK", description
# This method implements the "gnbsim_up" primitive. Uncomment and modify it if you need a primitive that
# starts up the gnbsim to test 5G OAI Core. It only needs "file" parameter, in this case use gnbsim.
async def ext_dn_up(self, id, params):
self.logger.debug("Execute action gnbsim_up, params: '{}'".format(params))
self._check_required_params(params, ["file"])
command = "docker-compose up -d oai-ext-dn"
return_code, description = await mylib.ssh_exec(self.config_params["ssh-hostname"], self.config_params["ssh-username"], command)
if return_code != 0:
yield "ERROR", description
else:
yield "OK", description
# This method implements the "gnbsim_down" primitive. Uncomment and modify it if you need a primitive that
# stops the gnbsim for testing 5G OAI Core. It only needs "file" parameter, in this case use gnbsim.
async def ext_dn_down(self, id, params):
self.logger.debug("Execute action gnbsim_down, params: '{}'".format(params))
self._check_required_params(params, ["file"])
command = "docker-compose down"
return_code, description = await mylib.ssh_exec(self.config_params["ssh-hostname"], self.config_params["ssh-username"], command)
if return_code != 0:
yield "ERROR", description
else:
yield "OK", description
# This method implements the "send_command" primitive. Uncomment and modify it if you need a primitive that
# sends any command to the VM through SSH. It only needs "file" parameter which contains the command to send.
async def send_command(self, id, params):
self.logger.debug("Execute action send_command, params: '{}'".format(params))
self._check_required_params(params, ["file"])
command = params["file"]
return_code, description = await mylib.ssh_exec(self.config_params["ssh-hostname"], self.config_params["ssh-username"], command)
if return_code != 0:
yield "ERROR", description
else:
yield "OK", description
# This method implements the "check_database" primitive. Uncomment and modify it if a primitive requires
# accessing to a service provided by a helm subchart. Access parameters are read from environment variables
# async def check_database(self, id, params):
# self.logger.debug("Execute action check_database, params: '{}'".format(params))
# host = os.getenv('mysql_host')
# user = os.getenv('mysql_user')
# password = os.getenv('mysql_password')
# retries = 3
# query = "SHOW DATABASES"
# return_code, description = mylib.mysql_query(host, user, password, retries, query)
# if return_code != 0:
# yield "ERROR", description
# else:
# yield "OK", description
# Static method that verifies whether a parameter exists in the map
@staticmethod
def _check_required_params(params, required_params):
for required_param in required_params:
if required_param not in params:
raise VnfException("Missing required param: {}".format(required_param))
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "eechart.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "eechart.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "eechart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "eechart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "eechart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "eechart.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "eechart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "eechart.labels" -}}
app.kubernetes.io/name: {{ include "eechart.name" . }}
helm.sh/chart: {{ include "eechart.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "eechart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "eechart.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "eechart.fullname" . }}
data:
{{ (.Files.Glob "source/*").AsConfig | indent 2 }}
apiVersion: v1
kind: ConfigMap
metadata:
name: "vnf-snmp-generator-{{ .Values.global.osm.vnf_id | lower }}"
data:
generator.yml: |-
{{ .Files.Get "snmp/generator.yml" | nindent 4}}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment