Initial commit

This commit is contained in:
Frode Nordahl 2019-09-27 15:45:48 +02:00 committed by Frode Nordahl
commit 52522348af
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
12 changed files with 364 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.tox
.stestr
*__pycache__*
*.pyc
build
interfaces
layers
*.swp

3
.stestr.conf Normal file
View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=./unit_tests
top_dir=./

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
# This file is managed centrally. If you find the need to modify this as a
# one-off, please don't. Intead, consult #openstack-charms and ask about
# requirements management in charms via bot-control. Thank you.
#
# Build requirements
charm-tools>=2.4.4
simplejson

11
src/layer.yaml Normal file
View File

@ -0,0 +1,11 @@
includes:
- layer:openstack
- interface:ovsdb
options:
basic:
use_venv: True
include_system_packages: False
repo: https://github.com/openstack/charm-ovn-controller
config:
deletes:
- verbose

View File

@ -0,0 +1,96 @@
import os
import subprocess
import charmhelpers.core as ch_core
import charms_openstack.charm
OVS_ETCDIR = '/etc/openvswitch'
@charms_openstack.adapters.config_property
def cluster_local_addr(cls):
"""Address the ``ovsdb-server`` processes should be bound to.
:param cls: charms_openstack.adapters.ConfigurationAdapter derived class
instance. Charm class instance is at cls.charm_instance.
:type: cls: charms_openstack.adapters.ConfiguartionAdapter
:returns: IP address selected for cluster communication on local unit.
:rtype: str
"""
# XXX this should probably be made space aware
# for addr in cls.charm_instance.get_local_addresses():
# return addr
return ch_core.hookenv.unit_get('private-address')
@charms_openstack.adapters.config_property
def ovn_key(cls):
return os.path.join(OVS_ETCDIR, 'key_host')
@charms_openstack.adapters.config_property
def ovn_cert(cls):
return os.path.join(OVS_ETCDIR, 'cert_host')
@charms_openstack.adapters.config_property
def ovn_ca_cert(cls):
return os.path.join(OVS_ETCDIR,
'{}.crt'.format(cls.charm_instance.name))
class OVNControllerCharm(charms_openstack.charm.OpenStackCharm):
release = 'stein'
name = 'ovn-controller'
packages = ['ovn-host']
services = ['ovn-host']
required_relations = ['certificates', 'ovsdb']
restart_map = {
'/etc/default/ovn-host': 'ovn-host',
}
python_version = 3
def run(self, *args):
cp = subprocess.run(
args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True,
universal_newlines=True)
ch_core.hookenv.log(cp, level=ch_core.hookenv.INFO)
def configure_tls(self, certificates_interface=None):
"""Override default handler prepare certs per OVNs taste."""
# The default handler in ``OpenStackCharm`` class does the CA only
tls_objects = super().configure_tls(
certificates_interface=certificates_interface)
for tls_object in tls_objects:
with open(ovn_ca_cert(self.adapters_instance), 'w') as crt:
crt.write(
tls_object['ca'] +
os.linesep +
tls_object.get('chain', ''))
self.configure_cert(OVS_ETCDIR,
tls_object['cert'],
tls_object['key'],
cn='host')
break
def configure_ovs(self, ovsdb_interface):
self.run('ovs-vsctl',
'set-ssl',
ovn_key(self.adapters_instance),
ovn_cert(self.adapters_instance),
ovn_ca_cert(self.adapters_instance))
self.run('ovs-vsctl',
'set',
'open',
'.',
'external-ids:ovn-remote={}'
.format(','.join(ovsdb_interface.db_sb_connection_strs)))
self.run('ovs-vsctl', 'set', 'open', '.',
'external-ids:ovn-encap-type=geneve')
self.run('ovs-vsctl', 'set', 'open', '.',
'external-ids:ovn-encap-ip={}'
.format(cluster_local_addr(None)))
self.restart_all()

18
src/metadata.yaml Normal file
View File

@ -0,0 +1,18 @@
name: ovn-controller
summary: Open Virtual Network for Open vSwitch
maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
description: |
Subordinate charm that deploys the OVN local controller and Open vSwitch
Database and Switch.
tags:
- networking
series:
- bionic
- disco
subordinate: true
requires:
juju-info:
interface: juju-info
scope: container
ovsdb:
interface: ovsdb

View File

@ -0,0 +1,25 @@
import charms.reactive as reactive
import charms_openstack.bus
import charms_openstack.charm as charm
charms_openstack.bus.discover()
# Use the charms.openstack defaults for common states and hooks
charm.use_defaults(
'charm.installed',
'config.changed',
'update-status',
'upgrade-charm',
'certificates.available',
)
@reactive.when('ovsdb.available')
def configure_ovs():
ovsdb = reactive.endpoint_from_flag('ovsdb.available')
with charm.provide_charm_instance() as charm_instance:
charm_instance.render_with_interfaces([ovsdb])
charm_instance.configure_ovs(ovsdb)
charm_instance.assess_status()

12
src/templates/ovn-host Normal file
View File

@ -0,0 +1,12 @@
# This is a POSIX shell fragment -*- sh -*-
###############################################################################
# [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten.
# Configuration managed by neutron-openvswitch charm
###############################################################################
# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example,
# a suitable place to specify --ovn-controller-wrapper=valgrind.
# OVN_CTL_OPTS=

13
test-requirements.txt Normal file
View File

@ -0,0 +1,13 @@
# This file is managed centrally. If you find the need to modify this as a
# one-off, please don't. Intead, consult #openstack-charms and ask about
# requirements management in charms via bot-control. Thank you.
#
# Lint and unit test requirements
flake8>=2.2.4,<=2.4.1
stestr>=2.2.0
requests>=2.18.4
charms.reactive
mock>=1.2
nose>=1.3.7
coverage>=3.6
git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack

80
tox.ini Normal file
View File

@ -0,0 +1,80 @@
# Source charm: ./tox.ini
# This file is managed centrally by release-tools and should not be modified
# within individual charm repos.
[tox]
skipsdist = True
envlist = pep8,py3
[testenv]
setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
TERM=linux
CHARM_LAYERS_DIR={toxinidir}/layers
CHARM_INTERFACES_DIR={toxinidir}/interfaces
JUJU_REPOSITORY={toxinidir}/build
passenv = http_proxy https_proxy INTERFACE_PATH
install_command =
pip install {opts} {packages}
deps =
-r{toxinidir}/requirements.txt
[testenv:build]
basepython = python3
commands =
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
[testenv:py3]
basepython = python3
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
[testenv:py35]
basepython = python3.5
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
[testenv:py36]
basepython = python3.6
deps = -r{toxinidir}/test-requirements.txt
commands = stestr run {posargs}
[testenv:pep8]
basepython = python3
deps = -r{toxinidir}/test-requirements.txt
commands = flake8 {posargs} src unit_tests
[testenv:cover]
# Technique based heavily upon
# https://github.com/openstack/nova/blob/master/tox.ini
basepython = python3
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
setenv =
{[testenv]setenv}
PYTHON=coverage run
commands =
coverage erase
stestr run {posargs}
coverage combine
coverage html -d cover
coverage xml -o cover/coverage.xml
coverage report
[coverage:run]
branch = True
concurrency = multiprocessing
parallel = True
source =
.
omit =
.tox/*
*/charmhelpers/*
unit_tests/*
[testenv:venv]
basepython = python3
commands = {posargs}
[flake8]
# E402 ignore necessary for path append before sys module import in actions
ignore = E402

39
unit_tests/__init__.py Normal file
View File

@ -0,0 +1,39 @@
# Copyright 2018 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 sys
sys.path.append('src')
sys.path.append('src/lib')
# Mock out charmhelpers so that we can test without it.
import charms_openstack.test_mocks # noqa
charms_openstack.test_mocks.mock_charmhelpers()
import mock
import charms
charms.leadership = mock.MagicMock()
keystoneauth1 = mock.MagicMock()
neutronclient = mock.MagicMock()
sys.modules['charms.leadership'] = charms.leadership
keystoneauth1 = mock.MagicMock()
novaclient = mock.MagicMock()
neutron_lib = mock.MagicMock()
sys.modules['charms.leadership'] = charms.leadership
sys.modules['keystoneauth1'] = keystoneauth1
sys.modules['novaclient'] = novaclient
sys.modules['neutronclient'] = neutronclient
sys.modules['neutronclient.v2_0'] = neutronclient.v2_0
sys.modules['neutron_lib'] = neutron_lib
sys.modules['neutron_lib.constants'] = neutron_lib.constants

View File

@ -0,0 +1,52 @@
# Copyright 2019 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 mock
import reactive.ovn_controller_handlers as handlers
import charms_openstack.test_utils as test_utils
class TestRegisteredHooks(test_utils.TestRegisteredHooks):
def test_hooks(self):
defaults = [
'charm.installed',
'config.changed',
'update-status',
'upgrade-charm',
'certificates.available',
]
hook_set = {
'when': {
'configure_ovs': ('ovsdb.available',),
},
}
# test that the hooks were registered via the
# reactive.ovn_handlers
self.registered_hooks_test_helper(handlers, hook_set, defaults)
class TestOvnHandlers(test_utils.PatchHelper):
def setUp(self):
super().setUp()
# self.patch_release(octavia.OctaviaCharm.release)
self.charm = mock.MagicMock()
self.patch_object(handlers.charm, 'provide_charm_instance',
new=mock.MagicMock())
self.provide_charm_instance().__enter__.return_value = \
self.charm
self.provide_charm_instance().__exit__.return_value = None