From ea95b0dc5ce30714e9cd74b42ca8e3b620205945 Mon Sep 17 00:00:00 2001 From: Theodoros Tsioutsias Date: Thu, 7 Mar 2019 16:39:03 +0000 Subject: [PATCH] ng-3: Adapt existing drivers The existing drivers are adapted to get node_count and master_count information from the cluster's nodegroups. At the same time the output mappings were updated to reflect the changes in the stack to the nodegroups. story: 2005266 Change-Id: I725413e77f5a7bdb48131e8a10e5dc884b5e066a --- magnum/drivers/heat/driver.py | 31 ++- .../drivers/heat/k8s_coreos_template_def.py | 43 +++- .../drivers/heat/k8s_fedora_template_def.py | 21 +- magnum/drivers/heat/k8s_template_def.py | 34 +++- .../drivers/heat/swarm_fedora_template_def.py | 48 ++++- .../drivers/heat/swarm_mode_template_def.py | 63 ++++-- magnum/drivers/heat/template_def.py | 188 +++++++++++++++--- .../drivers/mesos_ubuntu_v1/template_def.py | 45 ++++- .../handlers/test_cluster_conductor.py | 8 +- .../handlers/test_k8s_cluster_conductor.py | 148 +++++++++----- .../handlers/test_mesos_cluster_conductor.py | 92 ++++++++- .../handlers/test_swarm_cluster_conductor.py | 92 ++++++++- magnum/tests/unit/drivers/test_heat_driver.py | 57 ++++-- .../unit/drivers/test_template_definition.py | 146 ++++++++++---- 14 files changed, 820 insertions(+), 196 deletions(-) diff --git a/magnum/drivers/heat/driver.py b/magnum/drivers/heat/driver.py index 9b6ac5c7f2..18d256d0b1 100755 --- a/magnum/drivers/heat/driver.py +++ b/magnum/drivers/heat/driver.py @@ -164,11 +164,17 @@ class HeatDriver(driver.Driver): rollback=False): definition = self.get_template_definition() + osc = clients.OpenStackClients(context) heat_params = {} - # stack node_count parameter name - stack_nc_param = definition.get_heat_param(cluster_attr='node_count') - heat_params[stack_nc_param] = cluster.node_count + # Find what changed checking the stack params + # against the ones in the template_def. + stack = osc.heat().stacks.get(cluster.stack_id, + resolve_outputs=True) + stack_params = stack.parameters + definition.add_nodegroup_params(cluster) + heat_params = definition.get_stack_diff(context, stack_params, cluster) + LOG.debug('Updating stack with these params: %s', heat_params) scale_params = definition.get_scale_params(context, cluster, scale_manager) @@ -180,16 +186,22 @@ class HeatDriver(driver.Driver): 'disable_rollback': not rollback } - osc = clients.OpenStackClients(context) osc.heat().stacks.update(cluster.stack_id, **fields) def _resize_stack(self, context, cluster, resize_manager, node_count, nodes_to_remove, nodegroup=None, rollback=False): definition = self.get_template_definition() - heat_params = {} - stack_nc_param = definition.get_heat_param(cluster_attr='node_count') - heat_params[stack_nc_param] = node_count or cluster.node_count + osc = clients.OpenStackClients(context) + + # Find what changed checking the stack params + # against the ones in the template_def. + stack = osc.heat().stacks.get(cluster.stack_id, + resolve_outputs=True) + stack_params = stack.parameters + definition.add_nodegroup_params(cluster) + heat_params = definition.get_stack_diff(context, stack_params, cluster) + LOG.debug('Updating stack with these params: %s', heat_params) scale_params = definition.get_scale_params(context, cluster, @@ -244,6 +256,8 @@ class HeatPoller(object): self._sync_cluster_and_template_status(stack) elif stack.stack_status != self.cluster.status: + self.template_def.update_outputs(stack, self.cluster_template, + self.cluster) self._sync_cluster_status(stack) if stack.stack_status in (fields.ClusterStatus.CREATE_FAILED, @@ -273,9 +287,6 @@ class HeatPoller(object): def _sync_cluster_status(self, stack): self.cluster.status = stack.stack_status self.cluster.status_reason = stack.stack_status_reason - stack_nc_param = self.template_def.get_heat_param( - cluster_attr='node_count') - self.cluster.node_count = stack.parameters[stack_nc_param] self.cluster.save() def get_version_info(self, stack): diff --git a/magnum/drivers/heat/k8s_coreos_template_def.py b/magnum/drivers/heat/k8s_coreos_template_def.py index 0393f6e541..7aa3ebd42e 100644 --- a/magnum/drivers/heat/k8s_coreos_template_def.py +++ b/magnum/drivers/heat/k8s_coreos_template_def.py @@ -26,14 +26,16 @@ CONF = cfg.CONF LOG = logging.getLogger(__name__) -class ServerAddressOutputMapping(template_def.OutputMapping): +class ServerAddressOutputMapping(template_def.NodeGroupOutputMapping): public_ip_output_key = None private_ip_output_key = None - def __init__(self, dummy_arg, cluster_attr=None): - self.cluster_attr = cluster_attr + def __init__(self, dummy_arg, nodegroup_attr=None, nodegroup_uuid=None): + self.nodegroup_attr = nodegroup_attr + self.nodegroup_uuid = nodegroup_uuid self.heat_output = self.public_ip_output_key + self.is_stack_param = False def set_output(self, stack, cluster_template, cluster): if not cluster_template.floating_ip_enabled: @@ -63,12 +65,43 @@ class CoreOSK8sTemplateDefinition(k8s_template_def.K8sTemplateDefinition): cluster_attr='docker_volume_size') self.add_parameter('docker_storage_driver', cluster_template_attr='docker_storage_driver') + + def add_nodegroup_params(self, cluster): + super(CoreOSK8sTemplateDefinition, + self).add_nodegroup_params(cluster) + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_parameter('number_of_minions', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('minion_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('master_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=master_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_output('kube_minions', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + nodegroup_uuid=worker_ng.uuid, mapping_type=NodeAddressOutputMapping) self.add_output('kube_masters', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + nodegroup_uuid=master_ng.uuid, mapping_type=MasterAddressOutputMapping) + self.add_output('number_of_minions', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + is_stack_param=True) + super(CoreOSK8sTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) diff --git a/magnum/drivers/heat/k8s_fedora_template_def.py b/magnum/drivers/heat/k8s_fedora_template_def.py index c829aaa46b..f1e12a218c 100644 --- a/magnum/drivers/heat/k8s_fedora_template_def.py +++ b/magnum/drivers/heat/k8s_fedora_template_def.py @@ -27,14 +27,16 @@ CONF = cfg.CONF LOG = logging.getLogger(__name__) -class ServerAddressOutputMapping(template_def.OutputMapping): +class ServerAddressOutputMapping(template_def.NodeGroupOutputMapping): public_ip_output_key = None private_ip_output_key = None - def __init__(self, dummy_arg, cluster_attr=None): - self.cluster_attr = cluster_attr + def __init__(self, dummy_arg, nodegroup_attr=None, nodegroup_uuid=None): + self.nodegroup_attr = nodegroup_attr + self.nodegroup_uuid = nodegroup_uuid self.heat_output = self.public_ip_output_key + self.is_stack_param = False def set_output(self, stack, cluster_template, cluster): if not cluster_template.floating_ip_enabled: @@ -64,12 +66,21 @@ class K8sFedoraTemplateDefinition(k8s_template_def.K8sTemplateDefinition): cluster_attr='docker_volume_size') self.add_parameter('docker_storage_driver', cluster_template_attr='docker_storage_driver') + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_output('kube_minions', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + nodegroup_uuid=worker_ng.uuid, mapping_type=NodeAddressOutputMapping) self.add_output('kube_masters', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + nodegroup_uuid=master_ng.uuid, mapping_type=MasterAddressOutputMapping) + super(K8sFedoraTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) diff --git a/magnum/drivers/heat/k8s_template_def.py b/magnum/drivers/heat/k8s_template_def.py index ef50248955..1e3a3854e1 100644 --- a/magnum/drivers/heat/k8s_template_def.py +++ b/magnum/drivers/heat/k8s_template_def.py @@ -55,12 +55,6 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition): def __init__(self): super(K8sTemplateDefinition, self).__init__() - self.add_parameter('master_flavor', - cluster_attr='master_flavor_id') - self.add_parameter('minion_flavor', - cluster_attr='flavor_id') - self.add_parameter('number_of_minions', - cluster_attr='node_count') self.add_parameter('external_network', cluster_template_attr='external_network_id', required=True) @@ -93,6 +87,34 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition): self.add_output('kube_masters_private', cluster_attr=None) + def add_nodegroup_params(self, cluster): + super(K8sTemplateDefinition, + self).add_nodegroup_params(cluster) + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_parameter('number_of_minions', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('minion_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('master_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=master_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + self.add_output('number_of_minions', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + is_stack_param=True, + mapping_type=template_def.NodeGroupOutputMapping) + super(K8sTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) + def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) diff --git a/magnum/drivers/heat/swarm_fedora_template_def.py b/magnum/drivers/heat/swarm_fedora_template_def.py index eeb411fb0a..c4602ecb69 100644 --- a/magnum/drivers/heat/swarm_fedora_template_def.py +++ b/magnum/drivers/heat/swarm_fedora_template_def.py @@ -45,12 +45,6 @@ class SwarmFedoraTemplateDefinition(template_def.BaseTemplateDefinition): self.add_parameter('cluster_uuid', cluster_attr='uuid', param_type=str) - self.add_parameter('number_of_nodes', - cluster_attr='node_count') - self.add_parameter('master_flavor', - cluster_attr='master_flavor_id') - self.add_parameter('node_flavor', - cluster_attr='flavor_id') self.add_parameter('docker_volume_size', cluster_attr='docker_volume_size') self.add_parameter('volume_driver', @@ -79,15 +73,49 @@ class SwarmFedoraTemplateDefinition(template_def.BaseTemplateDefinition): mapping_type=SwarmApiAddressOutputMapping) self.add_output('swarm_master_private', cluster_attr=None) - self.add_output('swarm_masters', - cluster_attr='master_addresses') self.add_output('swarm_nodes_private', cluster_attr=None) - self.add_output('swarm_nodes', - cluster_attr='node_addresses') self.add_output('discovery_url', cluster_attr='discovery_url') + def add_nodegroup_params(self, cluster): + super(SwarmFedoraTemplateDefinition, + self).add_nodegroup_params(cluster) + master_ng = cluster.default_ng_master + worker_ng = cluster.default_ng_worker + self.add_parameter('number_of_nodes', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('node_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('master_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=master_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + + self.add_output('swarm_masters', + nodegroup_attr='node_addresses', + nodegroup_uuid=master_ng.uuid, + mapping_type=template_def.NodeGroupOutputMapping) + self.add_output('swarm_nodes', + nodegroup_attr='node_addresses', + nodegroup_uuid=worker_ng.uuid, + mapping_type=template_def.NodeGroupOutputMapping) + self.add_output('number_of_nodes', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + is_stack_param=True, + mapping_type=template_def.NodeGroupOutputMapping) + super(SwarmFedoraTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) + def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) extra_params['discovery_url'] = \ diff --git a/magnum/drivers/heat/swarm_mode_template_def.py b/magnum/drivers/heat/swarm_mode_template_def.py index 7a5aa27c01..23c9c55613 100644 --- a/magnum/drivers/heat/swarm_mode_template_def.py +++ b/magnum/drivers/heat/swarm_mode_template_def.py @@ -39,13 +39,15 @@ class SwarmModeApiAddressOutputMapping(template_def.OutputMapping): setattr(cluster, self.cluster_attr, value) -class ServerAddressOutputMapping(template_def.OutputMapping): +class ServerAddressOutputMapping(template_def.NodeGroupOutputMapping): public_ip_output_key = None private_ip_output_key = None - def __init__(self, dummy_arg, cluster_attr=None): - self.cluster_attr = cluster_attr + def __init__(self, dummy_arg, nodegroup_attr=None, nodegroup_uuid=None): self.heat_output = self.public_ip_output_key + self.nodegroup_attr = nodegroup_attr + self.nodegroup_uuid = nodegroup_uuid + self.is_stack_param = False class MasterAddressOutputMapping(ServerAddressOutputMapping): @@ -63,7 +65,10 @@ class MasterAddressOutputMapping(ServerAddressOutputMapping): for output in stack.to_dict().get('outputs', []): if output['output_key'] in self.heat_output: _master_addresses += output['output_value'] - setattr(cluster, self.cluster_attr, _master_addresses) + + for ng in cluster.nodegroups: + if ng.uuid == self.nodegroup_uuid: + setattr(ng, self.nodegroup_attr, _master_addresses) class NodeAddressOutputMapping(ServerAddressOutputMapping): @@ -87,12 +92,6 @@ class SwarmModeTemplateDefinition(template_def.BaseTemplateDefinition): self.add_parameter('cluster_uuid', cluster_attr='uuid', param_type=str) - self.add_parameter('number_of_nodes', - cluster_attr='node_count') - self.add_parameter('master_flavor', - cluster_attr='master_flavor_id') - self.add_parameter('node_flavor', - cluster_attr='flavor_id') self.add_parameter('docker_volume_size', cluster_attr='docker_volume_size') self.add_parameter('volume_driver', @@ -113,12 +112,6 @@ class SwarmModeTemplateDefinition(template_def.BaseTemplateDefinition): self.add_output('api_address', cluster_attr='api_address', mapping_type=SwarmModeApiAddressOutputMapping) - self.add_output('swarm_masters', - cluster_attr='master_addresses', - mapping_type=MasterAddressOutputMapping) - self.add_output('swarm_nodes', - cluster_attr='node_addresses', - mapping_type=NodeAddressOutputMapping) def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) @@ -148,6 +141,44 @@ class SwarmModeTemplateDefinition(template_def.BaseTemplateDefinition): extra_params=extra_params, **kwargs) + def add_nodegroup_params(self, cluster): + super(SwarmModeTemplateDefinition, + self).add_nodegroup_params(cluster) + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_parameter('number_of_nodes', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('node_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('master_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=master_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + + self.add_output('swarm_masters', + nodegroup_attr='node_addresses', + nodegroup_uuid=master_ng.uuid, + mapping_type=MasterAddressOutputMapping) + self.add_output('swarm_nodes', + nodegroup_attr='node_addresses', + nodegroup_uuid=worker_ng.uuid, + mapping_type=NodeAddressOutputMapping) + self.add_output('number_of_nodes', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + is_stack_param=True, + mapping_type=template_def.NodeGroupOutputMapping) + super(SwarmModeTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) + def get_env_files(self, cluster_template, cluster): env_files = [] diff --git a/magnum/drivers/heat/template_def.py b/magnum/drivers/heat/template_def.py index 57a1668079..2bf4fb559b 100755 --- a/magnum/drivers/heat/template_def.py +++ b/magnum/drivers/heat/template_def.py @@ -16,6 +16,7 @@ import ast from oslo_log import log as logging from oslo_utils import strutils +from oslo_utils import uuidutils import re import requests import six @@ -51,8 +52,7 @@ class ParameterMapping(object): isn't set, a RequiredArgumentNotProvided exception will be raised. """ def __init__(self, heat_param, cluster_template_attr=None, - cluster_attr=None, required=False, - param_type=lambda x: x): + cluster_attr=None, required=False, param_type=lambda x: x): self.heat_param = heat_param self.cluster_template_attr = cluster_template_attr self.cluster_attr = cluster_attr @@ -60,8 +60,17 @@ class ParameterMapping(object): self.param_type = param_type def set_param(self, params, cluster_template, cluster): - value = None + value = self.get_value(cluster_template, cluster) + if self.required and value is None: + kwargs = dict(heat_param=self.heat_param) + raise exception.RequiredParameterNotProvided(**kwargs) + if value is not None: + value = self.param_type(value) + params[self.heat_param] = value + + def get_value(self, cluster_template, cluster): + value = None if (self.cluster_template_attr and getattr(cluster_template, self.cluster_template_attr, None) is not None): @@ -69,13 +78,26 @@ class ParameterMapping(object): elif (self.cluster_attr and getattr(cluster, self.cluster_attr, None) is not None): value = getattr(cluster, self.cluster_attr) - elif self.required: - kwargs = dict(heat_param=self.heat_param) - raise exception.RequiredParameterNotProvided(**kwargs) + return value - if value is not None: - value = self.param_type(value) - params[self.heat_param] = value + +class NodeGroupParameterMapping(ParameterMapping): + + def __init__(self, heat_param, nodegroup_attr=None, nodegroup_uuid=None, + required=False, param_type=lambda x: x): + self.heat_param = heat_param + self.nodegroup_attr = nodegroup_attr + self.nodegroup_uuid = nodegroup_uuid + self.required = required + self.param_type = param_type + + def get_value(self, cluster_template, cluster): + value = None + for ng in cluster.nodegroups: + if ng.uuid == self.nodegroup_uuid: + value = getattr(ng, self.nodegroup_attr) + break + return value class OutputMapping(object): @@ -94,8 +116,9 @@ class OutputMapping(object): return output_value = self.get_output_value(stack) - if output_value is not None: - setattr(cluster, self.cluster_attr, output_value) + if output_value is None: + return + setattr(cluster, self.cluster_attr, output_value) def matched(self, output_key): return self.heat_output == output_key @@ -109,6 +132,51 @@ class OutputMapping(object): return None +class NodeGroupOutputMapping(OutputMapping): + """A mapping associating stack info and nodegroup attr. + + A NodeGroupOutputMapping is an association of a Heat output or parameter + with a nodegroup field. By default stack output values are reflected to the + specified nodegroup attribute. In the case where is_stack_param is set to + True, the specified heat information will come from the stack parameters. + """ + def __init__(self, heat_output, nodegroup_attr=None, nodegroup_uuid=None, + is_stack_param=False): + self.nodegroup_attr = nodegroup_attr + self.nodegroup_uuid = nodegroup_uuid + self.heat_output = heat_output + self.is_stack_param = is_stack_param + + def set_output(self, stack, cluster_template, cluster): + if self.nodegroup_attr is None: + return + + output_value = self.get_output_value(stack) + if output_value is None: + return + + for ng in cluster.nodegroups: + if ng.uuid == self.nodegroup_uuid: + # nodegroups are fetched from the database every + # time, so the bad thing here is that we need to + # save each change. + setattr(ng, self.nodegroup_attr, output_value) + ng.save() + + def get_output_value(self, stack): + if not self.is_stack_param: + return super(NodeGroupOutputMapping, self).get_output_value(stack) + return self.get_param_value(stack) + + def get_param_value(self, stack): + for param, value in stack.parameters.items(): + if param == self.heat_output: + return value + + LOG.warning('stack does not have param %s', self.heat_output) + return None + + @six.add_metaclass(abc.ABCMeta) class TemplateDefinition(object): """A mapping between Magnum objects and Heat templates. @@ -123,7 +191,8 @@ class TemplateDefinition(object): self.output_mappings = list() def add_parameter(self, *args, **kwargs): - param = ParameterMapping(*args, **kwargs) + param_class = kwargs.pop('param_class', ParameterMapping) + param = param_class(*args, **kwargs) self.param_mappings.append(param) def add_output(self, *args, **kwargs): @@ -171,7 +240,8 @@ class TemplateDefinition(object): """ return [] - def get_heat_param(self, cluster_attr=None, cluster_template_attr=None): + def get_heat_param(self, cluster_attr=None, cluster_template_attr=None, + nodegroup_attr=None, nodegroup_uuid=None): """Returns stack param name. Return stack param name using cluster and cluster_template attributes @@ -183,12 +253,53 @@ class TemplateDefinition(object): :return: stack parameter name or None """ for mapping in self.param_mappings: - if (mapping.cluster_attr == cluster_attr and - mapping.cluster_template_attr == cluster_template_attr): - return mapping.heat_param + if hasattr(mapping, 'cluster_attr'): + if mapping.cluster_attr == cluster_attr and \ + mapping.cluster_template_attr == cluster_template_attr: + return mapping.heat_param + if hasattr(mapping, 'nodegroup_attr'): + if mapping.nodegroup_attr == nodegroup_attr and \ + mapping.nodegroup_uuid == nodegroup_uuid: + return mapping.heat_param return None + def get_stack_diff(self, context, heat_params, cluster): + """Returns all the params that are changed. + + Compares the current params of a stack with the template def for + the cluster and return the ones that changed. + :param heat_params: a dict containing the current params and values + for a stack + :param cluster: the cluster we need to compare with. + """ + diff = {} + for mapping in self.param_mappings: + try: + heat_param_name = mapping.heat_param + stack_value = heat_params[heat_param_name] + value = mapping.get_value(cluster.cluster_template, cluster) + if value is None: + continue + # We need to avoid changing the param values if it's not + # necessary, so for some attributes we need to resolve the + # value either to name or uuid. + value = self.resolve_ambiguous_values(context, heat_param_name, + stack_value, value) + if stack_value != value: + diff.update({heat_param_name: value}) + except KeyError: + # If the key is not in heat_params just skip it. In case + # of update we don't want to trigger a rebuild.... + continue + return diff + + def resolve_ambiguous_values(self, context, heat_param, heat_value, value): + return str(value) + + def add_nodegroup_params(self, cluster): + pass + def update_outputs(self, stack, cluster_template, cluster): for output in self.output_mappings: output.set_output(stack, cluster_template, cluster) @@ -224,8 +335,6 @@ class BaseTemplateDefinition(TemplateDefinition): cluster_template_attr='https_proxy') self.add_parameter('no_proxy', cluster_template_attr='no_proxy') - self.add_parameter('number_of_masters', - cluster_attr='master_count') @property def driver_module_path(self): @@ -243,6 +352,9 @@ class BaseTemplateDefinition(TemplateDefinition): def get_params(self, context, cluster_template, cluster, **kwargs): osc = self.get_osc(context) + # Add all the params from the cluster's nodegroups + self.add_nodegroup_params(cluster) + extra_params = kwargs.pop('extra_params', {}) extra_params['trustee_domain_id'] = osc.keystone().trustee_domain_id extra_params['trustee_user_id'] = cluster.trustee_user_id @@ -271,6 +383,39 @@ class BaseTemplateDefinition(TemplateDefinition): extra_params=extra_params, **kwargs) + def resolve_ambiguous_values(self, context, heat_param, heat_value, value): + # Ambiguous values should be converted to the same format. + osc = self.get_osc(context) + if heat_param == 'external_network': + network = osc.neutron().show_network(heat_value).get('network') + if uuidutils.is_uuid_like(heat_value): + value = network.get('id') + else: + value = network('name') + # Any other values we might need to resolve? + return super(BaseTemplateDefinition, self).resolve_ambiguous_values( + context, heat_param, heat_value, value) + + def add_nodegroup_params(self, cluster): + # Assuming that all the drivers that will not override + # this method do not support more than two nodegroups. + # Meaning that we have one master and one worker. + master_ng = cluster.default_ng_master + self.add_parameter('number_of_masters', + nodegroup_attr='node_count', + nodegroup_uuid=master_ng.uuid, + param_class=NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + master_ng = cluster.default_ng_master + self.add_output('number_of_masters', + nodegroup_attr='node_count', + nodegroup_uuid=master_ng.uuid, + is_stack_param=True, + mapping_type=NodeGroupOutputMapping) + super(BaseTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) + def validate_discovery_url(self, discovery_url, expect_size): url = str(discovery_url) if url[len(url)-1] == '/': @@ -327,11 +472,8 @@ class BaseTemplateDefinition(TemplateDefinition): def get_discovery_url(self, cluster, cluster_template=None): if hasattr(cluster, 'discovery_url') and cluster.discovery_url: - if getattr(cluster, 'master_count', None) is not None: - self.validate_discovery_url(cluster.discovery_url, - cluster.master_count) - else: - self.validate_discovery_url(cluster.discovery_url, 1) + self.validate_discovery_url(cluster.discovery_url, + cluster.master_count) discovery_url = cluster.discovery_url else: discovery_endpoint = ( diff --git a/magnum/drivers/mesos_ubuntu_v1/template_def.py b/magnum/drivers/mesos_ubuntu_v1/template_def.py index 6511387a6c..2becab1fae 100644 --- a/magnum/drivers/mesos_ubuntu_v1/template_def.py +++ b/magnum/drivers/mesos_ubuntu_v1/template_def.py @@ -31,12 +31,6 @@ class UbuntuMesosTemplateDefinition(template_def.BaseTemplateDefinition): cluster_template_attr='fixed_network') self.add_parameter('fixed_subnet', cluster_template_attr='fixed_subnet') - self.add_parameter('number_of_slaves', - cluster_attr='node_count') - self.add_parameter('master_flavor', - cluster_attr='master_flavor_id') - self.add_parameter('slave_flavor', - cluster_attr='flavor_id') self.add_parameter('cluster_name', cluster_attr='name') self.add_parameter('volume_driver', @@ -46,12 +40,45 @@ class UbuntuMesosTemplateDefinition(template_def.BaseTemplateDefinition): cluster_attr='api_address') self.add_output('mesos_master_private', cluster_attr=None) - self.add_output('mesos_master', - cluster_attr='master_addresses') self.add_output('mesos_slaves_private', cluster_attr=None) + + def add_nodegroup_params(self, cluster): + super(UbuntuMesosTemplateDefinition, + self).add_nodegroup_params(cluster) + master_ng = cluster.default_ng_master + worker_ng = cluster.default_ng_worker + self.add_parameter('number_of_slaves', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('slave_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=worker_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + self.add_parameter('master_flavor', + nodegroup_attr='flavor_id', + nodegroup_uuid=master_ng.uuid, + param_class=template_def.NodeGroupParameterMapping) + + def update_outputs(self, stack, cluster_template, cluster): + worker_ng = cluster.default_ng_worker + master_ng = cluster.default_ng_master + self.add_output('mesos_master', + nodegroup_attr='node_addresses', + nodegroup_uuid=master_ng.uuid, + mapping_type=template_def.NodeGroupOutputMapping) self.add_output('mesos_slaves', - cluster_attr='node_addresses') + nodegroup_attr='node_addresses', + nodegroup_uuid=worker_ng.uuid, + mapping_type=template_def.NodeGroupOutputMapping) + self.add_output('number_of_slaves', + nodegroup_attr='node_count', + nodegroup_uuid=worker_ng.uuid, + is_stack_param=True, + mapping_type=template_def.NodeGroupOutputMapping) + super(UbuntuMesosTemplateDefinition, + self).update_outputs(stack, cluster_template, cluster) def get_params(self, context, cluster_template, cluster, **kwargs): extra_params = kwargs.pop('extra_params', {}) diff --git a/magnum/tests/unit/conductor/handlers/test_cluster_conductor.py b/magnum/tests/unit/conductor/handlers/test_cluster_conductor.py index dd86217e5e..96e8535649 100644 --- a/magnum/tests/unit/conductor/handlers/test_cluster_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_cluster_conductor.py @@ -121,8 +121,8 @@ class TestHandler(db_base.DbTestCase): taxonomy.OUTCOME_FAILURE, notifications[0].payload['outcome']) cluster = objects.Cluster.get(self.context, self.cluster.uuid) - self.assertEqual(1, cluster.node_count) self.assertEqual(1, self.worker.node_count) + self.assertEqual(1, cluster.node_count) @patch('magnum.conductor.scale_manager.get_scale_manager') @patch('magnum.drivers.common.driver.Driver.get_driver') @@ -218,8 +218,8 @@ class TestHandler(db_base.DbTestCase): # created and the previous solution seems kind of hacky. cluster_dict = utils.get_test_cluster(node_count=1) cluster = objects.Cluster(self.context, **cluster_dict) - node_count = cluster.node_count - master_count = cluster.node_count + node_count = 1 + master_count = 1 del cluster_dict['id'] del cluster_dict['uuid'] cluster_obj = objects.Cluster(self.context, **cluster_dict) @@ -242,6 +242,8 @@ class TestHandler(db_base.DbTestCase): mock_trust_manager.create_trustee_and_trust.assert_called_once_with( osc, cluster) self.assertEqual(2, len(cluster.nodegroups)) + self.assertEqual(node_count, cluster.node_count) + self.assertEqual(master_count, cluster.master_count) self.assertEqual(node_count, cluster.default_ng_worker.node_count) self.assertEqual(master_count, cluster.default_ng_master.node_count) diff --git a/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py b/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py index 750cce253a..9df0ba8b2e 100644 --- a/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py @@ -114,6 +114,38 @@ class TestClusterConductorWithK8s(base.TestCase): 'flavor_id': 'flavor_id', 'project_id': 'project_id', } + self.worker_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', + 'name': 'worker_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.4'], + 'node_count': 1, + 'role': 'worker', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } + self.master_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a54', + 'name': 'master_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'master_flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.18'], + 'node_count': 1, + 'role': 'master', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } self.context.user_name = 'fake_user' self.context.project_id = 'fake_tenant' osc_patcher = mock.patch('magnum.common.clients.OpenStackClients') @@ -137,6 +169,7 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -146,11 +179,13 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self._test_extract_template_definition( mock_generate_csr_and_key, mock_sign_node_certificate, - mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get) + mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list) def _test_extract_template_definition( self, @@ -159,11 +194,16 @@ class TestClusterConductorWithK8s(base.TestCase): mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list, missing_attr=None): if missing_attr in self.cluster_template_dict: self.cluster_template_dict[missing_attr] = None elif missing_attr in self.cluster_dict: self.cluster_dict[missing_attr] = None + elif missing_attr == 'node_count': + self.worker_ng_dict['node_count'] = None + elif missing_attr == 'master_count': + self.master_ng_dict['node_count'] = None cluster_template = objects.ClusterTemplate( self.context, **self.cluster_template_dict) mock_generate_csr_and_key.return_value = {'csr': 'csr', @@ -179,6 +219,9 @@ class TestClusterConductorWithK8s(base.TestCase): mock_resp.status_code = 200 mock_get.return_value = mock_resp cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = k8s_dr.Driver() (template_path, @@ -310,6 +353,7 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -319,6 +363,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['registry_enabled'] = True @@ -336,6 +381,9 @@ class TestClusterConductorWithK8s(base.TestCase): mock_resp.text = expected_result mock_get.return_value = mock_resp cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = k8s_dr.Driver() CONF.set_override('swift_region', @@ -431,6 +479,7 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -440,6 +489,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): @@ -467,6 +517,9 @@ class TestClusterConductorWithK8s(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = k8s_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] (template_path, definition, @@ -540,10 +593,12 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_coreos_with_disovery( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['cluster_distro'] = 'coreos' @@ -558,6 +613,9 @@ class TestClusterConductorWithK8s(base.TestCase): mock_resp.status_code = 200 mock_get.return_value = mock_resp cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = k8s_coreos_dr.Driver() (template_path, @@ -640,10 +698,12 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_coreos_no_discoveryurl( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, reqget): self.cluster_template_dict['cluster_distro'] = 'coreos' @@ -656,6 +716,9 @@ class TestClusterConductorWithK8s(base.TestCase): mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = k8s_coreos_dr.Driver() (template_path, @@ -738,6 +801,7 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -747,6 +811,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): mock_driver.return_value = k8s_dr.Driver() @@ -756,10 +821,12 @@ class TestClusterConductorWithK8s(base.TestCase): mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list, missing_attr='dns_nameserver') @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -769,6 +836,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): mock_driver.return_value = k8s_dr.Driver() @@ -778,10 +846,12 @@ class TestClusterConductorWithK8s(base.TestCase): mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list, missing_attr='image_id') @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -791,6 +861,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): mock_driver.return_value = k8s_dr.Driver() @@ -800,10 +871,12 @@ class TestClusterConductorWithK8s(base.TestCase): mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list, missing_attr='docker_storage_driver') @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -813,6 +886,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): mock_driver.return_value = k8s_dr.Driver() @@ -822,54 +896,12 @@ class TestClusterConductorWithK8s(base.TestCase): mock_driver, mock_objects_cluster_template_get_by_uuid, mock_get, + mock_objects_nodegroup_list, missing_attr='apiserver_port') @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') - @patch('magnum.drivers.common.driver.Driver.get_driver') - @patch('magnum.conductor.handlers.common.cert_manager' - '.sign_node_certificate') - @patch('magnum.common.x509.operations.generate_csr_and_key') - def test_extract_template_definition_without_node_count( - self, - mock_generate_csr_and_key, - mock_sign_node_certificate, - mock_driver, - mock_objects_cluster_template_get_by_uuid, - mock_get): - mock_driver.return_value = k8s_dr.Driver() - self._test_extract_template_definition( - mock_generate_csr_and_key, - mock_sign_node_certificate, - mock_driver, - mock_objects_cluster_template_get_by_uuid, - mock_get, - missing_attr='node_count') - - @patch('requests.get') - @patch('magnum.objects.ClusterTemplate.get_by_uuid') - @patch('magnum.drivers.common.driver.Driver.get_driver') - @patch('magnum.conductor.handlers.common.cert_manager' - '.sign_node_certificate') - @patch('magnum.common.x509.operations.generate_csr_and_key') - def test_extract_template_definition_without_master_count( - self, - mock_generate_csr_and_key, - mock_sign_node_certificate, - mock_driver, - mock_objects_cluster_template_get_by_uuid, - mock_get): - mock_driver.return_value = k8s_dr.Driver() - self._test_extract_template_definition( - mock_generate_csr_and_key, - mock_sign_node_certificate, - mock_driver, - mock_objects_cluster_template_get_by_uuid, - mock_get, - missing_attr='master_count') - - @patch('requests.get') - @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.conductor.handlers.common.cert_manager' '.sign_node_certificate') @@ -879,6 +911,7 @@ class TestClusterConductorWithK8s(base.TestCase): mock_generate_csr_and_key, mock_sign_node_certificate, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, reqget): cluster_template = objects.ClusterTemplate( @@ -892,6 +925,9 @@ class TestClusterConductorWithK8s(base.TestCase): cluster_dict = self.cluster_dict cluster_dict['discovery_url'] = None cluster = objects.Cluster(self.context, **cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = k8s_dr.Driver() CONF.set_override('etcd_discovery_service_endpoint_format', @@ -1109,21 +1145,35 @@ class TestClusterConductorWithK8s(base.TestCase): @patch('magnum.drivers.k8s_fedora_atomic_v1.driver.Driver.' '_extract_template_definition') @patch('magnum.common.clients.OpenStackClients') + @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') def test_update_stack(self, + mock_objects_nodegroup_list, + mock_objects_cluster_template_get_by_uuid, mock_osc, mock_extract_template_definition, mock_get_template_contents): mock_stack_id = 'xx-xx-xx-xx' - mock_heat_client = mock.MagicMock() + mock_stack = mock.MagicMock(parameters={'number_of_minions': 1}) + mock_stacks = mock.MagicMock() + mock_stacks.get.return_value = mock_stack + mock_heat_client = mock.MagicMock(stacks=mock_stacks) mock_osc.return_value.heat.return_value = mock_heat_client - mock_cluster = mock.MagicMock() - mock_cluster.stack_id = mock_stack_id + mock_template = objects.ClusterTemplate( + self.context, **self.cluster_template_dict) + mock_objects_cluster_template_get_by_uuid.return_value = mock_template + mock_cluster = objects.Cluster(self.context, **self.cluster_dict) + mock_cluster.cluster_template = mock_template + self.worker_ng_dict['node_count'] = 2 + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] k8s_dr.Driver().update_cluster({}, mock_cluster) expected_args = { - 'parameters': {'number_of_minions': mock_cluster.node_count}, + 'parameters': {'number_of_minions': '2'}, 'existing': True, 'disable_rollback': True } diff --git a/magnum/tests/unit/conductor/handlers/test_mesos_cluster_conductor.py b/magnum/tests/unit/conductor/handlers/test_mesos_cluster_conductor.py index 5bce564061..9ffc37092d 100644 --- a/magnum/tests/unit/conductor/handlers/test_mesos_cluster_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_mesos_cluster_conductor.py @@ -76,6 +76,38 @@ class TestClusterConductorWithMesos(base.TestCase): 'mesos_slave_work_dir': '/tmp/mesos/slave' }, } + self.worker_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', + 'name': 'worker_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.4'], + 'node_count': 1, + 'role': 'worker', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } + self.master_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a54', + 'name': 'master_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'master_flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.18'], + 'node_count': 1, + 'role': 'master', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } self.context.user_name = 'mesos_user' self.context.project_id = 'admin' self.context.domain_name = 'domainname' @@ -91,16 +123,21 @@ class TestClusterConductorWithMesos(base.TestCase): self.mock_osc.url_for.return_value = 'http://192.168.10.10:5000/v3' @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_all_values( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid): cluster_template = objects.ClusterTemplate( self.context, **self.cluster_template_dict) mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = mesos_dr.Driver() (template_path, @@ -150,10 +187,12 @@ class TestClusterConductorWithMesos(base.TestCase): env_files) @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_only_required( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid): not_required = ['image_id', 'master_flavor_id', 'flavor_id', 'dns_nameserver', 'fixed_network', 'http_proxy', @@ -167,6 +206,9 @@ class TestClusterConductorWithMesos(base.TestCase): mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = mesos_dr.Driver() (template_path, @@ -208,12 +250,14 @@ class TestClusterConductorWithMesos(base.TestCase): env_files) @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_with_lb_neutron( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid): self.cluster_template_dict['master_lb_enabled'] = True cluster_template = objects.ClusterTemplate( @@ -221,6 +265,9 @@ class TestClusterConductorWithMesos(base.TestCase): mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = mesos_dr.Driver() mock_kc.return_value.client.services.list.return_value = [] @@ -272,12 +319,14 @@ class TestClusterConductorWithMesos(base.TestCase): env_files) @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_with_lb_octavia( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid): self.cluster_template_dict['master_lb_enabled'] = True cluster_template = objects.ClusterTemplate( @@ -285,6 +334,9 @@ class TestClusterConductorWithMesos(base.TestCase): mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = mesos_dr.Driver() class Service(object): @@ -341,20 +393,25 @@ class TestClusterConductorWithMesos(base.TestCase): env_files) @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_multi_master( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid): self.cluster_template_dict['master_lb_enabled'] = True - self.cluster_dict['master_count'] = 2 + self.master_ng_dict['node_count'] = 2 cluster_template = objects.ClusterTemplate( self.context, **self.cluster_template_dict) mock_objects_cluster_template_get_by_uuid.return_value = \ cluster_template cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_driver.return_value = mesos_dr.Driver() mock_kc.return_value.client.services.list.return_value = [] @@ -409,11 +466,23 @@ class TestClusterConductorWithMesos(base.TestCase): @patch('magnum.conf.CONF') @patch('magnum.common.clients.OpenStackClients') @patch('magnum.drivers.common.driver.Driver.get_driver') - def setup_poll_test(self, mock_driver, mock_openstack_client, mock_conf, - mock_retrieve_cluster_template): + def setup_poll_test(self, mock_driver, mock_openstack_client, + mock_conf, mock_retrieve_cluster_template): mock_conf.cluster_heat.max_attempts = 10 - cluster = mock.MagicMock() + worker_ng = mock.MagicMock( + uuid='5d12f6fd-a196-4bf0-ae4c-1f639a523a53', + role='worker', + node_count=1, + ) + master_ng = mock.MagicMock( + uuid='5d12f6fd-a196-4bf0-ae4c-1f639a523a54', + role='master', + node_count=1, + ) + cluster = mock.MagicMock(nodegroups=[worker_ng, master_ng], + default_ng_worker=worker_ng, + default_ng_master=master_ng) mock_heat_stack = mock.MagicMock() mock_heat_client = mock.MagicMock() mock_heat_client.stacks.get.return_value = mock_heat_stack @@ -425,23 +494,30 @@ class TestClusterConductorWithMesos(base.TestCase): poller = heat_driver.HeatPoller(mock_openstack_client, mock.MagicMock(), cluster, mesos_dr.Driver()) + poller.template_def.add_nodegroup_params(cluster) poller.get_version_info = mock.MagicMock() return (mock_heat_stack, cluster, poller) def test_poll_node_count(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_slaves': 1} + mock_heat_stack.parameters = { + 'number_of_slaves': 1, + 'number_of_masters': 1 + } mock_heat_stack.stack_status = cluster_status.CREATE_IN_PROGRESS poller.poll_and_check() - self.assertEqual(1, cluster.node_count) + self.assertEqual(1, cluster.default_ng_worker.node_count) def test_poll_node_count_by_update(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_slaves': 2} + mock_heat_stack.parameters = { + 'number_of_slaves': 2, + 'number_of_masters': 1 + } mock_heat_stack.stack_status = cluster_status.UPDATE_COMPLETE poller.poll_and_check() - self.assertEqual(2, cluster.node_count) + self.assertEqual(2, cluster.default_ng_worker.node_count) diff --git a/magnum/tests/unit/conductor/handlers/test_swarm_cluster_conductor.py b/magnum/tests/unit/conductor/handlers/test_swarm_cluster_conductor.py index 4837984e08..44ed5fbbd3 100644 --- a/magnum/tests/unit/conductor/handlers/test_swarm_cluster_conductor.py +++ b/magnum/tests/unit/conductor/handlers/test_swarm_cluster_conductor.py @@ -86,6 +86,38 @@ class TestClusterConductorWithSwarm(base.TestCase): 'availability_zone': 'az_1'}, 'coe_version': 'fake-version' } + self.worker_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', + 'name': 'worker_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.4'], + 'node_count': 1, + 'role': 'worker', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } + self.master_ng_dict = { + 'uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a54', + 'name': 'master_ng', + 'cluster_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', + 'project_id': 'project_id', + 'docker_volume_size': 20, + 'labels': self.cluster_dict['labels'], + 'flavor_id': 'master_flavor_id', + 'image_id': 'image_id', + 'node_addresses': ['172.17.2.18'], + 'node_count': 1, + 'role': 'master', + 'max_nodes': 5, + 'min_nodes': 1, + 'is_default': True + } # We need this due to volume_driver=rexray CONF.set_override('cluster_user_trust', @@ -105,10 +137,12 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_all_values( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): cluster_template = objects.ClusterTemplate( @@ -122,6 +156,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] (template_path, definition, @@ -177,10 +214,12 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_with_registry( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['registry_enabled'] = True @@ -195,6 +234,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] CONF.set_override('swift_region', 'RegionOne', @@ -256,10 +298,12 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') def test_extract_template_definition_only_required( self, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): @@ -284,6 +328,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] (template_path, definition, @@ -329,12 +376,14 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_with_lb_neutron( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['master_lb_enabled'] = True @@ -349,6 +398,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_kc.return_value.client.services.list.return_value = [] @@ -406,12 +458,14 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_with_lb_octavia( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['master_lb_enabled'] = True @@ -426,6 +480,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] class Service(object): def __init__(self): @@ -488,16 +545,19 @@ class TestClusterConductorWithSwarm(base.TestCase): @patch('requests.get') @patch('magnum.objects.ClusterTemplate.get_by_uuid') + @patch('magnum.objects.NodeGroup.list') @patch('magnum.drivers.common.driver.Driver.get_driver') @patch('magnum.common.keystone.KeystoneClientV3') def test_extract_template_definition_multi_master( self, mock_kc, mock_driver, + mock_objects_nodegroup_list, mock_objects_cluster_template_get_by_uuid, mock_get): self.cluster_template_dict['master_lb_enabled'] = True self.cluster_dict['master_count'] = 2 + self.master_ng_dict['node_count'] = 2 cluster_template = objects.ClusterTemplate( self.context, **self.cluster_template_dict) mock_objects_cluster_template_get_by_uuid.return_value = \ @@ -509,6 +569,9 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_get.return_value = mock_resp mock_driver.return_value = swarm_dr.Driver() cluster = objects.Cluster(self.context, **self.cluster_dict) + worker_ng = objects.NodeGroup(self.context, **self.worker_ng_dict) + master_ng = objects.NodeGroup(self.context, **self.master_ng_dict) + mock_objects_nodegroup_list.return_value = [master_ng, worker_ng] mock_kc.return_value.client.services.list.return_value = [] @@ -572,7 +635,19 @@ class TestClusterConductorWithSwarm(base.TestCase): mock_retrieve_cluster_template): mock_conf.cluster_heat.max_attempts = 10 - cluster = mock.MagicMock() + worker_ng = mock.MagicMock( + uuid='5d12f6fd-a196-4bf0-ae4c-1f639a523a53', + role='worker', + node_count=1, + ) + master_ng = mock.MagicMock( + uuid='5d12f6fd-a196-4bf0-ae4c-1f639a523a54', + role='master', + node_count=1, + ) + cluster = mock.MagicMock(nodegroups=[worker_ng, master_ng], + default_ng_worker=worker_ng, + default_ng_master=master_ng) mock_heat_stack = mock.MagicMock() mock_heat_client = mock.MagicMock() mock_heat_client.stacks.get.return_value = mock_heat_stack @@ -585,23 +660,30 @@ class TestClusterConductorWithSwarm(base.TestCase): poller = heat_driver.HeatPoller(mock_openstack_client, mock.MagicMock(), cluster, swarm_dr.Driver()) + poller.template_def.add_nodegroup_params(cluster) poller.get_version_info = mock.MagicMock() return (mock_heat_stack, cluster, poller) def test_poll_node_count(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_nodes': 1} + mock_heat_stack.parameters = { + 'number_of_nodes': 1, + 'number_of_masters': 1 + } mock_heat_stack.stack_status = cluster_status.CREATE_IN_PROGRESS poller.poll_and_check() - self.assertEqual(1, cluster.node_count) + self.assertEqual(1, cluster.default_ng_worker.node_count) def test_poll_node_count_by_update(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_nodes': 2} + mock_heat_stack.parameters = { + 'number_of_nodes': 2, + 'number_of_masters': 1 + } mock_heat_stack.stack_status = cluster_status.UPDATE_COMPLETE poller.poll_and_check() - self.assertEqual(2, cluster.node_count) + self.assertEqual(2, cluster.default_ng_worker.node_count) diff --git a/magnum/tests/unit/drivers/test_heat_driver.py b/magnum/tests/unit/drivers/test_heat_driver.py index 76a5d84ac7..cc9547294b 100644 --- a/magnum/tests/unit/drivers/test_heat_driver.py +++ b/magnum/tests/unit/drivers/test_heat_driver.py @@ -34,7 +34,12 @@ class TestHeatPoller(base.TestCase): mock_retrieve_cluster_template): cfg.CONF.cluster_heat.max_attempts = 10 - cluster = mock.MagicMock() + worker_ng = mock.MagicMock(uuid='worker_ng', role='worker') + master_ng = mock.MagicMock(uuid='master_ng', role='master') + nodegroups = [worker_ng, master_ng] + cluster = mock.MagicMock(nodegroups=nodegroups, + default_ng_worker=worker_ng, + default_ng_master=master_ng) cluster_template_dict = utils.get_test_cluster_template( coe='kubernetes') mock_heat_stack = mock.MagicMock() @@ -85,45 +90,63 @@ class TestHeatPoller(base.TestCase): mock_heat_stack, cluster, poller = self.setup_poll_test() mock_heat_stack.stack_status = cluster_status.UPDATE_COMPLETE - mock_heat_stack.parameters = {'number_of_minions': 2} + mock_heat_stack.parameters = { + 'number_of_minions': 2, + 'number_of_masters': 1 + } self.assertIsNone(poller.poll_and_check()) self.assertEqual(1, cluster.save.call_count) + self.assertEqual(1, cluster.default_ng_worker.save.call_count) + self.assertEqual(1, cluster.default_ng_master.save.call_count) self.assertEqual(cluster_status.UPDATE_COMPLETE, cluster.status) - self.assertEqual(2, cluster.node_count) + self.assertEqual(2, cluster.default_ng_worker.node_count) + self.assertEqual(1, cluster.default_ng_master.node_count) def test_poll_done_by_update_failed(self): mock_heat_stack, cluster, poller = self.setup_poll_test() mock_heat_stack.stack_status = cluster_status.UPDATE_FAILED - mock_heat_stack.parameters = {'number_of_minions': 2} + mock_heat_stack.parameters = { + 'number_of_minions': 2, + 'number_of_masters': 1 + } self.assertIsNone(poller.poll_and_check()) self.assertEqual(2, cluster.save.call_count) self.assertEqual(cluster_status.UPDATE_FAILED, cluster.status) - self.assertEqual(2, cluster.node_count) + self.assertEqual(2, cluster.default_ng_worker.node_count) + self.assertEqual(1, cluster.default_ng_master.node_count) def test_poll_done_by_rollback_complete(self): mock_heat_stack, cluster, poller = self.setup_poll_test() mock_heat_stack.stack_status = cluster_status.ROLLBACK_COMPLETE - mock_heat_stack.parameters = {'number_of_minions': 1} + mock_heat_stack.parameters = { + 'number_of_minions': 1, + 'number_of_masters': 1 + } self.assertIsNone(poller.poll_and_check()) self.assertEqual(2, cluster.save.call_count) self.assertEqual(cluster_status.ROLLBACK_COMPLETE, cluster.status) - self.assertEqual(1, cluster.node_count) + self.assertEqual(1, cluster.default_ng_worker.node_count) + self.assertEqual(1, cluster.default_ng_master.node_count) def test_poll_done_by_rollback_failed(self): mock_heat_stack, cluster, poller = self.setup_poll_test() mock_heat_stack.stack_status = cluster_status.ROLLBACK_FAILED - mock_heat_stack.parameters = {'number_of_minions': 1} + mock_heat_stack.parameters = { + 'number_of_minions': 1, + 'number_of_masters': 1 + } self.assertIsNone(poller.poll_and_check()) self.assertEqual(2, cluster.save.call_count) self.assertEqual(cluster_status.ROLLBACK_FAILED, cluster.status) - self.assertEqual(1, cluster.node_count) + self.assertEqual(1, cluster.default_ng_worker.node_count) + self.assertEqual(1, cluster.default_ng_master.node_count) @patch('os.path.join') def test_poll_destroy(self, mock_join): @@ -147,20 +170,28 @@ class TestHeatPoller(base.TestCase): def test_poll_node_count(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_minions': 1} + mock_heat_stack.parameters = { + 'number_of_minions': 1, + 'number_of_masters': 1 + } mock_heat_stack.stack_status = cluster_status.CREATE_IN_PROGRESS poller.poll_and_check() - self.assertEqual(1, cluster.node_count) + self.assertEqual(1, cluster.default_ng_worker.node_count) + self.assertEqual(1, cluster.default_ng_master.node_count) def test_poll_node_count_by_update(self): mock_heat_stack, cluster, poller = self.setup_poll_test() - mock_heat_stack.parameters = {'number_of_minions': 2} + mock_heat_stack.parameters = { + 'number_of_minions': 2, + 'number_of_masters': 3 + } mock_heat_stack.stack_status = cluster_status.UPDATE_COMPLETE self.assertIsNone(poller.poll_and_check()) - self.assertEqual(2, cluster.node_count) + self.assertEqual(2, cluster.default_ng_worker.node_count) + self.assertEqual(3, cluster.default_ng_master.node_count) @patch('magnum.drivers.heat.driver.trust_manager') @patch('magnum.drivers.heat.driver.cert_manager') diff --git a/magnum/tests/unit/drivers/test_template_definition.py b/magnum/tests/unit/drivers/test_template_definition.py index 940c6d5c2d..56ade10a9b 100644 --- a/magnum/tests/unit/drivers/test_template_definition.py +++ b/magnum/tests/unit/drivers/test_template_definition.py @@ -303,6 +303,15 @@ class TemplateDefinitionTestCase(base.TestCase): @six.add_metaclass(abc.ABCMeta) class BaseK8sTemplateDefinitionTestCase(base.TestCase): + def setUp(self): + super(BaseK8sTemplateDefinitionTestCase, self).setUp() + self.master_ng = mock.MagicMock(uuid='master_ng', role='master') + self.worker_ng = mock.MagicMock(uuid='worker_ng', role='worker') + self.nodegroups = [self.master_ng, self.worker_ng] + self.mock_cluster = mock.MagicMock(nodegroups=self.nodegroups, + default_ng_worker=self.worker_ng, + default_ng_master=self.master_ng) + @abc.abstractmethod def get_definition(self): """Returns the template definition.""" @@ -313,7 +322,9 @@ class BaseK8sTemplateDefinitionTestCase(base.TestCase): floating_ip_enabled=True, public_ip_output_key='kube_masters', private_ip_output_key='kube_masters_private', - cluster_attr='master_addresses', + cluster_attr=None, + nodegroup_attr=None, + is_master=False ): definition = self.get_definition() @@ -332,14 +343,23 @@ class BaseK8sTemplateDefinitionTestCase(base.TestCase): ] mock_stack = mock.MagicMock() mock_stack.to_dict.return_value = {'outputs': outputs} - mock_cluster = mock.MagicMock() mock_cluster_template = mock.MagicMock() mock_cluster_template.floating_ip_enabled = floating_ip_enabled definition.update_outputs(mock_stack, mock_cluster_template, - mock_cluster) + self.mock_cluster) - self.assertEqual(expected_address, getattr(mock_cluster, cluster_attr)) + actual = None + if cluster_attr: + actual = getattr(self.mock_cluster, cluster_attr) + elif is_master: + actual = getattr( + self.mock_cluster.default_ng_master, nodegroup_attr) + else: + actual = getattr( + self.mock_cluster.default_ng_worker, nodegroup_attr) + + self.assertEqual(expected_address, actual) class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase): @@ -1107,8 +1127,13 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase): def test_k8s_get_heat_param(self): k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition() - heat_param = k8s_def.get_heat_param(cluster_attr='node_count') + k8s_def.add_nodegroup_params(self.mock_cluster) + heat_param = k8s_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='worker_ng') self.assertEqual('number_of_minions', heat_param) + heat_param = k8s_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='master_ng') + self.assertEqual('number_of_masters', heat_param) @mock.patch('requests.get') def test_k8s_get_discovery_url_not_found(self, mock_get): @@ -1246,14 +1271,16 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase): self._test_update_outputs_server_address( public_ip_output_key='kube_masters', private_ip_output_key='kube_masters_private', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + is_master=True ) def test_update_outputs_node_address(self): self._test_update_outputs_server_address( public_ip_output_key='kube_minions', private_ip_output_key='kube_minions_private', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + is_master=False ) def test_update_outputs_master_address_fip_disabled(self): @@ -1261,7 +1288,8 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase): floating_ip_enabled=False, public_ip_output_key='kube_masters', private_ip_output_key='kube_masters_private', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + is_master=True ) def test_update_outputs_node_address_fip_disabled(self): @@ -1269,7 +1297,8 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase): floating_ip_enabled=False, public_ip_output_key='kube_minions', private_ip_output_key='kube_minions_private', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + is_master=False ) @@ -1379,6 +1408,15 @@ class FedoraK8sIronicTemplateDefinitionTestCase(base.TestCase): class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): + def setUp(self): + super(AtomicSwarmModeTemplateDefinitionTestCase, self).setUp() + self.master_ng = mock.MagicMock(uuid='master_ng', role='master') + self.worker_ng = mock.MagicMock(uuid='worker_ng', role='worker') + self.nodegroups = [self.master_ng, self.worker_ng] + self.mock_cluster = mock.MagicMock(nodegroups=self.nodegroups, + default_ng_worker=self.worker_ng, + default_ng_master=self.master_ng) + def get_definition(self): return swarm_v2_dr.Driver().get_template_definition() @@ -1387,7 +1425,9 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): floating_ip_enabled=True, public_ip_output_key='swarm_nodes', private_ip_output_key='swarm_nodes_private', - cluster_attr='node_addresses', + cluster_attr=None, + nodegroup_attr=None, + is_master=False ): definition = self.get_definition() @@ -1407,14 +1447,20 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): ] mock_stack = mock.MagicMock() mock_stack.to_dict.return_value = {'outputs': outputs} - mock_cluster = mock.MagicMock() mock_cluster_template = mock.MagicMock() mock_cluster_template.floating_ip_enabled = floating_ip_enabled definition.update_outputs(mock_stack, mock_cluster_template, - mock_cluster) + self.mock_cluster) - self.assertEqual(expected_address, getattr(mock_cluster, cluster_attr)) + actual = None + if cluster_attr: + actual = getattr(self.mock_cluster, cluster_attr) + elif is_master: + actual = getattr(self.master_ng, nodegroup_attr) + else: + actual = getattr(self.worker_ng, nodegroup_attr) + self.assertEqual(expected_address, actual) @mock.patch('magnum.common.clients.OpenStackClients') @mock.patch('magnum.drivers.swarm_fedora_atomic_v2.template_def' @@ -1472,8 +1518,12 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): def test_swarm_get_heat_param(self): swarm_def = swarm_v2_tdef.AtomicSwarmTemplateDefinition() - heat_param = swarm_def.get_heat_param(cluster_attr='node_count') + swarm_def.add_nodegroup_params(self.mock_cluster) + heat_param = swarm_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='worker_ng') self.assertEqual('number_of_nodes', heat_param) + heat_param = swarm_def.get_heat_param(cluster_attr='uuid') + self.assertEqual('cluster_uuid', heat_param) def test_update_outputs(self): swarm_def = swarm_v2_tdef.AtomicSwarmTemplateDefinition() @@ -1500,27 +1550,29 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): ] mock_stack = mock.MagicMock() mock_stack.to_dict.return_value = {'outputs': outputs} - mock_cluster = mock.MagicMock() mock_cluster_template = mock.MagicMock() swarm_def.update_outputs(mock_stack, mock_cluster_template, - mock_cluster) + self.mock_cluster) expected_api_address = "tcp://%s:2375" % expected_api_address - self.assertEqual(expected_api_address, mock_cluster.api_address) - self.assertEqual(expected_node_addresses, mock_cluster.node_addresses) + self.assertEqual(expected_api_address, self.mock_cluster.api_address) + self.assertEqual(expected_node_addresses, + self.mock_cluster.default_ng_worker.node_addresses) def test_update_outputs_master_address(self): self._test_update_outputs_server_address( public_ip_output_key='swarm_primary_master', private_ip_output_key='swarm_primary_master_private', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + is_master=True ) def test_update_outputs_node_address(self): self._test_update_outputs_server_address( public_ip_output_key='swarm_nodes', private_ip_output_key='swarm_nodes_private', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + is_master=False ) def test_update_outputs_master_address_fip_disabled(self): @@ -1528,7 +1580,8 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): floating_ip_enabled=False, public_ip_output_key='swarm_primary_master', private_ip_output_key='swarm_primary_master_private', - cluster_attr='master_addresses', + nodegroup_attr='node_addresses', + is_master=True ) def test_update_outputs_node_address_fip_disabled(self): @@ -1536,12 +1589,22 @@ class AtomicSwarmModeTemplateDefinitionTestCase(base.TestCase): floating_ip_enabled=False, public_ip_output_key='swarm_nodes', private_ip_output_key='swarm_nodes_private', - cluster_attr='node_addresses', + nodegroup_attr='node_addresses', + is_master=False ) class AtomicSwarmTemplateDefinitionTestCase(base.TestCase): + def setUp(self): + super(AtomicSwarmTemplateDefinitionTestCase, self).setUp() + self.master_ng = mock.MagicMock(uuid='master_ng', role='master') + self.worker_ng = mock.MagicMock(uuid='worker_ng', role='worker') + self.nodegroups = [self.master_ng, self.worker_ng] + self.mock_cluster = mock.MagicMock(nodegroups=self.nodegroups, + default_ng_worker=self.worker_ng, + default_ng_master=self.master_ng) + @mock.patch('magnum.common.clients.OpenStackClients') @mock.patch('magnum.drivers.swarm_fedora_atomic_v1.template_def' '.AtomicSwarmTemplateDefinition.get_discovery_url') @@ -1757,7 +1820,9 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase): def test_swarm_get_heat_param(self): swarm_def = swarm_tdef.AtomicSwarmTemplateDefinition() - heat_param = swarm_def.get_heat_param(cluster_attr='node_count') + swarm_def.add_nodegroup_params(self.mock_cluster) + heat_param = swarm_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='worker_ng') self.assertEqual('number_of_nodes', heat_param) def test_update_outputs(self): @@ -1785,18 +1850,27 @@ class AtomicSwarmTemplateDefinitionTestCase(base.TestCase): ] mock_stack = mock.MagicMock() mock_stack.to_dict.return_value = {'outputs': outputs} - mock_cluster = mock.MagicMock() mock_cluster_template = mock.MagicMock() swarm_def.update_outputs(mock_stack, mock_cluster_template, - mock_cluster) + self.mock_cluster) expected_api_address = "tcp://%s:2376" % expected_api_address - self.assertEqual(expected_api_address, mock_cluster.api_address) - self.assertEqual(expected_node_addresses, mock_cluster.node_addresses) + self.assertEqual(expected_api_address, self.mock_cluster.api_address) + self.assertEqual(expected_node_addresses, + self.worker_ng.node_addresses) class UbuntuMesosTemplateDefinitionTestCase(base.TestCase): + def setUp(self): + super(UbuntuMesosTemplateDefinitionTestCase, self).setUp() + self.master_ng = mock.MagicMock(uuid='master_ng', role='master') + self.worker_ng = mock.MagicMock(uuid='worker_ng', role='worker') + self.nodegroups = [self.master_ng, self.worker_ng] + self.mock_cluster = mock.MagicMock(nodegroups=self.nodegroups, + default_ng_worker=self.worker_ng, + default_ng_master=self.master_ng) + @mock.patch('magnum.common.clients.OpenStackClients') @mock.patch('magnum.drivers.heat.template_def.BaseTemplateDefinition' '.get_params') @@ -1875,10 +1949,14 @@ class UbuntuMesosTemplateDefinitionTestCase(base.TestCase): def test_mesos_get_heat_param(self): mesos_def = mesos_tdef.UbuntuMesosTemplateDefinition() - heat_param = mesos_def.get_heat_param(cluster_attr='node_count') + mesos_def.add_nodegroup_params(self.mock_cluster) + + heat_param = mesos_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='worker_ng') self.assertEqual('number_of_slaves', heat_param) - heat_param = mesos_def.get_heat_param(cluster_attr='master_count') + heat_param = mesos_def.get_heat_param(nodegroup_attr='node_count', + nodegroup_uuid='master_ng') self.assertEqual('number_of_masters', heat_param) def test_update_outputs(self): @@ -1907,13 +1985,13 @@ class UbuntuMesosTemplateDefinitionTestCase(base.TestCase): ] mock_stack = mock.MagicMock() mock_stack.to_dict.return_value = {'outputs': outputs} - mock_cluster = mock.MagicMock() mock_cluster_template = mock.MagicMock() mesos_def.update_outputs(mock_stack, mock_cluster_template, - mock_cluster) + self.mock_cluster) - self.assertEqual(expected_api_address, mock_cluster.api_address) - self.assertEqual(expected_node_addresses, mock_cluster.node_addresses) + self.assertEqual(expected_api_address, self.mock_cluster.api_address) + self.assertEqual(expected_node_addresses, + self.mock_cluster.default_ng_worker.node_addresses) self.assertEqual(expected_master_addresses, - mock_cluster.master_addresses) + self.mock_cluster.default_ng_master.node_addresses)