コード例 #1
0
ファイル: properties.py プロジェクト: Hybrid-Cloud/conveyor
        def constraints():
            def get_num(key):
                val = schema_dict.get(key)
                if val is not None:
                    val = Schema.str_to_num(val)
                return val

            if MIN_VALUE in schema_dict or MAX_VALUE in schema_dict:
                yield constr.Range(get_num(MIN_VALUE), get_num(MAX_VALUE))
            if MIN_LENGTH in schema_dict or MAX_LENGTH in schema_dict:
                yield constr.Length(get_num(MIN_LENGTH), get_num(MAX_LENGTH))
            if ALLOWED_VALUES in schema_dict:
                yield constr.AllowedValues(schema_dict[ALLOWED_VALUES])
            if ALLOWED_PATTERN in schema_dict:
                yield constr.AllowedPattern(schema_dict[ALLOWED_PATTERN])
コード例 #2
0
ファイル: parameters.py プロジェクト: Hybrid-Cloud/conveyor
        def constraints():
            desc = schema_dict.get(CONSTRAINT_DESCRIPTION)

            if MIN_VALUE in schema_dict or MAX_VALUE in schema_dict:
                yield constr.Range(Schema.get_num(MIN_VALUE, schema_dict),
                                   Schema.get_num(MAX_VALUE, schema_dict),
                                   desc)
            if MIN_LENGTH in schema_dict or MAX_LENGTH in schema_dict:
                yield constr.Length(Schema.get_num(MIN_LENGTH, schema_dict),
                                    Schema.get_num(MAX_LENGTH, schema_dict),
                                    desc)
            if ALLOWED_VALUES in schema_dict:
                yield constr.AllowedValues(schema_dict[ALLOWED_VALUES], desc)
            if ALLOWED_PATTERN in schema_dict:
                yield constr.AllowedPattern(schema_dict[ALLOWED_PATTERN], desc)
コード例 #3
0
        def constraints():
            constraints = schema_dict.get(cls.CONSTRAINTS)
            if constraints is None:
                return

            if not isinstance(constraints, list):
                raise exception.InvalidSchemaError(
                    message=_("Invalid parameter constraints for parameter "
                              "%s, expected a list") % param_name)

            for constraint in constraints:
                cls._check_dict(constraint, PARAM_CONSTRAINTS,
                                'parameter constraints')
                desc = constraint.get(DESCRIPTION)
                if RANGE in constraint:
                    cdef = constraint.get(RANGE)
                    cls._check_dict(cdef, RANGE_KEYS, 'range constraint')
                    yield constr.Range(parameters.Schema.get_num(MIN, cdef),
                                       parameters.Schema.get_num(MAX, cdef),
                                       desc)
                elif LENGTH in constraint:
                    cdef = constraint.get(LENGTH)
                    cls._check_dict(cdef, RANGE_KEYS, 'length constraint')
                    yield constr.Length(parameters.Schema.get_num(MIN, cdef),
                                        parameters.Schema.get_num(MAX, cdef),
                                        desc)
                elif ALLOWED_VALUES in constraint:
                    cdef = constraint.get(ALLOWED_VALUES)
                    yield constr.AllowedValues(cdef, desc)
                elif ALLOWED_PATTERN in constraint:
                    cdef = constraint.get(ALLOWED_PATTERN)
                    yield constr.AllowedPattern(cdef, desc)
                elif CUSTOM_CONSTRAINT in constraint:
                    cdef = constraint.get(CUSTOM_CONSTRAINT)
                    yield constr.CustomConstraint(cdef, desc)
                else:
                    raise exception.InvalidSchemaError(
                        message=_("No constraint expressed"))
コード例 #4
0
class ResourceGroup(stack_resource.StackResource):
    """Creates one or more identically configured nested resources.

    In addition to the `refs` attribute, this resource implements synthetic
    attributes that mirror those of the resources in the group. When
    getting an attribute from this resource, however, a list of attribute
    values for each resource in the group is returned. To get attribute values
    for a single resource in the group, synthetic attributes of the form
    `resource.{resource index}.{attribute name}` can be used. The resource ID
    of a particular resource in the group can be obtained via the synthetic
    attribute `resource.{resource index}`. Note, that if you get attribute
    without `{resource index}`, e.g. `[resource, {attribute_name}]`, you'll get
    a list of this attribute's value for all resources in group.

    While each resource in the group will be identically configured, this
    resource does allow for some index-based customization of the properties
    of the resources in the group. For example::

      resources:
        my_indexed_group:
          type: OS::Heat::ResourceGroup
          properties:
            count: 3
            resource_def:
              type: OS::Nova::Server
              properties:
                # create a unique name for each server
                # using its index in the group
                name: my_server_%index%
                image: CentOS 6.5
                flavor: 4GB Performance

    would result in a group of three servers having the same image and flavor,
    but names of `my_server_0`, `my_server_1`, and `my_server_2`. The variable
    used for substitution can be customized by using the `index_var` property.
    """

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        COUNT,
        INDEX_VAR,
        RESOURCE_DEF,
        REMOVAL_POLICIES,
    ) = (
        'count',
        'index_var',
        'resource_def',
        'removal_policies',
    )

    _RESOURCE_DEF_KEYS = (
        RESOURCE_DEF_TYPE,
        RESOURCE_DEF_PROPERTIES,
        RESOURCE_DEF_METADATA,
    ) = (
        'type',
        'properties',
        'metadata',
    )

    _REMOVAL_POLICIES_KEYS = (REMOVAL_RSRC_LIST, ) = ('resource_list', )

    _ROLLING_UPDATES_SCHEMA_KEYS = (
        MIN_IN_SERVICE,
        MAX_BATCH_SIZE,
        PAUSE_TIME,
    ) = (
        'min_in_service',
        'max_batch_size',
        'pause_time',
    )

    _BATCH_CREATE_SCHEMA_KEYS = (
        MAX_BATCH_SIZE,
        PAUSE_TIME,
    ) = (
        'max_batch_size',
        'pause_time',
    )

    _UPDATE_POLICY_SCHEMA_KEYS = (
        ROLLING_UPDATE,
        BATCH_CREATE,
    ) = (
        'rolling_update',
        'batch_create',
    )

    ATTRIBUTES = (
        REFS,
        ATTR_ATTRIBUTES,
    ) = (
        'refs',
        'attributes',
    )

    properties_schema = {
        COUNT:
        properties.Schema(properties.Schema.INTEGER,
                          _('The number of resources to create.'),
                          default=1,
                          constraints=[
                              constraints.Range(min=0),
                          ],
                          update_allowed=True),
        INDEX_VAR:
        properties.Schema(
            properties.Schema.STRING,
            _('A variable that this resource will use to replace with the '
              'current index of a given resource in the group. Can be used, '
              'for example, to customize the name property of grouped '
              'servers in order to differentiate them when listed with '
              'nova client.'),
            default="%index%",
            constraints=[constraints.Length(min=3)],
            support_status=support.SupportStatus(version='2014.2')),
        RESOURCE_DEF:
        properties.Schema(
            properties.Schema.MAP,
            _('Resource definition for the resources in the group. The value '
              'of this property is the definition of a resource just as if '
              'it had been declared in the template itself.'),
            schema={
                RESOURCE_DEF_TYPE:
                properties.Schema(properties.Schema.STRING,
                                  _('The type of the resources in the group.'),
                                  required=True),
                RESOURCE_DEF_PROPERTIES:
                properties.Schema(
                    properties.Schema.MAP,
                    _('Property values for the resources in the group.')),
                RESOURCE_DEF_METADATA:
                properties.Schema(
                    properties.Schema.MAP,
                    _('Supplied metadata for the resources in the group.'),
                    support_status=support.SupportStatus(version='5.0.0')),
            },
            required=True,
            update_allowed=True),
        REMOVAL_POLICIES:
        properties.Schema(
            properties.Schema.LIST,
            _('Policies for removal of resources on update.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                _('Policy to be processed when doing an update which '
                  'requires removal of specific resources.'),
                schema={
                    REMOVAL_RSRC_LIST:
                    properties.Schema(
                        properties.Schema.LIST,
                        _("List of resources to be removed "
                          "when doing an update which requires removal of "
                          "specific resources. "
                          "The resource may be specified several ways: "
                          "(1) The resource name, as in the nested stack, "
                          "(2) The resource reference returned from "
                          "get_resource in a template, as available via "
                          "the 'refs' attribute. "
                          "Note this is destructive on update when specified; "
                          "even if the count is not being reduced, and once "
                          "a resource name is removed, it's name is never "
                          "reused in subsequent updates."),
                        default=[]),
                },
            ),
            update_allowed=True,
            default=[],
            support_status=support.SupportStatus(version='2015.1')),
    }

    attributes_schema = {
        REFS:
        attributes.Schema(
            _("A list of resource IDs for the resources in the group."),
            type=attributes.Schema.LIST),
        ATTR_ATTRIBUTES:
        attributes.Schema(
            _("A map of resource names to the specified attribute of each "
              "individual resource. "
              "Requires heat_template_version: 2014-10-16."),
            support_status=support.SupportStatus(version='2014.2'),
            type=attributes.Schema.MAP),
    }

    rolling_update_schema = {
        MIN_IN_SERVICE:
        properties.Schema(properties.Schema.INTEGER,
                          _('The minimum number of resources in service while '
                            'rolling updates are being executed.'),
                          constraints=[constraints.Range(min=0)],
                          default=0),
        MAX_BATCH_SIZE:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The maximum number of resources to replace at once.'),
            constraints=[constraints.Range(min=1)],
            default=1),
        PAUSE_TIME:
        properties.Schema(properties.Schema.NUMBER,
                          _('The number of seconds to wait between batches of '
                            'updates.'),
                          constraints=[constraints.Range(min=0)],
                          default=0),
    }

    batch_create_schema = {
        MAX_BATCH_SIZE:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The maximum number of resources to create at once.'),
            constraints=[constraints.Range(min=1)],
            default=1),
        PAUSE_TIME:
        properties.Schema(properties.Schema.NUMBER,
                          _('The number of seconds to wait between batches.'),
                          constraints=[constraints.Range(min=0)],
                          default=0),
    }

    update_policy_schema = {
        ROLLING_UPDATE:
        properties.Schema(
            properties.Schema.MAP,
            schema=rolling_update_schema,
            support_status=support.SupportStatus(version='5.0.0')),
        BATCH_CREATE:
        properties.Schema(
            properties.Schema.MAP,
            schema=batch_create_schema,
            support_status=support.SupportStatus(version='5.0.0'))
    }

    def get_size(self):
        return self.properties.get(self.COUNT)

    def validate_nested_stack(self):
        # Only validate the resource definition (which may be a
        # nested template) if count is non-zero, to enable folks
        # to disable features via a zero count if they wish
        if not self.get_size():
            return

        test_tmpl = self._assemble_nested(["0"], include_all=True)
        res_def = next(
            six.itervalues(test_tmpl.resource_definitions(self.stack)))
        # make sure we can resolve the nested resource type
        self.stack.env.get_class_to_instantiate(res_def.resource_type)

        try:
            name = "%s-%s" % (self.stack.name, self.name)
            nested_stack = self._parse_nested_stack(name, test_tmpl,
                                                    self.child_params())
            nested_stack.strict_validate = False
            nested_stack.validate()
        except Exception as ex:
            msg = _("Failed to validate: %s") % six.text_type(ex)
            raise exception.StackValidationFailed(message=msg)

    def _name_blacklist(self):
        """Resolve the remove_policies to names for removal."""

        nested = self.nested()

        # To avoid reusing names after removal, we store a comma-separated
        # blacklist in the resource data
        db_rsrc_names = self.data().get('name_blacklist')
        if db_rsrc_names:
            current_blacklist = db_rsrc_names.split(',')
        else:
            current_blacklist = []

        # Now we iterate over the removal policies, and update the blacklist
        # with any additional names
        rsrc_names = set(current_blacklist)

        if nested:
            for r in self.properties[self.REMOVAL_POLICIES]:
                if self.REMOVAL_RSRC_LIST in r:
                    # Tolerate string or int list values
                    for n in r[self.REMOVAL_RSRC_LIST]:
                        str_n = six.text_type(n)
                        if str_n in nested:
                            rsrc_names.add(str_n)
                            continue
                        rsrc = nested.resource_by_refid(str_n)
                        if rsrc:
                            rsrc_names.add(rsrc.name)

        # If the blacklist has changed, update the resource data
        if rsrc_names != set(current_blacklist):
            self.data_set('name_blacklist', ','.join(rsrc_names))
        return rsrc_names

    def _resource_names(self, size=None):
        name_blacklist = self._name_blacklist()
        if size is None:
            size = self.get_size()

        def is_blacklisted(name):
            return name in name_blacklist

        candidates = six.moves.map(six.text_type, itertools.count())

        return itertools.islice(
            six.moves.filterfalse(is_blacklisted, candidates), size)

    def _count_black_listed(self):
        """Return the number of current resource names that are blacklisted."""
        existing_members = grouputils.get_member_names(self)
        return len(self._name_blacklist() & set(existing_members))

    def handle_create(self):
        if self.update_policy.get(self.BATCH_CREATE):
            batch_create = self.update_policy[self.BATCH_CREATE]
            max_batch_size = batch_create[self.MAX_BATCH_SIZE]
            pause_sec = batch_create[self.PAUSE_TIME]
            checkers = self._replace(0, max_batch_size, pause_sec)
            checkers[0].start()
            return checkers
        else:
            names = self._resource_names()
            self.create_with_template(self._assemble_nested(names),
                                      self.child_params(),
                                      self.stack.timeout_secs())

    def check_create_complete(self, checkers=None):
        if checkers is None:
            return super(ResourceGroup, self).check_create_complete()
        for checker in checkers:
            if not checker.started():
                checker.start()
            if not checker.step():
                return False
        return True

    def _run_to_completion(self, template, timeout):
        updater = self.update_with_template(template, {}, timeout)

        while not super(ResourceGroup, self).check_update_complete(updater):
            yield

    def _run_update(self, total_capacity, max_updates, timeout):
        template = self._assemble_for_rolling_update(total_capacity,
                                                     max_updates)
        return self._run_to_completion(template, timeout)

    def check_update_complete(self, checkers):
        for checker in checkers:
            if not checker.started():
                checker.start()
            if not checker.step():
                return False
        return True

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if tmpl_diff:
            # parse update policy
            if rsrc_defn.UPDATE_POLICY in tmpl_diff:
                up = json_snippet.update_policy(self.update_policy_schema,
                                                self.context)
                self.update_policy = up

        checkers = []
        self.properties = json_snippet.properties(self.properties_schema,
                                                  self.context)
        if prop_diff and self.RESOURCE_DEF in prop_diff:
            updaters = self._try_rolling_update()
            if updaters:
                checkers.extend(updaters)

        if not checkers:
            resizer = scheduler.TaskRunner(
                self._run_to_completion,
                self._assemble_nested(self._resource_names()),
                self.stack.timeout_mins)
            checkers.append(resizer)

        checkers[0].start()
        return checkers

    def get_attribute(self, key, *path):
        if key.startswith("resource."):
            return grouputils.get_nested_attrs(self, key, False, *path)

        names = self._resource_names()
        if key == self.REFS:
            vals = [grouputils.get_rsrc_id(self, key, False, n) for n in names]
            return attributes.select_from_attribute(vals, path)
        if key == self.ATTR_ATTRIBUTES:
            if not path:
                raise exception.InvalidTemplateAttribute(resource=self.name,
                                                         key=key)
            return dict(
                (n, grouputils.get_rsrc_attr(self, key, False, n, *path))
                for n in names)

        path = [key] + list(path)
        return [
            grouputils.get_rsrc_attr(self, key, False, n, *path) for n in names
        ]

    def build_resource_definition(self, res_name, res_defn):
        res_def = copy.deepcopy(res_defn)
        props = res_def.get(self.RESOURCE_DEF_PROPERTIES)
        if props:
            repl_props = self._handle_repl_val(res_name, props)
            res_def[self.RESOURCE_DEF_PROPERTIES] = repl_props
        return template.HOTemplate20130523.rsrc_defn_from_snippet(
            res_name, res_def)

    def get_resource_def(self, include_all=False):
        """Returns the resource definition portion of the group.

        :param include_all: if False, only properties for the resource
               definition that are not empty will be included
        :type include_all: bool
        :return: resource definition for the group
        :rtype: dict
        """

        # At this stage, we don't mind if all of the parameters have values
        # assigned. Pass in a custom resolver to the properties to not
        # error when a parameter does not have a user entered value.
        def ignore_param_resolve(snippet):
            while isinstance(snippet, function.Function):
                try:
                    snippet = snippet.result()
                except exception.UserParameterMissing:
                    return None

            if isinstance(snippet, collections.Mapping):
                return dict(
                    (k, ignore_param_resolve(v)) for k, v in snippet.items())
            elif (not isinstance(snippet, six.string_types)
                  and isinstance(snippet, collections.Iterable)):
                return [ignore_param_resolve(v) for v in snippet]

            return snippet

        self.properties.resolve = ignore_param_resolve

        res_def = self.properties[self.RESOURCE_DEF]
        if not include_all:
            return self._clean_props(res_def)
        return res_def

    def _clean_props(self, res_defn):
        res_def = copy.deepcopy(res_defn)
        props = res_def.get(self.RESOURCE_DEF_PROPERTIES)
        if props:
            clean = dict((k, v) for k, v in props.items() if v is not None)
            props = clean
            res_def[self.RESOURCE_DEF_PROPERTIES] = props
        return res_def

    def _handle_repl_val(self, res_name, val):
        repl_var = self.properties[self.INDEX_VAR]

        def recurse(x):
            return self._handle_repl_val(res_name, x)

        if isinstance(val, six.string_types):
            return val.replace(repl_var, res_name)
        elif isinstance(val, collections.Mapping):
            return {k: recurse(v) for k, v in val.items()}
        elif isinstance(val, collections.Sequence):
            return [recurse(v) for v in val]
        return val

    def _assemble_nested(self,
                         names,
                         include_all=False,
                         template_version=('heat_template_version',
                                           '2015-04-30')):

        def_dict = self.get_resource_def(include_all)
        definitions = [(k, self.build_resource_definition(k, def_dict))
                       for k in names]
        return scl_template.make_template(definitions,
                                          version=template_version)

    def _assemble_for_rolling_update(self,
                                     total_capacity,
                                     max_updates,
                                     include_all=False,
                                     template_version=('heat_template_version',
                                                       '2015-04-30')):
        names = list(self._resource_names(total_capacity))
        name_blacklist = self._name_blacklist()

        valid_resources = [(n, d)
                           for n, d in grouputils.get_member_definitions(self)
                           if n not in name_blacklist]

        targ_cap = self.get_size()

        def replace_priority(res_item):
            name, defn = res_item
            try:
                index = names.index(name)
            except ValueError:
                # High priority - delete immediately
                return 0
            else:
                if index < targ_cap:
                    # Update higher indices first
                    return targ_cap - index
                else:
                    # Low priority - don't update
                    return total_capacity

        old_resources = sorted(valid_resources, key=replace_priority)
        existing_names = set(n for n, d in valid_resources)
        new_names = six.moves.filterfalse(lambda n: n in existing_names, names)
        res_def = self.get_resource_def(include_all)
        definitions = scl_template.member_definitions(
            old_resources, res_def, total_capacity, max_updates,
            lambda: next(new_names), self.build_resource_definition)
        return scl_template.make_template(definitions,
                                          version=template_version)

    def _try_rolling_update(self):
        if self.update_policy[self.ROLLING_UPDATE]:
            policy = self.update_policy[self.ROLLING_UPDATE]
            return self._replace(policy[self.MIN_IN_SERVICE],
                                 policy[self.MAX_BATCH_SIZE],
                                 policy[self.PAUSE_TIME])

    def _update_timeout(self, batch_cnt, pause_sec):
        total_pause_time = pause_sec * max(batch_cnt - 1, 0)
        if total_pause_time >= self.stack.timeout_secs():
            msg = _('The current %s will result in stack update '
                    'timeout.') % rsrc_defn.UPDATE_POLICY
            raise ValueError(msg)
        return self.stack.timeout_secs() - total_pause_time

    @staticmethod
    def _get_batches(targ_cap, curr_cap, batch_size, min_in_service):
        updated = 0

        while rolling_update.needs_update(targ_cap, curr_cap, updated):
            new_cap, total_new = rolling_update.next_batch(
                targ_cap, curr_cap, updated, batch_size, min_in_service)

            yield new_cap, total_new

            updated += total_new - max(new_cap - max(curr_cap, targ_cap), 0)
            curr_cap = new_cap

    def _replace(self, min_in_service, batch_size, pause_sec):
        def pause_between_batch(pause_sec):
            duration = timeutils.Duration(pause_sec)
            while not duration.expired():
                yield

        # blacklist count existing
        num_blacklist = self._count_black_listed()

        # current capacity not including existing blacklisted
        curr_cap = len(self.nested()) - num_blacklist if self.nested() else 0

        batches = list(
            self._get_batches(self.get_size(), curr_cap, batch_size,
                              min_in_service))
        update_timeout = self._update_timeout(len(batches), pause_sec)

        def tasks():
            for index, (curr_cap, max_upd) in enumerate(batches):
                yield scheduler.TaskRunner(self._run_update, curr_cap, max_upd,
                                           update_timeout)

                if index < (len(batches) - 1) and pause_sec > 0:
                    yield scheduler.TaskRunner(pause_between_batch, pause_sec)

        return list(tasks())

    def child_template(self):
        names = self._resource_names()
        return self._assemble_nested(names)

    def child_params(self):
        return {}

    def handle_adopt(self, resource_data):
        names = self._resource_names()
        if names:
            return self.create_with_template(self._assemble_nested(names), {},
                                             adopt_data=resource_data)
コード例 #5
0
class RandomString(resource.Resource):
    """A resource which generates a random string.

    This is useful for configuring passwords and secrets on services. Random
    string can be generated from specified character sequences, which means
    that all characters will be randomly chosen from specified sequences, or
    with some classes, e.g. letterdigits, which means that all character will
    be randomly chosen from union of ascii letters and digits. Output string
    will be randomly generated string with specified length (or with length of
    32, if length property doesn't specified).
    """

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        LENGTH,
        SEQUENCE,
        CHARACTER_CLASSES,
        CHARACTER_SEQUENCES,
        SALT,
    ) = (
        'length',
        'sequence',
        'character_classes',
        'character_sequences',
        'salt',
    )

    _CHARACTER_CLASSES_KEYS = (
        CHARACTER_CLASSES_CLASS,
        CHARACTER_CLASSES_MIN,
    ) = (
        'class',
        'min',
    )

    _CHARACTER_SEQUENCES = (
        CHARACTER_SEQUENCES_SEQUENCE,
        CHARACTER_SEQUENCES_MIN,
    ) = (
        'sequence',
        'min',
    )

    ATTRIBUTES = (VALUE, ) = ('value', )

    properties_schema = {
        LENGTH:
        properties.Schema(properties.Schema.INTEGER,
                          _('Length of the string to generate.'),
                          default=32,
                          constraints=[
                              constraints.Range(1, 512),
                          ]),
        SEQUENCE:
        properties.Schema(
            properties.Schema.STRING,
            _('Sequence of characters to build the random string from.'),
            constraints=[
                constraints.AllowedValues([
                    'lettersdigits', 'letters', 'lowercase', 'uppercase',
                    'digits', 'hexdigits', 'octdigits'
                ]),
            ],
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='5.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    message=_('Use property %s.') % CHARACTER_CLASSES,
                    version='2014.2'))),
        CHARACTER_CLASSES:
        properties.Schema(
            properties.Schema.LIST,
            _('A list of character class and their constraints to generate '
              'the random string from.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    CHARACTER_CLASSES_CLASS:
                    properties.Schema(
                        properties.Schema.STRING,
                        (_('A character class and its corresponding %(min)s '
                           'constraint to generate the random string from.') %
                         {
                             'min': CHARACTER_CLASSES_MIN
                         }),
                        constraints=[
                            constraints.AllowedValues([
                                'lettersdigits', 'letters', 'lowercase',
                                'uppercase', 'digits', 'hexdigits', 'octdigits'
                            ]),
                        ],
                        default='lettersdigits'),
                    CHARACTER_CLASSES_MIN:
                    properties.Schema(
                        properties.Schema.INTEGER,
                        _('The minimum number of characters from this '
                          'character class that will be in the generated '
                          'string.'),
                        default=1,
                        constraints=[
                            constraints.Range(1, 512),
                        ])
                }),
            # add defaults for backward compatibility
            default=[{
                CHARACTER_CLASSES_CLASS: 'lettersdigits',
                CHARACTER_CLASSES_MIN: 1
            }]),
        CHARACTER_SEQUENCES:
        properties.Schema(
            properties.Schema.LIST,
            _('A list of character sequences and their constraints to '
              'generate the random string from.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    CHARACTER_SEQUENCES_SEQUENCE:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('A character sequence and its corresponding %(min)s '
                          'constraint to generate the random string '
                          'from.') % {'min': CHARACTER_SEQUENCES_MIN},
                        required=True),
                    CHARACTER_SEQUENCES_MIN:
                    properties.Schema(
                        properties.Schema.INTEGER,
                        _('The minimum number of characters from this '
                          'sequence that will be in the generated '
                          'string.'),
                        default=1,
                        constraints=[
                            constraints.Range(1, 512),
                        ])
                })),
        SALT:
        properties.Schema(
            properties.Schema.STRING,
            _('Value which can be set or changed on stack update to trigger '
              'the resource for replacement with a new random string. The '
              'salt value itself is ignored by the random generator.')),
    }

    attributes_schema = {
        VALUE:
        attributes.Schema(_(
            'The random string generated by this resource. This value is '
            'also available by referencing the resource.'),
                          cache_mode=attributes.Schema.CACHE_NONE,
                          type=attributes.Schema.STRING),
    }

    _sequences = {
        'lettersdigits': string.ascii_letters + string.digits,
        'letters': string.ascii_letters,
        'lowercase': string.ascii_lowercase,
        'uppercase': string.ascii_uppercase,
        'digits': string.digits,
        'hexdigits': string.digits + 'ABCDEF',
        'octdigits': string.octdigits
    }

    def translation_rules(self, props):
        if props.get(self.SEQUENCE):
            return [
                translation.TranslationRule(
                    props, translation.TranslationRule.ADD,
                    [self.CHARACTER_CLASSES],
                    [{
                        self.CHARACTER_CLASSES_CLASS: props.get(self.SEQUENCE),
                        self.CHARACTER_CLASSES_MIN: 1
                    }]),
                translation.TranslationRule(props,
                                            translation.TranslationRule.DELETE,
                                            [self.SEQUENCE])
            ]

    def _generate_random_string(self, char_sequences, char_classes, length):
        random_string = ""

        # Add the minimum number of chars from each char sequence & char class
        if char_sequences:
            for char_seq in char_sequences:
                seq = char_seq[self.CHARACTER_SEQUENCES_SEQUENCE]
                seq_min = char_seq[self.CHARACTER_SEQUENCES_MIN]
                for i in six.moves.xrange(seq_min):
                    random_string += random.choice(seq)

        if char_classes:
            for char_class in char_classes:
                cclass_class = char_class[self.CHARACTER_CLASSES_CLASS]
                cclass_seq = self._sequences[cclass_class]
                cclass_min = char_class[self.CHARACTER_CLASSES_MIN]
                for i in six.moves.xrange(cclass_min):
                    random_string += random.choice(cclass_seq)

        def random_class_char():
            cclass_dict = random.choice(char_classes)
            cclass_class = cclass_dict[self.CHARACTER_CLASSES_CLASS]
            cclass_seq = self._sequences[cclass_class]
            return random.choice(cclass_seq)

        def random_seq_char():
            seq_dict = random.choice(char_sequences)
            seq = seq_dict[self.CHARACTER_SEQUENCES_SEQUENCE]
            return random.choice(seq)

        # Fill up rest with random chars from provided sequences & classes
        if char_sequences and char_classes:
            weighted_choices = ([True] * len(char_classes) +
                                [False] * len(char_sequences))
            while len(random_string) < length:
                if random.choice(weighted_choices):
                    random_string += random_class_char()
                else:
                    random_string += random_seq_char()

        elif char_sequences:
            while len(random_string) < length:
                random_string += random_seq_char()

        else:
            while len(random_string) < length:
                random_string += random_class_char()

        # Randomize string
        random_string = ''.join(
            random.sample(random_string, len(random_string)))
        return random_string

    def validate(self):
        super(RandomString, self).validate()
        char_sequences = self.properties[self.CHARACTER_SEQUENCES]
        char_classes = self.properties[self.CHARACTER_CLASSES]

        def char_min(char_dicts, min_prop):
            if char_dicts:
                return sum(char_dict[min_prop] for char_dict in char_dicts)
            return 0

        length = self.properties[self.LENGTH]
        min_length = (char_min(char_sequences, self.CHARACTER_SEQUENCES_MIN) +
                      char_min(char_classes, self.CHARACTER_CLASSES_MIN))
        if min_length > length:
            msg = _("Length property cannot be smaller than combined "
                    "character class and character sequence minimums")
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        char_sequences = self.properties[self.CHARACTER_SEQUENCES]
        char_classes = self.properties[self.CHARACTER_CLASSES]
        length = self.properties[self.LENGTH]

        random_string = self._generate_random_string(char_sequences,
                                                     char_classes, length)
        self.data_set('value', random_string, redact=True)
        self.resource_id_set(self.physical_resource_name())

    def _resolve_attribute(self, name):
        if name == self.VALUE:
            return self.data().get(self.VALUE)

    def get_reference_id(self):
        if self.resource_id is not None:
            return self.data().get('value')
        else:
            return six.text_type(self.name)
コード例 #6
0
class HeatWaitCondition(resource.Resource):
    """Resource for handling signals received by WaitConditionHandle.

    Resource takes WaitConditionHandle and starts to create. Resource is in
    CREATE_IN_PROGRESS status until WaitConditionHandle doesn't receive
    sufficient number of successful signals (this number can be specified with
    count property) and successfully creates after that, or fails due to
    timeout.
    """

    support_status = support.SupportStatus(version='2014.2')

    PROPERTIES = (
        HANDLE,
        TIMEOUT,
        COUNT,
    ) = (
        'handle',
        'timeout',
        'count',
    )

    ATTRIBUTES = (DATA, ) = ('data', )

    properties_schema = {
        HANDLE:
        properties.Schema(
            properties.Schema.STRING,
            _('A reference to the wait condition handle used to signal this '
              'wait condition.'),
            required=True),
        TIMEOUT:
        properties.Schema(
            properties.Schema.NUMBER,
            _('The number of seconds to wait for the correct number of '
              'signals to arrive.'),
            required=True,
            constraints=[
                constraints.Range(1, 43200),
            ]),
        COUNT:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The number of success signals that must be received before '
              'the stack creation process continues.'),
            constraints=[
                constraints.Range(min=1),
            ],
            default=1,
            update_allowed=True),
    }

    attributes_schema = {
        DATA:
        attributes.Schema(_('JSON string containing data associated with wait '
                            'condition signals sent to the handle.'),
                          cache_mode=attributes.Schema.CACHE_NONE,
                          type=attributes.Schema.STRING),
    }

    def __init__(self, name, definition, stack):
        super(HeatWaitCondition, self).__init__(name, definition, stack)

    def _get_handle_resource(self):
        return self.stack.resource_by_refid(self.properties[self.HANDLE])

    def _validate_handle_resource(self, handle):
        if not isinstance(handle, wc_base.BaseWaitConditionHandle):
            raise ValueError(
                _('%(name)s is not a valid wait condition '
                  'handle.') % {'name': handle.name})

    def _wait(self, handle, started_at, timeout_in):
        if timeutils.is_older_than(started_at, timeout_in):
            exc = wc_base.WaitConditionTimeout(self, handle)
            LOG.info(_LI('%(name)s Timed out (%(timeout)s)'), {
                'name': str(self),
                'timeout': str(exc)
            })
            raise exc

        handle_status = handle.get_status()

        if any(s != handle.STATUS_SUCCESS for s in handle_status):
            failure = wc_base.WaitConditionFailure(self, handle)
            LOG.info(_LI('%(name)s Failed (%(failure)s)'), {
                'name': str(self),
                'failure': str(failure)
            })
            raise failure

        if len(handle_status) >= self.properties[self.COUNT]:
            LOG.info(_LI("%s Succeeded"), str(self))
            return True
        return False

    def handle_create(self):
        handle = self._get_handle_resource()
        self._validate_handle_resource(handle)
        started_at = timeutils.utcnow()
        return handle, started_at, float(self.properties[self.TIMEOUT])

    def check_create_complete(self, data):
        return self._wait(*data)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.properties = json_snippet.properties(self.properties_schema,
                                                      self.context)

        handle = self._get_handle_resource()
        started_at = timeutils.utcnow()
        return handle, started_at, float(self.properties[self.TIMEOUT])

    def check_update_complete(self, data):
        return self._wait(*data)

    def handle_delete(self):
        handle = self._get_handle_resource()
        if handle and handle.id and handle.action != handle.INIT:
            handle.metadata_set({})

    def _resolve_attribute(self, key):
        handle = self._get_handle_resource()
        if key == self.DATA:
            meta = handle.metadata_get(refresh=True)
            res = {k: meta[k][handle.DATA] for k in meta}
            LOG.debug('%(name)s.GetAtt(%(key)s) == %(res)s' % {
                'name': self.name,
                'key': key,
                'res': res
            })

            return six.text_type(jsonutils.dumps(res))
コード例 #7
0
ファイル: swiftsignal.py プロジェクト: Hybrid-Cloud/conveyor
class SwiftSignal(resource.Resource):
    """Resource for handling signals received by SwiftSignalHandle.

    This resource handles signals received by SwiftSignalHandle and
    is same as WaitCondition resource.
    """

    support_status = support.SupportStatus(version='2014.2')
    default_client_name = "swift"

    PROPERTIES = (
        HANDLE,
        TIMEOUT,
        COUNT,
    ) = (
        'handle',
        'timeout',
        'count',
    )

    properties_schema = {
        HANDLE:
        properties.Schema(properties.Schema.STRING,
                          required=True,
                          description=_(
                              'URL of TempURL where resource will signal '
                              'completion and optionally upload data.')),
        TIMEOUT:
        properties.Schema(
            properties.Schema.NUMBER,
            description=_('The maximum number of seconds to wait for the '
                          'resource to signal completion. Once the timeout '
                          'is reached, creation of the signal resource will '
                          'fail.'),
            required=True,
            constraints=[
                constraints.Range(1, 43200),
            ]),
        COUNT:
        properties.Schema(properties.Schema.INTEGER,
                          description=_(
                              'The number of success signals that must be '
                              'received before the stack creation process '
                              'continues.'),
                          default=1,
                          constraints=[
                              constraints.Range(1, 1000),
                          ])
    }

    ATTRIBUTES = (DATA) = 'data'

    attributes_schema = {
        DATA:
        attributes.Schema(
            _('JSON data that was uploaded via the SwiftSignalHandle.'),
            type=attributes.Schema.STRING)
    }

    WAIT_STATUSES = (
        STATUS_FAILURE,
        STATUS_SUCCESS,
    ) = (
        'FAILURE',
        'SUCCESS',
    )

    METADATA_KEYS = (DATA, REASON, STATUS, UNIQUE_ID) = ('data', 'reason',
                                                         'status', 'id')

    def __init__(self, name, json_snippet, stack):
        super(SwiftSignal, self).__init__(name, json_snippet, stack)
        self._obj_name = None
        self._url = None

    @property
    def url(self):
        if not self._url:
            self._url = parse.urlparse(self.properties[self.HANDLE])
        return self._url

    @property
    def obj_name(self):
        if not self._obj_name:
            self._obj_name = self.url.path.split('/')[4]
        return self._obj_name

    def _validate_handle_url(self):
        parts = self.url.path.split('/')
        msg = _('"%(url)s" is not a valid SwiftSignalHandle.  The %(part)s '
                'is invalid')
        cplugin = self.client_plugin()
        if not cplugin.is_valid_temp_url_path(self.url.path):
            raise ValueError(msg % {
                'url': self.url.path,
                'part': 'Swift TempURL path'
            })
        if not parts[3] == self.stack.id:
            raise ValueError(msg % {
                'url': self.url.path,
                'part': 'container name'
            })

    def handle_create(self):
        self._validate_handle_url()
        started_at = timeutils.utcnow()
        return started_at, float(self.properties[self.TIMEOUT])

    def get_signals(self):
        try:
            container = self.client().get_container(self.stack.id)
        except Exception as exc:
            self.client_plugin().ignore_not_found(exc)
            LOG.debug("Swift container %s was not found" % self.stack.id)
            return []

        index = container[1]
        if not index:
            LOG.debug("Swift objects in container %s were not found" %
                      self.stack.id)
            return []

        # Remove objects in that are for other handle resources, since
        # multiple SwiftSignalHandle resources in the same stack share
        # a container
        filtered = [obj for obj in index if self.obj_name in obj['name']]

        # Fetch objects from Swift and filter results
        obj_bodies = []
        for obj in filtered:
            try:
                signal = self.client().get_object(self.stack.id, obj['name'])
            except Exception as exc:
                self.client_plugin().ignore_not_found(exc)
                continue

            body = signal[1]
            if body == swift.IN_PROGRESS:  # Ignore the initial object
                continue
            if body == "":
                obj_bodies.append({})
                continue
            try:
                obj_bodies.append(jsonutils.loads(body))
            except ValueError:
                raise exception.Error(
                    _("Failed to parse JSON data: %s") % body)

        # Set default values on each signal
        signals = []
        signal_num = 1
        for signal in obj_bodies:

            # Remove previous signals with the same ID
            sig_id = self.UNIQUE_ID
            ids = [s.get(sig_id) for s in signals if sig_id in s]
            if ids and sig_id in signal and ids.count(signal[sig_id]) > 0:
                [
                    signals.remove(s) for s in signals
                    if s.get(sig_id) == signal[sig_id]
                ]

            # Make sure all fields are set, since all are optional
            signal.setdefault(self.DATA, None)
            unique_id = signal.setdefault(sig_id, signal_num)
            reason = 'Signal %s received' % unique_id
            signal.setdefault(self.REASON, reason)
            signal.setdefault(self.STATUS, self.STATUS_SUCCESS)

            signals.append(signal)
            signal_num += 1

        return signals

    def get_status(self):
        return [s[self.STATUS] for s in self.get_signals()]

    def get_status_reason(self, status):
        return [
            s[self.REASON] for s in self.get_signals()
            if s[self.STATUS] == status
        ]

    def get_data(self):
        signals = self.get_signals()
        if not signals:
            return None
        data = {}
        for signal in signals:
            data[signal[self.UNIQUE_ID]] = signal[self.DATA]
        return data

    def check_create_complete(self, create_data):
        if timeutils.is_older_than(*create_data):
            raise SwiftSignalTimeout(self)

        statuses = self.get_status()
        if not statuses:
            return False

        for status in statuses:
            if status == self.STATUS_FAILURE:
                failure = SwiftSignalFailure(self)
                LOG.info(_LI('%(name)s Failed (%(failure)s)'), {
                    'name': str(self),
                    'failure': str(failure)
                })
                raise failure
            elif status != self.STATUS_SUCCESS:
                raise exception.Error(_("Unknown status: %s") % status)

        if len(statuses) >= self.properties[self.COUNT]:
            LOG.info(_LI("%s Succeeded"), str(self))
            return True
        return False

    def _resolve_attribute(self, key):
        if key == self.DATA:
            return six.text_type(jsonutils.dumps(self.get_data()))
コード例 #8
0
class QoSBandwidthLimitRule(QoSRule):
    """A resource for Neutron QoS bandwidth limit rule.

    This rule can be associated with QoS policy, and then the policy
    can be used by neutron port and network, to provide bandwidth limit
    QoS capabilities.

    The default policy usage of this resource is limited to
    administrators only.
    """

    PROPERTIES = (
        MAX_BANDWIDTH, MAX_BURST_BANDWIDTH,
    ) = (
        'max_kbps', 'max_burst_kbps',
    )

    properties_schema = {
        MAX_BANDWIDTH: properties.Schema(
            properties.Schema.INTEGER,
            _('Max bandwidth in kbps.'),
            required=True,
            update_allowed=True,
            constraints=[
                constraints.Range(min=0)
            ]
        ),
        MAX_BURST_BANDWIDTH: properties.Schema(
            properties.Schema.INTEGER,
            _('Max burst bandwidth in kbps.'),
            update_allowed=True,
            constraints=[
                constraints.Range(min=0)
            ],
            default=0
        )
    }

    properties_schema.update(QoSRule.properties_schema)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        props.pop(self.POLICY)

        rule = self.client().create_bandwidth_limit_rule(
            self.policy_id,
            {'bandwidth_limit_rule': props})['bandwidth_limit_rule']

        self.resource_id_set(rule['id'])

    def handle_delete(self):
        if self.resource_id is None:
            return

        with self.client_plugin().ignore_not_found:
            self.client().delete_bandwidth_limit_rule(
                self.resource_id, self.policy_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.client().update_bandwidth_limit_rule(
                self.resource_id,
                self.policy_id,
                {'bandwidth_limit_rule': prop_diff})

    def _show_resource(self):
        return self.client().show_bandwidth_limit_rule(
            self.resource_id, self.policy_id)['bandwidth_limit_rule']
コード例 #9
0
class SubnetPool(neutron.NeutronResource):
    """A resource that implements neutron subnet pool.

    This resource can be used to create a subnet pool with a large block
    of addresses and create subnets from it.
    """

    support_status = support.SupportStatus(version='6.0.0')

    required_service_extension = 'subnet_allocation'

    PROPERTIES = (
        NAME,
        PREFIXES,
        ADDRESS_SCOPE,
        DEFAULT_QUOTA,
        DEFAULT_PREFIXLEN,
        MIN_PREFIXLEN,
        MAX_PREFIXLEN,
        IS_DEFAULT,
        TENANT_ID,
        SHARED,
    ) = (
        'name',
        'prefixes',
        'address_scope',
        'default_quota',
        'default_prefixlen',
        'min_prefixlen',
        'max_prefixlen',
        'is_default',
        'tenant_id',
        'shared',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('Name of the subnet pool.'),
                          update_allowed=True),
        PREFIXES:
        properties.Schema(
            properties.Schema.LIST,
            _('List of subnet prefixes to assign.'),
            schema=properties.Schema(
                properties.Schema.STRING,
                constraints=[
                    constraints.CustomConstraint('net_cidr'),
                ],
            ),
            constraints=[constraints.Length(min=1)],
            required=True,
            update_allowed=True,
        ),
        ADDRESS_SCOPE:
        properties.Schema(
            properties.Schema.STRING,
            _('An address scope ID to assign to the subnet pool.'),
            constraints=[
                constraints.CustomConstraint('neutron.address_scope')
            ],
            update_allowed=True,
        ),
        DEFAULT_QUOTA:
        properties.Schema(
            properties.Schema.INTEGER,
            _('A per-tenant quota on the prefix space that can be allocated '
              'from the subnet pool for tenant subnets.'),
            constraints=[constraints.Range(min=0)],
            update_allowed=True,
        ),
        DEFAULT_PREFIXLEN:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The size of the prefix to allocate when the cidr or '
              'prefixlen attributes are not specified while creating '
              'a subnet.'),
            constraints=[constraints.Range(min=0)],
            update_allowed=True,
        ),
        MIN_PREFIXLEN:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Smallest prefix size that can be allocated '
              'from the subnet pool.'),
            constraints=[constraints.Range(min=0)],
            update_allowed=True,
        ),
        MAX_PREFIXLEN:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Maximum prefix size that can be allocated '
              'from the subnet pool.'),
            constraints=[constraints.Range(min=0)],
            update_allowed=True,
        ),
        IS_DEFAULT:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Whether this is default IPv4/IPv6 subnet pool. '
              'There can only be one default subnet pool for each IP family. '
              'Note that the default policy setting restricts administrative '
              'users to set this to True.'),
            default=False,
            update_allowed=True,
        ),
        TENANT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the tenant who owns the subnet pool. Only '
              'administrative users can specify a tenant ID '
              'other than their own.')),
        SHARED:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Whether the subnet pool will be shared across all tenants. '
              'Note that the default policy setting restricts usage of this '
              'attribute to administrative users only.'),
            default=False,
        ),
    }

    def validate(self):
        super(SubnetPool, self).validate()
        self._validate_prefix_bounds()

    def _validate_prefix_bounds(self):
        min_prefixlen = self.properties[self.MIN_PREFIXLEN]
        default_prefixlen = self.properties[self.DEFAULT_PREFIXLEN]
        max_prefixlen = self.properties[self.MAX_PREFIXLEN]
        msg_fmt = _('Illegal prefix bounds: %(key1)s=%(value1)s, '
                    '%(key2)s=%(value2)s.')
        # min_prefixlen can not be greater than max_prefixlen
        if min_prefixlen and max_prefixlen and min_prefixlen > max_prefixlen:
            msg = msg_fmt % dict(key1=self.MAX_PREFIXLEN,
                                 value1=max_prefixlen,
                                 key2=self.MIN_PREFIXLEN,
                                 value2=min_prefixlen)
            raise exception.StackValidationFailed(message=msg)

        if default_prefixlen:
            # default_prefixlen can not be greater than max_prefixlen
            if max_prefixlen and default_prefixlen > max_prefixlen:
                msg = msg_fmt % dict(key1=self.MAX_PREFIXLEN,
                                     value1=max_prefixlen,
                                     key2=self.DEFAULT_PREFIXLEN,
                                     value2=default_prefixlen)
                raise exception.StackValidationFailed(message=msg)
            # min_prefixlen can not be greater than default_prefixlen
            if min_prefixlen and min_prefixlen > default_prefixlen:
                msg = msg_fmt % dict(key1=self.MIN_PREFIXLEN,
                                     value1=min_prefixlen,
                                     key2=self.DEFAULT_PREFIXLEN,
                                     value2=default_prefixlen)
                raise exception.StackValidationFailed(message=msg)

    def _validate_prefixes_for_update(self, prop_diff):
        old_prefixes = self.properties[self.PREFIXES]
        new_prefixes = prop_diff[self.PREFIXES]
        # check new_prefixes is a superset of old_prefixes
        if not netutils.is_prefix_subset(old_prefixes, new_prefixes):
            msg = (_('Property %(key)s updated value %(new)s should '
                     'be superset of existing value '
                     '%(old)s.') % dict(key=self.PREFIXES,
                                        new=sorted(new_prefixes),
                                        old=sorted(old_prefixes)))
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        if self.ADDRESS_SCOPE in props and props[self.ADDRESS_SCOPE]:
            props['address_scope_id'] = self.client_plugin(
            ).find_resourceid_by_name_or_id('address_scope',
                                            props.pop(self.ADDRESS_SCOPE))
        subnetpool = self.client().create_subnetpool({'subnetpool':
                                                      props})['subnetpool']
        self.resource_id_set(subnetpool['id'])

    def handle_delete(self):
        if self.resource_id is not None:
            with self.client_plugin().ignore_not_found:
                self.client().delete_subnetpool(self.resource_id)

    def _show_resource(self):
        return self.client().show_subnetpool(self.resource_id)['subnetpool']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        # check that new prefixes are superset of existing prefixes
        if self.PREFIXES in prop_diff:
            self._validate_prefixes_for_update(prop_diff)
        if self.ADDRESS_SCOPE in prop_diff:
            if prop_diff[self.ADDRESS_SCOPE]:
                prop_diff['address_scope_id'] = self.client_plugin(
                ).find_resourceid_by_name_or_id(
                    self.client(), 'address_scope',
                    prop_diff.pop(self.ADDRESS_SCOPE))
            else:
                prop_diff['address_scope_id'] = prop_diff.pop(
                    self.ADDRESS_SCOPE)
        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_subnetpool(self.resource_id,
                                            {'subnetpool': prop_diff})
コード例 #10
0
ファイル: loadbalancer.py プロジェクト: Hybrid-Cloud/conveyor
class LoadBalancer(elb_res_base.ElbBaseResource):
    """A resource for ELB Loadbalancer.

    Load Balancer resource for Elastic Load Balance Service.
    """

    PROPERTIES = (
        NAME,
        DESCRIPTION,
        VPC_ID,
        BANDWIDTH,
        TYPE,
        ADMIN_STATE_UP,
        VIP_SUBNET,
        AVAILABILITY_ZONE,
        SECURITY_GROUP,
    ) = (
        'name',
        'description',
        'vpc_id',
        'bandwidth',
        'type',
        'admin_state_up',
        'vip_subnet_id',
        'az',
        'security_group',
    )

    _LB_TYPES = (
        EXTERNAL,
        INTERNAL,
    ) = (
        'External',
        'Internal',
    )

    ATTRIBUTES = (
        VIP_ADDRESS_ATTR,
        VIP_SUBNET_ATTR,
        STATUS_ATTR,
    ) = (
        'vip_address',
        VIP_SUBNET,
        'status',
    )
    properties_schema = {
        NAME:
        properties.Schema(
            properties.Schema.STRING,
            _('The name of the load balancer.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.AllowedPattern('^[0-9a-zA-Z-_]{1,64}$')]),
        DESCRIPTION:
        properties.Schema(
            properties.Schema.STRING,
            _('The description of the load balancer.'),
            update_allowed=True,
            constraints=[constraints.AllowedPattern('^[^<>]{1,128}$')]),
        VPC_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of vpc.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.router')]),
        BANDWIDTH:
        properties.Schema(properties.Schema.INTEGER,
                          _('The bandwidth of the load balancer, in Mbit/s.'),
                          constraints=[constraints.Range(min=1, max=300)],
                          update_allowed=True),
        TYPE:
        properties.Schema(properties.Schema.STRING,
                          _('The type of the load balancer.'),
                          constraints=[constraints.AllowedValues(_LB_TYPES)],
                          required=True),
        ADMIN_STATE_UP:
        properties.Schema(properties.Schema.BOOLEAN,
                          _('The admin state of the load balancer.'),
                          update_allowed=True,
                          default=True),
        VIP_SUBNET:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the network on which to allocate the VIP.'),
            constraints=[constraints.CustomConstraint('neutron.network')]),
        AVAILABILITY_ZONE:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the availability zone.'),
        ),
        SECURITY_GROUP:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the security group.'),
        ),
    }

    attributes_schema = {
        VIP_ADDRESS_ATTR:
        attributes.Schema(_('The vip address of the load balancer.'), ),
        VIP_SUBNET_ATTR:
        attributes.Schema(_('The vip subnet of the load balancer.'), ),
        STATUS_ATTR:
        attributes.Schema(_('The status of the load balancer.'), ),
    }

    def validate(self):
        super(LoadBalancer, self).validate()
        lb_type = self.properties[self.TYPE]
        bandwidth = self.properties[self.BANDWIDTH]
        vip_subnet = self.properties[self.VIP_SUBNET]
        az = self.properties[self.AVAILABILITY_ZONE]
        sec_group = self.properties[self.SECURITY_GROUP]

        if lb_type == self.EXTERNAL:
            if not bandwidth:
                msg = (_('The %(bdw)s must be provided when lb is %(type)s.') %
                       {
                           'bdw': self.BANDWIDTH,
                           'type': lb_type
                       })
                raise exception.StackValidationFailed(message=msg)
        elif lb_type == self.INTERNAL:
            if vip_subnet is None or az is None or sec_group is None:
                msg = (_('The %(sub)s, %(az)s and %(sg)s must be provided '
                         'when lb is %(type)s.') % {
                             'sub': self.VIP_SUBNET,
                             'az': self.AVAILABILITY_ZONE,
                             'sg': self.SECURITY_GROUP,
                             'type': lb_type
                         })
                raise exception.StackValidationFailed(message=msg)

    def _resolve_attribute(self, name):
        if not self.resource_id:
            return None

        elb = self.client().loadbalancer.get(self.resource_id)
        if name == self.VIP_ADDRESS_ATTR:
            return elb.vip_address
        if name == self.VIP_SUBNET_ATTR:
            return elb.vip_subnet_id
        if name == self.STATUS_ATTR:
            return elb.status

    def handle_create(self):
        props = self._prepare_properties(self.properties)
        job_id = self.client().loadbalancer.create(**props)['job_id']
        job_info = {'job_id': job_id, 'action': self.action}
        self._set_job(job_info)
        return job_id

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            return self.client().loadbalancer.update(
                loadbalancer_id=self.resource_id, **prop_diff)['job_id']

    def handle_delete(self):
        if not self.resource_id:
            job_info = self._get_job()
            job_id = job_info.get('job_id')
            if not job_id:
                return

            try:
                job_status, entities, error_code = self._get_job_info(job_id)
            except Exception as e:
                if self.client_plugin().is_not_found(e):
                    LOG.info('job %s not found', job_id)
                    return
                raise e

            elb_info = entities.get('elb', {})
            elb_id = elb_info.get('id')
            if not elb_id:
                return
            self.resource_id_set(elb_id)

        try:
            lb = self.client().loadbalancer.get(self.resource_id)
            return self.client().loadbalancer.delete(lb.id)['job_id']
        except Exception as e:
            self.client_plugin().ignore_not_found(e)

    def check_create_complete(self, job_id):
        if self.resource_id is None:
            job_status, entities, error_code = self._get_job_info(job_id)
            elb_status = 'unknown'
            if entities:
                elb_info = entities.get('elb', {})
                elb_id = elb_info.get('id')
                elb_status = elb_info.get('status')
                if elb_id:
                    self.resource_id_set(elb_id)
                    self._set_job({})
            if job_status == utils.FAIL:
                self._set_job({})
                raise exception.ResourceUnknownStatus(
                    result=(_('Job %(job)s failed: %(error_code)s, '
                              '%(entities)s') % {
                                  'job': job_id,
                                  'error_code': error_code,
                                  'entities': entities
                              }),
                    resource_status=elb_status)
            return self._check_active(elb_status)
        else:
            elb = self.client().loadbalancer.get(self.resource_id)
            return self._check_active(elb.status)

    def check_update_complete(self, job_id):
        if not job_id:
            return True
        return self._check_job_success(job_id)

    def check_delete_complete(self, job_id):
        if not job_id:
            return True
        return self._check_job_success(job_id, ignore_not_found=True)
コード例 #11
0
class NetworkGateway(neutron.NeutronResource):
    """Network Gateway resource in Neutron Network Gateway.

    Resource for connecting internal networks with specified devices.
    """

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        NAME,
        DEVICES,
        CONNECTIONS,
    ) = (
        'name',
        'devices',
        'connections',
    )

    ATTRIBUTES = (DEFAULT, ) = ('default', )

    _DEVICES_KEYS = (
        ID,
        INTERFACE_NAME,
    ) = (
        'id',
        'interface_name',
    )

    _CONNECTIONS_KEYS = (
        NETWORK_ID,
        NETWORK,
        SEGMENTATION_TYPE,
        SEGMENTATION_ID,
    ) = (
        'network_id',
        'network',
        'segmentation_type',
        'segmentation_id',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          description=_('The name of the network gateway.'),
                          update_allowed=True),
        DEVICES:
        properties.Schema(
            properties.Schema.LIST,
            description=_('Device info for this network gateway.'),
            required=True,
            constraints=[constraints.Length(min=1)],
            update_allowed=True,
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    ID:
                    properties.Schema(properties.Schema.STRING,
                                      description=_(
                                          'The device id for the network '
                                          'gateway.'),
                                      required=True),
                    INTERFACE_NAME:
                    properties.Schema(properties.Schema.STRING,
                                      description=_(
                                          'The interface name for the '
                                          'network gateway.'),
                                      required=True)
                })),
        CONNECTIONS:
        properties.Schema(
            properties.Schema.LIST,
            description=_('Connection info for this network gateway.'),
            default={},
            update_allowed=True,
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    NETWORK_ID:
                    properties.Schema(
                        properties.Schema.STRING,
                        support_status=support.SupportStatus(
                            status=support.HIDDEN,
                            message=_('Use property %s.') % NETWORK,
                            version='5.0.0',
                            previous_status=support.SupportStatus(
                                status=support.DEPRECATED, version='2014.2')),
                        constraints=[
                            constraints.CustomConstraint('neutron.network')
                        ],
                    ),
                    NETWORK:
                    properties.Schema(
                        properties.Schema.STRING,
                        description=_('The internal network to connect on '
                                      'the network gateway.'),
                        support_status=support.SupportStatus(version='2014.2'),
                        required=True,
                        constraints=[
                            constraints.CustomConstraint('neutron.network')
                        ],
                    ),
                    SEGMENTATION_TYPE:
                    properties.Schema(
                        properties.Schema.STRING,
                        description=_(
                            'L2 segmentation strategy on the external '
                            'side of the network gateway.'),
                        default='flat',
                        constraints=[
                            constraints.AllowedValues(('flat', 'vlan'))
                        ]),
                    SEGMENTATION_ID:
                    properties.Schema(
                        properties.Schema.INTEGER,
                        description=_(
                            'The id for L2 segment on the external side '
                            'of the network gateway. Must be specified '
                            'when using vlan.'),
                        constraints=[constraints.Range(0, 4094)])
                }))
    }

    attributes_schema = {
        DEFAULT:
        attributes.Schema(_("A boolean value of default flag."),
                          type=attributes.Schema.STRING),
    }

    def translation_rules(self, props):
        return [
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.CONNECTIONS, self.NETWORK],
                                        value_name=self.NETWORK_ID),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.CONNECTIONS, self.NETWORK],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='network')
        ]

    def _show_resource(self):
        return self.client().show_network_gateway(
            self.resource_id)['network_gateway']

    def validate(self):
        """Validate any of the provided params."""
        super(NetworkGateway, self).validate()
        connections = self.properties[self.CONNECTIONS]

        for connection in connections:
            segmentation_type = connection[self.SEGMENTATION_TYPE]
            segmentation_id = connection.get(self.SEGMENTATION_ID)

            if segmentation_type == 'vlan' and segmentation_id is None:
                msg = _("segmentation_id must be specified for using vlan")
                raise exception.StackValidationFailed(message=msg)

            if segmentation_type == 'flat' and segmentation_id:
                msg = _("segmentation_id cannot be specified except 0 for "
                        "using flat")
                raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())

        connections = props.pop(self.CONNECTIONS)
        ret = self.client().create_network_gateway({'network_gateway':
                                                    props})['network_gateway']

        self.resource_id_set(ret['id'])

        for connection in connections:
            if self.NETWORK in connection:
                connection['network_id'] = connection.pop(self.NETWORK)
            self.client().connect_network_gateway(ret['id'], connection)

    def handle_delete(self):
        if not self.resource_id:
            return

        connections = self.properties[self.CONNECTIONS]
        for connection in connections:
            with self.client_plugin().ignore_not_found:
                if self.NETWORK in connection:
                    connection['network_id'] = connection.pop(self.NETWORK)
                self.client().disconnect_network_gateway(
                    self.resource_id, connection)
        try:
            self.client().delete_network_gateway(self.resource_id)
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            return True

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        connections = None
        if self.CONNECTIONS in prop_diff:
            connections = prop_diff.pop(self.CONNECTIONS)

        if self.DEVICES in prop_diff:
            self.handle_delete()
            self.properties.data.update(prop_diff)
            self.handle_create()
            return

        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_network_gateway(
                self.resource_id, {'network_gateway': prop_diff})

        if connections:
            for connection in self.properties[self.CONNECTIONS]:
                with self.client_plugin().ignore_not_found:
                    if self.NETWORK in connection:
                        connection['network_id'] = connection.pop(self.NETWORK)
                    self.client().disconnect_network_gateway(
                        self.resource_id, connection)
            for connection in connections:
                if self.NETWORK in connection:
                    connection['network_id'] = connection.pop(self.NETWORK)
                self.client().connect_network_gateway(self.resource_id,
                                                      connection)
コード例 #12
0
class PoolMember(neutron.NeutronResource):
    """A resource to handle loadbalancer members.

    A pool member represents the application running on backend server.
    """

    required_service_extension = 'lbaas'

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        POOL_ID, ADDRESS, PROTOCOL_PORT, WEIGHT, ADMIN_STATE_UP,
    ) = (
        'pool_id', 'address', 'protocol_port', 'weight', 'admin_state_up',
    )

    ATTRIBUTES = (
        ADMIN_STATE_UP_ATTR, TENANT_ID, WEIGHT_ATTR, ADDRESS_ATTR,
        POOL_ID_ATTR, PROTOCOL_PORT_ATTR,
    ) = (
        'admin_state_up', 'tenant_id', 'weight', 'address',
        'pool_id', 'protocol_port',
    )

    properties_schema = {
        POOL_ID: properties.Schema(
            properties.Schema.STRING,
            _('The ID of the load balancing pool.'),
            required=True,
            update_allowed=True
        ),
        ADDRESS: properties.Schema(
            properties.Schema.STRING,
            _('IP address of the pool member on the pool network.'),
            required=True,
            constraints=[
                constraints.CustomConstraint('ip_addr')
            ]
        ),
        PROTOCOL_PORT: properties.Schema(
            properties.Schema.INTEGER,
            _('TCP port on which the pool member listens for requests or '
              'connections.'),
            required=True,
            constraints=[
                constraints.Range(0, 65535),
            ]
        ),
        WEIGHT: properties.Schema(
            properties.Schema.INTEGER,
            _('Weight of pool member in the pool (default to 1).'),
            constraints=[
                constraints.Range(0, 256),
            ],
            update_allowed=True
        ),
        ADMIN_STATE_UP: properties.Schema(
            properties.Schema.BOOLEAN,
            _('The administrative state of the pool member.'),
            default=True
        ),
    }

    attributes_schema = {
        ADMIN_STATE_UP_ATTR: attributes.Schema(
            _('The administrative state of this pool member.'),
            type=attributes.Schema.STRING
        ),
        TENANT_ID: attributes.Schema(
            _('Tenant owning the pool member.'),
            type=attributes.Schema.STRING
        ),
        WEIGHT_ATTR: attributes.Schema(
            _('Weight of the pool member in the pool.'),
            type=attributes.Schema.STRING
        ),
        ADDRESS_ATTR: attributes.Schema(
            _('IP address of the pool member.'),
            type=attributes.Schema.STRING
        ),
        POOL_ID_ATTR: attributes.Schema(
            _('The ID of the load balancing pool.'),
            type=attributes.Schema.STRING
        ),
        PROTOCOL_PORT_ATTR: attributes.Schema(
            _('TCP port on which the pool member listens for requests or '
              'connections.'),
            type=attributes.Schema.STRING
        ),
    }

    def handle_create(self):
        pool = self.properties[self.POOL_ID]
        protocol_port = self.properties[self.PROTOCOL_PORT]
        address = self.properties[self.ADDRESS]
        admin_state_up = self.properties[self.ADMIN_STATE_UP]
        weight = self.properties[self.WEIGHT]

        params = {
            'pool_id': pool,
            'address': address,
            'protocol_port': protocol_port,
            'admin_state_up': admin_state_up
        }

        if weight is not None:
            params['weight'] = weight

        member = self.client().create_member({'member': params})['member']
        self.resource_id_set(member['id'])

    def _show_resource(self):
        return self.client().show_member(self.resource_id)['member']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.client().update_member(
                self.resource_id, {'member': prop_diff})

    def handle_delete(self):
        if not self.resource_id:
            return

        try:
            self.client().delete_member(self.resource_id)
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            return True
コード例 #13
0
class AWSScalingPolicy(heat_sp.AutoScalingPolicy):
    PROPERTIES = (
        AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE,
        COOLDOWN, MIN_ADJUSTMENT_STEP,
    ) = (
        'AutoScalingGroupName', 'ScalingAdjustment', 'AdjustmentType',
        'Cooldown', 'MinAdjustmentStep',
    )

    ATTRIBUTES = (
        ALARM_URL,
    ) = (
        'AlarmUrl',
    )

    properties_schema = {
        AUTO_SCALING_GROUP_NAME: properties.Schema(
            properties.Schema.STRING,
            _('AutoScaling group name to apply policy to.'),
            required=True
        ),
        SCALING_ADJUSTMENT: properties.Schema(
            properties.Schema.INTEGER,
            _('Size of adjustment.'),
            required=True,
            update_allowed=True
        ),
        ADJUSTMENT_TYPE: properties.Schema(
            properties.Schema.STRING,
            _('Type of adjustment (absolute or percentage).'),
            required=True,
            constraints=[
                constraints.AllowedValues(
                    [sc_util.CFN_CHANGE_IN_CAPACITY,
                     sc_util.CFN_EXACT_CAPACITY,
                     sc_util.CFN_PERCENT_CHANGE_IN_CAPACITY]),
            ],
            update_allowed=True
        ),
        COOLDOWN: properties.Schema(
            properties.Schema.INTEGER,
            _('Cooldown period, in seconds.'),
            update_allowed=True
        ),
        MIN_ADJUSTMENT_STEP: properties.Schema(
            properties.Schema.INTEGER,
            _('Minimum number of resources that are added or removed '
              'when the AutoScaling group scales up or down. This can '
              'be used only when specifying PercentChangeInCapacity '
              'for the AdjustmentType property.'),
            constraints=[
                constraints.Range(
                    min=0,
                ),
            ],
            update_allowed=True
        ),

    }

    attributes_schema = {
        ALARM_URL: attributes.Schema(
            _("A signed url to handle the alarm. (Heat extension)."),
            type=attributes.Schema.STRING
        ),
    }

    def _validate_min_adjustment_step(self):
        adjustment_type = self.properties.get(self.ADJUSTMENT_TYPE)
        adjustment_step = self.properties.get(self.MIN_ADJUSTMENT_STEP)
        if (adjustment_type != sc_util.CFN_PERCENT_CHANGE_IN_CAPACITY
                and adjustment_step is not None):
            raise exception.ResourcePropertyValueDependency(
                prop1=self.MIN_ADJUSTMENT_STEP,
                prop2=self.ADJUSTMENT_TYPE,
                value=sc_util.CFN_PERCENT_CHANGE_IN_CAPACITY)

    def get_reference_id(self):
        if self.resource_id is not None:
            return six.text_type(self._get_ec2_signed_url())
        else:
            return six.text_type(self.name)
コード例 #14
0
ファイル: image.py プロジェクト: Hybrid-Cloud/conveyor
class GlanceImage(resource.Resource):
    """A resource managing images in Glance.

    A resource provides managing images that are meant to be used with other
    services.
    """

    support_status = support.SupportStatus(version='2014.2')

    PROPERTIES = (
        NAME, IMAGE_ID, IS_PUBLIC, MIN_DISK, MIN_RAM, PROTECTED,
        DISK_FORMAT, CONTAINER_FORMAT, LOCATION
    ) = (
        'name', 'id', 'is_public', 'min_disk', 'min_ram', 'protected',
        'disk_format', 'container_format', 'location'
    )

    properties_schema = {
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('Name for the image. The name of an image is not '
              'unique to a Image Service node.')
        ),
        IMAGE_ID: properties.Schema(
            properties.Schema.STRING,
            _('The image ID. Glance will generate a UUID if not specified.')
        ),
        IS_PUBLIC: properties.Schema(
            properties.Schema.BOOLEAN,
            _('Scope of image accessibility. Public or private. '
              'Default value is False means private.'),
            default=False,
        ),
        MIN_DISK: properties.Schema(
            properties.Schema.INTEGER,
            _('Amount of disk space (in GB) required to boot image. '
              'Default value is 0 if not specified '
              'and means no limit on the disk size.'),
            constraints=[
                constraints.Range(min=0),
            ],
            default=0
        ),
        MIN_RAM: properties.Schema(
            properties.Schema.INTEGER,
            _('Amount of ram (in MB) required to boot image. Default value '
              'is 0 if not specified and means no limit on the ram size.'),
            constraints=[
                constraints.Range(min=0),
            ],
            default=0
        ),
        PROTECTED: properties.Schema(
            properties.Schema.BOOLEAN,
            _('Whether the image can be deleted. If the value is True, '
              'the image is protected and cannot be deleted.'),
            default=False
        ),
        DISK_FORMAT: properties.Schema(
            properties.Schema.STRING,
            _('Disk format of image.'),
            required=True,
            constraints=[
                constraints.AllowedValues(['ami', 'ari', 'aki',
                                           'vhd', 'vmdk', 'raw',
                                           'qcow2', 'vdi', 'iso'])
            ]
        ),
        CONTAINER_FORMAT: properties.Schema(
            properties.Schema.STRING,
            _('Container format of image.'),
            required=True,
            constraints=[
                constraints.AllowedValues(['ami', 'ari', 'aki',
                                           'bare', 'ova', 'ovf'])
            ]
        ),
        LOCATION: properties.Schema(
            properties.Schema.STRING,
            _('URL where the data for this image already resides. For '
              'example, if the image data is stored in swift, you could '
              'specify "swift://example.com/container/obj".'),
            required=True,
        ),
    }

    default_client_name = 'glance'

    entity = 'images'

    def handle_create(self):
        args = dict((k, v) for k, v in self.properties.items()
                    if v is not None)
        image_id = self.client().images.create(**args).id
        self.resource_id_set(image_id)
        return image_id

    def check_create_complete(self, image_id):
        image = self.client().images.get(image_id)
        return image.status == 'active'

    def _show_resource(self):
        if self.glance().version == 1.0:
            return super(GlanceImage, self)._show_resource()
        else:
            image = self.glance().images.get(self.resource_id)
            return dict(image)

    def validate(self):
        super(GlanceImage, self).validate()
        container_format = self.properties[self.CONTAINER_FORMAT]
        if (container_format in ['ami', 'ari', 'aki']
                and self.properties[self.DISK_FORMAT] != container_format):
            msg = _("Invalid mix of disk and container formats. When "
                    "setting a disk or container format to one of 'aki', "
                    "'ari', or 'ami', the container and disk formats must "
                    "match.")
            raise exception.StackValidationFailed(message=msg)

    def get_live_resource_data(self):
        image_data = super(GlanceImage, self).get_live_resource_data()
        if image_data.get('status') in ('deleted', 'killed'):
                raise exception.EntityNotFound(entity='Resource',
                                               name=self.name)
        return image_data

    def parse_live_resource_data(self, resource_properties, resource_data):
        image_reality = {}

        # NOTE(prazumovsky): At first, there's no way to get location from
        # glance; at second, location property is doubtful, because glance
        # client v2 doesn't use location, it uses locations. So, we should
        # get location property from resource properties.
        if self.client().version == 1.0:
            image_reality.update(
                {self.LOCATION: resource_properties[self.LOCATION]})

        for key in self.PROPERTIES:
            if key == self.LOCATION:
                continue
            if key == self.IMAGE_ID:
                if (resource_properties.get(self.IMAGE_ID) is not None or
                        resource_data.get(self.IMAGE_ID) != self.resource_id):
                    image_reality.update({self.IMAGE_ID: resource_data.get(
                        self.IMAGE_ID)})
                else:
                    image_reality.update({self.IMAGE_ID: None})
            else:
                image_reality.update({key: resource_data.get(key)})

        return image_reality
コード例 #15
0
class HealthMonitor(neutron.NeutronResource):
    """A resource to handle load balancer health monitors.

    This resource creates and manages Neutron LBaaS v2 healthmonitors,
    which watches status of the load balanced servers.
    """

    support_status = support.SupportStatus(version='6.0.0')

    required_service_extension = 'lbaasv2'

    # Properties inputs for the resources create/update.
    PROPERTIES = (
        ADMIN_STATE_UP, DELAY, EXPECTED_CODES, HTTP_METHOD,
        MAX_RETRIES, POOL, TIMEOUT, TYPE, URL_PATH, TENANT_ID
    ) = (
        'admin_state_up', 'delay', 'expected_codes', 'http_method',
        'max_retries', 'pool', 'timeout', 'type', 'url_path', 'tenant_id'
    )

    # Supported HTTP methods
    HTTP_METHODS = (
        GET, HEAT, POST, PUT, DELETE, TRACE, OPTIONS,
        CONNECT, PATCH
    ) = (
        'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'OPTIONS',
        'CONNECT', 'PATCH'
    )

    # Supported output attributes of the resources.
    ATTRIBUTES = (POOLS_ATTR) = ('pools')

    properties_schema = {
        ADMIN_STATE_UP: properties.Schema(
            properties.Schema.BOOLEAN,
            _('The administrative state of the health monitor.'),
            default=True,
            update_allowed=True,
            constraints=[constraints.AllowedValues(['True'])]
        ),
        DELAY: properties.Schema(
            properties.Schema.INTEGER,
            _('The minimum time in seconds between regular connections of '
              'the member.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.Range(min=0, max=2147483647)]
        ),
        EXPECTED_CODES: properties.Schema(
            properties.Schema.STRING,
            _('The HTTP status codes expected in response from the '
              'member to declare it healthy. Specify one of the following '
              'values: a single value, such as 200. a list, such as 200, 202. '
              'a range, such as 200-204.'),
            update_allowed=True,
            default='200'
        ),
        HTTP_METHOD: properties.Schema(
            properties.Schema.STRING,
            _('The HTTP method used for requests by the monitor of type '
              'HTTP.'),
            update_allowed=True,
            default=GET,
            constraints=[constraints.AllowedValues(HTTP_METHODS)]
        ),
        MAX_RETRIES: properties.Schema(
            properties.Schema.INTEGER,
            _('Number of permissible connection failures before changing the '
              'member status to INACTIVE.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.Range(min=1, max=10)],
        ),
        POOL: properties.Schema(
            properties.Schema.STRING,
            _('ID or name of the load balancing pool.'),
            required=True
        ),
        TIMEOUT: properties.Schema(
            properties.Schema.INTEGER,
            _('Maximum number of seconds for a monitor to wait for a '
              'connection to be established before it times out.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.Range(min=0, max=2147483647)]
        ),
        TYPE: properties.Schema(
            properties.Schema.STRING,
            _('One of predefined health monitor types.'),
            required=True,
            constraints=[
                constraints.AllowedValues(['PING', 'TCP', 'HTTP']),
            ]
        ),
        URL_PATH: properties.Schema(
            properties.Schema.STRING,
            _('The HTTP path used in the HTTP request used by the monitor to '
              'test a member health. A valid value is a string the begins '
              'with a forward slash (/).'),
            update_allowed=True,
            default='/'
        ),
        TENANT_ID: properties.Schema(
            properties.Schema.STRING,
            _('ID of the tenant who owns the health monitor.')
        )
    }

    attributes_schema = {
        POOLS_ATTR: attributes.Schema(
            _('The list of Pools related to this monitor.'),
            type=attributes.Schema.LIST
        )
    }

    def __init__(self, name, definition, stack):
        super(HealthMonitor, self).__init__(name, definition, stack)
        self._lb_id = None

    @property
    def lb_id(self):
        if self._lb_id is None:
            pool_id = self.client_plugin().find_resourceid_by_name_or_id(
                self.POOL,
                self.properties[self.POOL],
                cmd_resource='lbaas_pool')
            pool = self.client().show_lbaas_pool(pool_id)['pool']

            listener_id = pool['listeners'][0]['id']
            listener = self.client().show_listener(listener_id)['listener']

            self._lb_id = listener['loadbalancers'][0]['id']
        return self._lb_id

    def _check_lb_status(self):
        return self.client_plugin().check_lb_status(self.lb_id)

    def handle_create(self):
        properties = self.prepare_properties(
            self.properties,
            self.physical_resource_name())

        self.client_plugin().resolve_pool(
            properties, self.POOL, 'pool_id')

        return properties

    def check_create_complete(self, properties):
        if self.resource_id is None:
            try:
                healthmonitor = self.client().create_lbaas_healthmonitor(
                    {'healthmonitor': properties})['healthmonitor']
                self.resource_id_set(healthmonitor['id'])
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def _show_resource(self):
        return self.client().show_lbaas_healthmonitor(
            self.resource_id)['healthmonitor']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        self._update_called = False
        return prop_diff

    def check_update_complete(self, prop_diff):
        if not prop_diff:
            return True

        if not self._update_called:
            try:
                self.client().update_lbaas_healthmonitor(
                    self.resource_id, {'healthmonitor': prop_diff})
                self._update_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def handle_delete(self):
        self._delete_called = False

    def check_delete_complete(self, data):
        if self.resource_id is None:
            return True

        if not self._delete_called:
            try:
                self.client().delete_lbaas_healthmonitor(self.resource_id)
                self._delete_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                elif self.client_plugin().is_not_found(ex):
                    return True
                raise

        return self._check_lb_status()
コード例 #16
0
ファイル: health_check.py プロジェクト: Hybrid-Cloud/conveyor
class HealthCheck(elb_res_base.ElbBaseResource):
    """A resource for ELB Health Check.

    Health Check resource for Elastic Load Balance Service.
    """

    PROPERTIES = (
        LISTENER_ID,
        PROTOCOL,
        URI,
        CONNECT_PORT,
        HEALTHY_THRESHOLD,
        UNHEALTHY_THRESHOLD,
        TIMEOUT,
        INTERVAL,
    ) = (
        'listener_id',
        'healthcheck_protocol',
        'healthcheck_uri',
        'healthcheck_connect_port',
        'healthy_threshold',
        'unhealthy_threshold',
        'healthcheck_timeout',
        'healthcheck_interval',
    )

    _PROTOCOLS = (
        HTTP,
        TCP,
    ) = (
        'HTTP',
        'TCP',
    )

    properties_schema = {
        LISTENER_ID:
        properties.Schema(properties.Schema.STRING,
                          _('The ID of listener associated.'),
                          required=True,
                          constraints=[constraints.CustomConstraint('elb.ls')
                                       ]),
        PROTOCOL:
        properties.Schema(properties.Schema.STRING,
                          _('The protocol of the health check.'),
                          constraints=[constraints.AllowedValues(_PROTOCOLS)],
                          update_allowed=True),
        URI:
        properties.Schema(
            properties.Schema.STRING,
            _('The HTTP path used in the HTTP request to check a member '
              'health.'),
            constraints=[
                constraints.AllowedPattern('^[/][0-9a-zA-Z-/.%?#&]{0,79}$')
            ],
            update_allowed=True),
        CONNECT_PORT:
        properties.Schema(properties.Schema.INTEGER,
                          _('The port of the health check.'),
                          constraints=[constraints.Range(min=1, max=65535)],
                          update_allowed=True),
        HEALTHY_THRESHOLD:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The number of the successful threshold before change the '
              'member status to healthy.'),
            constraints=[constraints.Range(min=1, max=10)],
            update_allowed=True),
        UNHEALTHY_THRESHOLD:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The number of the failure threshold before change the '
              'member status to unhealthy.'),
            constraints=[constraints.Range(min=1, max=10)],
            update_allowed=True),
        TIMEOUT:
        properties.Schema(properties.Schema.INTEGER,
                          _('The timeout of the health check in seconds.'),
                          constraints=[constraints.Range(min=1, max=50)],
                          update_allowed=True),
        INTERVAL:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The interval between the health checks in seconds.'),
            constraints=[constraints.Range(min=1, max=5)],
            update_allowed=True),
    }

    def validate(self):
        super(HealthCheck, self).validate()
        protocol = self.properties[self.PROTOCOL]
        uri = self.properties[self.URI]

        if uri and protocol != self.HTTP:
            msg = (_('Property %(uri)s is valid if %(protocol)s '
                     'is %(http)s.') % {
                         'uri': self.URI,
                         'protocol': self.PROTOCOL,
                         'http': self.HTTP
                     })
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        props = self._prepare_properties(self.properties)
        healthy_check = self.client().healthcheck.create(**props)
        self.resource_id_set(healthy_check.id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.client().healthcheck.update(healthcheck_id=self.resource_id,
                                             **prop_diff)

    def handle_delete(self):
        if not self.resource_id:
            return
        try:
            self.client().healthcheck.delete(self.resource_id)
        except Exception as e:
            # here we don't use ignore_not_found, because elb raises:
            # BadRequest("Bad Request {'message': 'this healthcheck is
            # not exist', 'code': 'ELB.7020'}",)
            if 'ELB.7020' in e.message:
                return
            raise
コード例 #17
0
class WaitCondition(heat_wc.HeatWaitCondition):

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        HANDLE, TIMEOUT, COUNT,
    ) = (
        'Handle', 'Timeout', 'Count',
    )

    ATTRIBUTES = (
        DATA,
    ) = (
        'Data',
    )

    properties_schema = {
        HANDLE: properties.Schema(
            properties.Schema.STRING,
            _('A reference to the wait condition handle used to signal this '
              'wait condition.'),
            required=True
        ),
        TIMEOUT: properties.Schema(
            properties.Schema.INTEGER,
            _('The number of seconds to wait for the correct number of '
              'signals to arrive.'),
            required=True,
            constraints=[
                constraints.Range(1, 43200),
            ]
        ),
        COUNT: properties.Schema(
            properties.Schema.INTEGER,
            _('The number of success signals that must be received before '
              'the stack creation process continues.'),
            constraints=[
                constraints.Range(min=1),
            ],
            default=1,
            update_allowed=True
        ),
    }

    attributes_schema = {
        DATA: attributes.Schema(
            _('JSON string containing data associated with wait '
              'condition signals sent to the handle.'),
            cache_mode=attributes.Schema.CACHE_NONE,
            type=attributes.Schema.STRING
        ),
    }

    def __init__(self, name, json_snippet, stack):
        super(WaitCondition, self).__init__(name, json_snippet, stack)

    def _validate_handle_url(self):
        handle_url = self.properties[self.HANDLE]
        handle_id = identifier.ResourceIdentifier.from_arn_url(handle_url)
        if handle_id.tenant != self.stack.context.tenant_id:
            raise ValueError(_("WaitCondition invalid Handle tenant %s") %
                             handle_id.tenant)
        if handle_id.stack_name != self.stack.name:
            raise ValueError(_("WaitCondition invalid Handle stack %s") %
                             handle_id.stack_name)
        if handle_id.stack_id != self.stack.id:
            raise ValueError(_("WaitCondition invalid Handle stack %s") %
                             handle_id.stack_id)
        if handle_id.resource_name not in self.stack:
            raise ValueError(_("WaitCondition invalid Handle %s") %
                             handle_id.resource_name)
        if not isinstance(self.stack[handle_id.resource_name],
                          aws_wch.WaitConditionHandle):
            raise ValueError(_("WaitCondition invalid Handle %s") %
                             handle_id.resource_name)

    def _get_handle_resource(self):
        handle_url = self.properties[self.HANDLE]
        handle_id = identifier.ResourceIdentifier.from_arn_url(handle_url)
        return self.stack[handle_id.resource_name]

    def handle_create(self):
        self._validate_handle_url()
        return super(WaitCondition, self).handle_create()
コード例 #18
0
ファイル: pool_member.py プロジェクト: Hybrid-Cloud/conveyor
class PoolMember(neutron.NeutronResource):
    """A resource for managing LBaaS v2 Pool Members.

    A pool member represents a single backend node.
    """

    support_status = support.SupportStatus(version='6.0.0')

    required_service_extension = 'lbaasv2'

    PROPERTIES = (
        POOL, ADDRESS, PROTOCOL_PORT, WEIGHT, ADMIN_STATE_UP,
        SUBNET,
    ) = (
        'pool', 'address', 'protocol_port', 'weight', 'admin_state_up',
        'subnet'
    )

    ATTRIBUTES = (
        ADDRESS_ATTR, POOL_ID_ATTR
    ) = (
        'address', 'pool_id'
    )

    properties_schema = {
        POOL: properties.Schema(
            properties.Schema.STRING,
            _('Name or ID of the load balancing pool.'),
            required=True
        ),
        ADDRESS: properties.Schema(
            properties.Schema.STRING,
            _('IP address of the pool member on the pool network.'),
            required=True,
            constraints=[
                constraints.CustomConstraint('ip_addr')
            ]
        ),
        PROTOCOL_PORT: properties.Schema(
            properties.Schema.INTEGER,
            _('Port on which the pool member listens for requests or '
              'connections.'),
            required=True,
            constraints=[
                constraints.Range(1, 65535),
            ]
        ),
        WEIGHT: properties.Schema(
            properties.Schema.INTEGER,
            _('Weight of pool member in the pool (default to 1).'),
            default=1,
            constraints=[
                constraints.Range(0, 256),
            ],
            update_allowed=True
        ),
        ADMIN_STATE_UP: properties.Schema(
            properties.Schema.BOOLEAN,
            _('The administrative state of the pool member.'),
            default=True,
            update_allowed=True,
            constraints=[constraints.AllowedValues(['True'])]
        ),
        SUBNET: properties.Schema(
            properties.Schema.STRING,
            _('Subnet name or ID of this member.'),
            constraints=[
                constraints.CustomConstraint('neutron.subnet')
            ]
        ),
    }

    attributes_schema = {
        ADDRESS_ATTR: attributes.Schema(
            _('The IP address of the pool member.'),
            type=attributes.Schema.STRING
        ),
        POOL_ID_ATTR: attributes.Schema(
            _('The ID of the pool to which the pool member belongs.'),
            type=attributes.Schema.STRING
        )
    }

    def __init__(self, name, definition, stack):
        super(PoolMember, self).__init__(name, definition, stack)
        self._pool_id = None
        self._lb_id = None

    @property
    def pool_id(self):
        if self._pool_id is None:
            self._pool_id = self.client_plugin().find_resourceid_by_name_or_id(
                self.POOL,
                self.properties[self.POOL],
                cmd_resource='lbaas_pool')
        return self._pool_id

    @property
    def lb_id(self):
        if self._lb_id is None:
            pool = self.client().show_lbaas_pool(self.pool_id)['pool']

            listener_id = pool['listeners'][0]['id']
            listener = self.client().show_listener(listener_id)['listener']

            self._lb_id = listener['loadbalancers'][0]['id']
        return self._lb_id

    def _check_lb_status(self):
        return self.client_plugin().check_lb_status(self.lb_id)

    def handle_create(self):
        properties = self.prepare_properties(
            self.properties,
            self.physical_resource_name())

        self.client_plugin().resolve_pool(
            properties, self.POOL, 'pool_id')
        properties.pop('pool_id')

        if self.SUBNET in properties:
            self.client_plugin().resolve_subnet(
                properties, self.SUBNET, 'subnet_id')

        return properties

    def check_create_complete(self, properties):
        if self.resource_id is None:
            try:
                member = self.client().create_lbaas_member(
                    self.pool_id, {'member': properties})['member']
                self.resource_id_set(member['id'])
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def _show_resource(self):
        member = self.client().show_lbaas_member(self.resource_id,
                                                 self.pool_id)
        return member['member']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        self._update_called = False
        return prop_diff

    def check_update_complete(self, prop_diff):
        if not prop_diff:
            return True

        if not self._update_called:
            try:
                self.client().update_lbaas_member(self.resource_id,
                                                  self.pool_id,
                                                  {'member': prop_diff})
                self._update_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def handle_delete(self):
        self._delete_called = False

    def check_delete_complete(self, data):
        if self.resource_id is None:
            return True

        if not self._delete_called:
            try:
                self.client().delete_lbaas_member(self.resource_id,
                                                  self.pool_id)
                self._delete_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                elif self.client_plugin().is_not_found(ex):
                    return True
                raise

        return self._check_lb_status()
コード例 #19
0
class Listener(elb_res_base.ElbBaseResource):
    """A resource for ELB Listener.

    Listener resource for Elastic Load Balance Service.
    """

    PROPERTIES = (
        NAME, DESCRIPTION, LB_ID, PROTOCOL, PORT,
        BACKEND_PROTOCOL, BACKEND_PORT, LB_ALGORITHM, SESSION_STICKY,
        STICKY_SESSION_TYPE, COOKIE_TIMEOUT, CERTIFICATE,
        TCP_TIMEOUT,
    ) = (
        'name', 'description', 'loadbalancer_id', 'protocol', 'port',
        'backend_protocol', 'backend_port', 'lb_algorithm', 'session_sticky',
        'sticky_session_type', 'cookie_timeout', 'certificate_id',
        'tcp_timeout',
    )

    _BACKEND_PROTOCOLS = (
        HTTP, TCP,
    ) = (
        'HTTP', 'TCP',
    )

    HTTPS = ('HTTPS')
    _PROTOCOLS = _BACKEND_PROTOCOLS + (HTTPS,)

    _LB_ALGORITHMS = (
        ROUND_ROBIN, LEAST_CONNECTIONS, SOURCE_IP,
    ) = (
        'roundrobin', 'leastconn', 'source',
    )

    ATTRIBUTES = (
        MEMBER_NUMBER_ATTR, STATUS_ATTR,
    ) = (
        'member_number', 'status',
    )
    properties_schema = {
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('The name of the listener.'),
            required=True,
            update_allowed=True,
            constraints=[
                constraints.AllowedPattern('^[0-9a-zA-Z-_]{1,64}$')]
        ),
        DESCRIPTION: properties.Schema(
            properties.Schema.STRING,
            _('The description of the listener.'),
            update_allowed=True,
            constraints=[constraints.AllowedPattern('^[^<>]{1,128}$')]
        ),
        LB_ID: properties.Schema(
            properties.Schema.STRING,
            _('The ID of load balancer associated.'),
            required=True,
            constraints=[
                constraints.CustomConstraint('elb.lb')
            ]
        ),
        PROTOCOL: properties.Schema(
            properties.Schema.STRING,
            _('The protocol of the listener.'),
            constraints=[
                constraints.AllowedValues(_PROTOCOLS)
            ],
            required=True
        ),
        BACKEND_PROTOCOL: properties.Schema(
            properties.Schema.STRING,
            _('The backend protocol of the listener.'),
            constraints=[
                constraints.AllowedValues(_BACKEND_PROTOCOLS)
            ],
            required=True
        ),
        PORT: properties.Schema(
            properties.Schema.INTEGER,
            _('The port of the listener.'),
            constraints=[
                constraints.Range(min=1, max=65535)
            ],
            required=True,
            update_allowed=True,
        ),
        BACKEND_PORT: properties.Schema(
            properties.Schema.INTEGER,
            _('The backend port of the listener.'),
            constraints=[
                constraints.Range(min=1, max=65535)
            ],
            required=True,
            update_allowed=True,
        ),
        LB_ALGORITHM: properties.Schema(
            properties.Schema.STRING,
            _('The algorithm used to distribute load.'),
            constraints=[
                constraints.AllowedValues(_LB_ALGORITHMS)
            ],
            required=True,
            update_allowed=True,
        ),
        SESSION_STICKY: properties.Schema(
            properties.Schema.BOOLEAN,
            _('Whether to keep the session.'),
            update_allowed=True
        ),
        STICKY_SESSION_TYPE: properties.Schema(
            properties.Schema.STRING,
            _('The way of handing cookie.'),
            constraints=[
                constraints.AllowedValues(['insert'])
            ],
        ),
        COOKIE_TIMEOUT: properties.Schema(
            properties.Schema.INTEGER,
            _('The timeout of cookie in minute.'),
            constraints=[
                constraints.Range(min=1, max=1440)
            ],
            update_allowed=True
        ),
        CERTIFICATE: properties.Schema(
            properties.Schema.STRING,
            _('The ID of certificate.'),
            constraints=[
                constraints.CustomConstraint('elb.cert')
            ]
        ),
        TCP_TIMEOUT: properties.Schema(
            properties.Schema.INTEGER,
            _('The timeout of TCP session in minute.'),
            constraints=[
                constraints.Range(min=1, max=5)
            ],
            update_allowed=True
        ),
    }

    attributes_schema = {
        MEMBER_NUMBER_ATTR: attributes.Schema(
            _('The number of the members listened by this listener.'),
        ),
        STATUS_ATTR: attributes.Schema(
            _('The status of the listener.'),
        ),
    }

    def validate(self):
        super(Listener, self).validate()
        protocol = self.properties[self.PROTOCOL]
        session_sticky = self.properties[self.SESSION_STICKY]
        sticky_type = self.properties[self.STICKY_SESSION_TYPE]
        certificate = self.properties[self.CERTIFICATE]
        tcp_timeout = self.properties[self.TCP_TIMEOUT]

        if protocol == self.HTTP and session_sticky:
            if sticky_type != 'insert':
                msg = (_('Property %(sticky_type)s should be "insert" '
                         'when %(protocol)s is %(http)s and '
                         '%(session_sticky)s is enabled.') %
                       {'sticky_type': self.STICKY_SESSION_TYPE,
                        'protocol': self.PROTOCOL,
                        'http': self.HTTP,
                        'session_sticky': self.SESSION_STICKY})
                raise exception.StackValidationFailed(message=msg)
        if protocol == self.HTTPS:
            if not certificate:
                msg = (_('Property %(cert)s is required when %(protocol)s '
                         'is %(https)s') %
                       {'cert': self.CERTIFICATE,
                        'protocol': self.PROTOCOL,
                        'https': self.HTTPS})
                raise exception.StackValidationFailed(message=msg)
        if tcp_timeout and protocol != self.TCP:
            msg = (_('Property %(tcpt)s is valid when %(protocol)s '
                     'is %(tcp)s') %
                   {'tcpt': self.TCP_TIMEOUT,
                    'protocol': self.PROTOCOL,
                    'tcp': self.TCP})
            raise exception.StackValidationFailed(message=msg)

    def _resolve_attribute(self, name):
        if not self.resource_id:
            return

        ls = self.client().listener.get(self.resource_id)
        if name == self.MEMBER_NUMBER_ATTR:
            return ls.extra['member_number']
        if name == self.STATUS_ATTR:
            return ls.status

    def FnGetRefId(self):
        return self.resource_id

    def handle_create(self):
        props = self._prepare_properties(self.properties)
        ls = self.client().listener.create(**props)
        self.resource_id_set(ls.id)
        return ls.status

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            if self.COOKIE_TIMEOUT in prop_diff:
                if prop_diff[self.COOKIE_TIMEOUT] is None:
                    prop_diff.pop(self.COOKIE_TIMEOUT)
            if self.TCP_TIMEOUT in prop_diff:
                if prop_diff[self.TCP_TIMEOUT] is None:
                    prop_diff.pop(self.TCP_TIMEOUT)
            if self.SESSION_STICKY in prop_diff:
                if prop_diff[self.SESSION_STICKY] is None:
                    prop_diff.pop(self.SESSION_STICKY)
            self.client().listener.update(listener_id=self.resource_id,
                                          **prop_diff)

    def handle_delete(self):
        if not self.resource_id:
            return

        try:
            self.client().listener.delete(self.resource_id)
        except Exception as e:
            # here we don't use ignore_not_found, because elb raises:
            # BadRequest("Bad Request {'message': 'find listener failed',
            # 'code': 'ELB.6030'}",)
            if 'ELB.6030' in e.message:
                return
            raise

    def check_create_complete(self, ls_status):
        return self._check_active(ls_status)

    def needs_replace_failed(self):
        if not self.resource_id:
            return True

        with self.client_plugin().ignore_not_found:
            ls = self.client().listener.get(self.resource_id)
            return ls.status == 'ERROR'

        return True
コード例 #20
0
ファイル: subnet.py プロジェクト: Hybrid-Cloud/conveyor
class Subnet(neutron.NeutronResource):
    """A resource for managing Neutron subnets.

    A subnet represents an IP address block that can be used for assigning IP
    addresses to virtual instances. Each subnet must have a CIDR and must be
    associated with a network. IPs can be either selected from the whole subnet
    CIDR, or from "allocation pools" that can be specified by the user.
    """

    PROPERTIES = (
        NETWORK_ID,
        NETWORK,
        SUBNETPOOL,
        PREFIXLEN,
        CIDR,
        VALUE_SPECS,
        NAME,
        IP_VERSION,
        DNS_NAMESERVERS,
        GATEWAY_IP,
        ENABLE_DHCP,
        ALLOCATION_POOLS,
        TENANT_ID,
        HOST_ROUTES,
        IPV6_RA_MODE,
        IPV6_ADDRESS_MODE,
    ) = (
        'network_id',
        'network',
        'subnetpool',
        'prefixlen',
        'cidr',
        'value_specs',
        'name',
        'ip_version',
        'dns_nameservers',
        'gateway_ip',
        'enable_dhcp',
        'allocation_pools',
        'tenant_id',
        'host_routes',
        'ipv6_ra_mode',
        'ipv6_address_mode',
    )

    _ALLOCATION_POOL_KEYS = (
        ALLOCATION_POOL_START,
        ALLOCATION_POOL_END,
    ) = (
        'start',
        'end',
    )

    _HOST_ROUTES_KEYS = (
        ROUTE_DESTINATION,
        ROUTE_NEXTHOP,
    ) = (
        'destination',
        'nexthop',
    )

    _IPV6_DHCP_MODES = (
        DHCPV6_STATEFUL,
        DHCPV6_STATELESS,
        SLAAC,
    ) = (
        'dhcpv6-stateful',
        'dhcpv6-stateless',
        'slaac',
    )

    ATTRIBUTES = (
        NAME_ATTR,
        NETWORK_ID_ATTR,
        TENANT_ID_ATTR,
        ALLOCATION_POOLS_ATTR,
        GATEWAY_IP_ATTR,
        HOST_ROUTES_ATTR,
        IP_VERSION_ATTR,
        CIDR_ATTR,
        DNS_NAMESERVERS_ATTR,
        ENABLE_DHCP_ATTR,
    ) = (
        'name',
        'network_id',
        'tenant_id',
        'allocation_pools',
        'gateway_ip',
        'host_routes',
        'ip_version',
        'cidr',
        'dns_nameservers',
        'enable_dhcp',
    )

    properties_schema = {
        NETWORK_ID:
        properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='5.0.0',
                message=_('Use property %s.') % NETWORK,
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED, version='2014.2')),
            constraints=[constraints.CustomConstraint('neutron.network')],
        ),
        NETWORK:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the attached network.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.network')],
            support_status=support.SupportStatus(version='2014.2')),
        SUBNETPOOL:
        properties.Schema(
            properties.Schema.STRING,
            _('The name or ID of the subnet pool.'),
            constraints=[constraints.CustomConstraint('neutron.subnetpool')],
            support_status=support.SupportStatus(version='6.0.0'),
        ),
        PREFIXLEN:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Prefix length for subnet allocation from subnet pool.'),
            constraints=[constraints.Range(min=0)],
            support_status=support.SupportStatus(version='6.0.0'),
        ),
        CIDR:
        properties.Schema(
            properties.Schema.STRING,
            _('The CIDR.'),
            constraints=[constraints.CustomConstraint('net_cidr')]),
        VALUE_SPECS:
        properties.Schema(properties.Schema.MAP,
                          _('Extra parameters to include in the request.'),
                          default={},
                          update_allowed=True),
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('The name of the subnet.'),
                          update_allowed=True),
        IP_VERSION:
        properties.Schema(properties.Schema.INTEGER,
                          _('The IP version, which is 4 or 6.'),
                          default=4,
                          constraints=[
                              constraints.AllowedValues([4, 6]),
                          ]),
        DNS_NAMESERVERS:
        properties.Schema(properties.Schema.LIST,
                          _('A specified set of DNS name servers to be used.'),
                          default=[],
                          update_allowed=True),
        GATEWAY_IP:
        properties.Schema(
            properties.Schema.STRING,
            _('The gateway IP address. Set to any of [ null | ~ | "" ] '
              'to create/update a subnet without a gateway. '
              'If omitted when creation, neutron will assign the first '
              'free IP address within the subnet to the gateway '
              'automatically. If remove this from template when update, '
              'the old gateway IP address will be detached.'),
            update_allowed=True),
        ENABLE_DHCP:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Set to true if DHCP is enabled and false if DHCP is disabled.'),
            default=True,
            update_allowed=True),
        ALLOCATION_POOLS:
        properties.Schema(
            properties.Schema.LIST,
            _('The start and end addresses for the allocation pools.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    ALLOCATION_POOL_START:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('Start address for the allocation pool.'),
                        required=True,
                        constraints=[constraints.CustomConstraint('ip_addr')]),
                    ALLOCATION_POOL_END:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('End address for the allocation pool.'),
                        required=True,
                        constraints=[constraints.CustomConstraint('ip_addr')]),
                },
            ),
            update_allowed=True),
        TENANT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the tenant who owns the network. Only administrative '
              'users can specify a tenant ID other than their own.')),
        HOST_ROUTES:
        properties.Schema(
            properties.Schema.LIST,
            _('A list of host route dictionaries for the subnet.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema={
                    ROUTE_DESTINATION:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('The destination for static route.'),
                        required=True,
                        constraints=[constraints.CustomConstraint('net_cidr')
                                     ]),
                    ROUTE_NEXTHOP:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('The next hop for the destination.'),
                        required=True,
                        constraints=[constraints.CustomConstraint('ip_addr')]),
                },
            ),
            update_allowed=True),
        IPV6_RA_MODE:
        properties.Schema(
            properties.Schema.STRING,
            _('IPv6 RA (Router Advertisement) mode.'),
            constraints=[
                constraints.AllowedValues(
                    [DHCPV6_STATEFUL, DHCPV6_STATELESS, SLAAC]),
            ],
            support_status=support.SupportStatus(version='2015.1')),
        IPV6_ADDRESS_MODE:
        properties.Schema(
            properties.Schema.STRING,
            _('IPv6 address mode.'),
            constraints=[
                constraints.AllowedValues(
                    [DHCPV6_STATEFUL, DHCPV6_STATELESS, SLAAC]),
            ],
            support_status=support.SupportStatus(version='2015.1')),
    }

    attributes_schema = {
        NAME_ATTR:
        attributes.Schema(_("Friendly name of the subnet."),
                          type=attributes.Schema.STRING),
        NETWORK_ID_ATTR:
        attributes.Schema(_("Parent network of the subnet."),
                          type=attributes.Schema.STRING),
        TENANT_ID_ATTR:
        attributes.Schema(_("Tenant owning the subnet."),
                          type=attributes.Schema.STRING),
        ALLOCATION_POOLS_ATTR:
        attributes.Schema(_("Ip allocation pools and their ranges."),
                          type=attributes.Schema.LIST),
        GATEWAY_IP_ATTR:
        attributes.Schema(_("Ip of the subnet's gateway."),
                          type=attributes.Schema.STRING),
        HOST_ROUTES_ATTR:
        attributes.Schema(_("Additional routes for this subnet."),
                          type=attributes.Schema.LIST),
        IP_VERSION_ATTR:
        attributes.Schema(_("Ip version for the subnet."),
                          type=attributes.Schema.STRING),
        CIDR_ATTR:
        attributes.Schema(_("CIDR block notation for this subnet."),
                          type=attributes.Schema.STRING),
        DNS_NAMESERVERS_ATTR:
        attributes.Schema(_("List of dns nameservers."),
                          type=attributes.Schema.LIST),
        ENABLE_DHCP_ATTR:
        attributes.Schema(
            _("'true' if DHCP is enabled for this subnet; 'false' otherwise."),
            type=attributes.Schema.STRING),
    }

    def translation_rules(self, props):
        return [
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.NETWORK],
                                        value_path=[self.NETWORK_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.NETWORK],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='network'),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.SUBNETPOOL],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='subnetpool')
        ]

    @classmethod
    def _null_gateway_ip(cls, props):
        if cls.GATEWAY_IP not in props:
            return
        # Specifying null in the gateway_ip will result in
        # a property containing an empty string.
        # A null gateway_ip has special meaning in the API
        # so this needs to be set back to None.
        # See bug https://bugs.launchpad.net/heat/+bug/1226666
        if props.get(cls.GATEWAY_IP) == '':
            props[cls.GATEWAY_IP] = None

    def validate(self):
        super(Subnet, self).validate()
        subnetpool = self.properties[self.SUBNETPOOL]
        prefixlen = self.properties[self.PREFIXLEN]
        cidr = self.properties[self.CIDR]
        if subnetpool and cidr:
            raise exception.ResourcePropertyConflict(self.SUBNETPOOL,
                                                     self.CIDR)
        if not subnetpool and not cidr:
            raise exception.PropertyUnspecifiedError(self.SUBNETPOOL,
                                                     self.CIDR)
        if prefixlen and cidr:
            raise exception.ResourcePropertyConflict(self.PREFIXLEN, self.CIDR)
        ra_mode = self.properties[self.IPV6_RA_MODE]
        address_mode = self.properties[self.IPV6_ADDRESS_MODE]

        if (self.properties[self.IP_VERSION] == 4) and (ra_mode
                                                        or address_mode):
            msg = _('ipv6_ra_mode and ipv6_address_mode are not supported '
                    'for ipv4.')
            raise exception.StackValidationFailed(message=msg)
        if ra_mode and address_mode and (ra_mode != address_mode):
            msg = _('When both ipv6_ra_mode and ipv6_address_mode are set, '
                    'they must be equal.')
            raise exception.StackValidationFailed(message=msg)

        gateway_ip = self.properties.get(self.GATEWAY_IP)
        if (gateway_ip and gateway_ip not in ['~', '']
                and not netutils.is_valid_ip(gateway_ip)):
            msg = (_('Gateway IP address "%(gateway)s" is in '
                     'invalid format.'), gateway_ip)
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        props['network_id'] = props.pop(self.NETWORK)
        if self.SUBNETPOOL in props and props[self.SUBNETPOOL]:
            props['subnetpool_id'] = props.pop('subnetpool')
        self._null_gateway_ip(props)
        subnet = self.client().create_subnet({'subnet': props})['subnet']
        self.resource_id_set(subnet['id'])

    def handle_delete(self):
        if not self.resource_id:
            return

        try:
            self.client().delete_subnet(self.resource_id)
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            return True

    def _show_resource(self):
        return self.client().show_subnet(self.resource_id)['subnet']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.prepare_update_properties(prop_diff)
            if (self.ALLOCATION_POOLS in prop_diff
                    and prop_diff[self.ALLOCATION_POOLS] is None):
                prop_diff[self.ALLOCATION_POOLS] = []

            # If the new value is '', set to None
            self._null_gateway_ip(prop_diff)

            self.client().update_subnet(self.resource_id,
                                        {'subnet': prop_diff})

    def is_allow_replace(self):
        return True
コード例 #21
0
class Volume(vb.BaseVolume):

    PROPERTIES = (
        AVAILABILITY_ZONE,
        SIZE,
        BACKUP_ID,
        TAGS,
    ) = (
        'AvailabilityZone',
        'Size',
        'SnapshotId',
        'Tags',
    )

    _TAG_KEYS = (
        TAG_KEY,
        TAG_VALUE,
    ) = (
        'Key',
        'Value',
    )

    properties_schema = {
        AVAILABILITY_ZONE:
        properties.Schema(
            properties.Schema.STRING,
            _('The availability zone in which the volume will be created.'),
            required=True,
            immutable=True),
        SIZE:
        properties.Schema(properties.Schema.INTEGER,
                          _('The size of the volume in GB.'),
                          immutable=True,
                          constraints=[
                              constraints.Range(min=1),
                          ]),
        BACKUP_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('If specified, the backup used as the source to create the '
              'volume.'),
            immutable=True,
            constraints=[constraints.CustomConstraint('cinder.backup')]),
        TAGS:
        properties.Schema(properties.Schema.LIST,
                          _('The list of tags to associate with the volume.'),
                          immutable=True,
                          schema=properties.Schema(
                              properties.Schema.MAP,
                              schema={
                                  TAG_KEY:
                                  properties.Schema(properties.Schema.STRING,
                                                    required=True),
                                  TAG_VALUE:
                                  properties.Schema(properties.Schema.STRING,
                                                    required=True),
                              },
                          )),
    }

    _volume_creating_status = ['creating', 'restoring-backup']

    def _create_arguments(self):
        if self.properties[self.TAGS]:
            tags = dict((tm[self.TAG_KEY], tm[self.TAG_VALUE])
                        for tm in self.properties[self.TAGS])
        else:
            tags = None

        return {
            'size': self.properties[self.SIZE],
            'availability_zone': (self.properties[self.AVAILABILITY_ZONE]
                                  or None),
            'metadata': tags
        }
コード例 #22
0
class AutoScalingPolicy(signal_responder.SignalResponder,
                        cooldown.CooldownMixin):
    """A resource to manage scaling of `OS::Heat::AutoScalingGroup`.

    **Note** while it may incidentally support
    `AWS::AutoScaling::AutoScalingGroup` for now, please don't use it for that
    purpose and use `AWS::AutoScaling::ScalingPolicy` instead.

    Resource to manage scaling for `OS::Heat::AutoScalingGroup`, i.e. define
    which metric should be scaled and scaling adjustment, set cooldown etc.
    """
    PROPERTIES = (AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE,
                  COOLDOWN, MIN_ADJUSTMENT_STEP) = (
                      'auto_scaling_group_id',
                      'scaling_adjustment',
                      'adjustment_type',
                      'cooldown',
                      'min_adjustment_step',
                  )

    ATTRIBUTES = (ALARM_URL, SIGNAL_URL) = ('alarm_url', 'signal_url')

    properties_schema = {
        # TODO(Qiming): property name should be AUTO_SCALING_GROUP_ID
        AUTO_SCALING_GROUP_NAME:
        properties.Schema(properties.Schema.STRING,
                          _('AutoScaling group ID to apply policy to.'),
                          required=True),
        SCALING_ADJUSTMENT:
        properties.Schema(properties.Schema.NUMBER,
                          _('Size of adjustment.'),
                          required=True,
                          update_allowed=True),
        ADJUSTMENT_TYPE:
        properties.Schema(properties.Schema.STRING,
                          _('Type of adjustment (absolute or percentage).'),
                          required=True,
                          constraints=[
                              constraints.AllowedValues([
                                  sc_util.CHANGE_IN_CAPACITY,
                                  sc_util.EXACT_CAPACITY,
                                  sc_util.PERCENT_CHANGE_IN_CAPACITY
                              ]),
                          ],
                          update_allowed=True),
        COOLDOWN:
        properties.Schema(properties.Schema.NUMBER,
                          _('Cooldown period, in seconds.'),
                          update_allowed=True),
        MIN_ADJUSTMENT_STEP:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Minimum number of resources that are added or removed '
              'when the AutoScaling group scales up or down. This can '
              'be used only when specifying percent_change_in_capacity '
              'for the adjustment_type property.'),
            constraints=[
                constraints.Range(min=0, ),
            ],
            update_allowed=True),
    }

    attributes_schema = {
        ALARM_URL:
        attributes.Schema(_("A signed url to handle the alarm."),
                          type=attributes.Schema.STRING),
        SIGNAL_URL:
        attributes.Schema(
            _("A url to handle the alarm using native API."),
            support_status=support.SupportStatus(version='5.0.0'),
            type=attributes.Schema.STRING),
    }

    def validate(self):
        """Add validation for min_adjustment_step."""
        super(AutoScalingPolicy, self).validate()
        self._validate_min_adjustment_step()

    def _validate_min_adjustment_step(self):
        adjustment_type = self.properties.get(self.ADJUSTMENT_TYPE)
        adjustment_step = self.properties.get(self.MIN_ADJUSTMENT_STEP)
        if (adjustment_type != sc_util.PERCENT_CHANGE_IN_CAPACITY
                and adjustment_step is not None):
            raise exception.ResourcePropertyValueDependency(
                prop1=self.MIN_ADJUSTMENT_STEP,
                prop2=self.ADJUSTMENT_TYPE,
                value=sc_util.PERCENT_CHANGE_IN_CAPACITY)

    def handle_metadata_reset(self):
        metadata = self.metadata_get()
        if 'scaling_in_progress' in metadata:
            metadata['scaling_in_progress'] = False
            self.metadata_set(metadata)

    def handle_create(self):
        super(AutoScalingPolicy, self).handle_create()
        self.resource_id_set(self._get_user_id())

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        """Updates self.properties, if Properties has changed.

        If Properties has changed, update self.properties, so we get the new
        values during any subsequent adjustment.
        """
        if prop_diff:
            self.properties = json_snippet.properties(self.properties_schema,
                                                      self.context)

    def handle_signal(self, details=None):
        # ceilometer sends details like this:
        # {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm',
        #  u'reason': u'...'})
        # in this policy we currently assume that this gets called
        # only when there is an alarm. But the template writer can
        # put the policy in all the alarm notifiers (nodata, and ok).
        #
        # our watchrule has upper case states so lower() them all.
        if details is None:
            alarm_state = 'alarm'
        else:
            alarm_state = details.get('current', details.get('state',
                                                             'alarm')).lower()

        LOG.info(_LI('Alarm %(name)s, new state %(state)s'), {
            'name': self.name,
            'state': alarm_state
        })

        if alarm_state != 'alarm':
            raise exception.NoActionRequired()
        if not self._is_scaling_allowed():
            LOG.info(
                _LI("%(name)s NOT performing scaling action, "
                    "cooldown %(cooldown)s"), {
                        'name': self.name,
                        'cooldown': self.properties[self.COOLDOWN]
                    })
            raise exception.NoActionRequired()

        asgn_id = self.properties[self.AUTO_SCALING_GROUP_NAME]
        group = self.stack.resource_by_refid(asgn_id)
        changed_size = False
        try:
            if group is None:
                raise exception.NotFound(
                    _('Alarm %(alarm)s could not find '
                      'scaling group named "%(group)s"') % {
                          'alarm': self.name,
                          'group': asgn_id
                      })

            LOG.info(
                _LI('%(name)s Alarm, adjusting Group %(group)s with id '
                    '%(asgn_id)s by %(filter)s'), {
                        'name': self.name,
                        'group': group.name,
                        'asgn_id': asgn_id,
                        'filter': self.properties[self.SCALING_ADJUSTMENT]
                    })
            changed_size = group.adjust(
                self.properties[self.SCALING_ADJUSTMENT],
                self.properties[self.ADJUSTMENT_TYPE],
                self.properties[self.MIN_ADJUSTMENT_STEP])
        finally:
            self._finished_scaling("%s : %s" %
                                   (self.properties[self.ADJUSTMENT_TYPE],
                                    self.properties[self.SCALING_ADJUSTMENT]),
                                   changed_size=changed_size)

    def _resolve_attribute(self, name):
        if self.resource_id is None:
            return
        if name == self.ALARM_URL:
            return six.text_type(self._get_ec2_signed_url())
        elif name == self.SIGNAL_URL:
            return six.text_type(self._get_heat_signal_url())

    def get_reference_id(self):
        return resource.Resource.get_reference_id(self)
コード例 #23
0
class Listener(neutron.NeutronResource):
    """A resource for managing LBaaS v2 Listeners.

    This resource creates and manages Neutron LBaaS v2 Listeners,
    which represent a listening endpoint for the vip.
    """

    support_status = support.SupportStatus(version='6.0.0')

    required_service_extension = 'lbaasv2'

    PROPERTIES = (PROTOCOL_PORT, PROTOCOL, LOADBALANCER, NAME, ADMIN_STATE_UP,
                  DESCRIPTION, DEFAULT_TLS_CONTAINER_REF, SNI_CONTAINER_REFS,
                  CONNECTION_LIMIT,
                  TENANT_ID) = ('protocol_port', 'protocol', 'loadbalancer',
                                'name', 'admin_state_up', 'description',
                                'default_tls_container_ref',
                                'sni_container_refs', 'connection_limit',
                                'tenant_id')

    PROTOCOLS = (
        TCP,
        HTTP,
    ) = (
        'TCP',
        'HTTP',
    )

    ATTRIBUTES = (LOADBALANCERS_ATTR,
                  DEFAULT_POOL_ID_ATTR) = ('loadbalancers', 'default_pool_id')

    properties_schema = {
        PROTOCOL_PORT:
        properties.Schema(
            properties.Schema.INTEGER,
            _('TCP or UDP port on which to listen for client traffic.'),
            required=True,
            constraints=[
                constraints.Range(1, 65535),
            ]),
        PROTOCOL:
        properties.Schema(
            properties.Schema.STRING,
            _('Protocol on which to listen for the client traffic.'),
            required=True,
            constraints=[
                constraints.AllowedValues(PROTOCOLS),
            ]),
        LOADBALANCER:
        properties.Schema(
            properties.Schema.STRING,
            _('ID or name of the load balancer with which listener '
              'is associated.'),
            required=True),
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('Name of this listener.'),
                          update_allowed=True),
        ADMIN_STATE_UP:
        properties.Schema(properties.Schema.BOOLEAN,
                          _('The administrative state of this listener.'),
                          update_allowed=True,
                          default=True,
                          constraints=[constraints.AllowedValues(['True'])]),
        DESCRIPTION:
        properties.Schema(properties.Schema.STRING,
                          _('Description of this listener.'),
                          update_allowed=True,
                          default=''),
        DEFAULT_TLS_CONTAINER_REF:
        properties.Schema(properties.Schema.STRING,
                          _('Default TLS container reference to retrieve TLS '
                            'information.'),
                          update_allowed=True),
        SNI_CONTAINER_REFS:
        properties.Schema(properties.Schema.LIST,
                          _('List of TLS container references for SNI.'),
                          update_allowed=True),
        CONNECTION_LIMIT:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The maximum number of connections permitted for this '
              'load balancer. Defaults to -1, which is infinite.'),
            update_allowed=True,
            default=-1,
            constraints=[
                constraints.Range(min=-1, max=2147483647),
            ]),
        TENANT_ID:
        properties.Schema(properties.Schema.STRING,
                          _('The ID of the tenant who owns the listener.')),
    }

    attributes_schema = {
        LOADBALANCERS_ATTR:
        attributes.Schema(
            _('ID of the load balancer this listener is associated to.'),
            type=attributes.Schema.LIST),
        DEFAULT_POOL_ID_ATTR:
        attributes.Schema(
            _('ID of the default pool this listener is associated to.'),
            type=attributes.Schema.STRING)
    }

    def __init__(self, name, definition, stack):
        super(Listener, self).__init__(name, definition, stack)
        self._lb_id = None

    @property
    def lb_id(self):
        if self._lb_id is None:
            self._lb_id = self.client_plugin().find_resourceid_by_name_or_id(
                'loadbalancer', self.properties[self.LOADBALANCER])
        return self._lb_id

    def validate(self):
        res = super(Listener, self).validate()
        if res:
            return res

    def _check_lb_status(self):
        return self.client_plugin().check_lb_status(self.lb_id)

    def handle_create(self):
        properties = self.prepare_properties(self.properties,
                                             self.physical_resource_name())

        self.client_plugin().resolve_loadbalancer(properties,
                                                  self.LOADBALANCER,
                                                  'loadbalancer_id')

        return properties

    def check_create_complete(self, properties):
        if self.resource_id is None:
            try:
                listener = self.client().create_listener(
                    {'listener': properties})['listener']
                self.resource_id_set(listener['id'])
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def _show_resource(self):
        return self.client().show_listener(self.resource_id)['listener']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        self._update_called = False
        return prop_diff

    def check_update_complete(self, prop_diff):
        if not prop_diff:
            return True

        if not self._update_called:
            try:
                self.client().update_listener(self.resource_id,
                                              {'listener': prop_diff})
                self._update_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                raise

        return self._check_lb_status()

    def handle_delete(self):
        self._delete_called = False

    def check_delete_complete(self, data):
        if self.resource_id is None:
            return True

        if not self._delete_called:
            try:
                self.client().delete_listener(self.resource_id)
                self._delete_called = True
            except Exception as ex:
                if self.client_plugin().is_invalid(ex):
                    return False
                elif self.client_plugin().is_not_found(ex):
                    return True
                raise

        return self._check_lb_status()
コード例 #24
0
class LoadBalancer(resource.Resource):
    """A resource to link a neutron pool with servers.

    A loadbalancer allows linking a neutron pool with specified servers to some
    port.
    """

    required_service_extension = 'lbaas'

    PROPERTIES = (
        POOL_ID, PROTOCOL_PORT, MEMBERS,
    ) = (
        'pool_id', 'protocol_port', 'members',
    )

    properties_schema = {
        POOL_ID: properties.Schema(
            properties.Schema.STRING,
            _('The ID of the load balancing pool.'),
            required=True,
            update_allowed=True
        ),
        PROTOCOL_PORT: properties.Schema(
            properties.Schema.INTEGER,
            _('Port number on which the servers are running on the members.'),
            required=True,
            constraints=[
                constraints.Range(0, 65535),
            ]
        ),
        MEMBERS: properties.Schema(
            properties.Schema.LIST,
            _('The list of Nova server IDs load balanced.'),
            update_allowed=True
        ),
    }

    default_client_name = 'neutron'

    def handle_create(self):
        pool = self.properties[self.POOL_ID]
        protocol_port = self.properties[self.PROTOCOL_PORT]

        for member in self.properties[self.MEMBERS] or []:
            address = self.client_plugin('nova').server_to_ipaddress(member)
            lb_member = self.client().create_member({
                'member': {
                    'pool_id': pool,
                    'address': address,
                    'protocol_port': protocol_port}})['member']
            self.data_set(member, lb_member['id'])

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        new_props = json_snippet.properties(self.properties_schema,
                                            self.context)

        # Valid use cases are:
        # - Membership controlled by members property in template
        # - Empty members property in template; membership controlled by
        #   "updates" triggered from autoscaling group.
        # Mixing the two will lead to undefined behaviour.
        if (self.MEMBERS in prop_diff and
                (self.properties[self.MEMBERS] is not None or
                 new_props[self.MEMBERS] is not None)):
            members = set(new_props[self.MEMBERS] or [])
            rd_members = self.data()
            old_members = set(six.iterkeys(rd_members))
            for member in old_members - members:
                member_id = rd_members[member]
                with self.client_plugin().ignore_not_found:
                    self.client().delete_member(member_id)
                self.data_delete(member)
            pool = self.properties[self.POOL_ID]
            protocol_port = self.properties[self.PROTOCOL_PORT]
            for member in members - old_members:
                address = self.client_plugin('nova').server_to_ipaddress(
                    member)
                lb_member = self.client().create_member({
                    'member': {
                        'pool_id': pool,
                        'address': address,
                        'protocol_port': protocol_port}})['member']
                self.data_set(member, lb_member['id'])

    def handle_delete(self):
        # FIXME(pshchelo): this deletes members in a tight loop,
        # so is prone to OverLimit bug similar to LP 1265937
        for member, member_id in self.data().items():
            with self.client_plugin().ignore_not_found:
                self.client().delete_member(member_id)
            self.data_delete(member)
コード例 #25
0
class SecurityGroup(neutron.NeutronResource):
    """A resource for managing Neutron security groups.

    Security groups are sets of IP filter rules that are applied to an
    instance's networking. They are project specific, and project members can
    edit the default rules for their group and add new rules sets. All projects
    have a "default" security group, which is applied to instances that have no
    other security group defined.
    """

    required_service_extension = 'security-group'

    support_status = support.SupportStatus(version='2014.1')

    PROPERTIES = (
        NAME,
        DESCRIPTION,
        RULES,
    ) = (
        'name',
        'description',
        'rules',
    )

    _RULE_KEYS = (
        RULE_DIRECTION,
        RULE_ETHERTYPE,
        RULE_PORT_RANGE_MIN,
        RULE_PORT_RANGE_MAX,
        RULE_PROTOCOL,
        RULE_REMOTE_MODE,
        RULE_REMOTE_GROUP_ID,
        RULE_REMOTE_IP_PREFIX,
    ) = (
        'direction',
        'ethertype',
        'port_range_min',
        'port_range_max',
        'protocol',
        'remote_mode',
        'remote_group_id',
        'remote_ip_prefix',
    )

    _rule_schema = {
        RULE_DIRECTION:
        properties.Schema(
            properties.Schema.STRING,
            _('The direction in which the security group rule is applied. '
              'For a compute instance, an ingress security group rule '
              'matches traffic that is incoming (ingress) for that '
              'instance. An egress rule is applied to traffic leaving '
              'the instance.'),
            default='ingress',
            constraints=[
                constraints.AllowedValues(['ingress', 'egress']),
            ]),
        RULE_ETHERTYPE:
        properties.Schema(properties.Schema.STRING,
                          _('Ethertype of the traffic.'),
                          default='IPv4',
                          constraints=[
                              constraints.AllowedValues(['IPv4', 'IPv6']),
                          ]),
        RULE_PORT_RANGE_MIN:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The minimum port number in the range that is matched by the '
              'security group rule. If the protocol is TCP or UDP, this '
              'value must be less than or equal to the value of the '
              'port_range_max attribute. If the protocol is ICMP, this '
              'value must be an ICMP type.'),
            constraints=[constraints.Range(0, 65535)]),
        RULE_PORT_RANGE_MAX:
        properties.Schema(
            properties.Schema.INTEGER,
            _('The maximum port number in the range that is matched by the '
              'security group rule. The port_range_min attribute constrains '
              'the port_range_max attribute. If the protocol is ICMP, this '
              'value must be an ICMP type.'),
            constraints=[constraints.Range(0, 65535)]),
        RULE_PROTOCOL:
        properties.Schema(
            properties.Schema.STRING,
            _('The protocol that is matched by the security group rule. '
              'Valid values include tcp, udp, and icmp.')),
        RULE_REMOTE_MODE:
        properties.Schema(
            properties.Schema.STRING,
            _('Whether to specify a remote group or a remote IP prefix.'),
            default='remote_ip_prefix',
            constraints=[
                constraints.AllowedValues(
                    ['remote_ip_prefix', 'remote_group_id']),
            ]),
        RULE_REMOTE_GROUP_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The remote group ID to be associated with this security group '
              'rule. If no value is specified then this rule will use this '
              'security group for the remote_group_id. The remote mode '
              'parameter must be set to "remote_group_id".')),
        RULE_REMOTE_IP_PREFIX:
        properties.Schema(
            properties.Schema.STRING,
            _('The remote IP prefix (CIDR) to be associated with this '
              'security group rule.'),
            constraints=[constraints.CustomConstraint('net_cidr')]),
    }

    properties_schema = {
        NAME:
        properties.Schema(
            properties.Schema.STRING,
            _('A string specifying a symbolic name for the security group, '
              'which is not required to be unique.'),
            update_allowed=True),
        DESCRIPTION:
        properties.Schema(properties.Schema.STRING,
                          _('Description of the security group.'),
                          update_allowed=True),
        RULES:
        properties.Schema(properties.Schema.LIST,
                          _('List of security group rules.'),
                          default=[],
                          schema=properties.Schema(properties.Schema.MAP,
                                                   schema=_rule_schema),
                          update_allowed=True),
    }

    default_egress_rules = [{
        "direction": "egress",
        "ethertype": "IPv4"
    }, {
        "direction": "egress",
        "ethertype": "IPv6"
    }]

    def _show_resource(self):
        return self.client().show_security_group(
            self.resource_id)['security_group']

    def validate(self):
        super(SecurityGroup, self).validate()
        if self.properties[self.NAME] == 'default':
            msg = _('Security groups cannot be assigned the name "default".')
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        rules = props.pop(self.RULES, [])

        sec = self.client().create_security_group({'security_group':
                                                   props})['security_group']

        self.resource_id_set(sec['id'])
        self._create_rules(rules)

    def _format_rule(self, r):
        rule = dict(r)
        rule['security_group_id'] = self.resource_id

        if 'remote_mode' in rule:
            remote_mode = rule.get(self.RULE_REMOTE_MODE)
            del (rule[self.RULE_REMOTE_MODE])

            if remote_mode == self.RULE_REMOTE_GROUP_ID:
                rule[self.RULE_REMOTE_IP_PREFIX] = None
                if not rule.get(self.RULE_REMOTE_GROUP_ID):
                    # if remote group is not specified then make this
                    # a self-referencing rule
                    rule[self.RULE_REMOTE_GROUP_ID] = self.resource_id
            else:
                rule[self.RULE_REMOTE_GROUP_ID] = None

        for key in (self.RULE_PORT_RANGE_MIN, self.RULE_PORT_RANGE_MAX):
            if rule.get(key) is not None:
                rule[key] = str(rule[key])
        return rule

    def _create_rules(self, rules):
        egress_deleted = False

        for i in rules:
            if i[self.RULE_DIRECTION] == 'egress' and not egress_deleted:
                # There is at least one egress rule, so delete the default
                # rules which allow all egress traffic
                egress_deleted = True

                def is_egress(rule):
                    return rule[self.RULE_DIRECTION] == 'egress'

                self._delete_rules(is_egress)

            rule = self._format_rule(i)

            try:
                self.client().create_security_group_rule(
                    {'security_group_rule': rule})
            except Exception as ex:
                if not self.client_plugin().is_conflict(ex):
                    raise

    def _delete_rules(self, to_delete=None):
        try:
            sec = self.client().show_security_group(
                self.resource_id)['security_group']
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            for rule in sec['security_group_rules']:
                if to_delete is None or to_delete(rule):
                    try:
                        self.client().delete_security_group_rule(rule['id'])
                    except Exception as ex:
                        self.client_plugin().ignore_not_found(ex)

    def handle_delete(self):
        if self.resource_id is None:
            return

        self._delete_rules()
        with self.client_plugin().ignore_not_found:
            self.client().delete_security_group(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        # handle rules changes by:
        # * deleting all rules
        # * restoring the default egress rules
        # * creating new rules
        rules = None
        if self.RULES in prop_diff:
            rules = prop_diff.pop(self.RULES)
            self._delete_rules()
            self._create_rules(self.default_egress_rules)

        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_security_group(self.resource_id,
                                                {'security_group': prop_diff})
        if rules:
            self._create_rules(rules)