[james-page, r=gnuoy] Add 0mq support

This commit is contained in:
Liam Young 2015-04-01 16:13:54 +01:00
commit 679b42a79e
14 changed files with 364 additions and 73 deletions

View File

@ -15,6 +15,7 @@
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
import six import six
from collections import OrderedDict
from charmhelpers.contrib.amulet.deployment import ( from charmhelpers.contrib.amulet.deployment import (
AmuletDeployment AmuletDeployment
) )
@ -100,12 +101,34 @@ class OpenStackAmuletDeployment(AmuletDeployment):
""" """
(self.precise_essex, self.precise_folsom, self.precise_grizzly, (self.precise_essex, self.precise_folsom, self.precise_grizzly,
self.precise_havana, self.precise_icehouse, self.precise_havana, self.precise_icehouse,
self.trusty_icehouse) = range(6) self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
releases = { releases = {
('precise', None): self.precise_essex, ('precise', None): self.precise_essex,
('precise', 'cloud:precise-folsom'): self.precise_folsom, ('precise', 'cloud:precise-folsom'): self.precise_folsom,
('precise', 'cloud:precise-grizzly'): self.precise_grizzly, ('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
('precise', 'cloud:precise-havana'): self.precise_havana, ('precise', 'cloud:precise-havana'): self.precise_havana,
('precise', 'cloud:precise-icehouse'): self.precise_icehouse, ('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
('trusty', None): self.trusty_icehouse} ('trusty', None): self.trusty_icehouse,
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
def _get_openstack_release_string(self):
"""Get openstack release string.
Return a string representing the openstack release.
"""
releases = OrderedDict([
('precise', 'essex'),
('quantal', 'folsom'),
('raring', 'grizzly'),
('saucy', 'havana'),
('trusty', 'icehouse'),
('utopic', 'juno'),
('vivid', 'kilo'),
])
if self.openstack:
os_origin = self.openstack.split(':')[1]
return os_origin.split('%s-' % self.series)[1].split('/')[0]
else:
return releases[self.series]

View File

@ -47,6 +47,7 @@ from charmhelpers.core.hookenv import (
) )
from charmhelpers.core.sysctl import create as sysctl_create from charmhelpers.core.sysctl import create as sysctl_create
from charmhelpers.core.strutils import bool_from_string
from charmhelpers.core.host import ( from charmhelpers.core.host import (
list_nics, list_nics,
@ -67,6 +68,7 @@ from charmhelpers.contrib.hahelpers.apache import (
) )
from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.neutron import (
neutron_plugin_attribute, neutron_plugin_attribute,
parse_data_port_mappings,
) )
from charmhelpers.contrib.openstack.ip import ( from charmhelpers.contrib.openstack.ip import (
resolve_address, resolve_address,
@ -82,7 +84,6 @@ from charmhelpers.contrib.network.ip import (
is_bridge_member, is_bridge_member,
) )
from charmhelpers.contrib.openstack.utils import get_host_ip from charmhelpers.contrib.openstack.utils import get_host_ip
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
ADDRESS_TYPES = ['admin', 'internal', 'public'] ADDRESS_TYPES = ['admin', 'internal', 'public']
@ -1162,3 +1163,145 @@ class SysctlContext(OSContextGenerator):
sysctl_create(sysctl_dict, sysctl_create(sysctl_dict,
'/etc/sysctl.d/50-{0}.conf'.format(charm_name())) '/etc/sysctl.d/50-{0}.conf'.format(charm_name()))
return {'sysctl': sysctl_dict} 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 {}

View File

@ -0,0 +1,13 @@
description "{{ service_description }}"
author "Juju {{ service_name }} Charm <juju@localhost>"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
exec start-stop-daemon --start --chuid {{ user_name }} \
--chdir {{ start_dir }} --name {{ process_name }} \
--exec {{ executable_name }} -- \
--config-file={{ config_file }} \
--log-file={{ log_file }}

View File

@ -3,12 +3,12 @@
rpc_backend = zmq rpc_backend = zmq
rpc_zmq_host = {{ zmq_host }} rpc_zmq_host = {{ zmq_host }}
{% if zmq_redis_address -%} {% if zmq_redis_address -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis rpc_zmq_matchmaker = redis
matchmaker_heartbeat_freq = 15 matchmaker_heartbeat_freq = 15
matchmaker_heartbeat_ttl = 30 matchmaker_heartbeat_ttl = 30
[matchmaker_redis] [matchmaker_redis]
host = {{ zmq_redis_address }} host = {{ zmq_redis_address }}
{% else -%} {% else -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing rpc_zmq_matchmaker = ring
{% endif -%} {% endif -%}
{% endif -%} {% endif -%}

View File

@ -30,6 +30,10 @@ import yaml
from charmhelpers.contrib.network import ip from charmhelpers.contrib.network import ip
from charmhelpers.core import (
unitdata,
)
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
config, config,
log as juju_log, log as juju_log,
@ -330,6 +334,21 @@ def configure_installation_source(rel):
error_out("Invalid openstack-release specified: %s" % rel) error_out("Invalid openstack-release specified: %s" % rel)
def config_value_changed(option):
"""
Determine if config value changed since last call to this function.
"""
hook_data = unitdata.HookData()
with hook_data():
db = unitdata.kv()
current = config(option)
saved = db.get(option)
db.set(option, current)
if saved is None:
return False
return current != saved
def save_script_rc(script_path="scripts/scriptrc", **env_vars): def save_script_rc(script_path="scripts/scriptrc", **env_vars):
""" """
Write an rc file in the charm-delivered directory containing Write an rc file in the charm-delivered directory containing
@ -469,82 +488,95 @@ def os_requires_version(ostack_release, pkg):
def git_install_requested(): def git_install_requested():
"""Returns true if openstack-origin-git is specified.""" """
return config('openstack-origin-git') != "None" Returns true if openstack-origin-git is specified.
"""
return config('openstack-origin-git') is not None
requirements_dir = None requirements_dir = None
def git_clone_and_install(file_name, core_project): def git_clone_and_install(projects_yaml, core_project):
"""Clone/install all OpenStack repos specified in yaml config file.""" """
global requirements_dir Clone/install all specified OpenStack repositories.
if file_name == "None": The expected format of projects_yaml is:
repositories:
- {name: keystone,
repository: 'git://git.openstack.org/openstack/keystone.git',
branch: 'stable/icehouse'}
- {name: requirements,
repository: 'git://git.openstack.org/openstack/requirements.git',
branch: 'stable/icehouse'}
directory: /mnt/openstack-git
The directory key is optional.
"""
global requirements_dir
parent_dir = '/mnt/openstack-git'
if not projects_yaml:
return return
yaml_file = os.path.join(charm_dir(), file_name) projects = yaml.load(projects_yaml)
_git_validate_projects_yaml(projects, core_project)
# clone/install the requirements project first if 'directory' in projects.keys():
installed = _git_clone_and_install_subset(yaml_file, parent_dir = projects['directory']
whitelist=['requirements'])
if 'requirements' not in installed:
error_out('requirements git repository must be specified')
# clone/install all other projects except requirements and the core project for p in projects['repositories']:
blacklist = ['requirements', core_project] repo = p['repository']
_git_clone_and_install_subset(yaml_file, blacklist=blacklist, branch = p['branch']
update_requirements=True) if p['name'] == 'requirements':
repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
# clone/install the core project update_requirements=False)
whitelist = [core_project] requirements_dir = repo_dir
installed = _git_clone_and_install_subset(yaml_file, whitelist=whitelist, else:
update_requirements=True) repo_dir = _git_clone_and_install_single(repo, branch, parent_dir,
if core_project not in installed: update_requirements=True)
error_out('{} git repository must be specified'.format(core_project))
def _git_clone_and_install_subset(yaml_file, whitelist=[], blacklist=[], def _git_validate_projects_yaml(projects, core_project):
update_requirements=False): """
"""Clone/install subset of OpenStack repos specified in yaml config file.""" Validate the projects yaml.
global requirements_dir """
installed = [] _git_ensure_key_exists('repositories', projects)
with open(yaml_file, 'r') as fd: for project in projects['repositories']:
projects = yaml.load(fd) _git_ensure_key_exists('name', project.keys())
for proj, val in projects.items(): _git_ensure_key_exists('repository', project.keys())
# The project subset is chosen based on the following 3 rules: _git_ensure_key_exists('branch', project.keys())
# 1) If project is in blacklist, we don't clone/install it, period.
# 2) If whitelist is empty, we clone/install everything else. if projects['repositories'][0]['name'] != 'requirements':
# 3) If whitelist is not empty, we clone/install everything in the error_out('{} git repo must be specified first'.format('requirements'))
# whitelist.
if proj in blacklist: if projects['repositories'][-1]['name'] != core_project:
continue error_out('{} git repo must be specified last'.format(core_project))
if whitelist and proj not in whitelist:
continue
repo = val['repository']
branch = val['branch']
repo_dir = _git_clone_and_install_single(repo, branch,
update_requirements)
if proj == 'requirements':
requirements_dir = repo_dir
installed.append(proj)
return installed
def _git_clone_and_install_single(repo, branch, update_requirements=False): def _git_ensure_key_exists(key, keys):
"""Clone and install a single git repository.""" """
dest_parent_dir = "/mnt/openstack-git/" Ensure that key exists in keys.
dest_dir = os.path.join(dest_parent_dir, os.path.basename(repo)) """
if key not in keys:
error_out('openstack-origin-git key \'{}\' is missing'.format(key))
if not os.path.exists(dest_parent_dir):
juju_log('Host dir not mounted at {}. ' def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements):
'Creating directory there instead.'.format(dest_parent_dir)) """
os.mkdir(dest_parent_dir) Clone and install a single git repository.
"""
dest_dir = os.path.join(parent_dir, os.path.basename(repo))
if not os.path.exists(parent_dir):
juju_log('Directory already exists at {}. '
'No need to create directory.'.format(parent_dir))
os.mkdir(parent_dir)
if not os.path.exists(dest_dir): if not os.path.exists(dest_dir):
juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch)) juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch))
repo_dir = install_remote(repo, dest=dest_parent_dir, branch=branch) repo_dir = install_remote(repo, dest=parent_dir, branch=branch)
else: else:
repo_dir = dest_dir repo_dir = dest_dir
@ -561,16 +593,39 @@ def _git_clone_and_install_single(repo, branch, update_requirements=False):
def _git_update_requirements(package_dir, reqs_dir): def _git_update_requirements(package_dir, reqs_dir):
"""Update from global requirements. """
Update from global requirements.
Update an OpenStack git directory's requirements.txt and Update an OpenStack git directory's requirements.txt and
test-requirements.txt from global-requirements.txt.""" test-requirements.txt from global-requirements.txt.
"""
orig_dir = os.getcwd() orig_dir = os.getcwd()
os.chdir(reqs_dir) os.chdir(reqs_dir)
cmd = "python update.py {}".format(package_dir) cmd = ['python', 'update.py', package_dir]
try: try:
subprocess.check_call(cmd.split(' ')) subprocess.check_call(cmd)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
package = os.path.basename(package_dir) package = os.path.basename(package_dir)
error_out("Error updating {} from global-requirements.txt".format(package)) error_out("Error updating {} from global-requirements.txt".format(package))
os.chdir(orig_dir) os.chdir(orig_dir)
def git_src_dir(projects_yaml, project):
"""
Return the directory where the specified project's source is located.
"""
parent_dir = '/mnt/openstack-git'
if not projects_yaml:
return
projects = yaml.load(projects_yaml)
if 'directory' in projects.keys():
parent_dir = projects['directory']
for p in projects['repositories']:
if p['name'] == project:
return os.path.join(parent_dir, os.path.basename(p['repository']))
return None

View File

@ -443,7 +443,7 @@ class HookData(object):
data = hookenv.execution_environment() data = hookenv.execution_environment()
self.conf = conf_delta = self.kv.delta(data['conf'], 'config') self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
self.rels = rels_delta = self.kv.delta(data['rels'], 'rels') self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
self.kv.set('env', data['env']) self.kv.set('env', dict(data['env']))
self.kv.set('unit', data['unit']) self.kv.set('unit', data['unit'])
self.kv.set('relid', data.get('relid')) self.kv.set('relid', data.get('relid'))
return conf_delta, rels_delta return conf_delta, rels_delta

View File

@ -30,6 +30,7 @@ from charmhelpers.fetch import (
from charmhelpers.contrib.openstack.utils import ( from charmhelpers.contrib.openstack.utils import (
configure_installation_source, configure_installation_source,
openstack_upgrade_available, openstack_upgrade_available,
os_requires_version,
) )
from charmhelpers.contrib.storage.linux.ceph import ( from charmhelpers.contrib.storage.linux.ceph import (
@ -57,6 +58,7 @@ from nova_compute_utils import (
ceph_config_file, CEPH_SECRET, ceph_config_file, CEPH_SECRET,
enable_shell, disable_shell, enable_shell, disable_shell,
fix_path_ownership, fix_path_ownership,
get_topics,
assert_charm_supports_ipv6, assert_charm_supports_ipv6,
manage_ovs, manage_ovs,
) )
@ -118,6 +120,8 @@ def config_changed():
fix_path_ownership(fp, user='nova') fix_path_ownership(fp, user='nova')
[compute_joined(rid) for rid in relation_ids('cloud-compute')] [compute_joined(rid) for rid in relation_ids('cloud-compute')]
for rid in relation_ids('zeromq-configuration'):
zeromq_configuration_relation_joined(rid)
update_nrpe_config() update_nrpe_config()
@ -316,6 +320,20 @@ def nova_ceilometer_relation_changed():
CONFIGS.write_all() CONFIGS.write_all()
@hooks.hook('zeromq-configuration-relation-joined')
@os_requires_version('kilo', 'nova-common')
def zeromq_configuration_relation_joined(relid=None):
relation_set(relation_id=relid,
topics=" ".join(get_topics()),
users="nova")
@hooks.hook('zeromq-configuration-relation-changed')
@restart_on_change(restart_map())
def zeromq_configuration_relation_changed():
CONFIGS.write(NOVA_CONF)
@hooks.hook('nrpe-external-master-relation-joined', @hooks.hook('nrpe-external-master-relation-joined',
'nrpe-external-master-relation-changed') 'nrpe-external-master-relation-changed')
def update_nrpe_config(): def update_nrpe_config():

View File

@ -101,6 +101,8 @@ BASE_RESOURCE_MAP = {
service='nova', service='nova',
config_file=NOVA_CONF), config_file=NOVA_CONF),
InstanceConsoleContext(), InstanceConsoleContext(),
context.ZeroMQContext(),
context.NotificationDriverContext(),
MetadataServiceContext(), MetadataServiceContext(),
HostIPContext()], HostIPContext()],
}, },
@ -496,6 +498,10 @@ def fix_path_ownership(path, user='nova'):
check_call(cmd) check_call(cmd)
def get_topics():
return ['compute']
def assert_charm_supports_ipv6(): def assert_charm_supports_ipv6():
"""Check whether we are able to support charms ipv6.""" """Check whether we are able to support charms ipv6."""
if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty": if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty":

View File

@ -0,0 +1 @@
nova_compute_hooks.py

View File

@ -0,0 +1 @@
nova_compute_hooks.py

View File

@ -30,6 +30,6 @@ requires:
neutron-plugin: neutron-plugin:
interface: neutron-plugin interface: neutron-plugin
scope: container scope: container
peers: zeromq-configuration:
compute-peer: interface: zeromq-configuration
interface: nova scope: container

View File

@ -101,6 +101,8 @@ instances_path = {{ instances_path }}
{% endfor -%} {% endfor -%}
{% endif -%} {% endif -%}
{% include "section-zeromq" %}
{% if network_manager == 'neutron' and network_manager_config -%} {% if network_manager == 'neutron' and network_manager_config -%}
[neutron] [neutron]
url = {{ network_manager_config.neutron_url }} url = {{ network_manager_config.neutron_url }}

View File

@ -15,6 +15,7 @@
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
import six import six
from collections import OrderedDict
from charmhelpers.contrib.amulet.deployment import ( from charmhelpers.contrib.amulet.deployment import (
AmuletDeployment AmuletDeployment
) )
@ -100,12 +101,34 @@ class OpenStackAmuletDeployment(AmuletDeployment):
""" """
(self.precise_essex, self.precise_folsom, self.precise_grizzly, (self.precise_essex, self.precise_folsom, self.precise_grizzly,
self.precise_havana, self.precise_icehouse, self.precise_havana, self.precise_icehouse,
self.trusty_icehouse) = range(6) self.trusty_icehouse, self.trusty_juno, self.trusty_kilo) = range(8)
releases = { releases = {
('precise', None): self.precise_essex, ('precise', None): self.precise_essex,
('precise', 'cloud:precise-folsom'): self.precise_folsom, ('precise', 'cloud:precise-folsom'): self.precise_folsom,
('precise', 'cloud:precise-grizzly'): self.precise_grizzly, ('precise', 'cloud:precise-grizzly'): self.precise_grizzly,
('precise', 'cloud:precise-havana'): self.precise_havana, ('precise', 'cloud:precise-havana'): self.precise_havana,
('precise', 'cloud:precise-icehouse'): self.precise_icehouse, ('precise', 'cloud:precise-icehouse'): self.precise_icehouse,
('trusty', None): self.trusty_icehouse} ('trusty', None): self.trusty_icehouse,
('trusty', 'cloud:trusty-juno'): self.trusty_juno,
('trusty', 'cloud:trusty-kilo'): self.trusty_kilo}
return releases[(self.series, self.openstack)] return releases[(self.series, self.openstack)]
def _get_openstack_release_string(self):
"""Get openstack release string.
Return a string representing the openstack release.
"""
releases = OrderedDict([
('precise', 'essex'),
('quantal', 'folsom'),
('raring', 'grizzly'),
('saucy', 'havana'),
('trusty', 'icehouse'),
('utopic', 'juno'),
('vivid', 'kilo'),
])
if self.openstack:
os_origin = self.openstack.split(':')[1]
return os_origin.split('%s-' % self.series)[1].split('/')[0]
else:
return releases[self.series]

View File

@ -85,6 +85,7 @@ class NovaComputeRelationsTests(CharmTestCase):
@patch.object(hooks, 'compute_joined') @patch.object(hooks, 'compute_joined')
def test_config_changed_with_migration(self, compute_joined): def test_config_changed_with_migration(self, compute_joined):
self.migration_enabled.return_value = True self.migration_enabled.return_value = True
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
self.test_config.set('migration-auth-type', 'ssh') self.test_config.set('migration-auth-type', 'ssh')
self.relation_ids.return_value = [ self.relation_ids.return_value = [
'cloud-compute:0', 'cloud-compute:0',
@ -97,10 +98,12 @@ class NovaComputeRelationsTests(CharmTestCase):
] ]
self.assertEquals(ex, compute_joined.call_args_list) self.assertEquals(ex, compute_joined.call_args_list)
self.assertTrue(self.initialize_ssh_keys.called) self.assertTrue(self.initialize_ssh_keys.called)
self.assertTrue(_zmq_joined.called)
@patch.object(hooks, 'compute_joined') @patch.object(hooks, 'compute_joined')
def test_config_changed_with_resize(self, compute_joined): def test_config_changed_with_resize(self, compute_joined):
self.test_config.set('enable-resize', True) self.test_config.set('enable-resize', True)
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
self.relation_ids.return_value = [ self.relation_ids.return_value = [
'cloud-compute:0', 'cloud-compute:0',
'cloud-compute:1' 'cloud-compute:1'
@ -113,10 +116,12 @@ class NovaComputeRelationsTests(CharmTestCase):
self.assertEquals(ex, compute_joined.call_args_list) self.assertEquals(ex, compute_joined.call_args_list)
self.initialize_ssh_keys.assert_called_with(user='nova') self.initialize_ssh_keys.assert_called_with(user='nova')
self.enable_shell.assert_called_with(user='nova') self.enable_shell.assert_called_with(user='nova')
self.assertTrue(_zmq_joined.called)
@patch.object(hooks, 'compute_joined') @patch.object(hooks, 'compute_joined')
def test_config_changed_without_resize(self, compute_joined): def test_config_changed_without_resize(self, compute_joined):
self.test_config.set('enable-resize', False) self.test_config.set('enable-resize', False)
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
self.relation_ids.return_value = [ self.relation_ids.return_value = [
'cloud-compute:0', 'cloud-compute:0',
'cloud-compute:1' 'cloud-compute:1'
@ -128,6 +133,7 @@ class NovaComputeRelationsTests(CharmTestCase):
] ]
self.assertEquals(ex, compute_joined.call_args_list) self.assertEquals(ex, compute_joined.call_args_list)
self.disable_shell.assert_called_with(user='nova') self.disable_shell.assert_called_with(user='nova')
self.assertTrue(_zmq_joined.called)
@patch.object(hooks, 'compute_joined') @patch.object(hooks, 'compute_joined')
def test_config_changed_no_upgrade_no_migration(self, compute_joined): def test_config_changed_no_upgrade_no_migration(self, compute_joined):