"Fossies" - the Fresh Open Source Software Archive

Member "senlin-8.0.0/senlin/policies/batch_policy.py" (16 Oct 2019, 4848 Bytes) of package /linux/misc/openstack/senlin-8.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 "batch_policy.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 6.0.0_vs_7.0.0.

    1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 # not use this file except in compliance with the License. You may obtain
    3 # a copy of the License at
    4 #
    5 #         http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 # Unless required by applicable law or agreed to in writing, software
    8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 # License for the specific language governing permissions and limitations
   11 # under the License.
   12 
   13 """
   14 Policy for batching operations on a cluster.
   15 
   16 NOTE: How update policy works
   17 
   18 Input:
   19    cluster: the cluster whose nodes are to be updated.
   20 Output:
   21    stored in action.data: A dictionary containing a detailed update schedule.
   22    {
   23      'status': 'OK',
   24      'update': {
   25        'pause_time': 2,
   26        'plan': [{
   27            'node-id-1',
   28            'node-id-2',
   29          }, {
   30            'node-id-3',
   31            'node-id-4',
   32          }, {
   33            'node-id-5',
   34          }
   35        ]
   36      }
   37    }
   38 """
   39 import math
   40 
   41 from senlin.common import consts
   42 from senlin.common.i18n import _
   43 from senlin.common import scaleutils as su
   44 from senlin.common import schema
   45 from senlin.policies import base
   46 
   47 
   48 class BatchPolicy(base.Policy):
   49     """Policy for batching the operations on a cluster's nodes."""
   50 
   51     VERSION = '1.0'
   52     VERSIONS = {
   53         '1.0': [
   54             {'status': consts.EXPERIMENTAL, 'since': '2017.02'}
   55         ]
   56     }
   57     PRIORITY = 200
   58 
   59     TARGET = [
   60         ('BEFORE', consts.CLUSTER_UPDATE),
   61     ]
   62 
   63     PROFILE_TYPE = [
   64         'ANY'
   65     ]
   66 
   67     KEYS = (
   68         MIN_IN_SERVICE, MAX_BATCH_SIZE, PAUSE_TIME,
   69     ) = (
   70         'min_in_service', 'max_batch_size', 'pause_time',
   71     )
   72 
   73     properties_schema = {
   74         MIN_IN_SERVICE: schema.Integer(
   75             _('Minimum number of nodes in service when performing updates.'),
   76             default=1,
   77         ),
   78         MAX_BATCH_SIZE: schema.Integer(
   79             _('Maximum number of nodes that will be updated in parallel.'),
   80             default=-1,
   81         ),
   82         PAUSE_TIME: schema.Integer(
   83             _('Interval in seconds between update batches if any.'),
   84             default=60,
   85         )
   86     }
   87 
   88     def __init__(self, name, spec, **kwargs):
   89         super(BatchPolicy, self).__init__(name, spec, **kwargs)
   90 
   91         self.min_in_service = self.properties[self.MIN_IN_SERVICE]
   92         self.max_batch_size = self.properties[self.MAX_BATCH_SIZE]
   93         self.pause_time = self.properties[self.PAUSE_TIME]
   94 
   95     def _get_batch_size(self, total):
   96         """Get batch size for update operation.
   97 
   98         :param total: Total number of nodes.
   99         :returns: Size of each batch.
  100         """
  101 
  102         # if the number of nodes less than min_in_service,
  103         # we divided it to 2 batches
  104         diff = int(math.ceil(float(total) / 2))
  105         if total > self.min_in_service:
  106             diff = total - self.min_in_service
  107 
  108         # max_batch_size is -1 if not specified
  109         if self.max_batch_size == -1 or diff < self.max_batch_size:
  110             batch_size = diff
  111         else:
  112             batch_size = self.max_batch_size
  113 
  114         return batch_size
  115 
  116     def _pick_nodes(self, nodes, batch_size):
  117         """Select nodes based on size and number of batches.
  118 
  119         :param nodes: list of node objects.
  120         :param batch_size: the number of nodes of each batch.
  121         :returns: a list of sets containing the nodes' IDs we
  122                   selected based on the input params.
  123         """
  124         candidates, good = su.filter_error_nodes(nodes)
  125         result = []
  126         # NOTE: we leave the nodes known to be good (ACTIVE) at the end of the
  127         # list so that we have a better chance to ensure 'min_in_service'
  128         # constraint
  129         for n in good:
  130             candidates.append(n.id)
  131 
  132         for start in range(0, len(candidates), batch_size):
  133             end = start + batch_size
  134             result.append(set(candidates[start:end]))
  135 
  136         return result
  137 
  138     def _create_plan(self, action):
  139         nodes = action.entity.nodes
  140 
  141         plan = {'pause_time': self.pause_time}
  142         if len(nodes) == 0:
  143             plan['plan'] = []
  144             return True, plan
  145 
  146         batch_size = self._get_batch_size(len(nodes))
  147         plan['plan'] = self._pick_nodes(nodes, batch_size)
  148 
  149         return True, plan
  150 
  151     def pre_op(self, cluster_id, action):
  152 
  153         pd = {
  154             'status': base.CHECK_OK,
  155             'reason': _('Batching request validated.'),
  156         }
  157         # for updating
  158         result, value = self._create_plan(action)
  159 
  160         if result is False:
  161             pd = {
  162                 'status': base.CHECK_ERROR,
  163                 'reason': value,
  164             }
  165         else:
  166             pd['update'] = value
  167 
  168         action.data.update(pd)
  169         action.store(action.context)
  170 
  171         return