charm-ovn-central/unit_tests/test_lib_charms_ovn_central.py
Frode Nordahl e7e01ecb55
Enable Ussuri and Focal
Add tactical workaround for LP: #1864640

Keep Focal as dev-bundle until neighbouring charms are enabled.

To allow running lint on a Python 3.8 system (Focal), unpin
flake8.

Clean up test-requirements.txt and mock out dependencies rather
than installing them in the unit test virtualenv.

Change-Id: I9a4a8dc422b8bdcf61b457f7b3ba828008c415d8
2020-03-03 13:40:04 +01:00

362 lines
16 KiB
Python

# 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 io
import mock
import charms_openstack.test_utils as test_utils
import charm.openstack.ovn_central as ovn_central
class Helper(test_utils.PatchHelper):
def setUp(self):
super().setUp()
self.patch_release(ovn_central.UssuriOVNCentralCharm.release)
self.patch_object(
ovn_central.charms_openstack.adapters, 'config_property')
self.target = ovn_central.UssuriOVNCentralCharm()
def patch_target(self, attr, return_value=None):
mocked = mock.patch.object(self.target, attr)
self._patches[attr] = mocked
started = mocked.start()
started.return_value = return_value
self._patches_start[attr] = started
setattr(self, attr, started)
class TestOVNCentralCharm(Helper):
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',
self.target.services[0],):
calls.append(
mock.call('/etc/systemd/system/{}.service'.format(service)))
self.islink.assert_has_calls(calls)
calls = []
for service in ('openvswitch-switch', 'ovs-vswitchd', 'ovsdb-server',
self.target.services[0],):
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_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(),
[6641, 6642])
def test_ports_to_check(self):
self.target._default_port_list = mock.MagicMock()
self.target.ports_to_check()
self.target._default_port_list.assert_called_once_with()
def test_custom_assess_status_last_check(self):
self.patch_object(ovn_central.ovn, 'is_cluster_leader')
self.patch_object(ovn_central.ovn, 'is_northd_active')
self.is_cluster_leader.side_effect = [False, False]
self.is_northd_active.return_value = False
self.assertEquals(
self.target.custom_assess_status_last_check(),
(None, None))
self.is_cluster_leader.assert_has_calls([
mock.call('ovnnb_db'),
mock.call('ovnsb_db'),
])
self.is_cluster_leader.side_effect = [True, False]
self.assertEquals(
self.target.custom_assess_status_last_check(),
('active', 'Unit is ready (leader: ovnnb_db)'))
self.is_cluster_leader.side_effect = [True, True]
self.assertEquals(
self.target.custom_assess_status_last_check(),
('active', 'Unit is ready (leader: ovnnb_db, ovnsb_db)'))
self.is_cluster_leader.side_effect = [False, False]
self.is_northd_active.return_value = True
self.assertEquals(
self.target.custom_assess_status_last_check(),
('active', 'Unit is ready (northd: active)'))
self.is_cluster_leader.side_effect = [True, False]
self.assertEquals(
self.target.custom_assess_status_last_check(),
('active', 'Unit is ready (leader: ovnnb_db northd: active)'))
self.is_cluster_leader.side_effect = [True, True]
self.assertEquals(
self.target.custom_assess_status_last_check(),
('active',
'Unit is ready (leader: ovnnb_db, ovnsb_db northd: active)'))
def test_enable_services(self):
self.patch_object(ovn_central.ch_core.host, 'service_resume')
self.target.check_if_paused = mock.MagicMock()
self.target.check_if_paused.return_value = ('status', 'message')
self.target.enable_services()
self.target.check_if_paused.assert_called_once_with()
self.assertFalse(self.service_resume.called)
self.target.check_if_paused.return_value = (None, None)
self.target.enable_services()
calls = []
for service in self.target.services:
calls.append(mock.call(service))
self.service_resume.assert_has_calls(calls)
def test_run(self):
self.patch_object(ovn_central.subprocess, 'run')
self.patch_object(ovn_central.ch_core.hookenv, 'log')
self.target.run('some', 'args')
self.run.assert_called_once_with(
('some', 'args'),
stdout=ovn_central.subprocess.PIPE,
stderr=ovn_central.subprocess.STDOUT,
check=True,
universal_newlines=True)
def test_join_cluster(self):
self.patch_target('run')
self.target.join_cluster('/a/db.file',
'aSchema',
['ssl:a.b.c.d:1234'],
['ssl:e.f.g.h:1234', 'ssl:i.j.k.l:1234'])
self.run.assert_called_once_with(
'ovsdb-tool', 'join-cluster', '/a/db.file', 'aSchema',
'ssl:a.b.c.d:1234', 'ssl:e.f.g.h:1234', 'ssl:i.j.k.l:1234')
def test_configure_tls(self):
self.patch_target('get_certs_and_keys')
self.get_certs_and_keys.return_value = [{
'cert': 'fakecert',
'key': 'fakekey',
'cn': 'fakecn',
'ca': 'fakeca',
'chain': 'fakechain',
}]
self.patch_object(ovn_central, 'ovn_charm')
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/ovn/ovn-central.crt', 'w')
mocked_file.__enter__().write.assert_called_once_with(
'fakeca\nfakechain')
self.target.configure_cert.assert_called_once_with(
'/etc/ovn',
'fakecert',
'fakekey',
cn='host')
def test_configure_ovn_listener(self):
self.patch_object(ovn_central.ovn, 'is_cluster_leader')
self.patch_object(ovn_central.ovn, 'SimpleOVSDB')
self.patch_target('run')
port_map = {6641: {'inactivity_probe': 42},
6642: {'role': 'ovn-controller'}}
self.is_cluster_leader.return_value = False
self.target.configure_ovn_listener('nb', port_map)
self.assertFalse(self.SimpleOVSDB.called)
self.is_cluster_leader.return_value = True
connections = mock.MagicMock()
connections.find.side_effect = [
[],
[{'_uuid': 'fake-uuid'}],
[],
[{'_uuid': 'fake-uuid'}],
]
self.SimpleOVSDB.return_value = connections
self.target.configure_ovn_listener('nb', port_map)
self.run.assert_has_calls([
mock.call('ovn-nbctl', '--', '--id=@connection', 'create',
'connection', 'target="pssl:6641"', '--', 'add',
'NB_Global', '.', 'connections', '@connection'),
mock.call('ovn-nbctl', '--', '--id=@connection', 'create',
'connection', 'target="pssl:6642"', '--', 'add',
'NB_Global', '.', 'connections', '@connection'),
])
connections.set.assert_has_calls([
mock.call('fake-uuid', 'inactivity_probe', 42),
mock.call('fake-uuid', 'role', 'ovn-controller')
])
def test_configure_ovn(self):
self.patch_target('config')
self.config.__getitem__.return_value = 42
self.patch_target('configure_ovn_listener')
self.target.configure_ovn(1, 2, 3)
self.config.__getitem__.assert_called_once_with(
'ovsdb-server-inactivity-probe')
self.configure_ovn_listener.assert_has_calls([
mock.call('nb', {1: {'inactivity_probe': 42000}}),
mock.call('sb', {2: {'role': 'ovn-controller',
'inactivity_probe': 42000}}),
mock.call('sb', {3: {'inactivity_probe': 42000}}),
])
def test_initialize_firewall(self):
self.patch_object(ovn_central, 'ch_ufw')
self.target.initialize_firewall()
self.ch_ufw.enable.assert_called_once_with()
self.ch_ufw.default_policy.assert_has_calls([
mock.call('allow', 'incoming'),
mock.call('allow', 'outgoing'),
mock.call('allow', 'routed'),
])
def test_configure_firewall(self):
self.patch_object(ovn_central, 'ch_ufw')
self.ch_ufw.status.return_value = [
(42, {
'action': 'allow in',
'from': 'q.r.s.t',
'comment': 'charm-ovn-central'}),
(51, {
'action': 'reject in',
'from': 'any',
'comment': 'charm-ovn-central'}),
]
self.target.configure_firewall({
(1, 2, 3, 4,): ('a.b.c.d', 'e.f.g.h',),
(1, 2,): ('i.j.k.l', 'm.n.o.p',),
})
self.ch_ufw.modify_access.assert_has_calls([
mock.call(src=None, dst='any', port=1,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=2,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=3,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=4,
proto='tcp', action='reject',
comment='charm-ovn-central'),
], any_order=True)
self.ch_ufw.modify_access.assert_has_calls([
mock.call('a.b.c.d', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=3, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=3, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=4, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=4, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('i.j.k.l', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('m.n.o.p', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('i.j.k.l', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('m.n.o.p', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
], any_order=True)
self.ch_ufw.modify_access.assert_has_calls([
mock.call(None, dst=None, action='delete', index=42)
])
self.ch_ufw.reset_mock()
self.target.configure_firewall({
(1, 2, 3, 4,): ('a.b.c.d', 'e.f.g.h',),
(1, 2, 5,): None,
})
self.ch_ufw.modify_access.assert_has_calls([
mock.call(src=None, dst='any', port=1,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=2,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=3,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=4,
proto='tcp', action='reject',
comment='charm-ovn-central'),
mock.call(src=None, dst='any', port=5,
proto='tcp', action='reject',
comment='charm-ovn-central'),
], any_order=True)
self.ch_ufw.modify_access.assert_has_calls([
mock.call('a.b.c.d', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=1, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=2, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=3, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=3, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('a.b.c.d', port=4, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
mock.call('e.f.g.h', port=4, proto='tcp', action='allow',
prepend=True, comment='charm-ovn-central'),
], any_order=True)
self.ch_ufw.modify_access.assert_has_calls([
mock.call(None, dst=None, action='delete', index=42)
])