Exemple #1
0
class VolumeConnector(base.APIBase):
    """API representation of a volume connector.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a volume
    connector.
    """

    _node_uuid = None

    def _get_node_uuid(self):
        return self._node_uuid

    def _set_node_identifiers(self, value):
        """Set both UUID and ID of a node for VolumeConnector object

        :param value: UUID, ID of a node, or wtypes.Unset
        """
        if value == wtypes.Unset:
            self._node_uuid = wtypes.Unset
        elif value and self._node_uuid != value:
            try:
                node = objects.Node.get(pecan.request.context, value)
                self._node_uuid = node.uuid
                # NOTE(smoriya): Create the node_id attribute on-the-fly
                #                to satisfy the api -> rpc object conversion.
                self.node_id = node.id
            except exception.NodeNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a VolumeConnector
                e.code = http_client.BAD_REQUEST  # BadRequest
                raise

    uuid = types.uuid
    """Unique UUID for this volume connector"""

    type = wsme.wsattr(wtypes.text, mandatory=True)
    """The type of volume connector"""

    connector_id = wsme.wsattr(wtypes.text, mandatory=True)
    """The connector_id for this volume connector"""

    extra = {wtypes.text: types.jsontype}
    """The metadata for this volume connector"""

    node_uuid = wsme.wsproperty(types.uuid,
                                _get_node_uuid,
                                _set_node_identifiers,
                                mandatory=True)
    """The UUID of the node this volume connector belongs to"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated volume connector links"""

    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.VolumeConnector.fields)
        for field in fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

        # NOTE(smoriya): node_id is an attribute created on-the-fly
        # by _set_node_uuid(), it needs to be present in the fields so
        # that as_dict() will contain node_id field when converting it
        # before saving it in the database.
        self.fields.append('node_id')
        # NOTE(smoriya): node_uuid is not part of objects.VolumeConnector.-
        #                fields because it's an API-only attribute
        self.fields.append('node_uuid')
        # NOTE(jtaryma): Additionally to node_uuid, node_id is handled as a
        # secondary identifier in case RPC volume connector object dictionary
        # was passed to the constructor.
        self.node_uuid = kwargs.get('node_uuid') or kwargs.get(
            'node_id', wtypes.Unset)

    @staticmethod
    def _convert_with_links(connector, url):

        connector.links = [
            link.Link.make_link('self', url, 'volume/connectors',
                                connector.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'volume/connectors',
                                connector.uuid,
                                bookmark=True)
        ]
        return connector

    @classmethod
    def convert_with_links(cls, rpc_connector, fields=None, sanitize=True):
        connector = VolumeConnector(**rpc_connector.as_dict())

        if fields is not None:
            api_utils.check_for_invalid_fields(fields, connector.as_dict())

        connector = cls._convert_with_links(connector,
                                            pecan.request.public_url)

        if not sanitize:
            return connector

        connector.sanitize(fields)

        return connector

    def sanitize(self, fields=None):
        """Removes sensitive and unrequested data.

        Will only keep the fields specified in the ``fields`` parameter.

        :param fields:
            list of fields to preserve, or ``None`` to preserve them all
        :type fields: list of str
        """

        if fields is not None:
            self.unset_fields_except(fields)

        # never expose the node_id attribute
        self.node_id = wtypes.Unset

    @classmethod
    def sample(cls, expand=True):
        time = datetime.datetime(2000, 1, 1, 12, 0, 0)
        sample = cls(uuid='86cfd480-0842-4abb-8386-e46149beb82f',
                     type='iqn',
                     connector_id='iqn.2010-10.org.openstack:51332b70524',
                     extra={'foo': 'bar'},
                     created_at=time,
                     updated_at=time)
        sample._node_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
        fields = None if expand else _DEFAULT_RETURN_FIELDS
        return cls._convert_with_links(sample,
                                       'http://localhost:6385',
                                       fields=fields)
Exemple #2
0
class Pod(v1_base.K8sResourceBase):
    """API representation of a pod.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a pod.
    """

    uuid = types.uuid
    """Unique UUID for this pod"""

    desc = wtypes.text
    """Description of this pod"""

    images = [wtypes.text]
    """A list of images used by containers in this pod."""

    status = wtypes.text
    """Staus of this pod """

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated pod links"""

    host = wtypes.text
    """The host of this pod"""
    def __init__(self, **kwargs):
        super(Pod, self).__init__()

        self.fields = []
        for field in objects.Pod.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(pod, url, expand=True):
        if not expand:
            pod.unset_fields_except([
                'uuid', 'name', 'desc', 'bay_uuid', 'images', 'labels',
                'status', 'host'
            ])

        pod.links = [
            link.Link.make_link('self', url, 'pods', pod.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'pods',
                                pod.uuid,
                                bookmark=True)
        ]
        return pod

    @classmethod
    def convert_with_links(cls, rpc_pod, expand=True):
        pod = Pod(**rpc_pod.as_dict())
        return cls._convert_with_links(pod, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='f978db47-9a37-4e9f-8572-804a10abc0aa',
                     name='MyPod',
                     desc='Pod - Description',
                     bay_uuid='7ae81bb3-dec3-4289-8d6c-da80bd8001ae',
                     images=['MyImage'],
                     labels={'name': 'foo'},
                     status='Running',
                     host='10.0.0.3',
                     manifest_url='file:///tmp/rc.yaml',
                     manifest='''{
                         "metadata": {
                             "name": "name_of_pod"
                         },
                         "spec": {
                             "containers": [
                                 {
                                     "name": "test",
                                     "image": "test"
                                 }
                             ]
                         }
                     }''',
                     created_at=datetime.datetime.utcnow(),
                     updated_at=datetime.datetime.utcnow())
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)

    def parse_manifest(self):
        try:
            manifest = k8s_manifest.parse(self._get_manifest())
        except ValueError as e:
            raise exception.InvalidParameterValue(message=str(e))
        try:
            self.name = manifest["metadata"]["name"]
        except (KeyError, TypeError):
            raise exception.InvalidParameterValue(
                "Field metadata['name'] can't be empty in manifest.")
        images = []
        try:
            for container in manifest["spec"]["containers"]:
                images.append(container["image"])
            self.images = images
        except (KeyError, TypeError):
            raise exception.InvalidParameterValue(
                "Field spec['containers'] can't be empty in manifest.")
        if "labels" in manifest["metadata"]:
            self.labels = manifest["metadata"]["labels"]
Exemple #3
0
class Port(base.APIBase):
    """API representation of a port.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a port.
    """

    _node_uuid = None
    _portgroup_uuid = None

    def _get_node_uuid(self):
        return self._node_uuid

    def _set_node_uuid(self, value):
        if value and self._node_uuid != value:
            try:
                # FIXME(comstud): One should only allow UUID here, but
                # there seems to be a bug in that tests are passing an
                # ID. See bug #1301046 for more details.
                node = objects.Node.get(pecan.request.context, value)
                self._node_uuid = node.uuid
                # NOTE(lucasagomes): Create the node_id attribute on-the-fly
                #                    to satisfy the api -> rpc object
                #                    conversion.
                self.node_id = node.id
            except exception.NodeNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Port
                e.code = http_client.BAD_REQUEST  # BadRequest
                raise
        elif value == wtypes.Unset:
            self._node_uuid = wtypes.Unset

    def _get_portgroup_uuid(self):
        return self._portgroup_uuid

    def _set_portgroup_uuid(self, value):
        if value and self._portgroup_uuid != value:
            if not api_utils.allow_portgroups_subcontrollers():
                self._portgroup_uuid = wtypes.Unset
                return
            try:
                portgroup = objects.Portgroup.get(pecan.request.context, value)
                if portgroup.node_id != self.node_id:
                    raise exception.BadRequest(
                        _('Port can not be added to a '
                          'portgroup belonging to a '
                          'different node.'))
                self._portgroup_uuid = portgroup.uuid
                # NOTE(lucasagomes): Create the portgroup_id attribute
                #                    on-the-fly to satisfy the api ->
                #                    rpc object conversion.
                self.portgroup_id = portgroup.id
            except exception.PortgroupNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Port
                e.code = http_client.BAD_REQUEST  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._portgroup_uuid = wtypes.Unset
        elif value is None and api_utils.allow_portgroups_subcontrollers():
            # This is to output portgroup_uuid field if API version allows this
            self._portgroup_uuid = None

    uuid = types.uuid
    """Unique UUID for this port"""

    address = wsme.wsattr(types.macaddress, mandatory=True)
    """MAC Address for this port"""

    extra = {wtypes.text: types.jsontype}
    """This port's meta data"""

    internal_info = wsme.wsattr({wtypes.text: types.jsontype}, readonly=True)
    """This port's internal information maintained by ironic"""

    node_uuid = wsme.wsproperty(types.uuid,
                                _get_node_uuid,
                                _set_node_uuid,
                                mandatory=True)
    """The UUID of the node this port belongs to"""

    portgroup_uuid = wsme.wsproperty(types.uuid,
                                     _get_portgroup_uuid,
                                     _set_portgroup_uuid,
                                     mandatory=False)
    """The UUID of the portgroup this port belongs to"""

    pxe_enabled = types.boolean
    """Indicates whether pxe is enabled or disabled on the node."""

    local_link_connection = types.locallinkconnectiontype
    """The port binding profile for the port"""

    physical_network = wtypes.StringType(max_length=64)
    """The name of the physical network to which this port is connected."""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated port links"""

    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.Port.fields)
        # NOTE(lucasagomes): node_uuid is not part of objects.Port.fields
        #                    because it's an API-only attribute
        fields.append('node_uuid')
        # NOTE: portgroup_uuid is not part of objects.Port.fields
        #                    because it's an API-only attribute
        fields.append('portgroup_uuid')
        for field in fields:
            # Add fields we expose.
            if hasattr(self, field):
                self.fields.append(field)
                setattr(self, field, kwargs.get(field, wtypes.Unset))

        # NOTE(lucasagomes): node_id is an attribute created on-the-fly
        # by _set_node_uuid(), it needs to be present in the fields so
        # that as_dict() will contain node_id field when converting it
        # before saving it in the database.
        self.fields.append('node_id')
        setattr(self, 'node_uuid', kwargs.get('node_id', wtypes.Unset))

        # NOTE: portgroup_id is an attribute created on-the-fly
        # by _set_portgroup_uuid(), it needs to be present in the fields so
        # that as_dict() will contain portgroup_id field when converting it
        # before saving it in the database.
        self.fields.append('portgroup_id')
        setattr(self, 'portgroup_uuid', kwargs.get('portgroup_id',
                                                   wtypes.Unset))

    @staticmethod
    def _convert_with_links(port, url, fields=None):
        # NOTE(lucasagomes): Since we are able to return a specified set of
        # fields the "uuid" can be unset, so we need to save it in another
        # variable to use when building the links
        port_uuid = port.uuid
        if fields is not None:
            port.unset_fields_except(fields)

        # never expose the node_id attribute
        port.node_id = wtypes.Unset

        # never expose the portgroup_id attribute
        port.portgroup_id = wtypes.Unset

        port.links = [
            link.Link.make_link('self', url, 'ports', port_uuid),
            link.Link.make_link('bookmark',
                                url,
                                'ports',
                                port_uuid,
                                bookmark=True)
        ]
        return port

    @classmethod
    def convert_with_links(cls, rpc_port, fields=None):
        port = Port(**rpc_port.as_dict())

        if fields is not None:
            api_utils.check_for_invalid_fields(fields, port.as_dict())

        hide_fields_in_newer_versions(port)

        return cls._convert_with_links(port,
                                       pecan.request.public_url,
                                       fields=fields)

    @classmethod
    def sample(cls, expand=True):
        time = datetime.datetime(2000, 1, 1, 12, 0, 0)
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     address='fe:54:00:77:07:d9',
                     extra={'foo': 'bar'},
                     internal_info={},
                     created_at=time,
                     updated_at=time,
                     pxe_enabled=True,
                     local_link_connection={
                         'switch_info': 'host',
                         'port_id': 'Gig0/1',
                         'switch_id': 'aa:bb:cc:dd:ee:ff'
                     },
                     physical_network='physnet1')
        # NOTE(lucasagomes): node_uuid getter() method look at the
        # _node_uuid variable
        sample._node_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
        sample._portgroup_uuid = '037d9a52-af89-4560-b5a3-a33283295ba2'
        fields = None if expand else _DEFAULT_RETURN_FIELDS
        return cls._convert_with_links(sample,
                                       'http://localhost:6385',
                                       fields=fields)
Exemple #4
0
class OldSample(base.Base):
    """A single measurement for a given meter and resource.

    This class is deprecated in favor of Sample.
    """

    source = wtypes.text
    "The ID of the source that identifies where the sample comes from"

    counter_name = wsme.wsattr(wtypes.text, mandatory=True)
    "The name of the meter"
    # FIXME(dhellmann): Make this meter_name?

    counter_type = wsme.wsattr(wtypes.text, mandatory=True)
    "The type of the meter (see :ref:`measurements`)"
    # FIXME(dhellmann): Make this meter_type?

    counter_unit = wsme.wsattr(wtypes.text, mandatory=True)
    "The unit of measure for the value in counter_volume"
    # FIXME(dhellmann): Make this meter_unit?

    counter_volume = wsme.wsattr(float, mandatory=True)
    "The actual measured value"

    user_id = wtypes.text
    "The ID of the user who last triggered an update to the resource"

    project_id = wtypes.text
    "The ID of the project or tenant that owns the resource"

    resource_id = wsme.wsattr(wtypes.text, mandatory=True)
    "The ID of the :class:`Resource` for which the measurements are taken"

    timestamp = datetime.datetime
    "UTC date and time when the measurement was made"

    recorded_at = datetime.datetime
    "When the sample has been recorded."

    resource_metadata = {wtypes.text: wtypes.text}
    "Arbitrary metadata associated with the resource"

    message_id = wtypes.text
    "A unique identifier for the sample"

    def __init__(self,
                 counter_volume=None,
                 resource_metadata=None,
                 timestamp=None,
                 **kwds):
        resource_metadata = resource_metadata or {}
        if counter_volume is not None:
            counter_volume = float(counter_volume)
        resource_metadata = v2_utils.flatten_metadata(resource_metadata)
        # this is to make it easier for clients to pass a timestamp in
        if timestamp and isinstance(timestamp, six.string_types):
            timestamp = timeutils.parse_isotime(timestamp)

        super(OldSample, self).__init__(counter_volume=counter_volume,
                                        resource_metadata=resource_metadata,
                                        timestamp=timestamp,
                                        **kwds)

        if self.resource_metadata in (wtypes.Unset, None):
            self.resource_metadata = {}

    @classmethod
    def sample(cls):
        return cls(
            source='openstack',
            counter_name='instance',
            counter_type='gauge',
            counter_unit='instance',
            counter_volume=1,
            resource_id='bd9431c1-8d69-4ad3-803a-8d4a6b89fd36',
            project_id='35b17138-b364-4e6a-a131-8f3099c5be68',
            user_id='efd87807-12d2-4b38-9c70-5f5c2ac427ff',
            recorded_at=datetime.datetime(2015, 1, 1, 12, 0, 0, 0),
            timestamp=datetime.datetime(2015, 1, 1, 12, 0, 0, 0),
            resource_metadata={
                'name1': 'value1',
                'name2': 'value2'
            },
            message_id='5460acce-4fd6-480d-ab18-9735ec7b1996',
        )
Exemple #5
0
class Accelerator(base.APIBase):
    """API representation of a accelerator.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of
    a accelerator.
    """

    uuid = types.uuid
    """The UUID of the accelerator"""

    name = wtypes.text
    """The name of the accelerator"""

    description = wtypes.text
    """The description of the accelerator"""

    project_id = types.uuid
    """The project UUID of the accelerator"""

    user_id = types.uuid
    """The user UUID of the accelerator"""

    device_type = wtypes.text
    """The device type of the accelerator"""

    acc_type = wtypes.text
    """The type of the accelerator"""

    acc_capability = wtypes.text
    """The capability of the accelerator"""

    vendor_id = wtypes.text
    """The vendor id of the accelerator"""

    product_id = wtypes.text
    """The product id of the accelerator"""

    remotable = wtypes.IntegerType()
    """Whether the accelerator is remotable"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link"""
    def __init__(self, **kwargs):
        super(Accelerator, self).__init__(**kwargs)
        self.fields = []
        for field in objects.Accelerator.fields:
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @classmethod
    def convert_with_links(cls, obj_acc):
        api_acc = cls(**obj_acc.as_dict())
        url = pecan.request.public_url
        api_acc.links = [
            link.Link.make_link('self', url, 'accelerators', api_acc.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'accelerators',
                                api_acc.uuid,
                                bookmark=True)
        ]
        return api_acc
Exemple #6
0
class Alarm(base.Base):
    """Representation of an alarm.

    .. note::
        combination_rule and threshold_rule are mutually exclusive. The *type*
        of the alarm should be set to *threshold* or *combination* and the
        appropriate rule should be filled.
    """

    alarm_id = wtypes.text
    "The UUID of the alarm"

    name = wsme.wsattr(wtypes.text, mandatory=True)
    "The name for the alarm"

    _description = None  # provide a default

    def get_description(self):
        rule = getattr(self, '%s_rule' % self.type, None)
        if not self._description:
            if hasattr(rule, 'default_description'):
                return six.text_type(rule.default_description)
            return "%s alarm rule" % self.type
        return self._description

    def set_description(self, value):
        self._description = value

    description = wsme.wsproperty(wtypes.text, get_description,
                                  set_description)
    "The description of the alarm"

    enabled = wsme.wsattr(bool, default=True)
    "This alarm is enabled?"

    ok_actions = wsme.wsattr([wtypes.text], default=[])
    "The actions to do when alarm state change to ok"

    alarm_actions = wsme.wsattr([wtypes.text], default=[])
    "The actions to do when alarm state change to alarm"

    insufficient_data_actions = wsme.wsattr([wtypes.text], default=[])
    "The actions to do when alarm state change to insufficient data"

    repeat_actions = wsme.wsattr(bool, default=False)
    "The actions should be re-triggered on each evaluation cycle"

    type = base.AdvEnum('type', str, *ALARMS_RULES.names(), mandatory=True)
    "Explicit type specifier to select which rule to follow below."

    time_constraints = wtypes.wsattr([AlarmTimeConstraint], default=[])
    """Describe time constraints for the alarm"""

    # These settings are ignored in the PUT or POST operations, but are
    # filled in for GET
    project_id = wtypes.text
    "The ID of the project or tenant that owns the alarm"

    user_id = wtypes.text
    "The ID of the user who created the alarm"

    timestamp = datetime.datetime
    "The date of the last alarm definition update"

    state = base.AdvEnum('state',
                         str,
                         *state_kind,
                         default='insufficient data')
    "The state offset the alarm"

    state_timestamp = datetime.datetime
    "The date of the last alarm state changed"

    severity = base.AdvEnum('severity', str, *severity_kind, default='low')
    "The severity of the alarm"

    def __init__(self, rule=None, time_constraints=None, **kwargs):
        super(Alarm, self).__init__(**kwargs)

        if rule:
            setattr(self, '%s_rule' % self.type,
                    ALARMS_RULES[self.type].plugin(**rule))

        if time_constraints:
            self.time_constraints = [
                AlarmTimeConstraint(**tc) for tc in time_constraints
            ]

    @staticmethod
    def validate(alarm):

        Alarm.check_rule(alarm)
        Alarm.check_alarm_actions(alarm)

        ALARMS_RULES[alarm.type].plugin.validate_alarm(alarm)

        if alarm.time_constraints:
            tc_names = [tc.name for tc in alarm.time_constraints]
            if len(tc_names) > len(set(tc_names)):
                error = _("Time constraint names must be "
                          "unique for a given alarm.")
                raise base.ClientSideError(error)

        return alarm

    @staticmethod
    def check_rule(alarm):
        if (not pecan.request.cfg.api.enable_combination_alarms
                and alarm.type == 'combination'):
            raise base.ClientSideError("Unavailable alarm type")

        rule = '%s_rule' % alarm.type
        if getattr(alarm, rule) in (wtypes.Unset, None):
            error = _("%(rule)s must be set for %(type)s"
                      " type alarm") % {
                          "rule": rule,
                          "type": alarm.type
                      }
            raise base.ClientSideError(error)

        rule_set = None
        for ext in ALARMS_RULES:
            name = "%s_rule" % ext.name
            if getattr(alarm, name):
                if rule_set is None:
                    rule_set = name
                else:
                    error = _("%(rule1)s and %(rule2)s cannot be set at the "
                              "same time") % {
                                  'rule1': rule_set,
                                  'rule2': name
                              }
                    raise base.ClientSideError(error)

    @staticmethod
    def check_alarm_actions(alarm):
        max_actions = pecan.request.cfg.api.alarm_max_actions
        for state in state_kind:
            actions_name = state.replace(" ", "_") + '_actions'
            actions = getattr(alarm, actions_name)
            if not actions:
                continue

            action_set = set(actions)
            if len(actions) != len(action_set):
                LOG.info(
                    _LI('duplicate actions are found: %s, '
                        'remove duplicate ones'), actions)
                actions = list(action_set)
                setattr(alarm, actions_name, actions)

            if 0 < max_actions < len(actions):
                error = _('%(name)s count exceeds maximum value '
                          '%(maximum)d') % {
                              "name": actions_name,
                              "maximum": max_actions
                          }
                raise base.ClientSideError(error)

            limited = rbac.get_limited_to_project(pecan.request.headers,
                                                  pecan.request.enforcer)

            for action in actions:
                try:
                    url = netutils.urlsplit(action)
                except Exception:
                    error = _("Unable to parse action %s") % action
                    raise base.ClientSideError(error)
                if url.scheme not in ACTIONS_SCHEMA:
                    error = _("Unsupported action %s") % action
                    raise base.ClientSideError(error)
                if limited and url.scheme in ('log', 'test'):
                    error = _('You are not authorized to create '
                              'action: %s') % action
                    raise base.ClientSideError(error, status_code=401)

    @classmethod
    def sample(cls):
        return cls(
            alarm_id=None,
            name="SwiftObjectAlarm",
            description="An alarm",
            type='combination',
            time_constraints=[AlarmTimeConstraint.sample().as_dict()],
            user_id="c96c887c216949acbdfbd8b494863567",
            project_id="c96c887c216949acbdfbd8b494863567",
            enabled=True,
            timestamp=datetime.datetime(2015, 1, 1, 12, 0, 0, 0),
            state="ok",
            severity="moderate",
            state_timestamp=datetime.datetime(2015, 1, 1, 12, 0, 0, 0),
            ok_actions=["http://site:8000/ok"],
            alarm_actions=["http://site:8000/alarm"],
            insufficient_data_actions=["http://site:8000/nodata"],
            repeat_actions=False,
            combination_rule=combination.AlarmCombinationRule.sample(),
        )

    def as_dict(self, db_model):
        d = super(Alarm, self).as_dict(db_model)
        for k in d:
            if k.endswith('_rule'):
                del d[k]
        rule = getattr(self, "%s_rule" % self.type)
        d['rule'] = rule if isinstance(rule, dict) else rule.as_dict()
        if self.time_constraints:
            d['time_constraints'] = [
                tc.as_dict() for tc in self.time_constraints
            ]
        return d

    @staticmethod
    def _is_trust_url(url):
        return url.scheme.startswith('trust+')

    def _get_existing_trust_ids(self):
        for action in itertools.chain(self.ok_actions or [], self.alarm_actions
                                      or [], self.insufficient_data_actions
                                      or []):
            url = netutils.urlsplit(action)
            if self._is_trust_url(url):
                trust_id = url.username
                if trust_id and url.password == 'delete':
                    yield trust_id

    def update_actions(self, old_alarm=None):
        trustor_user_id = pecan.request.headers.get('X-User-Id')
        trustor_project_id = pecan.request.headers.get('X-Project-Id')
        roles = pecan.request.headers.get('X-Roles', '')
        if roles:
            roles = roles.split(',')
        else:
            roles = []
        auth_plugin = pecan.request.environ.get('keystone.token_auth')

        if old_alarm:
            prev_trust_ids = set(old_alarm._get_existing_trust_ids())
        else:
            prev_trust_ids = set()
        trust_id = prev_trust_ids.pop() if prev_trust_ids else None
        trust_id_used = False

        for actions in (self.ok_actions, self.alarm_actions,
                        self.insufficient_data_actions):
            if actions is not None:
                for index, action in enumerate(actions[:]):
                    url = netutils.urlsplit(action)
                    if self._is_trust_url(url):
                        if '@' in url.netloc:
                            continue
                        if trust_id is None:
                            # We have a trust action without a trust ID,
                            # create it
                            trust_id = keystone_client.create_trust_id(
                                pecan.request.cfg, trustor_user_id,
                                trustor_project_id, roles, auth_plugin)
                        if trust_id_used:
                            pw = ''
                        else:
                            pw = ':delete'
                            trust_id_used = True
                        netloc = '%s%s@%s' % (trust_id, pw, url.netloc)
                        url = urlparse.SplitResult(url.scheme, netloc,
                                                   url.path, url.query,
                                                   url.fragment)
                        actions[index] = url.geturl()
        if trust_id is not None and not trust_id_used:
            prev_trust_ids.add(trust_id)
        for old_trust_id in prev_trust_ids:
            keystone_client.delete_trust_id(old_trust_id, auth_plugin)

    def delete_actions(self):
        auth_plugin = pecan.request.environ.get('keystone.token_auth')
        for trust_id in self._get_existing_trust_ids():
            keystone_client.delete_trust_id(trust_id, auth_plugin)
Exemple #7
0
class AlarmThresholdRule(base.AlarmRule):
    """Alarm Threshold Rule

    Describe when to trigger the alarm based on computed statistics
    """

    meter_name = wsme.wsattr(wtypes.text, mandatory=True)
    "The name of the meter"

    # FIXME(sileht): default doesn't work
    # workaround: default is set in validate method
    query = wsme.wsattr([base.Query], default=[])
    """The query to find the data for computing statistics.
    Ownership settings are automatically included based on the Alarm owner.
    """

    period = wsme.wsattr(wtypes.IntegerType(minimum=1), default=60)
    "The time range in seconds over which query"

    comparison_operator = base.AdvEnum('comparison_operator', str,
                                       'lt', 'le', 'eq', 'ne', 'ge', 'gt',
                                       default='eq')
    "The comparison against the alarm threshold"

    threshold = wsme.wsattr(float, mandatory=True)
    "The threshold of the alarm"

    statistic = base.AdvEnum('statistic', str, 'max', 'min', 'avg', 'sum',
                             'count', default='avg')
    "The statistic to compare to the threshold"

    evaluation_periods = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
    "The number of historical periods to evaluate the threshold"

    exclude_outliers = wsme.wsattr(bool, default=False)
    "Whether datapoints with anomalously low sample counts are excluded"

    def __init__(self, query=None, **kwargs):
        query = [base.Query(**q) for q in query] if query else []
        super(AlarmThresholdRule, self).__init__(query=query, **kwargs)

    @staticmethod
    def validate(threshold_rule):
        # note(sileht): wsme default doesn't work in some case
        # workaround for https://bugs.launchpad.net/wsme/+bug/1227039
        if not threshold_rule.query:
            threshold_rule.query = []

        # Timestamp is not allowed for AlarmThresholdRule query, as the alarm
        # evaluator will construct timestamp bounds for the sequence of
        # statistics queries as the sliding evaluation window advances
        # over time.
        v2_utils.validate_query(threshold_rule.query,
                                storage.SampleFilter.__init__,
                                allow_timestamps=False)
        return threshold_rule

    @staticmethod
    def validate_alarm(alarm):
        # ensure an implicit constraint on project_id is added to
        # the query if not already present
        alarm.threshold_rule.query = v2_utils.sanitize_query(
            alarm.threshold_rule.query,
            storage.SampleFilter.__init__,
            on_behalf_of=alarm.project_id
        )

    @property
    def default_description(self):
        return (_('Alarm when %(meter_name)s is %(comparison_operator)s a '
                  '%(statistic)s of %(threshold)s over %(period)s seconds') %
                dict(comparison_operator=self.comparison_operator,
                     statistic=self.statistic,
                     threshold=self.threshold,
                     meter_name=self.meter_name,
                     period=self.period))

    def as_dict(self):
        rule = self.as_dict_from_keys(['period', 'comparison_operator',
                                       'threshold', 'statistic',
                                       'evaluation_periods', 'meter_name',
                                       'exclude_outliers'])
        rule['query'] = [q.as_dict() for q in self.query]
        return rule

    @classmethod
    def sample(cls):
        return cls(meter_name='cpu_util',
                   period=60,
                   evaluation_periods=1,
                   threshold=300.0,
                   statistic='avg',
                   comparison_operator='gt',
                   query=[{'field': 'resource_id',
                           'value': '2a4d689b-f0b8-49c1-9eef-87cae58d80db',
                           'op': 'eq',
                           'type': 'string'}])
Exemple #8
0
class RuntimePool(Resource):
    name = wsme.wsattr(wtypes.text, readonly=True)
    capacity = wsme.wsattr(RuntimePoolCapacity, readonly=True)
Exemple #9
0
class BaseResult(wtypes.Base):
    code = wsme.wsattr(int, default=0)
Exemple #10
0
class FunctionWorker(Resource):
    function_id = wsme.wsattr(types.uuid, readonly=True)
    function_version = wsme.wsattr(int, readonly=True)
    worker_name = wsme.wsattr(wtypes.text, readonly=True)
Exemple #11
0
class RuntimePoolCapacity(Resource):
    total = wsme.wsattr(int, readonly=True)
    available = wsme.wsattr(int, readonly=True)
Exemple #12
0
class Port(base.APIBase):
    """API representation of a port.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a port.
    """

    _node_uuid = None

    def _get_node_uuid(self):
        return self._node_uuid

    def _set_node_uuid(self, value):
        if value and self._node_uuid != value:
            try:
                # FIXME(comstud): One should only allow UUID here, but
                # there seems to be a bug in that tests are passing an
                # ID. See bug #1301046 for more details.
                node = objects.Node.get(pecan.request.context, value)
                self._node_uuid = node.uuid
                # NOTE(lucasagomes): Create the node_id attribute on-the-fly
                #                    to satisfy the api -> rpc object
                #                    conversion.
                self.node_id = node.id
            except exception.NodeNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Port
                e.code = 400  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._node_uuid = wtypes.Unset

    uuid = types.uuid
    "Unique UUID for this port"

    address = wsme.wsattr(types.macaddress, mandatory=True)
    "MAC Address for this port"

    extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)}
    "This port's meta data"

    node_uuid = wsme.wsproperty(types.uuid,
                                _get_node_uuid,
                                _set_node_uuid,
                                mandatory=True)
    "The UUID of the node this port belongs to"

    links = wsme.wsattr([link.Link], readonly=True)
    "A list containing a self link and associated port links"

    def __init__(self, **kwargs):
        self.fields = objects.Port.fields.keys()
        # NOTE(lucasagomes): node_uuid is not part of objects.Port.fields
        #                    because it's an API-only attribute
        self.fields.append('node_uuid')
        for k in self.fields:
            setattr(self, k, kwargs.get(k))
        setattr(self, 'node_uuid', kwargs.get('node_id'))

    @classmethod
    def convert_with_links(cls, rpc_port, expand=True):
        port = Port(**rpc_port.as_dict())
        if not expand:
            port.unset_fields_except(['uuid', 'address'])

        # never expose the node_id attribute
        port.node_id = wtypes.Unset

        port.links = [
            link.Link.make_link('self', pecan.request.host_url, 'ports',
                                port.uuid),
            link.Link.make_link('bookmark',
                                pecan.request.host_url,
                                'ports',
                                port.uuid,
                                bookmark=True)
        ]
        return port

    @classmethod
    def sample(cls):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     address='fe:54:00:77:07:d9',
                     extra={'foo': 'bar'},
                     created_at=datetime.datetime.utcnow(),
                     updated_at=datetime.datetime.utcnow())
        # NOTE(lucasagomes): node_uuid getter() method look at the
        # _node_uuid variable
        sample._node_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
        return sample
Exemple #13
0
class Bay(base.APIBase):
    """API representation of a bay.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a bay.
    """

    _baymodel_id = None

    def _get_baymodel_id(self):
        return self._baymodel_id

    def _set_baymodel_id(self, value):
        if value and self._baymodel_id != value:
            try:
                baymodel = api_utils.get_resource('ClusterTemplate', value)
                self._baymodel_id = baymodel.uuid
            except exception.ClusterTemplateNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Cluster
                e.code = 400  # BadRequest
                raise
        elif value == wtypes.Unset:
            self._baymodel_id = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this bay"""

    name = wtypes.StringType(min_length=1,
                             max_length=242,
                             pattern='^[a-zA-Z][a-zA-Z0-9_.-]*$')
    """Name of this bay, max length is limited to 242 because of heat stack
    requires max length limit to 255, and Magnum amend a uuid length"""

    baymodel_id = wsme.wsproperty(wtypes.text,
                                  _get_baymodel_id,
                                  _set_baymodel_id,
                                  mandatory=True)
    """The baymodel UUID"""

    node_count = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
    """The node count for this bay. Default to 1 if not set"""

    master_count = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
    """The number of master nodes for this bay. Default to 1 if not set"""

    bay_create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=60)
    """Timeout for creating the bay in minutes. Default to 60 if not set"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated bay links"""

    stack_id = wsme.wsattr(wtypes.text, readonly=True)
    """Stack id of the heat stack"""

    status = wtypes.Enum(str, *fields.ClusterStatus.ALL)
    """Status of the bay from the heat stack"""

    status_reason = wtypes.text
    """Status reason of the bay from the heat stack"""

    discovery_url = wtypes.text
    """Url used for bay node discovery"""

    api_address = wsme.wsattr(wtypes.text, readonly=True)
    """Api address of cluster master node"""

    coe_version = wsme.wsattr(wtypes.text, readonly=True)
    """Version of the COE software currently running in this cluster.
    Example: swarm version or kubernetes version."""

    container_version = wsme.wsattr(wtypes.text, readonly=True)
    """Version of the container software. Example: docker version."""

    node_addresses = wsme.wsattr([wtypes.text], readonly=True)
    """IP addresses of cluster slave nodes"""

    master_addresses = wsme.wsattr([wtypes.text], readonly=True)
    """IP addresses of cluster master nodes"""

    bay_faults = wsme.wsattr(wtypes.DictType(str, wtypes.text))
    """Fault info collected from the heat resources of this bay"""

    def __init__(self, **kwargs):
        super(Bay, self).__init__()

        self.fields = []
        for field in objects.Cluster.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

        # Set the renamed attributes for bay backwards compatibility
        self.fields.append('baymodel_id')
        if 'baymodel_id' in kwargs.keys():
            setattr(self, 'cluster_template_id',
                    kwargs.get('baymodel_id', None))
            setattr(self, 'baymodel_id', kwargs.get('baymodel_id', None))
        else:
            setattr(self, 'baymodel_id', kwargs.get('cluster_template_id',
                                                    None))

        self.fields.append('bay_create_timeout')
        if 'bay_create_timeout' in kwargs.keys():
            setattr(self, 'create_timeout',
                    kwargs.get('bay_create_timeout', wtypes.Unset))
            setattr(self, 'bay_create_timeout',
                    kwargs.get('bay_create_timeout', wtypes.Unset))
        else:
            setattr(self, 'bay_create_timeout',
                    kwargs.get('create_timeout', wtypes.Unset))

        self.fields.append('bay_faults')
        if 'bay_faults' in kwargs.keys():
            setattr(self, 'faults', kwargs.get('bay_faults', wtypes.Unset))
            setattr(self, 'bay_faults', kwargs.get('bay_faults', wtypes.Unset))
        else:
            setattr(self, 'bay_faults', kwargs.get('faults', wtypes.Unset))

    @staticmethod
    def _convert_with_links(bay, url, expand=True):
        if not expand:
            bay.unset_fields_except([
                'uuid', 'name', 'baymodel_id', 'node_count', 'status',
                'bay_create_timeout', 'master_count', 'stack_id'
            ])

        bay.links = [
            link.Link.make_link('self', url, 'bays', bay.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'bays',
                                bay.uuid,
                                bookmark=True)
        ]
        return bay

    @classmethod
    def convert_with_links(cls, rpc_bay, expand=True):
        bay = Bay(**rpc_bay.as_dict())
        return cls._convert_with_links(bay, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     name='example',
                     baymodel_id='4a96ac4b-2447-43f1-8ca6-9fd6f36d146d',
                     node_count=2,
                     master_count=1,
                     bay_create_timeout=15,
                     stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63',
                     status=fields.ClusterStatus.CREATE_COMPLETE,
                     status_reason="CREATE completed successfully",
                     api_address='172.24.4.3',
                     node_addresses=['172.24.4.4', '172.24.4.5'],
                     created_at=timeutils.utcnow(),
                     updated_at=timeutils.utcnow(),
                     coe_version=None,
                     container_version=None)
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)

    def as_dict(self):
        """Render this object as a dict of its fields."""

        # Override this for old bay values
        d = super(Bay, self).as_dict()

        d['cluster_template_id'] = d['baymodel_id']
        del d['baymodel_id']

        d['create_timeout'] = d['bay_create_timeout']
        del d['bay_create_timeout']

        if 'bay_faults' in d.keys():
            d['faults'] = d['bay_faults']
            del d['bay_faults']

        return d
Exemple #14
0
class Bay(base.APIBase):
    """API representation of a bay.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a bay.
    """

    _baymodel_id = None

    def _get_baymodel_id(self):
        return self._baymodel_id

    def _set_baymodel_id(self, value):
        if value and self._baymodel_id != value:
            try:
                baymodel = api_utils.get_resource('BayModel', value)
                self._baymodel_id = baymodel.uuid
            except exception.BayModelNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Bay
                e.code = 400  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._baymodel_id = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this bay"""

    name = wtypes.StringType(min_length=1, max_length=255)
    """Name of this bay"""

    baymodel_id = wsme.wsproperty(wtypes.text,
                                  _get_baymodel_id,
                                  _set_baymodel_id,
                                  mandatory=True)
    """The baymodel UUID"""

    node_count = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
    """The node count for this bay. Default to 1 if not set"""

    master_count = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
    """The number of master nodes for this bay. Default to 1 if not set"""

    bay_create_timeout = wsme.wsattr(wtypes.IntegerType(minimum=0), default=0)
    """Timeout for creating the bay in minutes. Default to 0 if not set"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated bay links"""

    stack_id = wsme.wsattr(wtypes.text, readonly=True)
    """Stack id of the heat stack"""

    status = wtypes.Enum(str, *fields.BayStatus.ALL)
    """Status of the bay from the heat stack"""

    status_reason = wtypes.text
    """Status reason of the bay from the heat stack"""

    discovery_url = wtypes.text
    """Url used for bay node discovery"""

    api_address = wsme.wsattr(wtypes.text, readonly=True)
    """Api address of cluster master node"""

    node_addresses = wsme.wsattr([wtypes.text], readonly=True)
    """IP addresses of cluster slave nodes"""

    master_addresses = wsme.wsattr([wtypes.text], readonly=True)
    """IP addresses of cluster master nodes"""

    bay_faults = wsme.wsattr(wtypes.DictType(str, wtypes.text))
    """Fault info collected from the heat resources of this bay"""

    def __init__(self, **kwargs):
        super(Bay, self).__init__()

        self.fields = []
        for field in objects.Bay.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(bay, url, expand=True):
        if not expand:
            bay.unset_fields_except([
                'uuid', 'name', 'baymodel_id', 'node_count', 'status',
                'bay_create_timeout', 'master_count', 'stack_id'
            ])

        bay.links = [
            link.Link.make_link('self', url, 'bays', bay.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'bays',
                                bay.uuid,
                                bookmark=True)
        ]
        return bay

    @classmethod
    def convert_with_links(cls, rpc_bay, expand=True):
        bay = Bay(**rpc_bay.as_dict())
        return cls._convert_with_links(bay, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     name='example',
                     baymodel_id='4a96ac4b-2447-43f1-8ca6-9fd6f36d146d',
                     node_count=2,
                     master_count=1,
                     bay_create_timeout=15,
                     stack_id='49dc23f5-ffc9-40c3-9d34-7be7f9e34d63',
                     status=fields.BayStatus.CREATE_COMPLETE,
                     status_reason="CREATE completed successfully",
                     api_address='172.24.4.3',
                     node_addresses=['172.24.4.4', '172.24.4.5'],
                     created_at=timeutils.utcnow(),
                     updated_at=timeutils.utcnow())
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)
Exemple #15
0
class Node(base.APIBase):
    """API representation of a bare metal node.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a node.
    """

    _chassis_uuid = None

    def _get_chassis_uuid(self):
        return self._chassis_uuid

    def _set_chassis_uuid(self, value):
        if value and self._chassis_uuid != value:
            try:
                chassis = objects.Chassis.get(pecan.request.context, value)
                self._chassis_uuid = chassis.uuid
                # NOTE(lucasagomes): Create the chassis_id attribute on-the-fly
                #                    to satisfy the api -> rpc object
                #                    conversion.
                self.chassis_id = chassis.id
            except exception.ChassisNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Port
                e.code = 400  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._chassis_uuid = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this node"""

    instance_uuid = types.uuid
    """The UUID of the instance in nova-compute"""

    power_state = wsme.wsattr(wtypes.text, readonly=True)
    """Represent the current (not transition) power state of the node"""

    target_power_state = wsme.wsattr(wtypes.text, readonly=True)
    """The user modified desired power state of the node."""

    last_error = wsme.wsattr(wtypes.text, readonly=True)
    """Any error from the most recent (last) asynchronous transaction that
    started but failed to finish."""

    provision_state = wsme.wsattr(wtypes.text, readonly=True)
    """Represent the current (not transition) provision state of the node"""

    reservation = wsme.wsattr(wtypes.text, readonly=True)
    """The hostname of the conductor that holds an exclusive lock on
    the node."""

    provision_updated_at = datetime.datetime
    """The UTC date and time of the last provision state change"""

    maintenance = types.boolean
    """Indicates whether the node is in maintenance mode."""

    maintenance_reason = wsme.wsattr(wtypes.text, readonly=True)
    """Indicates reason for putting a node in maintenance mode."""

    target_provision_state = wsme.wsattr(wtypes.text, readonly=True)
    """The user modified desired provision state of the node."""

    console_enabled = types.boolean
    """Indicates whether the console access is enabled or disabled on
    the node."""

    instance_info = {wtypes.text: types.jsontype}
    """This node's instance info."""

    driver = wsme.wsattr(wtypes.text, mandatory=True)
    """The driver responsible for controlling the node"""

    driver_info = {wtypes.text: types.jsontype}
    """This node's driver configuration"""

    extra = {wtypes.text: types.jsontype}
    """This node's meta data"""

    # NOTE: properties should use a class to enforce required properties
    #       current list: arch, cpus, disk, ram, image
    properties = {wtypes.text: types.jsontype}
    """The physical characteristics of this node"""

    chassis_uuid = wsme.wsproperty(types.uuid, _get_chassis_uuid,
                                   _set_chassis_uuid)
    """The UUID of the chassis this node belongs"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated node links"""

    ports = wsme.wsattr([link.Link], readonly=True)
    """Links to the collection of ports on this node"""

    # NOTE(deva): "conductor_affinity" shouldn't be presented on the
    #             API because it's an internal value. Don't add it here.

    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.Node.fields)
        # NOTE(lucasagomes): chassis_uuid is not part of objects.Node.fields
        # because it's an API-only attribute.
        fields.append('chassis_uuid')
        for k in fields:
            # Skip fields we do not expose.
            if not hasattr(self, k):
                continue
            self.fields.append(k)
            setattr(self, k, kwargs.get(k, wtypes.Unset))

        # NOTE(lucasagomes): chassis_id is an attribute created on-the-fly
        # by _set_chassis_uuid(), it needs to be present in the fields so
        # that as_dict() will contain chassis_id field when converting it
        # before saving it in the database.
        self.fields.append('chassis_id')
        setattr(self, 'chassis_uuid', kwargs.get('chassis_id', wtypes.Unset))

    @staticmethod
    def _convert_with_links(node, url, expand=True):
        if not expand:
            except_list = [
                'instance_uuid', 'maintenance', 'power_state',
                'provision_state', 'uuid'
            ]
            node.unset_fields_except(except_list)
        else:
            node.ports = [
                link.Link.make_link('self', url, 'nodes',
                                    node.uuid + "/ports"),
                link.Link.make_link('bookmark',
                                    url,
                                    'nodes',
                                    node.uuid + "/ports",
                                    bookmark=True)
            ]

        # NOTE(lucasagomes): The numeric ID should not be exposed to
        #                    the user, it's internal only.
        node.chassis_id = wtypes.Unset

        node.links = [
            link.Link.make_link('self', url, 'nodes', node.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'nodes',
                                node.uuid,
                                bookmark=True)
        ]
        return node

    @classmethod
    def convert_with_links(cls, rpc_node, expand=True):
        node = Node(**rpc_node.as_dict())
        return cls._convert_with_links(node, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        time = datetime.datetime(2000, 1, 1, 12, 0, 0)
        node_uuid = '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
        instance_uuid = 'dcf1fbc5-93fc-4596-9395-b80572f6267b'
        sample = cls(uuid=node_uuid,
                     instance_uuid=instance_uuid,
                     power_state=ir_states.POWER_ON,
                     target_power_state=ir_states.NOSTATE,
                     last_error=None,
                     provision_state=ir_states.ACTIVE,
                     target_provision_state=ir_states.NOSTATE,
                     reservation=None,
                     driver='fake',
                     driver_info={},
                     extra={},
                     properties={
                         'memory_mb': '1024',
                         'local_gb': '10',
                         'cpus': '1'
                     },
                     updated_at=time,
                     created_at=time,
                     provision_updated_at=time,
                     instance_info={},
                     maintenance=False,
                     maintenance_reason=None,
                     console_enabled=False)
        # NOTE(matty_dubs): The chassis_uuid getter() is based on the
        # _chassis_uuid variable:
        sample._chassis_uuid = 'edcad704-b2da-41d5-96d9-afd580ecfa12'
        return cls._convert_with_links(sample, 'http://localhost:6385', expand)
Exemple #16
0
class FailResult(BaseResult):
    code = wsme.wsattr(wtypes.text, default=0xFFFF1111)
    msg = wsme.wsattr(wtypes.text, default=str(_('Fail')))
    content = jsontype
Exemple #17
0
class BayModel(base.APIBase):
    """API representation of a Baymodel.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a Baymodel.
    """

    uuid = types.uuid
    """Unique UUID for this Baymodel"""

    name = wtypes.StringType(min_length=1, max_length=255)
    """The name of the Baymodel"""

    coe = wtypes.Enum(str, *fields.ClusterType.ALL, mandatory=True)
    """The Container Orchestration Engine for this bay model"""

    image_id = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
                           mandatory=True)
    """The image name or UUID to use as a base image for this Baymodel"""

    flavor_id = wtypes.StringType(min_length=1, max_length=255)
    """The flavor of this Baymodel"""

    master_flavor_id = wtypes.StringType(min_length=1, max_length=255)
    """The flavor of the master node for this Baymodel"""

    dns_nameserver = wtypes.IPv4AddressType()
    """The DNS nameserver address"""

    keypair_id = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
                             mandatory=True)
    """The name of the nova ssh keypair"""

    external_network_id = wtypes.StringType(min_length=1, max_length=255)
    """The external network to attach to the Bay"""

    fixed_network = wtypes.StringType(min_length=1, max_length=255)
    """The fixed network name to attach to the Bay"""

    fixed_subnet = wtypes.StringType(min_length=1, max_length=255)
    """The fixed subnet name to attach to the Bay"""

    network_driver = wtypes.StringType(min_length=1, max_length=255)
    """The name of the driver used for instantiating container networks"""

    apiserver_port = wtypes.IntegerType(minimum=1024, maximum=65535)
    """The API server port for k8s"""

    docker_volume_size = wtypes.IntegerType(minimum=1)
    """The size in GB of the docker volume"""

    cluster_distro = wtypes.StringType(min_length=1, max_length=255)
    """The Cluster distro for the bay, e.g. coreos, fedora-atomic, etc."""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated Baymodel links"""

    http_proxy = wtypes.StringType(min_length=1, max_length=255)
    """Address of a proxy that will receive all HTTP requests and relay them.
       The format is a URL including a port number.
       """

    https_proxy = wtypes.StringType(min_length=1, max_length=255)
    """Address of a proxy that will receive all HTTPS requests and relay them.
       The format is a URL including a port number.
       """

    no_proxy = wtypes.StringType(min_length=1, max_length=255)
    """A comma separated list of IPs for which proxies should not be
       used in the bay
       """

    volume_driver = wtypes.StringType(min_length=1, max_length=255)
    """The name of the driver used for instantiating container volumes"""

    registry_enabled = wsme.wsattr(types.boolean, default=False)
    """Indicates whether the docker registry is enabled"""

    labels = wtypes.DictType(str, str)
    """One or more key/value pairs"""

    tls_disabled = wsme.wsattr(types.boolean, default=False)
    """Indicates whether TLS should be disabled"""

    public = wsme.wsattr(types.boolean, default=False)
    """Indicates whether the Baymodel is public or not."""

    server_type = wsme.wsattr(wtypes.Enum(str, *fields.ServerType.ALL),
                              default='vm')
    """Server type for this bay model"""

    insecure_registry = wtypes.StringType(min_length=1, max_length=255)
    """Insecure registry URL when creating a Baymodel"""

    docker_storage_driver = wtypes.StringType(min_length=1, max_length=255)
    """Docker storage driver"""

    master_lb_enabled = wsme.wsattr(types.boolean, default=False)
    """Indicates whether created bays should have a load balancer for master
       nodes or not.
       """

    floating_ip_enabled = wsme.wsattr(types.boolean, default=True)
    """Indicates whether created bays should have a floating ip or not."""
    def __init__(self, **kwargs):
        self.fields = []
        for field in objects.ClusterTemplate.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(baymodel, url):
        baymodel.links = [
            link.Link.make_link('self', url, 'baymodels', baymodel.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'baymodels',
                                baymodel.uuid,
                                bookmark=True)
        ]
        return baymodel

    @classmethod
    def convert_with_links(cls, rpc_baymodel):
        baymodel = BayModel(**rpc_baymodel.as_dict())
        return cls._convert_with_links(baymodel, pecan.request.host_url)

    @classmethod
    def sample(cls):
        sample = cls(
            uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
            name='example',
            image_id='Fedora-k8s',
            flavor_id='m1.small',
            master_flavor_id='m1.small',
            dns_nameserver='8.8.1.1',
            keypair_id='keypair1',
            external_network_id='ffc44e4a-2319-4062-bce0-9ae1c38b05ba',
            fixed_network='private',
            fixed_subnet='private-subnet',
            network_driver='libnetwork',
            volume_driver='cinder',
            apiserver_port=8080,
            docker_volume_size=25,
            docker_storage_driver='devicemapper',
            cluster_distro='fedora-atomic',
            coe=fields.ClusterType.KUBERNETES,
            http_proxy='http://proxy.com:123',
            https_proxy='https://proxy.com:123',
            no_proxy='192.168.0.1,192.168.0.2,192.168.0.3',
            labels={
                'key1': 'val1',
                'key2': 'val2'
            },
            server_type='vm',
            insecure_registry='10.238.100.100:5000',
            created_at=timeutils.utcnow(),
            updated_at=timeutils.utcnow(),
            public=False,
            master_lb_enabled=False,
            floating_ip_enabled=True,
        )
        return cls._convert_with_links(sample, 'http://localhost:9511')
Exemple #18
0
class Bay(base.APIBase):
    """API representation of a bay.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a bay.
    """

    _baymodel_id = None

    def _get_baymodel_id(self):
        return self._baymodel_id

    def _set_baymodel_id(self, value):
        if value and self._baymodel_id != value:
            try:
                baymodel = api_utils.get_rpc_resource('BayModel', value)
                self._baymodel_id = baymodel.uuid
            except exception.BayModelNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Bay
                e.code = 400  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._baymodel_id = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this bay"""

    name = wtypes.text
    """Name of this bay"""

    baymodel_id = wsme.wsproperty(wtypes.text, _get_baymodel_id,
                                  _set_baymodel_id, mandatory=True)
    """The bay model UUID or id"""

    node_count = wtypes.IntegerType(minimum=1)
    """The node count for this bay"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated bay links"""

    status = wtypes.text
    """Status of the bay from the heat stack"""

    def __init__(self, **kwargs):
        super(Bay, self).__init__()

        self.fields = []
        for field in objects.Bay.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(bay, url, expand=True):
        if not expand:
            bay.unset_fields_except(['uuid', 'name', 'baymodel_id',
                                    'node_count', 'status'])

        bay.links = [link.Link.make_link('self', url,
                                          'bays', bay.uuid),
                      link.Link.make_link('bookmark', url,
                                          'bays', bay.uuid,
                                          bookmark=True)
                     ]
        return bay

    @classmethod
    def convert_with_links(cls, rpc_bay, expand=True):
        bay = Bay(**rpc_bay.as_dict())
        return cls._convert_with_links(bay, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     name='example',
                     baymodel_id='4a96ac4b-2447-43f1-8ca6-9fd6f36d146d',
                     node_count=1,
                     status="CREATED",
                     created_at=datetime.datetime.utcnow(),
                     updated_at=datetime.datetime.utcnow())
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)
Exemple #19
0
class EventLog(base.APIBase):
    """API representation of an event log.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of
    a event_log.
    """

    uuid = types.uuid
    "The UUID of the event_log"

    event_log_id = wsme.wsattr(wtypes.text, mandatory=True)
    "structured id for the event log;   AREA_ID  ID;   300-001"

    state = wsme.wsattr(wtypes.text, mandatory=True)
    "The state of the event"

    entity_type_id = wtypes.text
    "The type of the object event log"

    entity_instance_id = wsme.wsattr(wtypes.text, mandatory=True)
    "The original instance information of the object creating event log"

    timestamp = datetime.datetime
    "The time in UTC at which the event log is generated"

    severity = wsme.wsattr(wtypes.text, mandatory=True)
    "The severity of the log"

    reason_text = wtypes.text
    "The reason why the log is generated"

    event_log_type = wsme.wsattr(wtypes.text, mandatory=True)
    "The type of the event log"

    probable_cause = wsme.wsattr(wtypes.text, mandatory=True)
    "The probable cause of the event log"

    proposed_repair_action = wtypes.text
    "The action to clear the alarm"

    service_affecting = bool
    "Whether the log affects the service"

    suppression = bool
    "'allowed' or 'not-allowed'"

    suppression_status = wtypes.text
    "'suppressed' or 'unsuppressed'"

    links = [link.Link]
    "A list containing a self link and associated event links"

    def __init__(self, **kwargs):

        self.fields = objects.event_log.fields.keys()
        for k in self.fields:
            setattr(self, k, kwargs.get(k))

    @classmethod
    def convert_with_links(cls, rpc_event_log, expand=True):

        if isinstance(rpc_event_log, tuple):
            ievent_log = rpc_event_log[0]
            suppress_status = rpc_event_log[1]
        else:
            ievent_log = rpc_event_log
            suppress_status = rpc_event_log.suppression_status

        ievent_log['service_affecting'] = ievent_log['service_affecting']
        ievent_log['suppression'] = ievent_log['suppression']

        ilog = EventLog(**ievent_log.as_dict())
        if not expand:
            ilog.unset_fields_except([
                'uuid', 'event_log_id', 'entity_instance_id', 'severity',
                'timestamp', 'reason_text', 'state'
            ])

        ilog.entity_instance_id = \
            utils.make_display_id(ilog.entity_instance_id, replace=False)

        ilog.suppression_status = str(suppress_status)

        return ilog
class AuditTemplate(base.APIBase):
    """API representation of a audit template.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of an
    audit template.
    """

    _goal_uuid = None
    _goal_name = None

    _strategy_uuid = None
    _strategy_name = None

    def _get_goal(self, value):
        if value == wtypes.Unset:
            return None
        goal = None
        try:
            if (common_utils.is_uuid_like(value) or
                    common_utils.is_int_like(value)):
                goal = objects.Goal.get(
                    pecan.request.context, value)
            else:
                goal = objects.Goal.get_by_name(
                    pecan.request.context, value)
        except exception.GoalNotFound:
            pass
        if goal:
            self.goal_id = goal.id
        return goal

    def _get_strategy(self, value):
        if value == wtypes.Unset:
            return None
        strategy = None
        try:
            if (common_utils.is_uuid_like(value) or
                    common_utils.is_int_like(value)):
                strategy = objects.Strategy.get(
                    pecan.request.context, value)
            else:
                strategy = objects.Strategy.get_by_name(
                    pecan.request.context, value)
        except exception.StrategyNotFound:
            pass
        if strategy:
            self.strategy_id = strategy.id
        return strategy

    def _get_goal_uuid(self):
        return self._goal_uuid

    def _set_goal_uuid(self, value):
        if value and self._goal_uuid != value:
            self._goal_uuid = None
            goal = self._get_goal(value)
            if goal:
                self._goal_uuid = goal.uuid

    def _get_strategy_uuid(self):
        return self._strategy_uuid

    def _set_strategy_uuid(self, value):
        if value and self._strategy_uuid != value:
            self._strategy_uuid = None
            strategy = self._get_strategy(value)
            if strategy:
                self._strategy_uuid = strategy.uuid

    def _get_goal_name(self):
        return self._goal_name

    def _set_goal_name(self, value):
        if value and self._goal_name != value:
            self._goal_name = None
            goal = self._get_goal(value)
            if goal:
                self._goal_name = goal.name

    def _get_strategy_name(self):
        return self._strategy_name

    def _set_strategy_name(self, value):
        if value and self._strategy_name != value:
            self._strategy_name = None
            strategy = self._get_strategy(value)
            if strategy:
                self._strategy_name = strategy.name

    uuid = wtypes.wsattr(types.uuid, readonly=True)
    """Unique UUID for this audit template"""

    name = wtypes.text
    """Name of this audit template"""

    description = wtypes.wsattr(wtypes.text, mandatory=False)
    """Short description of this audit template"""

    goal_uuid = wsme.wsproperty(
        wtypes.text, _get_goal_uuid, _set_goal_uuid, mandatory=True)
    """Goal UUID the audit template refers to"""

    goal_name = wsme.wsproperty(
        wtypes.text, _get_goal_name, _set_goal_name, mandatory=False)
    """The name of the goal this audit template refers to"""

    strategy_uuid = wsme.wsproperty(
        wtypes.text, _get_strategy_uuid, _set_strategy_uuid, mandatory=False)
    """Strategy UUID the audit template refers to"""

    strategy_name = wsme.wsproperty(
        wtypes.text, _get_strategy_name, _set_strategy_name, mandatory=False)
    """The name of the strategy this audit template refers to"""

    audits = wsme.wsattr([link.Link], readonly=True)
    """Links to the collection of audits contained in this audit template"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated audit template links"""

    scope = wsme.wsattr(types.jsontype, mandatory=False)
    """Audit Scope"""

    def __init__(self, **kwargs):
        super(AuditTemplate, self).__init__()
        self.fields = []
        fields = list(objects.AuditTemplate.fields)

        for k in fields:
            # Skip fields we do not expose.
            if not hasattr(self, k):
                continue
            self.fields.append(k)
            setattr(self, k, kwargs.get(k, wtypes.Unset))

        self.fields.append('goal_id')
        self.fields.append('strategy_id')
        setattr(self, 'strategy_id', kwargs.get('strategy_id', wtypes.Unset))

        # goal_uuid & strategy_uuid are not part of
        # objects.AuditTemplate.fields because they're API-only attributes.
        self.fields.append('goal_uuid')
        self.fields.append('goal_name')
        self.fields.append('strategy_uuid')
        self.fields.append('strategy_name')
        setattr(self, 'goal_uuid', kwargs.get('goal_id', wtypes.Unset))
        setattr(self, 'goal_name', kwargs.get('goal_id', wtypes.Unset))
        setattr(self, 'strategy_uuid',
                kwargs.get('strategy_id', wtypes.Unset))
        setattr(self, 'strategy_name',
                kwargs.get('strategy_id', wtypes.Unset))

    @staticmethod
    def _convert_with_links(audit_template, url, expand=True):
        if not expand:
            audit_template.unset_fields_except(
                ['uuid', 'name', 'goal_uuid', 'goal_name',
                 'scope', 'strategy_uuid', 'strategy_name'])

        # The numeric ID should not be exposed to
        # the user, it's internal only.
        audit_template.goal_id = wtypes.Unset
        audit_template.strategy_id = wtypes.Unset

        audit_template.links = [link.Link.make_link('self', url,
                                                    'audit_templates',
                                                    audit_template.uuid),
                                link.Link.make_link('bookmark', url,
                                                    'audit_templates',
                                                    audit_template.uuid,
                                                    bookmark=True)]
        return audit_template

    @classmethod
    def convert_with_links(cls, rpc_audit_template, expand=True):
        audit_template = AuditTemplate(**rpc_audit_template.as_dict())
        return cls._convert_with_links(audit_template, pecan.request.host_url,
                                       expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     name='My Audit Template',
                     description='Description of my audit template',
                     goal_uuid='83e44733-b640-40e2-8d8a-7dd3be7134e6',
                     strategy_uuid='367d826e-b6a4-4b70-bc44-c3f6fe1c9986',
                     created_at=datetime.datetime.utcnow(),
                     deleted_at=None,
                     updated_at=datetime.datetime.utcnow(),
                     scope=[],)
        return cls._convert_with_links(sample, 'http://localhost:9322', expand)
Exemple #21
0
class Portgroup(base.APIBase):
    """API representation of a portgroup.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a
    portgroup.
    """

    _node_uuid = None

    def _get_node_uuid(self):
        return self._node_uuid

    def _set_node_uuid(self, value):
        if value and self._node_uuid != value:
            if not api_utils.allow_portgroups():
                self._node_uuid = wtypes.Unset
                return
            try:
                node = objects.Node.get(pecan.request.context, value)
                self._node_uuid = node.uuid
                # NOTE: Create the node_id attribute on-the-fly
                #       to satisfy the api -> rpc object
                #       conversion.
                self.node_id = node.id
            except exception.NodeNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Portgroup
                e.code = http_client.BAD_REQUEST
                raise e
        elif value == wtypes.Unset:
            self._node_uuid = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this portgroup"""

    address = wsme.wsattr(types.macaddress)
    """MAC Address for this portgroup"""

    extra = {wtypes.text: types.jsontype}
    """This portgroup's meta data"""

    internal_info = wsme.wsattr({wtypes.text: types.jsontype}, readonly=True)
    """This portgroup's internal info"""

    node_uuid = wsme.wsproperty(types.uuid,
                                _get_node_uuid,
                                _set_node_uuid,
                                mandatory=True)
    """The UUID of the node this portgroup belongs to"""

    name = wsme.wsattr(wtypes.text)
    """The logical name for this portgroup"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated portgroup links"""

    standalone_ports_supported = types.boolean
    """Indicates whether ports of this portgroup may be used as
       single NIC ports"""

    mode = wsme.wsattr(wtypes.text)
    """The mode for this portgroup. See linux bonding
    documentation for details:
    https://www.kernel.org/doc/Documentation/networking/bonding.txt"""

    properties = {wtypes.text: types.jsontype}
    """This portgroup's properties"""

    ports = wsme.wsattr([link.Link], readonly=True)
    """Links to the collection of ports of this portgroup"""

    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.Portgroup.fields)
        # NOTE: node_uuid is not part of objects.Portgroup.fields
        #       because it's an API-only attribute
        fields.append('node_uuid')
        for field in fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

        # NOTE: node_id is an attribute created on-the-fly
        # by _set_node_uuid(), it needs to be present in the fields so
        # that as_dict() will contain node_id field when converting it
        # before saving it in the database.
        self.fields.append('node_id')
        setattr(self, 'node_uuid', kwargs.get('node_id', wtypes.Unset))

    @staticmethod
    def _convert_with_links(portgroup, url, fields=None):
        """Add links to the portgroup."""
        # NOTE(lucasagomes): Since we are able to return a specified set of
        # fields the "uuid" can be unset, so we need to save it in another
        # variable to use when building the links
        portgroup_uuid = portgroup.uuid
        if fields is not None:
            portgroup.unset_fields_except(fields)
        else:
            portgroup.ports = [
                link.Link.make_link('self', url, 'portgroups',
                                    portgroup_uuid + "/ports"),
                link.Link.make_link('bookmark',
                                    url,
                                    'portgroups',
                                    portgroup_uuid + "/ports",
                                    bookmark=True)
            ]

        # never expose the node_id attribute
        portgroup.node_id = wtypes.Unset

        portgroup.links = [
            link.Link.make_link('self', url, 'portgroups', portgroup_uuid),
            link.Link.make_link('bookmark',
                                url,
                                'portgroups',
                                portgroup_uuid,
                                bookmark=True)
        ]
        return portgroup

    @classmethod
    def convert_with_links(cls, rpc_portgroup, fields=None):
        """Add links to the portgroup."""
        portgroup = Portgroup(**rpc_portgroup.as_dict())

        if fields is not None:
            api_utils.check_for_invalid_fields(fields, portgroup.as_dict())

        return cls._convert_with_links(portgroup,
                                       pecan.request.host_url,
                                       fields=fields)

    @classmethod
    def sample(cls, expand=True):
        """Return a sample of the portgroup."""
        sample = cls(uuid='a594544a-2daf-420c-8775-17a8c3e0852f',
                     address='fe:54:00:77:07:d9',
                     name='node1-portgroup-01',
                     extra={'foo': 'bar'},
                     internal_info={'baz': 'boo'},
                     standalone_ports_supported=True,
                     mode='active-backup',
                     properties={},
                     created_at=datetime.datetime(2000, 1, 1, 12, 0, 0),
                     updated_at=datetime.datetime(2000, 1, 1, 12, 0, 0))
        # NOTE(lucasagomes): node_uuid getter() method look at the
        # _node_uuid variable
        sample._node_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
        fields = None if expand else _DEFAULT_RETURN_FIELDS
        return cls._convert_with_links(sample,
                                       'http://localhost:6385',
                                       fields=fields)
Exemple #22
0
class Audit(base.APIBase):
    """API representation of a audit.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a audit.
    """
    _audit_template_uuid = None
    _audit_template_name = None

    def _get_audit_template(self, value):
        if value == wtypes.Unset:
            return None
        audit_template = None
        try:
            if utils.is_uuid_like(value) or utils.is_int_like(value):
                audit_template = objects.AuditTemplate.get(
                    pecan.request.context, value)
            else:
                audit_template = objects.AuditTemplate.get_by_name(
                    pecan.request.context, value)
        except exception.AuditTemplateNotFound:
            pass
        if audit_template:
            self.audit_template_id = audit_template.id
        return audit_template

    def _get_audit_template_uuid(self):
        return self._audit_template_uuid

    def _set_audit_template_uuid(self, value):
        if value and self._audit_template_uuid != value:
            self._audit_template_uuid = None
            audit_template = self._get_audit_template(value)
            if audit_template:
                self._audit_template_uuid = audit_template.uuid

    def _get_audit_template_name(self):
        return self._audit_template_name

    def _set_audit_template_name(self, value):
        if value and self._audit_template_name != value:
            self._audit_template_name = None
            audit_template = self._get_audit_template(value)
            if audit_template:
                self._audit_template_name = audit_template.name

    uuid = types.uuid
    """Unique UUID for this audit"""

    type = wtypes.text
    """Type of this audit"""

    deadline = datetime.datetime
    """deadline of the audit"""

    state = wtypes.text
    """This audit state"""

    audit_template_uuid = wsme.wsproperty(wtypes.text,
                                          _get_audit_template_uuid,
                                          _set_audit_template_uuid,
                                          mandatory=True)
    """The UUID of the audit template this audit refers to"""

    audit_template_name = wsme.wsproperty(wtypes.text,
                                          _get_audit_template_name,
                                          _set_audit_template_name,
                                          mandatory=False)
    """The name of the audit template this audit refers to"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated audit links"""

    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.Audit.fields)

        for k in fields:
            # Skip fields we do not expose.
            if not hasattr(self, k):
                continue
            self.fields.append(k)
            setattr(self, k, kwargs.get(k, wtypes.Unset))

        self.fields.append('audit_template_id')

        # audit_template_uuid & audit_template_name are not part of
        # objects.Audit.fields because they're API-only attributes.
        fields.append('audit_template_uuid')
        setattr(self, 'audit_template_uuid', kwargs.get('audit_template_id',
                wtypes.Unset))
        fields.append('audit_template_name')
        setattr(self, 'audit_template_name', kwargs.get('audit_template_id',
                wtypes.Unset))

    @staticmethod
    def _convert_with_links(audit, url, expand=True):
        if not expand:
            audit.unset_fields_except(['uuid', 'type', 'deadline',
                                       'state', 'audit_template_uuid',
                                       'audit_template_name'])

        # The numeric ID should not be exposed to
        # the user, it's internal only.
        audit.audit_template_id = wtypes.Unset

        audit.links = [link.Link.make_link('self', url,
                                           'audits', audit.uuid),
                       link.Link.make_link('bookmark', url,
                                           'audits', audit.uuid,
                                           bookmark=True)
                       ]

        return audit

    @classmethod
    def convert_with_links(cls, rpc_audit, expand=True):
        audit = Audit(**rpc_audit.as_dict())
        return cls._convert_with_links(audit, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     type='ONESHOT',
                     state='PENDING',
                     deadline=None,
                     created_at=datetime.datetime.utcnow(),
                     deleted_at=None,
                     updated_at=datetime.datetime.utcnow())
        sample._audit_template_uuid = '7ae81bb3-dec3-4289-8d6c-da80bd8001ae'
        return cls._convert_with_links(sample, 'http://localhost:9322', expand)
Exemple #23
0
class Query(Base):
    """Query filter."""

    # The data types supported by the query.
    _supported_types = ['integer', 'float', 'string', 'boolean', 'datetime']

    # Functions to convert the data field to the correct type.
    _type_converters = {
        'integer': int,
        'float': float,
        'boolean': functools.partial(strutils.bool_from_string, strict=True),
        'string': six.text_type,
        'datetime': timeutils.parse_isotime
    }

    _op = None  # provide a default

    def get_op(self):
        return self._op or 'eq'

    def set_op(self, value):
        self._op = value

    field = wsme.wsattr(wtypes.text, mandatory=True)
    "The name of the field to test"

    # op = wsme.wsattr(operation_kind, default='eq')
    # this ^ doesn't seem to work.
    op = wsme.wsproperty(operation_kind_enum, get_op, set_op)
    "The comparison operator. Defaults to 'eq'."

    value = wsme.wsattr(wtypes.text, mandatory=True)
    "The value to compare against the stored data"

    type = wtypes.text
    "The data type of value to compare against the stored data"

    def __repr__(self):
        # for logging calls
        return '<Query %r %s %r %s>' % (self.field, self.op, self.value,
                                        self.type)

    @classmethod
    def sample(cls):
        return cls(field='resource_id',
                   op='eq',
                   value='bd9431c1-8d69-4ad3-803a-8d4a6b89fd36',
                   type='string')

    def as_dict(self):
        return self.as_dict_from_keys(['field', 'op', 'type', 'value'])

    def get_value(self, forced_type=None):
        """Convert metadata value to the specified data type.

        This method is called during metadata query to help convert the
        querying metadata to the data type specified by user. If there is no
        data type given, the metadata will be parsed by ast.literal_eval to
        try to do a smart converting.

        NOTE (flwang) Using "_" as prefix to avoid an InvocationError raised
        from wsmeext/sphinxext.py. It's OK to call it outside the Query class.
        Because the "public" side of that class is actually the outside of the
        API, and the "private" side is the API implementation. The method is
        only used in the API implementation, so it's OK.

        :returns: metadata value converted with the specified data type.
        """
        type = forced_type or self.type
        try:
            converted_value = self.value
            if not type:
                try:
                    converted_value = ast.literal_eval(self.value)
                except (ValueError, SyntaxError):
                    # Unable to convert the metadata value automatically
                    # let it default to self.value
                    pass
            else:
                if type not in self._supported_types:
                    # Types must be explicitly declared so the
                    # correct type converter may be used. Subclasses
                    # of Query may define _supported_types and
                    # _type_converters to define their own types.
                    raise TypeError()
                converted_value = self._type_converters[type](self.value)
                if isinstance(converted_value, datetime.datetime):
                    converted_value = timeutils.normalize_time(converted_value)
        except ValueError:
            msg = (_('Unable to convert the value %(value)s'
                     ' to the expected data type %(type)s.') % {
                         'value': self.value,
                         'type': type
                     })
            raise ClientSideError(msg)
        except TypeError:
            msg = (_('The data type %(type)s is not supported. The supported'
                     ' data type list is: %(supported)s') % {
                         'type': type,
                         'supported': self._supported_types
                     })
            raise ClientSideError(msg)
        except Exception:
            msg = (_('Unexpected exception converting %(value)s to'
                     ' the expected data type %(type)s.') % {
                         'value': self.value,
                         'type': type
                     })
            raise ClientSideError(msg)
        return converted_value
Exemple #24
0
class HelloResult(wtypes.Base):
    status_code = wsme.wsattr(int, default=0)
    detail = wsme.wsattr(wtypes.text, default=str(_('success')))
    content = _types.jsontype
Exemple #25
0
class BayModel(base.APIBase):
    """API representation of a baymodel.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a baymodel.
    """

    _coe = None

    def _get_coe(self):
        return self._coe

    def _set_coe(self, value):
        if value and self._coe != value:
            self._coe = value
        elif value == wtypes.Unset:
            self._coe = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this baymodel"""

    name = wtypes.StringType(min_length=1, max_length=255)
    """The name of the bay model"""

    coe = wsme.wsproperty(wtypes.text, _get_coe, _set_coe, mandatory=True)
    """The Container Orchestration Engine for this bay model"""

    image_id = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
                           mandatory=True)
    """The image name or UUID to use as a base image for this baymodel"""

    flavor_id = wtypes.StringType(min_length=1, max_length=255)
    """The flavor of this bay model"""

    master_flavor_id = wtypes.StringType(min_length=1, max_length=255)
    """The flavor of the master node for this bay model"""

    dns_nameserver = wtypes.IPv4AddressType()
    """The DNS nameserver address"""

    keypair_id = wsme.wsattr(wtypes.StringType(min_length=1, max_length=255),
                             mandatory=True)
    """The name or id of the nova ssh keypair"""

    external_network_id = wtypes.StringType(min_length=1, max_length=255)
    """The external network to attach the Bay"""

    fixed_network = wtypes.StringType(min_length=1, max_length=255)
    """The fixed network name to attach the Bay"""

    apiserver_port = wtypes.IntegerType(minimum=1024, maximum=65535)
    """The API server port for k8s"""

    docker_volume_size = wtypes.IntegerType(minimum=1)
    """The size in GB of the docker volume"""

    ssh_authorized_key = wtypes.StringType(min_length=1)
    """The SSH Authorized Key"""

    cluster_distro = wtypes.StringType(min_length=1, max_length=255)
    """The Cluster distro for the bay, ex - coreos, fedora-atomic."""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated baymodel links"""

    def __init__(self, **kwargs):
        self.fields = []
        for field in objects.BayModel.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(baymodel, url, expand=True):
        if not expand:
            baymodel.unset_fields_except(['uuid', 'name', 'image_id',
                                          'apiserver_port', 'coe'])

        baymodel.links = [link.Link.make_link('self', url,
                                              'baymodels', baymodel.uuid),
                          link.Link.make_link('bookmark', url,
                                              'baymodels', baymodel.uuid,
                                              bookmark=True)]
        return baymodel

    @classmethod
    def convert_with_links(cls, rpc_baymodel, expand=True):
        baymodel = BayModel(**rpc_baymodel.as_dict())
        return cls._convert_with_links(baymodel, pecan.request.host_url,
                                       expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(
            uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
            name='example',
            image_id='Fedora-k8s',
            flavor_id='m1.small',
            master_flavor_id='m1.small',
            dns_nameserver='8.8.1.1',
            keypair_id='keypair1',
            external_network_id='ffc44e4a-2319-4062-bce0-9ae1c38b05ba',
            fixed_network='private',
            apiserver_port=8080,
            docker_volume_size=25,
            cluster_distro='fedora-atomic',
            ssh_authorized_key='ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB',
            coe='kubernetes',
            created_at=datetime.datetime.utcnow(),
            updated_at=datetime.datetime.utcnow())
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)
Exemple #26
0
class Chassis(base.APIBase):
    """API representation of a chassis.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of
    a chassis.
    """

    uuid = types.uuid
    "The UUID of the chassis"

    description = wtypes.text
    "The description of the chassis"

    extra = {wtypes.text: types.MultiType(wtypes.text, six.integer_types)}
    "The metadata of the chassis"

    links = wsme.wsattr([link.Link], readonly=True)
    "A list containing a self link and associated chassis links"

    nodes = wsme.wsattr([link.Link], readonly=True)
    "Links to the collection of nodes contained in this chassis"

    def __init__(self, **kwargs):
        self.fields = []
        for field in objects.Chassis.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field))

    @classmethod
    def _convert_with_links(cls, chassis, url, expand=True):
        if not expand:
            chassis.unset_fields_except(['uuid', 'description'])
        else:
            chassis.nodes = [
                link.Link.make_link('self', url, 'chassis',
                                    chassis.uuid + "/nodes"),
                link.Link.make_link('bookmark',
                                    url,
                                    'chassis',
                                    chassis.uuid + "/nodes",
                                    bookmark=True)
            ]
        chassis.links = [
            link.Link.make_link('self', url, 'chassis', chassis.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'chassis',
                                chassis.uuid,
                                bookmark=True)
        ]
        return chassis

    @classmethod
    def convert_with_links(cls, rpc_chassis, expand=True):
        chassis = Chassis(**rpc_chassis.as_dict())
        return cls._convert_with_links(chassis, pecan.request.host_url, expand)

    @classmethod
    def sample(cls, expand=True):
        time = datetime.datetime(2000, 1, 1, 12, 0, 0)
        sample = cls(uuid='eaaca217-e7d8-47b4-bb41-3f99f20eed89',
                     extra={},
                     description='Sample chassis',
                     created_at=time,
                     updated_at=time)
        return cls._convert_with_links(sample, 'http://localhost:6385', expand)
Exemple #27
0
class DeployTemplate(base.APIBase):
    """API representation of a deploy template."""

    uuid = types.uuid
    """Unique UUID for this deploy template."""

    name = wsme.wsattr(wtypes.text, mandatory=True)
    """The logical name for this deploy template."""

    steps = wsme.wsattr([DeployStepType], mandatory=True)
    """The deploy steps of this deploy template."""

    links = wsme.wsattr([link.Link])
    """A list containing a self link and associated deploy template links."""

    extra = {wtypes.text: types.jsontype}
    """This deploy template's meta data"""
    def __init__(self, **kwargs):
        self.fields = []
        fields = list(objects.DeployTemplate.fields)

        for field in fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue

            value = kwargs.get(field, wtypes.Unset)
            if field == 'steps' and value != wtypes.Unset:
                value = [DeployStepType(**step) for step in value]
            self.fields.append(field)
            setattr(self, field, value)

    @staticmethod
    def validate(value):
        if value is None:
            return

        # The name is mandatory, but the 'mandatory' attribute support in
        # wtypes.wsattr allows None.
        if value.name is None:
            err = _("Deploy template name cannot be None")
            raise exception.InvalidDeployTemplate(err=err)

        # The name must also be a valid trait.
        api_utils.validate_trait(
            value.name,
            error_prefix=_("Deploy template name must be a valid trait"))

        # There must be at least one step.
        if not value.steps:
            err = _("No deploy steps specified. A deploy template must have "
                    "at least one deploy step.")
            raise exception.InvalidDeployTemplate(err=err)

        # TODO(mgoddard): Determine the consequences of allowing duplicate
        # steps.
        # * What if one step has zero priority and another non-zero?
        # * What if a step that is enabled by default is included in a
        #   template? Do we override the default or add a second invocation?

        # Check for duplicate steps. Each interface/step combination can be
        # specified at most once.
        counter = collections.Counter(
            (step.interface, step.step) for step in value.steps)
        duplicates = {key for key, count in counter.items() if count > 1}
        if duplicates:
            duplicates = {
                "interface: %s, step: %s" % (interface, step)
                for interface, step in duplicates
            }
            err = _("Duplicate deploy steps. A deploy template cannot have "
                    "multiple deploy steps with the same interface and step. "
                    "Duplicates: %s") % "; ".join(duplicates)
            raise exception.InvalidDeployTemplate(err=err)
        return value

    @staticmethod
    def _convert_with_links(template, url, fields=None):
        template.links = [
            link.Link.make_link('self', url, 'deploy_templates',
                                template.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'deploy_templates',
                                template.uuid,
                                bookmark=True)
        ]
        return template

    @classmethod
    def convert_with_links(cls, rpc_template, fields=None, sanitize=True):
        """Add links to the deploy template."""
        template = DeployTemplate(**rpc_template.as_dict())

        if fields is not None:
            api_utils.check_for_invalid_fields(fields, template.as_dict())

        template = cls._convert_with_links(template,
                                           api.request.public_url,
                                           fields=fields)
        if sanitize:
            template.sanitize(fields)

        return template

    def sanitize(self, fields):
        """Removes sensitive and unrequested data.

        Will only keep the fields specified in the ``fields`` parameter.

        :param fields:
            list of fields to preserve, or ``None`` to preserve them all
        :type fields: list of str
        """
        if self.steps != wtypes.Unset:
            for step in self.steps:
                step.sanitize()

        if fields is not None:
            self.unset_fields_except(fields)

    @classmethod
    def sample(cls, expand=True):
        time = datetime.datetime(2000, 1, 1, 12, 0, 0)
        template_uuid = '534e73fa-1014-4e58-969a-814cc0cb9d43'
        template_name = 'CUSTOM_RAID1'
        template_steps = [{
            "interface": "raid",
            "step": "create_configuration",
            "args": {
                "logical_disks": [{
                    "size_gb": "MAX",
                    "raid_level": "1",
                    "is_root_volume": True
                }],
                "delete_configuration":
                True
            },
            "priority": 10
        }]
        template_extra = {'foo': 'bar'}
        sample = cls(uuid=template_uuid,
                     name=template_name,
                     steps=template_steps,
                     extra=template_extra,
                     created_at=time,
                     updated_at=time)
        fields = None if expand else _DEFAULT_RETURN_FIELDS
        return cls._convert_with_links(sample,
                                       'http://localhost:6385',
                                       fields=fields)
Exemple #28
0
class Container(base.APIBase):
    """API representation of a container.

    This class enforces type checking and value constraints, and converts
    between the internal object model and the API representation of a
    container.
    """

    _bay_uuid = None

    def _get_bay_uuid(self):
        return self._bay_uuid

    def _set_bay_uuid(self, value):
        if value and self._bay_uuid != value:
            try:
                bay = objects.Bay.get_by_uuid(pecan.request.context, value)
                self._bay_uuid = bay['uuid']
            except exception.BayNotFound as e:
                # Change error code because 404 (NotFound) is inappropriate
                # response for a POST request to create a Service
                e.code = 400  # BadRequest
                raise e
        elif value == wtypes.Unset:
            self._bay_uuid = wtypes.Unset

    uuid = types.uuid
    """Unique UUID for this container"""

    name = wtypes.StringType(min_length=1, max_length=255)
    """Name of this container"""

    image = wtypes.text
    """The image name or ID to use as a base image for this container"""

    bay_uuid = wsme.wsproperty(types.uuid,
                               _get_bay_uuid,
                               _set_bay_uuid,
                               mandatory=True)
    """Unique UUID of the bay this runs on"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing a self link and associated container links"""

    command = wtypes.text
    """The command execute when container starts"""

    status = wtypes.text
    """The status of container"""

    memory = wtypes.text
    """Memory limit for the container. Example: 512m"""

    environment = wtypes.DictType(str, str)
    """One or more key/value pairs"""

    def __init__(self, **kwargs):
        self.fields = []
        for field in objects.Container.fields:
            # Skip fields we do not expose.
            if not hasattr(self, field):
                continue
            self.fields.append(field)
            setattr(self, field, kwargs.get(field, wtypes.Unset))

    @staticmethod
    def _convert_with_links(container, url, expand=True):
        if not expand:
            container.unset_fields_except([
                'uuid', 'name', 'bay_uuid', 'image', 'command', 'status',
                'memory', 'environment'
            ])

        container.links = [
            link.Link.make_link('self', url, 'containers', container.uuid),
            link.Link.make_link('bookmark',
                                url,
                                'containers',
                                container.uuid,
                                bookmark=True)
        ]
        return container

    @classmethod
    def convert_with_links(cls, rpc_container, expand=True):
        container = Container(**rpc_container.as_dict())
        return cls._convert_with_links(container, pecan.request.host_url,
                                       expand)

    @classmethod
    def sample(cls, expand=True):
        sample = cls(uuid='27e3153e-d5bf-4b7e-b517-fb518e17f34c',
                     name='example',
                     image='ubuntu',
                     command='env',
                     status='Running',
                     memory='512m',
                     environment={
                         'key1': 'val1',
                         'key2': 'val2'
                     },
                     bay_uuid="fff114da-3bfa-4a0f-a123-c0dffad9718e",
                     created_at=timeutils.utcnow(),
                     updated_at=timeutils.utcnow())
        return cls._convert_with_links(sample, 'http://localhost:9511', expand)
Exemple #29
0
class PropertyType(types.Base, WSMEModelTransformer):
    # When used in collection of PropertyTypes, name is a dictionary key
    # and not included as separate field.
    name = wsme.wsattr(types.text, mandatory=False)

    type = wsme.wsattr(types.text, mandatory=True)
    title = wsme.wsattr(types.text, mandatory=True)
    description = wsme.wsattr(types.text, mandatory=False)
    operators = wsme.wsattr([types.text], mandatory=False)
    default = wsme.wsattr(types.bytes, mandatory=False)
    readonly = wsme.wsattr(bool, mandatory=False)

    # fields for type = string
    minimum = wsme.wsattr(int, mandatory=False)
    maximum = wsme.wsattr(int, mandatory=False)
    enum = wsme.wsattr([types.text], mandatory=False)
    pattern = wsme.wsattr(types.text, mandatory=False)

    # fields for type = integer, number
    minLength = wsme.wsattr(int, mandatory=False)
    maxLength = wsme.wsattr(int, mandatory=False)
    confidential = wsme.wsattr(bool, mandatory=False)

    # fields for type = array
    items = wsme.wsattr(ItemType, mandatory=False)
    uniqueItems = wsme.wsattr(bool, mandatory=False)
    minItems = wsme.wsattr(int, mandatory=False)
    maxItems = wsme.wsattr(int, mandatory=False)
    additionalItems = wsme.wsattr(bool, mandatory=False)

    def __init__(self, **kwargs):
        super(PropertyType, self).__init__(**kwargs)
class Driver(base.APIBase):
    """API representation of a driver."""

    name = wtypes.text
    """The name of the driver"""

    hosts = [wtypes.text]
    """A list of active conductors that support this driver"""

    type = wtypes.text
    """Whether the driver is classic or dynamic (hardware type)"""

    links = wsme.wsattr([link.Link], readonly=True)
    """A list containing self and bookmark links"""

    properties = wsme.wsattr([link.Link], readonly=True)
    """A list containing links to driver properties"""

    """Default interface for a hardware type"""
    default_boot_interface = wtypes.text
    default_console_interface = wtypes.text
    default_deploy_interface = wtypes.text
    default_inspect_interface = wtypes.text
    default_management_interface = wtypes.text
    default_network_interface = wtypes.text
    default_power_interface = wtypes.text
    default_raid_interface = wtypes.text
    default_storage_interface = wtypes.text
    default_vendor_interface = wtypes.text

    """A list of enabled interfaces for a hardware type"""
    enabled_boot_interfaces = [wtypes.text]
    enabled_console_interfaces = [wtypes.text]
    enabled_deploy_interfaces = [wtypes.text]
    enabled_inspect_interfaces = [wtypes.text]
    enabled_management_interfaces = [wtypes.text]
    enabled_network_interfaces = [wtypes.text]
    enabled_power_interfaces = [wtypes.text]
    enabled_raid_interfaces = [wtypes.text]
    enabled_storage_interfaces = [wtypes.text]
    enabled_vendor_interfaces = [wtypes.text]

    @staticmethod
    def convert_with_links(name, hosts, driver_type, detail=False,
                           interface_info=None):
        """Convert driver/hardware type info to an API-serializable object.

        :param name: name of driver or hardware type.
        :param hosts: list of conductor hostnames driver is active on.
        :param driver_type: 'classic' for classic drivers, 'dynamic' for
                            hardware types.
        :param detail: boolean, whether to include detailed info, such as
                       the 'type' field and default/enabled interfaces fields.
        :param interface_info: optional list of dicts of hardware interface
                               info.
        :returns: API-serializable driver object.
        """
        driver = Driver()
        driver.name = name
        driver.hosts = hosts
        driver.links = [
            link.Link.make_link('self',
                                pecan.request.public_url,
                                'drivers', name),
            link.Link.make_link('bookmark',
                                pecan.request.public_url,
                                'drivers', name,
                                bookmark=True)
        ]
        if api_utils.allow_links_node_states_and_driver_properties():
            driver.properties = [
                link.Link.make_link('self',
                                    pecan.request.public_url,
                                    'drivers', name + "/properties"),
                link.Link.make_link('bookmark',
                                    pecan.request.public_url,
                                    'drivers', name + "/properties",
                                    bookmark=True)
            ]

        if api_utils.allow_dynamic_drivers():
            driver.type = driver_type
            if driver_type == 'dynamic' and detail:
                if interface_info is None:
                    # TODO(jroll) objectify this
                    interface_info = (pecan.request.dbapi
                                      .list_hardware_type_interfaces([name]))
                for iface_type in driver_base.ALL_INTERFACES:
                    default = None
                    enabled = set()
                    for iface in interface_info:
                        if iface['interface_type'] == iface_type:
                            iface_name = iface['interface_name']
                            enabled.add(iface_name)
                            # NOTE(jroll) this assumes the default is the same
                            # on all conductors
                            if iface['default']:
                                default = iface_name

                    default_key = 'default_%s_interface' % iface_type
                    enabled_key = 'enabled_%s_interfaces' % iface_type
                    setattr(driver, default_key, default)
                    setattr(driver, enabled_key, list(enabled))

            elif detail:
                for iface_type in driver_base.ALL_INTERFACES:
                    # always return None for classic drivers
                    setattr(driver, 'default_%s_interface' % iface_type, None)
                    setattr(driver, 'enabled_%s_interfaces' % iface_type, None)

        hide_fields_in_newer_versions(driver)
        return driver

    @classmethod
    def sample(cls):
        attrs = {
            'name': 'sample-driver',
            'hosts': ['fake-host'],
            'type': 'classic',
        }
        for iface_type in driver_base.ALL_INTERFACES:
            attrs['default_%s_interface' % iface_type] = None
            attrs['enabled_%s_interfaces' % iface_type] = None

        sample = cls(**attrs)
        return sample