charm-neutron-openvswitch/unit_tests/test_neutron_ovs_hooks.py
James Page 96c1788e94 Misc updates for DPDK support
Fix use of OVS DPDK context by direct use of methods on context
for OVS table values.

For modern OVS versions that require the PCI address of the
DPDK device for type=dpdk ports, use a hash of the PCI address
for the port name rather than the index of the PCI device in
the current list of devices to use; this is idempotent in the
event that the configuration changes and new devices appear
in the list of devices to use for DPDK.

Only set OVS table values if the value has changed; OVS will
try to re-allocate hugepage memory, irrespective as to whether
the table value actually changed.

Switch to using /run/libvirt-vhost-user for libvirt created DPDK
sockets, allowing libvirt to directly create the socket as part
of instance creation; Use systemd-tmpfiles to ensure that the
vhost-user subdirectory is re-created on boot with the correct
permissions.

Scan data-port and dpdk-bond-mappings for PCI devices to use
for DPDK to avoid having to replicate all PCI devices in data-port
configuration when DPDK bonds are in use.

Change-Id: I2964046bc8681fa870d61c6cd23b6ad6fee47bf4
2018-09-04 12:43:03 +01:00

193 lines
6.9 KiB
Python

# 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.
from mock import MagicMock, patch, mock_open
from test_utils import CharmTestCase
with patch('charmhelpers.core.hookenv.config') as config:
config.return_value = 'neutron'
import neutron_ovs_utils as utils
_reg = utils.register_configs
_map = utils.restart_map
utils.register_configs = MagicMock()
utils.restart_map = MagicMock()
import neutron_ovs_hooks as hooks
utils.register_configs = _reg
utils.restart_map = _map
TO_PATCH = [
'config',
'CONFIGS',
'get_shared_secret',
'log',
'relation_ids',
'relation_set',
'configure_ovs',
'configure_sriov',
'use_dvr',
'install_packages',
'purge_packages',
'enable_nova_metadata',
'enable_local_dhcp',
'install_tmpfilesd',
]
NEUTRON_CONF_DIR = "/etc/neutron"
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
class NeutronOVSHooksTests(CharmTestCase):
def setUp(self):
super(NeutronOVSHooksTests, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
hooks.hooks._config_save = False
def _call_hook(self, hookname):
hooks.hooks.execute([
'hooks/{}'.format(hookname)])
def test_install_hook(self):
self._call_hook('install')
self.install_packages.assert_called_with()
@patch.object(hooks, 'restart_map')
@patch.object(hooks, 'restart_on_change')
def test_migrate_ovs_default_file(self, mock_restart, mock_restart_map):
# Tests that the /etc/default/openvswitch-switch file is/isn't
# migrated on the upgrade-charm hook and that no restarts are
# attempted of the openvswitch-switch service.
tests = [
('package-provided-openvswitch-switch', True),
('16.07-dpdk-openvswitch-switch', True),
('16.10-openvswitch-switch', False),
]
for sample, should_migrate in tests:
self.CONFIGS.write.reset_mock()
with open('unit_tests/%s' % sample, 'r') as f:
content = f.read()
for ovs_default_exists in [False, True]:
if ovs_default_exists:
mock_restart_map.return_value = {utils.OVS_DEFAULT: {}}
else:
mock_restart_map.return_value = {}
with patch('builtins.open', mock_open(read_data=content),
create=True):
self.CONFIGS.write.reset_mock()
self._call_hook('upgrade-charm')
if should_migrate and ovs_default_exists:
self.CONFIGS.write.assert_called_with(
utils.OVS_DEFAULT)
else:
self.CONFIGS.write.assert_not_called()
self.assertEqual(0, mock_restart.call_count)
def test_config_changed_dvr(self):
self._call_hook('config-changed')
self.install_packages.assert_called_with()
self.assertTrue(self.CONFIGS.write_all.called)
self.configure_ovs.assert_called_with()
@patch.object(hooks, 'neutron_plugin_joined')
def test_neutron_plugin_api(self, _plugin_joined):
self.relation_ids.return_value = ['rid']
self._call_hook('neutron-plugin-api-relation-changed')
self.configure_ovs.assert_called_with()
self.assertTrue(self.CONFIGS.write_all.called)
_plugin_joined.assert_called_with(relation_id='rid')
self.install_packages.assert_called_with()
@patch.object(hooks, 'neutron_plugin_joined')
def test_neutron_plugin_api_nodvr(self, _plugin_joined):
self.use_dvr.return_value = False
self.relation_ids.return_value = ['rid']
self._call_hook('neutron-plugin-api-relation-changed')
self.configure_ovs.assert_called_with()
self.assertTrue(self.CONFIGS.write_all.called)
_plugin_joined.assert_called_with(relation_id='rid')
self.purge_packages.assert_called_with(['neutron-l3-agent'])
def test_neutron_plugin_joined_dvr_dhcp(self):
self.enable_nova_metadata.return_value = True
self.enable_local_dhcp.return_value = True
self.use_dvr.return_value = True
self.get_shared_secret.return_value = 'secret'
self._call_hook('neutron-plugin-relation-joined')
rel_data = {
'metadata-shared-secret': 'secret',
}
self.relation_set.assert_called_with(
relation_id=None,
**rel_data
)
self.assertTrue(self.install_packages.called)
def test_neutron_plugin_joined_dvr_nodhcp(self):
self.enable_nova_metadata.return_value = True
self.enable_local_dhcp.return_value = False
self.use_dvr.return_value = True
self.get_shared_secret.return_value = 'secret'
self._call_hook('neutron-plugin-relation-joined')
rel_data = {
'metadata-shared-secret': 'secret',
}
self.relation_set.assert_called_with(
relation_id=None,
**rel_data
)
self.purge_packages.assert_called_with(['neutron-dhcp-agent'])
self.assertFalse(self.install_packages.called)
def test_neutron_plugin_joined_nodvr_nodhcp(self):
self.enable_nova_metadata.return_value = False
self.enable_local_dhcp.return_value = False
self.use_dvr.return_value = False
self.get_shared_secret.return_value = 'secret'
self._call_hook('neutron-plugin-relation-joined')
rel_data = {
'metadata-shared-secret': None,
}
self.relation_set.assert_called_with(
relation_id=None,
**rel_data
)
self.purge_packages.assert_called_with(['neutron-dhcp-agent',
'neutron-metadata-agent'])
self.assertFalse(self.install_packages.called)
def test_amqp_joined(self):
self._call_hook('amqp-relation-joined')
self.relation_set.assert_called_with(
username='neutron',
vhost='openstack',
relation_id=None
)
def test_amqp_changed(self):
self.CONFIGS.complete_contexts.return_value = ['amqp']
self._call_hook('amqp-relation-changed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))
def test_amqp_departed(self):
self._call_hook('amqp-relation-departed')
self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF))