From 50227af7528fd544826a238ddab7d40746f6873d Mon Sep 17 00:00:00 2001 From: Adam Israel Date: Tue, 4 Oct 2016 12:07:16 -0700 Subject: [PATCH] Initial import of charm layer to deploy and configure OpenMano Signed-off-by: Adam Israel --- charms/.gitignore | 2 + charms/README.md | 93 ++++++ charms/layers/openmano/README.md | 3 + charms/layers/openmano/config.yaml | 9 + charms/layers/openmano/icon.svg | 281 ++++++++++++++++++ charms/layers/openmano/layer.yaml | 26 ++ charms/layers/openmano/metadata.yaml | 21 ++ .../openmano/reactive/layer_openmano.py | 234 +++++++++++++++ .../openmano/scripts/create-datacenter.sh | 23 ++ .../layers/openmano/scripts/create-tenant.sh | 7 + .../layers/openmano/scripts/init_mano_db.sh | 142 +++++++++ .../layers/openmano/templates/openmanod.cfg | 45 +++ charms/layers/openmano/tests/00-setup | 5 + charms/layers/openmano/tests/10-deploy | 31 ++ 14 files changed, 922 insertions(+) create mode 100644 charms/.gitignore create mode 100644 charms/README.md create mode 100644 charms/layers/openmano/README.md create mode 100644 charms/layers/openmano/config.yaml create mode 100644 charms/layers/openmano/icon.svg create mode 100644 charms/layers/openmano/layer.yaml create mode 100644 charms/layers/openmano/metadata.yaml create mode 100644 charms/layers/openmano/reactive/layer_openmano.py create mode 100755 charms/layers/openmano/scripts/create-datacenter.sh create mode 100755 charms/layers/openmano/scripts/create-tenant.sh create mode 100755 charms/layers/openmano/scripts/init_mano_db.sh create mode 100644 charms/layers/openmano/templates/openmanod.cfg create mode 100755 charms/layers/openmano/tests/00-setup create mode 100755 charms/layers/openmano/tests/10-deploy diff --git a/charms/.gitignore b/charms/.gitignore new file mode 100644 index 00000000..9f751ef9 --- /dev/null +++ b/charms/.gitignore @@ -0,0 +1,2 @@ +deps/ +builds/ diff --git a/charms/README.md b/charms/README.md new file mode 100644 index 00000000..69df3a93 --- /dev/null +++ b/charms/README.md @@ -0,0 +1,93 @@ +# Juju Charm(s) for deploying OpenMano + +## Overview +These are the charm layers used to build Juju charms for deploying OpenVIM components. These charms are also published to the [Juju Charm Store](https://jujucharms.com/) and can be deployed directly from there using the [etsi-osm](https://jujucharms.com/u/nfv/osm-r1), or they can be build from these layers and deployed locally. + +## Building the OpenVIM Charms + +To build these charms, you will need [charm-tools][]. You should also read +over the developer [Getting Started][] page for an overview of charms and +building them. Then, in any of the charm layer directories, use `charm build`. +For example: + + (setup environment to build from layers) + mkdir src + cd src + git clone https://github.com/nfvlabs/openvim.git + export JUJU_REPOSITORY=$HOME/src/openvim/charms + export INTERFACE_PATH=$JUJU_REPOSITORY/interfaces + export LAYER_PATH=$JUJU_REPOSITORY/layers + + cd $LAYER_PATH/openvim + charm build + + cd $LAYER_PATH/openvim-compute + charm build + +This will build the OpenVIM controller and OpenVIM compute charms, pulling in + the appropriate base and interface layers from [interfaces.juju.solutions][], and place the resulting charms into $JUJU_REPOSITORY/builds. + +You can also use the local version of a bundle: + + juju deploy openvim/charms/bundles/openmano.yaml + +To publish: + + # You will need an account on Launchpad, and have it added to the ~nfv + # namespace. Please contact foo@bar for these permissions. + $ charm login + + $ cd $JUJU_REPOSITORY/builds/openvim + + # `charm push` will upload the charm into the store and report the revision + # of the latest push. + $ charm push . cs:~nfv/openvim + blah blah cs:~nfv/openvim-4 + + # Release the charm so that it is publicly consumable + $ charm release cs:~nfv/openvim-4 + + $ cd $JUJU_REPOSITORY/builds/openvim-compute + + # `charm push` will upload the charm into the store and report the revision + # of the latest push. + $ charm push . cs:~nfv/openvim-compute + blah blah cs:~nfv/openvim-compute-4 + + # Release the charm so that it is publicly consumable + $ charm release cs:~nfv/openvim-compute-4 + + # Finally, update and publish the bundle to point to the latest revision(s): + + cd $JUJU_REPOSITORY/bundles/openmano + + # Edit the `README.md` to reflect any notable changes. + + # Edit the `bundle.yaml` with the new revision to be deployed, i.e., change cs:~nfv/openvim-3 to cs:~nfv/openvim-4 + + $ charm push . cs:~nfv/bundle/osm-r1 + blah blah cs:~nfv/bundle/osm-r1-4 + + $ charm release cs:~nfv/bundle/osm-r1-4 + +To deploy the published charms from the charm store: + + # The recommended method + $ charm deploy cs:~nfv/bundles/openmano + + - or - + + # The manual method + $ juju deploy cs:~nfv/openvim + $ juju deploy cs:~nfv/openvim-compute + $ juju deploy cs:~nfv/openmano + $ juju deploy cs:mariadb + + $ juju add-relation mariadb openvim + $ juju add-relation mariadb openmano + $ juju add-relation openvim openvim-compute + $ juju add-relation openvim openmano + +[charm-tools]: https://jujucharms.com/docs/stable/tools-charm-tools +[Getting Started]: https://jujucharms.com/docs/devel/developer-getting-started +[interfaces.juju.solutions]: http://interfaces.juju.solutions/ diff --git a/charms/layers/openmano/README.md b/charms/layers/openmano/README.md new file mode 100644 index 00000000..99b1fe55 --- /dev/null +++ b/charms/layers/openmano/README.md @@ -0,0 +1,3 @@ +# Overview + +Deploys OpenMANO. diff --git a/charms/layers/openmano/config.yaml b/charms/layers/openmano/config.yaml new file mode 100644 index 00000000..6c8e2508 --- /dev/null +++ b/charms/layers/openmano/config.yaml @@ -0,0 +1,9 @@ +options: + repository: + type: string + default: "https://osm.etsi.org/gerrit/osm/RO.git" + description: "The Git repository to install OpenMano from." + branch: + type: string + default: "master" + description: "The Git branch to install." diff --git a/charms/layers/openmano/icon.svg b/charms/layers/openmano/icon.svg new file mode 100644 index 00000000..3503615a --- /dev/null +++ b/charms/layers/openmano/icon.svg @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/charms/layers/openmano/layer.yaml b/charms/layers/openmano/layer.yaml new file mode 100644 index 00000000..469e7f3e --- /dev/null +++ b/charms/layers/openmano/layer.yaml @@ -0,0 +1,26 @@ +includes: + - 'layer:basic' + - 'interface:mysql' + - 'interface:openvim' + - 'interface:http' + +options: + basic: + packages: + - python-yaml + - python-bottle + - python-mysqldb + - python-jsonschema + - python-paramiko + - python-argcomplete + - python-requests + - python3-git + + # These are for openstack as a VIM + # - python-novaclient + # - python-keystoneclient + # - python-glanceclient + # - python-neutronclient + + # mysql client needed to install database + - mariadb-client diff --git a/charms/layers/openmano/metadata.yaml b/charms/layers/openmano/metadata.yaml new file mode 100644 index 00000000..f8b5c99d --- /dev/null +++ b/charms/layers/openmano/metadata.yaml @@ -0,0 +1,21 @@ +name: openmano +summary: OpenMANO +maintainers: + - Adam Israel +description: | + Installs and configures OpenMANO +tags: + - nfv + - telco + - osm +series: + - xenial +subordinate: false +requires: + db: + interface: mysql + openvim-controller: + interface: openvim +provides: + openmano: + interface: http diff --git a/charms/layers/openmano/reactive/layer_openmano.py b/charms/layers/openmano/reactive/layer_openmano.py new file mode 100644 index 00000000..96bd4206 --- /dev/null +++ b/charms/layers/openmano/reactive/layer_openmano.py @@ -0,0 +1,234 @@ +from git import Repo as gitrepo +from shutil import rmtree + +import os +import subprocess + +from charmhelpers.core import host +from charmhelpers.core import hookenv +from charmhelpers.core import templating +from charmhelpers.core.unitdata import kv +from charmhelpers.core.hookenv import ( + config, + log, + open_port, + status_set, +) + +from charmhelpers.core.host import ( + chownr, +) + +from charms.reactive import ( + when, + when_not, + set_state, + is_state, +) + +kvdb = kv() + +INSTALL_PATH = '/opt/openmano' +USER = 'openmanod' + + +@when('openmano.installed') +@when('openmano.available') +def openmano_available(openmano): + # TODO make this configurable via charm config + openmano.configure(port=9090) + + +@when('openmano.installed') +@when('db.available', 'db.installed') +@when('openvim-controller.available') +@when('openmano.running') +def openvim_available(openvim, db): + for service in openvim.services(): + for endpoint in service['hosts']: + host = endpoint['hostname'] + port = endpoint['port'] + user = endpoint['user'] + + openvim_uri = '{}:{}'.format(host, port) + if kvdb.get('openvim_uri') == openvim_uri: + return + + import pdb; pdb.set_trace() + # TODO: encapsulate the logic in create-datacenter.sh into python + try: + cmd = './scripts/create-datacenter.sh {} {} {} {}'.format( + host, port, user, kvdb.get('openmano-tenant')) + out, err = _run(cmd) + except subprocess.CalledProcessError as e: + # Ignore the error if the datacenter already exists. + if e.returncode != 153: + raise + + kvdb.set('openvim_uri', openvim_uri) + if not is_state('db.available'): + status_set('waiting', 'Waiting for database') + break + break + + +@when('openmano.installed') +@when('db.available', 'db.installed') +@when('openvim-controller.available') +@when_not('openmano.running') +def start(*args): + # TODO: if the service fails to start, we should raise an error to the op + # Right now, it sets the state as running and the charm dies. Because + # service-openmano returns 0 when it fails. + cmd = "/home/{}/bin/service-openmano start".format(USER) + out, err = _run(cmd) + + if not kvdb.get('openmano-tenant'): + out, err = _run('./scripts/create-tenant.sh') + kvdb.set('openmano-tenant', out.strip()) + + status_set( + 'active', + 'Up on {host}:{port}'.format( + host=hookenv.unit_public_ip(), + port='9090')) + + set_state('openmano.running') + + +@when('openmano.installed') +@when('db.available') +@when_not('db.installed') +def setup_db(db): + """Setup the database + + """ + db_uri = 'mysql://{}:{}@{}:{}/{}'.format( + db.user(), + db.password(), + db.host(), + db.port(), + db.database(), + ) + + if kvdb.get('db_uri') == db_uri: + # We're already configured + return + + status_set('maintenance', 'Initializing database') + + try: + # HACK: use a packed version of init_mano_db until bug https://osm.etsi.org/bugzilla/show_bug.cgi?id=56 is fixed + # cmd = "{}/database_utils/init_mano_db.sh --createdb ".format(kvdb.get('repo')) + cmd = "./scripts//init_mano_db.sh --createdb " + cmd += "-u {} -p{} -h {} -d {} -P {}".format( + db.user(), + db.password(), + db.host(), + db.database(), + db.port(), + ) + output, err = _run(cmd) + except subprocess.CalledProcessError: + # Eat this. init_mano_db.sh will return error code 1 on success + pass + + context = { + 'user': db.user(), + 'password': db.password(), + 'host': db.host(), + 'database': db.database(), + 'port': db.port(), + } + templating.render( + 'openmanod.cfg', + os.path.join(kvdb.get('repo'), 'openmanod.cfg'), + context, + owner=USER, + group=USER, + ) + kvdb.set('db_uri', db_uri) + + status_set('active', 'Database installed.') + set_state('db.installed') + +@when_not('openvim-controller.available') +def need_openvim(): + status_set('waiting', 'Waiting for OpenVIM') + + +@when_not('db.available') +def need_db(): + status_set('waiting', 'Waiting for database') + + +@when_not('db.available') +@when_not('openvim-controller.available') +def need_everything(): + status_set('waiting', 'Waiting for database and OpenVIM') + + +@when_not('openmano.installed') +def install_layer_openmano(): + status_set('maintenance', 'Installing') + + cfg = config() + + # TODO change user home + # XXX security issue! + host.adduser(USER, password=USER) + + if os.path.isdir(INSTALL_PATH): + rmtree(INSTALL_PATH) + + gitrepo.clone_from( + cfg['repository'], + INSTALL_PATH, + branch=cfg['branch'], + ) + + chownr( + INSTALL_PATH, + owner=USER, + group=USER, + follow_links=False, + chowntopdir=True + ) + + os.mkdir(os.path.join(INSTALL_PATH, 'logs')) + chownr(INSTALL_PATH, USER, USER) + kvdb.set('repo', INSTALL_PATH) + + os.mkdir('/home/{}/bin'.format(USER)) + + os.symlink( + "{}/openmano".format(INSTALL_PATH), + "/home/{}/bin/openmano".format(USER)) + os.symlink( + "{}/scripts/openmano-report.sh".format(INSTALL_PATH), + "/home/{}/bin/openmano-report.sh".format(USER)) + os.symlink( + "{}/scripts/service-openmano.sh".format(INSTALL_PATH), + "/home/{}/bin/service-openmano".format(USER)) + + open_port(9090) + set_state('openmano.installed') + + +def _run(cmd, env=None): + if isinstance(cmd, str): + cmd = cmd.split() if ' ' in cmd else [cmd] + + log(cmd) + p = subprocess.Popen(cmd, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + retcode = p.poll() + if retcode > 0: + raise subprocess.CalledProcessError( + returncode=retcode, + cmd=cmd, + output=stderr.decode("utf-8").strip()) + return (stdout.decode('utf-8'), stderr.decode('utf-8')) diff --git a/charms/layers/openmano/scripts/create-datacenter.sh b/charms/layers/openmano/scripts/create-datacenter.sh new file mode 100755 index 00000000..4b192e49 --- /dev/null +++ b/charms/layers/openmano/scripts/create-datacenter.sh @@ -0,0 +1,23 @@ +#!/bin/sh +HOME=/home/openmanod +OPENMANO=$HOME/bin/openmano +export OPENMANO_TENANT=$4 + +OPENMANO_DATACENTER=`$OPENMANO datacenter-list myov` +if [ $? -ne 0 ]; then + # Make sure the datacenter is deleted + $OPENMANO datacenter-delete myov + OPENMANO_DATACENTER=`$OPENMANO datacenter-create myov http://$1:$2/openvim` +fi +export OPENMANO_DATACENTER=`echo $OPENMANO_DATACENTER |gawk '{print $1}'` + +#export OPENMANO_DATACENTER=`$OPENMANO datacenter-create myov http://$1:$2/openvim |gawk '{print $1}'` +# FIXME: don't add this to .bashrc if it already exists. +if ! grep -q "^export OPENMANO_DATACENTER" $HOME/.bashrc +then + echo "export OPENMANO_DATACENTER=$OPENMANO_DATACENTER " >> $HOME/.bashrc +fi + +# TODO: Test idempotency. We may need to check and remove existing data +$OPENMANO datacenter-attach myov --vim-tenant-id $3 +$OPENMANO datacenter-netmap-import -f --datacenter $OPENMANO_DATACENTER diff --git a/charms/layers/openmano/scripts/create-tenant.sh b/charms/layers/openmano/scripts/create-tenant.sh new file mode 100755 index 00000000..1cb644c7 --- /dev/null +++ b/charms/layers/openmano/scripts/create-tenant.sh @@ -0,0 +1,7 @@ +#!/bin/sh +OPENMANO=/home/openmanod/bin/openmano +OPENMANO_TENANT=`$OPENMANO tenant-create mytenant --description=mytenant` +if [ $? -ne 0 ]; then + OPENMANO_TENANT=`$OPENMANO tenant-list mytenant` +fi +echo $OPENMANO_TENANT |gawk '{print $1}' diff --git a/charms/layers/openmano/scripts/init_mano_db.sh b/charms/layers/openmano/scripts/init_mano_db.sh new file mode 100755 index 00000000..9d8b1581 --- /dev/null +++ b/charms/layers/openmano/scripts/init_mano_db.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +## +# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. +# This file is part of openmano +# 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 +## + +DBUSER="mano" +DBPASS="" +DBHOST="localhost" +DBPORT="3306" +DBNAME="mano_db" +CREATEDB="" + +# Detect paths +MYSQL=$(which mysql) +AWK=$(which awk) +GREP=$(which grep) +#DIRNAME=`dirname $0` +DIRNAME=/opt/openmano/database_utils + +function usage(){ + echo -e "Usage: $0 OPTIONS" + echo -e " Inits openmano database; deletes previous one and loads from ${DBNAME}_structure.sql" + echo -e " OPTIONS" + echo -e " -u USER database user. '$DBUSER' by default. Prompts if DB access fails" + echo -e " -p PASS database password. 'No password' by default. Prompts if DB access fails" + echo -e " -P PORT database port. '$DBPORT' by default" + echo -e " -h HOST database host. '$DBHOST' by default" + echo -e " -d NAME database name. '$DBNAME' by default. Prompts if DB access fails" + echo -e " --help shows this help" + echo -e " --createdb forces the deletion and creation of the database" +} + +while getopts ":u:p:P:d:h:-:" o; do + case "${o}" in + u) + DBUSER="$OPTARG" + ;; + p) + DBPASS="$OPTARG" + ;; + P) + DBPORT="$OPTARG" + ;; + d) + DBNAME="$OPTARG" + ;; + h) + DBHOST="$OPTARG" + ;; + -) + if [ "${OPTARG}" == "help" ]; then + usage && exit 0 + elif [ "${OPTARG}" == "createdb" ]; then + CREATEDB="yes" + else + echo "Invalid option: --$OPTARG" >&2 && usage >&2 + exit 1 + fi + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 && usage >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 && usage >&2 + exit 1 + ;; + *) + usage >&2 + exit -1 + ;; + esac +done +shift $((OPTIND-1)) + +#check and ask for database user password +DBUSER_="-u$DBUSER" +DBPASS_="" +[ -n "$DBPASS" ] && DBPASS_="-p$DBPASS" +DBHOST_="-h$DBHOST" +DBPORT_="-P$DBPORT" + +TEMPFILE="$(mktemp -q --tmpdir "initmanodb.XXXXXX")" +trap 'rm -f "$TEMPFILE"' EXIT SIGINT SIGTERM +chmod 0600 "$TEMPFILE" +cat >"$TEMPFILE" </dev/null 2>&1 +do + [ -n "$logintry" ] && echo -e "\nInvalid database credentials!!!. Try again (Ctrl+c to abort)" + [ -z "$logintry" ] && echo -e "\nProvide database credentials" +# read -e -p "mysql database name($DBNAME): " KK +# [ -n "$KK" ] && DBNAME="$KK" + read -e -p "mysql user($DBUSER): " KK + [ -n "$KK" ] && DBUSER="$KK" + read -e -s -p "mysql password: " DBPASS + cat >"$TEMPFILE" <, "to":} entry +#e.g. from 9000 to 9005: [{"from":9000, "to":9005}], or also [9000,9001,9002,9003,9004,9005] +#e.g. from 9000 to 9100 apart from 9050,9053: [{"from":9000, "to":9049},9051,9052,{"from":9054, "to":9099}] +http_console_ports: [{"from":9096, "to":9110}] + +#Database parameters +db_host: {{host}} # by default localhost +db_user: {{user}} # DB user +db_passwd: {{password}} # DB password +db_name: {{database}} # Name of the MANO DB + +#other MANO parameters +# Folder where the VNF descriptors will be stored +# The folder will be created in the execution folder if it does not exist +vnf_repository: "./vnfrepo" # Use an absolute path to avoid misunderstandings diff --git a/charms/layers/openmano/tests/00-setup b/charms/layers/openmano/tests/00-setup new file mode 100755 index 00000000..f0616a56 --- /dev/null +++ b/charms/layers/openmano/tests/00-setup @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo add-apt-repository ppa:juju/stable -y +sudo apt-get update +sudo apt-get install amulet python-requests -y diff --git a/charms/layers/openmano/tests/10-deploy b/charms/layers/openmano/tests/10-deploy new file mode 100755 index 00000000..7dce664c --- /dev/null +++ b/charms/layers/openmano/tests/10-deploy @@ -0,0 +1,31 @@ +#!/usr/bin/python3 + +import amulet +import requests +import unittest + + +class TestCharm(unittest.TestCase): + def setUp(self): + self.d = amulet.Deployment() + + self.d.add('layer-openmano') + self.d.expose('layer-openmano') + + self.d.setup(timeout=900) + self.d.sentry.wait() + + self.unit = self.d.sentry['layer-openmano'][0] + + def test_service(self): + # test we can access over http + page = requests.get('http://{}'.format(self.unit.info['public-address'])) + self.assertEqual(page.status_code, 200) + # Now you can use self.d.sentry[SERVICE][UNIT] to address each of the units and perform + # more in-depth steps. Each self.d.sentry[SERVICE][UNIT] has the following methods: + # - .info - An array of the information of that unit from Juju + # - .file(PATH) - Get the details of a file on that unit + # - .file_contents(PATH) - Get plain text output of PATH file from that unit + # - .directory(PATH) - Get details of directory + # - .directory_contents(PATH) - List files and folders in PATH on that unit + # - .relation(relation, service:rel) - Get relation data from return service -- 2.25.1