コード例 #1
0
class VolumeAttachment(vb.BaseVolumeAttachment):
    PROPERTIES = (
        INSTANCE_ID,
        VOLUME_ID,
        DEVICE,
    ) = (
        'InstanceId',
        'VolumeId',
        'Device',
    )

    properties_schema = {
        INSTANCE_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the instance to which the volume attaches.'),
            immutable=True,
            required=True,
            constraints=[constraints.CustomConstraint('nova.server')]),
        VOLUME_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of the volume to be attached.'),
            immutable=True,
            required=True,
            constraints=[constraints.CustomConstraint('cinder.volume')]),
        DEVICE:
        properties.Schema(
            properties.Schema.STRING,
            _('The device where the volume is exposed on the instance. This '
              'assignment may not be honored and it is advised that the path '
              '/dev/disk/by-id/virtio-<VolumeId> be used instead.'),
            immutable=True,
            required=True,
            constraints=[
                constraints.AllowedPattern('/dev/vd[b-z]'),
            ]),
    }
コード例 #2
0
class KeystoneGroupRoleAssignment(resource.Resource,
                                  KeystoneRoleAssignmentMixin):
    """Resource for granting roles to a group.

    Resource for specifying groups and their's roles.
    """

    support_status = support.SupportStatus(
        version='5.0.0', message=_('Supported versions: keystone v3'))

    default_client_name = 'keystone'

    PROPERTIES = (GROUP, ) = ('group', )

    properties_schema = {
        GROUP:
        properties.Schema(
            properties.Schema.STRING,
            _('Name or id of keystone group.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.CustomConstraint('keystone.group')])
    }

    properties_schema.update(
        KeystoneRoleAssignmentMixin.mixin_properties_schema)

    def __init__(self, *args, **kwargs):
        super(KeystoneGroupRoleAssignment, self).__init__(*args, **kwargs)

    @property
    def group_id(self):
        return (self.client_plugin().get_group_id(
            self.properties.get(self.GROUP)))

    def handle_create(self):
        self.create_assignment(group_id=self.group_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        self.update_assignment(group_id=self.group_id, prop_diff=prop_diff)

    def handle_delete(self):
        self.delete_assignment(group_id=self.group_id)

    def validate(self):
        super(KeystoneGroupRoleAssignment, self).validate()
        self.validate_assignment_properties()
コード例 #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 QoSRule(neutron.NeutronResource):
    """A resource for Neutron QoS base rule."""

    required_service_extension = 'qos'

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

    PROPERTIES = (
        POLICY, TENANT_ID,
    ) = (
        'policy', 'tenant_id',
    )

    properties_schema = {
        POLICY: properties.Schema(
            properties.Schema.STRING,
            _('ID or name of the QoS policy.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.qos_policy')]
        ),
        TENANT_ID: properties.Schema(
            properties.Schema.STRING,
            _('The owner tenant ID of this rule.')
        ),
    }

    def __init__(self, name, json_snippet, stack):
        super(QoSRule, self).__init__(name, json_snippet, stack)
        self._policy_id = None

    @property
    def policy_id(self):
        if not self._policy_id:
            self._policy_id = self.client_plugin().get_qos_policy_id(
                self.properties[self.POLICY])

        return self._policy_id
コード例 #5
0
ファイル: extraroute.py プロジェクト: Hybrid-Cloud/conveyor
class ExtraRoute(neutron.NeutronResource):
    """Resource for specifying extra routes for Neutron router.

    Resource allows to specify nexthop IP and destination network for router.
    """

    required_service_extension = 'extraroute'

    support_status = support.SupportStatus(
        status=support.UNSUPPORTED,
        message=_('Use this resource at your own risk.'))

    PROPERTIES = (
        ROUTER_ID, DESTINATION, NEXTHOP,
    ) = (
        'router_id', 'destination', 'nexthop',
    )

    properties_schema = {
        ROUTER_ID: properties.Schema(
            properties.Schema.STRING,
            description=_('The router id.'),
            required=True,
            constraints=[
                constraints.CustomConstraint('neutron.router')
            ]
        ),
        DESTINATION: properties.Schema(
            properties.Schema.STRING,
            description=_('Network in CIDR notation.'),
            required=True),
        NEXTHOP: properties.Schema(
            properties.Schema.STRING,
            description=_('Nexthop IP address.'),
            required=True)
    }

    def add_dependencies(self, deps):
        super(ExtraRoute, self).add_dependencies(deps)
        for resource in six.itervalues(self.stack):
            # depend on any RouterInterface in this template with the same
            # router_id as this router_id
            if resource.has_interface('OS::Neutron::RouterInterface'):
                dep_router_id = self.client_plugin().resolve_router({
                    'router': resource.properties.get('router'),
                    'router_id': None}, 'router', 'router_id')
                router_id = self.properties[self.ROUTER_ID]
                if dep_router_id == router_id:
                    deps += (self, resource)
            # depend on any RouterGateway in this template with the same
            # router_id as this router_id
            elif (resource.has_interface('OS::Neutron::RouterGateway') and
                  resource.properties['router_id'] ==
                    self.properties['router_id']):
                        deps += (self, resource)

    def handle_create(self):
        router_id = self.properties.get(self.ROUTER_ID)
        routes = self.client().show_router(
            router_id).get('router').get('routes')
        if not routes:
            routes = []
        new_route = {'destination': self.properties[self.DESTINATION],
                     'nexthop': self.properties[self.NEXTHOP]}
        if new_route in routes:
            msg = _('Route duplicates an existing route.')
            raise exception.Error(msg)
        routes.append(new_route)
        self.client().update_router(router_id,
                                    {'router': {'routes': routes}})
        new_route['router_id'] = router_id
        self.resource_id_set(
            '%(router_id)s:%(destination)s:%(nexthop)s' % new_route)

    def handle_delete(self):
        if not self.resource_id:
            return
        (router_id, destination, nexthop) = self.resource_id.split(':')
        with self.client_plugin().ignore_not_found:
            routes = self.client().show_router(
                router_id).get('router').get('routes', [])
            try:
                routes.remove({'destination': destination,
                               'nexthop': nexthop})
            except ValueError:
                return
            self.client().update_router(router_id,
                                        {'router': {'routes': routes}})
コード例 #6
0
class Pool(neutron.NeutronResource):
    """A resource for managing load balancer pools in Neutron.

    A load balancing pool is a logical set of devices, such as web servers,
    that you group together to receive and process traffic. The loadbalancing
    function chooses a member of the pool according to the configured load
    balancing method to handle the new requests or connections received on the
    VIP address. There is only one pool for a VIP.
    """

    required_service_extension = 'lbaas'

    PROPERTIES = (
        PROTOCOL, SUBNET_ID, SUBNET, LB_METHOD, NAME, DESCRIPTION,
        ADMIN_STATE_UP, VIP, MONITORS, PROVIDER,
    ) = (
        'protocol', 'subnet_id', 'subnet', 'lb_method', 'name', 'description',
        'admin_state_up', 'vip', 'monitors', 'provider',
    )

    _VIP_KEYS = (
        VIP_NAME, VIP_DESCRIPTION, VIP_SUBNET, VIP_ADDRESS,
        VIP_CONNECTION_LIMIT, VIP_PROTOCOL_PORT,
        VIP_SESSION_PERSISTENCE, VIP_ADMIN_STATE_UP,
    ) = (
        'name', 'description', 'subnet', 'address',
        'connection_limit', 'protocol_port',
        'session_persistence', 'admin_state_up',
    )

    _VIP_SESSION_PERSISTENCE_KEYS = (
        VIP_SESSION_PERSISTENCE_TYPE, VIP_SESSION_PERSISTENCE_COOKIE_NAME,
    ) = (
        'type', 'cookie_name',
    )

    ATTRIBUTES = (
        ADMIN_STATE_UP_ATTR, NAME_ATTR, PROTOCOL_ATTR, SUBNET_ID_ATTR,
        LB_METHOD_ATTR, DESCRIPTION_ATTR, TENANT_ID, VIP_ATTR, PROVIDER_ATTR,
    ) = (
        'admin_state_up', 'name', 'protocol', 'subnet_id',
        'lb_method', 'description', 'tenant_id', 'vip', 'provider',
    )

    properties_schema = {
        PROTOCOL: properties.Schema(
            properties.Schema.STRING,
            _('Protocol for balancing.'),
            required=True,
            constraints=[
                constraints.AllowedValues(['TCP', 'HTTP', 'HTTPS']),
            ]
        ),
        SUBNET_ID: properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='5.0.0',
                message=_('Use property %s.') % SUBNET,
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    version='2014.2'
                )
            ),
            constraints=[
                constraints.CustomConstraint('neutron.subnet')
            ]
        ),
        SUBNET: properties.Schema(
            properties.Schema.STRING,
            _('The subnet for the port on which the members '
              'of the pool will be connected.'),
            support_status=support.SupportStatus(version='2014.2'),
            required=True,
            constraints=[
                constraints.CustomConstraint('neutron.subnet')
            ]
        ),
        LB_METHOD: properties.Schema(
            properties.Schema.STRING,
            _('The algorithm used to distribute load between the members of '
              'the pool.'),
            required=True,
            constraints=[
                constraints.AllowedValues(['ROUND_ROBIN',
                                           'LEAST_CONNECTIONS', 'SOURCE_IP']),
            ],
            update_allowed=True
        ),
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('Name of the pool.')
        ),
        DESCRIPTION: properties.Schema(
            properties.Schema.STRING,
            _('Description of the pool.'),
            update_allowed=True
        ),
        ADMIN_STATE_UP: properties.Schema(
            properties.Schema.BOOLEAN,
            _('The administrative state of this pool.'),
            default=True,
            update_allowed=True
        ),
        PROVIDER: properties.Schema(
            properties.Schema.STRING,
            _('LBaaS provider to implement this load balancer instance.'),
            support_status=support.SupportStatus(version='5.0.0'),
            constraints=[
                constraints.CustomConstraint('neutron.lb.provider')
            ],
        ),
        VIP: properties.Schema(
            properties.Schema.MAP,
            _('IP address and port of the pool.'),
            schema={
                VIP_NAME: properties.Schema(
                    properties.Schema.STRING,
                    _('Name of the vip.')
                ),
                VIP_DESCRIPTION: properties.Schema(
                    properties.Schema.STRING,
                    _('Description of the vip.')
                ),
                VIP_SUBNET: properties.Schema(
                    properties.Schema.STRING,
                    _('Subnet of the vip.'),
                    constraints=[
                        constraints.CustomConstraint('neutron.subnet')
                    ]
                ),
                VIP_ADDRESS: properties.Schema(
                    properties.Schema.STRING,
                    _('IP address of the vip.'),
                    constraints=[
                        constraints.CustomConstraint('ip_addr')
                    ]
                ),
                VIP_CONNECTION_LIMIT: properties.Schema(
                    properties.Schema.INTEGER,
                    _('The maximum number of connections per second '
                      'allowed for the vip.')
                ),
                VIP_PROTOCOL_PORT: properties.Schema(
                    properties.Schema.INTEGER,
                    _('TCP port on which to listen for client traffic '
                      'that is associated with the vip address.'),
                    required=True
                ),
                VIP_SESSION_PERSISTENCE: properties.Schema(
                    properties.Schema.MAP,
                    _('Configuration of session persistence.'),
                    schema={
                        VIP_SESSION_PERSISTENCE_TYPE: properties.Schema(
                            properties.Schema.STRING,
                            _('Method of implementation of session '
                              'persistence feature.'),
                            required=True,
                            constraints=[constraints.AllowedValues(
                                ['SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE']
                            )]
                        ),
                        VIP_SESSION_PERSISTENCE_COOKIE_NAME: properties.Schema(
                            properties.Schema.STRING,
                            _('Name of the cookie, '
                              'required if type is APP_COOKIE.')
                        )
                    }
                ),
                VIP_ADMIN_STATE_UP: properties.Schema(
                    properties.Schema.BOOLEAN,
                    _('The administrative state of this vip.'),
                    default=True
                ),
            },
            required=True
        ),
        MONITORS: properties.Schema(
            properties.Schema.LIST,
            _('List of health monitors associated with the pool.'),
            default=[],
            update_allowed=True
        ),
    }

    attributes_schema = {
        ADMIN_STATE_UP_ATTR: attributes.Schema(
            _('The administrative state of this pool.'),
            type=attributes.Schema.STRING
        ),
        NAME_ATTR: attributes.Schema(
            _('Name of the pool.'),
            type=attributes.Schema.STRING
        ),
        PROTOCOL_ATTR: attributes.Schema(
            _('Protocol to balance.'),
            type=attributes.Schema.STRING
        ),
        SUBNET_ID_ATTR: attributes.Schema(
            _('The subnet for the port on which the members of the pool '
              'will be connected.'),
            type=attributes.Schema.STRING
        ),
        LB_METHOD_ATTR: attributes.Schema(
            _('The algorithm used to distribute load between the members '
              'of the pool.'),
            type=attributes.Schema.STRING
        ),
        DESCRIPTION_ATTR: attributes.Schema(
            _('Description of the pool.'),
            type=attributes.Schema.STRING
        ),
        TENANT_ID: attributes.Schema(
            _('Tenant owning the pool.'),
            type=attributes.Schema.STRING
        ),
        VIP_ATTR: attributes.Schema(
            _('Vip associated with the pool.'),
            type=attributes.Schema.MAP
        ),
        PROVIDER_ATTR: attributes.Schema(
            _('Provider implementing this load balancer instance.'),
            support_status=support.SupportStatus(version='5.0.0'),
            type=attributes.Schema.STRING,
        ),
    }

    def translation_rules(self, props):
        return [
            translation.TranslationRule(
                props,
                translation.TranslationRule.REPLACE,
                [self.SUBNET],
                value_path=[self.SUBNET_ID]
            ),
            translation.TranslationRule(
                props,
                translation.TranslationRule.RESOLVE,
                [self.SUBNET],
                client_plugin=self.client_plugin(),
                finder='find_resourceid_by_name_or_id',
                entity='subnet'
            ),
            translation.TranslationRule(
                props,
                translation.TranslationRule.RESOLVE,
                [self.VIP, self.VIP_SUBNET],
                client_plugin=self.client_plugin(),
                finder='find_resourceid_by_name_or_id',
                entity='subnet'
            )
        ]

    def validate(self):
        res = super(Pool, self).validate()
        if res:
            return res
        session_p = self.properties[self.VIP].get(self.VIP_SESSION_PERSISTENCE)
        if session_p is None:
            # session persistence is not configured, skip validation
            return

        persistence_type = session_p[self.VIP_SESSION_PERSISTENCE_TYPE]
        if persistence_type == 'APP_COOKIE':
            if session_p.get(self.VIP_SESSION_PERSISTENCE_COOKIE_NAME):
                return

            msg = _('Property cookie_name is required, when '
                    'session_persistence type is set to APP_COOKIE.')
            raise exception.StackValidationFailed(message=msg)

    def handle_create(self):
        properties = self.prepare_properties(
            self.properties,
            self.physical_resource_name())
        subnet_id = properties.pop(self.SUBNET)
        properties['subnet_id'] = subnet_id
        vip_properties = properties.pop(self.VIP)
        monitors = properties.pop(self.MONITORS)

        pool = self.client().create_pool({'pool': properties})['pool']
        self.resource_id_set(pool['id'])

        for monitor in monitors:
            self.client().associate_health_monitor(
                pool['id'], {'health_monitor': {'id': monitor}})

        vip_arguments = self.prepare_properties(
            vip_properties,
            '%s.vip' % (self.name,))

        session_p = vip_arguments.get(self.VIP_SESSION_PERSISTENCE)
        if session_p is not None:
            prepared_props = self.prepare_properties(session_p, None)
            vip_arguments['session_persistence'] = prepared_props

        vip_arguments['protocol'] = self.properties[self.PROTOCOL]

        if vip_arguments.get(self.VIP_SUBNET) is None:
            vip_arguments['subnet_id'] = subnet_id
        else:
            vip_arguments['subnet_id'] = vip_arguments.pop(self.VIP_SUBNET)

        vip_arguments['pool_id'] = pool['id']
        vip = self.client().create_vip({'vip': vip_arguments})['vip']

        self.metadata_set({'vip': vip['id']})

    def _show_resource(self):
        return self.client().show_pool(self.resource_id)['pool']

    def check_create_complete(self, data):
        attributes = self._show_resource()
        status = attributes['status']
        if status == 'PENDING_CREATE':
            return False
        elif status == 'ACTIVE':
            vip_attributes = self.client().show_vip(
                self.metadata_get()['vip'])['vip']
            vip_status = vip_attributes['status']
            if vip_status == 'PENDING_CREATE':
                return False
            if vip_status == 'ACTIVE':
                return True
            if vip_status == 'ERROR':
                raise exception.ResourceInError(
                    resource_status=vip_status,
                    status_reason=_('error in vip'))
            raise exception.ResourceUnknownStatus(
                resource_status=vip_status,
                result=_('Pool creation failed due to vip'))
        elif status == 'ERROR':
            raise exception.ResourceInError(
                resource_status=status,
                status_reason=_('error in pool'))
        else:
            raise exception.ResourceUnknownStatus(
                resource_status=status,
                result=_('Pool creation failed'))

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            if self.MONITORS in prop_diff:
                monitors = set(prop_diff.pop(self.MONITORS))
                old_monitors = set(self.properties[self.MONITORS])
                for monitor in old_monitors - monitors:
                    self.client().disassociate_health_monitor(
                        self.resource_id, monitor)
                for monitor in monitors - old_monitors:
                    self.client().associate_health_monitor(
                        self.resource_id, {'health_monitor': {'id': monitor}})

            if prop_diff:
                self.client().update_pool(self.resource_id,
                                          {'pool': prop_diff})

    def _resolve_attribute(self, name):
        if name == self.VIP_ATTR:
            return self.client().show_vip(self.metadata_get()['vip'])['vip']
        return super(Pool, self)._resolve_attribute(name)

    def handle_delete(self):
        if not self.resource_id:
            prg = progress.PoolDeleteProgress(True)
            return prg

        prg = progress.PoolDeleteProgress()
        if not self.metadata_get():
            prg.vip['delete_called'] = True
            prg.vip['deleted'] = True
        return prg

    def _delete_vip(self):
        return self._not_found_in_call(
            self.client().delete_vip, self.metadata_get()['vip'])

    def _check_vip_deleted(self):
        return self._not_found_in_call(
            self.client().show_vip, self.metadata_get()['vip'])

    def _delete_pool(self):
        return self._not_found_in_call(
            self.client().delete_pool, self.resource_id)

    def check_delete_complete(self, prg):
        if not prg.vip['delete_called']:
            prg.vip['deleted'] = self._delete_vip()
            prg.vip['delete_called'] = True
            return False
        if not prg.vip['deleted']:
            prg.vip['deleted'] = self._check_vip_deleted()
            return False
        if not prg.pool['delete_called']:
            prg.pool['deleted'] = self._delete_pool()
            prg.pool['delete_called'] = True
            return prg.pool['deleted']
        if not prg.pool['deleted']:
            prg.pool['deleted'] = super(Pool, self).check_delete_complete(True)
            return prg.pool['deleted']
        return True
コード例 #7
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
コード例 #8
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()
コード例 #9
0
class RouterInterface(neutron.NeutronResource):
    """A resource for managing Neutron router interfaces.

    Router interfaces associate routers with existing subnets or ports.
    """

    required_service_extension = 'router'

    PROPERTIES = (ROUTER, ROUTER_ID, SUBNET_ID, SUBNET, PORT_ID,
                  PORT) = ('router', 'router_id', 'subnet_id', 'subnet',
                           'port_id', 'port')

    properties_schema = {
        ROUTER:
        properties.Schema(
            properties.Schema.STRING,
            _('The router.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.router')],
        ),
        ROUTER_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('ID of the router.'),
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='6.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    message=_('Use property %s.') % ROUTER,
                    version='2015.1',
                    previous_status=support.SupportStatus(version='2013.1'))),
            constraints=[constraints.CustomConstraint('neutron.router')],
        ),
        SUBNET_ID:
        properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                message=_('Use property %s.') % SUBNET,
                version='5.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED, version='2014.2')),
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
        SUBNET:
        properties.Schema(
            properties.Schema.STRING,
            _('The subnet, either subnet or port should be '
              'specified.'),
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
        PORT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The port id, either subnet or port_id should be specified.'),
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='6.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    message=_('Use property %s.') % PORT,
                    version='2015.1',
                    previous_status=support.SupportStatus(version='2014.1'))),
            constraints=[constraints.CustomConstraint('neutron.port')]),
        PORT:
        properties.Schema(
            properties.Schema.STRING,
            _('The port, either subnet or port should be specified.'),
            support_status=support.SupportStatus(version='2015.1'),
            constraints=[constraints.CustomConstraint('neutron.port')])
    }

    def translation_rules(self, props):
        return [
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.PORT],
                                        value_path=[self.PORT_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.ROUTER],
                                        value_path=[self.ROUTER_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.SUBNET],
                                        value_path=[self.SUBNET_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.PORT],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='port'),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.ROUTER],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='router'),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.SUBNET],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='subnet')
        ]

    def validate(self):
        """Validate any of the provided params."""
        super(RouterInterface, self).validate()

        prop_subnet_exists = self.properties.get(self.SUBNET) is not None

        prop_port_exists = self.properties.get(self.PORT) is not None

        if prop_subnet_exists and prop_port_exists:
            raise exception.ResourcePropertyConflict(self.SUBNET, self.PORT)

        if not prop_subnet_exists and not prop_port_exists:
            raise exception.PropertyUnspecifiedError(self.SUBNET, self.PORT)

    def handle_create(self):
        router_id = dict(self.properties).get(self.ROUTER)
        key = 'subnet_id'
        value = dict(self.properties).get(self.SUBNET)
        if not value:
            key = 'port_id'
            value = dict(self.properties).get(self.PORT)
        self.client().add_interface_router(router_id, {key: value})
        self.resource_id_set('%s:%s=%s' % (router_id, key, value))

    def handle_delete(self):
        if not self.resource_id:
            return
        tokens = self.resource_id.replace('=', ':').split(':')
        if len(tokens) == 2:  # compatible with old data
            tokens.insert(1, 'subnet_id')
        (router_id, key, value) = tokens
        with self.client_plugin().ignore_not_found:
            self.client().remove_interface_router(router_id, {key: value})
コード例 #10
0
ファイル: endpoint.py プロジェクト: Hybrid-Cloud/conveyor
class KeystoneEndpoint(resource.Resource):
    """Heat Template Resource for Keystone Service Endpoint.

    Keystone endpoint is just the URL that can be used for accessing a service
    within OpenStack. Endpoint can be accessed by admin, by services or public,
    i.e. everyone can use this endpoint.
    """

    support_status = support.SupportStatus(
        version='5.0.0', message=_('Supported versions: keystone v3'))

    default_client_name = 'keystone'

    entity = 'endpoints'

    PROPERTIES = (
        NAME,
        REGION,
        SERVICE,
        INTERFACE,
        SERVICE_URL,
        ENABLED,
    ) = (
        'name',
        'region',
        'service',
        'interface',
        'url',
        'enabled',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('Name of keystone endpoint.'),
                          update_allowed=True),
        REGION:
        properties.Schema(
            properties.Schema.STRING,
            _('Name or Id of keystone region.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('keystone.region')]),
        SERVICE:
        properties.Schema(
            properties.Schema.STRING,
            _('Name or Id of keystone service.'),
            update_allowed=True,
            required=True,
            constraints=[constraints.CustomConstraint('keystone.service')]),
        INTERFACE:
        properties.Schema(properties.Schema.STRING,
                          _('Interface type of keystone service endpoint.'),
                          update_allowed=True,
                          required=True,
                          constraints=[
                              constraints.AllowedValues(
                                  ['public', 'internal', 'admin'])
                          ]),
        SERVICE_URL:
        properties.Schema(properties.Schema.STRING,
                          _('URL of keystone service endpoint.'),
                          update_allowed=True,
                          required=True),
        ENABLED:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('This endpoint is enabled or disabled.'),
            default=True,
            update_allowed=True,
            support_status=support.SupportStatus(version='6.0.0'))
    }

    def client(self):
        return super(KeystoneEndpoint, self).client().client

    def handle_create(self):
        region = self.properties[self.REGION]
        service = self.properties[self.SERVICE]
        interface = self.properties[self.INTERFACE]
        url = self.properties[self.SERVICE_URL]
        name = (self.properties[self.NAME] or self.physical_resource_name())
        enabled = self.properties[self.ENABLED]

        endpoint = self.client().endpoints.create(region=region,
                                                  service=service,
                                                  interface=interface,
                                                  url=url,
                                                  name=name,
                                                  enabled=enabled)

        self.resource_id_set(endpoint.id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            region = prop_diff.get(self.REGION)
            service = prop_diff.get(self.SERVICE)
            interface = prop_diff.get(self.INTERFACE)
            url = prop_diff.get(self.SERVICE_URL)
            name = None
            # Don't update the name if no change
            if self.NAME in prop_diff:
                name = prop_diff[self.NAME] or self.physical_resource_name()
            enabled = prop_diff.get(self.ENABLED)

            self.client().endpoints.update(endpoint=self.resource_id,
                                           region=region,
                                           service=service,
                                           interface=interface,
                                           url=url,
                                           name=name,
                                           enabled=enabled)
コード例 #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 AddressScope(neutron.NeutronResource):
    """A resource for Neutron address scope.

    This resource can be associated with multiple subnet pools
    in a one-to-many relationship. The subnet pools under an
    address scope must not overlap.
    """

    required_service_extension = 'address-scope'

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

    PROPERTIES = (
        NAME,
        SHARED,
        TENANT_ID,
        IP_VERSION,
    ) = (
        'name',
        'shared',
        'tenant_id',
        'ip_version',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('The name for the address scope.'),
                          update_allowed=True),
        SHARED:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Whether the address scope should be shared to other '
              'tenants. Note that the default policy setting '
              'restricts usage of this attribute to administrative '
              'users only, and restricts changing of shared address scope '
              'to unshared with update.'),
            default=False,
            update_allowed=True),
        TENANT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The owner tenant ID of the address scope. Only '
              'administrative users can specify a tenant ID '
              'other than their own.'),
            constraints=[constraints.CustomConstraint('keystone.project')]),
        IP_VERSION:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Address family of the address scope, which is 4 or 6.'),
            default=4,
            constraints=[
                constraints.AllowedValues([4, 6]),
            ]),
    }

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

        address_scope = self.client().create_address_scope(
            {'address_scope': props})['address_scope']
        self.resource_id_set(address_scope['id'])

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

        with self.client_plugin().ignore_not_found:
            self.client().delete_address_scope(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_address_scope(self.resource_id,
                                               {'address_scope': prop_diff})

    def _show_resource(self):
        return self.client().show_address_scope(
            self.resource_id)['address_scope']
コード例 #13
0
ファイル: vpnservice.py プロジェクト: Hybrid-Cloud/conveyor
class VPNService(neutron.NeutronResource):
    """A resource for VPN service in Neutron.

    VPN service is a high level object that associates VPN with a specific
    subnet and router.
    """

    required_service_extension = 'vpnaas'

    PROPERTIES = (NAME, DESCRIPTION, ADMIN_STATE_UP, SUBNET_ID, SUBNET,
                  ROUTER_ID, ROUTER) = ('name', 'description',
                                        'admin_state_up', 'subnet_id',
                                        'subnet', 'router_id', 'router')

    ATTRIBUTES = (
        ADMIN_STATE_UP_ATTR,
        DESCRIPTION_ATTR,
        NAME_ATTR,
        ROUTER_ID_ATTR,
        STATUS,
        SUBNET_ID_ATTR,
        TENANT_ID,
    ) = (
        'admin_state_up',
        'description',
        'name',
        'router_id',
        'status',
        'subnet_id',
        'tenant_id',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('Name for the vpn service.'),
                          update_allowed=True),
        DESCRIPTION:
        properties.Schema(properties.Schema.STRING,
                          _('Description for the vpn service.'),
                          update_allowed=True),
        ADMIN_STATE_UP:
        properties.Schema(properties.Schema.BOOLEAN,
                          _('Administrative state for the vpn service.'),
                          default=True,
                          update_allowed=True),
        SUBNET_ID:
        properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                message=_('Use property %s.') % SUBNET,
                version='5.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED, version='2014.2')),
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
        SUBNET:
        properties.Schema(
            properties.Schema.STRING,
            _('Subnet in which the vpn service will be created.'),
            support_status=support.SupportStatus(version='2014.2'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
        ROUTER_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Unique identifier for the router to which the vpn service '
              'will be inserted.'),
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='6.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    message=_('Use property %s') % ROUTER,
                    version='2015.1',
                    previous_status=support.SupportStatus(version='2013.2'))),
            constraints=[constraints.CustomConstraint('neutron.router')]),
        ROUTER:
        properties.Schema(
            properties.Schema.STRING,
            _('The router to which the vpn service will be inserted.'),
            support_status=support.SupportStatus(version='2015.1'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.router')])
    }

    attributes_schema = {
        ADMIN_STATE_UP_ATTR:
        attributes.Schema(_('The administrative state of the vpn service.'),
                          type=attributes.Schema.STRING),
        DESCRIPTION_ATTR:
        attributes.Schema(_('The description of the vpn service.'),
                          type=attributes.Schema.STRING),
        NAME_ATTR:
        attributes.Schema(_('The name of the vpn service.'),
                          type=attributes.Schema.STRING),
        ROUTER_ID_ATTR:
        attributes.Schema(_(
            'The unique identifier of the router to which the vpn service '
            'was inserted.'),
                          type=attributes.Schema.STRING),
        STATUS:
        attributes.Schema(_('The status of the vpn service.'),
                          type=attributes.Schema.STRING),
        SUBNET_ID_ATTR:
        attributes.Schema(_(
            'The unique identifier of the subnet in which the vpn service '
            'was created.'),
                          type=attributes.Schema.STRING),
        TENANT_ID:
        attributes.Schema(
            _('The unique identifier of the tenant owning the vpn service.'),
            type=attributes.Schema.STRING),
    }

    def translation_rules(self, props):
        return [
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.SUBNET],
                                        value_path=[self.SUBNET_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.SUBNET],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='subnet'),
            translation.TranslationRule(props,
                                        translation.TranslationRule.REPLACE,
                                        [self.ROUTER],
                                        value_path=[self.ROUTER_ID]),
            translation.TranslationRule(props,
                                        translation.TranslationRule.RESOLVE,
                                        [self.ROUTER],
                                        client_plugin=self.client_plugin(),
                                        finder='find_resourceid_by_name_or_id',
                                        entity='router'),
        ]

    def _show_resource(self):
        return self.client().show_vpnservice(self.resource_id)['vpnservice']

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        props['subnet_id'] = props.pop(self.SUBNET)
        props['router_id'] = props.pop(self.ROUTER)
        vpnservice = self.client().create_vpnservice({'vpnservice':
                                                      props})['vpnservice']
        self.resource_id_set(vpnservice['id'])

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_vpnservice(self.resource_id,
                                            {'vpnservice': prop_diff})

    def handle_delete(self):
        try:
            self.client().delete_vpnservice(self.resource_id)
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            return True
コード例 #14
0
ファイル: vpnservice.py プロジェクト: Hybrid-Cloud/conveyor
class IPsecSiteConnection(neutron.NeutronResource):
    """A resource for IPsec site connection in Neutron.

    This resource has details for the site-to-site IPsec connection, including
    the peer CIDRs, MTU, peer address, DPD settings and status.
    """

    required_service_extension = 'vpnaas'

    PROPERTIES = (
        NAME,
        DESCRIPTION,
        PEER_ADDRESS,
        PEER_ID,
        PEER_CIDRS,
        MTU,
        DPD,
        PSK,
        INITIATOR,
        ADMIN_STATE_UP,
        IKEPOLICY_ID,
        IPSECPOLICY_ID,
        VPNSERVICE_ID,
    ) = (
        'name',
        'description',
        'peer_address',
        'peer_id',
        'peer_cidrs',
        'mtu',
        'dpd',
        'psk',
        'initiator',
        'admin_state_up',
        'ikepolicy_id',
        'ipsecpolicy_id',
        'vpnservice_id',
    )

    _DPD_KEYS = (
        DPD_ACTIONS,
        DPD_INTERVAL,
        DPD_TIMEOUT,
    ) = (
        'actions',
        'interval',
        'timeout',
    )

    ATTRIBUTES = (
        ADMIN_STATE_UP_ATTR,
        AUTH_MODE,
        DESCRIPTION_ATTR,
        DPD_ATTR,
        IKEPOLICY_ID_ATTR,
        INITIATOR_ATTR,
        IPSECPOLICY_ID_ATTR,
        MTU_ATTR,
        NAME_ATTR,
        PEER_ADDRESS_ATTR,
        PEER_CIDRS_ATTR,
        PEER_ID_ATTR,
        PSK_ATTR,
        ROUTE_MODE,
        STATUS,
        TENANT_ID,
        VPNSERVICE_ID_ATTR,
    ) = (
        'admin_state_up',
        'auth_mode',
        'description',
        'dpd',
        'ikepolicy_id',
        'initiator',
        'ipsecpolicy_id',
        'mtu',
        'name',
        'peer_address',
        'peer_cidrs',
        'peer_id',
        'psk',
        'route_mode',
        'status',
        'tenant_id',
        'vpnservice_id',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('Name for the ipsec site connection.'),
                          update_allowed=True),
        DESCRIPTION:
        properties.Schema(properties.Schema.STRING,
                          _('Description for the ipsec site connection.'),
                          update_allowed=True),
        PEER_ADDRESS:
        properties.Schema(
            properties.Schema.STRING,
            _('Remote branch router public IPv4 address or IPv6 address or '
              'FQDN.'),
            required=True),
        PEER_ID:
        properties.Schema(properties.Schema.STRING,
                          _('Remote branch router identity.'),
                          required=True),
        PEER_CIDRS:
        properties.Schema(
            properties.Schema.LIST,
            _('Remote subnet(s) in CIDR format.'),
            required=True,
            schema=properties.Schema(
                properties.Schema.STRING,
                constraints=[constraints.CustomConstraint('net_cidr')])),
        MTU:
        properties.Schema(
            properties.Schema.INTEGER,
            _('Maximum transmission unit size (in bytes) for the ipsec site '
              'connection.'),
            default=1500),
        DPD:
        properties.Schema(
            properties.Schema.MAP,
            _('Dead Peer Detection protocol configuration for the ipsec site '
              'connection.'),
            schema={
                DPD_ACTIONS:
                properties.Schema(properties.Schema.STRING,
                                  _('Controls DPD protocol mode.'),
                                  default='hold',
                                  constraints=[
                                      constraints.AllowedValues([
                                          'clear', 'disabled', 'hold',
                                          'restart', 'restart-by-peer'
                                      ]),
                                  ]),
                DPD_INTERVAL:
                properties.Schema(properties.Schema.INTEGER,
                                  _('Number of seconds for the DPD delay.'),
                                  default=30),
                DPD_TIMEOUT:
                properties.Schema(properties.Schema.INTEGER,
                                  _('Number of seconds for the DPD timeout.'),
                                  default=120),
            }),
        PSK:
        properties.Schema(
            properties.Schema.STRING,
            _('Pre-shared key string for the ipsec site connection.'),
            required=True),
        INITIATOR:
        properties.Schema(
            properties.Schema.STRING,
            _('Initiator state in lowercase for the ipsec site connection.'),
            default='bi-directional',
            constraints=[
                constraints.AllowedValues(['bi-directional', 'response-only']),
            ]),
        ADMIN_STATE_UP:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Administrative state for the ipsec site connection.'),
            default=True,
            update_allowed=True),
        IKEPOLICY_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Unique identifier for the ike policy associated with the '
              'ipsec site connection.'),
            required=True),
        IPSECPOLICY_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Unique identifier for the ipsec policy associated with the '
              'ipsec site connection.'),
            required=True),
        VPNSERVICE_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Unique identifier for the vpn service associated with the '
              'ipsec site connection.'),
            required=True),
    }

    attributes_schema = {
        ADMIN_STATE_UP_ATTR:
        attributes.Schema(
            _('The administrative state of the ipsec site connection.'),
            type=attributes.Schema.STRING),
        AUTH_MODE:
        attributes.Schema(
            _('The authentication mode of the ipsec site connection.'),
            type=attributes.Schema.STRING),
        DESCRIPTION_ATTR:
        attributes.Schema(_('The description of the ipsec site connection.'),
                          type=attributes.Schema.STRING),
        DPD_ATTR:
        attributes.Schema(_(
            'The dead peer detection protocol configuration of the ipsec '
            'site connection.'),
                          type=attributes.Schema.MAP),
        IKEPOLICY_ID_ATTR:
        attributes.Schema(_(
            'The unique identifier of ike policy associated with the ipsec '
            'site connection.'),
                          type=attributes.Schema.STRING),
        INITIATOR_ATTR:
        attributes.Schema(_('The initiator of the ipsec site connection.'),
                          type=attributes.Schema.STRING),
        IPSECPOLICY_ID_ATTR:
        attributes.Schema(_(
            'The unique identifier of ipsec policy associated with the '
            'ipsec site connection.'),
                          type=attributes.Schema.STRING),
        MTU_ATTR:
        attributes.Schema(_(
            'The maximum transmission unit size (in bytes) of the ipsec '
            'site connection.'),
                          type=attributes.Schema.STRING),
        NAME_ATTR:
        attributes.Schema(_('The name of the ipsec site connection.'),
                          type=attributes.Schema.STRING),
        PEER_ADDRESS_ATTR:
        attributes.Schema(_(
            'The remote branch router public IPv4 address or IPv6 address '
            'or FQDN.'),
                          type=attributes.Schema.STRING),
        PEER_CIDRS_ATTR:
        attributes.Schema(_(
            'The remote subnet(s) in CIDR format of the ipsec site '
            'connection.'),
                          type=attributes.Schema.LIST),
        PEER_ID_ATTR:
        attributes.Schema(_(
            'The remote branch router identity of the ipsec site '
            'connection.'),
                          type=attributes.Schema.STRING),
        PSK_ATTR:
        attributes.Schema(
            _('The pre-shared key string of the ipsec site connection.'),
            type=attributes.Schema.STRING),
        ROUTE_MODE:
        attributes.Schema(_('The route mode of the ipsec site connection.'),
                          type=attributes.Schema.STRING),
        STATUS:
        attributes.Schema(_('The status of the ipsec site connection.'),
                          type=attributes.Schema.STRING),
        TENANT_ID:
        attributes.Schema(_(
            'The unique identifier of the tenant owning the ipsec site '
            'connection.'),
                          type=attributes.Schema.STRING),
        VPNSERVICE_ID_ATTR:
        attributes.Schema(_(
            'The unique identifier of vpn service associated with the ipsec '
            'site connection.'),
                          type=attributes.Schema.STRING),
    }

    def _show_resource(self):
        return self.client().show_ipsec_site_connection(
            self.resource_id)['ipsec_site_connection']

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        ipsec_site_connection = self.client().create_ipsec_site_connection(
            {'ipsec_site_connection': props})['ipsec_site_connection']
        self.resource_id_set(ipsec_site_connection['id'])

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

    def handle_delete(self):
        try:
            self.client().delete_ipsec_site_connection(self.resource_id)
        except Exception as ex:
            self.client_plugin().ignore_not_found(ex)
        else:
            return True
コード例 #15
0
class AutoScalingGroup(instgrp.InstanceGroup, cooldown.CooldownMixin):

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

    PROPERTIES = (
        AVAILABILITY_ZONES,
        LAUNCH_CONFIGURATION_NAME,
        MAX_SIZE,
        MIN_SIZE,
        COOLDOWN,
        DESIRED_CAPACITY,
        HEALTH_CHECK_GRACE_PERIOD,
        HEALTH_CHECK_TYPE,
        LOAD_BALANCER_NAMES,
        VPCZONE_IDENTIFIER,
        TAGS,
        INSTANCE_ID,
    ) = (
        'AvailabilityZones',
        'LaunchConfigurationName',
        'MaxSize',
        'MinSize',
        'Cooldown',
        'DesiredCapacity',
        'HealthCheckGracePeriod',
        'HealthCheckType',
        'LoadBalancerNames',
        'VPCZoneIdentifier',
        'Tags',
        'InstanceId',
    )

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

    _UPDATE_POLICY_SCHEMA_KEYS = (ROLLING_UPDATE) = (
        'AutoScalingRollingUpdate')

    _ROLLING_UPDATE_SCHEMA_KEYS = (MIN_INSTANCES_IN_SERVICE, MAX_BATCH_SIZE,
                                   PAUSE_TIME) = ('MinInstancesInService',
                                                  'MaxBatchSize', 'PauseTime')

    ATTRIBUTES = (INSTANCE_LIST, ) = ('InstanceList', )

    properties_schema = {
        AVAILABILITY_ZONES:
        properties.Schema(properties.Schema.LIST,
                          _('Not Implemented.'),
                          required=True),
        LAUNCH_CONFIGURATION_NAME:
        properties.Schema(
            properties.Schema.STRING,
            _('The reference to a LaunchConfiguration resource.'),
            update_allowed=True),
        INSTANCE_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('The ID of an existing instance to use to '
              'create the Auto Scaling group. If specify this property, '
              'will create the group use an existing instance instead of '
              'a launch configuration.'),
            constraints=[constraints.CustomConstraint("nova.server")]),
        MAX_SIZE:
        properties.Schema(properties.Schema.INTEGER,
                          _('Maximum number of instances in the group.'),
                          required=True,
                          update_allowed=True),
        MIN_SIZE:
        properties.Schema(properties.Schema.INTEGER,
                          _('Minimum number of instances in the group.'),
                          required=True,
                          update_allowed=True),
        COOLDOWN:
        properties.Schema(properties.Schema.INTEGER,
                          _('Cooldown period, in seconds.'),
                          update_allowed=True),
        DESIRED_CAPACITY:
        properties.Schema(properties.Schema.INTEGER,
                          _('Desired initial number of instances.'),
                          update_allowed=True),
        HEALTH_CHECK_GRACE_PERIOD:
        properties.Schema(properties.Schema.INTEGER,
                          _('Not Implemented.'),
                          implemented=False),
        HEALTH_CHECK_TYPE:
        properties.Schema(properties.Schema.STRING,
                          _('Not Implemented.'),
                          constraints=[
                              constraints.AllowedValues(['EC2', 'ELB']),
                          ],
                          implemented=False),
        LOAD_BALANCER_NAMES:
        properties.Schema(properties.Schema.LIST,
                          _('List of LoadBalancer resources.')),
        VPCZONE_IDENTIFIER:
        properties.Schema(
            properties.Schema.LIST,
            _('Use only with Neutron, to list the internal subnet to '
              'which the instance will be attached; '
              'needed only if multiple exist; '
              'list length must be exactly 1.'),
            schema=properties.Schema(
                properties.Schema.STRING,
                _('UUID of the internal subnet to which the instance '
                  'will be attached.'))),
        TAGS:
        properties.Schema(properties.Schema.LIST,
                          _('Tags to attach to this group.'),
                          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),
                              },
                          )),
    }

    attributes_schema = {
        INSTANCE_LIST:
        attributes.Schema(_("A comma-delimited list of server ip addresses. "
                            "(Heat extension)."),
                          type=attributes.Schema.STRING),
    }

    rolling_update_schema = {
        MIN_INSTANCES_IN_SERVICE:
        properties.Schema(properties.Schema.INTEGER, default=0),
        MAX_BATCH_SIZE:
        properties.Schema(properties.Schema.INTEGER, default=1),
        PAUSE_TIME:
        properties.Schema(properties.Schema.STRING, default='PT0S')
    }

    update_policy_schema = {
        ROLLING_UPDATE:
        properties.Schema(properties.Schema.MAP, schema=rolling_update_schema)
    }

    def handle_create(self):
        return self.create_with_template(self.child_template())

    def _get_members(self, group_id):
        members = []
        for res in self.stack.iter_resources(cfg.CONF.max_nested_stack_depth):
            if (res.type() in ['OS::Nova::Server']
                    and res.status == res.COMPLETE):
                members.append({
                    'id': res.resource_id,
                    'name': res.name,
                    'group_id': group_id
                })

        return members

    def _add_scheduler(self, group_id):
        task_args = {
            'group_name': 'groupwatch',
            'job_name': group_id,
            'job_type': 'period',
            'trigger_type': 'SIMPLE_TRIGGER',
            'interval': 240,
            'cover_flag': 'true',
            'end_time': 4076884800000,
            'meta_data': {
                'group_id': group_id,
                'project_id': self.context.tenant_id
            }
        }

        rsp = self.client('scheduler').scheduler.create(**task_args)
        return rsp.get('job_id')

    def _create_groupwatch(self):
        if not cfg.CONF.FusionSphere.groupwatch_enable:
            return

        group_id = self.stack.resource_by_refid(self.FnGetRefId()).resource_id
        members = self._get_members(group_id)
        job_id = self._add_scheduler(group_id)
        kwargs = {
            'id': group_id,
            'name': self.name,
            'type': 'VM',
            'data': {
                'scheduler_job_id': job_id
            },
            'members': members
        }

        self.client('groupwatch').groups.create(**kwargs)

    def _make_launch_config_resource(self, name, props):
        lc_res_type = 'AWS::AutoScaling::LaunchConfiguration'
        lc_res_def = rsrc_defn.ResourceDefinition(name, lc_res_type, props)
        lc_res = resource.Resource(name, lc_res_def, self.stack)
        return lc_res

    def _get_conf_properties(self):
        instance_id = self.properties.get(self.INSTANCE_ID)
        if instance_id:
            server = self.client_plugin('nova').get_server(instance_id)
            instance_props = {
                'ImageId': server.image['id'],
                'InstanceType': server.flavor['id'],
                'KeyName': server.key_name,
                'SecurityGroups':
                [sg['name'] for sg in server.security_groups]
            }
            conf = self._make_launch_config_resource(self.name, instance_props)
            props = function.resolve(conf.properties.data)
        else:
            conf, props = super(AutoScalingGroup, self)._get_conf_properties()

        vpc_zone_ids = self.properties.get(self.VPCZONE_IDENTIFIER)
        if vpc_zone_ids:
            props['SubnetId'] = vpc_zone_ids[0]

        return conf, props

    def check_create_complete(self, task):
        """Update cooldown timestamp after create succeeds."""
        done = super(AutoScalingGroup, self).check_create_complete(task)
        if done:
            self._create_groupwatch()
            self._finished_scaling(
                "%s : %s" %
                (sc_util.CFN_EXACT_CAPACITY, grouputils.get_size(self)))
        return done

    def check_update_complete(self, cookie):
        """Update the cooldown timestamp after update succeeds."""
        done = super(AutoScalingGroup, self).check_update_complete(cookie)
        if done:
            self._finished_scaling(
                "%s : %s" %
                (sc_util.CFN_EXACT_CAPACITY, grouputils.get_size(self)))
        return done

    def _get_new_capacity(self,
                          capacity,
                          adjustment,
                          adjustment_type=sc_util.CFN_EXACT_CAPACITY,
                          min_adjustment_step=None):
        lower = self.properties[self.MIN_SIZE]
        upper = self.properties[self.MAX_SIZE]
        return sc_util.calculate_new_capacity(capacity, adjustment,
                                              adjustment_type,
                                              min_adjustment_step, lower,
                                              upper)

    def _update_groupwatch(self):
        if not cfg.CONF.FusionSphere.groupwatch_enable:
            return

        group_id = self.stack.resource_by_refid(self.FnGetRefId()).resource_id
        members = self._get_members(group_id)
        kwargs = {
            'id': group_id,
            'name': self.name,
            'type': 'VM',
            'members': members
        }

        self.client('groupwatch').groups.update(group_id, **kwargs)

    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 tmpl_diff:
            # parse update policy
            if 'UpdatePolicy' in tmpl_diff:
                up = json_snippet.update_policy(self.update_policy_schema,
                                                self.context)
                self.update_policy = up

        self.properties = json_snippet.properties(self.properties_schema,
                                                  self.context)
        if prop_diff:
            # Replace instances first if launch configuration has changed
            self._try_rolling_update(prop_diff)

        # Update will happen irrespective of whether auto-scaling
        # is in progress or not.
        capacity = grouputils.get_size(self)
        desired_capacity = self.properties[self.DESIRED_CAPACITY] or capacity
        new_capacity = self._get_new_capacity(capacity, desired_capacity)
        self.resize(new_capacity)

    def adjust(self,
               adjustment,
               adjustment_type=sc_util.CFN_CHANGE_IN_CAPACITY,
               min_adjustment_step=None):
        """Adjust the size of the scaling group if the cooldown permits."""
        if not self._is_scaling_allowed():
            LOG.info(
                _LI("%(name)s NOT performing scaling adjustment, "
                    "cooldown %(cooldown)s"), {
                        'name': self.name,
                        'cooldown': self.properties[self.COOLDOWN]
                    })
            raise exception.NoActionRequired()

        capacity = grouputils.get_size(self)
        new_capacity = self._get_new_capacity(capacity, adjustment,
                                              adjustment_type,
                                              min_adjustment_step)

        changed_size = new_capacity != capacity
        # send a notification before, on-error and on-success.
        notif = {
            'stack': self.stack,
            'adjustment': adjustment,
            'adjustment_type': adjustment_type,
            'capacity': capacity,
            'groupname': self.FnGetRefId(),
            'message': _("Start resizing the group %(group)s") % {
                'group': self.FnGetRefId()
            },
            'suffix': 'start',
        }
        notification.send(**notif)
        try:
            self.resize(new_capacity)
        except Exception as resize_ex:
            with excutils.save_and_reraise_exception():
                try:
                    notif.update({
                        'suffix': 'error',
                        'message': six.text_type(resize_ex),
                        'capacity': grouputils.get_size(self),
                    })
                    notification.send(**notif)
                except Exception:
                    LOG.exception(_LE('Failed sending error notification'))
        else:
            notif.update({
                'suffix': 'end',
                'capacity': new_capacity,
                'message': _("End resizing the group %(group)s") % {
                    'group': notif['groupname']
                },
            })
            notification.send(**notif)
        finally:
            self._update_groupwatch()
            self._finished_scaling("%s : %s" % (adjustment_type, adjustment),
                                   changed_size=changed_size)
        return changed_size

    def _tags(self):
        """Add Identifying Tags to all servers in the group.

        This is so the Dimensions received from cfn-push-stats all include
        the groupname and stack id.
        Note: the group name must match what is returned from FnGetRefId
        """
        autoscaling_tag = [{
            self.TAG_KEY: 'metering.AutoScalingGroupName',
            self.TAG_VALUE: self.FnGetRefId()
        }]
        return super(AutoScalingGroup, self)._tags() + autoscaling_tag

    def validate(self):
        # check validity of group size
        min_size = self.properties[self.MIN_SIZE]
        max_size = self.properties[self.MAX_SIZE]

        if max_size < min_size:
            msg = _("MinSize can not be greater than MaxSize")
            raise exception.StackValidationFailed(message=msg)

        if min_size < 0:
            msg = _("The size of AutoScalingGroup can not be less than zero")
            raise exception.StackValidationFailed(message=msg)

        if self.properties[self.DESIRED_CAPACITY] is not None:
            desired_capacity = self.properties[self.DESIRED_CAPACITY]
            if desired_capacity < min_size or desired_capacity > max_size:
                msg = _("DesiredCapacity must be between MinSize and MaxSize")
                raise exception.StackValidationFailed(message=msg)

        # TODO(pasquier-s): once Neutron is able to assign subnets to
        # availability zones, it will be possible to specify multiple subnets.
        # For now, only one subnet can be specified. The bug #1096017 tracks
        # this issue.
        if (self.properties.get(self.VPCZONE_IDENTIFIER)
                and len(self.properties[self.VPCZONE_IDENTIFIER]) != 1):
            raise exception.NotSupported(feature=_("Anything other than one "
                                                   "VPCZoneIdentifier"))
        # validate properties InstanceId and LaunchConfigurationName
        # for aws auto scaling group.
        # should provide just only one of
        if self.type() == 'AWS::AutoScaling::AutoScalingGroup':
            instanceId = self.properties.get(self.INSTANCE_ID)
            launch_config = self.properties.get(self.LAUNCH_CONFIGURATION_NAME)
            if bool(instanceId) == bool(launch_config):
                msg = _("Either 'InstanceId' or 'LaunchConfigurationName' "
                        "must be provided.")
                raise exception.StackValidationFailed(message=msg)

        super(AutoScalingGroup, self).validate()

    def _resolve_attribute(self, name):
        """Resolves the resource's attributes.

        heat extension: "InstanceList" returns comma delimited list of server
        ip addresses.
        """
        if name == self.INSTANCE_LIST:
            return u','.join(
                inst.FnGetAtt('PublicIp')
                for inst in grouputils.get_members(self)) or None

    def child_template(self):
        if self.properties[self.DESIRED_CAPACITY]:
            num_instances = self.properties[self.DESIRED_CAPACITY]
        else:
            num_instances = self.properties[self.MIN_SIZE]
        return self._create_template(num_instances)

    def _delete_groupwatch(self):
        if not cfg.CONF.FusionSphere.groupwatch_enable:
            return

        if not self.resource_id:
            return

        group = None
        try:
            group = self.client('groupwatch').groups.get(self.resource_id)
        except Exception as ex:
            self.client_plugin('groupwatch').ignore_not_found(ex)
            return

        try:
            if (group and group.get('group') and 'data' in group.get('group')):
                scheduler_job_id = \
                    group.get('group').get('data').get('scheduler_job_id')
                self.client('scheduler').scheduler.delete(scheduler_job_id)
        except (AttributeError, KeyError):
            # do nothing
            pass
        except Exception as ex:
            self.client_plugin('scheduler').ignore_not_found(ex)

        try:
            self.client('groupwatch').groups.delete(self.resource_id)
        except Exception as ex:
            self.client_plugin('groupwatch').ignore_not_found(ex)

    def handle_delete(self):
        self._delete_groupwatch()
        return self.delete_nested()

    def handle_metadata_reset(self):
        metadata = self.metadata_get()
        if 'scaling_in_progress' in metadata:
            metadata['scaling_in_progress'] = False
            self.metadata_set(metadata)
コード例 #16
0
ファイル: eip.py プロジェクト: Hybrid-Cloud/conveyor
class ElasticIp(resource.Resource):
    PROPERTIES = (
        DOMAIN,
        INSTANCE_ID,
    ) = (
        'Domain',
        'InstanceId',
    )

    ATTRIBUTES = (ALLOCATION_ID, ) = ('AllocationId', )

    properties_schema = {
        DOMAIN:
        properties.Schema(
            properties.Schema.STRING,
            _('Set to "vpc" to have IP address allocation associated to your '
              'VPC.'),
            constraints=[
                constraints.AllowedValues(['vpc']),
            ]),
        INSTANCE_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Instance ID to associate with EIP.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('nova.server')]),
    }

    attributes_schema = {
        ALLOCATION_ID:
        attributes.Schema(_(
            'ID that AWS assigns to represent the allocation of the address '
            'for use with Amazon VPC. Returned only for VPC elastic IP '
            'addresses.'),
                          type=attributes.Schema.STRING),
    }

    default_client_name = 'nova'

    def __init__(self, name, json_snippet, stack):
        super(ElasticIp, self).__init__(name, json_snippet, stack)
        self.ipaddress = None

    def _ipaddress(self):
        if self.ipaddress is None and self.resource_id is not None:
            if self.properties[self.DOMAIN]:
                try:
                    ips = self.neutron().show_floatingip(self.resource_id)
                except Exception as ex:
                    self.client_plugin('neutron').ignore_not_found(ex)
                else:
                    self.ipaddress = ips['floatingip']['floating_ip_address']
            else:
                try:
                    ips = self.client().floating_ips.get(self.resource_id)
                except Exception as e:
                    self.client_plugin('nova').ignore_not_found(e)
                else:
                    self.ipaddress = ips.ip
        return self.ipaddress or ''

    def handle_create(self):
        """Allocate a floating IP for the current tenant."""
        ips = None
        if self.properties[self.DOMAIN]:
            ext_net = internet_gateway.InternetGateway.get_external_network_id(
                self.neutron())
            props = {'floating_network_id': ext_net}
            ips = self.neutron().create_floatingip({'floatingip':
                                                    props})['floatingip']
            self.ipaddress = ips['floating_ip_address']
            self.resource_id_set(ips['id'])
            LOG.info(_LI('ElasticIp create %s'), str(ips))
        else:
            try:
                ips = self.client().floating_ips.create()
            except Exception as e:
                with excutils.save_and_reraise_exception():
                    if self.client_plugin('nova').is_not_found(e):
                        LOG.error(
                            _LE("No default floating IP pool configured."
                                " Set 'default_floating_pool' in "
                                "nova.conf."))

            if ips:
                self.ipaddress = ips.ip
                self.resource_id_set(ips.id)
                LOG.info(_LI('ElasticIp create %s'), str(ips))

        instance_id = self.properties[self.INSTANCE_ID]
        if instance_id:
            server = self.client().servers.get(instance_id)
            server.add_floating_ip(self._ipaddress())

    def handle_delete(self):
        if self.resource_id is None:
            return
        # may be just create an eip when creation, or create the association
        # failed when creation, there will no association, if we attempt to
        # disassociate, an exception will raised, we need
        # to catch and ignore it, and then to deallocate the eip
        instance_id = self.properties[self.INSTANCE_ID]
        if instance_id:
            try:
                server = self.client().servers.get(instance_id)
                if server:
                    server.remove_floating_ip(self._ipaddress())
            except Exception as e:
                is_not_found = self.client_plugin('nova').is_not_found(e)
                is_unprocessable_entity = self.client_plugin(
                    'nova').is_unprocessable_entity(e)

                if (not is_not_found and not is_unprocessable_entity):
                    raise

        # deallocate the eip
        if self.properties[self.DOMAIN]:
            with self.client_plugin('neutron').ignore_not_found:
                self.neutron().delete_floatingip(self.resource_id)
        else:
            with self.client_plugin('nova').ignore_not_found:
                self.client().floating_ips.delete(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            if self.INSTANCE_ID in prop_diff:
                instance_id = prop_diff.get(self.INSTANCE_ID)
                if instance_id:
                    # no need to remove the floating ip from the old instance,
                    # nova does this automatically when calling
                    # add_floating_ip().
                    server = self.client().servers.get(instance_id)
                    server.add_floating_ip(self._ipaddress())
                else:
                    # to remove the floating_ip from the old instance
                    instance_id_old = self.properties[self.INSTANCE_ID]
                    if instance_id_old:
                        server = self.client().servers.get(instance_id_old)
                        server.remove_floating_ip(self._ipaddress())

    def get_reference_id(self):
        eip = self._ipaddress()
        if eip:
            return six.text_type(eip)
        else:
            return six.text_type(self.name)

    def _resolve_attribute(self, name):
        if name == self.ALLOCATION_ID:
            return six.text_type(self.resource_id)
コード例 #17
0
class KeystoneRegion(resource.Resource):
    """Heat Template Resource for Keystone Region.

    This plug-in helps to create, update and delete a keystone region. Also
    it can be used for enable or disable a given keystone region.
    """

    support_status = support.SupportStatus(
        version='6.0.0',
        message=_('Supported versions: keystone v3'))

    default_client_name = 'keystone'

    entity = 'regions'

    PROPERTIES = (
        ID, PARENT_REGION, DESCRIPTION, ENABLED
    ) = (
        'id', 'parent_region', 'description', 'enabled'
    )

    properties_schema = {
        ID: properties.Schema(
            properties.Schema.STRING,
            _('The user-defined region ID and should unique to the OpenStack '
              'deployment. While creating the region, heat will url encode '
              'this ID.')
        ),
        PARENT_REGION: properties.Schema(
            properties.Schema.STRING,
            _('If the region is hierarchically a child of another region, '
              'set this parameter to the ID of the parent region.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('keystone.region')]
        ),
        DESCRIPTION: properties.Schema(
            properties.Schema.STRING,
            _('Description of keystone region.'),
            update_allowed=True
        ),
        ENABLED: properties.Schema(
            properties.Schema.BOOLEAN,
            _('This region is enabled or disabled.'),
            default=True,
            update_allowed=True
        )
    }

    def client(self):
        return super(KeystoneRegion, self).client().client

    def handle_create(self):
        region_id = self.properties[self.ID]
        description = self.properties[self.DESCRIPTION]
        parent_region = self.properties[self.PARENT_REGION]
        enabled = self.properties[self.ENABLED]

        region = self.client().regions.create(
            id=parse.quote(region_id) if region_id else None,
            parent_region=parent_region,
            description=description,
            enabled=enabled)

        self.resource_id_set(region.id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            description = prop_diff.get(self.DESCRIPTION)
            enabled = prop_diff.get(self.ENABLED)
            parent_region = prop_diff.get(self.PARENT_REGION)

            self.client().regions.update(
                region=self.resource_id,
                parent_region=parent_region,
                description=description,
                enabled=enabled
            )
コード例 #18
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
        }
コード例 #19
0
class Router(neutron.NeutronResource):
    """A resource that implements Neutron router.

    Router is a physical or virtual network device that passes network traffic
    between different networks.
    """

    required_service_extension = 'router'

    PROPERTIES = (
        NAME,
        EXTERNAL_GATEWAY,
        VALUE_SPECS,
        ADMIN_STATE_UP,
        L3_AGENT_ID,
        L3_AGENT_IDS,
        DISTRIBUTED,
        HA,
    ) = ('name', 'external_gateway_info', 'value_specs', 'admin_state_up',
         'l3_agent_id', 'l3_agent_ids', 'distributed', 'ha')

    _EXTERNAL_GATEWAY_KEYS = (
        EXTERNAL_GATEWAY_NETWORK,
        EXTERNAL_GATEWAY_ENABLE_SNAT,
        EXTERNAL_GATEWAY_FIXED_IPS,
    ) = (
        'network',
        'enable_snat',
        'external_fixed_ips',
    )

    _EXTERNAL_GATEWAY_FIXED_IPS_KEYS = (IP_ADDRESS, SUBNET) = ('ip_address',
                                                               'subnet')

    ATTRIBUTES = (
        STATUS,
        EXTERNAL_GATEWAY_INFO_ATTR,
        NAME_ATTR,
        ADMIN_STATE_UP_ATTR,
        TENANT_ID,
    ) = (
        'status',
        'external_gateway_info',
        'name',
        'admin_state_up',
        'tenant_id',
    )

    properties_schema = {
        NAME:
        properties.Schema(properties.Schema.STRING,
                          _('The name of the router.'),
                          update_allowed=True),
        EXTERNAL_GATEWAY:
        properties.Schema(
            properties.Schema.MAP,
            _('External network gateway configuration for a router.'),
            schema={
                EXTERNAL_GATEWAY_NETWORK:
                properties.Schema(
                    properties.Schema.STRING,
                    _('ID or name of the external network for the gateway.'),
                    required=True,
                    update_allowed=True),
                EXTERNAL_GATEWAY_ENABLE_SNAT:
                properties.Schema(
                    properties.Schema.BOOLEAN,
                    _('Enables Source NAT on the router gateway. NOTE: The '
                      'default policy setting in Neutron restricts usage of '
                      'this property to administrative users only.'),
                    update_allowed=True),
                EXTERNAL_GATEWAY_FIXED_IPS:
                properties.Schema(
                    properties.Schema.LIST,
                    _('External fixed IP addresses for the gateway.'),
                    schema=properties.Schema(
                        properties.Schema.MAP,
                        schema={
                            IP_ADDRESS:
                            properties.Schema(
                                properties.Schema.STRING,
                                _('External fixed IP address.'),
                                constraints=[
                                    constraints.CustomConstraint('ip_addr'),
                                ]),
                            SUBNET:
                            properties.Schema(
                                properties.Schema.STRING,
                                _('Subnet of external fixed IP address.'),
                                constraints=[
                                    constraints.CustomConstraint(
                                        'neutron.subnet')
                                ]),
                        }),
                    update_allowed=True,
                    support_status=support.SupportStatus(version='6.0.0')),
            },
            update_allowed=True),
        VALUE_SPECS:
        properties.Schema(
            properties.Schema.MAP,
            _('Extra parameters to include in the creation request.'),
            default={},
            update_allowed=True),
        ADMIN_STATE_UP:
        properties.Schema(properties.Schema.BOOLEAN,
                          _('The administrative state of the router.'),
                          default=True,
                          update_allowed=True),
        L3_AGENT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('ID of the L3 agent. NOTE: The default policy setting in '
              'Neutron restricts usage of this property to administrative '
              'users only.'),
            update_allowed=True,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='6.0.0',
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED,
                    version='2015.1',
                    message=_('Use property %s.') % L3_AGENT_IDS,
                    previous_status=support.SupportStatus(version='2014.1'))),
        ),
        L3_AGENT_IDS:
        properties.Schema(
            properties.Schema.LIST,
            _('ID list of the L3 agent. User can specify multi-agents '
              'for highly available router. NOTE: The default policy '
              'setting in Neutron restricts usage of this property to '
              'administrative users only.'),
            schema=properties.Schema(properties.Schema.STRING, ),
            update_allowed=True,
            support_status=support.SupportStatus(version='2015.1')),
        DISTRIBUTED:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Indicates whether or not to create a distributed router. '
              'NOTE: The default policy setting in Neutron restricts usage '
              'of this property to administrative users only. This property '
              'can not be used in conjunction with the L3 agent ID.'),
            support_status=support.SupportStatus(version='2015.1')),
        HA:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Indicates whether or not to create a highly available router. '
              'NOTE: The default policy setting in Neutron restricts usage '
              'of this property to administrative users only. And now neutron '
              'do not support distributed and ha at the same time.'),
            support_status=support.SupportStatus(version='2015.1')),
    }

    attributes_schema = {
        STATUS:
        attributes.Schema(_("The status of the router."),
                          type=attributes.Schema.STRING),
        EXTERNAL_GATEWAY_INFO_ATTR:
        attributes.Schema(_("Gateway network for the router."),
                          type=attributes.Schema.MAP),
        NAME_ATTR:
        attributes.Schema(_("Friendly name of the router."),
                          type=attributes.Schema.STRING),
        ADMIN_STATE_UP_ATTR:
        attributes.Schema(_("Administrative state of the router."),
                          type=attributes.Schema.STRING),
        TENANT_ID:
        attributes.Schema(_("Tenant owning the router."),
                          type=attributes.Schema.STRING),
    }

    def translation_rules(self, props):
        rules = [
            translation.TranslationRule(
                props,
                translation.TranslationRule.RESOLVE,
                [self.EXTERNAL_GATEWAY, self.EXTERNAL_GATEWAY_NETWORK],
                client_plugin=self.client_plugin(),
                finder='find_resourceid_by_name_or_id',
                entity='network'),
            translation.TranslationRule(
                props,
                translation.TranslationRule.RESOLVE, [
                    self.EXTERNAL_GATEWAY, self.EXTERNAL_GATEWAY_FIXED_IPS,
                    self.SUBNET
                ],
                client_plugin=self.client_plugin(),
                finder='find_resourceid_by_name_or_id',
                entity='subnet')
        ]
        if props.get(self.L3_AGENT_ID):
            rules.extend([
                translation.TranslationRule(props,
                                            translation.TranslationRule.ADD,
                                            [self.L3_AGENT_IDS],
                                            [props.get(self.L3_AGENT_ID)]),
                translation.TranslationRule(props,
                                            translation.TranslationRule.DELETE,
                                            [self.L3_AGENT_ID])
            ])
        return rules

    def validate(self):
        super(Router, self).validate()
        is_distributed = self.properties[self.DISTRIBUTED]
        l3_agent_id = self.properties[self.L3_AGENT_ID]
        l3_agent_ids = self.properties[self.L3_AGENT_IDS]
        is_ha = self.properties[self.HA]
        if l3_agent_id and l3_agent_ids:
            raise exception.ResourcePropertyConflict(self.L3_AGENT_ID,
                                                     self.L3_AGENT_IDS)
        # do not specific l3 agent when creating a distributed router
        if is_distributed and (l3_agent_id or l3_agent_ids):
            raise exception.ResourcePropertyConflict(
                self.DISTRIBUTED,
                "/".join([self.L3_AGENT_ID, self.L3_AGENT_IDS]))
        if is_ha and is_distributed:
            raise exception.ResourcePropertyConflict(self.DISTRIBUTED, self.HA)
        if not is_ha and l3_agent_ids and len(l3_agent_ids) > 1:
            msg = _('Non HA routers can only have one L3 agent.')
            raise exception.StackValidationFailed(message=msg)

    def add_dependencies(self, deps):
        super(Router, self).add_dependencies(deps)
        external_gw = self.properties[self.EXTERNAL_GATEWAY]
        if external_gw:
            external_gw_net = external_gw.get(self.EXTERNAL_GATEWAY_NETWORK)
            for res in six.itervalues(self.stack):
                if res.has_interface('OS::Neutron::Subnet'):
                    subnet_net = res.properties.get(subnet.Subnet.NETWORK)
                    if subnet_net == external_gw_net:
                        deps += (self, res)

    def _resolve_gateway(self, props):
        gateway = props.get(self.EXTERNAL_GATEWAY)
        if gateway:
            gateway['network_id'] = gateway.pop(self.EXTERNAL_GATEWAY_NETWORK)
            if gateway[self.EXTERNAL_GATEWAY_ENABLE_SNAT] is None:
                del gateway[self.EXTERNAL_GATEWAY_ENABLE_SNAT]
            if gateway[self.EXTERNAL_GATEWAY_FIXED_IPS] is None:
                del gateway[self.EXTERNAL_GATEWAY_FIXED_IPS]
            else:
                self._resolve_subnet(gateway)
        return props

    def _get_l3_agent_list(self, props):
        l3_agent_id = props.pop(self.L3_AGENT_ID, None)
        l3_agent_ids = props.pop(self.L3_AGENT_IDS, None)
        if not l3_agent_ids and l3_agent_id:
            l3_agent_ids = [l3_agent_id]

        return l3_agent_ids

    def _resolve_subnet(self, gateway):
        external_gw_fixed_ips = gateway[self.EXTERNAL_GATEWAY_FIXED_IPS]
        for fixed_ip in external_gw_fixed_ips:
            for key, value in six.iteritems(fixed_ip):
                if value is None:
                    fixed_ip.pop(key)
            if self.SUBNET in fixed_ip:
                fixed_ip['subnet_id'] = fixed_ip.pop(self.SUBNET)

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

        router = self.client().create_router({'router': props})['router']
        self.resource_id_set(router['id'])

        if l3_agent_ids:
            self._replace_agent(l3_agent_ids)

    def _show_resource(self):
        return self.client().show_router(self.resource_id)['router']

    def check_create_complete(self, *args):
        attributes = self._show_resource()
        return self.is_built(attributes)

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

        try:
            self.client().delete_router(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):
        if self.EXTERNAL_GATEWAY in prop_diff:
            self._resolve_gateway(prop_diff)

        if self.L3_AGENT_IDS in prop_diff or self.L3_AGENT_ID in prop_diff:
            l3_agent_ids = self._get_l3_agent_list(prop_diff)
            self._replace_agent(l3_agent_ids)

        if prop_diff:
            self.prepare_update_properties(prop_diff)
            self.client().update_router(self.resource_id,
                                        {'router': prop_diff})

    def _replace_agent(self, l3_agent_ids=None):
        ret = self.client().list_l3_agent_hosting_routers(self.resource_id)
        for agent in ret['agents']:
            self.client().remove_router_from_l3_agent(agent['id'],
                                                      self.resource_id)
        if l3_agent_ids:
            for l3_agent_id in l3_agent_ids:
                self.client().add_router_to_l3_agent(
                    l3_agent_id, {'router_id': self.resource_id})
コード例 #20
0
ファイル: floatingip.py プロジェクト: Hybrid-Cloud/conveyor
class FloatingIP(neutron.NeutronResource):
    """A resource for managing Neutron floating ips.

    Floating IP addresses can change their association between routers by
    action of the user. One of the most common use cases for floating IPs is
    to provide public IP addresses to a private cloud, where there are a
    limited number of IP addresses available. Another is for a public cloud
    user to have a "static" IP address that can be reassigned when an instance
    is upgraded or moved.
    """
    PROPERTIES = (
        FLOATING_NETWORK_ID,
        FLOATING_NETWORK,
        VALUE_SPECS,
        PORT_ID,
        FIXED_IP_ADDRESS,
        FLOATING_IP_ADDRESS,
    ) = (
        'floating_network_id',
        'floating_network',
        'value_specs',
        'port_id',
        'fixed_ip_address',
        'floating_ip_address',
    )

    ATTRIBUTES = (
        ROUTER_ID,
        TENANT_ID,
        FLOATING_NETWORK_ID_ATTR,
        FIXED_IP_ADDRESS_ATTR,
        FLOATING_IP_ADDRESS_ATTR,
        PORT_ID_ATTR,
    ) = (
        'router_id',
        'tenant_id',
        'floating_network_id',
        'fixed_ip_address',
        'floating_ip_address',
        'port_id',
    )

    properties_schema = {
        FLOATING_NETWORK_ID:
        properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.HIDDEN,
                version='5.0.0',
                message=_('Use property %s.') % FLOATING_NETWORK,
                previous_status=support.SupportStatus(
                    status=support.DEPRECATED, version='2014.2')),
            constraints=[constraints.CustomConstraint('neutron.network')],
        ),
        FLOATING_NETWORK:
        properties.Schema(
            properties.Schema.STRING,
            _('Network to allocate floating IP from.'),
            support_status=support.SupportStatus(version='2014.2'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.network')],
        ),
        VALUE_SPECS:
        properties.Schema(
            properties.Schema.MAP,
            _('Extra parameters to include in the "floatingip" object in the '
              'creation request. Parameters are often specific to installed '
              'hardware or extensions.'),
            default={}),
        PORT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('ID of an existing port with at least one IP address to '
              'associate with this floating IP.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('neutron.port')]),
        FIXED_IP_ADDRESS:
        properties.Schema(
            properties.Schema.STRING,
            _('IP address to use if the port has multiple addresses.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('ip_addr')]),
        FLOATING_IP_ADDRESS:
        properties.Schema(
            properties.Schema.STRING,
            _('IP address of the floating IP. NOTE: The default policy '
              'setting in Neutron restricts usage of this property to '
              'administrative users only.'),
            constraints=[constraints.CustomConstraint('ip_addr')],
            support_status=support.SupportStatus(version='5.0.0'),
        ),
    }

    attributes_schema = {
        ROUTER_ID:
        attributes.Schema(_(
            'ID of the router used as gateway, set when associated with a '
            'port.'),
                          type=attributes.Schema.STRING),
        TENANT_ID:
        attributes.Schema(_('The tenant owning this floating IP.'),
                          type=attributes.Schema.STRING),
        FLOATING_NETWORK_ID_ATTR:
        attributes.Schema(
            _('ID of the network in which this IP is allocated.'),
            type=attributes.Schema.STRING),
        FIXED_IP_ADDRESS_ATTR:
        attributes.Schema(
            _('IP address of the associated port, if specified.'),
            type=attributes.Schema.STRING),
        FLOATING_IP_ADDRESS_ATTR:
        attributes.Schema(_('The allocated address of this IP.'),
                          type=attributes.Schema.STRING),
        PORT_ID_ATTR:
        attributes.Schema(_('ID of the port associated with this IP.'),
                          type=attributes.Schema.STRING),
    }

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

    def add_dependencies(self, deps):
        super(FloatingIP, self).add_dependencies(deps)

        for resource in six.itervalues(self.stack):
            # depend on any RouterGateway in this template with the same
            # network_id as this floating_network_id
            if resource.has_interface('OS::Neutron::RouterGateway'):
                gateway_network = resource.properties.get(
                    router.RouterGateway.NETWORK) or resource.properties.get(
                        router.RouterGateway.NETWORK_ID)
                floating_network = self.properties[self.FLOATING_NETWORK]
                if gateway_network == floating_network:
                    deps += (self, resource)

            # depend on any RouterInterface in this template which interfaces
            # with the same subnet that this floating IP's port is assigned
            # to
            elif resource.has_interface('OS::Neutron::RouterInterface'):

                def port_on_subnet(resource, subnet):
                    if not resource.has_interface('OS::Neutron::Port'):
                        return False
                    fixed_ips = resource.properties.get(port.Port.FIXED_IPS)
                    if not fixed_ips:
                        p_net = (resource.properties.get(port.Port.NETWORK) or
                                 resource.properties.get(port.Port.NETWORK_ID))
                        if p_net:
                            subnets = self.client().show_network(
                                p_net)['network']['subnets']
                            return subnet in subnets
                    else:
                        for fixed_ip in resource.properties.get(
                                port.Port.FIXED_IPS):

                            port_subnet = (
                                fixed_ip.get(port.Port.FIXED_IP_SUBNET)
                                or fixed_ip.get(port.Port.FIXED_IP_SUBNET_ID))
                            return subnet == port_subnet
                    return False

                interface_subnet = (resource.properties.get(
                    router.RouterInterface.SUBNET) or resource.properties.get(
                        router.RouterInterface.SUBNET_ID))
                # during create we have only unresolved value for functions, so
                # can not use None value for building correct dependencies
                if interface_subnet:
                    for d in deps.graph()[self]:
                        if port_on_subnet(d, interface_subnet):
                            deps += (self, resource)
                            break
            # depend on Router with EXTERNAL_GATEWAY_NETWORK property
            # this template with the same network_id as this
            # floating_network_id
            elif resource.has_interface('OS::Neutron::Router'):
                gateway = resource.properties.get(
                    router.Router.EXTERNAL_GATEWAY)
                if gateway:
                    gateway_network = gateway.get(
                        router.Router.EXTERNAL_GATEWAY_NETWORK)
                    floating_network = self.properties[self.FLOATING_NETWORK]
                    if gateway_network == floating_network:
                        deps += (self, resource)

    def validate(self):
        super(FloatingIP, self).validate()
        # fixed_ip_address cannot be specified without a port_id
        if self.properties[self.PORT_ID] is None and self.properties[
                self.FIXED_IP_ADDRESS] is not None:
            raise exception.ResourcePropertyDependency(
                prop1=self.FIXED_IP_ADDRESS, prop2=self.PORT_ID)

    def handle_create(self):
        props = self.prepare_properties(self.properties,
                                        self.physical_resource_name())
        props['floating_network_id'] = props.pop(self.FLOATING_NETWORK)
        fip = self.client().create_floatingip({'floatingip':
                                               props})['floatingip']
        self.resource_id_set(fip['id'])

    def _show_resource(self):
        return self.client().show_floatingip(self.resource_id)['floatingip']

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

        with self.client_plugin().ignore_not_found:
            self.client().delete_floatingip(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            port_id = prop_diff.get(self.PORT_ID,
                                    self.properties[self.PORT_ID])

            fixed_ip_address = prop_diff.get(
                self.FIXED_IP_ADDRESS, self.properties[self.FIXED_IP_ADDRESS])

            request_body = {
                'floatingip': {
                    'port_id': port_id,
                    'fixed_ip_address': fixed_ip_address
                }
            }

            self.client().update_floatingip(self.resource_id, request_body)
コード例 #21
0
class RouterGateway(neutron.NeutronResource):

    support_status = support.SupportStatus(
        status=support.HIDDEN,
        message=_('Use the `external_gateway_info` property in '
                  'the router resource to set up the gateway.'),
        version='5.0.0',
        previous_status=support.SupportStatus(status=support.DEPRECATED,
                                              version='2014.1'))

    PROPERTIES = (
        ROUTER_ID,
        NETWORK_ID,
        NETWORK,
    ) = ('router_id', 'network_id', 'network')

    properties_schema = {
        ROUTER_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('ID of the router.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.router')]),
        NETWORK_ID:
        properties.Schema(
            properties.Schema.STRING,
            support_status=support.SupportStatus(
                status=support.DEPRECATED,
                message=_('Use property %s.') % NETWORK,
                version='2014.2'),
            constraints=[constraints.CustomConstraint('neutron.network')],
        ),
        NETWORK:
        properties.Schema(
            properties.Schema.STRING,
            _('external network for the gateway.'),
            constraints=[constraints.CustomConstraint('neutron.network')],
        ),
    }

    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')
        ]

    def add_dependencies(self, deps):
        super(RouterGateway, self).add_dependencies(deps)
        for resource in six.itervalues(self.stack):
            # depend on any RouterInterface in this template with the same
            # router_id as this router_id
            if resource.has_interface('OS::Neutron::RouterInterface'):
                # Since RouterInterface translates router_id property to
                # router, we should correctly resolve it for RouterGateway.
                dep_router_id = self.client_plugin().resolve_router(
                    {
                        RouterInterface.ROUTER:
                        resource.properties.get(RouterInterface.ROUTER),
                        RouterInterface.ROUTER_ID:
                        None
                    }, RouterInterface.ROUTER, RouterInterface.ROUTER_ID)
                router_id = self.properties[self.ROUTER_ID]
                if dep_router_id == router_id:
                    deps += (self, resource)
            # depend on any subnet in this template with the same network_id
            # as this network_id, as the gateway implicitly creates a port
            # on that subnet
            if resource.has_interface('OS::Neutron::Subnet'):
                dep_network = resource.properties.get(subnet.Subnet.NETWORK)
                network = self.properties[self.NETWORK]
                if dep_network == network:
                    deps += (self, resource)

    def handle_create(self):
        router_id = self.properties[self.ROUTER_ID]
        network_id = dict(self.properties).get(self.NETWORK)
        self.client().add_gateway_router(router_id, {'network_id': network_id})
        self.resource_id_set('%s:%s' % (router_id, network_id))

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

        (router_id, network_id) = self.resource_id.split(':')
        with self.client_plugin().ignore_not_found:
            self.client().remove_gateway_router(router_id)
コード例 #22
0
ファイル: floatingip.py プロジェクト: Hybrid-Cloud/conveyor
class FloatingIPAssociation(neutron.NeutronResource):
    """A resource for associating floating ips and ports.

    This resource allows associating a floating IP to a port with at least one
    IP address to associate with this floating IP.
    """
    PROPERTIES = (
        FLOATINGIP_ID,
        PORT_ID,
        FIXED_IP_ADDRESS,
    ) = (
        'floatingip_id',
        'port_id',
        'fixed_ip_address',
    )

    properties_schema = {
        FLOATINGIP_ID:
        properties.Schema(properties.Schema.STRING,
                          _('ID of the floating IP to associate.'),
                          required=True,
                          update_allowed=True),
        PORT_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('ID of an existing port with at least one IP address to '
              'associate with this floating IP.'),
            required=True,
            update_allowed=True,
            constraints=[constraints.CustomConstraint('neutron.port')]),
        FIXED_IP_ADDRESS:
        properties.Schema(
            properties.Schema.STRING,
            _('IP address to use if the port has multiple addresses.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('ip_addr')]),
    }

    def add_dependencies(self, deps):
        super(FloatingIPAssociation, self).add_dependencies(deps)

        for resource in six.itervalues(self.stack):
            if resource.has_interface('OS::Neutron::RouterInterface'):

                def port_on_subnet(resource, subnet):
                    if not resource.has_interface('OS::Neutron::Port'):
                        return False
                    fixed_ips = resource.properties.get(
                        port.Port.FIXED_IPS) or []
                    for fixed_ip in fixed_ips:
                        port_subnet = (fixed_ip.get(port.Port.FIXED_IP_SUBNET)
                                       or fixed_ip.get(
                                           port.Port.FIXED_IP_SUBNET_ID))
                        return subnet == port_subnet
                    return False

                interface_subnet = (resource.properties.get(
                    router.RouterInterface.SUBNET) or resource.properties.get(
                        router.RouterInterface.SUBNET_ID))
                for d in deps.graph()[self]:
                    if port_on_subnet(d, interface_subnet):
                        deps += (self, resource)
                        break

    def handle_create(self):
        props = self.prepare_properties(self.properties, self.name)
        floatingip_id = props.pop(self.FLOATINGIP_ID)
        self.client().update_floatingip(floatingip_id, {'floatingip': props})
        self.resource_id_set(self.id)

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

        with self.client_plugin().ignore_not_found:
            self.client().update_floatingip(
                self.properties[self.FLOATINGIP_ID],
                {'floatingip': {
                    'port_id': None
                }})

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            floatingip_id = self.properties[self.FLOATINGIP_ID]
            port_id = self.properties[self.PORT_ID]
            # if the floatingip_id is changed, disassociate the port which
            # associated with the old floatingip_id
            if self.FLOATINGIP_ID in prop_diff:
                with self.client_plugin().ignore_not_found:
                    self.client().update_floatingip(
                        floatingip_id, {'floatingip': {
                            'port_id': None
                        }})

            # associate the floatingip with the new port
            floatingip_id = (prop_diff.get(self.FLOATINGIP_ID)
                             or floatingip_id)
            port_id = prop_diff.get(self.PORT_ID) or port_id

            fixed_ip_address = (prop_diff.get(self.FIXED_IP_ADDRESS)
                                or self.properties[self.FIXED_IP_ADDRESS])

            request_body = {
                'floatingip': {
                    'port_id': port_id,
                    'fixed_ip_address': fixed_ip_address
                }
            }

            self.client().update_floatingip(floatingip_id, request_body)
            self.resource_id_set(self.id)
コード例 #23
0
ファイル: route_table.py プロジェクト: Hybrid-Cloud/conveyor
class SubnetRouteTableAssociation(resource.Resource):

    PROPERTIES = (
        ROUTE_TABLE_ID,
        SUBNET_ID,
    ) = (
        'RouteTableId',
        'SubnetId',
    )

    properties_schema = {
        ROUTE_TABLE_ID:
        properties.Schema(properties.Schema.STRING,
                          _('Route table ID.'),
                          required=True),
        SUBNET_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Subnet ID.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
    }

    default_client_name = 'neutron'

    def handle_create(self):
        client = self.client()
        subnet_id = self.properties.get(self.SUBNET_ID)

        router_id = self.properties.get(self.ROUTE_TABLE_ID)

        # remove the default router association for this subnet.
        with self.client_plugin().ignore_not_found:
            previous_router = self._router_for_subnet(subnet_id)
            if previous_router:
                client.remove_interface_router(previous_router['id'],
                                               {'subnet_id': subnet_id})

        client.add_interface_router(router_id, {'subnet_id': subnet_id})

    def _router_for_subnet(self, subnet_id):
        client = self.client()
        subnet = client.show_subnet(subnet_id)['subnet']
        network_id = subnet['network_id']
        return vpc.VPC.router_for_vpc(client, network_id)

    def handle_delete(self):
        client = self.client()
        subnet_id = self.properties.get(self.SUBNET_ID)

        router_id = self.properties.get(self.ROUTE_TABLE_ID)

        with self.client_plugin().ignore_not_found:
            client.remove_interface_router(router_id, {'subnet_id': subnet_id})

        # add back the default router
        with self.client_plugin().ignore_not_found:
            default_router = self._router_for_subnet(subnet_id)
            if default_router:
                client.add_interface_router(default_router['id'],
                                            {'subnet_id': subnet_id})
コード例 #24
0
class KeystoneUser(resource.Resource,
                   role_assignments.KeystoneRoleAssignmentMixin):
    """Heat Template Resource for Keystone User.

    Users represent an individual API consumer. A user itself must be owned by
    a specific domain, and hence all user names are not globally unique, but
    only unique to their domain.
    """

    support_status = support.SupportStatus(
        version='2015.1',
        message=_('Supported versions: keystone v3'))

    default_client_name = 'keystone'

    entity = 'users'

    PROPERTIES = (
        NAME, DOMAIN, DESCRIPTION, ENABLED, EMAIL, PASSWORD,
        DEFAULT_PROJECT, GROUPS
    ) = (
        'name', 'domain', 'description', 'enabled', 'email', 'password',
        'default_project', 'groups'
    )

    properties_schema = {
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('Name of keystone user.'),
            update_allowed=True
        ),
        DOMAIN: properties.Schema(
            properties.Schema.STRING,
            _('Name of keystone domain.'),
            default='default',
            update_allowed=True,
            constraints=[constraints.CustomConstraint('keystone.domain')]
        ),
        DESCRIPTION: properties.Schema(
            properties.Schema.STRING,
            _('Description of keystone user.'),
            default='',
            update_allowed=True
        ),
        ENABLED: properties.Schema(
            properties.Schema.BOOLEAN,
            _('Keystone user is enabled or disabled.'),
            default=True,
            update_allowed=True
        ),
        EMAIL: properties.Schema(
            properties.Schema.STRING,
            _('Email address of keystone user.'),
            update_allowed=True
        ),
        PASSWORD: properties.Schema(
            properties.Schema.STRING,
            _('Password of keystone user.'),
            update_allowed=True
        ),
        DEFAULT_PROJECT: properties.Schema(
            properties.Schema.STRING,
            _('Default project of keystone user.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('keystone.project')]
        ),
        GROUPS: properties.Schema(
            properties.Schema.LIST,
            _('Keystone user groups.'),
            update_allowed=True,
            schema=properties.Schema(
                properties.Schema.STRING,
                _('Keystone user group.'),
                constraints=[constraints.CustomConstraint('keystone.group')]
            )
        )
    }

    properties_schema.update(
        role_assignments.KeystoneRoleAssignmentMixin.mixin_properties_schema)

    def validate(self):
        super(KeystoneUser, self).validate()
        self.validate_assignment_properties()

    def client(self):
        return super(KeystoneUser, self).client().client

    def _update_user(self,
                     user_id,
                     domain,
                     new_name=None,
                     new_description=None,
                     new_email=None,
                     new_password=None,
                     new_default_project=None,
                     enabled=None):
        values = dict()

        if new_name is not None:
            values['name'] = new_name
        if new_description is not None:
            values['description'] = new_description
        if new_email is not None:
            values['email'] = new_email
        if new_password is not None:
            values['password'] = new_password
        if new_default_project is not None:
            values['default_project'] = new_default_project
        if enabled is not None:
            values['enabled'] = enabled

        # If there're no args above, keystone raises BadRequest error with
        # message about not enough parameters for updating, so return from
        # this method to prevent raising error.
        if not values:
            return

        values['user'] = user_id
        domain = (self.client_plugin().get_domain_id(domain))

        values['domain'] = domain

        return self.client().users.update(**values)

    def _add_user_to_groups(self, user_id, groups):
        if groups is not None:
            group_ids = [self.client_plugin().get_group_id(group)
                         for group in groups]

            for group_id in group_ids:
                self.client().users.add_to_group(user_id,
                                                 group_id)

    def _remove_user_from_groups(self, user_id, groups):
        if groups is not None:
            group_ids = [self.client_plugin().get_group_id(group)
                         for group in groups]

            for group_id in group_ids:
                self.client().users.remove_from_group(user_id,
                                                      group_id)

    def _find_diff(self, updated_prps, stored_prps):
        new_group_ids = [self.client_plugin().get_group_id(group)
                         for group in
                         (set(updated_prps or []) -
                          set(stored_prps or []))]

        removed_group_ids = [self.client_plugin().get_group_id(group)
                             for group in
                             (set(stored_prps or []) -
                              set(updated_prps or []))]

        return new_group_ids, removed_group_ids

    def handle_create(self):
        user_name = (self.properties[self.NAME] or
                     self.physical_resource_name())
        description = self.properties[self.DESCRIPTION]
        domain = self.client_plugin().get_domain_id(
            self.properties[self.DOMAIN])
        enabled = self.properties[self.ENABLED]
        email = self.properties[self.EMAIL]
        password = self.properties[self.PASSWORD]
        default_project = self.client_plugin().get_project_id(
            self.properties[self.DEFAULT_PROJECT])
        groups = self.properties[self.GROUPS]

        user = self.client().users.create(
            name=user_name,
            domain=domain,
            description=description,
            enabled=enabled,
            email=email,
            password=password,
            default_project=default_project)

        self.resource_id_set(user.id)

        self._add_user_to_groups(user.id, groups)

        self.create_assignment(user_id=user.id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            name = None
            # Don't update the name if no change
            if self.NAME in prop_diff:
                name = prop_diff[self.NAME] or self.physical_resource_name()

            description = prop_diff.get(self.DESCRIPTION)
            enabled = prop_diff.get(self.ENABLED)
            email = prop_diff.get(self.EMAIL)
            password = prop_diff.get(self.PASSWORD)
            domain = (prop_diff.get(self.DOMAIN) or
                      self._stored_properties_data.get(self.DOMAIN))

            default_project = prop_diff.get(self.DEFAULT_PROJECT)

            self._update_user(
                user_id=self.resource_id,
                domain=domain,
                new_name=name,
                new_description=description,
                enabled=enabled,
                new_default_project=default_project,
                new_email=email,
                new_password=password
            )

            if self.GROUPS in prop_diff:
                (new_group_ids, removed_group_ids) = self._find_diff(
                    prop_diff[self.GROUPS],
                    self._stored_properties_data.get(self.GROUPS))
                if new_group_ids:
                    self._add_user_to_groups(self.resource_id, new_group_ids)

                if removed_group_ids:
                    self._remove_user_from_groups(self.resource_id,
                                                  removed_group_ids)

            self.update_assignment(prop_diff=prop_diff,
                                   user_id=self.resource_id)

    def handle_delete(self):
        if self.resource_id is not None:
            with self.client_plugin().ignore_not_found:
                if self._stored_properties_data.get(self.GROUPS) is not None:
                    self._remove_user_from_groups(
                        self.resource_id,
                        [self.client_plugin().get_group_id(group)
                         for group in
                         self._stored_properties_data.get(self.GROUPS)])

                self.client().users.delete(self.resource_id)
コード例 #25
0
class KeystoneRoleAssignmentMixin(object):
    """Implements role assignments between user/groups and project/domain.

    heat_template_version: 2013-05-23

    parameters:
      ... Group or User parameters
      group_role:
        type: string
        description: role
      group_role_domain:
        type: string
        description: group role domain
      group_role_project:
        type: string
        description: group role project

    resources:
      admin_group:
        type: OS::Keystone::Group OR OS::Keystone::User
        properties:
          ... Group or User properties
          roles:
            - role: {get_param: group_role}
              domain: {get_param: group_role_domain}
            - role: {get_param: group_role}
              project: {get_param: group_role_project}
    """

    PROPERTIES = (ROLES) = ('roles')

    _ROLES_MAPPING_PROPERTIES = (ROLE, DOMAIN, PROJECT) = ('role', 'domain',
                                                           'project')

    mixin_properties_schema = {
        ROLES:
        properties.Schema(
            properties.Schema.LIST,
            _('List of role assignments.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                _('Map between role with either project or domain.'),
                schema={
                    ROLE:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('Keystone role.'),
                        required=True,
                        constraints=([
                            constraints.CustomConstraint('keystone.role')
                        ])),
                    PROJECT:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('Keystone project.'),
                        constraints=([
                            constraints.CustomConstraint('keystone.project')
                        ])),
                    DOMAIN:
                    properties.Schema(
                        properties.Schema.STRING,
                        _('Keystone domain.'),
                        constraints=([
                            constraints.CustomConstraint('keystone.domain')
                        ])),
                }),
            update_allowed=True)
    }

    def _add_role_assignments_to_group(self, group_id, role_assignments):
        for role_assignment in self._normalize_to_id(role_assignments):
            if role_assignment.get(self.PROJECT) is not None:
                self.client().roles.grant(role=role_assignment.get(self.ROLE),
                                          project=role_assignment.get(
                                              self.PROJECT),
                                          group=group_id)
            elif role_assignment.get(self.DOMAIN) is not None:
                self.client().roles.grant(role=role_assignment.get(self.ROLE),
                                          domain=role_assignment.get(
                                              self.DOMAIN),
                                          group=group_id)

    def _add_role_assignments_to_user(self, user_id, role_assignments):
        for role_assignment in self._normalize_to_id(role_assignments):
            if role_assignment.get(self.PROJECT) is not None:
                self.client().roles.grant(role=role_assignment.get(self.ROLE),
                                          project=role_assignment.get(
                                              self.PROJECT),
                                          user=user_id)
            elif role_assignment.get(self.DOMAIN) is not None:
                self.client().roles.grant(role=role_assignment.get(self.ROLE),
                                          domain=role_assignment.get(
                                              self.DOMAIN),
                                          user=user_id)

    def _remove_role_assignments_from_group(self, group_id, role_assignments):
        for role_assignment in self._normalize_to_id(role_assignments):
            if role_assignment.get(self.PROJECT) is not None:
                self.client().roles.revoke(role=role_assignment.get(self.ROLE),
                                           project=role_assignment.get(
                                               self.PROJECT),
                                           group=group_id)
            elif role_assignment.get(self.DOMAIN) is not None:
                self.client().roles.revoke(role=role_assignment.get(self.ROLE),
                                           domain=role_assignment.get(
                                               self.DOMAIN),
                                           group=group_id)

    def _remove_role_assignments_from_user(self, user_id, role_assignments):
        for role_assignment in self._normalize_to_id(role_assignments):
            if role_assignment.get(self.PROJECT) is not None:
                self.client().roles.revoke(role=role_assignment.get(self.ROLE),
                                           project=role_assignment.get(
                                               self.PROJECT),
                                           user=user_id)
            elif role_assignment.get(self.DOMAIN) is not None:
                self.client().roles.revoke(role=role_assignment.get(self.ROLE),
                                           domain=role_assignment.get(
                                               self.DOMAIN),
                                           user=user_id)

    def _normalize_to_id(self, role_assignment_prps):
        role_assignments = []
        if role_assignment_prps is None:
            return role_assignments

        for role_assignment in role_assignment_prps:
            role = role_assignment.get(self.ROLE)
            project = role_assignment.get(self.PROJECT)
            domain = role_assignment.get(self.DOMAIN)

            role_assignments.append({
                self.ROLE:
                self.client_plugin().get_role_id(role),
                self.PROJECT: (self.client_plugin().get_project_id(project))
                if project else None,
                self.DOMAIN: (self.client_plugin().get_domain_id(domain))
                if domain else None
            })
        return role_assignments

    def _find_differences(self, updated_prps, stored_prps):
        updated_role_project_assignments = []
        updated_role_domain_assignments = []

        # Split the properties into two set of role assignments
        # (project, domain) from updated properties
        for role_assignment in updated_prps or []:
            if role_assignment.get(self.PROJECT) is not None:
                updated_role_project_assignments.append(
                    '%s:%s' % (role_assignment[self.ROLE],
                               role_assignment[self.PROJECT]))
            elif (role_assignment.get(self.DOMAIN) is not None):
                updated_role_domain_assignments.append(
                    '%s:%s' %
                    (role_assignment[self.ROLE], role_assignment[self.DOMAIN]))

        stored_role_project_assignments = []
        stored_role_domain_assignments = []

        # Split the properties into two set of role assignments
        # (project, domain) from updated properties
        for role_assignment in (stored_prps or []):
            if role_assignment.get(self.PROJECT) is not None:
                stored_role_project_assignments.append(
                    '%s:%s' % (role_assignment[self.ROLE],
                               role_assignment[self.PROJECT]))
            elif (role_assignment.get(self.DOMAIN) is not None):
                stored_role_domain_assignments.append(
                    '%s:%s' %
                    (role_assignment[self.ROLE], role_assignment[self.DOMAIN]))

        new_role_assignments = []
        removed_role_assignments = []
        # NOTE: finding the diff of list of strings is easier by using 'set'
        #       so properties are converted to string in above sections
        # New items
        for item in (set(updated_role_project_assignments) -
                     set(stored_role_project_assignments)):
            new_role_assignments.append({
                self.ROLE: item[:item.find(':')],
                self.PROJECT: item[item.find(':') + 1:]
            })

        for item in (set(updated_role_domain_assignments) -
                     set(stored_role_domain_assignments)):
            new_role_assignments.append({
                self.ROLE: item[:item.find(':')],
                self.DOMAIN: item[item.find(':') + 1:]
            })

        # Old items
        for item in (set(stored_role_project_assignments) -
                     set(updated_role_project_assignments)):
            removed_role_assignments.append({
                self.ROLE:
                item[:item.find(':')],
                self.PROJECT:
                item[item.find(':') + 1:]
            })
        for item in (set(stored_role_domain_assignments) -
                     set(updated_role_domain_assignments)):
            removed_role_assignments.append({
                self.ROLE:
                item[:item.find(':')],
                self.DOMAIN:
                item[item.find(':') + 1:]
            })

        return new_role_assignments, removed_role_assignments

    def create_assignment(self, user_id=None, group_id=None):
        if self.properties.get(self.ROLES) is not None:
            if user_id is not None:
                self._add_role_assignments_to_user(
                    user_id, self.properties.get(self.ROLES))
            elif group_id is not None:
                self._add_role_assignments_to_group(
                    group_id, self.properties.get(self.ROLES))

    def update_assignment(self, prop_diff, user_id=None, group_id=None):
        # if there is no change do not update
        if self.ROLES in prop_diff:
            (new_role_assignments,
             removed_role_assignments) = self._find_differences(
                 prop_diff.get(self.ROLES),
                 self._stored_properties_data.get(self.ROLES))

            if len(new_role_assignments) > 0:
                if user_id is not None:
                    self._add_role_assignments_to_user(user_id,
                                                       new_role_assignments)
                elif group_id is not None:
                    self._add_role_assignments_to_group(
                        group_id, new_role_assignments)

            if len(removed_role_assignments) > 0:
                if user_id is not None:
                    self._remove_role_assignments_from_user(
                        user_id, removed_role_assignments)
                elif group_id is not None:
                    self._remove_role_assignments_from_group(
                        group_id, removed_role_assignments)

    def delete_assignment(self, user_id=None, group_id=None):
        if self._stored_properties_data.get(self.ROLES) is not None:
            if user_id is not None:
                self._remove_role_assignments_from_user(
                    user_id, (self._stored_properties_data.get(self.ROLES)))
            elif group_id is not None:
                self._remove_role_assignments_from_group(
                    group_id, (self._stored_properties_data.get(self.ROLES)))

    def validate_assignment_properties(self):
        if self.properties.get(self.ROLES) is not None:
            for role_assignment in self.properties.get(self.ROLES):
                project = role_assignment.get(self.PROJECT)
                domain = role_assignment.get(self.DOMAIN)

                if project is not None and domain is not None:
                    raise exception.ResourcePropertyConflict(
                        self.PROJECT, self.DOMAIN)

                if project is None and domain is None:
                    msg = _('Either project or domain must be specified for'
                            ' role %s') % role_assignment.get(self.ROLE)
                    raise exception.StackValidationFailed(message=msg)
コード例 #26
0
ファイル: loadbalancer.py プロジェクト: Hybrid-Cloud/conveyor
class LoadBalancer(neutron.NeutronResource):
    """A resource for creating LBaaS v2 Load Balancers.

    This resource creates and manages Neutron LBaaS v2 Load Balancers,
    which allows traffic to be directed between servers.
    """

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

    required_service_extension = 'lbaasv2'

    PROPERTIES = (
        DESCRIPTION, NAME, PROVIDER, VIP_ADDRESS, VIP_SUBNET,
        ADMIN_STATE_UP, TENANT_ID
    ) = (
        'description', 'name', 'provider', 'vip_address', 'vip_subnet',
        'admin_state_up', 'tenant_id'
    )

    ATTRIBUTES = (
        VIP_ADDRESS_ATTR, VIP_PORT_ATTR, VIP_SUBNET_ATTR
    ) = (
        'vip_address', 'vip_port_id', 'vip_subnet_id'
    )

    properties_schema = {
        DESCRIPTION: properties.Schema(
            properties.Schema.STRING,
            _('Description of this Load Balancer.'),
            update_allowed=True,
            default=''
        ),
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('Name of this Load Balancer.'),
            update_allowed=True
        ),
        PROVIDER: properties.Schema(
            properties.Schema.STRING,
            _('Provider for this Load Balancer.'),
            constraints=[constraints.AllowedValues(['vlb'])]
        ),
        VIP_ADDRESS: properties.Schema(
            properties.Schema.STRING,
            _('IP address for the VIP.'),
            constraints=[
                constraints.CustomConstraint('ip_addr')
            ],
        ),
        VIP_SUBNET: properties.Schema(
            properties.Schema.STRING,
            _('The name or ID of the subnet on which to allocate the VIP '
              'address.'),
            constraints=[
                constraints.CustomConstraint('neutron.subnet')
            ],
            required=True
        ),
        ADMIN_STATE_UP: properties.Schema(
            properties.Schema.BOOLEAN,
            _('The administrative state of this Load Balancer.'),
            default=True,
            update_allowed=True,
            constraints=[constraints.AllowedValues(['True'])]
        ),
        TENANT_ID: properties.Schema(
            properties.Schema.STRING,
            _('The ID of the tenant who owns the Load Balancer. Only '
              'administrative users can specify a tenant ID other than '
              'their own.'),
            constraints=[
                constraints.CustomConstraint('keystone.project')
            ],
        )
    }

    attributes_schema = {
        VIP_ADDRESS_ATTR: attributes.Schema(
            _('The VIP address of the LoadBalancer.'),
            type=attributes.Schema.STRING
        ),
        VIP_PORT_ATTR: attributes.Schema(
            _('The VIP port of the LoadBalancer.'),
            type=attributes.Schema.STRING
        ),
        VIP_SUBNET_ATTR: attributes.Schema(
            _('The VIP subnet of the LoadBalancer.'),
            type=attributes.Schema.STRING
        )
    }

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

        self.client_plugin().resolve_subnet(
            properties, self.VIP_SUBNET, 'vip_subnet_id')

        lb = self.client().create_loadbalancer(
            {'loadbalancer': properties})['loadbalancer']
        self.resource_id_set(lb['id'])

    def check_create_complete(self, data):
        return self.client_plugin().check_lb_status(self.resource_id)

    def _show_resource(self):
        return self.client().show_loadbalancer(
            self.resource_id)['loadbalancer']

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.client().update_loadbalancer(
                self.resource_id,
                {'loadbalancer': prop_diff})
        return prop_diff

    def check_update_complete(self, prop_diff):
        if prop_diff:
            return self.client_plugin().check_lb_status(self.resource_id)
        return True

    def handle_delete(self):
        pass

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

        try:
            try:
                if self.client_plugin().check_lb_status(self.resource_id):
                    self.client().delete_loadbalancer(self.resource_id)
            except exception.ResourceInError:
                # Still try to delete loadbalancer in error state
                self.client().delete_loadbalancer(self.resource_id)
        except exceptions.NotFound:
            # Resource is gone
            return True

        return False
コード例 #27
0
class NetworkInterface(resource.Resource):

    PROPERTIES = (
        DESCRIPTION,
        GROUP_SET,
        PRIVATE_IP_ADDRESS,
        SOURCE_DEST_CHECK,
        SUBNET_ID,
        TAGS,
    ) = (
        'Description',
        'GroupSet',
        'PrivateIpAddress',
        'SourceDestCheck',
        'SubnetId',
        'Tags',
    )

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

    ATTRIBUTES = (PRIVATE_IP_ADDRESS_ATTR, ) = ('PrivateIpAddress', )

    properties_schema = {
        DESCRIPTION:
        properties.Schema(properties.Schema.STRING,
                          _('Description for this interface.')),
        GROUP_SET:
        properties.Schema(
            properties.Schema.LIST,
            _('List of security group IDs associated with this interface.'),
            update_allowed=True),
        PRIVATE_IP_ADDRESS:
        properties.Schema(properties.Schema.STRING),
        SOURCE_DEST_CHECK:
        properties.Schema(
            properties.Schema.BOOLEAN,
            _('Flag indicating if traffic to or from instance is validated.'),
            implemented=False),
        SUBNET_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Subnet ID to associate with this interface.'),
            required=True,
            constraints=[constraints.CustomConstraint('neutron.subnet')]),
        TAGS:
        properties.Schema(
            properties.Schema.LIST,
            schema=properties.Schema(
                properties.Schema.MAP,
                _('List of tags associated with this interface.'),
                schema={
                    TAG_KEY:
                    properties.Schema(properties.Schema.STRING, required=True),
                    TAG_VALUE:
                    properties.Schema(properties.Schema.STRING, required=True),
                },
                implemented=False,
            )),
    }

    attributes_schema = {
        PRIVATE_IP_ADDRESS:
        attributes.Schema(_('Private IP address of the network interface.'),
                          type=attributes.Schema.STRING),
    }

    default_client_name = 'neutron'

    @staticmethod
    def network_id_from_subnet_id(neutronclient, subnet_id):
        subnet_info = neutronclient.show_subnet(subnet_id)
        return subnet_info['subnet']['network_id']

    def __init__(self, name, json_snippet, stack):
        super(NetworkInterface, self).__init__(name, json_snippet, stack)
        self.fixed_ip_address = None

    def handle_create(self):
        subnet_id = self.properties[self.SUBNET_ID]
        network_id = self.client_plugin().network_id_from_subnet_id(subnet_id)

        fixed_ip = {'subnet_id': subnet_id}
        if self.properties[self.PRIVATE_IP_ADDRESS]:
            fixed_ip['ip_address'] = self.properties[self.PRIVATE_IP_ADDRESS]

        props = {
            'name': self.physical_resource_name(),
            'admin_state_up': True,
            'network_id': network_id,
            'fixed_ips': [fixed_ip]
        }
        # if without group_set, don't set the 'security_groups' property,
        # neutron will create the port with the 'default' securityGroup,
        # if has the group_set and the value is [], which means to create the
        # port without securityGroup(same as the behavior of neutron)
        if self.properties[self.GROUP_SET] is not None:
            sgs = self.client_plugin().get_secgroup_uuids(
                self.properties.get(self.GROUP_SET))
            props['security_groups'] = sgs
        port = self.client().create_port({'port': props})['port']
        self.resource_id_set(port['id'])

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

        with self.client_plugin().ignore_not_found:
            self.client().delete_port(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            update_props = {}
            if self.GROUP_SET in prop_diff:
                group_set = prop_diff.get(self.GROUP_SET)
                # update should keep the same behavior as creation,
                # if without the GroupSet in update template, we should
                # update the security_groups property to referent
                # the 'default' security group
                if group_set is not None:
                    sgs = self.client_plugin().get_secgroup_uuids(group_set)
                else:
                    sgs = self.client_plugin().get_secgroup_uuids(['default'])

                update_props['security_groups'] = sgs

                self.client().update_port(self.resource_id,
                                          {'port': update_props})

    def _get_fixed_ip_address(self):
        if self.fixed_ip_address is None:
            port = self.client().show_port(self.resource_id)['port']
            if port['fixed_ips'] and len(port['fixed_ips']) > 0:
                self.fixed_ip_address = port['fixed_ips'][0]['ip_address']

        return self.fixed_ip_address

    def _resolve_attribute(self, name):
        if name == self.PRIVATE_IP_ADDRESS:
            return self._get_fixed_ip_address()
コード例 #28
0
ファイル: eip.py プロジェクト: Hybrid-Cloud/conveyor
class ElasticIpAssociation(resource.Resource):
    PROPERTIES = (
        INSTANCE_ID,
        EIP,
        ALLOCATION_ID,
        NETWORK_INTERFACE_ID,
    ) = (
        'InstanceId',
        'EIP',
        'AllocationId',
        'NetworkInterfaceId',
    )

    properties_schema = {
        INSTANCE_ID:
        properties.Schema(
            properties.Schema.STRING,
            _('Instance ID to associate with EIP specified by EIP property.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('nova.server')]),
        EIP:
        properties.Schema(
            properties.Schema.STRING,
            _('EIP address to associate with instance.'),
            update_allowed=True,
            constraints=[constraints.CustomConstraint('ip_addr')]),
        ALLOCATION_ID:
        properties.Schema(properties.Schema.STRING,
                          _('Allocation ID for VPC EIP address.'),
                          update_allowed=True),
        NETWORK_INTERFACE_ID:
        properties.Schema(properties.Schema.STRING,
                          _('Network interface ID to associate with EIP.'),
                          update_allowed=True),
    }

    default_client_name = 'nova'

    def get_reference_id(self):
        return self.physical_resource_name_or_FnGetRefId()

    def validate(self):
        """Validate any of the provided parameters."""
        super(ElasticIpAssociation, self).validate()
        eip = self.properties[self.EIP]
        allocation_id = self.properties[self.ALLOCATION_ID]
        instance_id = self.properties[self.INSTANCE_ID]
        ni_id = self.properties[self.NETWORK_INTERFACE_ID]
        # to check EIP and ALLOCATION_ID, should provide one of
        if bool(eip) == bool(allocation_id):
            msg = _("Either 'EIP' or 'AllocationId' must be provided.")
            raise exception.StackValidationFailed(message=msg)
        # to check if has EIP, also should specify InstanceId
        if eip and not instance_id:
            msg = _("Must specify 'InstanceId' if you specify 'EIP'.")
            raise exception.StackValidationFailed(message=msg)
        # to check InstanceId and NetworkInterfaceId, should provide
        # at least one
        if not instance_id and not ni_id:
            raise exception.PropertyUnspecifiedError('InstanceId',
                                                     'NetworkInterfaceId')

    def _get_port_info(self, ni_id=None, instance_id=None):
        port_id = None
        port_rsrc = None
        if ni_id:
            port_rsrc = self.neutron().list_ports(id=ni_id)['ports'][0]
            port_id = ni_id
        elif instance_id:
            ports = self.neutron().list_ports(device_id=instance_id)
            port_rsrc = ports['ports'][0]
            port_id = port_rsrc['id']

        return port_id, port_rsrc

    def _neutron_add_gateway_router(self, float_id, network_id):
        router = vpc.VPC.router_for_vpc(self.neutron(), network_id)
        if router is not None:
            floatingip = self.neutron().show_floatingip(float_id)
            floating_net_id = floatingip['floatingip']['floating_network_id']
            self.neutron().add_gateway_router(router['id'],
                                              {'network_id': floating_net_id})

    def _neutron_update_floating_ip(self,
                                    allocationId,
                                    port_id=None,
                                    ignore_not_found=False):
        try:
            self.neutron().update_floatingip(
                allocationId, {'floatingip': {
                    'port_id': port_id
                }})
        except Exception as e:
            if ignore_not_found:
                self.client_plugin('neutron').ignore_not_found(e)
            else:
                raise

    def _nova_remove_floating_ip(self,
                                 instance_id,
                                 eip,
                                 ignore_not_found=False):
        server = None
        try:
            server = self.client().servers.get(instance_id)
            server.remove_floating_ip(eip)
        except Exception as e:
            is_not_found = self.client_plugin('nova').is_not_found(e)
            iue = self.client_plugin('nova').is_unprocessable_entity(e)
            if ((not ignore_not_found and is_not_found)
                    or (not is_not_found and not iue)):
                raise

        return server

    def _floatingIp_detach(self,
                           nova_ignore_not_found=False,
                           neutron_ignore_not_found=False):
        eip = self.properties[self.EIP]
        allocation_id = self.properties[self.ALLOCATION_ID]
        instance_id = self.properties[self.INSTANCE_ID]
        server = None
        if eip:
            # if has eip_old, to remove the eip_old from the instance
            server = self._nova_remove_floating_ip(instance_id, eip,
                                                   nova_ignore_not_found)
        else:
            # if hasn't eip_old, to update neutron floatingIp
            self._neutron_update_floating_ip(allocation_id, None,
                                             neutron_ignore_not_found)

        return server

    def _handle_update_eipInfo(self, prop_diff):
        eip_update = prop_diff.get(self.EIP)
        allocation_id_update = prop_diff.get(self.ALLOCATION_ID)
        instance_id = self.properties[self.INSTANCE_ID]
        ni_id = self.properties[self.NETWORK_INTERFACE_ID]
        if eip_update:
            server = self._floatingIp_detach(neutron_ignore_not_found=True)
            if server:
                # then to attach the eip_update to the instance
                server.add_floating_ip(eip_update)
                self.resource_id_set(eip_update)
        elif allocation_id_update:
            self._floatingIp_detach(nova_ignore_not_found=True)
            port_id, port_rsrc = self._get_port_info(ni_id, instance_id)
            if not port_id or not port_rsrc:
                LOG.error(_LE('Port not specified.'))
                raise exception.NotFound(
                    _('Failed to update, can not found '
                      'port info.'))

            network_id = port_rsrc['network_id']
            self._neutron_add_gateway_router(allocation_id_update, network_id)
            self._neutron_update_floating_ip(allocation_id_update, port_id)
            self.resource_id_set(allocation_id_update)

    def _handle_update_portInfo(self, prop_diff):
        instance_id_update = prop_diff.get(self.INSTANCE_ID)
        ni_id_update = prop_diff.get(self.NETWORK_INTERFACE_ID)
        eip = self.properties[self.EIP]
        allocation_id = self.properties[self.ALLOCATION_ID]
        # if update portInfo, no need to detach the port from
        # old instance/floatingip.
        if eip:
            server = self.client().servers.get(instance_id_update)
            server.add_floating_ip(eip)
        else:
            port_id, port_rsrc = self._get_port_info(ni_id_update,
                                                     instance_id_update)
            if not port_id or not port_rsrc:
                LOG.error(_LE('Port not specified.'))
                raise exception.NotFound(
                    _('Failed to update, can not found '
                      'port info.'))

            network_id = port_rsrc['network_id']
            self._neutron_add_gateway_router(allocation_id, network_id)
            self._neutron_update_floating_ip(allocation_id, port_id)

    def handle_create(self):
        """Add a floating IP address to a server."""
        if self.properties[self.EIP]:
            server = self.client().servers.get(
                self.properties[self.INSTANCE_ID])
            server.add_floating_ip(self.properties[self.EIP])
            self.resource_id_set(self.properties[self.EIP])
            LOG.debug(
                'ElasticIpAssociation '
                '%(instance)s.add_floating_ip(%(eip)s)', {
                    'instance': self.properties[self.INSTANCE_ID],
                    'eip': self.properties[self.EIP]
                })
        elif self.properties[self.ALLOCATION_ID]:
            ni_id = self.properties[self.NETWORK_INTERFACE_ID]
            instance_id = self.properties[self.INSTANCE_ID]
            port_id, port_rsrc = self._get_port_info(ni_id, instance_id)
            if not port_id or not port_rsrc:
                LOG.warning(
                    _LW('Skipping association, resource not '
                        'specified'))
                return

            float_id = self.properties[self.ALLOCATION_ID]
            network_id = port_rsrc['network_id']
            self._neutron_add_gateway_router(float_id, network_id)

            self._neutron_update_floating_ip(float_id, port_id)

            self.resource_id_set(float_id)

    def handle_delete(self):
        """Remove a floating IP address from a server or port."""
        if self.resource_id is None:
            return

        if self.properties[self.EIP]:
            instance_id = self.properties[self.INSTANCE_ID]
            eip = self.properties[self.EIP]
            self._nova_remove_floating_ip(instance_id,
                                          eip,
                                          ignore_not_found=True)
        elif self.properties[self.ALLOCATION_ID]:
            float_id = self.properties[self.ALLOCATION_ID]
            self._neutron_update_floating_ip(float_id,
                                             port_id=None,
                                             ignore_not_found=True)

    def needs_replace_with_prop_diff(self, changed_properties_set, after_props,
                                     before_props):
        if (self.ALLOCATION_ID in changed_properties_set
                or self.EIP in changed_properties_set):
            instance_id, ni_id = None, None
            if self.INSTANCE_ID in changed_properties_set:
                instance_id = after_props.get(self.INSTANCE_ID)
            if self.NETWORK_INTERFACE_ID in changed_properties_set:
                ni_id = after_props.get(self.NETWORK_INTERFACE_ID)
            return bool(instance_id or ni_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            if self.ALLOCATION_ID in prop_diff or self.EIP in prop_diff:
                self._handle_update_eipInfo(prop_diff)
            elif (self.INSTANCE_ID in prop_diff
                  or self.NETWORK_INTERFACE_ID in prop_diff):
                self._handle_update_portInfo(prop_diff)
コード例 #29
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
コード例 #30
0
class HostAggregate(resource.Resource):
    """A resource for further partition an availability zone with hosts.

    While availability zones are visible to users, host aggregates are only
    visible to administrators. Host aggregates started out as a way to use
    Xen hypervisor resource pools, but has been generalized to provide a
    mechanism to allow administrators to assign key-value pairs to groups of
    machines. Each node can have multiple aggregates, each aggregate can have
    multiple key-value pairs, and the same key-value pair can be assigned to
    multiple aggregate. This information can be used in the scheduler to
    enable advanced scheduling, to set up xen hypervisor resources pools or to
    define logical groups for migration.
    """

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

    default_client_name = 'nova'

    entity = 'aggregates'

    required_service_extension = 'os-aggregates'

    PROPERTIES = (NAME, AVAILABILITY_ZONE, HOSTS,
                  METADATA) = ('name', 'availability_zone', 'hosts',
                               'metadata')

    properties_schema = {
        NAME:
        properties.Schema(
            properties.Schema.STRING,
            _('Name for the aggregate.'),
            required=True,
            update_allowed=True,
        ),
        AVAILABILITY_ZONE:
        properties.Schema(
            properties.Schema.STRING,
            _('Name for the availability zone.'),
            required=True,
            update_allowed=True,
        ),
        HOSTS:
        properties.Schema(
            properties.Schema.LIST,
            _('List of hosts to join aggregate.'),
            update_allowed=True,
            schema=properties.Schema(
                properties.Schema.STRING,
                constraints=[constraints.CustomConstraint('nova.host')],
            ),
        ),
        METADATA:
        properties.Schema(
            properties.Schema.MAP,
            _('Arbitrary key/value metadata to store information '
              'for aggregate.'),
            update_allowed=True,
            default={}),
    }

    def _find_diff(self, update_prps, stored_prps):
        add_prps = list(set(update_prps or []) - set(stored_prps or []))
        remove_prps = list(set(stored_prps or []) - set(update_prps or []))
        return add_prps, remove_prps

    def handle_create(self):
        name = self.properties[self.NAME]
        availability_zone = self.properties[self.AVAILABILITY_ZONE]
        hosts = self.properties[self.HOSTS] or []
        metadata = self.properties[self.METADATA] or {}

        aggregate = self.client().aggregates.create(
            name=name, availability_zone=availability_zone)
        self.resource_id_set(aggregate.id)
        if metadata:
            aggregate.set_metadata(metadata)
        for host in hosts:
            aggregate.add_host(host)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            aggregate = self.client().aggregates.get(self.resource_id)
            if self.HOSTS in prop_diff:
                new_hosts = prop_diff.pop(self.HOSTS)
                old_hosts = aggregate.hosts
                add_hosts, remove_hosts = self._find_diff(new_hosts, old_hosts)
                for host in add_hosts:
                    aggregate.add_host(host)
                for host in remove_hosts:
                    aggregate.remove_host(host)
            if self.METADATA in prop_diff:
                metadata = prop_diff.pop(self.METADATA)
                if metadata:
                    aggregate.set_metadata(metadata)

            if prop_diff:
                aggregate.update(prop_diff)

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

        with self.client_plugin().ignore_not_found:
            aggregate = self.client().aggregates.get(self.resource_id)
            for host in aggregate.hosts:
                aggregate.remove_host(host)
        super(HostAggregate, self).handle_delete()