From: schillinge Date: Thu, 14 Mar 2019 21:41:52 +0000 (+0100) Subject: Added a script example for a performance evaluation of charm deployment X-Git-Tag: v6.0.0~14 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F27%2F7327%2F4;p=osm%2Fvim-emu.git Added a script example for a performance evaluation of charm deployment Change-Id: Ia70a7b7774a35d3b5d0363af4ae103be09580113 Signed-off-by: schillinge --- diff --git a/.gitignore b/.gitignore index 0c81ebe..d2bff6c 100755 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,12 @@ target/ # JUnit xml utils/ci/junit-xml/*.xml + +# charm builds +examples/charms/builds/ +examples/vnfs/**/charms/ + +# performance measurement results +examples/performance_measurements/*.csv +examples/autogenerated/ +!**/.gitkeep \ No newline at end of file diff --git a/devops-stages/stage-test.sh b/devops-stages/stage-test.sh index db8342c..e464b27 100755 --- a/devops-stages/stage-test.sh +++ b/devops-stages/stage-test.sh @@ -43,7 +43,7 @@ cd /son-emu/ echo "flake8 version:" flake8 --version echo "Doing flake8 style check ..." -flake8 --exclude=.eggs,devops --ignore=E501,W605,W504 . +flake8 --exclude=.eggs,devops,examples/charms --ignore=E501,W605,W504 . echo "done." # trigger the tests echo "Running unit tests ..." diff --git a/examples/charms/__init__.py b/examples/charms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/charms/layers/__init__.py b/examples/charms/layers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/charms/layers/simple/README.md b/examples/charms/layers/simple/README.md new file mode 100644 index 0000000..0bf3bc3 --- /dev/null +++ b/examples/charms/layers/simple/README.md @@ -0,0 +1,55 @@ +Example from https://osm.etsi.org/gitweb/?p=osm/devops.git;a=tree;f=charms/layers/simple;h=bdf2957b5a5f6342bf24e3a675c21634d6064e3a;hb=HEAD + +# Overview + +This is an example charm as demonstrated in the OSM [Hackfest](https://osm.etsi.org/wikipub/index.php/OSM_workshops_and_events) series. + +This is intended to provide a well-documented example of the proxy charm written by Hackfest participants. + +# Prerequisites + +There are two ways that you can exercise this charm: install the latest stable release of OSM or use Juju directly. + +The workshop materials and tutorials cover using charms as part of OSM. You can follow that approach, but this README will focus on using Juju directly. We highly recommend that vendors and charm developers use this approach for the initial development of the charm. + +## Ubuntu 16.04 or higher + +We recommend using Ubuntu 16.04 or higher for the development and testing of charms. It is assumed that you have installed Ubuntu either on physical hardware or in a Virtual Machine. + +## Install LXD and Juju + +We will be installing the required software via snap. Snaps are containerised software packages, preferred because they are easy to create and install, will automatically update to the latest stable version, and contain bundled dependencies. + +``` +snap install lxd +snap install juju +snap install charm +``` + +# Usage + + +## Known Limitations and Issues + +This not only helps users but gives people a place to start if they want to help +you add features to your charm. + +# Configuration + +The configuration options will be listed on the charm store, however If you're +making assumptions or opinionated decisions in the charm (like setting a default +administrator password), you should detail that here so the user knows how to +change it immediately, etc. + +# Contact Information + +## Upstream Project Name + + - Upstream website + - Upstream bug tracker + - Upstream mailing list or contact information + - Feel free to add things if it's useful for users + + +[service]: http://example.com +[icon guidelines]: https://jujucharms.com/docs/stable/authors-charm-icon diff --git a/examples/charms/layers/simple/__init__.py b/examples/charms/layers/simple/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/charms/layers/simple/actions.yaml b/examples/charms/layers/simple/actions.yaml new file mode 100644 index 0000000..6cd6f8c --- /dev/null +++ b/examples/charms/layers/simple/actions.yaml @@ -0,0 +1,9 @@ +touch: + description: "Touch a file on the VNF." + params: + filename: + description: "The name of the file to touch." + type: string + default: "" + required: + - filename diff --git a/examples/charms/layers/simple/actions/__init__.py b/examples/charms/layers/simple/actions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/charms/layers/simple/actions/touch b/examples/charms/layers/simple/actions/touch new file mode 100755 index 0000000..7e30af4 --- /dev/null +++ b/examples/charms/layers/simple/actions/touch @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +## +# Copyright 2016 Canonical Ltd. +# 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 sys +sys.path.append('lib') + +from charms.reactive import main, set_flag +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_flag('actions.{}'.format(action_name())) + +try: + main() +except Exception as e: + action_fail(repr(e)) diff --git a/examples/charms/layers/simple/config.yaml b/examples/charms/layers/simple/config.yaml new file mode 100644 index 0000000..51f2ce4 --- /dev/null +++ b/examples/charms/layers/simple/config.yaml @@ -0,0 +1,14 @@ +options: + string-option: + type: string + default: "Default Value" + description: "A short description of the configuration option" + boolean-option: + type: boolean + default: False + description: "A short description of the configuration option" + int-option: + type: int + default: 9001 + description: "A short description of the configuration option" + diff --git a/examples/charms/layers/simple/icon.svg b/examples/charms/layers/simple/icon.svg new file mode 100644 index 0000000..96a5d0c --- /dev/null +++ b/examples/charms/layers/simple/icon.svg @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/examples/charms/layers/simple/layer.yaml b/examples/charms/layers/simple/layer.yaml new file mode 100644 index 0000000..3fed5e2 --- /dev/null +++ b/examples/charms/layers/simple/layer.yaml @@ -0,0 +1,4 @@ +includes: ['layer:basic', 'layer:vnfproxy'] +options: + basic: + use_venv: false diff --git a/examples/charms/layers/simple/metadata.yaml b/examples/charms/layers/simple/metadata.yaml new file mode 100644 index 0000000..fd80d1a --- /dev/null +++ b/examples/charms/layers/simple/metadata.yaml @@ -0,0 +1,5 @@ +name: simple +summary: A simple VNF proxy charm +maintainer: Adam Israel +subordinate: false +series: ['xenial'] diff --git a/examples/charms/layers/simple/metrics.yaml b/examples/charms/layers/simple/metrics.yaml new file mode 100644 index 0000000..6ebb605 --- /dev/null +++ b/examples/charms/layers/simple/metrics.yaml @@ -0,0 +1,5 @@ +metrics: + uptime: + type: gauge + description: "Uptime of the VNF" + command: awk '{print $1}' /proc/uptime diff --git a/examples/charms/layers/simple/reactive/__init__.py b/examples/charms/layers/simple/reactive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/charms/layers/simple/reactive/simple.py b/examples/charms/layers/simple/reactive/simple.py new file mode 100644 index 0000000..228be3c --- /dev/null +++ b/examples/charms/layers/simple/reactive/simple.py @@ -0,0 +1,44 @@ +from charmhelpers.core.hookenv import ( + action_get, + action_fail, + action_set, + status_set, +) +from charms.reactive import ( + clear_flag, + set_flag, + when, + when_not, +) +import charms.sshproxy + + +@when('sshproxy.configured') +@when_not('simple.installed') +def install_simple_proxy_charm(): + """Post-install actions. + + This function will run when two conditions are met: + 1. The 'sshproxy.configured' state is set + 2. The 'simple.installed' state is not set + + This ensures that the workload status is set to active only when the SSH + proxy is properly configured. + """ + set_flag('simple.installed') + status_set('active', 'Ready!') + + +@when('actions.touch') +def touch(): + err = '' + try: + filename = action_get('filename') + cmd = ['touch {}'.format(filename)] + result, err = charms.sshproxy._run(cmd) + except: + action_fail('command failed:' + err) + else: + action_set({'output': result}) + finally: + clear_flag('actions.touch') diff --git a/examples/images/sshcontainer/Dockerfile b/examples/images/sshcontainer/Dockerfile new file mode 100644 index 0000000..cd6f6b1 --- /dev/null +++ b/examples/images/sshcontainer/Dockerfile @@ -0,0 +1,33 @@ +# Copyright (c) 2019 Erik Schilling +# 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. + +# based on https://docs.docker.com/engine/examples/running_ssh_service/ +FROM ubuntu:16.04 + +RUN apt update && apt install -y net-tools iproute +RUN apt install -y openssh-server +RUN mkdir /var/run/sshd +RUN echo 'root:test' | chpasswd +RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile + +EXPOSE 22 + +CMD ["/usr/sbin/sshd", "-D"] diff --git a/examples/performance_measurements/charm_deployment.py b/examples/performance_measurements/charm_deployment.py new file mode 100644 index 0000000..d158566 --- /dev/null +++ b/examples/performance_measurements/charm_deployment.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python2 +# Copyright (c) 2019 Erik Schilling +# 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 csv +import logging +import os +import subprocess + +import shutil +import time + +from emuvim.api.osm.pre_configured_osm import PreConfiguredOSM +from emuvim.api.util.docker_utils import build_dockerfile_dir +from mininet.log import setLogLevel + +logging.basicConfig(level=logging.DEBUG) +setLogLevel('debug') # set Mininet loglevel +logging.getLogger('werkzeug').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.base').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.compute').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.keystone').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.nova').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.neutron').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.heat').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.heat.parser').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.glance').setLevel(logging.DEBUG) +logging.getLogger('api.openstack.helper').setLevel(logging.DEBUG) + +prefix = os.path.dirname(os.path.abspath(__file__)) + +build_dockerfile_dir('../images/sshcontainer/', 'sshcontainer') + +layers_folder = os.path.join(prefix, '../charms/layers') +simple_charm_folder = os.path.join(layers_folder, 'simple') +charm_target_dir = os.path.join(prefix, '../vnfs/simple_charmed_vnfd/charms/') +shutil.rmtree(charm_target_dir, ignore_errors=True) +if not subprocess.call(['/snap/bin/charm', 'build'], cwd=simple_charm_folder, env={ + 'CHARM_BUILD_DIR': charm_target_dir, + 'CHARM_LAYERS_DIR': layers_folder +}) in [0, 100]: # 100 means tests skipped + raise RuntimeError('charm build failed') + + +def get_detailed_configuration_status(osm): + status = osm.ns_get(ns_id)['_admin']['deployed']['VCA'][0]['detailed-status'] + print('current status: %s' % status) + return status + + +def wait_for_detailed_configuration_status(osm, status): + while get_detailed_configuration_status(osm) != status: + time.sleep(1) + + +with open('charmed-%d.csv' % time.time(), 'w') as csvfile: + fieldnames = ['ns_create', 'charm_deployment_start', 'waiting_for_machine', 'installing_charm_software', + 'ns_action', 'ns_delete'] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + + for n in range(1, 10 + 1): + with PreConfiguredOSM() as osm: + osm.onboard_vnfd('../vnfs/simple_charmed_vnfd') + nsd_id = osm.onboard_nsd('../services/simple_charmed_nsd') + ns_create = time.time() + ns_id = osm.ns_create('charmed-ns-%d' % n, nsd_id) + osm.ns_wait_until_all_in_status('running') + ns_created = time.time() + + wait_for_detailed_configuration_status(osm, 'waiting for machine') + waiting_for_machine_start = time.time() + + wait_for_detailed_configuration_status(osm, 'installing charm software') + installing_charm_start = time.time() + + wait_for_detailed_configuration_status(osm, 'Ready!') + ready = time.time() + + instance = osm.api.compute.find_server_by_name_or_id('dc1_charmed-ns-%d-1--1' % n).emulator_compute + osm.ns_action(ns_id, 1, 'touch') + while instance.cmd('cat /testmanual') != '': + time.sleep(0.1) + ns_action_done = time.time() + + osm.ns_delete(ns_id) + osm.ns_wait_until_all_in_status('terminated') + ns_deleted = time.time() + + writer.writerow({ + 'ns_create': ns_created - ns_create, + 'charm_deployment_start': waiting_for_machine_start - ns_created, + 'waiting_for_machine': installing_charm_start - waiting_for_machine_start, + 'installing_charm_software': ready - installing_charm_start, + 'ns_action': ns_action_done - ready, + 'ns_delete': ns_deleted - ns_action_done, + }) + csvfile.flush() diff --git a/examples/services/simple_charmed_nsd/simple_charmed_nsd.yaml b/examples/services/simple_charmed_nsd/simple_charmed_nsd.yaml new file mode 100644 index 0000000..7612892 --- /dev/null +++ b/examples/services/simple_charmed_nsd/simple_charmed_nsd.yaml @@ -0,0 +1,32 @@ +# Copyright (c) 2019 Erik Schilling +# 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. + +nsd:nsd-catalog: + nsd: + - id: charmed + name: charmed + short-name: charmed + constituent-vnfd: + - vnfd-id-ref: charmed-vnf + member-vnf-index: '1' + vld: + - id: mgmt + type: ELAN + mgmt-network: 'true' + vim-network-name: default + vnfd-connection-point-ref: + - vnfd-id-ref: charmed-vnf + member-vnf-index-ref: '1' + vnfd-connection-point-ref: eth0 diff --git a/examples/vnfs/simple_charmed_vnfd/simple_charmed_vnfd.yaml b/examples/vnfs/simple_charmed_vnfd/simple_charmed_vnfd.yaml new file mode 100644 index 0000000..14b8e39 --- /dev/null +++ b/examples/vnfs/simple_charmed_vnfd/simple_charmed_vnfd.yaml @@ -0,0 +1,61 @@ +# Copyright (c) 2019 Erik Schilling +# 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. + +vnfd:vnfd-catalog: + vnfd: + - id: charmed-vnf + short-name: charmed-vnf + name: charmed-vnf + vdu: + - id: charmed-vnf-vdu + vm-flavor: + vcpu-count: 1 + memory-mb: 256 + storage-gb: 6 + image: sshcontainer + interface: + - name: eth0 + type: EXTERNAL + position: 0 + external-connection-point-ref: eth0 + mgmt-interface: + vdu-id: charmed-vnf-vdu + connection-point: + - name: eth0 + type: VPORT + vnf-configuration: + juju: + charm: simple + initial-config-primitive: + - seq: '1' + name: config + parameter: + - name: ssh-hostname + value: + - name: ssh-username + value: root + - name: ssh-password + value: test + - seq: '2' + name: touch + parameter: + - name: filename + value: /test + config-primitive: + - name: touch + parameter: + - name: filename + data-type: STRING + default-value: '/testmanual'