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)
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"]
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)
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', )
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
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)
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'}])
class RuntimePool(Resource): name = wsme.wsattr(wtypes.text, readonly=True) capacity = wsme.wsattr(RuntimePoolCapacity, readonly=True)
class BaseResult(wtypes.Base): code = wsme.wsattr(int, default=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)
class RuntimePoolCapacity(Resource): total = wsme.wsattr(int, readonly=True) available = wsme.wsattr(int, readonly=True)
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
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
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)
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)
class FailResult(BaseResult): code = wsme.wsattr(wtypes.text, default=0xFFFF1111) msg = wsme.wsattr(wtypes.text, default=str(_('Fail'))) content = jsontype
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')
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)
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)
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)
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)
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
class HelloResult(wtypes.Base): status_code = wsme.wsattr(int, default=0) detail = wsme.wsattr(wtypes.text, default=str(_('success'))) content = _types.jsontype
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)
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)
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)
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)
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