Initial commit
This commit is contained in:
commit
52522348af
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.tox
|
||||
.stestr
|
||||
*__pycache__*
|
||||
*.pyc
|
||||
build
|
||||
interfaces
|
||||
layers
|
||||
*.swp
|
3
.stestr.conf
Normal file
3
.stestr.conf
Normal file
@ -0,0 +1,3 @@
|
||||
[DEFAULT]
|
||||
test_path=./unit_tests
|
||||
top_dir=./
|
7
requirements.txt
Normal file
7
requirements.txt
Normal 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
11
src/layer.yaml
Normal 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
|
96
src/lib/charm/openstack/ovn_controller.py
Normal file
96
src/lib/charm/openstack/ovn_controller.py
Normal 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
18
src/metadata.yaml
Normal 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
|
25
src/reactive/ovn_controller_handlers.py
Normal file
25
src/reactive/ovn_controller_handlers.py
Normal 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
12
src/templates/ovn-host
Normal 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
13
test-requirements.txt
Normal 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
80
tox.ini
Normal 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
39
unit_tests/__init__.py
Normal 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
|
52
unit_tests/test_reactive_ovn_controller_handlers.py
Normal file
52
unit_tests/test_reactive_ovn_controller_handlers.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user