diff --git a/config.yaml b/config.yaml index d7b9ad45..63011664 100644 --- a/config.yaml +++ b/config.yaml @@ -50,6 +50,11 @@ options: type: string description: RabbitMQ Virtual Host default: openstack + use-syslog: + type: boolean + default: False + description: | + If set to True, supporting services will log to syslog. instance-mtu: type: int description: | diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index a2d4636b..6014ee1b 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -199,6 +199,7 @@ class AMQPContext(OSContextGenerator): ctxt = {} for rid in relation_ids('amqp'): + ha_vip_only = False for unit in related_units(rid): if relation_get('clustered', rid=rid, unit=unit): ctxt['clustered'] = True @@ -213,16 +214,18 @@ class AMQPContext(OSContextGenerator): unit=unit), 'rabbitmq_virtual_host': vhost, }) + if relation_get('ha_queues', rid=rid, unit=unit) is not None: + ctxt['rabbitmq_ha_queues'] = True + + ha_vip_only = relation_get('ha-vip-only', + rid=rid, unit=unit) is not None + if context_complete(ctxt): # Sufficient information found = break out! break # Used for active/active rabbitmq >= grizzly - if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \ - len(related_units(rid)) > 1: - if relation_get('ha_queues'): - ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues') - else: - ctxt['rabbitmq_ha_queues'] = False + if ('clustered' not in ctxt or ha_vip_only) \ + and len(related_units(rid)) > 1: rabbitmq_hosts = [] for unit in related_units(rid): rabbitmq_hosts.append(relation_get('private-address', diff --git a/hooks/charmhelpers/contrib/openstack/neutron.py b/hooks/charmhelpers/contrib/openstack/neutron.py index 8d32bd00..5badc39c 100644 --- a/hooks/charmhelpers/contrib/openstack/neutron.py +++ b/hooks/charmhelpers/contrib/openstack/neutron.py @@ -18,6 +18,22 @@ def headers_package(): return 'linux-headers-%s' % kver +def kernel_version(): + """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """ + kver = check_output(['uname', '-r']).strip() + kver = kver.split('.') + return (int(kver[0]), int(kver[1])) + + +def determine_dkms_package(): + """ Determine which DKMS package should be used based on kernel version """ + # NOTE: 3.13 kernels have support for GRE and VXLAN native + if kernel_version() >= (3, 13): + return [] + else: + return ['openvswitch-datapath-dkms'] + + # legacy def quantum_plugins(): from charmhelpers.contrib.openstack import context @@ -32,7 +48,7 @@ def quantum_plugins(): database=config('neutron-database'), relation_prefix='neutron')], 'services': ['quantum-plugin-openvswitch-agent'], - 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], + 'packages': [[headers_package()] + determine_dkms_package(), ['quantum-plugin-openvswitch-agent']], 'server_packages': ['quantum-server', 'quantum-plugin-openvswitch'], @@ -57,7 +73,8 @@ def quantum_plugins(): def neutron_plugins(): from charmhelpers.contrib.openstack import context - return { + release = os_release('nova-common') + plugins = { 'ovs': { 'config': '/etc/neutron/plugins/openvswitch/' 'ovs_neutron_plugin.ini', @@ -68,8 +85,8 @@ def neutron_plugins(): database=config('neutron-database'), relation_prefix='neutron')], 'services': ['neutron-plugin-openvswitch-agent'], - 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], - ['quantum-plugin-openvswitch-agent']], + 'packages': [[headers_package()] + determine_dkms_package(), + ['neutron-plugin-openvswitch-agent']], 'server_packages': ['neutron-server', 'neutron-plugin-openvswitch'], 'server_services': ['neutron-server'] @@ -89,6 +106,13 @@ def neutron_plugins(): 'server_services': ['neutron-server'] } } + # NOTE: patch in ml2 plugin for icehouse onwards + if release >= 'icehouse': + plugins['ovs']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini' + plugins['ovs']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin' + plugins['ovs']['server_packages'] = ['neutron-server', + 'neutron-plugin-ml2'] + return plugins def neutron_plugin_attribute(plugin, attr, net_manager=None): diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 56d04245..3261adf4 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -65,6 +65,9 @@ SWIFT_CODENAMES = OrderedDict([ ('1.10.0', 'havana'), ('1.9.1', 'havana'), ('1.9.0', 'havana'), + ('1.13.0', 'icehouse'), + ('1.12.0', 'icehouse'), + ('1.11.0', 'icehouse'), ]) DEFAULT_LOOPBACK_SIZE = '5G' @@ -420,19 +423,19 @@ def get_hostname(address, fqdn=True): Resolves hostname for given IP, or returns the input if it is already a hostname. """ - if not is_ip(address): - return address + if is_ip(address): + try: + import dns.reversename + except ImportError: + apt_install('python-dnspython') + import dns.reversename - try: - import dns.reversename - except ImportError: - apt_install('python-dnspython') - import dns.reversename - - rev = dns.reversename.from_address(address) - result = ns_query(rev) - if not result: - return None + rev = dns.reversename.from_address(address) + result = ns_query(rev) + if not result: + return None + else: + result = address if fqdn: # strip trailing . diff --git a/hooks/charmhelpers/contrib/storage/linux/utils.py b/hooks/charmhelpers/contrib/storage/linux/utils.py index c40218f0..5349c3ea 100644 --- a/hooks/charmhelpers/contrib/storage/linux/utils.py +++ b/hooks/charmhelpers/contrib/storage/linux/utils.py @@ -22,4 +22,5 @@ def zap_disk(block_device): :param block_device: str: Full path of block device to clean. ''' - check_call(['sgdisk', '--zap-all', '--mbrtogpt', block_device]) + check_call(['sgdisk', '--zap-all', '--clear', + '--mbrtogpt', block_device]) diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py index c05e0335..97a19912 100644 --- a/hooks/charmhelpers/fetch/__init__.py +++ b/hooks/charmhelpers/fetch/__init__.py @@ -97,6 +97,29 @@ def apt_install(packages, options=None, fatal=False): subprocess.call(cmd, env=env) +def apt_upgrade(options=None, fatal=False, dist=False): + """Upgrade all packages""" + if options is None: + options = ['--option=Dpkg::Options::=--force-confold'] + + cmd = ['apt-get', '--assume-yes'] + cmd.extend(options) + if dist: + cmd.append('dist-upgrade') + else: + cmd.append('upgrade') + log("Upgrading with options: {}".format(options)) + + env = os.environ.copy() + if 'DEBIAN_FRONTEND' not in env: + env['DEBIAN_FRONTEND'] = 'noninteractive' + + if fatal: + subprocess.check_call(cmd, env=env) + else: + subprocess.call(cmd, env=env) + + def apt_update(fatal=False): """Update local apt cache""" cmd = ['apt-get', 'update'] diff --git a/hooks/charmhelpers/fetch/archiveurl.py b/hooks/charmhelpers/fetch/archiveurl.py index e35b8f15..87e7071a 100644 --- a/hooks/charmhelpers/fetch/archiveurl.py +++ b/hooks/charmhelpers/fetch/archiveurl.py @@ -1,5 +1,7 @@ import os import urllib2 +import urlparse + from charmhelpers.fetch import ( BaseFetchHandler, UnhandledSource @@ -24,6 +26,19 @@ class ArchiveUrlFetchHandler(BaseFetchHandler): def download(self, source, dest): # propogate all exceptions # URLError, OSError, etc + proto, netloc, path, params, query, fragment = urlparse.urlparse(source) + if proto in ('http', 'https'): + auth, barehost = urllib2.splituser(netloc) + if auth is not None: + source = urlparse.urlunparse((proto, barehost, path, params, query, fragment)) + username, password = urllib2.splitpasswd(auth) + passman = urllib2.HTTPPasswordMgrWithDefaultRealm() + # Realm is set to None in add_password to force the username and password + # to be used whatever the realm + passman.add_password(None, source, username, password) + authhandler = urllib2.HTTPBasicAuthHandler(passman) + opener = urllib2.build_opener(authhandler) + urllib2.install_opener(opener) response = urllib2.urlopen(source) try: with open(dest, 'w') as dest_file: diff --git a/hooks/quantum_utils.py b/hooks/quantum_utils.py index 45d318af..7e500c0f 100644 --- a/hooks/quantum_utils.py +++ b/hooks/quantum_utils.py @@ -25,6 +25,9 @@ from charmhelpers.contrib.openstack.utils import ( ) import charmhelpers.contrib.openstack.context as context +from charmhelpers.contrib.openstack.context import ( + SyslogContext +) import charmhelpers.contrib.openstack.templating as templating from charmhelpers.contrib.openstack.neutron import headers_package from quantum_contexts import ( @@ -150,7 +153,8 @@ NOVA_CONFIG_FILES = { 'hook_contexts': [context.AMQPContext(), QuantumSharedDBContext(), NetworkServiceContext(), - QuantumGatewayContext()], + QuantumGatewayContext(), + SyslogContext()], 'services': ['nova-api-metadata'] }, } @@ -188,7 +192,8 @@ NEUTRON_SHARED_CONFIG_FILES.update(NOVA_CONFIG_FILES) QUANTUM_OVS_CONFIG_FILES = { QUANTUM_CONF: { 'hook_contexts': [context.AMQPContext(), - QuantumGatewayContext()], + QuantumGatewayContext(), + SyslogContext()], 'services': ['quantum-l3-agent', 'quantum-dhcp-agent', 'quantum-metadata-agent', @@ -214,7 +219,8 @@ QUANTUM_OVS_CONFIG_FILES.update(QUANTUM_SHARED_CONFIG_FILES) NEUTRON_OVS_CONFIG_FILES = { NEUTRON_CONF: { 'hook_contexts': [context.AMQPContext(), - QuantumGatewayContext()], + QuantumGatewayContext(), + SyslogContext()], 'services': ['neutron-l3-agent', 'neutron-dhcp-agent', 'neutron-metadata-agent', diff --git a/revision b/revision index 4b9026d8..4b6f9c39 100644 --- a/revision +++ b/revision @@ -1 +1 @@ -63 +64 \ No newline at end of file diff --git a/templates/folsom/metadata_agent.ini b/templates/folsom/metadata_agent.ini index 44624192..fae631ae 100644 --- a/templates/folsom/metadata_agent.ini +++ b/templates/folsom/metadata_agent.ini @@ -14,4 +14,4 @@ nova_metadata_port = 8775 # shared secret to prevent spoofing. You may select any string for a secret, # but it must match here and in the configuration used by the Nova Metadata # Server. NOTE: Nova uses a different key: quantum_metadata_proxy_shared_secret -metadata_proxy_shared_secret = {{ shared_secret }} +metadata_proxy_shared_secret = {{ shared_secret }} \ No newline at end of file diff --git a/templates/folsom/nova.conf b/templates/folsom/nova.conf index e58cfb32..6be73556 100644 --- a/templates/folsom/nova.conf +++ b/templates/folsom/nova.conf @@ -4,6 +4,7 @@ state_path=/var/lib/nova lock_path=/var/lock/nova root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf verbose=True +use_syslog = {{ use_syslog }} api_paste_config=/etc/nova/api-paste.ini enabled_apis=metadata multi_host=True diff --git a/templates/folsom/quantum.conf b/templates/folsom/quantum.conf index be3ace39..a8054f80 100644 --- a/templates/folsom/quantum.conf +++ b/templates/folsom/quantum.conf @@ -5,6 +5,7 @@ rabbit_virtual_host = {{ rabbitmq_virtual_host }} rabbit_host = {{ rabbitmq_host }} rabbit_password = {{ rabbitmq_password }} debug = True +use_syslog = {{ use_syslog }} bind_host = 0.0.0.0 bind_port = 9696 core_plugin = {{ core_plugin }} diff --git a/templates/havana/neutron.conf b/templates/havana/neutron.conf index 74d99763..e7d97d38 100644 --- a/templates/havana/neutron.conf +++ b/templates/havana/neutron.conf @@ -5,6 +5,7 @@ rabbit_virtual_host = {{ rabbitmq_virtual_host }} rabbit_host = {{ rabbitmq_host }} rabbit_password = {{ rabbitmq_password }} debug = True +use_syslog = {{ use_syslog }} bind_host = 0.0.0.0 bind_port = 9696 core_plugin = {{ core_plugin }} diff --git a/templates/havana/nova.conf b/templates/havana/nova.conf index df66747c..5a009d5d 100644 --- a/templates/havana/nova.conf +++ b/templates/havana/nova.conf @@ -4,6 +4,7 @@ state_path=/var/lib/nova lock_path=/var/lock/nova root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf verbose=True +use_syslog = {{ use_syslog }} api_paste_config=/etc/nova/api-paste.ini enabled_apis=metadata multi_host=True diff --git a/unit_tests/test_quantum_contexts.py b/unit_tests/test_quantum_contexts.py index 147cb652..9db1b1f5 100644 --- a/unit_tests/test_quantum_contexts.py +++ b/unit_tests/test_quantum_contexts.py @@ -1,5 +1,10 @@ -from mock import MagicMock, patch +from mock import ( + Mock, + MagicMock, + patch +) import quantum_contexts +import sys from contextlib import contextmanager from test_utils import ( @@ -199,6 +204,7 @@ class TestQuantumGatewayContext(CharmTestCase): def setUp(self): super(TestQuantumGatewayContext, self).setUp(quantum_contexts, TO_PATCH) + self.config.side_effect = self.test_config.get @patch.object(quantum_contexts, 'get_shared_secret') @patch.object(quantum_contexts, 'get_host_ip') @@ -256,6 +262,19 @@ class TestHostIP(CharmTestCase): super(TestHostIP, self).setUp(quantum_contexts, TO_PATCH) self.config.side_effect = self.test_config.get + # Save and inject + self.mods = {'dns': None, 'dns.resolver': None} + for mod in self.mods: + if mod not in sys.modules: + sys.modules[mod] = Mock() + else: + del self.mods[mod] + + def tearDown(self): + super(TestHostIP, self).tearDown() + # Cleanup + for mod in self.mods.keys(): + del sys.modules[mod] def test_get_host_ip_already_ip(self): self.assertEquals(quantum_contexts.get_host_ip('10.5.0.1'), @@ -268,8 +287,7 @@ class TestHostIP(CharmTestCase): @patch('dns.resolver.query') def test_get_host_ip_hostname_unresolvable(self, _query): - class NXDOMAIN(Exception): - pass + class NXDOMAIN(Exception): pass _query.side_effect = NXDOMAIN() self.assertRaises(NXDOMAIN, quantum_contexts.get_host_ip, 'missing.example.com')