"Fossies" - the Fresh Open Source Software Archive

Member "openstack-heat-13.0.0/heat/engine/resources/openstack/senlin/cluster.py" (16 Oct 2019, 15536 Bytes) of package /linux/misc/openstack/openstack-heat-13.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "cluster.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 12.0.0_vs_13.0.0.

    1 #    Copyright 2015 IBM Corp.
    2 #
    3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 #    not use this file except in compliance with the License. You may obtain
    5 #    a copy of the License at
    6 #
    7 #         http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 #    Unless required by applicable law or agreed to in writing, software
   10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 #    License for the specific language governing permissions and limitations
   13 #    under the License.
   14 
   15 import six
   16 
   17 from heat.common import exception
   18 from heat.common.i18n import _
   19 from heat.engine import attributes
   20 from heat.engine import constraints
   21 from heat.engine import properties
   22 from heat.engine.resources.openstack.senlin import res_base
   23 from heat.engine import support
   24 from heat.engine import translation
   25 
   26 
   27 class Cluster(res_base.BaseSenlinResource):
   28     """A resource that creates a Senlin Cluster.
   29 
   30     Cluster resource in senlin can create and manage objects of
   31     the same nature, e.g. Nova servers, Heat stacks, Cinder volumes, etc.
   32     The collection of these objects is referred to as a cluster.
   33     """
   34 
   35     entity = 'cluster'
   36 
   37     PROPERTIES = (
   38         NAME, PROFILE, DESIRED_CAPACITY, MIN_SIZE, MAX_SIZE,
   39         METADATA, TIMEOUT, POLICIES,
   40     ) = (
   41         'name', 'profile', 'desired_capacity', 'min_size', 'max_size',
   42         'metadata', 'timeout', 'policies',
   43     )
   44 
   45     ATTRIBUTES = (
   46         ATTR_NAME, ATTR_METADATA, ATTR_NODES, ATTR_DESIRED_CAPACITY,
   47         ATTR_MIN_SIZE, ATTR_MAX_SIZE, ATTR_POLICIES, ATTR_COLLECT,
   48     ) = (
   49         "name", 'metadata', 'nodes', 'desired_capacity',
   50         'min_size', 'max_size', 'policies', 'collect',
   51     )
   52 
   53     _POLICIES = (
   54         P_POLICY, P_ENABLED,
   55     ) = (
   56         "policy", "enabled",
   57     )
   58 
   59     _CLUSTER_STATUS = (
   60         CLUSTER_INIT, CLUSTER_ACTIVE, CLUSTER_ERROR, CLUSTER_WARNING,
   61         CLUSTER_CREATING, CLUSTER_DELETING, CLUSTER_UPDATING
   62     ) = (
   63         'INIT', 'ACTIVE', 'ERROR', 'WARNING',
   64         'CREATING', 'DELETING', 'UPDATING'
   65     )
   66 
   67     properties_schema = {
   68         PROFILE: properties.Schema(
   69             properties.Schema.STRING,
   70             _('The name or id of the Senlin profile.'),
   71             required=True,
   72             update_allowed=True,
   73             constraints=[
   74                 constraints.CustomConstraint('senlin.profile')
   75             ]
   76         ),
   77         NAME: properties.Schema(
   78             properties.Schema.STRING,
   79             _('Name of the cluster. By default, physical resource name '
   80               'is used.'),
   81             update_allowed=True,
   82         ),
   83         DESIRED_CAPACITY: properties.Schema(
   84             properties.Schema.INTEGER,
   85             _('Desired initial number of resources in cluster.'),
   86             default=0,
   87             update_allowed=True,
   88         ),
   89         MIN_SIZE: properties.Schema(
   90             properties.Schema.INTEGER,
   91             _('Minimum number of resources in the cluster.'),
   92             default=0,
   93             update_allowed=True,
   94             constraints=[
   95                 constraints.Range(min=0)
   96             ]
   97         ),
   98         MAX_SIZE: properties.Schema(
   99             properties.Schema.INTEGER,
  100             _('Maximum number of resources in the cluster. '
  101               '-1 means unlimited.'),
  102             default=-1,
  103             update_allowed=True,
  104             constraints=[
  105                 constraints.Range(min=-1)
  106             ]
  107         ),
  108         METADATA: properties.Schema(
  109             properties.Schema.MAP,
  110             _('Metadata key-values defined for cluster.'),
  111             update_allowed=True,
  112             default={},
  113         ),
  114         TIMEOUT: properties.Schema(
  115             properties.Schema.INTEGER,
  116             _('The number of seconds to wait for the cluster actions.'),
  117             update_allowed=True,
  118             constraints=[
  119                 constraints.Range(min=0)
  120             ]
  121         ),
  122         POLICIES: properties.Schema(
  123             properties.Schema.LIST,
  124             _('A list of policies to attach to this cluster.'),
  125             update_allowed=True,
  126             default=[],
  127             support_status=support.SupportStatus(version='8.0.0'),
  128             schema=properties.Schema(
  129                 properties.Schema.MAP,
  130                 schema={
  131                     P_POLICY: properties.Schema(
  132                         properties.Schema.STRING,
  133                         _("The name or ID of the policy."),
  134                         required=True,
  135                         constraints=[
  136                             constraints.CustomConstraint('senlin.policy')
  137                         ]
  138                     ),
  139                     P_ENABLED: properties.Schema(
  140                         properties.Schema.BOOLEAN,
  141                         _("Whether enable this policy on this cluster."),
  142                         default=True,
  143                     ),
  144                 }
  145             )
  146         ),
  147     }
  148 
  149     attributes_schema = {
  150         ATTR_NAME: attributes.Schema(
  151             _("Cluster name."),
  152             type=attributes.Schema.STRING
  153         ),
  154         ATTR_METADATA: attributes.Schema(
  155             _("Cluster metadata."),
  156             type=attributes.Schema.MAP
  157         ),
  158         ATTR_DESIRED_CAPACITY: attributes.Schema(
  159             _("Desired capacity of the cluster."),
  160             type=attributes.Schema.INTEGER
  161         ),
  162         ATTR_NODES: attributes.Schema(
  163             _("Nodes list in the cluster."),
  164             type=attributes.Schema.LIST,
  165             cache_mode=attributes.Schema.CACHE_NONE
  166         ),
  167         ATTR_MIN_SIZE: attributes.Schema(
  168             _("Min size of the cluster."),
  169             type=attributes.Schema.INTEGER
  170         ),
  171         ATTR_MAX_SIZE: attributes.Schema(
  172             _("Max size of the cluster."),
  173             type=attributes.Schema.INTEGER
  174         ),
  175         ATTR_POLICIES: attributes.Schema(
  176             _("Policies attached to the cluster."),
  177             type=attributes.Schema.LIST,
  178             support_status=support.SupportStatus(version='8.0.0'),
  179         ),
  180         ATTR_COLLECT: attributes.Schema(
  181             _("Attributes collected from cluster. According to the jsonpath "
  182               "following this attribute, it will return a list of attributes "
  183               "collected from the nodes of this cluster."),
  184             type=attributes.Schema.LIST,
  185             support_status=support.SupportStatus(version='8.0.0'),
  186             cache_mode=attributes.Schema.CACHE_NONE
  187         )
  188     }
  189 
  190     def translation_rules(self, props):
  191         rules = [
  192             translation.TranslationRule(
  193                 props,
  194                 translation.TranslationRule.RESOLVE,
  195                 translation_path=[self.PROFILE],
  196                 client_plugin=self.client_plugin(),
  197                 finder='get_profile_id'),
  198             translation.TranslationRule(
  199                 props,
  200                 translation.TranslationRule.RESOLVE,
  201                 translation_path=[self.POLICIES, self.P_POLICY],
  202                 client_plugin=self.client_plugin(),
  203                 finder='get_policy_id'),
  204         ]
  205         return rules
  206 
  207     def handle_create(self):
  208         actions = []
  209         params = {
  210             'name': (self.properties[self.NAME] or
  211                      self.physical_resource_name()),
  212             'profile_id': self.properties[self.PROFILE],
  213             'desired_capacity': self.properties[self.DESIRED_CAPACITY],
  214             'min_size': self.properties[self.MIN_SIZE],
  215             'max_size': self.properties[self.MAX_SIZE],
  216             'metadata': self.properties[self.METADATA],
  217             'timeout': self.properties[self.TIMEOUT]
  218         }
  219 
  220         cluster = self.client().create_cluster(**params)
  221         self.resource_id_set(cluster.id)
  222         # for cluster creation, we just to check the action status
  223         # the action is executed above
  224         action = {
  225             'cluster_id': cluster.id,
  226             'done': False,
  227         }
  228         actions.append(action)
  229         if self.properties[self.POLICIES]:
  230             for p in self.properties[self.POLICIES]:
  231                 params = {
  232                     'cluster': cluster.id,
  233                     'policy': p[self.P_POLICY],
  234                     'enabled': p[self.P_ENABLED],
  235                 }
  236                 action = {
  237                     'func': 'cluster_attach_policy',
  238                     'params': params,
  239                     'action_id': None,
  240                     'done': False,
  241                 }
  242                 actions.append(action)
  243         return actions
  244 
  245     def check_create_complete(self, actions):
  246         return self.client_plugin().execute_actions(actions)
  247 
  248     def handle_delete(self):
  249         if self.resource_id is not None:
  250             with self.client_plugin().ignore_not_found:
  251                 self.client().delete_cluster(self.resource_id)
  252         return self.resource_id
  253 
  254     def check_delete_complete(self, resource_id):
  255         if not resource_id:
  256             return True
  257 
  258         try:
  259             self.client().get_cluster(self.resource_id)
  260         except Exception as ex:
  261             self.client_plugin().ignore_not_found(ex)
  262             return True
  263         return False
  264 
  265     def handle_update(self, json_snippet, tmpl_diff, prop_diff):
  266         UPDATE_PROPS = (self.NAME, self.METADATA, self.TIMEOUT, self.PROFILE)
  267         RESIZE_PROPS = (self.MIN_SIZE, self.MAX_SIZE, self.DESIRED_CAPACITY)
  268         actions = []
  269         if not prop_diff:
  270             return actions
  271         cluster_obj = self.client().get_cluster(self.resource_id)
  272         # Update Policies
  273         if self.POLICIES in prop_diff:
  274             old_policies = self.properties[self.POLICIES]
  275             new_policies = prop_diff[self.POLICIES]
  276             old_policy_ids = [p[self.P_POLICY] for p in old_policies]
  277             update_policies = [p for p in new_policies
  278                                if p[self.P_POLICY] in old_policy_ids]
  279             update_policy_ids = [p[self.P_POLICY] for p in update_policies]
  280             add_policies = [p for p in new_policies
  281                             if p[self.P_POLICY] not in old_policy_ids]
  282             remove_policies = [p for p in old_policies
  283                                if p[self.P_POLICY] not in update_policy_ids]
  284             for p in update_policies:
  285                 params = {
  286                     'policy': p[self.P_POLICY],
  287                     'cluster': self.resource_id,
  288                     'enabled': p[self.P_ENABLED]
  289                 }
  290                 action = {
  291                     'func': 'cluster_update_policy',
  292                     'params': params,
  293                     'action_id': None,
  294                     'done': False,
  295                 }
  296                 actions.append(action)
  297             for p in remove_policies:
  298                 params = {
  299                     'policy': p[self.P_POLICY],
  300                     'cluster': self.resource_id,
  301                     'enabled': p[self.P_ENABLED]
  302                 }
  303                 action = {
  304                     'func': 'cluster_detach_policy',
  305                     'params': params,
  306                     'action_id': None,
  307                     'done': False,
  308                 }
  309                 actions.append(action)
  310             for p in add_policies:
  311                 params = {
  312                     'policy': p[self.P_POLICY],
  313                     'cluster': self.resource_id,
  314                     'enabled': p[self.P_ENABLED]
  315                 }
  316                 action = {
  317                     'func': 'cluster_attach_policy',
  318                     'params': params,
  319                     'action_id': None,
  320                     'done': False,
  321                 }
  322                 actions.append(action)
  323         # Update cluster
  324         if any(p in prop_diff for p in UPDATE_PROPS):
  325             params = dict((k, v) for k, v in six.iteritems(prop_diff)
  326                           if k in UPDATE_PROPS)
  327             params['cluster'] = cluster_obj
  328             if self.PROFILE in params:
  329                 params['profile_id'] = params.pop(self.PROFILE)
  330             action = {
  331                 'func': 'update_cluster',
  332                 'params': params,
  333                 'action_id': None,
  334                 'done': False,
  335             }
  336             actions.append(action)
  337         # Resize Cluster
  338         if any(p in prop_diff for p in RESIZE_PROPS):
  339             params = dict((k, v) for k, v in six.iteritems(prop_diff)
  340                           if k in RESIZE_PROPS)
  341             if self.DESIRED_CAPACITY in params:
  342                 params['adjustment_type'] = 'EXACT_CAPACITY'
  343                 params['number'] = params.pop(self.DESIRED_CAPACITY)
  344             params['cluster'] = self.resource_id
  345             action = {
  346                 'func': 'cluster_resize',
  347                 'params': params,
  348                 'action_id': None,
  349                 'done': False,
  350             }
  351             actions.append(action)
  352         return actions
  353 
  354     def check_update_complete(self, actions):
  355         return self.client_plugin().execute_actions(actions)
  356 
  357     def validate(self):
  358         min_size = self.properties[self.MIN_SIZE]
  359         max_size = self.properties[self.MAX_SIZE]
  360         desired_capacity = self.properties[self.DESIRED_CAPACITY]
  361 
  362         if max_size != -1 and max_size < min_size:
  363             msg = _("%(min_size)s can not be greater than %(max_size)s") % {
  364                 'min_size': self.MIN_SIZE,
  365                 'max_size': self.MAX_SIZE,
  366             }
  367             raise exception.StackValidationFailed(message=msg)
  368 
  369         if (desired_capacity < min_size or
  370                 (max_size != -1 and desired_capacity > max_size)):
  371             msg = _("%(desired_capacity)s must be between %(min_size)s "
  372                     "and %(max_size)s") % {
  373                 'desired_capacity': self.DESIRED_CAPACITY,
  374                 'min_size': self.MIN_SIZE,
  375                 'max_size': self.MAX_SIZE,
  376             }
  377             raise exception.StackValidationFailed(message=msg)
  378 
  379     def get_attribute(self, key, *path):
  380         if self.resource_id is None:
  381             return None
  382 
  383         if key == self.ATTR_COLLECT:
  384             if not path:
  385                 raise exception.InvalidTemplateAttribute(
  386                     resource=self.name, key=key)
  387             attrs = self.client().collect_cluster_attrs(
  388                 self.resource_id, path[0])
  389             attr = [attr.attr_value for attr in attrs]
  390             return attributes.select_from_attribute(attr, path[1:])
  391         else:
  392             return super(Cluster, self).get_attribute(key, *path)
  393 
  394     def _show_resource(self):
  395         cluster_dict = super(Cluster, self)._show_resource()
  396         cluster_dict[self.ATTR_POLICIES] = self.client().cluster_policies(
  397             self.resource_id)
  398         return cluster_dict
  399 
  400     def parse_live_resource_data(self, resource_properties, resource_data):
  401         reality = {}
  402 
  403         for key in self._update_allowed_properties:
  404             if key == self.PROFILE:
  405                 value = resource_data.get('profile_id')
  406             elif key == self.POLICIES:
  407                 value = []
  408                 for p in resource_data.get(self.POLICIES):
  409                     v = {
  410                         'policy': p.get('policy_id'),
  411                         'enabled': p.get('enabled'),
  412                     }
  413                     value.append(v)
  414             else:
  415                 value = resource_data.get(key)
  416             reality.update({key: value})
  417 
  418         return reality
  419 
  420 
  421 def resource_mapping():
  422     return {
  423         'OS::Senlin::Cluster': Cluster
  424     }