From 6d3fc4e7a2e0c27746fbc70522491d1474523b34 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Fri, 20 Jun 2014 11:02:09 +0100 Subject: [PATCH] Added openstack upgrade, unit tests and lint fixes --- .coveragerc | 7 ++++ hooks/neutron_api_hooks.py | 3 ++ hooks/neutron_api_utils.py | 36 +++++++++++++++- setup.cfg | 5 +++ unit_tests/test_neutron_api_context.py | 1 + unit_tests/test_neutron_api_hooks.py | 58 ++++++++++++++------------ unit_tests/test_neutron_api_utils.py | 40 +++++++++++++++--- 7 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 .coveragerc create mode 100644 setup.cfg diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..691f0ec4 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + if __name__ == .__main__.: + except UnregisteredHookError as e: +include= + hooks/neutron_* diff --git a/hooks/neutron_api_hooks.py b/hooks/neutron_api_hooks.py index d3d3ad7b..8f1706f1 100755 --- a/hooks/neutron_api_hooks.py +++ b/hooks/neutron_api_hooks.py @@ -43,6 +43,7 @@ from neutron_api_utils import ( NEUTRON_CONF, api_port, CLUSTER_RES, + do_openstack_upgrade, ) from charmhelpers.contrib.hahelpers.cluster import ( @@ -71,6 +72,8 @@ def install(): @restart_on_change(restart_map(), stopstart=True) def config_changed(): global CONFIGS + if openstack_upgrade_available('neutron-server'): + do_openstack_upgrade(CONFIGS) CONFIGS.write_all() for r_id in relation_ids('neutron-api'): neutron_api_relation_joined(rid=r_id) diff --git a/hooks/neutron_api_utils.py b/hooks/neutron_api_utils.py index 9b1c3715..a70cd6da 100644 --- a/hooks/neutron_api_utils.py +++ b/hooks/neutron_api_utils.py @@ -8,12 +8,15 @@ from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.utils import ( os_release, + get_os_codename_install_source, + configure_installation_source, ) from charmhelpers.core.hookenv import ( config, + log, ) - +from charmhelpers.fetch import apt_update, apt_install, apt_upgrade import neutron_api_context TEMPLATES = 'templates/' @@ -160,3 +163,34 @@ def keystone_ca_cert_b64(): return None with open(CA_CERT_PATH) as _in: return b64encode(_in.read()) + + +def do_openstack_upgrade(configs): + """ + Perform an upgrade. Takes care of upgrading packages, rewriting + configs, database migrations and potentially any other post-upgrade + actions. + + :param configs: The charms main OSConfigRenderer object. + """ + new_src = config('openstack-origin') + new_os_rel = get_os_codename_install_source(new_src) + + log('Performing OpenStack upgrade to %s.' % (new_os_rel)) + + configure_installation_source(new_src) + dpkg_opts = [ + '--option', 'Dpkg::Options::=--force-confnew', + '--option', 'Dpkg::Options::=--force-confdef', + ] + apt_update(fatal=True) + apt_upgrade(options=dpkg_opts, fatal=True, dist=True) + pkgs = determine_packages() + # Sort packages just to make unit tests easier + pkgs.sort() + apt_install(packages=pkgs, + options=dpkg_opts, + fatal=True) + + # set CONFIGS to load templates from new release + configs.set_release(openstack_release=new_os_rel) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..72c20b20 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[nosetests] +verbosity=1 +with-coverage=1 +cover-erase=1 +cover-package=hooks diff --git a/unit_tests/test_neutron_api_context.py b/unit_tests/test_neutron_api_context.py index 72cea8c0..1c3a2ee3 100644 --- a/unit_tests/test_neutron_api_context.py +++ b/unit_tests/test_neutron_api_context.py @@ -46,6 +46,7 @@ class IdentityServiceContext(CharmTestCase): ids_ctxt = context.IdentityServiceContext() self.assertEquals(ids_ctxt(), None) + class NeutronAPIContextsTest(CharmTestCase): def setUp(self): diff --git a/unit_tests/test_neutron_api_hooks.py b/unit_tests/test_neutron_api_hooks.py index a48d3a24..dfb2a64a 100644 --- a/unit_tests/test_neutron_api_hooks.py +++ b/unit_tests/test_neutron_api_hooks.py @@ -21,7 +21,6 @@ TO_PATCH = [ 'api_port', 'apt_update', 'apt_install', -# 'charm_dir', 'canonical_url', 'config', 'CONFIGS', @@ -29,6 +28,7 @@ TO_PATCH = [ 'determine_endpoints', 'determine_packages', 'determine_ports', + 'do_openstack_upgrade', 'execd_preinstall', 'is_leader', 'is_relation_made', @@ -36,6 +36,7 @@ TO_PATCH = [ 'network_manager', 'neutron_plugin_attribute', 'open_port', + 'openstack_upgrade_available', 'relation_get', 'relation_ids', 'relation_set', @@ -48,6 +49,7 @@ NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR from random import randrange + class NeutronAPIHooksTests(CharmTestCase): def setUp(self): @@ -57,12 +59,9 @@ class NeutronAPIHooksTests(CharmTestCase): self.relation_get.side_effect = self.test_relation.get self.test_config.set('openstack-origin', 'distro') self.test_config.set('neutron-plugin', 'ovs') -# self.test_config.set('neutron-external-network', 'ext_net') -# self.lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} -# self.charm_dir.return_value = '/var/lib/juju/charms/neutron/charm' def _fake_relids(self, rel_name): - return [ randrange(100) for _count in range(2) ] + return [randrange(100) for _count in range(2)] def _call_hook(self, hookname): hooks.hooks.execute([ @@ -71,7 +70,7 @@ class NeutronAPIHooksTests(CharmTestCase): def test_install_hook(self): _pkgs = ['foo', 'bar'] _ports = [80, 81, 82] - _port_calls = [ call(port) for port in _ports ] + _port_calls = [call(port) for port in _ports] self.determine_packages.return_value = _pkgs self.determine_ports.return_value = _ports self._call_hook('install') @@ -86,9 +85,11 @@ class NeutronAPIHooksTests(CharmTestCase): self.assertTrue(self.execd_preinstall.called) def test_config_changed(self): + self.openstack_upgrade_available.return_value = True self.relation_ids.side_effect = self._fake_relids _n_api_rel_joined = self.patch('neutron_api_relation_joined') - _n_plugin_api_rel_joined = self.patch('neutron_plugin_api_relation_joined') + _n_plugin_api_rel_joined =\ + self.patch('neutron_plugin_api_relation_joined') _amqp_rel_joined = self.patch('amqp_joined') _id_rel_joined = self.patch('identity_joined') self._call_hook('config-changed') @@ -97,6 +98,7 @@ class NeutronAPIHooksTests(CharmTestCase): self.assertTrue(_amqp_rel_joined.called) self.assertTrue(_id_rel_joined.called) self.assertTrue(self.CONFIGS.write_all.called) + self.assertTrue(self.do_openstack_upgrade.called) def test_amqp_joined(self): self._call_hook('amqp-relation-joined') @@ -200,7 +202,7 @@ class NeutronAPIHooksTests(CharmTestCase): self._call_hook('identity-service-relation-changed') self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF)) self.assertTrue(_api_rel_joined.called) - + @patch.object(hooks, '_get_keystone_info') def test_neutron_api_relation_no_id_joined(self, _get_ks_info): _get_ks_info.return_value = None @@ -239,11 +241,11 @@ class NeutronAPIHooksTests(CharmTestCase): @patch.object(hooks, '_get_keystone_info') def test_neutron_api_relation_joined(self, _get_ks_info): _ks_info = { - 'service_tenant': 'bob', - 'service_username': 'bob', - 'service_password': 'bob', - 'auth_url': 'http://127.0.0.2', - } + 'service_tenant': 'bob', + 'service_username': 'bob', + 'service_password': 'bob', + 'auth_url': 'http://127.0.0.2', + } _get_ks_info.return_value = _ks_info manager = 'neutron' host = 'http://127.0.0.1' @@ -292,25 +294,29 @@ class NeutronAPIHooksTests(CharmTestCase): @patch.object(hooks, 'get_hacluster_config') def test_ha_joined(self, _get_ha_config): _ha_config = { - 'vip': '10.0.0.1', - 'vip_cidr': '24', - 'vip_iface': 'eth0', - 'ha-bindiface': 'eth1', - 'ha-mcastport': '5405', - } + 'vip': '10.0.0.1', + 'vip_cidr': '24', + 'vip_iface': 'eth0', + 'ha-bindiface': 'eth1', + 'ha-mcastport': '5405', + } vip_params = 'params ip="%s" cidr_netmask="%s" nic="%s"' % \ - (_ha_config['vip'], _ha_config['vip_cidr'], _ha_config['vip_iface']) - + (_ha_config['vip'], _ha_config['vip_cidr'], + _ha_config['vip_iface']) _get_ha_config.return_value = _ha_config _relation_data = { 'init_services': {'res_neutron_haproxy': 'haproxy'}, 'corosync_bindiface': _ha_config['ha-bindiface'], 'corosync_mcastport': _ha_config['ha-mcastport'], - 'resources': {'res_neutron_vip': 'ocf:heartbeat:IPaddr2', - 'res_neutron_haproxy': 'lsb:haproxy'}, - 'resource_params': { 'res_neutron_vip': vip_params, - 'res_neutron_haproxy': 'op monitor interval="5s"'}, - 'clones': { 'cl_nova_haproxy': 'res_neutron_haproxy' } + 'resources': { + 'res_neutron_vip': 'ocf:heartbeat:IPaddr2', + 'res_neutron_haproxy': 'lsb:haproxy' + }, + 'resource_params': { + 'res_neutron_vip': vip_params, + 'res_neutron_haproxy': 'op monitor interval="5s"' + }, + 'clones': {'cl_nova_haproxy': 'res_neutron_haproxy'} } self._call_hook('ha-relation-joined') self.relation_set.assert_called_with( diff --git a/unit_tests/test_neutron_api_utils.py b/unit_tests/test_neutron_api_utils.py index 7f6929cc..e27ba476 100644 --- a/unit_tests/test_neutron_api_utils.py +++ b/unit_tests/test_neutron_api_utils.py @@ -1,5 +1,5 @@ -from mock import MagicMock, call, patch +from mock import MagicMock, patch from collections import OrderedDict import charmhelpers.contrib.openstack.templating as templating @@ -16,11 +16,18 @@ import charmhelpers.core.hookenv as hookenv TO_PATCH = [ + 'apt_install', + 'apt_update', + 'apt_upgrade', 'b64encode', 'config', + 'configure_installation_source', + 'get_os_codename_install_source', + 'log', 'neutron_plugin_attribute', ] + def _mock_npa(plugin, attr, net_manager=None): plugins = { 'ovs': { @@ -36,9 +43,8 @@ def _mock_npa(plugin, attr, net_manager=None): } return plugins[plugin][attr] + class TestNeutronAPIUtils(CharmTestCase): - - def setUp(self): super(TestNeutronAPIUtils, self).setUp(nutils, TO_PATCH) self.config.side_effect = self.test_config.get @@ -57,7 +63,7 @@ class TestNeutronAPIUtils(CharmTestCase): test_url = 'http://127.0.0.1' endpoints = nutils.determine_endpoints(test_url) neutron_url = '%s:%s' % (test_url, - nutils.api_port('neutron-server')) + nutils.api_port('neutron-server')) expect = { 'quantum_service': 'quantum', 'quantum_region': 'region101', @@ -125,5 +131,29 @@ class TestNeutronAPIUtils(CharmTestCase): def test_keystone_ca_cert_b64(self, _isfile): _isfile.return_value = True with patch_open() as (_open, _file): - cert = nutils.keystone_ca_cert_b64() + nutils.keystone_ca_cert_b64() self.assertTrue(self.b64encode.called) + + def test_do_openstack_upgrade(self): + self.config.side_effect = self.test_config.get + self.test_config.set('openstack-origin', 'cloud:precise-havana') + self.get_os_codename_install_source.return_value = 'havana' + configs = MagicMock() + nutils.do_openstack_upgrade(configs) + configs.set_release.assert_called_with(openstack_release='havana') + self.log.assert_called() + self.apt_update.assert_called_with(fatal=True) + dpkg_opts = [ + '--option', 'Dpkg::Options::=--force-confnew', + '--option', 'Dpkg::Options::=--force-confdef', + ] + pkgs = nutils.BASE_PACKAGES + pkgs.sort() + self.apt_install.assert_called_with( + options=dpkg_opts, + packages=pkgs, + fatal=True + ) + self.configure_installation_source.assert_called_with( + 'cloud:precise-havana' + )