diff --git a/requirements.txt b/requirements.txt index 20f335d..f65abf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,6 @@ # # Build requirements charm-tools>=2.4.4 +# importlib-resources 1.1.0 removed Python 3.5 support +importlib-resources<1.1.0 simplejson diff --git a/src/lib/charm/openstack/ovn_central.py b/src/lib/charm/openstack/ovn_central.py index 59433f5..f9e8950 100644 --- a/src/lib/charm/openstack/ovn_central.py +++ b/src/lib/charm/openstack/ovn_central.py @@ -26,51 +26,38 @@ import charms.ovn as ovn import charms.ovn_charm as ovn_charm -class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): - # OpenvSwitch and OVN is distributed as part of the Ubuntu Cloud Archive - # Pockets get their name from OpenStack releases - release = 'train' +class BaseOVNCentralCharm(charms_openstack.charm.OpenStackCharm): + abstract_class = True package_codenames = { 'ovn-central': collections.OrderedDict([ - ('2.12', 'train'), + ('2', 'train'), + ('20', 'ussuri'), ]), } name = 'ovn-central' packages = ['ovn-central'] services = ['ovn-central'] + release_pkg = 'ovn-central' required_relations = ['certificates'] - # NOTE(fnordahl) we replace the package sysv init script with our own - # systemd service files. - # - # The issue that triggered this change is that to be able to pass the - # correct command line arguments to ``ovn-nortrhd`` we need to create - # a ``/etc/openvswitch/ovn-northd-db-params.conf`` which has the side - # effect of profoundly changing the behaviour of the ``ovn-ctl`` tool - # that the ``ovn-central`` init script makes use of. - # - # https://github.com/ovn-org/ovn/blob/dc0e10c068c20c4e59c9c86ecee26baf8ed50e90/utilities/ovn-ctl#L323 - # - # TODO: The systemd service files should be upstreamed and removed from - # the charm restart_map = { '/etc/default/ovn-central': services, os.path.join( - ovn_charm.OVS_ETCDIR, 'ovn-northd-db-params.conf'): ['ovn-northd'], - '/lib/systemd/system/ovn-central.service': [], - '/lib/systemd/system/ovn-northd.service': [], - '/lib/systemd/system/ovn-nb-ovsdb.service': [], - '/lib/systemd/system/ovn-sb-ovsdb.service': [], + ovn.ovn_sysconfdir(), 'ovn-northd-db-params.conf'): ['ovn-northd'], } python_version = 3 source_config_key = 'source' def __init__(self, **kwargs): super().__init__(**kwargs) - charms_openstack.adapters.config_property(ovn_charm.ovn_key) - charms_openstack.adapters.config_property(ovn_charm.ovn_cert) - charms_openstack.adapters.config_property(ovn_charm.ovn_ca_cert) + try: + charms_openstack.adapters.config_property(ovn_charm.ovn_key) + charms_openstack.adapters.config_property(ovn_charm.ovn_cert) + charms_openstack.adapters.config_property(ovn_charm.ovn_ca_cert) + except RuntimeError: + # The custom config properties are allready registered + pass - def install(self): + def install(self, service_masks=None): """Extend the default install method. Mask services before initial installation. @@ -84,15 +71,9 @@ class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): We also configure source before installing as OpenvSwitch and OVN packages are distributed as part of the UCA. """ - # NOTE(fnordahl) The OVN central components are currently packaged with - # a dependency on openvswitch-switch, but it does not need the switch - # or stock ovsdb running. - service_masks = [ - 'openvswitch-switch.service', - 'ovs-vswitchd.service', - 'ovsdb-server.service', - 'ovn-central.service', - ] + # NOTE(fnordahl): The actual masks are provided by the release specific + # classes. + service_masks = service_masks or [] for service_file in service_masks: abs_path_svc = os.path.join('/etc/systemd/system', service_file) if not os.path.islink(abs_path_svc): @@ -169,6 +150,20 @@ class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): def join_cluster(self, db_file, schema_name, local_conn, remote_conn): """Maybe create a OVSDB file with remote peer connection information. + This function will return immediately if the database file already + exists. + + Because of a shortcoming in the ``ovn-ctl`` script used to start the + OVN databases we call to ``ovsdb-tool join-cluster`` ourself. + + That will create a database file on disk with the required information + and the ``ovn-ctl`` script will not touch it. + + The ``ovn-ctl`` ``db-nb-cluster-remote-addr`` and + ``db-sb-cluster-remote-addr`` configuration options only take one + remote and one must be provided for correct startup, but the values in + the on-disk database file will be used by the ``ovsdb-server`` process. + :param db_file: Full path to OVSDB file :type db_file: str :param schema_name: OVSDB Schema [OVN_Northbound, OVN_Southbound] @@ -179,9 +174,16 @@ class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): :type remote_conn: Union[str, ...] :raises: subprocess.CalledProcessError """ - if os.path.exists(db_file): + if self.release == 'train': + absolute_path = os.path.join('/var/lib/openvswitch', db_file) + else: + absolute_path = os.path.join('/var/lib/ovn', db_file) + if os.path.exists(absolute_path): + ch_core.hookenv.log('OVN database "{}" exists on disk, not ' + 'creating a new one joining cluster', + level=ch_core.hookenv.DEBUG) return - cmd = ['ovsdb-tool', 'join-cluster', db_file, schema_name] + cmd = ['ovsdb-tool', 'join-cluster', absolute_path, schema_name] cmd.extend(list(local_conn)) cmd.extend(list(remote_conn)) ch_core.hookenv.log(cmd, level=ch_core.hookenv.INFO) @@ -206,7 +208,7 @@ class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): else: crt.write(tls_object['ca']) - self.configure_cert(ovn_charm.OVS_ETCDIR, + self.configure_cert(ovn.ovn_sysconfdir(), tls_object['cert'], tls_object['key'], cn='host') @@ -349,3 +351,83 @@ class OVNCentralCharm(charms_openstack.charm.OpenStackCharm): delete_rules.append(num) for rule in sorted(delete_rules, reverse=True): ch_ufw.modify_access(None, dst=None, action='delete', index=rule) + + +class TrainOVNCentralCharm(BaseOVNCentralCharm): + # OpenvSwitch and OVN is distributed as part of the Ubuntu Cloud Archive + # Pockets get their name from OpenStack releases + release = 'train' + + # NOTE(fnordahl) we have to replace the package sysv init script with + # systemd service files, this should be removed from the charm when the + # systemd service files committed to Focal can be backported to the Train + # UCA. + # + # The issue that triggered this change is that to be able to pass the + # correct command line arguments to ``ovn-nortrhd`` we need to create + # a ``/etc/openvswitch/ovn-northd-db-params.conf`` which has the side + # effect of profoundly changing the behaviour of the ``ovn-ctl`` tool + # that the ``ovn-central`` init script makes use of. + # + # https://github.com/ovn-org/ovn/blob/dc0e10c068c20c4e59c9c86ecee26baf8ed50e90/utilities/ovn-ctl#L323 + def __init__(self, **kwargs): + """Override class init to adjust restart_map for Train. + + NOTE(fnordahl): the restart_map functionality in charms.openstack + combines the process of writing a charm template to disk and + restarting a service whenever the target file changes. + + In this instance we are only interested in getting the files written + to disk. The restart operation will be taken care of when + ``/etc/default/ovn-central`` as defined in ``BaseOVNCentralCharm``. + """ + super().__init__(**kwargs) + self.restart_map.update({ + '/lib/systemd/system/ovn-central.service': [], + '/lib/systemd/system/ovn-northd.service': [], + '/lib/systemd/system/ovn-nb-ovsdb.service': [], + '/lib/systemd/system/ovn-sb-ovsdb.service': [], + }) + + def install(self): + """Override charm install method. + + NOTE(fnordahl) At Train, the OVN central components is packaged with + a dependency on openvswitch-switch, but it does not need the switch + or stock ovsdb running. + """ + service_masks = [ + 'openvswitch-switch.service', + 'ovs-vswitchd.service', + 'ovsdb-server.service', + 'ovn-central.service', + ] + super().install(service_masks=service_masks) + + +class UssuriOVNCentralCharm(BaseOVNCentralCharm): + # OpenvSwitch and OVN is distributed as part of the Ubuntu Cloud Archive + # Pockets get their name from OpenStack releases + release = 'ussuri' + + def __init__(self, **kwargs): + """Override class init to adjust service map for Ussuri.""" + super().__init__(**kwargs) + # We need to list the OVN ovsdb-server services explicitly so they get + # unmasked on render of ``ovn-central``. + self.services.extend([ + 'ovn-ovsdb-server-nb', + 'ovn-ovsdb-server-sb', + ]) + + def install(self): + """Override charm install method.""" + + # This is done to prevent extraneous standalone DB initialization and + # subsequent upgrade to clustered DB when configuration is rendered. + service_masks = [ + 'ovn-central.service', + 'ovn-ovsdb-server-nb.service', + 'ovn-ovsdb-server-sb.service', + ] + super().install(service_masks=service_masks) diff --git a/src/metadata.yaml b/src/metadata.yaml index 95e2425..ef833f7 100644 --- a/src/metadata.yaml +++ b/src/metadata.yaml @@ -26,6 +26,7 @@ tags: series: - bionic - eoan + - focal subordinate: false provides: ovsdb: diff --git a/src/reactive/ovn_central_handlers.py b/src/reactive/ovn_central_handlers.py index b00ec8e..bafa39a 100644 --- a/src/reactive/ovn_central_handlers.py +++ b/src/reactive/ovn_central_handlers.py @@ -27,6 +27,7 @@ charms_openstack.bus.discover() charm.use_defaults( 'charm.installed', 'config.changed', + 'charm.default-select-release', 'update-status', 'upgrade-charm', ) @@ -121,7 +122,9 @@ def configure_firewall(): ovsdb_peer.db_sb_cluster_port, ovsdb_peer.db_nb_cluster_port,): ovsdb_peer.cluster_remote_addrs, - (ovsdb_peer.db_nb_port,): + # NOTE(fnordahl): Tactical workaround for LP: #1864640 + (ovsdb_peer.db_nb_port, + ovsdb_peer.db_sb_admin_port,): ovsdb_cms.client_remote_addrs if ovsdb_cms else None, }) ovn_charm.assess_status() @@ -142,6 +145,17 @@ def publish_addr_to_clients(): ep.publish_cluster_local_addr(ovsdb_peer.cluster_local_addr) +@reactive.when_none('run-default-update-status') +@reactive.when('config.changed.source', 'ovsdb-peer.available') +def maybe_do_upgrade(): + ovsdb_peer = reactive.endpoint_from_flag('ovsdb-peer.available') + with charm.provide_charm_instance() as ovn_charm: + ovn_charm.configure_source() + ovn_charm.upgrade_if_available([ovsdb_peer]) + reactive.clear_flag('config.changed.source') + ovn_charm.assess_status() + + @reactive.when_none('run-default-update-status') @reactive.when('ovsdb-peer.available', 'leadership.set.nb_cid', @@ -164,16 +178,14 @@ def render(): # # Replace this with functionality in ``ovn-ctl`` when support has been # added upstream. - ovn_charm.join_cluster('/var/lib/openvswitch/ovnnb_db.db', - 'OVN_Northbound', + ovn_charm.join_cluster('ovnnb_db.db', 'OVN_Northbound', ovsdb_peer.db_connection_strs( (ovsdb_peer.cluster_local_addr,), ovsdb_peer.db_nb_cluster_port), ovsdb_peer.db_connection_strs( ovsdb_peer.cluster_remote_addrs, ovsdb_peer.db_nb_cluster_port)) - ovn_charm.join_cluster('/var/lib/openvswitch/ovnsb_db.db', - 'OVN_Southbound', + ovn_charm.join_cluster('ovnsb_db.db', 'OVN_Southbound', ovsdb_peer.db_connection_strs( (ovsdb_peer.cluster_local_addr,), ovsdb_peer.db_sb_cluster_port), diff --git a/src/templates/ovn-central b/src/templates/train/ovn-central similarity index 67% rename from src/templates/ovn-central rename to src/templates/train/ovn-central index 1a5bd21..43a071a 100644 --- a/src/templates/ovn-central +++ b/src/templates/train/ovn-central @@ -6,11 +6,19 @@ # Configuration managed by ovn-central charm ############################################################################### -# FORCE_COREFILES: If 'yes' then core files will be enabled. -# FORCE_COREFILES=yes - # OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, # a suitable place to specify --ovn-northd-wrapper=valgrind. + +# NOTE(fnordahl): Cluster join is directed towards all peers by a direct call +# to `ovsdb-tool join-cluster` by the charm. +# +# That will create a database file on disk with the required information and +# the `ovn-ctl` script will not touch it. +# +# The `ovn-ctl` `db-nb-cluster-remote-addr` and `db-sb-cluster-remote-addr` +# configuration options only take one remote and one must be provided for +# correct startup, but the values in the on-disk database file will be used by +# `ovsdb-server`. OVN_CTL_OPTS=--db-nb-file=/var/lib/openvswitch/ovnnb_db.db \ --db-nb-cluster-local-addr={{ ovsdb_peer.cluster_local_addr }} \ --db-nb-cluster-local-port={{ ovsdb_peer.db_nb_cluster_port }} \ @@ -29,17 +37,5 @@ OVN_CTL_OPTS=--db-nb-file=/var/lib/openvswitch/ovnnb_db.db \ --ovn-sb-db-ssl-cert={{ options.ovn_cert }} \ --ovn-sb-db-ssl-ca-cert={{ options.ovn_ca_cert }} \ --db-sb-cluster-remote-addr={{ ovsdb_peer.cluster_remote_addrs | first }} \ - --db-nb-cluster-remote-port={{ ovsdb_peer.db_nb_cluster_port }} \ + --db-sb-cluster-remote-port={{ ovsdb_peer.db_sb_cluster_port }} \ --db-sb-cluster-remote-proto=ssl - -# DPDK options are now configured via ovs-vsctl/ovsdb, see: -# - /usr/share/doc/openvswitch-common/INSTALL.DPDK.md.gz -# - /usr/share/doc/openvswitch-common/INSTALL.DPDK-ADVANCED.md.gz -# dpdk-extra is the fallback for users who formerly set e.g. -# '--vhost-owner libvirt-qemu:kvm --vhost-perm 0664' -# here or any other DPDK EAL arguments via DPDK_OPTS which are not covered by -# the default switches provided by ovs-vsctl. -# This is an arbitrary string that will get appended to the EAL commandline. -# For more on the new ovsdb based configuration of DPDK features, see: -# `man ovs-vswitchd.conf.db` - diff --git a/src/templates/ovn-central.service b/src/templates/train/ovn-central.service similarity index 100% rename from src/templates/ovn-central.service rename to src/templates/train/ovn-central.service diff --git a/src/templates/ovn-nb-ovsdb.service b/src/templates/train/ovn-nb-ovsdb.service similarity index 100% rename from src/templates/ovn-nb-ovsdb.service rename to src/templates/train/ovn-nb-ovsdb.service diff --git a/src/templates/ovn-northd.service b/src/templates/train/ovn-northd.service similarity index 100% rename from src/templates/ovn-northd.service rename to src/templates/train/ovn-northd.service diff --git a/src/templates/ovn-sb-ovsdb.service b/src/templates/train/ovn-sb-ovsdb.service similarity index 100% rename from src/templates/ovn-sb-ovsdb.service rename to src/templates/train/ovn-sb-ovsdb.service diff --git a/src/templates/ussuri/ovn-central b/src/templates/ussuri/ovn-central new file mode 100644 index 0000000..dc9d2db --- /dev/null +++ b/src/templates/ussuri/ovn-central @@ -0,0 +1,40 @@ +# This is a systemd EnvironmentFile as documented in systemd.exec(5) +# +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +# Configuration managed by ovn-central charm +############################################################################### + +# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, +# a suitable place to specify --ovn-northd-wrapper=valgrind. + +# NOTE(fnordahl): Cluster join is directed towards all peers by a direct call +# to `ovsdb-tool join-cluster` by the charm. +# +# That will create a database file on disk with the required information and +# the `ovn-ctl` script will not touch it. +# +# The `ovn-ctl` `db-nb-cluster-remote-addr` and `db-sb-cluster-remote-addr` +# configuration options only take one remote and one must be provided for +# correct startup, but the values in the on-disk database file will be used by +# `ovsdb-server`. +OVN_CTL_OPTS= \ + --db-nb-cluster-local-addr={{ ovsdb_peer.cluster_local_addr }} \ + --db-nb-cluster-local-port={{ ovsdb_peer.db_nb_cluster_port }} \ + --db-nb-cluster-local-proto=ssl \ + --ovn-nb-db-ssl-key={{ options.ovn_key }} \ + --ovn-nb-db-ssl-cert={{ options.ovn_cert }} \ + --ovn-nb-db-ssl-ca-cert={{ options.ovn_ca_cert }} \ + --db-nb-cluster-remote-addr={{ ovsdb_peer.cluster_remote_addrs | first }} \ + --db-nb-cluster-remote-port={{ ovsdb_peer.db_nb_cluster_port }} \ + --db-nb-cluster-remote-proto=ssl \ + --db-sb-cluster-local-addr={{ ovsdb_peer.cluster_local_addr }} \ + --db-sb-cluster-local-port={{ ovsdb_peer.db_sb_cluster_port }} \ + --db-sb-cluster-local-proto=ssl \ + --ovn-sb-db-ssl-key={{ options.ovn_key }} \ + --ovn-sb-db-ssl-cert={{ options.ovn_cert }} \ + --ovn-sb-db-ssl-ca-cert={{ options.ovn_ca_cert }} \ + --db-sb-cluster-remote-addr={{ ovsdb_peer.cluster_remote_addrs | first }} \ + --db-sb-cluster-remote-port={{ ovsdb_peer.db_sb_cluster_port }} \ + --db-sb-cluster-remote-proto=ssl diff --git a/src/tests/bundles/bionic.yaml b/src/tests/bundles/bionic-train.yaml similarity index 100% rename from src/tests/bundles/bionic.yaml rename to src/tests/bundles/bionic-train.yaml diff --git a/src/tests/bundles/bionic-ussuri.yaml b/src/tests/bundles/bionic-ussuri.yaml new file mode 100644 index 0000000..6ca8df0 --- /dev/null +++ b/src/tests/bundles/bionic-ussuri.yaml @@ -0,0 +1,19 @@ +series: bionic +relations: +- - vault:shared-db + - mysql:shared-db +- - ovn-central:certificates + - vault:certificates +applications: + mysql: + charm: cs:~openstack-charmers-next/percona-cluster + num_units: 1 + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + ovn-central: + series: bionic + charm: cs:~openstack-charmers-next/ovn-central + num_units: 3 + options: + source: cloud:bionic-ussuri/proposed diff --git a/src/tests/bundles/eoan.yaml b/src/tests/bundles/eoan.yaml index 0418aa4..3846007 100644 --- a/src/tests/bundles/eoan.yaml +++ b/src/tests/bundles/eoan.yaml @@ -1,13 +1,17 @@ series: eoan relations: +- - vault-mysql-router:db-router + - mysql-innodb-cluster:db-router - - vault:shared-db - - mysql:shared-db + - vault-mysql-router:shared-db - - ovn-central:certificates - vault:certificates applications: - mysql: + mysql-innodb-cluster: charm: cs:~openstack-charmers-next/mysql-innodb-cluster num_units: 3 + vault-mysql-router: + charm: cs:~openstack-charmers-next/mysql-router vault: charm: cs:~openstack-charmers-next/vault num_units: 1 diff --git a/src/tests/bundles/focal.yaml b/src/tests/bundles/focal.yaml new file mode 100644 index 0000000..673b0d2 --- /dev/null +++ b/src/tests/bundles/focal.yaml @@ -0,0 +1,21 @@ +series: focal +relations: +- - vault-mysql-router:db-router + - mysql-innodb-cluster:db-router +- - vault:shared-db + - vault-mysql-router:shared-db +- - ovn-central:certificates + - vault:certificates +applications: + mysql-innodb-cluster: + charm: cs:~openstack-charmers-next/mysql-innodb-cluster + num_units: 3 + vault-mysql-router: + charm: cs:~openstack-charmers-next/mysql-router + vault: + charm: cs:~openstack-charmers-next/vault + num_units: 1 + ovn-central: + series: focal + charm: cs:~openstack-charmers-next/ovn-central + num_units: 3 diff --git a/src/tests/tests.yaml b/src/tests/tests.yaml index 2942112..6ffc598 100644 --- a/src/tests/tests.yaml +++ b/src/tests/tests.yaml @@ -1,9 +1,12 @@ charm_name: ovn-central gate_bundles: +- bionic-train - eoan -- bionic +- bionic-ussuri smoke_bundles: -- bionic +- bionic-ussuri +dev_bundles: +- focal target_deploy_status: ovn-central: workload-status: blocked diff --git a/test-requirements.txt b/test-requirements.txt index 14b380e..c43a8db 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -3,11 +3,10 @@ # 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 +mock>=1.2 +pep8>=1.7.0 +flake8>=2.2.4 +os-testr>=0.4.1 + git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack diff --git a/tox.ini b/tox.ini index 23750a8..1d1b04a 100644 --- a/tox.ini +++ b/tox.ini @@ -77,4 +77,5 @@ commands = {posargs} [flake8] # E402 ignore necessary for path append before sys module import in actions -ignore = E402 +# W504 is necessary as both W503 and W504 is enabled in flake8 at the same time +ignore = E402,W504 diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py index dffc71c..661d3fc 100644 --- a/unit_tests/__init__.py +++ b/unit_tests/__init__.py @@ -22,22 +22,55 @@ import charms_openstack.test_mocks # noqa charms_openstack.test_mocks.mock_charmhelpers() import mock -import charms + + +class _fake_decorator(object): + + def __init__(self, *args): + pass + + def __call__(self, f): + return f + + +charms = mock.MagicMock() +sys.modules['charms'] = charms charms.leadership = mock.MagicMock() +sys.modules['charms.leadership'] = charms.leadership +charms.reactive = mock.MagicMock() +charms.reactive.when = _fake_decorator +charms.reactive.when_all = _fake_decorator +charms.reactive.when_any = _fake_decorator +charms.reactive.when_not = _fake_decorator +charms.reactive.when_none = _fake_decorator +charms.reactive.when_not_all = _fake_decorator +charms.reactive.not_unless = _fake_decorator +charms.reactive.when_file_changed = _fake_decorator +charms.reactive.collect_metrics = _fake_decorator +charms.reactive.meter_status_changed = _fake_decorator +charms.reactive.only_once = _fake_decorator +charms.reactive.hook = _fake_decorator +charms.reactive.bus = mock.MagicMock() +charms.reactive.flags = mock.MagicMock() +charms.reactive.relations = mock.MagicMock() +sys.modules['charms.reactive'] = charms.reactive +sys.modules['charms.reactive.bus'] = charms.reactive.bus +sys.modules['charms.reactive.bus'] = charms.reactive.decorators +sys.modules['charms.reactive.flags'] = charms.reactive.flags +sys.modules['charms.reactive.relations'] = charms.reactive.relations charms.ovn = mock.MagicMock() -charms.ovn_charm = mock.MagicMock() -keystoneauth1 = mock.MagicMock() -neutronclient = mock.MagicMock() -sys.modules['charms.leadership'] = charms.leadership sys.modules['charms.ovn'] = charms.ovn -sys.modules['charms.ovn_charm'] = charms.ovn_charm +charms.ovn_charm = mock.MagicMock() +sys.modules['charms.ovn_charm'] = charms.ovn keystoneauth1 = mock.MagicMock() -novaclient = mock.MagicMock() -neutron_lib = mock.MagicMock() -sys.modules['charms.leadership'] = charms.leadership sys.modules['keystoneauth1'] = keystoneauth1 -sys.modules['novaclient'] = novaclient +netaddr = mock.MagicMock() +sys.modules['netaddr'] = netaddr +neutronclient = mock.MagicMock() sys.modules['neutronclient'] = neutronclient sys.modules['neutronclient.v2_0'] = neutronclient.v2_0 +neutron_lib = mock.MagicMock() sys.modules['neutron_lib'] = neutron_lib sys.modules['neutron_lib.constants'] = neutron_lib.constants +novaclient = mock.MagicMock() +sys.modules['novaclient'] = novaclient diff --git a/unit_tests/test_lib_charms_ovn_central.py b/unit_tests/test_lib_charms_ovn_central.py index ec5e042..5901442 100644 --- a/unit_tests/test_lib_charms_ovn_central.py +++ b/unit_tests/test_lib_charms_ovn_central.py @@ -14,7 +14,6 @@ import io import mock -import os import charms_openstack.test_utils as test_utils @@ -25,10 +24,10 @@ class Helper(test_utils.PatchHelper): def setUp(self): super().setUp() - self.patch_release(ovn_central.OVNCentralCharm.release) + self.patch_release(ovn_central.UssuriOVNCentralCharm.release) self.patch_object( ovn_central.charms_openstack.adapters, 'config_property') - self.target = ovn_central.OVNCentralCharm() + self.target = ovn_central.UssuriOVNCentralCharm() def patch_target(self, attr, return_value=None): mocked = mock.patch.object(self.target, attr) @@ -41,13 +40,16 @@ class Helper(test_utils.PatchHelper): class TestOVNCentralCharm(Helper): - def test_install(self): + def test_install_train(self): + self.patch_release(ovn_central.TrainOVNCentralCharm.release) + self.target = ovn_central.TrainOVNCentralCharm() self.patch_object(ovn_central.charms_openstack.charm.OpenStackCharm, 'install') self.patch_object(ovn_central.os.path, 'islink') self.islink.return_value = False self.patch_object(ovn_central.os, 'symlink') self.patch_target('configure_source') + self.patch_object(ovn_central.os, 'mkdir') self.target.install() calls = [] for service in ('openvswitch-switch', 'ovs-vswitchd', 'ovsdb-server', @@ -65,6 +67,32 @@ class TestOVNCentralCharm(Helper): self.install.assert_called_once_with() self.configure_source.assert_called_once_with() + def test_install(self): + self.patch_object(ovn_central.charms_openstack.charm.OpenStackCharm, + 'install') + self.patch_object(ovn_central.os.path, 'islink') + self.islink.return_value = False + self.patch_object(ovn_central.os, 'symlink') + self.patch_target('configure_source') + self.patch_object(ovn_central.os, 'mkdir') + self.target.install() + calls = [] + for service in (self.target.services[0], + 'ovn-ovsdb-server-nb', + 'ovn-ovsdb-server-sb',): + calls.append( + mock.call('/etc/systemd/system/{}.service'.format(service))) + self.islink.assert_has_calls(calls) + calls = [] + for service in (self.target.services[0], 'ovn-ovsdb-server-nb', + 'ovn-ovsdb-server-sb',): + calls.append( + mock.call('/dev/null', + '/etc/systemd/system/{}.service'.format(service))) + self.symlink.assert_has_calls(calls) + self.install.assert_called_once_with() + self.configure_source.assert_called_once_with() + def test__default_port_list(self): self.assertEquals( self.target._default_port_list(), @@ -155,20 +183,20 @@ class TestOVNCentralCharm(Helper): 'chain': 'fakechain', }] self.patch_object(ovn_central, 'ovn_charm') - self.ovn_charm.OVS_ETCDIR = '/etc/openvswitch' - self.ovn_charm.ovn_ca_cert.return_value = os.path.join( - self.ovn_charm.OVS_ETCDIR, 'ovn-central.crt') + self.ovn_charm.ovn_ca_cert.return_value = '/etc/ovn/ovn-central.crt' + self.patch_object(ovn_central.ovn, 'ovn_sysconfdir') + self.ovn_sysconfdir.return_value = '/etc/ovn' with mock.patch('builtins.open', create=True) as mocked_open: mocked_file = mock.MagicMock(spec=io.FileIO) mocked_open.return_value = mocked_file self.target.configure_cert = mock.MagicMock() self.target.configure_tls() mocked_open.assert_called_once_with( - '/etc/openvswitch/ovn-central.crt', 'w') + '/etc/ovn/ovn-central.crt', 'w') mocked_file.__enter__().write.assert_called_once_with( 'fakeca\nfakechain') self.target.configure_cert.assert_called_once_with( - self.ovn_charm.OVS_ETCDIR, + '/etc/ovn', 'fakecert', 'fakekey', cn='host') diff --git a/unit_tests/test_reactive_ovn_central_handlers.py b/unit_tests/test_reactive_ovn_central_handlers.py index 0514e21..e4a920b 100644 --- a/unit_tests/test_reactive_ovn_central_handlers.py +++ b/unit_tests/test_reactive_ovn_central_handlers.py @@ -25,6 +25,7 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks): defaults = [ 'charm.installed', 'config.changed', + 'charm.default-select-release', 'update-status', 'upgrade-charm', ] @@ -39,6 +40,7 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks): 'initialize_ovsdbs': ('run-default-update-status', 'leadership.set.nb_cid', 'leadership.set.sb_cid',), + 'maybe_do_upgrade': ('run-default-update-status',), 'publish_addr_to_clients': ('run-default-update-status',), 'render': ('run-default-update-status',), }, @@ -55,6 +57,8 @@ class TestRegisteredHooks(test_utils.TestRegisteredHooks): 'initialize_ovsdbs': ('charm.installed', 'leadership.is_leader', 'ovsdb-peer.connected',), + 'maybe_do_upgrade': ('config.changed.source', + 'ovsdb-peer.available',), 'publish_addr_to_clients': ('ovsdb-peer.available', 'leadership.set.nb_cid', 'leadership.set.sb_cid', @@ -76,7 +80,6 @@ class TestOvnCentralHandlers(test_utils.PatchHelper): def setUp(self): super().setUp() - # self.patch_release(octavia.OctaviaCharm.release) self.target = mock.MagicMock() self.patch_object(handlers.charm, 'provide_charm_instance', new=mock.MagicMock()) @@ -149,7 +152,8 @@ class TestOvnCentralHandlers(test_utils.PatchHelper): ovsdb_peer.db_sb_cluster_port, ovsdb_peer.db_nb_cluster_port,): ovsdb_peer.cluster_remote_addrs, - (ovsdb_peer.db_nb_port,): None, + (ovsdb_peer.db_nb_port, + ovsdb_peer.db_sb_admin_port,): None, }) self.target.assess_status.assert_called_once_with() self.target.configure_firewall.reset_mock() @@ -162,8 +166,8 @@ class TestOvnCentralHandlers(test_utils.PatchHelper): ovsdb_peer.db_sb_cluster_port, ovsdb_peer.db_nb_cluster_port,): ovsdb_peer.cluster_remote_addrs, - (ovsdb_peer.db_nb_port,): - ovsdb_cms.client_remote_addrs, + (ovsdb_peer.db_nb_port, + ovsdb_peer.db_sb_admin_port,): ovsdb_cms.client_remote_addrs, }) def test_publish_addr_to_clients(self): @@ -196,11 +200,11 @@ class TestOvnCentralHandlers(test_utils.PatchHelper): self.target.render_with_interfaces.assert_called_once_with( [ovsdb_peer]) self.target.join_cluster.assert_has_calls([ - mock.call('/var/lib/openvswitch/ovnnb_db.db', + mock.call('ovnnb_db.db', 'OVN_Northbound', connection_strs, connection_strs), - mock.call('/var/lib/openvswitch/ovnsb_db.db', + mock.call('ovnsb_db.db', 'OVN_Southbound', connection_strs, connection_strs),