diff --git a/hooks/memcache-relation-broken b/hooks/memcache-relation-broken new file mode 120000 index 00000000..f6702415 --- /dev/null +++ b/hooks/memcache-relation-broken @@ -0,0 +1 @@ +nova_cc_hooks.py \ No newline at end of file diff --git a/hooks/memcache-relation-changed b/hooks/memcache-relation-changed new file mode 120000 index 00000000..f6702415 --- /dev/null +++ b/hooks/memcache-relation-changed @@ -0,0 +1 @@ +nova_cc_hooks.py \ No newline at end of file diff --git a/hooks/memcache-relation-departed b/hooks/memcache-relation-departed new file mode 120000 index 00000000..f6702415 --- /dev/null +++ b/hooks/memcache-relation-departed @@ -0,0 +1 @@ +nova_cc_hooks.py \ No newline at end of file diff --git a/hooks/memcache-relation-joined b/hooks/memcache-relation-joined new file mode 120000 index 00000000..f6702415 --- /dev/null +++ b/hooks/memcache-relation-joined @@ -0,0 +1 @@ +nova_cc_hooks.py \ No newline at end of file diff --git a/hooks/nova_cc_context.py b/hooks/nova_cc_context.py index 3d0f79c9..c7a3fcf8 100644 --- a/hooks/nova_cc_context.py +++ b/hooks/nova_cc_context.py @@ -6,6 +6,7 @@ from charmhelpers.core.hookenv import ( ERROR, unit_get, related_units, + relations_for_id, relation_get, ) from charmhelpers.fetch import ( @@ -299,3 +300,24 @@ class NovaIPv6Context(context.BindHostContext): ctxt = super(NovaIPv6Context, self).__call__() ctxt['use_ipv6'] = config('prefer-ipv6') return ctxt + + +class InstanceConsoleContext(context.OSContextGenerator): + interfaces = [] + + def __call__(self): + ctxt = {} + servers = [] + try: + for rid in relation_ids('memcache'): + for rel in relations_for_id(rid): + priv_addr = rel['private-address'] + # Format it as IPv6 address if needed + priv_addr = format_ipv6_addr(priv_addr) or priv_addr + servers.append("%s:%s" % (priv_addr, rel['port'])) + except Exception as ex: + log("Could not get memcache servers: %s" % (ex), level='WARNING') + servers = [] + + ctxt['memcached_servers'] = ','.join(servers) + return ctxt diff --git a/hooks/nova_cc_hooks.py b/hooks/nova_cc_hooks.py index 6e53a903..003b240e 100755 --- a/hooks/nova_cc_hooks.py +++ b/hooks/nova_cc_hooks.py @@ -849,6 +849,15 @@ def neutron_api_relation_broken(): quantum_joined(rid=rid) +@hooks.hook('memcache-relation-joined', + 'memcache-relation-departed', + 'memcache-relation-changed', + 'memcache-relation-broken') +@restart_on_change(restart_map()) +def memcached_joined(): + CONFIGS.write(NOVA_CONF) + + def main(): try: hooks.execute(sys.argv) diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index 6a42dd81..4073089c 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -69,6 +69,7 @@ BASE_PACKAGES = [ 'python-psutil', 'python-six', 'uuid', + 'python-memcache', ] BASE_SERVICES = [ @@ -124,7 +125,8 @@ BASE_RESOURCE_MAP = OrderedDict([ nova_cc_context.VolumeServiceContext(), nova_cc_context.NovaIPv6Context(), nova_cc_context.NeutronCCContext(), - nova_cc_context.NovaConfigContext()], + nova_cc_context.NovaConfigContext(), + nova_cc_context.InstanceConsoleContext()], }), (NOVA_API_PASTE, { 'services': [s for s in BASE_SERVICES if 'api' in s], diff --git a/metadata.yaml b/metadata.yaml index 573e5232..f277b1cd 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -40,6 +40,8 @@ requires: nova-vmware: interface: nova-vmware scope: container + memcache: + interface: memcache peers: cluster: interface: nova-ha diff --git a/templates/folsom/nova.conf b/templates/folsom/nova.conf index ed0f13ac..0e84d5a1 100644 --- a/templates/folsom/nova.conf +++ b/templates/folsom/nova.conf @@ -21,6 +21,11 @@ volumes_path=/var/lib/nova/volumes enabled_apis=ec2,osapi_compute,metadata auth_strategy=keystone compute_driver=libvirt.LibvirtDriver + +{% if memcached_servers %} +memcached_servers = {{ memcached_servers }} +{% endif %} + {% if keystone_ec2_url -%} keystone_ec2_url = {{ keystone_ec2_url }} {% endif -%} diff --git a/templates/grizzly/nova.conf b/templates/grizzly/nova.conf index 9c308fae..4921d893 100644 --- a/templates/grizzly/nova.conf +++ b/templates/grizzly/nova.conf @@ -20,6 +20,11 @@ volumes_path=/var/lib/nova/volumes enabled_apis=ec2,osapi_compute,metadata auth_strategy=keystone compute_driver=libvirt.LibvirtDriver + +{% if memcached_servers %} +memcached_servers = {{ memcached_servers }} +{% endif %} + {% if keystone_ec2_url -%} keystone_ec2_url = {{ keystone_ec2_url }} {% endif -%} diff --git a/templates/havana/nova.conf b/templates/havana/nova.conf index ba354fb0..77b88110 100644 --- a/templates/havana/nova.conf +++ b/templates/havana/nova.conf @@ -27,6 +27,10 @@ cpu_allocation_ratio = {{ cpu_allocation_ratio }} use_syslog={{ use_syslog }} my_ip = {{ host_ip }} +{% if memcached_servers %} +memcached_servers = {{ memcached_servers }} +{% endif %} + {% if keystone_ec2_url -%} keystone_ec2_url = {{ keystone_ec2_url }} {% endif -%} diff --git a/templates/icehouse/nova.conf b/templates/icehouse/nova.conf index d70f8365..67385124 100644 --- a/templates/icehouse/nova.conf +++ b/templates/icehouse/nova.conf @@ -38,6 +38,10 @@ ram_allocation_ratio = {{ ram_allocation_ratio }} use_syslog={{ use_syslog }} my_ip = {{ host_ip }} +{% if memcached_servers %} +memcached_servers = {{ memcached_servers }} +{% endif %} + {% if keystone_ec2_url -%} keystone_ec2_url = {{ keystone_ec2_url }} {% endif -%} diff --git a/unit_tests/test_nova_cc_contexts.py b/unit_tests/test_nova_cc_contexts.py new file mode 100644 index 00000000..76486f34 --- /dev/null +++ b/unit_tests/test_nova_cc_contexts.py @@ -0,0 +1,87 @@ +from __future__ import print_function + +import mock + +##### +# NOTE(freyes): this is a workaround to patch config() function imported by +# nova_cc_utils before it gets a reference to the actual config() provided by +# hookenv module. +from charmhelpers.core import hookenv +_conf = hookenv.config +hookenv.config = mock.MagicMock() +import nova_cc_utils as _utils +# this assert is a double check + to avoid pep8 warning +assert _utils.config == hookenv.config +hookenv.config = _conf +##### + +import nova_cc_context as context + +from charmhelpers.contrib.openstack import utils + +from test_utils import CharmTestCase + + +TO_PATCH = [ + 'apt_install', + 'filter_installed_packages', + 'relation_ids', + 'relation_get', + 'related_units', + 'config', + 'log', + 'unit_get', + 'relations_for_id', +] + + +def fake_log(msg, level=None): + level = level or 'INFO' + print('[juju test log (%s)] %s' % (level, msg)) + + +class NovaComputeContextTests(CharmTestCase): + def setUp(self): + super(NovaComputeContextTests, self).setUp(context, TO_PATCH) + self.relation_get.side_effect = self.test_relation.get + self.config.side_effect = self.test_config.get + self.log.side_effect = fake_log + + @mock.patch.object(utils, 'os_release') + @mock.patch('charmhelpers.contrib.network.ip.log') + def test_instance_console_context_without_memcache(self, os_release, log_): + self.unit_get.return_value = '127.0.0.1' + self.relation_ids.return_value = 'cache:0' + self.related_units.return_value = 'memcached/0' + instance_console = context.InstanceConsoleContext() + os_release.return_value = 'icehouse' + self.assertEqual({'memcached_servers': ''}, + instance_console()) + + @mock.patch.object(utils, 'os_release') + @mock.patch('charmhelpers.contrib.network.ip.log') + def test_instance_console_context_with_memcache(self, os_release, log_): + self.check_instance_console_context_with_memcache(os_release, + '127.0.1.1', + '127.0.1.1') + + @mock.patch.object(utils, 'os_release') + @mock.patch('charmhelpers.contrib.network.ip.log') + def test_instance_console_context_with_memcache_ipv6(self, os_release, + log_): + self.check_instance_console_context_with_memcache(os_release, '::1', + '[::1]') + + def check_instance_console_context_with_memcache(self, os_release, ip, + formated_ip): + memcached_servers = [{'private-address': formated_ip, + 'port': '11211'}] + self.unit_get.return_value = ip + self.relation_ids.return_value = ['cache:0'] + self.relations_for_id.return_value = memcached_servers + self.related_units.return_value = 'memcached/0' + instance_console = context.InstanceConsoleContext() + os_release.return_value = 'icehouse' + self.maxDiff = None + self.assertEqual({'memcached_servers': "%s:11211" % (formated_ip, )}, + instance_console()) diff --git a/unit_tests/test_nova_cc_hooks.py b/unit_tests/test_nova_cc_hooks.py index 17eed8d2..d0ff0e70 100644 --- a/unit_tests/test_nova_cc_hooks.py +++ b/unit_tests/test_nova_cc_hooks.py @@ -1,6 +1,7 @@ from mock import MagicMock, patch, call from test_utils import CharmTestCase, patch_open import os + with patch('charmhelpers.core.hookenv.config') as config: config.return_value = 'neutron' import nova_cc_utils as utils