From d2fa08858ebeba0878311d48793e0ed9a045b7b7 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Mon, 2 Feb 2015 13:32:22 +0000 Subject: [PATCH 01/12] Add dvr support --- hooks/quantum_contexts.py | 17 ++++++++++++----- hooks/quantum_utils.py | 3 +++ templates/juno/l3_agent.ini | 25 +++++++++++++++++++++++++ templates/juno/ml2_conf.ini | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 templates/juno/l3_agent.ini create mode 100644 templates/juno/ml2_conf.ini diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 508bdac1..78f4f519 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -111,6 +111,7 @@ def _neutron_api_settings(): ''' neutron_settings = { 'l2_population': False, + 'enable_dvr': False, 'overlay_network_type': 'gre', } @@ -119,10 +120,11 @@ def _neutron_api_settings(): rdata = relation_get(rid=rid, unit=unit) if 'l2-population' not in rdata: continue - neutron_settings = { - 'l2_population': rdata['l2-population'], - 'overlay_network_type': rdata['overlay-network-type'], - } + neutron_settings['l2_population'] = rdata['l2-population'] + if 'overlay-network-type' in rdata: + neutron_settings['overlay_network_type'] = rdata['overlay-network-type'] + if 'enable-dvr' in rdata: + neutron_settings['enable_dvr'] = rdata['enable-dvr'] return neutron_settings return neutron_settings @@ -158,6 +160,7 @@ class NetworkServiceContext(OSContextGenerator): class L3AgentContext(OSContextGenerator): def __call__(self): + neutron_api_settings = _neutron_api_settings() ctxt = {} if config('run-internal-router') == 'leader': ctxt['handle_internal_only_router'] = eligible_leader(None) @@ -170,9 +173,12 @@ class L3AgentContext(OSContextGenerator): if config('external-network-id'): ctxt['ext_net_id'] = config('external-network-id') - if config('plugin'): ctxt['plugin'] = config('plugin') + if neutron_api_settings['enable_dvr'] == 'True': + ctxt['agent_mode'] = 'dvr_snat' + else: + ctxt['agent_mode'] = 'legacy' return ctxt @@ -243,6 +249,7 @@ class QuantumGatewayContext(OSContextGenerator): 'verbose': config('verbose'), 'instance_mtu': config('instance-mtu'), 'l2_population': neutron_api_settings['l2_population'], + 'enable_dvr': neutron_api_settings['enable_dvr'], 'overlay_network_type': neutron_api_settings['overlay_network_type'], } diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 857f8d9a..eac92d50 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -413,6 +413,9 @@ def register_configs(): drop_config = NEUTRON_ML2_PLUGIN_CONF if release >= 'icehouse': drop_config = NEUTRON_OVS_PLUGIN_CONF + # NOTE(gnuoy) neutron-vpn-agent supercedes l3-agent for icehouse + CONFIG_FILES[name][plugin][NEUTRON_L3_AGENT_CONF]['services'] = \ + ['neutron-vpn-agent'] if drop_config in CONFIG_FILES[name][plugin]: CONFIG_FILES[name][plugin].pop(drop_config) diff --git a/templates/juno/l3_agent.ini b/templates/juno/l3_agent.ini new file mode 100644 index 00000000..6fd6cd4b --- /dev/null +++ b/templates/juno/l3_agent.ini @@ -0,0 +1,25 @@ +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +[DEFAULT] +interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver +auth_url = {{ service_protocol }}://{{ keystone_host }}:{{ service_port }}/v2.0 +auth_region = {{ region }} +admin_tenant_name = {{ service_tenant }} +admin_user = {{ service_username }} +admin_password = {{ service_password }} +root_helper = sudo /usr/bin/neutron-rootwrap /etc/neutron/rootwrap.conf +handle_internal_only_routers = {{ handle_internal_only_router }} +{% if plugin == 'n1kv' %} +l3_agent_manager = neutron.agent.l3_agent.L3NATAgentWithStateReport +external_network_bridge = br-int +ovs_use_veth = False +use_namespaces = True +{% else %} +ovs_use_veth = True +{% endif %} +{% if ext_net_id -%} +gateway_external_network_id = {{ ext_net_id }} +{% endif -%} +agent_mode = {{ agent_mode }} diff --git a/templates/juno/ml2_conf.ini b/templates/juno/ml2_conf.ini new file mode 100644 index 00000000..5354b4c2 --- /dev/null +++ b/templates/juno/ml2_conf.ini @@ -0,0 +1,33 @@ +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +[ml2] +type_drivers = gre,vxlan,vlan,flat +tenant_network_types = gre,vxlan,vlan,flat +mechanism_drivers = openvswitch,l2population + +[ml2_type_gre] +tunnel_id_ranges = 1:1000 + +[ml2_type_vxlan] +vni_ranges = 1001:2000 + +[ml2_type_vlan] +network_vlan_ranges = physnet1:1000:2000 + +[ml2_type_flat] +flat_networks = physnet1 + +[ovs] +enable_tunneling = True +local_ip = {{ local_ip }} +bridge_mappings = physnet1:br-data + +[agent] +tunnel_types = {{ overlay_network_type }} +l2_population = {{ l2_population }} +enable_distributed_routing = {{ enable_dvr }} + +[securitygroup] +firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver From 06f8796f935fd7f44dd1672f5a3626c2c4d718ba Mon Sep 17 00:00:00 2001 From: Liam Young Date: Wed, 4 Feb 2015 16:21:36 +0000 Subject: [PATCH 02/12] Restart neutron-openvswitch-agent on changes to relevant files --- hooks/quantum_utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index eac92d50..b77e6a7e 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -307,7 +307,8 @@ NEUTRON_OVS_CONFIG_FILES = { 'neutron-metering-agent', 'neutron-lbaas-agent', 'neutron-plugin-vpn-agent', - 'neutron-vpn-agent'] + 'neutron-vpn-agent', + 'neutron-openvswitch-agent'] }, NEUTRON_L3_AGENT_CONF: { 'hook_contexts': [NetworkServiceContext(), @@ -339,7 +340,8 @@ NEUTRON_OVS_CONFIG_FILES = { }, NEUTRON_ML2_PLUGIN_CONF: { 'hook_contexts': [QuantumGatewayContext()], - 'services': ['neutron-plugin-openvswitch-agent'] + 'services': ['neutron-plugin-openvswitch-agent', + 'neutron-openvswitch-agent'] }, EXT_PORT_CONF: { 'hook_contexts': [ExternalPortContext()], From f24dc04e46099e95532cd8ba1916a123856083be Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 19 Feb 2015 16:16:07 +0000 Subject: [PATCH 03/12] Fix unit tests --- hooks/quantum_contexts.py | 3 ++- hooks/quantum_utils.py | 3 +-- unit_tests/test_quantum_contexts.py | 19 +++++++++++++------ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 78f4f519..5cb55fff 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -122,7 +122,8 @@ def _neutron_api_settings(): continue neutron_settings['l2_population'] = rdata['l2-population'] if 'overlay-network-type' in rdata: - neutron_settings['overlay_network_type'] = rdata['overlay-network-type'] + neutron_settings['overlay_network_type'] = \ + rdata['overlay-network-type'] if 'enable-dvr' in rdata: neutron_settings['enable_dvr'] = rdata['enable-dvr'] return neutron_settings diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index b77e6a7e..4c9fe9d7 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -307,8 +307,7 @@ NEUTRON_OVS_CONFIG_FILES = { 'neutron-metering-agent', 'neutron-lbaas-agent', 'neutron-plugin-vpn-agent', - 'neutron-vpn-agent', - 'neutron-openvswitch-agent'] + 'neutron-vpn-agent'] }, NEUTRON_L3_AGENT_CONF: { 'hook_contexts': [NetworkServiceContext(), diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index 88a961ff..d1c6a1f1 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -192,7 +192,8 @@ class TestL3AgentContext(CharmTestCase): self.test_config.set('external-network-id', '') self.eligible_leader.return_value = False self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': False, + {'agent_mode': 'legacy', + 'handle_internal_only_router': False, 'plugin': 'ovs'}) def test_hior_leader(self): @@ -200,7 +201,8 @@ class TestL3AgentContext(CharmTestCase): self.test_config.set('external-network-id', 'netid') self.eligible_leader.return_value = True self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': True, + {'agent_mode': 'legacy', + 'handle_internal_only_router': True, 'ext_net_id': 'netid', 'plugin': 'ovs'}) @@ -209,7 +211,8 @@ class TestL3AgentContext(CharmTestCase): self.test_config.set('external-network-id', 'netid') self.eligible_leader.return_value = True self.assertEquals(quantum_contexts.L3AgentContext()(), - {'handle_internal_only_router': True, + {'agent_mode': 'legacy', + 'handle_internal_only_router': True, 'ext_net_id': 'netid', 'plugin': 'ovs'}) @@ -233,6 +236,7 @@ class TestQuantumGatewayContext(CharmTestCase): _secret.return_value = 'testsecret' self.assertEquals(quantum_contexts.QuantumGatewayContext()(), { 'shared_secret': 'testsecret', + 'enable_dvr': False, 'local_ip': '10.5.0.1', 'instance_mtu': 1420, 'core_plugin': "quantum.plugins.openvswitch.ovs_quantum_plugin." @@ -370,7 +374,8 @@ class TestMisc(CharmTestCase): 'overlay-network-type': 'gre', }) self.relation_get.side_effect = self.test_relation.get self.assertEquals(quantum_contexts._neutron_api_settings(), - {'l2_population': True, + {'enable_dvr': False, + 'l2_population': True, 'overlay_network_type': 'gre'}) def test_neutron_api_settings2(self): @@ -380,11 +385,13 @@ class TestMisc(CharmTestCase): 'overlay-network-type': 'gre', }) self.relation_get.side_effect = self.test_relation.get self.assertEquals(quantum_contexts._neutron_api_settings(), - {'l2_population': True, + {'enable_dvr': False, + 'l2_population': True, 'overlay_network_type': 'gre'}) def test_neutron_api_settings_no_apiplugin(self): self.relation_ids.return_value = [] self.assertEquals(quantum_contexts._neutron_api_settings(), - {'l2_population': False, + {'enable_dvr': False, + 'l2_population': False, 'overlay_network_type': 'gre', }) From 6d74a7126dad72d78a8d51756d3c2a910bfe2f38 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Fri, 20 Feb 2015 11:50:46 +0000 Subject: [PATCH 04/12] Add vrrp ha support --- hooks/quantum_contexts.py | 4 ++++ hooks/quantum_hooks.py | 11 +++++++++-- hooks/quantum_utils.py | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 5cb55fff..51971353 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -112,6 +112,7 @@ def _neutron_api_settings(): neutron_settings = { 'l2_population': False, 'enable_dvr': False, + 'enable_l3ha': False, 'overlay_network_type': 'gre', } @@ -126,6 +127,8 @@ def _neutron_api_settings(): rdata['overlay-network-type'] if 'enable-dvr' in rdata: neutron_settings['enable_dvr'] = rdata['enable-dvr'] + if 'enable-l3ha' in rdata: + neutron_settings['enable_l3ha'] = rdata['enable-l3ha'] return neutron_settings return neutron_settings @@ -251,6 +254,7 @@ class QuantumGatewayContext(OSContextGenerator): 'instance_mtu': config('instance-mtu'), 'l2_population': neutron_api_settings['l2_population'], 'enable_dvr': neutron_api_settings['enable_dvr'], + 'enable_l3ha': neutron_api_settings['enable_l3ha'], 'overlay_network_type': neutron_api_settings['overlay_network_type'], } diff --git a/hooks/quantum_hooks.py b/hooks/quantum_hooks.py index e8b74861..722998ec 100755 --- a/hooks/quantum_hooks.py +++ b/hooks/quantum_hooks.py @@ -193,13 +193,20 @@ def amqp_departed(): 'pgsql-db-relation-changed', 'amqp-relation-changed', 'cluster-relation-changed', - 'cluster-relation-joined', - 'neutron-plugin-api-relation-changed') + 'cluster-relation-joined') @restart_on_change(restart_map()) def db_amqp_changed(): CONFIGS.write_all() +@hooks.hook('neutron-plugin-api-relation-changed') +@restart_on_change(restart_map()) +def neutron_plugin_api_changed(): + apt_install(filter_installed_packages(get_packages()), + fatal=True) + CONFIGS.write_all() + + @hooks.hook('quantum-network-service-relation-changed') @restart_on_change(restart_map()) def nm_changed(): diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 4c9fe9d7..9a768617 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -197,6 +197,8 @@ def get_packages(): packages.append('openswan') if source >= 'kilo': packages.append('python-neutron-fwaas') + if QuantumGatewayContext()()['enable_l3ha']: + packages.append('keepalived') return packages From a5af21f87747010f72f0356ab8af4041739b2589 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Fri, 20 Feb 2015 13:11:12 +0000 Subject: [PATCH 05/12] Fix bug blocking install --- hooks/quantum_contexts.py | 16 ++++++++-------- hooks/quantum_utils.py | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 51971353..226fc743 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -104,7 +104,7 @@ def core_plugin(): return CORE_PLUGIN[networking_name()][plugin] -def _neutron_api_settings(): +def neutron_api_settings(): ''' Inspects current neutron-plugin-api relation for neutron settings. Return defaults if it is not present @@ -164,7 +164,7 @@ class NetworkServiceContext(OSContextGenerator): class L3AgentContext(OSContextGenerator): def __call__(self): - neutron_api_settings = _neutron_api_settings() + api_settings = neutron_api_settings() ctxt = {} if config('run-internal-router') == 'leader': ctxt['handle_internal_only_router'] = eligible_leader(None) @@ -179,7 +179,7 @@ class L3AgentContext(OSContextGenerator): ctxt['ext_net_id'] = config('external-network-id') if config('plugin'): ctxt['plugin'] = config('plugin') - if neutron_api_settings['enable_dvr'] == 'True': + if api_settings['enable_dvr'] == 'True': ctxt['agent_mode'] = 'dvr_snat' else: ctxt['agent_mode'] = 'legacy' @@ -241,7 +241,7 @@ class DataPortContext(NeutronPortContext): class QuantumGatewayContext(OSContextGenerator): def __call__(self): - neutron_api_settings = _neutron_api_settings() + api_settings = neutron_api_settings() ctxt = { 'shared_secret': get_shared_secret(), 'local_ip': @@ -252,11 +252,11 @@ class QuantumGatewayContext(OSContextGenerator): 'debug': config('debug'), 'verbose': config('verbose'), 'instance_mtu': config('instance-mtu'), - 'l2_population': neutron_api_settings['l2_population'], - 'enable_dvr': neutron_api_settings['enable_dvr'], - 'enable_l3ha': neutron_api_settings['enable_l3ha'], + 'l2_population': api_settings['l2_population'], + 'enable_dvr': api_settings['enable_dvr'], + 'enable_l3ha': api_settings['enable_l3ha'], 'overlay_network_type': - neutron_api_settings['overlay_network_type'], + api_settings['overlay_network_type'], } return ctxt diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 9a768617..308e9e5d 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -54,7 +54,8 @@ from quantum_contexts import ( L3AgentContext, ExternalPortContext, DataPortContext, - remap_plugin + remap_plugin, + neutron_api_settings, ) from copy import deepcopy @@ -197,7 +198,7 @@ def get_packages(): packages.append('openswan') if source >= 'kilo': packages.append('python-neutron-fwaas') - if QuantumGatewayContext()()['enable_l3ha']: + if neutron_api_settings()['enable_l3ha']: packages.append('keepalived') return packages From 1f228745aab74e4a62e474c29d7573d7ea08cf97 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 26 Feb 2015 09:09:27 +0000 Subject: [PATCH 06/12] Unit tests and lint fixes --- .../charmhelpers/contrib/charmsupport/nrpe.py | 48 ++++++- .../charmhelpers/contrib/hahelpers/cluster.py | 6 +- hooks/charmhelpers/contrib/network/ufw.py | 28 ++-- .../contrib/openstack/amulet/deployment.py | 7 +- .../charmhelpers/contrib/openstack/context.py | 37 ++++-- .../contrib/openstack/files/__init__.py | 18 +++ hooks/charmhelpers/contrib/openstack/ip.py | 37 ++++++ hooks/charmhelpers/contrib/openstack/utils.py | 1 + hooks/charmhelpers/contrib/python/packages.py | 4 +- hooks/charmhelpers/core/fstab.py | 8 +- hooks/charmhelpers/core/strutils.py | 42 ++++++ hooks/charmhelpers/core/sysctl.py | 4 +- hooks/charmhelpers/core/unitdata.py | 2 +- hooks/charmhelpers/fetch/archiveurl.py | 20 +-- hooks/charmhelpers/fetch/giturl.py | 2 +- hooks/quantum_contexts.py | 12 +- hooks/quantum_utils.py | 10 +- tests/charmhelpers/contrib/amulet/utils.py | 124 +++++++++++++++++- .../contrib/openstack/amulet/deployment.py | 7 +- unit_tests/test_quantum_contexts.py | 55 ++++++-- unit_tests/test_quantum_hooks.py | 6 + unit_tests/test_quantum_utils.py | 15 ++- 22 files changed, 420 insertions(+), 73 deletions(-) create mode 100644 hooks/charmhelpers/contrib/openstack/files/__init__.py create mode 100644 hooks/charmhelpers/core/strutils.py diff --git a/hooks/charmhelpers/contrib/charmsupport/nrpe.py b/hooks/charmhelpers/contrib/charmsupport/nrpe.py index 0fd0a9d8..9d961cfb 100644 --- a/hooks/charmhelpers/contrib/charmsupport/nrpe.py +++ b/hooks/charmhelpers/contrib/charmsupport/nrpe.py @@ -24,6 +24,8 @@ import subprocess import pwd import grp import os +import glob +import shutil import re import shlex import yaml @@ -161,7 +163,7 @@ define service {{ log('Check command not found: {}'.format(parts[0])) return '' - def write(self, nagios_context, hostname, nagios_servicegroups=None): + def write(self, nagios_context, hostname, nagios_servicegroups): nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( self.command) with open(nrpe_check_file, 'w') as nrpe_check_config: @@ -177,14 +179,11 @@ define service {{ nagios_servicegroups) def write_service_config(self, nagios_context, hostname, - nagios_servicegroups=None): + nagios_servicegroups): for f in os.listdir(NRPE.nagios_exportdir): if re.search('.*{}.cfg'.format(self.command), f): os.remove(os.path.join(NRPE.nagios_exportdir, f)) - if not nagios_servicegroups: - nagios_servicegroups = nagios_context - templ_vars = { 'nagios_hostname': hostname, 'nagios_servicegroup': nagios_servicegroups, @@ -211,10 +210,10 @@ class NRPE(object): super(NRPE, self).__init__() self.config = config() self.nagios_context = self.config['nagios_context'] - if 'nagios_servicegroups' in self.config: + if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']: self.nagios_servicegroups = self.config['nagios_servicegroups'] else: - self.nagios_servicegroups = 'juju' + self.nagios_servicegroups = self.nagios_context self.unit_name = local_unit().replace('/', '-') if hostname: self.hostname = hostname @@ -322,3 +321,38 @@ def add_init_service_checks(nrpe, services, unit_name): check_cmd='check_status_file.py -f ' '/var/lib/nagios/service-check-%s.txt' % svc, ) + + +def copy_nrpe_checks(): + """ + Copy the nrpe checks into place + + """ + NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' + nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks', + 'charmhelpers', 'contrib', 'openstack', + 'files') + + if not os.path.exists(NAGIOS_PLUGINS): + os.makedirs(NAGIOS_PLUGINS) + for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")): + if os.path.isfile(fname): + shutil.copy2(fname, + os.path.join(NAGIOS_PLUGINS, os.path.basename(fname))) + + +def add_haproxy_checks(nrpe, unit_name): + """ + Add checks for each service in list + + :param NRPE nrpe: NRPE object to add check to + :param str unit_name: Unit name to use in check description + """ + nrpe.add_check( + shortname='haproxy_servers', + description='Check HAProxy {%s}' % unit_name, + check_cmd='check_haproxy.sh') + nrpe.add_check( + shortname='haproxy_queue', + description='Check HAProxy queue depth {%s}' % unit_name, + check_cmd='check_haproxy_queue_depth.sh') diff --git a/hooks/charmhelpers/contrib/hahelpers/cluster.py b/hooks/charmhelpers/contrib/hahelpers/cluster.py index 9a2588b6..9333efc3 100644 --- a/hooks/charmhelpers/contrib/hahelpers/cluster.py +++ b/hooks/charmhelpers/contrib/hahelpers/cluster.py @@ -48,6 +48,9 @@ from charmhelpers.core.hookenv import ( from charmhelpers.core.decorators import ( retry_on_exception, ) +from charmhelpers.core.strutils import ( + bool_from_string, +) class HAIncompleteConfig(Exception): @@ -164,7 +167,8 @@ def https(): . returns: boolean ''' - if config_get('use-https') == "yes": + use_https = config_get('use-https') + if use_https and bool_from_string(use_https): return True if config_get('ssl_cert') and config_get('ssl_key'): return True diff --git a/hooks/charmhelpers/contrib/network/ufw.py b/hooks/charmhelpers/contrib/network/ufw.py index 1e79a0ca..560e6a03 100644 --- a/hooks/charmhelpers/contrib/network/ufw.py +++ b/hooks/charmhelpers/contrib/network/ufw.py @@ -37,19 +37,22 @@ Examples: >>> ufw.enable() >>> ufw.service('4949', 'close') # munin """ - -__author__ = "Felipe Reyes " - import re import os import subprocess from charmhelpers.core import hookenv +__author__ = "Felipe Reyes " + class UFWError(Exception): pass +class UFWIPv6Error(UFWError): + pass + + def is_enabled(): """ Check if `ufw` is enabled @@ -66,10 +69,13 @@ def is_enabled(): return len(m) >= 1 -def is_ipv6_ok(): +def is_ipv6_ok(soft_fail=False): """ Check if IPv6 support is present and ip6tables functional + :param soft_fail: If set to True and IPv6 support is broken, then reports + that the host doesn't have IPv6 support, otherwise a + UFWIPv6Error exception is raised. :returns: True if IPv6 is working, False otherwise """ @@ -89,8 +95,11 @@ def is_ipv6_ok(): hookenv.log("Couldn't load ip6_tables module: %s" % ex.output, level="WARN") # we are in a world where ip6tables isn't working - # so we inform that the machine doesn't have IPv6 - return False + if soft_fail: + # so we inform that the machine doesn't have IPv6 + return False + else: + raise UFWIPv6Error("IPv6 firewall support broken") else: # the module is present :) return True @@ -113,16 +122,19 @@ def disable_ipv6(): raise UFWError("Couldn't disable IPv6 support in ufw") -def enable(): +def enable(soft_fail=False): """ Enable ufw + :param soft_fail: If set to True silently disables IPv6 support in ufw, + otherwise a UFWIPv6Error exception is raised when IP6 + support is broken. :returns: True if ufw is successfully enabled """ if is_enabled(): return True - if not is_ipv6_ok(): + if not is_ipv6_ok(soft_fail): disable_ipv6() output = subprocess.check_output(['ufw', 'enable'], diff --git a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py index c50d3ec6..0cfeaa4c 100644 --- a/hooks/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/hooks/charmhelpers/contrib/openstack/amulet/deployment.py @@ -71,16 +71,19 @@ class OpenStackAmuletDeployment(AmuletDeployment): services.append(this_service) use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', 'ceph-osd', 'ceph-radosgw'] + # Openstack subordinate charms do not expose an origin option as that + # is controlled by the principle + ignore = ['neutron-openvswitch'] if self.openstack: for svc in services: - if svc['name'] not in use_source: + if svc['name'] not in use_source + ignore: config = {'openstack-origin': self.openstack} self.d.configure(svc['name'], config) if self.source: for svc in services: - if svc['name'] in use_source: + if svc['name'] in use_source and svc['name'] not in ignore: config = {'source': self.source} self.d.configure(svc['name'], config) diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index c7c4cd4a..d268ea8f 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -279,9 +279,25 @@ def db_ssl(rdata, ctxt, ssl_dir): class IdentityServiceContext(OSContextGenerator): interfaces = ['identity-service'] + def __init__(self, service=None, service_user=None): + self.service = service + self.service_user = service_user + def __call__(self): log('Generating template context for identity-service', level=DEBUG) ctxt = {} + + if self.service and self.service_user: + # This is required for pki token signing if we don't want /tmp to + # be used. + cachedir = '/var/cache/%s' % (self.service) + if not os.path.isdir(cachedir): + log("Creating service cache dir %s" % (cachedir), level=DEBUG) + mkdir(path=cachedir, owner=self.service_user, + group=self.service_user, perms=0o700) + + ctxt['signing_dir'] = cachedir + for rid in relation_ids('identity-service'): for unit in related_units(rid): rdata = relation_get(rid=rid, unit=unit) @@ -291,15 +307,16 @@ class IdentityServiceContext(OSContextGenerator): auth_host = format_ipv6_addr(auth_host) or auth_host svc_protocol = rdata.get('service_protocol') or 'http' auth_protocol = rdata.get('auth_protocol') or 'http' - ctxt = {'service_port': rdata.get('service_port'), - 'service_host': serv_host, - 'auth_host': auth_host, - 'auth_port': rdata.get('auth_port'), - 'admin_tenant_name': rdata.get('service_tenant'), - 'admin_user': rdata.get('service_username'), - 'admin_password': rdata.get('service_password'), - 'service_protocol': svc_protocol, - 'auth_protocol': auth_protocol} + ctxt.update({'service_port': rdata.get('service_port'), + 'service_host': serv_host, + 'auth_host': auth_host, + 'auth_port': rdata.get('auth_port'), + 'admin_tenant_name': rdata.get('service_tenant'), + 'admin_user': rdata.get('service_username'), + 'admin_password': rdata.get('service_password'), + 'service_protocol': svc_protocol, + 'auth_protocol': auth_protocol}) + if context_complete(ctxt): # NOTE(jamespage) this is required for >= icehouse # so a missing value just indicates keystone needs @@ -1021,6 +1038,8 @@ class ZeroMQContext(OSContextGenerator): for unit in related_units(rid): ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) ctxt['zmq_host'] = relation_get('host', unit, rid) + ctxt['zmq_redis_address'] = relation_get( + 'zmq_redis_address', unit, rid) return ctxt diff --git a/hooks/charmhelpers/contrib/openstack/files/__init__.py b/hooks/charmhelpers/contrib/openstack/files/__init__.py new file mode 100644 index 00000000..75876796 --- /dev/null +++ b/hooks/charmhelpers/contrib/openstack/files/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2014-2015 Canonical Limited. +# +# This file is part of charm-helpers. +# +# charm-helpers is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 as +# published by the Free Software Foundation. +# +# charm-helpers is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with charm-helpers. If not, see . + +# dummy __init__.py to fool syncer into thinking this is a syncable python +# module diff --git a/hooks/charmhelpers/contrib/openstack/ip.py b/hooks/charmhelpers/contrib/openstack/ip.py index 9eabed73..29bbddcb 100644 --- a/hooks/charmhelpers/contrib/openstack/ip.py +++ b/hooks/charmhelpers/contrib/openstack/ip.py @@ -26,6 +26,8 @@ from charmhelpers.contrib.network.ip import ( ) from charmhelpers.contrib.hahelpers.cluster import is_clustered +from functools import partial + PUBLIC = 'public' INTERNAL = 'int' ADMIN = 'admin' @@ -107,3 +109,38 @@ def resolve_address(endpoint_type=PUBLIC): "clustered=%s)" % (net_type, clustered)) return resolved_address + + +def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC, + override=None): + """Returns the correct endpoint URL to advertise to Keystone. + + This method provides the correct endpoint URL which should be advertised to + the keystone charm for endpoint creation. This method allows for the url to + be overridden to force a keystone endpoint to have specific URL for any of + the defined scopes (admin, internal, public). + + :param configs: OSTemplateRenderer config templating object to inspect + for a complete https context. + :param url_template: str format string for creating the url template. Only + two values will be passed - the scheme+hostname + returned by the canonical_url and the port. + :param endpoint_type: str endpoint type to resolve. + :param override: str the name of the config option which overrides the + endpoint URL defined by the charm itself. None will + disable any overrides (default). + """ + if override: + # Return any user-defined overrides for the keystone endpoint URL. + user_value = config(override) + if user_value: + return user_value.strip() + + return url_template % (canonical_url(configs, endpoint_type), port) + + +public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC) + +internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL) + +admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN) diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 26259a03..af2b3596 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -103,6 +103,7 @@ SWIFT_CODENAMES = OrderedDict([ ('2.1.0', 'juno'), ('2.2.0', 'juno'), ('2.2.1', 'kilo'), + ('2.2.2', 'kilo'), ]) DEFAULT_LOOPBACK_SIZE = '5G' diff --git a/hooks/charmhelpers/contrib/python/packages.py b/hooks/charmhelpers/contrib/python/packages.py index d848a120..8659516b 100644 --- a/hooks/charmhelpers/contrib/python/packages.py +++ b/hooks/charmhelpers/contrib/python/packages.py @@ -17,8 +17,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with charm-helpers. If not, see . -__author__ = "Jorge Niedbalski " - from charmhelpers.fetch import apt_install, apt_update from charmhelpers.core.hookenv import log @@ -29,6 +27,8 @@ except ImportError: apt_install('python-pip') from pip import main as pip_execute +__author__ = "Jorge Niedbalski " + def parse_options(given, available): """Given a set of options, check if available""" diff --git a/hooks/charmhelpers/core/fstab.py b/hooks/charmhelpers/core/fstab.py index be7de248..3056fbac 100644 --- a/hooks/charmhelpers/core/fstab.py +++ b/hooks/charmhelpers/core/fstab.py @@ -17,11 +17,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with charm-helpers. If not, see . -__author__ = 'Jorge Niedbalski R. ' - import io import os +__author__ = 'Jorge Niedbalski R. ' + class Fstab(io.FileIO): """This class extends file in order to implement a file reader/writer @@ -77,7 +77,7 @@ class Fstab(io.FileIO): for line in self.readlines(): line = line.decode('us-ascii') try: - if line.strip() and not line.startswith("#"): + if line.strip() and not line.strip().startswith("#"): yield self._hydrate_entry(line) except ValueError: pass @@ -104,7 +104,7 @@ class Fstab(io.FileIO): found = False for index, line in enumerate(lines): - if not line.startswith("#"): + if line.strip() and not line.strip().startswith("#"): if self._hydrate_entry(line) == entry: found = True break diff --git a/hooks/charmhelpers/core/strutils.py b/hooks/charmhelpers/core/strutils.py new file mode 100644 index 00000000..efc4402e --- /dev/null +++ b/hooks/charmhelpers/core/strutils.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2014-2015 Canonical Limited. +# +# This file is part of charm-helpers. +# +# charm-helpers is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 as +# published by the Free Software Foundation. +# +# charm-helpers is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with charm-helpers. If not, see . + +import six + + +def bool_from_string(value): + """Interpret string value as boolean. + + Returns True if value translates to True otherwise False. + """ + if isinstance(value, six.string_types): + value = six.text_type(value) + else: + msg = "Unable to interpret non-string value '%s' as boolean" % (value) + raise ValueError(msg) + + value = value.strip().lower() + + if value in ['y', 'yes', 'true', 't']: + return True + elif value in ['n', 'no', 'false', 'f']: + return False + + msg = "Unable to interpret string value '%s' as boolean" % (value) + raise ValueError(msg) diff --git a/hooks/charmhelpers/core/sysctl.py b/hooks/charmhelpers/core/sysctl.py index 8e1b9eeb..21cc8ab2 100644 --- a/hooks/charmhelpers/core/sysctl.py +++ b/hooks/charmhelpers/core/sysctl.py @@ -17,8 +17,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with charm-helpers. If not, see . -__author__ = 'Jorge Niedbalski R. ' - import yaml from subprocess import check_call @@ -29,6 +27,8 @@ from charmhelpers.core.hookenv import ( ERROR, ) +__author__ = 'Jorge Niedbalski R. ' + def create(sysctl_dict, sysctl_file): """Creates a sysctl.conf file from a YAML associative array diff --git a/hooks/charmhelpers/core/unitdata.py b/hooks/charmhelpers/core/unitdata.py index 01329ab7..3000134a 100644 --- a/hooks/charmhelpers/core/unitdata.py +++ b/hooks/charmhelpers/core/unitdata.py @@ -435,7 +435,7 @@ class HookData(object): os.path.join(charm_dir, 'revision')).read().strip() charm_rev = charm_rev or '0' revs = self.kv.get('charm_revisions', []) - if not charm_rev in revs: + if charm_rev not in revs: revs.append(charm_rev.strip() or '0') self.kv.set('charm_revisions', revs) diff --git a/hooks/charmhelpers/fetch/archiveurl.py b/hooks/charmhelpers/fetch/archiveurl.py index d25a0ddd..8dfce505 100644 --- a/hooks/charmhelpers/fetch/archiveurl.py +++ b/hooks/charmhelpers/fetch/archiveurl.py @@ -18,6 +18,16 @@ import os import hashlib import re +from charmhelpers.fetch import ( + BaseFetchHandler, + UnhandledSource +) +from charmhelpers.payload.archive import ( + get_archive_handler, + extract, +) +from charmhelpers.core.host import mkdir, check_hash + import six if six.PY3: from urllib.request import ( @@ -35,16 +45,6 @@ else: ) from urlparse import urlparse, urlunparse, parse_qs -from charmhelpers.fetch import ( - BaseFetchHandler, - UnhandledSource -) -from charmhelpers.payload.archive import ( - get_archive_handler, - extract, -) -from charmhelpers.core.host import mkdir, check_hash - def splituser(host): '''urllib.splituser(), but six's support of this seems broken''' diff --git a/hooks/charmhelpers/fetch/giturl.py b/hooks/charmhelpers/fetch/giturl.py index 5376786b..93aae87b 100644 --- a/hooks/charmhelpers/fetch/giturl.py +++ b/hooks/charmhelpers/fetch/giturl.py @@ -32,7 +32,7 @@ except ImportError: apt_install("python-git") from git import Repo -from git.exc import GitCommandError +from git.exc import GitCommandError # noqa E402 class GitUrlFetchHandler(BaseFetchHandler): diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 226fc743..d1123710 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -34,6 +34,7 @@ from charmhelpers.contrib.network.ip import ( get_ipv6_addr, is_bridge_member, ) +from charmhelpers.core.strutils import bool_from_string DB_USER = "quantum" QUANTUM_DB = "quantum" @@ -121,14 +122,17 @@ def neutron_api_settings(): rdata = relation_get(rid=rid, unit=unit) if 'l2-population' not in rdata: continue - neutron_settings['l2_population'] = rdata['l2-population'] + neutron_settings['l2_population'] = \ + bool_from_string(rdata['l2-population']) if 'overlay-network-type' in rdata: neutron_settings['overlay_network_type'] = \ rdata['overlay-network-type'] if 'enable-dvr' in rdata: - neutron_settings['enable_dvr'] = rdata['enable-dvr'] + neutron_settings['enable_dvr'] = \ + bool_from_string(rdata['enable-dvr']) if 'enable-l3ha' in rdata: - neutron_settings['enable_l3ha'] = rdata['enable-l3ha'] + neutron_settings['enable_l3ha'] = \ + bool_from_string(rdata['enable-l3ha']) return neutron_settings return neutron_settings @@ -179,7 +183,7 @@ class L3AgentContext(OSContextGenerator): ctxt['ext_net_id'] = config('external-network-id') if config('plugin'): ctxt['plugin'] = config('plugin') - if api_settings['enable_dvr'] == 'True': + if api_settings['enable_dvr']: ctxt['agent_mode'] = 'dvr_snat' else: ctxt['agent_mode'] = 'legacy' diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 308e9e5d..6b7d1bcc 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -316,7 +316,7 @@ NEUTRON_OVS_CONFIG_FILES = { 'hook_contexts': [NetworkServiceContext(), L3AgentContext(), QuantumGatewayContext()], - 'services': ['neutron-l3-agent'] + 'services': ['neutron-l3-agent', 'neutron-vpn-agent'] }, NEUTRON_METERING_AGENT_CONF: { 'hook_contexts': [QuantumGatewayContext()], @@ -334,7 +334,7 @@ NEUTRON_OVS_CONFIG_FILES = { }, NEUTRON_FWAAS_CONF: { 'hook_contexts': [QuantumGatewayContext()], - 'services': ['neutron-l3-agent'] + 'services': ['neutron-l3-agent', 'neutron-vpn-agent'] }, NEUTRON_OVS_PLUGIN_CONF: { 'hook_contexts': [QuantumGatewayContext()], @@ -342,8 +342,7 @@ NEUTRON_OVS_CONFIG_FILES = { }, NEUTRON_ML2_PLUGIN_CONF: { 'hook_contexts': [QuantumGatewayContext()], - 'services': ['neutron-plugin-openvswitch-agent', - 'neutron-openvswitch-agent'] + 'services': ['neutron-plugin-openvswitch-agent'] }, EXT_PORT_CONF: { 'hook_contexts': [ExternalPortContext()], @@ -417,9 +416,6 @@ def register_configs(): drop_config = NEUTRON_ML2_PLUGIN_CONF if release >= 'icehouse': drop_config = NEUTRON_OVS_PLUGIN_CONF - # NOTE(gnuoy) neutron-vpn-agent supercedes l3-agent for icehouse - CONFIG_FILES[name][plugin][NEUTRON_L3_AGENT_CONF]['services'] = \ - ['neutron-vpn-agent'] if drop_config in CONFIG_FILES[name][plugin]: CONFIG_FILES[name][plugin].pop(drop_config) diff --git a/tests/charmhelpers/contrib/amulet/utils.py b/tests/charmhelpers/contrib/amulet/utils.py index 3464b873..65219d33 100644 --- a/tests/charmhelpers/contrib/amulet/utils.py +++ b/tests/charmhelpers/contrib/amulet/utils.py @@ -169,8 +169,13 @@ class AmuletUtils(object): cmd = 'pgrep -o -f {}'.format(service) else: cmd = 'pgrep -o {}'.format(service) - proc_dir = '/proc/{}'.format(sentry_unit.run(cmd)[0].strip()) - return self._get_dir_mtime(sentry_unit, proc_dir) + cmd = cmd + ' | grep -v pgrep || exit 0' + cmd_out = sentry_unit.run(cmd) + self.log.debug('CMDout: ' + str(cmd_out)) + if cmd_out[0]: + self.log.debug('Pid for %s %s' % (service, str(cmd_out[0]))) + proc_dir = '/proc/{}'.format(cmd_out[0].strip()) + return self._get_dir_mtime(sentry_unit, proc_dir) def service_restarted(self, sentry_unit, service, filename, pgrep_full=False, sleep_time=20): @@ -187,6 +192,121 @@ class AmuletUtils(object): else: return False + def service_restarted_since(self, sentry_unit, mtime, service, + pgrep_full=False, sleep_time=20, + retry_count=2): + """Check if service was been started after a given time. + + Args: + sentry_unit (sentry): The sentry unit to check for the service on + mtime (float): The epoch time to check against + service (string): service name to look for in process table + pgrep_full (boolean): Use full command line search mode with pgrep + sleep_time (int): Seconds to sleep before looking for process + retry_count (int): If service is not found, how many times to retry + + Returns: + bool: True if service found and its start time it newer than mtime, + False if service is older than mtime or if service was + not found. + """ + self.log.debug('Checking %s restarted since %s' % (service, mtime)) + time.sleep(sleep_time) + proc_start_time = self._get_proc_start_time(sentry_unit, service, + pgrep_full) + while retry_count > 0 and not proc_start_time: + self.log.debug('No pid file found for service %s, will retry %i ' + 'more times' % (service, retry_count)) + time.sleep(30) + proc_start_time = self._get_proc_start_time(sentry_unit, service, + pgrep_full) + retry_count = retry_count - 1 + + if not proc_start_time: + self.log.warn('No proc start time found, assuming service did ' + 'not start') + return False + if proc_start_time >= mtime: + self.log.debug('proc start time is newer than provided mtime' + '(%s >= %s)' % (proc_start_time, mtime)) + return True + else: + self.log.warn('proc start time (%s) is older than provided mtime ' + '(%s), service did not restart' % (proc_start_time, + mtime)) + return False + + def config_updated_since(self, sentry_unit, filename, mtime, + sleep_time=20): + """Check if file was modified after a given time. + + Args: + sentry_unit (sentry): The sentry unit to check the file mtime on + filename (string): The file to check mtime of + mtime (float): The epoch time to check against + sleep_time (int): Seconds to sleep before looking for process + + Returns: + bool: True if file was modified more recently than mtime, False if + file was modified before mtime, + """ + self.log.debug('Checking %s updated since %s' % (filename, mtime)) + time.sleep(sleep_time) + file_mtime = self._get_file_mtime(sentry_unit, filename) + if file_mtime >= mtime: + self.log.debug('File mtime is newer than provided mtime ' + '(%s >= %s)' % (file_mtime, mtime)) + return True + else: + self.log.warn('File mtime %s is older than provided mtime %s' + % (file_mtime, mtime)) + return False + + def validate_service_config_changed(self, sentry_unit, mtime, service, + filename, pgrep_full=False, + sleep_time=20, retry_count=2): + """Check service and file were updated after mtime + + Args: + sentry_unit (sentry): The sentry unit to check for the service on + mtime (float): The epoch time to check against + service (string): service name to look for in process table + filename (string): The file to check mtime of + pgrep_full (boolean): Use full command line search mode with pgrep + sleep_time (int): Seconds to sleep before looking for process + retry_count (int): If service is not found, how many times to retry + + Typical Usage: + u = OpenStackAmuletUtils(ERROR) + ... + mtime = u.get_sentry_time(self.cinder_sentry) + self.d.configure('cinder', {'verbose': 'True', 'debug': 'True'}) + if not u.validate_service_config_changed(self.cinder_sentry, + mtime, + 'cinder-api', + '/etc/cinder/cinder.conf') + amulet.raise_status(amulet.FAIL, msg='update failed') + Returns: + bool: True if both service and file where updated/restarted after + mtime, False if service is older than mtime or if service was + not found or if filename was modified before mtime. + """ + self.log.debug('Checking %s restarted since %s' % (service, mtime)) + time.sleep(sleep_time) + service_restart = self.service_restarted_since(sentry_unit, mtime, + service, + pgrep_full=pgrep_full, + sleep_time=0, + retry_count=retry_count) + config_update = self.config_updated_since(sentry_unit, filename, mtime, + sleep_time=0) + return service_restart and config_update + + def get_sentry_time(self, sentry_unit): + """Return current epoch time on a sentry""" + cmd = "date +'%s'" + return float(sentry_unit.run(cmd)[0]) + def relation_error(self, name, data): return 'unexpected relation data in {} - {}'.format(name, data) diff --git a/tests/charmhelpers/contrib/openstack/amulet/deployment.py b/tests/charmhelpers/contrib/openstack/amulet/deployment.py index c50d3ec6..0cfeaa4c 100644 --- a/tests/charmhelpers/contrib/openstack/amulet/deployment.py +++ b/tests/charmhelpers/contrib/openstack/amulet/deployment.py @@ -71,16 +71,19 @@ class OpenStackAmuletDeployment(AmuletDeployment): services.append(this_service) use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', 'ceph-osd', 'ceph-radosgw'] + # Openstack subordinate charms do not expose an origin option as that + # is controlled by the principle + ignore = ['neutron-openvswitch'] if self.openstack: for svc in services: - if svc['name'] not in use_source: + if svc['name'] not in use_source + ignore: config = {'openstack-origin': self.openstack} self.d.configure(svc['name'], config) if self.source: for svc in services: - if svc['name'] in use_source: + if svc['name'] in use_source and svc['name'] not in ignore: config = {'source': self.source} self.d.configure(svc['name'], config) diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index d1c6a1f1..a2b62f05 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -216,6 +216,12 @@ class TestL3AgentContext(CharmTestCase): 'ext_net_id': 'netid', 'plugin': 'ovs'}) + @patch.object(quantum_contexts, 'neutron_api_settings') + def test_dvr(self, _napi_settings): + _napi_settings.return_value = {'enable_dvr': True} + self.assertEquals(quantum_contexts.L3AgentContext()()['agent_mode'], + 'dvr_snat') + class TestQuantumGatewayContext(CharmTestCase): @@ -224,9 +230,14 @@ class TestQuantumGatewayContext(CharmTestCase): TO_PATCH) self.config.side_effect = self.test_config.get + @patch.object(quantum_contexts, 'neutron_api_settings') @patch.object(quantum_contexts, 'get_shared_secret') @patch.object(quantum_contexts, 'get_host_ip') - def test_all(self, _host_ip, _secret): + def test_all(self, _host_ip, _secret, napi_settings): + napi_settings.return_value = {'l2_population': True, + 'enable_dvr': True, + 'overlay_network_type': 'gre', + 'enable_l3ha': True} self.test_config.set('plugin', 'ovs') self.test_config.set('debug', False) self.test_config.set('verbose', True) @@ -236,7 +247,8 @@ class TestQuantumGatewayContext(CharmTestCase): _secret.return_value = 'testsecret' self.assertEquals(quantum_contexts.QuantumGatewayContext()(), { 'shared_secret': 'testsecret', - 'enable_dvr': False, + 'enable_dvr': True, + 'enable_l3ha': True, 'local_ip': '10.5.0.1', 'instance_mtu': 1420, 'core_plugin': "quantum.plugins.openvswitch.ovs_quantum_plugin." @@ -244,7 +256,7 @@ class TestQuantumGatewayContext(CharmTestCase): 'plugin': 'ovs', 'debug': False, 'verbose': True, - 'l2_population': False, + 'l2_population': True, 'overlay_network_type': 'gre', }) @@ -370,28 +382,55 @@ class TestMisc(CharmTestCase): def test_neutron_api_settings(self): self.relation_ids.return_value = ['foo'] self.related_units.return_value = ['bar'] - self.test_relation.set({'l2-population': True, + self.test_relation.set({'l2-population': 'True', 'overlay-network-type': 'gre', }) self.relation_get.side_effect = self.test_relation.get - self.assertEquals(quantum_contexts._neutron_api_settings(), + self.assertEquals(quantum_contexts.neutron_api_settings(), {'enable_dvr': False, + 'enable_l3ha': False, 'l2_population': True, 'overlay_network_type': 'gre'}) def test_neutron_api_settings2(self): self.relation_ids.return_value = ['foo'] self.related_units.return_value = ['bar'] - self.test_relation.set({'l2-population': True, + self.test_relation.set({'l2-population': 'True', 'overlay-network-type': 'gre', }) self.relation_get.side_effect = self.test_relation.get - self.assertEquals(quantum_contexts._neutron_api_settings(), + self.assertEquals(quantum_contexts.neutron_api_settings(), {'enable_dvr': False, + 'enable_l3ha': False, 'l2_population': True, 'overlay_network_type': 'gre'}) def test_neutron_api_settings_no_apiplugin(self): self.relation_ids.return_value = [] - self.assertEquals(quantum_contexts._neutron_api_settings(), + self.assertEquals(quantum_contexts.neutron_api_settings(), {'enable_dvr': False, + 'enable_l3ha': False, 'l2_population': False, 'overlay_network_type': 'gre', }) + + def test_neutron_api_dvr(self): + self.relation_ids.return_value = ['foo'] + self.related_units.return_value = ['bar'] + self.test_relation.set({'l2-population': 'True', + 'enable-dvr': 'True'}) + self.relation_get.side_effect = self.test_relation.get + self.assertEquals(quantum_contexts.neutron_api_settings(), + {'enable_dvr': True, + 'enable_l3ha': False, + 'l2_population': True, + 'overlay_network_type': 'gre'}) + + def test_neutron_api_l3ha(self): + self.relation_ids.return_value = ['foo'] + self.related_units.return_value = ['bar'] + self.test_relation.set({'l2-population': 'False', + 'enable-l3ha': 'True'}) + self.relation_get.side_effect = self.test_relation.get + self.assertEquals(quantum_contexts.neutron_api_settings(), + {'enable_dvr': False, + 'enable_l3ha': True, + 'l2_population': False, + 'overlay_network_type': 'gre'}) diff --git a/unit_tests/test_quantum_hooks.py b/unit_tests/test_quantum_hooks.py index 7ce1651f..2465273a 100644 --- a/unit_tests/test_quantum_hooks.py +++ b/unit_tests/test_quantum_hooks.py @@ -246,6 +246,12 @@ class TestQuantumHooks(CharmTestCase): self.assertTrue(self.CONFIGS.write_all.called) self.install_ca_cert.assert_called_with('cert') + def test_neutron_plugin_changed(self): + self.filter_installed_packages.return_value = ['foo'] + self._call_hook('neutron-plugin-api-relation-changed') + self.apt_install.assert_called_with(['foo'], fatal=True) + self.assertTrue(self.CONFIGS.write_all.called) + def test_cluster_departed_nvp(self): self.test_config.set('plugin', 'nvp') self._call_hook('cluster-relation-departed') diff --git a/unit_tests/test_quantum_utils.py b/unit_tests/test_quantum_utils.py index 8c319761..0249aaf8 100644 --- a/unit_tests/test_quantum_utils.py +++ b/unit_tests/test_quantum_utils.py @@ -46,7 +46,8 @@ TO_PATCH = [ 'is_relation_made', 'lsb_release', 'mkdir', - 'copy2' + 'copy2', + 'neutron_api_settings', ] @@ -139,6 +140,11 @@ class TestQuantumUtils(CharmTestCase): self.get_os_codename_install_source.return_value = 'kilo' self.assertTrue('python-neutron-fwaas' in quantum_utils.get_packages()) + def test_get_packages_l3ha(self): + self.config.return_value = 'ovs' + self.get_os_codename_install_source.return_value = 'juno' + self.assertTrue('keepalived' in quantum_utils.get_packages()) + def test_configure_ovs_starts_service_if_required(self): self.config.return_value = 'ovs' self.service_running.return_value = False @@ -241,6 +247,7 @@ class TestQuantumUtils(CharmTestCase): def test_restart_map_ovs(self): self.config.return_value = 'ovs' + self.get_os_codename_install_source.return_value = 'havana' ex_map = { quantum_utils.NEUTRON_CONF: ['neutron-l3-agent', 'neutron-dhcp-agent', @@ -261,9 +268,11 @@ class TestQuantumUtils(CharmTestCase): quantum_utils.NEUTRON_VPNAAS_AGENT_CONF: [ 'neutron-plugin-vpn-agent', 'neutron-vpn-agent'], - quantum_utils.NEUTRON_L3_AGENT_CONF: ['neutron-l3-agent'], + quantum_utils.NEUTRON_L3_AGENT_CONF: ['neutron-l3-agent', + 'neutron-vpn-agent'], quantum_utils.NEUTRON_DHCP_AGENT_CONF: ['neutron-dhcp-agent'], - quantum_utils.NEUTRON_FWAAS_CONF: ['neutron-l3-agent'], + quantum_utils.NEUTRON_FWAAS_CONF: ['neutron-l3-agent', + 'neutron-vpn-agent'], quantum_utils.NEUTRON_METERING_AGENT_CONF: ['neutron-metering-agent', 'neutron-plugin-metering-agent'], quantum_utils.NOVA_CONF: ['nova-api-metadata'], From a0bc348fbb668f56ba6bf1a0acb5fef9934a690c Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 3 Mar 2015 10:40:41 +0000 Subject: [PATCH 07/12] Purge keepalived if l3ha is turned off --- hooks/quantum_hooks.py | 11 ++++++++--- hooks/quantum_utils.py | 13 +++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/hooks/quantum_hooks.py b/hooks/quantum_hooks.py index 722998ec..ca3fcd17 100755 --- a/hooks/quantum_hooks.py +++ b/hooks/quantum_hooks.py @@ -40,6 +40,7 @@ from charmhelpers.contrib.charmsupport import nrpe import sys from quantum_utils import ( + L3HA_PACKAGES, register_configs, restart_map, services, @@ -56,7 +57,8 @@ from quantum_utils import ( install_legacy_ha_files, cleanup_ovs_netns, reassign_agent_resources, - stop_neutron_ha_monitor_daemon + stop_neutron_ha_monitor_daemon, + use_l3ha, ) hooks = Hooks() @@ -202,8 +204,11 @@ def db_amqp_changed(): @hooks.hook('neutron-plugin-api-relation-changed') @restart_on_change(restart_map()) def neutron_plugin_api_changed(): - apt_install(filter_installed_packages(get_packages()), - fatal=True) + if use_l3ha(): + apt_update() + apt_install(L3HA_PACKAGES, fatal=True) + else: + apt_purge(L3HA_PACKAGES, fatal=True) CONFIGS.write_all() diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 6b7d1bcc..1c1f74cf 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -168,7 +168,7 @@ LEGACY_FILES_MAP = { }, } LEGACY_RES_MAP = ['res_monitor'] - +L3HA_PACKAGES = ['keepalived'] def get_early_packages(): '''Return a list of package for pre-install based on configured plugin''' @@ -182,7 +182,6 @@ def get_early_packages(): return pkgs + [headers_package()] return pkgs - def get_packages(): '''Return a list of packages for install based on the configured plugin''' plugin = remap_plugin(config('plugin')) @@ -198,10 +197,13 @@ def get_packages(): packages.append('openswan') if source >= 'kilo': packages.append('python-neutron-fwaas') - if neutron_api_settings()['enable_l3ha']: - packages.append('keepalived') + packages.extend(determine_l3ha_packages()) return packages +def determine_l3ha_packages(): + if use_l3ha(): + return L3HA_PACKAGES + return [] def get_common_package(): if get_os_codename_package('quantum-common', fatal=False) is not None: @@ -209,6 +211,9 @@ def get_common_package(): else: return 'neutron-common' +def use_l3ha(): + return neutron_api_settings()['enable_l3ha'] + EXT_PORT_CONF = '/etc/init/ext-port.conf' TEMPLATES = 'templates' From 0a5e106ed472a893da58c7a0bda240db3702e3b9 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 3 Mar 2015 11:22:28 +0000 Subject: [PATCH 08/12] Fix lint and unit tests --- hooks/quantum_utils.py | 5 +++++ unit_tests/test_quantum_hooks.py | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 1c1f74cf..b6080b8e 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -170,6 +170,7 @@ LEGACY_FILES_MAP = { LEGACY_RES_MAP = ['res_monitor'] L3HA_PACKAGES = ['keepalived'] + def get_early_packages(): '''Return a list of package for pre-install based on configured plugin''' if config('plugin') in [OVS]: @@ -182,6 +183,7 @@ def get_early_packages(): return pkgs + [headers_package()] return pkgs + def get_packages(): '''Return a list of packages for install based on the configured plugin''' plugin = remap_plugin(config('plugin')) @@ -200,17 +202,20 @@ def get_packages(): packages.extend(determine_l3ha_packages()) return packages + def determine_l3ha_packages(): if use_l3ha(): return L3HA_PACKAGES return [] + def get_common_package(): if get_os_codename_package('quantum-common', fatal=False) is not None: return 'quantum-common' else: return 'neutron-common' + def use_l3ha(): return neutron_api_settings()['enable_l3ha'] diff --git a/unit_tests/test_quantum_hooks.py b/unit_tests/test_quantum_hooks.py index 2465273a..3146b771 100644 --- a/unit_tests/test_quantum_hooks.py +++ b/unit_tests/test_quantum_hooks.py @@ -47,7 +47,8 @@ TO_PATCH = [ 'get_hacluster_config', 'remove_legacy_ha_files', 'cleanup_ovs_netns', - 'stop_neutron_ha_monitor_daemon' + 'stop_neutron_ha_monitor_daemon', + 'use_l3ha', ] @@ -247,9 +248,9 @@ class TestQuantumHooks(CharmTestCase): self.install_ca_cert.assert_called_with('cert') def test_neutron_plugin_changed(self): - self.filter_installed_packages.return_value = ['foo'] + self.use_l3ha.return_value = True self._call_hook('neutron-plugin-api-relation-changed') - self.apt_install.assert_called_with(['foo'], fatal=True) + self.apt_install.assert_called_with(['keepalived'], fatal=True) self.assertTrue(self.CONFIGS.write_all.called) def test_cluster_departed_nvp(self): From 877b10431a4aa763c1b409c76c12bd70add31dd8 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Wed, 25 Mar 2015 07:55:25 +0000 Subject: [PATCH 09/12] Fixes for contexts moving into charmhelpers --- charm-helpers-hooks.yaml | 3 +- .../charmhelpers/contrib/openstack/context.py | 145 +++++++++++++++++- hooks/quantum_contexts.py | 125 +-------------- hooks/quantum_utils.py | 14 +- unit_tests/test_quantum_utils.py | 8 +- 5 files changed, 161 insertions(+), 134 deletions(-) diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml index 84cc6c76..0b5106d1 100644 --- a/charm-helpers-hooks.yaml +++ b/charm-helpers-hooks.yaml @@ -1,4 +1,5 @@ -branch: lp:charm-helpers +#branch: lp:charm-helpers +branch: lp:~gnuoy/charm-helpers/neutron-shuffle destination: hooks/charmhelpers include: - core diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index 90ac6d69..45e65790 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -47,6 +47,7 @@ from charmhelpers.core.hookenv import ( ) from charmhelpers.core.sysctl import create as sysctl_create +from charmhelpers.core.strutils import bool_from_string from charmhelpers.core.host import ( list_nics, @@ -67,6 +68,7 @@ from charmhelpers.contrib.hahelpers.apache import ( ) from charmhelpers.contrib.openstack.neutron import ( neutron_plugin_attribute, + parse_data_port_mappings, ) from charmhelpers.contrib.openstack.ip import ( resolve_address, @@ -82,7 +84,6 @@ from charmhelpers.contrib.network.ip import ( is_bridge_member, ) from charmhelpers.contrib.openstack.utils import get_host_ip - CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' ADDRESS_TYPES = ['admin', 'internal', 'public'] @@ -1162,3 +1163,145 @@ class SysctlContext(OSContextGenerator): sysctl_create(sysctl_dict, '/etc/sysctl.d/50-{0}.conf'.format(charm_name())) return {'sysctl': sysctl_dict} + + +class NeutronAPIContext(OSContextGenerator): + ''' + Inspects current neutron-plugin-api relation for neutron settings. Return + defaults if it is not present. + ''' + interfaces = ['neutron-plugin-api'] + + def __call__(self): + self.neutron_defaults = { + 'l2_population': { + 'rel_key': 'l2-population', + 'default': False, + }, + 'overlay_network_type': { + 'rel_key': 'overlay-network-type', + 'default': 'gre', + }, + 'neutron_security_groups': { + 'rel_key': 'neutron-security-groups', + 'default': False, + }, + 'network_device_mtu': { + 'rel_key': 'network-device-mtu', + 'default': None, + }, + 'enable_dvr': { + 'rel_key': 'enable-dvr', + 'default': False, + }, + 'enable_l3ha': { + 'rel_key': 'enable-l3ha', + 'default': False, + }, + } + ctxt = self.get_neutron_options({}) + for rid in relation_ids('neutron-plugin-api'): + for unit in related_units(rid): + rdata = relation_get(rid=rid, unit=unit) + if 'l2-population' in rdata: + ctxt.update(self.get_neutron_options(rdata)) + + return ctxt + + def get_neutron_options(self, rdata): + settings = {} + for nkey in self.neutron_defaults.keys(): + defv = self.neutron_defaults[nkey]['default'] + rkey = self.neutron_defaults[nkey]['rel_key'] + if rkey in rdata.keys(): + if type(defv) is bool: + settings[nkey] = bool_from_string(rdata[rkey]) + else: + settings[nkey] = rdata[rkey] + else: + settings[nkey] = defv + return settings + + +class ExternalPortContext(NeutronPortContext): + + def __call__(self): + ctxt = {} + ports = config('ext-port') + if ports: + ports = [p.strip() for p in ports.split()] + ports = self.resolve_ports(ports) + if ports: + ctxt = {"ext_port": ports[0]} + napi_settings = NeutronAPIContext()() + mtu = napi_settings.get('network_device_mtu') + if mtu: + ctxt['ext_port_mtu'] = mtu + + return ctxt + + +class DataPortContext(NeutronPortContext): + + def __call__(self): + ports = config('data-port') + if ports: + portmap = parse_data_port_mappings(ports) + ports = portmap.values() + resolved = self.resolve_ports(ports) + normalized = {get_nic_hwaddr(port): port for port in resolved + if port not in ports} + normalized.update({port: port for port in resolved + if port in ports}) + if resolved: + return {bridge: normalized[port] for bridge, port in + six.iteritems(portmap) if port in normalized.keys()} + + return None + + +class PhyNICMTUContext(DataPortContext): + + def __call__(self): + ctxt = {} + mappings = super(PhyNICMTUContext, self).__call__() + if mappings and mappings.values(): + ports = mappings.values() + napi_settings = NeutronAPIContext()() + mtu = napi_settings.get('network_device_mtu') + if mtu: + ctxt["devs"] = '\\n'.join(ports) + ctxt['mtu'] = mtu + + return ctxt + + +class NetworkServiceContext(OSContextGenerator): + + def __init__(self, rel_name='quantum-network-service'): + self.rel_name = rel_name + self.interfaces = [rel_name] + + def __call__(self): + for rid in relation_ids(self.rel_name): + for unit in related_units(rid): + rdata = relation_get(rid=rid, unit=unit) + ctxt = { + 'keystone_host': rdata.get('keystone_host'), + 'service_port': rdata.get('service_port'), + 'auth_port': rdata.get('auth_port'), + 'service_tenant': rdata.get('service_tenant'), + 'service_username': rdata.get('service_username'), + 'service_password': rdata.get('service_password'), + 'quantum_host': rdata.get('quantum_host'), + 'quantum_port': rdata.get('quantum_port'), + 'quantum_url': rdata.get('quantum_url'), + 'region': rdata.get('region'), + 'service_protocol': + rdata.get('service_protocol') or 'http', + 'auth_protocol': + rdata.get('auth_protocol') or 'http', + } + if context_complete(ctxt): + return ctxt + return {} diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index 853f178b..b6b1ef19 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -17,6 +17,7 @@ from charmhelpers.contrib.openstack.context import ( OSContextGenerator, context_complete, NeutronPortContext, + NeutronAPIContext, ) from charmhelpers.contrib.openstack.utils import ( get_os_codename_install_source @@ -106,75 +107,10 @@ def core_plugin(): return CORE_PLUGIN[networking_name()][plugin] -def neutron_api_settings(): - ''' - Inspects current neutron-plugin-api relation for neutron settings. Return - defaults if it is not present - ''' - NEUTRON_DEFAULTS = { - 'l2_population': {'rel_key': 'l2-population', 'default': False}, - 'enable_dvr': {'rel_key': 'enable-dvr', 'default': False}, - 'enable_l3ha': {'rel_key': 'enable-l3ha', 'default': False}, - 'overlay_network_type': {'rel_key': 'overlay-network-type', 'default': 'gre'}, - 'network_device_mtu': {'rel_key': 'network-device-mtu', 'default': None}, - } - - def get_neutron_options(rdata): - settings = {} - for nkey in NEUTRON_DEFAULTS.keys(): - defv = NEUTRON_DEFAULTS[nkey]['default'] - rkey = NEUTRON_DEFAULTS[nkey]['rel_key'] - if rkey in rdata.keys(): - if type(defv) is bool: - settings[nkey] = bool_from_string(rdata[rkey]) - else: - settings[nkey] = rdata[rkey] - elif defv is not None: - settings[nkey] = defv - return settings - - neutron_settings = get_neutron_options({}) - for rid in relation_ids('neutron-plugin-api'): - for unit in related_units(rid): - rdata = relation_get(rid=rid, unit=unit) - if 'l2-population' in relation_get(rid=rid, unit=unit): - neutron_settings.update(get_neutron_options(rdata)) - - return neutron_settings - - -class NetworkServiceContext(OSContextGenerator): - interfaces = ['quantum-network-service'] - - def __call__(self): - for rid in relation_ids('quantum-network-service'): - for unit in related_units(rid): - rdata = relation_get(rid=rid, unit=unit) - ctxt = { - 'keystone_host': rdata.get('keystone_host'), - 'service_port': rdata.get('service_port'), - 'auth_port': rdata.get('auth_port'), - 'service_tenant': rdata.get('service_tenant'), - 'service_username': rdata.get('service_username'), - 'service_password': rdata.get('service_password'), - 'quantum_host': rdata.get('quantum_host'), - 'quantum_port': rdata.get('quantum_port'), - 'quantum_url': rdata.get('quantum_url'), - 'region': rdata.get('region'), - 'service_protocol': - rdata.get('service_protocol') or 'http', - 'auth_protocol': - rdata.get('auth_protocol') or 'http', - } - if context_complete(ctxt): - return ctxt - return {} - - class L3AgentContext(OSContextGenerator): def __call__(self): - api_settings = neutron_api_settings() + api_settings = NeutronAPIContext()() ctxt = {} if config('run-internal-router') == 'leader': ctxt['handle_internal_only_router'] = eligible_leader(None) @@ -196,63 +132,10 @@ class L3AgentContext(OSContextGenerator): return ctxt -class ExternalPortContext(NeutronPortContext): - - def __call__(self): - ctxt = {} - ports = config('ext-port') - if ports: - ports = [p.strip() for p in ports.split()] - ports = self.resolve_ports(ports) - if ports: - ctxt = {"ext_port": ports[0]} - napi_settings = neutron_api_settings() - mtu = napi_settings.get('network_device_mtu') - if mtu: - ctxt['ext_port_mtu'] = mtu - - return ctxt - - -class DataPortContext(NeutronPortContext): - - def __call__(self): - ports = config('data-port') - if ports: - portmap = parse_data_port_mappings(ports) - ports = portmap.values() - resolved = self.resolve_ports(ports) - normalized = {get_nic_hwaddr(port): port for port in resolved - if port not in ports} - normalized.update({port: port for port in resolved - if port in ports}) - if resolved: - return {bridge: normalized[port] for bridge, port in - portmap.iteritems() if port in normalized.keys()} - - return None - - -class PhyNICMTUContext(DataPortContext): - - def __call__(self): - ctxt = {} - mappings = super(PhyNICMTUContext, self).__call__() - if mappings and mappings.values(): - ports = mappings.values() - napi_settings = neutron_api_settings() - mtu = napi_settings.get('network_device_mtu') - if mtu: - ctxt["devs"] = '\\n'.join(ports) - ctxt['mtu'] = mtu - - return ctxt - - class QuantumGatewayContext(OSContextGenerator): def __call__(self): - api_settings = neutron_api_settings() + api_settings = NeutronAPIContext()() ctxt = { 'shared_secret': get_shared_secret(), 'local_ip': @@ -281,7 +164,7 @@ class QuantumGatewayContext(OSContextGenerator): ctxt['network_providers'] = ' '.join(providers) ctxt['vlan_ranges'] = vlan_ranges - net_dev_mtu = neutron_api_settings().get('network_device_mtu') + net_dev_mtu = api_settings['network_device_mtu'] if net_dev_mtu: ctxt['network_device_mtu'] = net_dev_mtu ctxt['veth_mtu'] = net_dev_mtu diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 6583b860..04beae1b 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -41,7 +41,12 @@ from charmhelpers.contrib.openstack.neutron import ( import charmhelpers.contrib.openstack.context as context from charmhelpers.contrib.openstack.context import ( - SyslogContext + SyslogContext, + NeutronAPIContext, + ExternalPortContext, + PhyNICMTUContext, + DataPortContext, + NetworkServiceContext, ) import charmhelpers.contrib.openstack.templating as templating from charmhelpers.contrib.openstack.neutron import headers_package @@ -50,13 +55,8 @@ from quantum_contexts import ( NEUTRON, QUANTUM, networking_name, QuantumGatewayContext, - NetworkServiceContext, L3AgentContext, - ExternalPortContext, - PhyNICMTUContext, - DataPortContext, remap_plugin, - neutron_api_settings, ) from charmhelpers.contrib.openstack.neutron import ( parse_bridge_mappings, @@ -221,7 +221,7 @@ def get_common_package(): def use_l3ha(): - return neutron_api_settings()['enable_l3ha'] + return NeutronAPIContext()()['enable_l3ha'] EXT_PORT_CONF = '/etc/init/ext-port.conf' PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf' diff --git a/unit_tests/test_quantum_utils.py b/unit_tests/test_quantum_utils.py index 6be942a8..7fa472c6 100644 --- a/unit_tests/test_quantum_utils.py +++ b/unit_tests/test_quantum_utils.py @@ -46,7 +46,7 @@ TO_PATCH = [ 'lsb_release', 'mkdir', 'copy2', - 'neutron_api_settings', + 'NeutronAPIContext', ] @@ -144,7 +144,7 @@ class TestQuantumUtils(CharmTestCase): self.get_os_codename_install_source.return_value = 'juno' self.assertTrue('keepalived' in quantum_utils.get_packages()) - @patch('quantum_contexts.config') + @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_starts_service_if_required(self, mock_config): mock_config.side_effect = self.test_config.get self.config.return_value = 'ovs' @@ -157,7 +157,7 @@ class TestQuantumUtils(CharmTestCase): quantum_utils.configure_ovs() self.assertFalse(self.full_restart.called) - @patch('quantum_contexts.config') + @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_ovs_ext_port(self, mock_config): mock_config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get @@ -173,7 +173,7 @@ class TestQuantumUtils(CharmTestCase): ]) self.add_bridge_port.assert_called_with('br-ex', 'eth0') - @patch('quantum_contexts.config') + @patch('charmhelpers.contrib.openstack.context.config') def test_configure_ovs_ovs_data_port(self, mock_config): mock_config.side_effect = self.test_config.get self.config.side_effect = self.test_config.get From 0cb03bd5313a29f556c7053d5251995f56b3813f Mon Sep 17 00:00:00 2001 From: Liam Young Date: Wed, 25 Mar 2015 07:58:50 +0000 Subject: [PATCH 10/12] Fix lint --- charm-helpers-hooks.yaml | 3 +-- hooks/quantum_contexts.py | 11 ----------- unit_tests/test_quantum_contexts.py | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml index 0b5106d1..84cc6c76 100644 --- a/charm-helpers-hooks.yaml +++ b/charm-helpers-hooks.yaml @@ -1,5 +1,4 @@ -#branch: lp:charm-helpers -branch: lp:~gnuoy/charm-helpers/neutron-shuffle +branch: lp:charm-helpers destination: hooks/charmhelpers include: - core diff --git a/hooks/quantum_contexts.py b/hooks/quantum_contexts.py index b6b1ef19..82f112e7 100644 --- a/hooks/quantum_contexts.py +++ b/hooks/quantum_contexts.py @@ -4,9 +4,6 @@ import uuid import socket from charmhelpers.core.hookenv import ( config, - relation_ids, - related_units, - relation_get, unit_get, cached ) @@ -15,8 +12,6 @@ from charmhelpers.fetch import ( ) from charmhelpers.contrib.openstack.context import ( OSContextGenerator, - context_complete, - NeutronPortContext, NeutronAPIContext, ) from charmhelpers.contrib.openstack.utils import ( @@ -29,14 +24,8 @@ from charmhelpers.contrib.network.ip import ( get_address_in_network, ) from charmhelpers.contrib.openstack.neutron import ( - parse_data_port_mappings, parse_vlan_range_mappings, ) -from charmhelpers.core.host import ( - get_nic_hwaddr, -) -from charmhelpers.core.strutils import bool_from_string -import copy DB_USER = "quantum" QUANTUM_DB = "quantum" diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index d92deb68..9ccc7d06 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -267,7 +267,7 @@ class TestQuantumGatewayContext(CharmTestCase): super(TestQuantumGatewayContext, self).setUp(quantum_contexts, TO_PATCH) self.config.side_effect = self.test_config.get - self.maxDiff = None + self.maxDiff = None @patch.object(quantum_contexts, 'get_shared_secret') @patch.object(quantum_contexts, 'get_host_ip') From 5bfe69f4d28f38d193c4d812300895a2f4229010 Mon Sep 17 00:00:00 2001 From: Liam Young Date: Thu, 26 Mar 2015 10:49:09 +0000 Subject: [PATCH 11/12] Fix juno templates --- templates/juno/ml2_conf.ini | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/templates/juno/ml2_conf.ini b/templates/juno/ml2_conf.ini index 5354b4c2..4c1dfa9d 100644 --- a/templates/juno/ml2_conf.ini +++ b/templates/juno/ml2_conf.ini @@ -14,20 +14,23 @@ tunnel_id_ranges = 1:1000 vni_ranges = 1001:2000 [ml2_type_vlan] -network_vlan_ranges = physnet1:1000:2000 +network_vlan_ranges = {{ vlan_ranges }} [ml2_type_flat] -flat_networks = physnet1 +flat_networks = {{ network_providers }} [ovs] enable_tunneling = True local_ip = {{ local_ip }} -bridge_mappings = physnet1:br-data +bridge_mappings = {{ bridge_mappings }} [agent] tunnel_types = {{ overlay_network_type }} l2_population = {{ l2_population }} enable_distributed_routing = {{ enable_dvr }} +{% if veth_mtu -%} +veth_mtu = {{ veth_mtu }} +{% endif %} [securitygroup] firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver From 1cd6743aeec64ee561cdc5471fd9bb60331e095c Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 31 Mar 2015 08:48:28 +0100 Subject: [PATCH 12/12] Fix unit tests --- unit_tests/test_quantum_contexts.py | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index 2d37dae4..f5513c74 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -38,6 +38,15 @@ def patch_open(): yield mock_open, mock_file +class DummyNeutronAPIContext(): + + def __init__(self, return_value): + self.return_value = return_value + + def __call__(self): + return self.return_value + + class TestL3AgentContext(CharmTestCase): def setUp(self): @@ -45,7 +54,10 @@ class TestL3AgentContext(CharmTestCase): TO_PATCH) self.config.side_effect = self.test_config.get - def test_no_ext_netid(self): + @patch('quantum_contexts.NeutronAPIContext') + def test_no_ext_netid(self, _NeutronAPIContext): + _NeutronAPIContext.return_value = \ + DummyNeutronAPIContext(return_value={'enable_dvr': False}) self.test_config.set('run-internal-router', 'none') self.test_config.set('external-network-id', '') self.eligible_leader.return_value = False @@ -54,7 +66,10 @@ class TestL3AgentContext(CharmTestCase): 'handle_internal_only_router': False, 'plugin': 'ovs'}) - def test_hior_leader(self): + @patch('quantum_contexts.NeutronAPIContext') + def test_hior_leader(self, _NeutronAPIContext): + _NeutronAPIContext.return_value = \ + DummyNeutronAPIContext(return_value={'enable_dvr': False}) self.test_config.set('run-internal-router', 'leader') self.test_config.set('external-network-id', 'netid') self.eligible_leader.return_value = True @@ -64,7 +79,10 @@ class TestL3AgentContext(CharmTestCase): 'ext_net_id': 'netid', 'plugin': 'ovs'}) - def test_hior_all(self): + @patch('quantum_contexts.NeutronAPIContext') + def test_hior_all(self, _NeutronAPIContext): + _NeutronAPIContext.return_value = \ + DummyNeutronAPIContext(return_value={'enable_dvr': False}) self.test_config.set('run-internal-router', 'all') self.test_config.set('external-network-id', 'netid') self.eligible_leader.return_value = True @@ -74,9 +92,10 @@ class TestL3AgentContext(CharmTestCase): 'ext_net_id': 'netid', 'plugin': 'ovs'}) - @patch.object(quantum_contexts, 'neutron_api_settings') - def test_dvr(self, _napi_settings): - _napi_settings.return_value = {'enable_dvr': True} + @patch('quantum_contexts.NeutronAPIContext') + def test_dvr(self, _NeutronAPIContext): + _NeutronAPIContext.return_value = \ + DummyNeutronAPIContext(return_value={'enable_dvr': True}) self.assertEquals(quantum_contexts.L3AgentContext()()['agent_mode'], 'dvr_snat')