From a55c2425f5c316656a6e6cee11c0ec76066f3819 Mon Sep 17 00:00:00 2001 From: Michael Skalka Date: Fri, 27 Jul 2018 11:51:40 -0400 Subject: [PATCH] initial commit --- src/README.ex | 0 src/config.yaml | 43 ++++ src/icon.svg | 279 ++++++++++++++++++++++ src/layer.yaml | 3 + src/lib/charm/openstack/neutron_arista.py | 77 ++++++ src/metadata.yaml | 19 ++ src/reactive/neutron_arista_handlers.py | 56 +++++ src/templates/ml2_conf_arista.ini | 11 + src/tests/00-setup | 5 + src/tests/10-deploy | 35 +++ 10 files changed, 528 insertions(+) create mode 100644 src/README.ex create mode 100644 src/config.yaml create mode 100644 src/icon.svg create mode 100644 src/layer.yaml create mode 100644 src/lib/charm/openstack/neutron_arista.py create mode 100644 src/metadata.yaml create mode 100644 src/reactive/neutron_arista_handlers.py create mode 100644 src/templates/ml2_conf_arista.ini create mode 100755 src/tests/00-setup create mode 100755 src/tests/10-deploy diff --git a/src/README.ex b/src/README.ex new file mode 100644 index 0000000..e69de29 diff --git a/src/config.yaml b/src/config.yaml new file mode 100644 index 0000000..09938d2 --- /dev/null +++ b/src/config.yaml @@ -0,0 +1,43 @@ +options: + arista-version: + default: '2017.2.2' + type: string + description: Arista Driver version + pip-proxy: + type: string + default: None + description: Proxy address to install python modules behind a proxy. + eapi-host: + default: + type: string + description: | + Set a comma separated list of IP addresses for each CVX instance. + If CVX has been deployed in a highly available (HA) cluster, + specify each instance IP separated by a comma. + eapi-username: + default: + type: string + description: EOS command API username. This is required field. + eapi-password: + default: + type: string + description: EOS command API password. This is required field. + api-type: + default: EAPI + type: string + description: | + Tells the plugin to use a sepcific API interfaces to communicate + with CVX. Valid options are: + . + EAPI - Use EOS extensible API. + JSON - Use EOS JSON/REST API. + . + region-name: + default: RegionOne + type: string + description: | + Name of the OpenStack region. + service-plugins: + default: router,firewall,metering,neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 + type: string + description: Override of Neutron-API service plugins setting. diff --git a/src/icon.svg b/src/icon.svg new file mode 100644 index 0000000..96a5d0c --- /dev/null +++ b/src/icon.svg @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/layer.yaml b/src/layer.yaml new file mode 100644 index 0000000..b36fbb8 --- /dev/null +++ b/src/layer.yaml @@ -0,0 +1,3 @@ +includes: +- 'layer:openstack' +- 'interface:neutron-plugin-api-subordinate' diff --git a/src/lib/charm/openstack/neutron_arista.py b/src/lib/charm/openstack/neutron_arista.py new file mode 100644 index 0000000..055d9e4 --- /dev/null +++ b/src/lib/charm/openstack/neutron_arista.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +import subprocess + +import charmhelpers.core.hookenv as hookenv +from charmhelpers.core.hookenv import ( + config, + log, + status_set, + is_leader, + leader_get, + leader_set, +) +import charms_openstack.charm + +from charmhelpers.contrib.python.packages import pip_install + +from charmhelpers.contrib.openstack import context +from charmhelpers.contrib.openstack.utils import ( + CompareOpenStackReleases, + os_release, +) + +ML2_CONFIG_ARISTA = '/etc/neutron/plugins/ml2/ml2_conf_arista.ini' +TEMPLATES = 'templates/' + + +class NeutronAristaCharm(charms_openstack.charm.OpenStackCharm): + + # Internal name of charm + service_name = name = 'neutron-arista' + + # First release supported + release = 'queens' + + # List of packages to install for this charm + packages = [''] + + def install(self): + package_version = config('arista-version') + package_name = 'networking-arista==%s' % package_version + proxy = config('pip-proxy') + if proxy: + pip_install(package_name, fatal=True, proxy=proxy) + else: + pip_install(package_name, fatal=True) + status_set('active', 'Unit is ready') + + +class AristaMl2Context(context.OSContextGenerator): + + def __call__(self): + ctxt = {'eapi_host': config('eapi-host'), + 'eapi_username': config('eapi-username'), + 'eapi_password': config('eapi-password'), + 'region_name': config('region-name'), + 'api_type': config('api-type'), + 'arista_version': config('arista-version')} + return ctxt + + +def register_configs(release=None): + resources = OrderedDict([ + (ML2_CONFIG, { + 'services': ['neutron-server'], + 'contexts': [AristaMl2Context(), ] + }), + (ML2_CONFIG_ARISTA, { + 'services': ['neutron-server'], + 'contexts': [AristaMl2Context(), ] + }), + ]) + release = os_release('neutron-common') + configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, + openstack_release=release) + for cfg, rscs in resources.iteritems(): + configs.register(cfg, rscs['contexts']) + return configs diff --git a/src/metadata.yaml b/src/metadata.yaml new file mode 100644 index 0000000..0b3395d --- /dev/null +++ b/src/metadata.yaml @@ -0,0 +1,19 @@ +name: neutron-arista +summary: Arista ML2 plugin support for Neutron-API +maintainer: Michael Skalka +description: | + +tags: + - misc + - networking +subordinate: true +series: + - xenial +provides: + neutron-arista: + interface: neutron-plugin-api-subordinate + scope: container +requires: + container: + interface: juju-info + scope: container diff --git a/src/reactive/neutron_arista_handlers.py b/src/reactive/neutron_arista_handlers.py new file mode 100644 index 0000000..facb5ac --- /dev/null +++ b/src/reactive/neutron_arista_handlers.py @@ -0,0 +1,56 @@ +# Copyright 2016 Canonical Ltd +# +# 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 charms.reactive as reactive + +from charms_openstack.charm import ( + provide_charm_instance, + use_defaults, +) +import charm.openstack.keystone_ico as ico # noqa + +CONFIGS = register_configs() + +use_defaults('update-status') + + +@reactive.when_not('neutron-arista.installed') +def install_neutron_arista(): + with provide_charm_instance() as charm_class: + charm_class.install() + reactive.set_state('neutron-arista.installed') + + +@reactive.when('neutron-plugin-api-subordinate.connected') +@reactive.when('neutron-arista.installed') +def configure_principle(api_principle): + with provide_charm_instance() as charm_class: + charm_class.render_ml2_conf() + injet_config = { + 'neutron-api': { + '/etc/neutron/neutron.conf': { + 'sections': { + 'DEFAULT': [ + ], + } + } + } + } + api_principle.configure_plugin( + neutron_plugin='arista', + core_plugin='neutron.plugins.ml2.plugin.Ml2Plugin', + neutron_plugin_config='/etc/neutron/plugins/ml2/ml2_conf_arista.ini', + service_plugins=config('service-plugins'), + subordinate_configuration=inject_config) + api_principle.request_restart(service_type='neutron-server') diff --git a/src/templates/ml2_conf_arista.ini b/src/templates/ml2_conf_arista.ini new file mode 100644 index 0000000..0f3b856 --- /dev/null +++ b/src/templates/ml2_conf_arista.ini @@ -0,0 +1,11 @@ +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### + +[ml2_arista] +eapi_host = {{ eapi_host }} +eapi_username = {{ eapi_username }} +eapi_password = {{ eapi_password }} +region_name = {{ region_name }} +api_type = {{ api_type }} diff --git a/src/tests/00-setup b/src/tests/00-setup new file mode 100755 index 0000000..f0616a5 --- /dev/null +++ b/src/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/src/tests/10-deploy b/src/tests/10-deploy new file mode 100755 index 0000000..d177d2e --- /dev/null +++ b/src/tests/10-deploy @@ -0,0 +1,35 @@ +#!/usr/bin/python3 + +import amulet +import requests +import unittest + + +class TestCharm(unittest.TestCase): + def setUp(self): + self.d = amulet.Deployment() + + self.d.add('charm-neutron-arista') + self.d.expose('charm-neutron-arista') + + self.d.setup(timeout=900) + self.d.sentry.wait() + + self.unit = self.d.sentry['charm-neutron-arista'][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 + + +if __name__ == '__main__': + unittest.main()