From 0357a47900fd4de810e05551647e56b05be51620 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 3 Jan 2024 18:26:40 +0900 Subject: [PATCH] Remove Senlin support The senlin project was marked inactive during this cycle[1]. Its project health has not been improved even at m-2 thus 2024.1 release will not be created for this project. The project is retired because of no interest/volunteer to maintain it[2]. [1] https://review.opendev.org/c/openstack/governance/+/902626 [2] https://review.opendev.org/c/openstack/governance/+/919347 Change-Id: I615de4d659f1d7879ad311e9f1c97a6937934262 --- README.rst | 1 - heat/common/config.py | 2 +- heat/engine/clients/os/senlin.py | 160 ------- .../resources/openstack/senlin/cluster.py | 390 +----------------- .../engine/resources/openstack/senlin/node.py | 191 +-------- .../resources/openstack/senlin/policy.py | 188 +-------- .../resources/openstack/senlin/profile.py | 61 +-- .../resources/openstack/senlin/receiver.py | 93 +---- .../resources/openstack/senlin/res_base.py | 39 +- heat/tests/clients/test_senlin_client.py | 198 --------- heat/tests/openstack/senlin/__init__.py | 0 heat/tests/openstack/senlin/test_cluster.py | 390 ------------------ heat/tests/openstack/senlin/test_node.py | 242 ----------- heat/tests/openstack/senlin/test_policy.py | 195 --------- heat/tests/openstack/senlin/test_profile.py | 116 ------ heat/tests/openstack/senlin/test_receiver.py | 129 ------ .../notes/remove-senlin-586d6754a7610bdf.yaml | 15 + setup.cfg | 6 - 18 files changed, 32 insertions(+), 2384 deletions(-) delete mode 100644 heat/engine/clients/os/senlin.py delete mode 100644 heat/tests/clients/test_senlin_client.py delete mode 100644 heat/tests/openstack/senlin/__init__.py delete mode 100644 heat/tests/openstack/senlin/test_cluster.py delete mode 100644 heat/tests/openstack/senlin/test_node.py delete mode 100644 heat/tests/openstack/senlin/test_policy.py delete mode 100644 heat/tests/openstack/senlin/test_profile.py delete mode 100644 heat/tests/openstack/senlin/test_receiver.py create mode 100644 releasenotes/notes/remove-senlin-586d6754a7610bdf.yaml diff --git a/README.rst b/README.rst index 6e1ff8e95a..61e3f5056a 100644 --- a/README.rst +++ b/README.rst @@ -74,6 +74,5 @@ We have integration with * https://opendev.org/openstack/python-zunclient (container management service) * https://opendev.org/openstack/python-blazarclient (reservation service) * https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service) -* https://opendev.org/openstack/python-senlinclient (Clustering service) * https://opendev.org/openstack/python-vitrageclient.git (RCA service) * https://opendev.org/openstack/python-ironicclient (baremetal provisioning service) diff --git a/heat/common/config.py b/heat/common/config.py index c6a1dfcfe4..a1b6806c32 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -460,7 +460,7 @@ def list_opts(): for client in ('aodh', 'barbican', 'cinder', 'designate', 'glance', 'heat', 'keystone', 'magnum', 'manila', 'mistral', - 'monasca', 'neutron', 'nova', 'octavia', 'sahara', 'senlin', + 'monasca', 'neutron', 'nova', 'octavia', 'sahara', 'swift', 'trove', 'vitrage', 'zaqar' ): client_specific_group = 'clients_' + client diff --git a/heat/engine/clients/os/senlin.py b/heat/engine/clients/os/senlin.py deleted file mode 100644 index 6b62d01c7f..0000000000 --- a/heat/engine/clients/os/senlin.py +++ /dev/null @@ -1,160 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from openstack import exceptions - -from heat.common import exception -from heat.common.i18n import _ -from heat.engine.clients.os import openstacksdk as sdk_plugin -from heat.engine import constraints - -CLIENT_NAME = 'senlin' - - -class SenlinClientPlugin(sdk_plugin.OpenStackSDKPlugin): - - exceptions_module = exceptions - - def _create(self, version=None): - client = super(SenlinClientPlugin, self)._create(version=version) - return client.clustering - - def _get_additional_create_args(self, version): - return { - 'clustering_api_version': version or '1' - } - - def generate_spec(self, spec_type, spec_props): - spec = {'properties': spec_props} - spec['type'], spec['version'] = spec_type.split('-') - return spec - - def check_action_status(self, action_id): - action = self.client().get_action(action_id) - if action.status == 'SUCCEEDED': - return True - elif action.status == 'FAILED': - raise exception.ResourceInError( - status_reason=action.status_reason, - resource_status=action.status, - ) - return False - - def cluster_is_active(self, cluster_id): - cluster = self.client().get_cluster(cluster_id) - if cluster.status == 'ACTIVE': - return True - elif cluster.status == 'ERROR': - raise exception.ResourceInError( - status_reason=cluster.status_reason, - resource_status=cluster.status, - ) - return False - - def get_profile_id(self, profile_name): - profile = self.client().get_profile(profile_name) - return profile.id - - def get_cluster_id(self, cluster_name): - cluster = self.client().get_cluster(cluster_name) - return cluster.id - - def get_policy_id(self, policy_name): - policy = self.client().get_policy(policy_name) - return policy.id - - def is_bad_request(self, ex): - return (isinstance(ex, exceptions.HttpException) and - ex.status_code == 400) - - def execute_actions(self, actions): - all_executed = True - for action in actions: - if action['done']: - continue - all_executed = False - if 'action_id' in action: - if action['action_id'] is None: - func = getattr(self.client(), action['func']) - ret = func(**action['params']) - if isinstance(ret, dict): - action['action_id'] = ret['action'] - else: - action['action_id'] = ret.location.split('/')[-1] - else: - ret = self.check_action_status(action['action_id']) - action['done'] = ret - else: - ret = self.cluster_is_active(action['cluster_id']) - action['done'] = ret - # Execute these actions one by one. - break - return all_executed - - -class ProfileConstraint(constraints.BaseCustomConstraint): - # If name is not unique, will raise exceptions.HttpException - expected_exceptions = (exceptions.HttpException,) - - def validate_with_client(self, client, profile): - client.client(CLIENT_NAME).get_profile(profile) - - -class ClusterConstraint(constraints.BaseCustomConstraint): - # If name is not unique, will raise exceptions.HttpException - expected_exceptions = (exceptions.HttpException,) - - def validate_with_client(self, client, value): - client.client(CLIENT_NAME).get_cluster(value) - - -class PolicyConstraint(constraints.BaseCustomConstraint): - # If name is not unique, will raise exceptions.HttpException - expected_exceptions = (exceptions.HttpException,) - - def validate_with_client(self, client, value): - client.client(CLIENT_NAME).get_policy(value) - - -class ProfileTypeConstraint(constraints.BaseCustomConstraint): - - expected_exceptions = (exception.StackValidationFailed,) - - def validate_with_client(self, client, value): - conn = client.client(CLIENT_NAME) - type_list = conn.profile_types() - names = [pt.name for pt in type_list] - if value not in names: - not_found_message = ( - _("Unable to find senlin profile type '%(pt)s', " - "available profile types are %(pts)s.") % - {'pt': value, 'pts': names} - ) - raise exception.StackValidationFailed(message=not_found_message) - - -class PolicyTypeConstraint(constraints.BaseCustomConstraint): - - expected_exceptions = (exception.StackValidationFailed,) - - def validate_with_client(self, client, value): - conn = client.client(CLIENT_NAME) - type_list = conn.policy_types() - names = [pt.name for pt in type_list] - if value not in names: - not_found_message = ( - _("Unable to find senlin policy type '%(pt)s', " - "available policy types are %(pts)s.") % - {'pt': value, 'pts': names} - ) - raise exception.StackValidationFailed(message=not_found_message) diff --git a/heat/engine/resources/openstack/senlin/cluster.py b/heat/engine/resources/openstack/senlin/cluster.py index 57ccf7ceed..c123160513 100644 --- a/heat/engine/resources/openstack/senlin/cluster.py +++ b/heat/engine/resources/openstack/senlin/cluster.py @@ -12,14 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from heat.common import exception -from heat.common.i18n import _ -from heat.engine import attributes -from heat.engine import constraints -from heat.engine import properties from heat.engine.resources.openstack.senlin import res_base -from heat.engine import support -from heat.engine import translation class Cluster(res_base.BaseSenlinResource): @@ -29,388 +22,7 @@ class Cluster(res_base.BaseSenlinResource): the same nature, e.g. Nova servers, Heat stacks, Cinder volumes, etc. The collection of these objects is referred to as a cluster. """ - - entity = 'cluster' - - PROPERTIES = ( - NAME, PROFILE, DESIRED_CAPACITY, MIN_SIZE, MAX_SIZE, - METADATA, TIMEOUT, POLICIES, - ) = ( - 'name', 'profile', 'desired_capacity', 'min_size', 'max_size', - 'metadata', 'timeout', 'policies', - ) - - ATTRIBUTES = ( - ATTR_NAME, ATTR_METADATA, ATTR_NODES, ATTR_DESIRED_CAPACITY, - ATTR_MIN_SIZE, ATTR_MAX_SIZE, ATTR_POLICIES, ATTR_COLLECT, - ) = ( - "name", 'metadata', 'nodes', 'desired_capacity', - 'min_size', 'max_size', 'policies', 'collect', - ) - - _POLICIES = ( - P_POLICY, P_ENABLED, - ) = ( - "policy", "enabled", - ) - - _CLUSTER_STATUS = ( - CLUSTER_INIT, CLUSTER_ACTIVE, CLUSTER_ERROR, CLUSTER_WARNING, - CLUSTER_CREATING, CLUSTER_DELETING, CLUSTER_UPDATING - ) = ( - 'INIT', 'ACTIVE', 'ERROR', 'WARNING', - 'CREATING', 'DELETING', 'UPDATING' - ) - - properties_schema = { - PROFILE: properties.Schema( - properties.Schema.STRING, - _('The name or id of the Senlin profile.'), - required=True, - update_allowed=True, - constraints=[ - constraints.CustomConstraint('senlin.profile') - ] - ), - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the cluster. By default, physical resource name ' - 'is used.'), - update_allowed=True, - ), - DESIRED_CAPACITY: properties.Schema( - properties.Schema.INTEGER, - _('Desired initial number of resources in cluster.'), - default=0, - update_allowed=True, - ), - MIN_SIZE: properties.Schema( - properties.Schema.INTEGER, - _('Minimum number of resources in the cluster.'), - default=0, - update_allowed=True, - constraints=[ - constraints.Range(min=0) - ] - ), - MAX_SIZE: properties.Schema( - properties.Schema.INTEGER, - _('Maximum number of resources in the cluster. ' - '-1 means unlimited.'), - default=-1, - update_allowed=True, - constraints=[ - constraints.Range(min=-1) - ] - ), - METADATA: properties.Schema( - properties.Schema.MAP, - _('Metadata key-values defined for cluster.'), - update_allowed=True, - default={}, - ), - TIMEOUT: properties.Schema( - properties.Schema.INTEGER, - _('The number of seconds to wait for the cluster actions.'), - update_allowed=True, - constraints=[ - constraints.Range(min=0) - ] - ), - POLICIES: properties.Schema( - properties.Schema.LIST, - _('A list of policies to attach to this cluster.'), - update_allowed=True, - default=[], - support_status=support.SupportStatus(version='8.0.0'), - schema=properties.Schema( - properties.Schema.MAP, - schema={ - P_POLICY: properties.Schema( - properties.Schema.STRING, - _("The name or ID of the policy."), - required=True, - constraints=[ - constraints.CustomConstraint('senlin.policy') - ] - ), - P_ENABLED: properties.Schema( - properties.Schema.BOOLEAN, - _("Whether enable this policy on this cluster."), - default=True, - ), - } - ) - ), - } - - attributes_schema = { - ATTR_NAME: attributes.Schema( - _("Cluster name."), - type=attributes.Schema.STRING - ), - ATTR_METADATA: attributes.Schema( - _("Cluster metadata."), - type=attributes.Schema.MAP - ), - ATTR_DESIRED_CAPACITY: attributes.Schema( - _("Desired capacity of the cluster."), - type=attributes.Schema.INTEGER - ), - ATTR_NODES: attributes.Schema( - _("Nodes list in the cluster."), - type=attributes.Schema.LIST, - cache_mode=attributes.Schema.CACHE_NONE - ), - ATTR_MIN_SIZE: attributes.Schema( - _("Min size of the cluster."), - type=attributes.Schema.INTEGER - ), - ATTR_MAX_SIZE: attributes.Schema( - _("Max size of the cluster."), - type=attributes.Schema.INTEGER - ), - ATTR_POLICIES: attributes.Schema( - _("Policies attached to the cluster."), - type=attributes.Schema.LIST, - support_status=support.SupportStatus(version='8.0.0'), - ), - ATTR_COLLECT: attributes.Schema( - _("Attributes collected from cluster. According to the jsonpath " - "following this attribute, it will return a list of attributes " - "collected from the nodes of this cluster."), - type=attributes.Schema.LIST, - support_status=support.SupportStatus(version='8.0.0'), - cache_mode=attributes.Schema.CACHE_NONE - ) - } - - def translation_rules(self, props): - rules = [ - translation.TranslationRule( - props, - translation.TranslationRule.RESOLVE, - translation_path=[self.PROFILE], - client_plugin=self.client_plugin(), - finder='get_profile_id'), - translation.TranslationRule( - props, - translation.TranslationRule.RESOLVE, - translation_path=[self.POLICIES, self.P_POLICY], - client_plugin=self.client_plugin(), - finder='get_policy_id'), - ] - return rules - - def handle_create(self): - actions = [] - params = { - 'name': (self.properties[self.NAME] or - self.physical_resource_name()), - 'profile_id': self.properties[self.PROFILE], - 'desired_capacity': self.properties[self.DESIRED_CAPACITY], - 'min_size': self.properties[self.MIN_SIZE], - 'max_size': self.properties[self.MAX_SIZE], - 'metadata': self.properties[self.METADATA], - 'timeout': self.properties[self.TIMEOUT] - } - - cluster = self.client().create_cluster(**params) - self.resource_id_set(cluster.id) - # for cluster creation, we just to check the action status - # the action is executed above - action = { - 'cluster_id': cluster.id, - 'done': False, - } - actions.append(action) - if self.properties[self.POLICIES]: - for p in self.properties[self.POLICIES]: - params = { - 'cluster': cluster.id, - 'policy': p[self.P_POLICY], - 'enabled': p[self.P_ENABLED], - } - action = { - 'func': 'attach_policy_to_cluster', - 'params': params, - 'action_id': None, - 'done': False, - } - actions.append(action) - return actions - - def check_create_complete(self, actions): - return self.client_plugin().execute_actions(actions) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().delete_cluster(self.resource_id) - return self.resource_id - - def check_delete_complete(self, resource_id): - if resource_id: - with self.client_plugin().ignore_not_found: - self.client().get_cluster(self.resource_id) - return False - - return True - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - UPDATE_PROPS = (self.NAME, self.METADATA, self.TIMEOUT, self.PROFILE) - RESIZE_PROPS = (self.MIN_SIZE, self.MAX_SIZE, self.DESIRED_CAPACITY) - actions = [] - if not prop_diff: - return actions - cluster_obj = self.client().get_cluster(self.resource_id) - # Update Policies - if self.POLICIES in prop_diff: - old_policies = self.properties[self.POLICIES] - new_policies = prop_diff[self.POLICIES] - old_policy_ids = [p[self.P_POLICY] for p in old_policies] - update_policies = [p for p in new_policies - if p[self.P_POLICY] in old_policy_ids] - update_policy_ids = [p[self.P_POLICY] for p in update_policies] - add_policies = [p for p in new_policies - if p[self.P_POLICY] not in old_policy_ids] - remove_policies = [p for p in old_policies - if p[self.P_POLICY] not in update_policy_ids] - for p in update_policies: - params = { - 'policy': p[self.P_POLICY], - 'cluster': self.resource_id, - 'enabled': p[self.P_ENABLED] - } - action = { - 'func': 'update_cluster_policy', - 'params': params, - 'action_id': None, - 'done': False, - } - actions.append(action) - for p in remove_policies: - params = { - 'policy': p[self.P_POLICY], - 'cluster': self.resource_id, - 'enabled': p[self.P_ENABLED] - } - action = { - 'func': 'detach_policy_from_cluster', - 'params': params, - 'action_id': None, - 'done': False, - } - actions.append(action) - for p in add_policies: - params = { - 'policy': p[self.P_POLICY], - 'cluster': self.resource_id, - 'enabled': p[self.P_ENABLED] - } - action = { - 'func': 'attach_policy_to_cluster', - 'params': params, - 'action_id': None, - 'done': False, - } - actions.append(action) - # Update cluster - if any(p in prop_diff for p in UPDATE_PROPS): - params = dict((k, v) for k, v in prop_diff.items() - if k in UPDATE_PROPS) - params['cluster'] = cluster_obj - if self.PROFILE in params: - params['profile_id'] = params.pop(self.PROFILE) - - self.client().update_cluster(**params) - action = { - 'cluster_id': self.resource_id, - 'done': False - } - actions.append(action) - # Resize Cluster - if any(p in prop_diff for p in RESIZE_PROPS): - params = dict((k, v) for k, v in prop_diff.items() - if k in RESIZE_PROPS) - if self.DESIRED_CAPACITY in params: - params['adjustment_type'] = 'EXACT_CAPACITY' - params['number'] = params.pop(self.DESIRED_CAPACITY) - params['cluster'] = self.resource_id - action = { - 'func': 'resize_cluster', - 'params': params, - 'action_id': None, - 'done': False, - } - actions.append(action) - return actions - - def check_update_complete(self, actions): - return self.client_plugin().execute_actions(actions) - - def validate(self): - min_size = self.properties[self.MIN_SIZE] - max_size = self.properties[self.MAX_SIZE] - desired_capacity = self.properties[self.DESIRED_CAPACITY] - - if max_size != -1 and max_size < min_size: - msg = _("%(min_size)s can not be greater than %(max_size)s") % { - 'min_size': self.MIN_SIZE, - 'max_size': self.MAX_SIZE, - } - raise exception.StackValidationFailed(message=msg) - - if (desired_capacity < min_size or - (max_size != -1 and desired_capacity > max_size)): - msg = _("%(desired_capacity)s must be between %(min_size)s " - "and %(max_size)s") % { - 'desired_capacity': self.DESIRED_CAPACITY, - 'min_size': self.MIN_SIZE, - 'max_size': self.MAX_SIZE, - } - raise exception.StackValidationFailed(message=msg) - - def get_attribute(self, key, *path): - if self.resource_id is None: - return None - - if key == self.ATTR_COLLECT: - if not path: - raise exception.InvalidTemplateAttribute( - resource=self.name, key=key) - attrs = self.client().collect_cluster_attrs( - self.resource_id, path[0]) - attr = [attr.attr_value for attr in attrs] - return attributes.select_from_attribute(attr, path[1:]) - else: - return super(Cluster, self).get_attribute(key, *path) - - def _show_resource(self): - cluster_dict = super(Cluster, self)._show_resource() - cluster_dict[self.ATTR_POLICIES] = self.client().cluster_policies( - self.resource_id) - return cluster_dict - - def parse_live_resource_data(self, resource_properties, resource_data): - reality = {} - - for key in self._update_allowed_properties: - if key == self.PROFILE: - value = resource_data.get('profile_id') - elif key == self.POLICIES: - value = [] - for p in resource_data.get(self.POLICIES): - v = { - 'policy': p.get('policy_id'), - 'enabled': p.get('enabled'), - } - value.append(v) - else: - value = resource_data.get(key) - reality.update({key: value}) - - return reality + pass def resource_mapping(): diff --git a/heat/engine/resources/openstack/senlin/node.py b/heat/engine/resources/openstack/senlin/node.py index 7d254cbe42..b13a6a79a1 100644 --- a/heat/engine/resources/openstack/senlin/node.py +++ b/heat/engine/resources/openstack/senlin/node.py @@ -11,15 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. -import copy - -from heat.common.i18n import _ -from heat.engine import attributes -from heat.engine import constraints -from heat.engine import properties from heat.engine.resources.openstack.senlin import res_base -from heat.engine import support -from heat.engine import translation class Node(res_base.BaseSenlinResource): @@ -28,188 +20,7 @@ class Node(res_base.BaseSenlinResource): Node is an object that belongs to at most one Cluster, it can be created based on a profile. """ - - entity = 'node' - - PROPERTIES = ( - NAME, METADATA, PROFILE, CLUSTER - ) = ( - 'name', 'metadata', 'profile', 'cluster' - ) - - _NODE_STATUS = ( - INIT, ACTIVE, CREATING, - ) = ( - 'INIT', 'ACTIVE', 'CREATING', - ) - - ATTRIBUTES = ( - ATTR_DETAILS, ATTR_CLUSTER, - ) = ( - 'details', 'cluster_id' - ) - - properties_schema = { - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the senlin node. By default, physical resource name ' - 'is used.'), - update_allowed=True, - ), - METADATA: properties.Schema( - properties.Schema.MAP, - _('Metadata key-values defined for node.'), - update_allowed=True, - ), - PROFILE: properties.Schema( - properties.Schema.STRING, - _('Name or ID of senlin profile to create this node.'), - required=True, - update_allowed=True, - constraints=[ - constraints.CustomConstraint('senlin.profile') - ] - ), - CLUSTER: properties.Schema( - properties.Schema.STRING, - _('The name of senlin cluster to attach to.'), - update_allowed=True, - constraints=[ - constraints.CustomConstraint('senlin.cluster') - ], - support_status=support.SupportStatus(version='8.0.0'), - ), - } - - attributes_schema = { - ATTR_DETAILS: attributes.Schema( - _("The details of physical object."), - type=attributes.Schema.MAP - ), - ATTR_CLUSTER: attributes.Schema( - _("The cluster ID this node belongs to."), - type=attributes.Schema.STRING - ), - } - - def translation_rules(self, props): - rules = [ - translation.TranslationRule( - props, - translation.TranslationRule.RESOLVE, - translation_path=[self.PROFILE], - client_plugin=self.client_plugin(), - finder='get_profile_id'), - translation.TranslationRule( - props, - translation.TranslationRule.RESOLVE, - translation_path=[self.CLUSTER], - client_plugin=self.client_plugin(), - finder='get_cluster_id'), - ] - return rules - - def handle_create(self): - params = { - 'name': (self.properties[self.NAME] or - self.physical_resource_name()), - 'metadata': self.properties[self.METADATA], - 'profile_id': self.properties[self.PROFILE], - 'cluster_id': self.properties[self.CLUSTER], - } - - node = self.client().create_node(**params) - action_id = node.location.split('/')[-1] - self.resource_id_set(node.id) - return action_id - - def check_create_complete(self, action_id): - return self.client_plugin().check_action_status(action_id) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().delete_node(self.resource_id) - return self.resource_id - - def check_delete_complete(self, res_id): - if res_id: - with self.client_plugin().ignore_not_found: - self.client().get_node(self.resource_id) - return False - - return True - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - actions = [] - if prop_diff: - old_cluster = None - new_cluster = None - if self.PROFILE in prop_diff: - prop_diff['profile_id'] = prop_diff.pop(self.PROFILE) - if self.CLUSTER in prop_diff: - old_cluster = self.properties[self.CLUSTER] - new_cluster = prop_diff.pop(self.CLUSTER) - if old_cluster: - params = { - 'cluster': old_cluster, - 'nodes': [self.resource_id], - } - action = { - 'func': 'remove_nodes_from_cluster', - 'action_id': None, - 'params': params, - 'done': False, - } - actions.append(action) - if prop_diff: - node = self.client().get_node(self.resource_id) - params = copy.deepcopy(prop_diff) - params['node'] = node - action = { - 'func': 'update_node', - 'action_id': None, - 'params': params, - 'done': False, - } - actions.append(action) - if new_cluster: - params = { - 'cluster': new_cluster, - 'nodes': [self.resource_id], - } - action = { - 'func': 'add_nodes_to_cluster', - 'action_id': None, - 'params': params, - 'done': False, - } - actions.append(action) - - return actions - - def check_update_complete(self, actions): - return self.client_plugin().execute_actions(actions) - - def _resolve_attribute(self, name): - if self.resource_id is None: - return - node = self.client().get_node(self.resource_id, details=True) - return getattr(node, name, None) - - def parse_live_resource_data(self, resource_properties, resource_data): - reality = {} - - for key in self._update_allowed_properties: - if key == self.PROFILE: - value = resource_data.get('profile_id') - elif key == self.CLUSTER: - value = resource_data.get('cluster_id') - else: - value = resource_data.get(key) - reality.update({key: value}) - - return reality + pass def resource_mapping(): diff --git a/heat/engine/resources/openstack/senlin/policy.py b/heat/engine/resources/openstack/senlin/policy.py index d225e85f1b..d1bbd631b8 100644 --- a/heat/engine/resources/openstack/senlin/policy.py +++ b/heat/engine/resources/openstack/senlin/policy.py @@ -12,14 +12,7 @@ # under the License. # -import copy - -from heat.common import exception -from heat.common.i18n import _ -from heat.engine import constraints -from heat.engine import properties from heat.engine.resources.openstack.senlin import res_base -from heat.engine import translation class Policy(res_base.BaseSenlinResource): @@ -28,186 +21,7 @@ class Policy(res_base.BaseSenlinResource): A policy is a set of rules that can be checked and/or enforced when an action is performed on a Cluster. """ - - entity = 'policy' - - PROPERTIES = ( - NAME, TYPE, POLICY_PROPS, BINDINGS, - ) = ( - 'name', 'type', 'properties', 'bindings' - ) - - _BINDINGS = ( - BD_CLUSTER, BD_ENABLED, - ) = ( - 'cluster', 'enabled' - ) - - _ACTION_STATUS = ( - ACTION_SUCCEEDED, ACTION_FAILED, - ) = ( - 'SUCCEEDED', 'FAILED', - ) - - properties_schema = { - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the senlin policy. By default, physical resource name ' - 'is used.'), - update_allowed=True, - ), - TYPE: properties.Schema( - properties.Schema.STRING, - _('The type of senlin policy.'), - required=True, - constraints=[ - constraints.CustomConstraint('senlin.policy_type') - ] - ), - POLICY_PROPS: properties.Schema( - properties.Schema.MAP, - _('Properties of this policy.'), - ), - BINDINGS: properties.Schema( - properties.Schema.LIST, - _('A list of clusters to which this policy is attached.'), - update_allowed=True, - schema=properties.Schema( - properties.Schema.MAP, - schema={ - BD_CLUSTER: properties.Schema( - properties.Schema.STRING, - _("The name or ID of target cluster."), - required=True, - constraints=[ - constraints.CustomConstraint('senlin.cluster') - ] - ), - BD_ENABLED: properties.Schema( - properties.Schema.BOOLEAN, - _("Whether enable this policy on that cluster."), - default=True, - ), - } - ) - ) - } - - def translation_rules(self, props): - rules = [ - translation.TranslationRule( - props, - translation.TranslationRule.RESOLVE, - translation_path=[self.BINDINGS, self.BD_CLUSTER], - client_plugin=self.client_plugin(), - finder='get_cluster_id'), - ] - return rules - - def remove_bindings(self, bindings): - for bd in bindings: - try: - bd['action'] = self.client().detach_policy_from_cluster( - bd[self.BD_CLUSTER], self.resource_id)['action'] - bd['finished'] = False - except Exception as ex: - # policy didn't attach to cluster, skip. - if (self.client_plugin().is_bad_request(ex) or - self.client_plugin().is_not_found(ex)): - bd['finished'] = True - else: - raise - - def add_bindings(self, bindings): - for bd in bindings: - bd['action'] = self.client().attach_policy_to_cluster( - bd[self.BD_CLUSTER], self.resource_id, - enabled=bd[self.BD_ENABLED])['action'] - bd['finished'] = False - - def check_action_done(self, bindings): - ret = True - if not bindings: - return ret - for bd in bindings: - if bd.get('finished', False): - continue - action = self.client().get_action(bd['action']) - if action.status == self.ACTION_SUCCEEDED: - bd['finished'] = True - elif action.status == self.ACTION_FAILED: - err_msg = _('Failed to execute %(action)s for ' - '%(cluster)s: %(reason)s') % { - 'action': action.action, - 'cluster': bd[self.BD_CLUSTER], - 'reason': action.status_reason} - raise exception.ResourceInError( - status_reason=err_msg, - resource_status=self.FAILED) - else: - ret = False - return ret - - def handle_create(self): - params = { - 'name': (self.properties[self.NAME] or - self.physical_resource_name()), - 'spec': self.client_plugin().generate_spec( - self.properties[self.TYPE], - self.properties[self.POLICY_PROPS] - ) - } - - policy = self.client().create_policy(**params) - self.resource_id_set(policy.id) - bindings = copy.deepcopy(self.properties[self.BINDINGS]) - if bindings: - self.add_bindings(bindings) - return bindings - - def check_create_complete(self, bindings): - return self.check_action_done(bindings) - - def handle_delete(self): - if not self.resource_id: - return - - return copy.deepcopy(self.properties[self.BINDINGS]) - - def check_delete_complete(self, bindings): - if not bindings: - return True - self.remove_bindings(bindings) - if self.check_action_done(bindings): - with self.client_plugin().ignore_not_found: - self.client().delete_policy(self.resource_id) - return True - return False - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - if self.NAME in prop_diff: - param = {'name': prop_diff[self.NAME]} - policy_obj = self.client().get_policy(self.resource_id) - self.client().update_policy(policy_obj, **param) - actions = dict() - if self.BINDINGS in prop_diff: - old = self.properties[self.BINDINGS] or [] - new = prop_diff[self.BINDINGS] or [] - actions['remove'] = [bd for bd in old if bd not in new] - actions['add'] = [bd for bd in new if bd not in old] - self.remove_bindings(actions['remove']) - return actions - - def check_update_complete(self, actions): - ret = True - remove_done = self.check_action_done(actions.get('remove', [])) - # wait until detach finished, then start attach - if remove_done and 'add' in actions: - if not actions.get('add_started', False): - self.add_bindings(actions['add']) - actions['add_started'] = True - ret = self.check_action_done(actions['add']) - return ret + pass def resource_mapping(): diff --git a/heat/engine/resources/openstack/senlin/profile.py b/heat/engine/resources/openstack/senlin/profile.py index a32d869650..c5b4086e2d 100644 --- a/heat/engine/resources/openstack/senlin/profile.py +++ b/heat/engine/resources/openstack/senlin/profile.py @@ -13,9 +13,6 @@ # # Copyright 2015 IBM Corp. -from heat.common.i18n import _ -from heat.engine import constraints -from heat.engine import properties from heat.engine.resources.openstack.senlin import res_base @@ -25,63 +22,7 @@ class Profile(res_base.BaseSenlinResource): Profile resource in senlin is a template describing how to create nodes in cluster. """ - - entity = 'profile' - - PROPERTIES = ( - NAME, TYPE, METADATA, PROFILE_PROPERTIES, - ) = ( - 'name', 'type', 'metadata', 'properties', - ) - - properties_schema = { - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the senlin profile. By default, physical resource name ' - 'is used.'), - update_allowed=True, - ), - TYPE: properties.Schema( - properties.Schema.STRING, - _('The type of profile.'), - required=True, - constraints=[ - constraints.CustomConstraint('senlin.profile_type') - ] - ), - METADATA: properties.Schema( - properties.Schema.MAP, - _('Metadata key-values defined for profile.'), - update_allowed=True, - ), - PROFILE_PROPERTIES: properties.Schema( - properties.Schema.MAP, - _('Properties for profile.'), - ) - } - - def handle_create(self): - params = { - 'name': (self.properties[self.NAME] or - self.physical_resource_name()), - 'spec': self.client_plugin().generate_spec( - spec_type=self.properties[self.TYPE], - spec_props=self.properties[self.PROFILE_PROPERTIES]), - 'metadata': self.properties[self.METADATA], - } - - profile = self.client().create_profile(**params) - self.resource_id_set(profile.id) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().delete_profile(self.resource_id) - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - if prop_diff: - profile_obj = self.client().get_profile(self.resource_id) - self.client().update_profile(profile_obj, **prop_diff) + pass def resource_mapping(): diff --git a/heat/engine/resources/openstack/senlin/receiver.py b/heat/engine/resources/openstack/senlin/receiver.py index 8221e16299..33e1165581 100644 --- a/heat/engine/resources/openstack/senlin/receiver.py +++ b/heat/engine/resources/openstack/senlin/receiver.py @@ -11,10 +11,6 @@ # License for the specific language governing permissions and limitations # under the License. -from heat.common.i18n import _ -from heat.engine import attributes -from heat.engine import constraints -from heat.engine import properties from heat.engine.resources.openstack.senlin import res_base @@ -24,94 +20,7 @@ class Receiver(res_base.BaseSenlinResource): Receiver is an abstract resource created at the senlin engine that can be used to hook the engine to some external event/alarm sources. """ - - entity = 'receiver' - - PROPERTIES = ( - CLUSTER, ACTION, NAME, TYPE, PARAMS, - ) = ( - 'cluster', 'action', 'name', 'type', 'params', - ) - - ATTRIBUTES = ( - ATTR_CHANNEL, - ) = ( - 'channel', - ) - - _ACTIONS = ( - CLUSTER_SCALE_OUT, CLUSTER_SCALE_IN, - ) = ( - 'CLUSTER_SCALE_OUT', 'CLUSTER_SCALE_IN', - ) - - _TYPES = ( - WEBHOOK, - ) = ( - 'webhook', - ) - - properties_schema = { - CLUSTER: properties.Schema( - properties.Schema.STRING, - _('Name or ID of target cluster.'), - required=True, - constraints=[ - constraints.CustomConstraint('senlin.cluster') - ] - ), - ACTION: properties.Schema( - properties.Schema.STRING, - _('The action to be executed when the receiver is signaled.'), - required=True, - constraints=[ - constraints.AllowedValues(_ACTIONS) - ] - ), - NAME: properties.Schema( - properties.Schema.STRING, - _('Name of the senlin receiver. By default, ' - 'physical resource name is used.'), - ), - TYPE: properties.Schema( - properties.Schema.STRING, - _('Type of receiver.'), - default=WEBHOOK, - constraints=[ - constraints.AllowedValues(_TYPES) - ] - ), - PARAMS: properties.Schema( - properties.Schema.MAP, - _('The parameters passed to action when the receiver ' - 'is signaled.'), - ), - } - - attributes_schema = { - ATTR_CHANNEL: attributes.Schema( - _("The channel for receiving signals."), - type=attributes.Schema.MAP - ), - } - - def handle_create(self): - params = { - 'name': (self.properties[self.NAME] or - self.physical_resource_name()), - 'cluster_id': self.properties[self.CLUSTER], - 'type': self.properties[self.TYPE], - 'action': self.properties[self.ACTION], - 'params': self.properties[self.PARAMS], - } - - recv = self.client().create_receiver(**params) - self.resource_id_set(recv.id) - - def handle_delete(self): - if self.resource_id is not None: - with self.client_plugin().ignore_not_found: - self.client().delete_receiver(self.resource_id) + pass def resource_mapping(): diff --git a/heat/engine/resources/openstack/senlin/res_base.py b/heat/engine/resources/openstack/senlin/res_base.py index 3df3fd6043..af68e81a4a 100644 --- a/heat/engine/resources/openstack/senlin/res_base.py +++ b/heat/engine/resources/openstack/senlin/res_base.py @@ -12,39 +12,22 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_log import log as logging - from heat.common.i18n import _ -from heat.engine import resource +from heat.engine.resources.openstack.heat import none_resource from heat.engine import support -LOG = logging.getLogger(__name__) - -class BaseSenlinResource(resource.Resource): +class BaseSenlinResource(none_resource.NoneResource): """A base class for Senlin resources.""" support_status = support.SupportStatus( - version='22.0.0', - status=support.DEPRECATED, - message=_('Senlin project was marked inactive'), + version='23.0.0', + status=support.HIDDEN, + message=_('Senlin project was retired'), previous_status=support.SupportStatus( - version='6.0.0', - )) - - default_client_name = 'senlin' - - def _show_resource(self): - method_name = 'get_' + self.entity - try: - client_method = getattr(self.client(), method_name) - res_info = client_method(self.resource_id) - return res_info.to_dict() - except AttributeError as ex: - LOG.warning("No method to get the resource: %s", ex) - - def _resolve_attribute(self, name): - if self.resource_id is None: - return - res_info = self._show_resource() - return res_info.get(name) + version='22.0.0', + status=support.DEPRECATED, + message=_('Senlin project was marked inactive'), + previous_status=support.SupportStatus( + version='6.0.0', + ))) diff --git a/heat/tests/clients/test_senlin_client.py b/heat/tests/clients/test_senlin_client.py deleted file mode 100644 index 0e3bdf3d8c..0000000000 --- a/heat/tests/clients/test_senlin_client.py +++ /dev/null @@ -1,198 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from unittest import mock - -from openstack import exceptions - -from heat.engine.clients.os import senlin as senlin_plugin -from heat.tests import common -from heat.tests import utils - - -class SenlinClientPluginTest(common.HeatTestCase): - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(SenlinClientPluginTest, self).setUp() - context = utils.dummy_context() - self.plugin = context.clients.client_plugin('senlin') - self.client = self.plugin.client() - - def test_cluster_get(self): - self.assertIsNotNone(self.client.clusters) - - def test_is_bad_request(self): - self.assertTrue(self.plugin.is_bad_request( - exceptions.HttpException(http_status=400))) - self.assertFalse(self.plugin.is_bad_request(Exception)) - self.assertFalse(self.plugin.is_bad_request( - exceptions.HttpException(http_status=404))) - - def test_check_action_success(self): - mock_action = mock.MagicMock() - mock_action.status = 'SUCCEEDED' - mock_get = self.patchobject(self.client, 'get_action') - mock_get.return_value = mock_action - self.assertTrue(self.plugin.check_action_status('fake_id')) - mock_get.assert_called_once_with('fake_id') - - def test_get_profile_id(self): - mock_profile = mock.Mock(id='fake_profile_id') - mock_get = self.patchobject(self.client, 'get_profile', - return_value=mock_profile) - ret = self.plugin.get_profile_id('fake_profile') - self.assertEqual('fake_profile_id', ret) - mock_get.assert_called_once_with('fake_profile') - - def test_get_cluster_id(self): - mock_cluster = mock.Mock(id='fake_cluster_id') - mock_get = self.patchobject(self.client, 'get_cluster', - return_value=mock_cluster) - ret = self.plugin.get_cluster_id('fake_cluster') - self.assertEqual('fake_cluster_id', ret) - mock_get.assert_called_once_with('fake_cluster') - - def test_get_policy_id(self): - mock_policy = mock.Mock(id='fake_policy_id') - mock_get = self.patchobject(self.client, 'get_policy', - return_value=mock_policy) - ret = self.plugin.get_policy_id('fake_policy') - self.assertEqual('fake_policy_id', ret) - mock_get.assert_called_once_with('fake_policy') - - -class ProfileConstraintTest(common.HeatTestCase): - - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(ProfileConstraintTest, self).setUp() - self.senlin_client = mock.MagicMock() - self.ctx = utils.dummy_context() - self.mock_get_profile = mock.Mock() - self.ctx.clients.client( - 'senlin').get_profile = self.mock_get_profile - self.constraint = senlin_plugin.ProfileConstraint() - - def test_validate_true(self): - self.mock_get_profile.return_value = None - self.assertTrue(self.constraint.validate("PROFILE_ID", self.ctx)) - - def test_validate_false(self): - self.mock_get_profile.side_effect = exceptions.ResourceNotFound( - 'PROFILE_ID') - self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx)) - self.mock_get_profile.side_effect = exceptions.HttpException( - 'PROFILE_ID') - self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx)) - - -class ClusterConstraintTest(common.HeatTestCase): - - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(ClusterConstraintTest, self).setUp() - self.senlin_client = mock.MagicMock() - self.ctx = utils.dummy_context() - self.mock_get_cluster = mock.Mock() - self.ctx.clients.client( - 'senlin').get_cluster = self.mock_get_cluster - self.constraint = senlin_plugin.ClusterConstraint() - - def test_validate_true(self): - self.mock_get_cluster.return_value = None - self.assertTrue(self.constraint.validate("CLUSTER_ID", self.ctx)) - - def test_validate_false(self): - self.mock_get_cluster.side_effect = exceptions.ResourceNotFound( - 'CLUSTER_ID') - self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx)) - self.mock_get_cluster.side_effect = exceptions.HttpException( - 'CLUSTER_ID') - self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx)) - - -class PolicyConstraintTest(common.HeatTestCase): - - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(PolicyConstraintTest, self).setUp() - self.senlin_client = mock.MagicMock() - self.ctx = utils.dummy_context() - self.mock_get_policy = mock.Mock() - self.ctx.clients.client( - 'senlin').get_policy = self.mock_get_policy - self.constraint = senlin_plugin.PolicyConstraint() - - def test_validate_true(self): - self.mock_get_policy.return_value = None - self.assertTrue(self.constraint.validate("POLICY_ID", self.ctx)) - - def test_validate_false(self): - self.mock_get_policy.side_effect = exceptions.ResourceNotFound( - 'POLICY_ID') - self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx)) - self.mock_get_policy.side_effect = exceptions.HttpException( - 'POLICY_ID') - self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx)) - - -class ProfileTypeConstraintTest(common.HeatTestCase): - - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(ProfileTypeConstraintTest, self).setUp() - self.senlin_client = mock.MagicMock() - self.ctx = utils.dummy_context() - heat_profile_type = mock.MagicMock() - heat_profile_type.name = 'os.heat.stack-1.0' - nova_profile_type = mock.MagicMock() - nova_profile_type.name = 'os.nova.server-1.0' - self.mock_profile_types = mock.Mock( - return_value=[heat_profile_type, nova_profile_type]) - self.ctx.clients.client( - 'senlin').profile_types = self.mock_profile_types - self.constraint = senlin_plugin.ProfileTypeConstraint() - - def test_validate_true(self): - self.assertTrue(self.constraint.validate("os.heat.stack-1.0", - self.ctx)) - - def test_validate_false(self): - self.assertFalse(self.constraint.validate("Invalid_type", - self.ctx)) - - -class PolicyTypeConstraintTest(common.HeatTestCase): - - @mock.patch('openstack.connection.Connection') - def setUp(self, mock_connection): - super(PolicyTypeConstraintTest, self).setUp() - self.senlin_client = mock.MagicMock() - self.ctx = utils.dummy_context() - deletion_policy_type = mock.MagicMock() - deletion_policy_type.name = 'senlin.policy.deletion-1.0' - lb_policy_type = mock.MagicMock() - lb_policy_type.name = 'senlin.policy.loadbalance-1.0' - self.mock_policy_types = mock.Mock( - return_value=[deletion_policy_type, lb_policy_type]) - self.ctx.clients.client( - 'senlin').policy_types = self.mock_policy_types - self.constraint = senlin_plugin.PolicyTypeConstraint() - - def test_validate_true(self): - self.assertTrue(self.constraint.validate( - "senlin.policy.deletion-1.0", self.ctx)) - - def test_validate_false(self): - self.assertFalse(self.constraint.validate("Invalid_type", - self.ctx)) diff --git a/heat/tests/openstack/senlin/__init__.py b/heat/tests/openstack/senlin/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/heat/tests/openstack/senlin/test_cluster.py b/heat/tests/openstack/senlin/test_cluster.py deleted file mode 100644 index e30dd25837..0000000000 --- a/heat/tests/openstack/senlin/test_cluster.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright 2015 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -from unittest import mock - -from oslo_config import cfg - -from heat.common import exception -from heat.common import template_format -from heat.engine.clients.os import senlin -from heat.engine.resources.openstack.senlin import cluster as sc -from heat.engine import scheduler -from heat.engine import template -from heat.tests import common -from heat.tests import utils -from openstack import exceptions - - -cluster_stack_template = """ -heat_template_version: 2016-04-08 -description: Senlin Cluster Template -resources: - senlin-cluster: - type: OS::Senlin::Cluster - properties: - name: SenlinCluster - profile: fake_profile - policies: - - policy: fake_policy - enabled: true - min_size: 0 - max_size: -1 - desired_capacity: 1 - timeout: 3600 - metadata: - foo: bar -""" - - -class FakeCluster(object): - def __init__(self, id='some_id', status='ACTIVE'): - self.status = status - self.status_reason = 'Unknown' - self.id = id - self.name = "SenlinCluster" - self.metadata = {} - self.nodes = ['node1'] - self.desired_capacity = 1 - self.metadata = {'foo': 'bar'} - self.timeout = 3600 - self.max_size = -1 - self.min_size = 0 - self.location = 'actions/fake-action' - self.profile_name = 'fake_profile' - self.profile_id = 'fake_profile_id' - - def to_dict(self): - return { - 'id': self.id, - 'status': self.status, - 'status_reason': self.status_reason, - 'name': self.name, - 'metadata': self.metadata, - 'timeout': self.timeout, - 'desired_capacity': self.desired_capacity, - 'max_size': self.max_size, - 'min_size': self.min_size, - 'nodes': self.nodes, - 'profile_name': self.profile_name, - 'profile_id': self.profile_id - } - - -class SenlinClusterTest(common.HeatTestCase): - def setUp(self): - super(SenlinClusterTest, self).setUp() - self.senlin_mock = mock.MagicMock() - self.senlin_mock.get_profile.return_value = mock.Mock( - id='fake_profile_id' - ) - self.patchobject(sc.Cluster, 'client', return_value=self.senlin_mock) - self.patchobject(senlin.SenlinClientPlugin, 'client', - return_value=self.senlin_mock) - self.patchobject(senlin.ProfileConstraint, 'validate', - return_value=True) - self.patchobject(senlin.PolicyConstraint, 'validate', - return_value=True) - self.fake_cl = FakeCluster() - self.t = template_format.parse(cluster_stack_template) - - def _init_cluster(self, template): - self.stack = utils.parse_stack(template) - cluster = self.stack['senlin-cluster'] - return cluster - - def _create_cluster(self, template): - cluster = self._init_cluster(template) - self.senlin_mock.create_cluster.return_value = self.fake_cl - self.senlin_mock.get_cluster.return_value = self.fake_cl - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - self.senlin_mock.get_policy.return_value = mock.Mock( - id='fake_policy_id' - ) - self.senlin_mock.cluster_policies.return_value = [ - {'policy_id': 'fake_policy_id', 'enabled': True} - ] - scheduler.TaskRunner(cluster.create)() - self.assertEqual((cluster.CREATE, cluster.COMPLETE), - cluster.state) - self.assertEqual(self.fake_cl.id, cluster.resource_id) - self.assertEqual(1, self.senlin_mock.get_action.call_count) - self.assertEqual(1, self.senlin_mock.get_cluster.call_count) - return cluster - - def test_cluster_create_success(self): - self._create_cluster(self.t) - create_cluster_kwargs = { - 'name': 'SenlinCluster', - 'profile_id': 'fake_profile_id', - 'desired_capacity': 1, - 'min_size': 0, - 'max_size': -1, - 'metadata': {'foo': 'bar'}, - 'timeout': 3600, - } - attach_policy_kwargs = { - 'cluster': self.fake_cl.id, - 'policy': 'fake_policy_id', - 'enabled': True - } - self.senlin_mock.create_cluster.assert_called_once_with( - **create_cluster_kwargs) - self.senlin_mock.attach_policy_to_cluster.assert_called_once_with( - **attach_policy_kwargs) - - def test_cluster_create_error(self): - cfg.CONF.set_override('action_retry_limit', 0) - cluster = self._init_cluster(self.t) - self.senlin_mock.create_cluster.return_value = self.fake_cl - mock_cluster = mock.MagicMock() - mock_cluster.status = 'ERROR' - mock_cluster.status_reason = 'oops' - self.senlin_mock.get_policy.return_value = mock.Mock( - id='fake_policy_id' - ) - self.senlin_mock.get_cluster.return_value = mock_cluster - create_task = scheduler.TaskRunner(cluster.create) - ex = self.assertRaises(exception.ResourceFailure, create_task) - expected = ('ResourceInError: resources.senlin-cluster: ' - 'Went to status ERROR due to "oops"') - self.assertEqual(expected, str(ex)) - - def test_cluster_delete_success(self): - cluster = self._create_cluster(self.t) - self.senlin_mock.get_cluster.side_effect = [ - exceptions.ResourceNotFound('SenlinCluster'), - ] - scheduler.TaskRunner(cluster.delete)() - self.senlin_mock.delete_cluster.assert_called_once_with( - cluster.resource_id) - - def test_cluster_delete_error(self): - cluster = self._create_cluster(self.t) - self.senlin_mock.get_cluster.side_effect = exception.Error('oops') - delete_task = scheduler.TaskRunner(cluster.delete) - ex = self.assertRaises(exception.ResourceFailure, delete_task) - expected = 'Error: resources.senlin-cluster: oops' - self.assertEqual(expected, str(ex)) - - def test_cluster_update_profile(self): - cluster = self._create_cluster(self.t) - # Mock translate rules - self.senlin_mock.get_profile.side_effect = [ - mock.Mock(id='new_profile_id'), - mock.Mock(id='fake_profile_id'), - mock.Mock(id='new_profile_id'), - ] - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-cluster']['properties'] - props['profile'] = 'new_profile' - props['name'] = 'new_name' - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_cluster = rsrc_defns['senlin-cluster'] - self.senlin_mock.update_cluster.return_value = mock.Mock( - cluster=new_cluster) - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(cluster.update, new_cluster)() - self.assertEqual((cluster.UPDATE, cluster.COMPLETE), cluster.state) - cluster_update_kwargs = { - 'profile_id': 'new_profile_id', - 'name': 'new_name' - } - self.senlin_mock.update_cluster.assert_called_once_with( - cluster=self.fake_cl, **cluster_update_kwargs) - self.assertEqual(1, self.senlin_mock.get_action.call_count) - - def test_cluster_update_desire_capacity(self): - cluster = self._create_cluster(self.t) - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-cluster']['properties'] - props['desired_capacity'] = 10 - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_cluster = rsrc_defns['senlin-cluster'] - self.senlin_mock.resize_cluster.return_value = { - 'action': 'fake-action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(cluster.update, new_cluster)() - self.assertEqual((cluster.UPDATE, cluster.COMPLETE), cluster.state) - cluster_resize_kwargs = { - 'adjustment_type': 'EXACT_CAPACITY', - 'number': 10 - } - self.senlin_mock.resize_cluster.assert_called_once_with( - cluster=cluster.resource_id, **cluster_resize_kwargs) - self.assertEqual(2, self.senlin_mock.get_action.call_count) - - def test_cluster_update_policy_add_remove(self): - cluster = self._create_cluster(self.t) - # Mock translate rules - self.senlin_mock.get_policy.side_effect = [ - mock.Mock(id='new_policy_id'), - mock.Mock(id='fake_policy_id'), - mock.Mock(id='new_policy_id'), - ] - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-cluster']['properties'] - props['policies'] = [{'policy': 'new_policy'}] - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_cluster = rsrc_defns['senlin-cluster'] - self.senlin_mock.detach_policy_from_cluster.return_value = { - 'action': 'fake-action'} - self.senlin_mock.attach_policy_to_cluster.return_value = { - 'action': 'fake-action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(cluster.update, new_cluster)() - self.assertEqual((cluster.UPDATE, cluster.COMPLETE), cluster.state) - detach_policy_kwargs = { - 'policy': 'fake_policy_id', - 'cluster': cluster.resource_id, - 'enabled': True, - } - self.assertEqual(2, - self.senlin_mock.attach_policy_to_cluster.call_count) - self.senlin_mock.detach_policy_from_cluster.assert_called_once_with( - **detach_policy_kwargs) - self.assertEqual(0, self.senlin_mock.update_cluster_policy.call_count) - self.assertEqual(3, self.senlin_mock.get_action.call_count) - - def test_cluster_update_policy_exists(self): - cluster = self._create_cluster(self.t) - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-cluster']['properties'] - props['policies'] = [{'policy': 'fake_policy', 'enabled': False}] - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_cluster = rsrc_defns['senlin-cluster'] - self.senlin_mock.update_cluster_policy.return_value = { - 'action': 'fake-action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(cluster.update, new_cluster)() - self.assertEqual((cluster.UPDATE, cluster.COMPLETE), cluster.state) - update_policy_kwargs = { - 'policy': 'fake_policy_id', - 'cluster': cluster.resource_id, - 'enabled': False, - } - self.senlin_mock.update_cluster_policy.assert_called_once_with( - **update_policy_kwargs) - self.assertEqual(1, self.senlin_mock. - attach_policy_to_cluster.call_count) - self.assertEqual(0, self.senlin_mock. - detach_policy_from_cluster.call_count) - - def test_cluster_update_failed(self): - cluster = self._create_cluster(self.t) - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-cluster']['properties'] - props['desired_capacity'] = 3 - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - update_snippet = rsrc_defns['senlin-cluster'] - self.senlin_mock.resize_cluster.return_value = { - 'action': 'fake-action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='FAILED', status_reason='Unknown') - exc = self.assertRaises( - exception.ResourceFailure, - scheduler.TaskRunner(cluster.update, update_snippet)) - self.assertEqual('ResourceInError: resources.senlin-cluster: ' - 'Went to status FAILED due to "Unknown"', - str(exc)) - - def test_cluster_get_attr_collect(self): - cluster = self._create_cluster(self.t) - self.senlin_mock.collect_cluster_attrs.return_value = [ - mock.Mock(attr_value='ip1')] - attr_path1 = ['details.addresses.private[0].addr'] - self.assertEqual( - ['ip1'], cluster.get_attribute(cluster.ATTR_COLLECT, *attr_path1)) - attr_path2 = ['details.addresses.private[0].addr', 0] - self.assertEqual( - 'ip1', cluster.get_attribute(cluster.ATTR_COLLECT, *attr_path2)) - self.senlin_mock.collect_cluster_attrs.assert_called_with( - cluster.resource_id, attr_path2[0]) - - def test_cluster_resolve_attribute(self): - excepted_show = { - 'id': 'some_id', - 'status': 'ACTIVE', - 'status_reason': 'Unknown', - 'name': 'SenlinCluster', - 'metadata': {'foo': 'bar'}, - 'timeout': 3600, - 'desired_capacity': 1, - 'max_size': -1, - 'min_size': 0, - 'nodes': ['node1'], - 'profile_name': 'fake_profile', - 'profile_id': 'fake_profile_id', - 'policies': [{'policy_id': 'fake_policy_id', 'enabled': True}] - } - cluster = self._create_cluster(self.t) - self.assertEqual(self.fake_cl.desired_capacity, - cluster._resolve_attribute('desired_capacity')) - self.assertEqual(['node1'], - cluster._resolve_attribute('nodes')) - self.assertEqual(excepted_show, - cluster._show_resource()) - - def test_cluster_get_live_state(self): - expected_reality = { - 'name': 'SenlinCluster', - 'metadata': {'foo': 'bar'}, - 'timeout': 3600, - 'desired_capacity': 1, - 'max_size': -1, - 'min_size': 0, - 'profile': 'fake_profile_id', - 'policies': [{'policy': 'fake_policy_id', 'enabled': True}] - } - cluster = self._create_cluster(self.t) - self.senlin_mock.get_cluster.return_value = self.fake_cl - - reality = cluster.get_live_state(cluster.properties) - - self.assertEqual(expected_reality, reality) - - -class TestSenlinClusterValidation(common.HeatTestCase): - def setUp(self): - super(TestSenlinClusterValidation, self).setUp() - self.t = template_format.parse(cluster_stack_template) - - def test_invalid_min_max_size(self): - self.t['resources']['senlin-cluster']['properties']['min_size'] = 2 - self.t['resources']['senlin-cluster']['properties']['max_size'] = 1 - stack = utils.parse_stack(self.t) - ex = self.assertRaises(exception.StackValidationFailed, - stack['senlin-cluster'].validate) - self.assertEqual('min_size can not be greater than max_size', - str(ex)) - - def test_invalid_desired_capacity(self): - self.t['resources']['senlin-cluster']['properties']['min_size'] = 1 - self.t['resources']['senlin-cluster']['properties']['max_size'] = 2 - self.t['resources']['senlin-cluster']['properties'][ - 'desired_capacity'] = 3 - stack = utils.parse_stack(self.t) - ex = self.assertRaises(exception.StackValidationFailed, - stack['senlin-cluster'].validate) - self.assertEqual( - 'desired_capacity must be between min_size and max_size', - str(ex) - ) diff --git a/heat/tests/openstack/senlin/test_node.py b/heat/tests/openstack/senlin/test_node.py deleted file mode 100644 index dc801b3304..0000000000 --- a/heat/tests/openstack/senlin/test_node.py +++ /dev/null @@ -1,242 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -from unittest import mock - -from oslo_config import cfg - -from heat.common import exception -from heat.common import template_format -from heat.engine.clients.os import senlin -from heat.engine.resources.openstack.senlin import node as sn -from heat.engine import scheduler -from heat.engine import template -from heat.tests import common -from heat.tests import utils -from openstack import exceptions - - -node_stack_template = """ -heat_template_version: 2016-04-08 -description: Senlin Node Template -resources: - senlin-node: - type: OS::Senlin::Node - properties: - name: SenlinNode - profile: fake_profile - cluster: fake_cluster - metadata: - foo: bar -""" - - -class FakeNode(object): - def __init__(self, id='some_id', status='ACTIVE'): - self.status = status - self.status_reason = 'Unknown' - self.id = id - self.name = "SenlinNode" - self.metadata = {'foo': 'bar'} - self.profile_id = "fake_profile_id" - self.cluster_id = "fake_cluster_id" - self.details = {'id': 'physical_object_id'} - self.location = "actions/fake_action" - - def to_dict(self): - return { - 'id': self.id, - 'status': self.status, - 'status_reason': self.status_reason, - 'name': self.name, - 'metadata': self.metadata, - 'profile_id': self.profile_id, - 'cluster_id': self.cluster_id, - } - - -class SenlinNodeTest(common.HeatTestCase): - def setUp(self): - super(SenlinNodeTest, self).setUp() - self.senlin_mock = mock.MagicMock() - self.senlin_mock.get_profile.return_value = mock.Mock( - id='fake_profile_id' - ) - self.senlin_mock.get_cluster.return_value = mock.Mock( - id='fake_cluster_id' - ) - self.patchobject(sn.Node, 'client', return_value=self.senlin_mock) - self.patchobject(senlin.SenlinClientPlugin, 'client', - return_value=self.senlin_mock) - self.patchobject(senlin.ProfileConstraint, 'validate', - return_value=True) - self.patchobject(senlin.ClusterConstraint, 'validate', - return_value=True) - self.fake_node = FakeNode() - self.t = template_format.parse(node_stack_template) - self.stack = utils.parse_stack(self.t) - self.node = self.stack['senlin-node'] - - def _create_node(self): - self.senlin_mock.create_node.return_value = self.fake_node - self.senlin_mock.get_node.return_value = self.fake_node - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(self.node.create)() - self.assertEqual((self.node.CREATE, self.node.COMPLETE), - self.node.state) - self.assertEqual(self.fake_node.id, self.node.resource_id) - return self.node - - def test_node_create_success(self): - self._create_node() - expect_kwargs = { - 'name': 'SenlinNode', - 'profile_id': 'fake_profile_id', - 'metadata': {'foo': 'bar'}, - 'cluster_id': 'fake_cluster_id', - } - self.senlin_mock.create_node.assert_called_once_with( - **expect_kwargs) - - def test_node_create_error(self): - cfg.CONF.set_override('action_retry_limit', 0) - self.senlin_mock.create_node.return_value = self.fake_node - mock_action = mock.MagicMock() - mock_action.status = 'FAILED' - mock_action.status_reason = 'oops' - self.senlin_mock.get_action.return_value = mock_action - create_task = scheduler.TaskRunner(self.node.create) - ex = self.assertRaises(exception.ResourceFailure, create_task) - expected = ('ResourceInError: resources.senlin-node: ' - 'Went to status FAILED due to "oops"') - self.assertEqual(expected, str(ex)) - - def test_node_delete_success(self): - node = self._create_node() - self.senlin_mock.get_node.side_effect = [ - exceptions.ResourceNotFound('SenlinNode'), - ] - scheduler.TaskRunner(node.delete)() - self.senlin_mock.delete_node.assert_called_once_with( - node.resource_id) - - def test_cluster_delete_error(self): - node = self._create_node() - self.senlin_mock.get_node.side_effect = exception.Error('oops') - delete_task = scheduler.TaskRunner(node.delete) - ex = self.assertRaises(exception.ResourceFailure, delete_task) - expected = 'Error: resources.senlin-node: oops' - self.assertEqual(expected, str(ex)) - - def test_node_update_profile(self): - node = self._create_node() - # Mock translate rules - self.senlin_mock.get_profile.side_effect = [ - mock.Mock(id='new_profile_id'), - mock.Mock(id='fake_profile_id'), - mock.Mock(id='new_profile_id'), - ] - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-node']['properties'] - props['profile'] = 'new_profile' - props['name'] = 'new_name' - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_node = rsrc_defns['senlin-node'] - self.senlin_mock.update_node.return_value = mock.Mock( - location='/actions/fake-action') - scheduler.TaskRunner(node.update, new_node)() - self.assertEqual((node.UPDATE, node.COMPLETE), node.state) - node_update_kwargs = { - 'profile_id': 'new_profile_id', - 'name': 'new_name' - } - self.senlin_mock.update_node.assert_called_once_with( - node=self.fake_node, **node_update_kwargs) - self.assertEqual(2, self.senlin_mock.get_action.call_count) - - def test_node_update_cluster(self): - node = self._create_node() - # Mock translate rules - self.senlin_mock.get_cluster.side_effect = [ - mock.Mock(id='new_cluster_id'), - mock.Mock(id='fake_cluster_id'), - mock.Mock(id='new_cluster_id'), - ] - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-node']['properties'] - props['cluster'] = 'new_cluster' - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_node = rsrc_defns['senlin-node'] - self.senlin_mock.remove_nodes_from_cluster.return_value = { - 'action': 'remove_node_from_cluster' - } - self.senlin_mock.add_nodes_to_cluster.return_value = { - 'action': 'add_node_to_cluster' - } - scheduler.TaskRunner(node.update, new_node)() - self.assertEqual((node.UPDATE, node.COMPLETE), node.state) - self.senlin_mock.remove_nodes_from_cluster.assert_called_once_with( - cluster='fake_cluster_id', nodes=[node.resource_id]) - self.senlin_mock.add_nodes_to_cluster.assert_called_once_with( - cluster='new_cluster_id', nodes=[node.resource_id]) - - def test_node_update_failed(self): - node = self._create_node() - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-node']['properties'] - props['name'] = 'new_name' - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_node = rsrc_defns['senlin-node'] - self.senlin_mock.update_node.return_value = mock.Mock( - location='/actions/fake-action') - self.senlin_mock.get_action.return_value = mock.Mock( - status='FAILED', status_reason='oops') - update_task = scheduler.TaskRunner(node.update, new_node) - ex = self.assertRaises(exception.ResourceFailure, update_task) - expected = ('ResourceInError: resources.senlin-node: Went to ' - 'status FAILED due to "oops"') - self.assertEqual(expected, str(ex)) - self.assertEqual((node.UPDATE, node.FAILED), node.state) - self.assertEqual(2, self.senlin_mock.get_action.call_count) - - def test_cluster_resolve_attribute(self): - excepted_show = { - 'id': 'some_id', - 'status': 'ACTIVE', - 'status_reason': 'Unknown', - 'name': 'SenlinNode', - 'metadata': {'foo': 'bar'}, - 'profile_id': 'fake_profile_id', - 'cluster_id': 'fake_cluster_id' - } - node = self._create_node() - self.assertEqual(excepted_show, - node._show_resource()) - self.assertEqual(self.fake_node.details, - node._resolve_attribute('details')) - self.senlin_mock.get_node.assert_called_with( - node.resource_id, details=True) - - def test_node_get_live_state(self): - expected_reality = { - 'name': 'SenlinNode', - 'metadata': {'foo': 'bar'}, - 'profile': 'fake_profile_id', - 'cluster': 'fake_cluster_id' - } - node = self._create_node() - reality = node.get_live_state(node.properties) - self.assertEqual(expected_reality, reality) diff --git a/heat/tests/openstack/senlin/test_policy.py b/heat/tests/openstack/senlin/test_policy.py deleted file mode 100644 index 5763cc561d..0000000000 --- a/heat/tests/openstack/senlin/test_policy.py +++ /dev/null @@ -1,195 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -from unittest import mock - -from openstack.clustering.v1._proxy import Proxy -from openstack import exceptions -from oslo_config import cfg - -from heat.common import exception -from heat.common import template_format -from heat.engine.clients.os import senlin -from heat.engine.resources.openstack.senlin import policy -from heat.engine import scheduler -from heat.engine import template -from heat.tests import common -from heat.tests import utils - - -policy_stack_template = """ -heat_template_version: 2016-04-08 -description: Senlin Policy Template -resources: - senlin-policy: - type: OS::Senlin::Policy - properties: - name: SenlinPolicy - type: senlin.policy.deletion-1.0 - properties: - criteria: OLDEST_FIRST - bindings: - - cluster: c1 -""" - -policy_spec = { - 'type': 'senlin.policy.deletion', - 'version': '1.0', - 'properties': { - 'criteria': 'OLDEST_FIRST' - } -} - - -class FakePolicy(object): - def __init__(self, id='some_id', spec=None): - self.id = id - self.name = "SenlinPolicy" - - def to_dict(self): - return { - 'id': self.id, - 'name': self.name, - } - - -class SenlinPolicyTest(common.HeatTestCase): - def setUp(self): - super(SenlinPolicyTest, self).setUp() - self.patchobject(senlin.ClusterConstraint, 'validate', - return_value=True) - self.patchobject(senlin.PolicyTypeConstraint, 'validate', - return_value=True) - self.senlin_mock = mock.MagicMock(spec=Proxy) - self.senlin_mock.get_cluster.return_value = mock.Mock( - id='c1_id') - self.patchobject(policy.Policy, 'client', - return_value=self.senlin_mock) - self.patchobject(senlin.SenlinClientPlugin, 'client', - return_value=self.senlin_mock) - self.fake_p = FakePolicy() - self.t = template_format.parse(policy_stack_template) - - def _init_policy(self, template): - self.stack = utils.parse_stack(template) - policy = self.stack['senlin-policy'] - return policy - - def _create_policy(self, template): - policy = self._init_policy(template) - self.senlin_mock.create_policy.return_value = self.fake_p - self.senlin_mock.attach_policy_to_cluster.return_value = { - 'action': 'fake_action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='SUCCEEDED') - scheduler.TaskRunner(policy.create)() - self.assertEqual((policy.CREATE, policy.COMPLETE), - policy.state) - self.assertEqual(self.fake_p.id, policy.resource_id) - self.senlin_mock.attach_policy_to_cluster.assert_called_once_with( - 'c1_id', policy.resource_id, enabled=True) - self.senlin_mock.get_action.assert_called_once_with('fake_action') - return policy - - def test_policy_create(self): - self._create_policy(self.t) - expect_kwargs = { - 'name': 'SenlinPolicy', - 'spec': policy_spec - } - self.senlin_mock.create_policy.assert_called_once_with( - **expect_kwargs) - - def test_policy_create_fail(self): - cfg.CONF.set_override('action_retry_limit', 0) - policy = self._init_policy(self.t) - self.senlin_mock.create_policy.return_value = self.fake_p - self.senlin_mock.attach_policy_to_cluster.return_value = { - 'action': 'fake_action'} - self.senlin_mock.get_action.return_value = mock.Mock( - status='FAILED', status_reason='oops', - action='CLUSTER_ATTACH_POLICY') - create_task = scheduler.TaskRunner(policy.create) - self.assertRaises(exception.ResourceFailure, create_task) - self.assertEqual((policy.CREATE, policy.FAILED), - policy.state) - err_msg = ('ResourceInError: resources.senlin-policy: Went to status ' - 'FAILED due to "Failed to execute CLUSTER_ATTACH_POLICY ' - 'for c1_id: oops"') - self.assertEqual(err_msg, policy.status_reason) - - def test_policy_delete_not_found(self): - self.senlin_mock.detach_policy_from_cluster.return_value = { - 'action': 'fake_action'} - policy = self._create_policy(self.t) - self.senlin_mock.get_policy.side_effect = [ - exceptions.ResourceNotFound('SenlinPolicy'), - ] - scheduler.TaskRunner(policy.delete)() - self.senlin_mock.detach_policy_from_cluster.assert_called_once_with( - 'c1_id', policy.resource_id) - self.senlin_mock.delete_policy.assert_called_once_with( - policy.resource_id) - - def test_policy_delete_not_attached(self): - policy = self._create_policy(self.t) - self.senlin_mock.get_policy.side_effect = [ - exceptions.ResourceNotFound('SenlinPolicy'), - ] - self.senlin_mock.detach_policy_from_cluster.side_effect = [ - exceptions.HttpException(http_status=400), - ] - scheduler.TaskRunner(policy.delete)() - self.senlin_mock.detach_policy_from_cluster.assert_called_once_with( - 'c1_id', policy.resource_id) - self.senlin_mock.delete_policy.assert_called_once_with( - policy.resource_id) - - def test_policy_update(self): - policy = self._create_policy(self.t) - # Mock translate rules - self.senlin_mock.get_cluster.side_effect = [ - mock.Mock(id='c2_id'), - mock.Mock(id='c1_id'), - mock.Mock(id='c2_id'), - ] - new_t = copy.deepcopy(self.t) - props = new_t['resources']['senlin-policy']['properties'] - props['bindings'] = [{'cluster': 'c2'}] - props['name'] = 'new_name' - rsrc_defns = template.Template(new_t).resource_definitions(self.stack) - new_cluster = rsrc_defns['senlin-policy'] - self.senlin_mock.attach_policy_to_cluster.return_value = { - 'action': 'fake_action1'} - self.senlin_mock.detach_policy_from_cluster.return_value = { - 'action': 'fake_action2'} - self.senlin_mock.get_policy.return_value = self.fake_p - scheduler.TaskRunner(policy.update, new_cluster)() - self.assertEqual((policy.UPDATE, policy.COMPLETE), policy.state) - self.senlin_mock.update_policy.assert_called_once_with( - self.fake_p, name='new_name') - self.senlin_mock.detach_policy_from_cluster.assert_called_once_with( - 'c1_id', policy.resource_id) - self.senlin_mock.attach_policy_to_cluster.assert_called_with( - 'c2_id', policy.resource_id, enabled=True) - - def test_policy_resolve_attribute(self): - excepted_show = { - 'id': 'some_id', - 'name': 'SenlinPolicy', - } - policy = self._create_policy(self.t) - self.senlin_mock.get_policy.return_value = FakePolicy() - self.assertEqual(excepted_show, policy._show_resource()) diff --git a/heat/tests/openstack/senlin/test_profile.py b/heat/tests/openstack/senlin/test_profile.py deleted file mode 100644 index c3eb6b869d..0000000000 --- a/heat/tests/openstack/senlin/test_profile.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2015 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -from heat.common import template_format -from heat.engine.clients.os import senlin -from heat.engine.resources.openstack.senlin import profile as sp -from heat.engine import scheduler -from heat.tests import common -from heat.tests import utils - - -profile_stack_template = """ -heat_template_version: 2016-04-08 -description: Senlin Profile Template -resources: - senlin-profile: - type: OS::Senlin::Profile - properties: - name: SenlinProfile - type: os.heat.stack-1.0 - properties: - template: - heat_template_version: 2014-10-16 - resources: - random: - type: OS::Heat::RandomString -""" - -profile_spec = { - 'type': 'os.heat.stack', - 'version': '1.0', - 'properties': { - 'template': { - 'heat_template_version': '2014-10-16', - 'resources': { - 'random': { - 'type': 'OS::Heat::RandomString' - } - } - } - } -} - - -class FakeProfile(object): - def __init__(self, id='some_id', spec=None): - self.id = id - self.name = "SenlinProfile" - self.metadata = {} - self.spec = spec or profile_spec - - -class SenlinProfileTest(common.HeatTestCase): - def setUp(self): - super(SenlinProfileTest, self).setUp() - self.senlin_mock = mock.MagicMock() - self.patchobject(sp.Profile, 'client', return_value=self.senlin_mock) - self.patchobject(senlin.ProfileTypeConstraint, 'validate', - return_value=True) - self.fake_p = FakeProfile() - self.t = template_format.parse(profile_stack_template) - - def _init_profile(self, template): - self.stack = utils.parse_stack(template) - profile = self.stack['senlin-profile'] - return profile - - def _create_profile(self, template): - profile = self._init_profile(template) - self.senlin_mock.create_profile.return_value = self.fake_p - scheduler.TaskRunner(profile.create)() - self.assertEqual((profile.CREATE, profile.COMPLETE), - profile.state) - self.assertEqual(self.fake_p.id, profile.resource_id) - return profile - - def test_profile_create(self): - self._create_profile(self.t) - expect_kwargs = { - 'name': 'SenlinProfile', - 'metadata': None, - 'spec': profile_spec - } - self.senlin_mock.create_profile.assert_called_once_with( - **expect_kwargs) - - def test_profile_delete(self): - self.senlin_mock.delete_profile.return_value = None - profile = self._create_profile(self.t) - scheduler.TaskRunner(profile.delete)() - self.senlin_mock.delete_profile.assert_called_once_with( - profile.resource_id) - - def test_profile_update(self): - profile = self._create_profile(self.t) - prop_diff = {'metadata': {'foo': 'bar'}} - self.senlin_mock.get_profile.return_value = self.fake_p - profile.handle_update(json_snippet=None, - tmpl_diff=None, - prop_diff=prop_diff) - self.senlin_mock.update_profile.assert_called_once_with( - self.fake_p, **prop_diff) diff --git a/heat/tests/openstack/senlin/test_receiver.py b/heat/tests/openstack/senlin/test_receiver.py deleted file mode 100644 index 5685424b2d..0000000000 --- a/heat/tests/openstack/senlin/test_receiver.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import mock - -from openstack import exceptions - -from heat.common import template_format -from heat.engine.clients.os import senlin -from heat.engine.resources.openstack.senlin import receiver as sr -from heat.engine import scheduler -from heat.tests import common -from heat.tests import utils - - -receiver_stack_template = """ -heat_template_version: 2016-04-08 -description: Senlin Receiver Template -resources: - senlin-receiver: - type: OS::Senlin::Receiver - properties: - name: SenlinReceiver - cluster: fake_cluster - action: CLUSTER_SCALE_OUT - type: webhook - params: - foo: bar -""" - - -class FakeReceiver(object): - def __init__(self, id='some_id'): - self.id = id - self.name = "SenlinReceiver" - self.cluster_id = "fake_cluster" - self.action = "CLUSTER_SCALE_OUT" - self.channel = {'alarm_url': "http://foo.bar/webhooks/fake_url"} - - def to_dict(self): - return { - 'id': self.id, - 'name': self.name, - 'cluster_id': self.cluster_id, - 'action': self.action, - 'channel': self.channel, - 'actor': {'trust_id': ['fake_trust_id']} - } - - -class SenlinReceiverTest(common.HeatTestCase): - def setUp(self): - super(SenlinReceiverTest, self).setUp() - self.senlin_mock = mock.MagicMock() - self.patchobject(sr.Receiver, 'client', - return_value=self.senlin_mock) - self.patchobject(senlin.ClusterConstraint, 'validate', - return_value=True) - self.fake_r = FakeReceiver() - self.t = template_format.parse(receiver_stack_template) - - def _init_recv(self, template): - self.stack = utils.parse_stack(template) - recv = self.stack['senlin-receiver'] - return recv - - def _create_recv(self, template): - recv = self._init_recv(template) - self.senlin_mock.create_receiver.return_value = self.fake_r - self.senlin_mock.get_receiver.return_value = self.fake_r - scheduler.TaskRunner(recv.create)() - self.assertEqual((recv.CREATE, recv.COMPLETE), - recv.state) - self.assertEqual(self.fake_r.id, recv.resource_id) - return recv - - def test_recv_create_success(self): - self._create_recv(self.t) - expect_kwargs = { - 'name': 'SenlinReceiver', - 'cluster_id': 'fake_cluster', - 'action': 'CLUSTER_SCALE_OUT', - 'type': 'webhook', - 'params': {'foo': 'bar'}, - } - self.senlin_mock.create_receiver.assert_called_once_with( - **expect_kwargs) - - def test_recv_delete_success(self): - self.senlin_mock.delete_receiver.return_value = None - recv = self._create_recv(self.t) - scheduler.TaskRunner(recv.delete)() - self.senlin_mock.delete_receiver.assert_called_once_with( - recv.resource_id) - - def test_recv_delete_not_found(self): - self.senlin_mock.delete_receiver.side_effect = [ - exceptions.ResourceNotFound(http_status=404) - ] - recv = self._create_recv(self.t) - scheduler.TaskRunner(recv.delete)() - self.senlin_mock.delete_receiver.assert_called_once_with( - recv.resource_id) - - def test_cluster_resolve_attribute(self): - excepted_show = { - 'id': 'some_id', - 'name': 'SenlinReceiver', - 'cluster_id': 'fake_cluster', - 'action': 'CLUSTER_SCALE_OUT', - 'channel': {'alarm_url': "http://foo.bar/webhooks/fake_url"}, - 'actor': {'trust_id': ['fake_trust_id']} - } - recv = self._create_recv(self.t) - self.assertEqual(self.fake_r.channel, - recv._resolve_attribute('channel')) - self.assertEqual(excepted_show, - recv._show_resource()) diff --git a/releasenotes/notes/remove-senlin-586d6754a7610bdf.yaml b/releasenotes/notes/remove-senlin-586d6754a7610bdf.yaml new file mode 100644 index 0000000000..a9fc7c1d96 --- /dev/null +++ b/releasenotes/notes/remove-senlin-586d6754a7610bdf.yaml @@ -0,0 +1,15 @@ +--- +upgrade: + - | + Integration with senlin has been removed because the senlin project has + been retired. Because of the removal, the following resource types are no + longer supported and now hidden. + + - ``OS::Senlin::Cluster`` + - ``OS::Senlin::Node`` + - ``OS::Senlin::Policy`` + - ``OS::Senlin::Profile`` + - ``OS::Senlin::Receiver`` + + Also, the options in ``[clients_senlin]`` section have been removed. + diff --git a/setup.cfg b/setup.cfg index 33b50f9e0a..c2cd5591f9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,7 +90,6 @@ heat.clients = octavia = heat.engine.clients.os.octavia:OctaviaClientPlugin openstack = heat.engine.clients.os.openstacksdk:OpenStackSDKPlugin sahara = heat.engine.clients.os.sahara:SaharaClientPlugin - senlin = heat.engine.clients.os.senlin:SenlinClientPlugin swift = heat.engine.clients.os.swift:SwiftClientPlugin trove = heat.engine.clients.os.trove:TroveClientPlugin vitrage = heat.engine.clients.os.vitrage:VitrageClientPlugin @@ -171,11 +170,6 @@ heat.constraints = sahara.job_binary = heat.engine.clients.os.sahara:JobBinaryConstraint sahara.job_type = heat.engine.clients.os.sahara:JobTypeConstraint sahara.plugin = heat.engine.clients.os.sahara:PluginConstraint - senlin.cluster = heat.engine.clients.os.senlin:ClusterConstraint - senlin.policy = heat.engine.clients.os.senlin:PolicyConstraint - senlin.policy_type = heat.engine.clients.os.senlin:PolicyTypeConstraint - senlin.profile = heat.engine.clients.os.senlin:ProfileConstraint - senlin.profile_type = heat.engine.clients.os.senlin:ProfileTypeConstraint trove.flavor = heat.engine.clients.os.trove:FlavorConstraint zaqar.queue = heat.engine.clients.os.zaqar:QueueConstraint #ironic