class Controller(rest.RestController): """Version 1 API controller root.""" nodes = node.NodesController() ports = port.PortsController() chassis = chassis.ChassisController() drivers = driver.DriversController() lookup = ramdisk.LookupController() heartbeat = ramdisk.HeartbeatController() @expose.expose(V1) def get(self): # NOTE: The reason why convert() it's being called for every # request is because we need to get the host url from # the request object to make the links. return V1.convert() def _check_version(self, version, headers=None): if headers is None: headers = {} # ensure that major version in the URL matches the header if version.major != BASE_VERSION: raise exc.HTTPNotAcceptable( _("Mutually exclusive versions requested. Version %(ver)s " "requested but not supported by this service. The supported " "version range is: [%(min)s, %(max)s].") % { 'ver': version, 'min': versions.MIN_VERSION_STRING, 'max': versions.MAX_VERSION_STRING }, headers=headers) # ensure the minor version is within the supported range if version < MIN_VER or version > MAX_VER: raise exc.HTTPNotAcceptable( _("Version %(ver)s was requested but the minor version is not " "supported by this service. The supported version range is: " "[%(min)s, %(max)s].") % { 'ver': version, 'min': versions.MIN_VERSION_STRING, 'max': versions.MAX_VERSION_STRING }, headers=headers) @pecan.expose() def _route(self, args): v = base.Version(pecan.request.headers, versions.MIN_VERSION_STRING, versions.MAX_VERSION_STRING) # Always set the min and max headers pecan.response.headers[base.Version.min_string] = ( versions.MIN_VERSION_STRING) pecan.response.headers[base.Version.max_string] = ( versions.MAX_VERSION_STRING) # assert that requested version is supported self._check_version(v, pecan.response.headers) pecan.response.headers[base.Version.string] = str(v) pecan.request.version = v return super(Controller, self)._route(args)
def _assess_db_object_and_api_performance(mock_log, mock_request): print('Phase - Assess DB & Object conversion Performance') _add_a_line() # Just mock it to silence it since getting the logger to update # config seems like not a thing once started. :\ mock_log.debug = mock.Mock() # Internal logic requires major/minor versions and a context to # proceed. This is just to make the NodesController respond properly. mock_request.context = context.get_admin_context() mock_request.version.major = 1 mock_request.version.minor = 71 start = time.time() node_api_controller = node_api.NodesController() node_api_controller.context = context.get_admin_context() fields = ("uuid,power_state,target_power_state,provision_state," "target_provision_state,last_error,maintenance,properties," "instance_uuid,traits,resource_class") total_nodes = 0 res = node_api_controller._get_nodes_collection( chassis_uuid=None, instance_uuid=None, associated=None, maintenance=None, retired=None, provision_state=None, marker=None, limit=None, sort_key="id", sort_dir="asc", fields=fields.split(',')) total_nodes = len(res['nodes']) while len(res['nodes']) != 1: print(" ** Getting nodes ** %s Elapsed: %s seconds." % (total_nodes, _calculate_delta(start, time.time()))) res = node_api_controller._get_nodes_collection( chassis_uuid=None, instance_uuid=None, associated=None, maintenance=None, retired=None, provision_state=None, marker=res['nodes'][-1]['uuid'], limit=None, sort_key="id", sort_dir="asc", fields=fields.split(',')) new_nodes = len(res['nodes']) if new_nodes == 0: break total_nodes = total_nodes + new_nodes delta = _calculate_delta(start, time.time()) print('Took %s seconds to return all %s nodes via ' 'nodes API call pattern.\n' % (delta, total_nodes))
class Controller(rest.RestController): """Version 1 API controller root.""" nodes = node.NodesController() ports = port.PortsController() chassis = chassis.ChassisController() @wsme_pecan.wsexpose(V1) def get(self): # NOTE: The reason why convert() it's being called for every # request is because we need to get the host url from # the request object to make the links. return V1.convert()
class Controller(rest.RestController): """Version 1 API controller root.""" nodes = node.NodesController() ports = port.PortsController() chassis = chassis.ChassisController() drivers = driver.DriversController() @wsme_pecan.wsexpose(V1) def get(self): # NOTE: The reason why convert() it's being called for every # request is because we need to get the host url from # the request object to make the links. return V1.convert() def _check_version(self, version): # ensure that major version in the URL matches the header if version.major != 1: raise exc.HTTPNotAcceptable( _("Mutually exclusive versions requested. Version %(ver)s " "requested but not supported by this service.") % {'ver': version}) # ensure the minor version is within the supported range if version.minor < MIN_VER or version.minor > MAX_VER: raise exc.HTTPNotAcceptable( _("Unsupported minor version requested. This API service " "supports the following version range: " "[%(min)s, %(max)s].") % { 'min': MIN_VER, 'max': MAX_VER }) version.set_min_max(MIN_VER, MAX_VER) @pecan.expose() def _route(self, args): v = base.Version(pecan.request.headers) # Always set the min and max headers # FIXME: these are not being sent if _check_version raises an exception pecan.response.headers[base.Version.min_string] = str(MIN_VER) pecan.response.headers[base.Version.max_string] = str(MAX_VER) # assert that requested version is supported self._check_version(v) pecan.response.headers[base.Version.string] = str(v) pecan.request.version = v return super(Controller, self)._route(args)
class ChassisController(rest.RestController): """REST controller for Chassis.""" nodes = node.NodesController() """Expose nodes as a sub-element of chassis""" # Set the flag to indicate that the requests to this resource are # coming from a top-level resource nodes.from_chassis = True _custom_actions = { 'detail': ['GET'], } invalid_sort_key_list = ['extra'] def _get_chassis_collection(self, marker, limit, sort_key, sort_dir, resource_url=None, fields=None, detail=None): limit = api_utils.validate_limit(limit) sort_dir = api_utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.Chassis.get_by_uuid(api.request.context, marker) if sort_key in self.invalid_sort_key_list: raise exception.InvalidParameterValue( _("The sort_key value %(key)s is an invalid field for sorting") % {'key': sort_key}) chassis = objects.Chassis.list(api.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) parameters = {} if detail is not None: parameters['detail'] = detail return ChassisCollection.convert_with_links(chassis, limit, url=resource_url, fields=fields, sort_key=sort_key, sort_dir=sort_dir, **parameters) @METRICS.timer('ChassisController.get_all') @expose.expose(ChassisCollection, types.uuid, int, str, str, types.listtype, types.boolean) def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc', fields=None, detail=None): """Retrieve a list of chassis. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. This value cannot be larger than the value of max_limit in the [api] section of the ironic configuration, or only max_limit resources will be returned. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. :param fields: Optional, a list with a specified set of fields of the resource to be returned. """ cdict = api.request.context.to_policy_values() policy.authorize('baremetal:chassis:get', cdict, cdict) api_utils.check_allow_specify_fields(fields) fields = api_utils.get_request_return_fields(fields, detail, _DEFAULT_RETURN_FIELDS) return self._get_chassis_collection(marker, limit, sort_key, sort_dir, fields=fields, detail=detail) @METRICS.timer('ChassisController.detail') @expose.expose(ChassisCollection, types.uuid, int, str, str) def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'): """Retrieve a list of chassis with detail. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. This value cannot be larger than the value of max_limit in the [api] section of the ironic configuration, or only max_limit resources will be returned. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. """ cdict = api.request.context.to_policy_values() policy.authorize('baremetal:chassis:get', cdict, cdict) # /detail should only work against collections parent = api.request.path.split('/')[:-1][-1] if parent != "chassis": raise exception.HTTPNotFound() resource_url = '/'.join(['chassis', 'detail']) return self._get_chassis_collection(marker, limit, sort_key, sort_dir, resource_url) @METRICS.timer('ChassisController.get_one') @expose.expose(Chassis, types.uuid, types.listtype) def get_one(self, chassis_uuid, fields=None): """Retrieve information about the given chassis. :param chassis_uuid: UUID of a chassis. :param fields: Optional, a list with a specified set of fields of the resource to be returned. """ cdict = api.request.context.to_policy_values() policy.authorize('baremetal:chassis:get', cdict, cdict) api_utils.check_allow_specify_fields(fields) rpc_chassis = objects.Chassis.get_by_uuid(api.request.context, chassis_uuid) return Chassis.convert_with_links(rpc_chassis, fields=fields) @METRICS.timer('ChassisController.post') @expose.expose(Chassis, body=Chassis, status_code=http_client.CREATED) def post(self, chassis): """Create a new chassis. :param chassis: a chassis within the request body. """ context = api.request.context cdict = context.to_policy_values() policy.authorize('baremetal:chassis:create', cdict, cdict) # NOTE(yuriyz): UUID is mandatory for notifications payload if not chassis.uuid: chassis.uuid = uuidutils.generate_uuid() new_chassis = objects.Chassis(context, **chassis.as_dict()) notify.emit_start_notification(context, new_chassis, 'create') with notify.handle_error_notification(context, new_chassis, 'create'): new_chassis.create() notify.emit_end_notification(context, new_chassis, 'create') # Set the HTTP Location Header api.response.location = link.build_url('chassis', new_chassis.uuid) return Chassis.convert_with_links(new_chassis) @METRICS.timer('ChassisController.patch') @expose.validate(types.uuid, [ChassisPatchType]) @expose.expose(Chassis, types.uuid, body=[ChassisPatchType]) def patch(self, chassis_uuid, patch): """Update an existing chassis. :param chassis_uuid: UUID of a chassis. :param patch: a json PATCH document to apply to this chassis. """ context = api.request.context cdict = context.to_policy_values() policy.authorize('baremetal:chassis:update', cdict, cdict) rpc_chassis = objects.Chassis.get_by_uuid(context, chassis_uuid) chassis = Chassis( **api_utils.apply_jsonpatch(rpc_chassis.as_dict(), patch)) # Update only the fields that have changed for field in objects.Chassis.fields: try: patch_val = getattr(chassis, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == atypes.Unset: patch_val = None if rpc_chassis[field] != patch_val: rpc_chassis[field] = patch_val notify.emit_start_notification(context, rpc_chassis, 'update') with notify.handle_error_notification(context, rpc_chassis, 'update'): rpc_chassis.save() notify.emit_end_notification(context, rpc_chassis, 'update') return Chassis.convert_with_links(rpc_chassis) @METRICS.timer('ChassisController.delete') @expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT) def delete(self, chassis_uuid): """Delete a chassis. :param chassis_uuid: UUID of a chassis. """ context = api.request.context cdict = context.to_policy_values() policy.authorize('baremetal:chassis:delete', cdict, cdict) rpc_chassis = objects.Chassis.get_by_uuid(context, chassis_uuid) notify.emit_start_notification(context, rpc_chassis, 'delete') with notify.handle_error_notification(context, rpc_chassis, 'delete'): rpc_chassis.destroy() notify.emit_end_notification(context, rpc_chassis, 'delete')
class ChassisController(rest.RestController): """REST controller for Chassis.""" nodes = node.NodesController() "Expose nodes as a sub-element of chassis" # Set the flag to indicate that the requests to this resource are # coming from a top-level resource nodes.from_chassis = True _custom_actions = { 'detail': ['GET'], } def _get_chassis_collection(self, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): limit = api_utils.validate_limit(limit) sort_dir = api_utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.Chassis.get_by_uuid(pecan.request.context, marker) chassis = objects.Chassis.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) return ChassisCollection.convert_with_links(chassis, limit, url=resource_url, expand=expand, sort_key=sort_key, sort_dir=sort_dir) @wsme_pecan.wsexpose(ChassisCollection, types.uuid, int, wtypes.text, wtypes.text) def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc'): """Retrieve a list of chassis. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. """ return self._get_chassis_collection(marker, limit, sort_key, sort_dir) @wsme_pecan.wsexpose(ChassisCollection, types.uuid, int, wtypes.text, wtypes.text) def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'): """Retrieve a list of chassis with detail. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. """ # /detail should only work agaist collections parent = pecan.request.path.split('/')[:-1][-1] if parent != "chassis": raise exception.HTTPNotFound expand = True resource_url = '/'.join(['chassis', 'detail']) return self._get_chassis_collection(marker, limit, sort_key, sort_dir, expand, resource_url) @wsme_pecan.wsexpose(Chassis, types.uuid) def get_one(self, chassis_uuid): """Retrieve information about the given chassis. :param chassis_uuid: UUID of a chassis. """ rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) return Chassis.convert_with_links(rpc_chassis) @wsme_pecan.wsexpose(Chassis, body=Chassis, status_code=201) def post(self, chassis): """Create a new chassis. :param chassis: a chassis within the request body. """ new_chassis = objects.Chassis(pecan.request.context, **chassis.as_dict()) new_chassis.create() # Set the HTTP Location Header pecan.response.location = link.build_url('chassis', new_chassis.uuid) return Chassis.convert_with_links(new_chassis) @wsme.validate(types.uuid, [ChassisPatchType]) @wsme_pecan.wsexpose(Chassis, types.uuid, body=[ChassisPatchType]) def patch(self, chassis_uuid, patch): """Update an existing chassis. :param chassis_uuid: UUID of a chassis. :param patch: a json PATCH document to apply to this chassis. """ rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) try: chassis = Chassis( **api_utils.apply_jsonpatch(rpc_chassis.as_dict(), patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Chassis.fields: try: patch_val = getattr(chassis, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if rpc_chassis[field] != patch_val: rpc_chassis[field] = patch_val rpc_chassis.save() return Chassis.convert_with_links(rpc_chassis) @wsme_pecan.wsexpose(None, types.uuid, status_code=204) def delete(self, chassis_uuid): """Delete a chassis. :param chassis_uuid: UUID of a chassis. """ rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) rpc_chassis.destroy()
class ChassisController(rest.RestController): """REST controller for Chassis.""" nodes = node.NodesController() """Expose nodes as a sub-element of chassis""" # Set the flag to indicate that the requests to this resource are # coming from a top-level resource nodes.from_chassis = True _custom_actions = { 'detail': ['GET'], } invalid_sort_key_list = ['extra'] def _get_chassis_collection(self, marker, limit, sort_key, sort_dir, resource_url=None, fields=None): limit = api_utils.validate_limit(limit) sort_dir = api_utils.validate_sort_dir(sort_dir) marker_obj = None if marker: marker_obj = objects.Chassis.get_by_uuid(pecan.request.context, marker) if sort_key in self.invalid_sort_key_list: raise exception.InvalidParameterValue( _("The sort_key value %(key)s is an invalid field for sorting") % {'key': sort_key}) chassis = objects.Chassis.list(pecan.request.context, limit, marker_obj, sort_key=sort_key, sort_dir=sort_dir) return ChassisCollection.convert_with_links(chassis, limit, url=resource_url, fields=fields, sort_key=sort_key, sort_dir=sort_dir) @expose.expose(ChassisCollection, types.uuid, int, wtypes.text, wtypes.text, types.listtype) def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc', fields=None): """Retrieve a list of chassis. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. :param fields: Optional, a list with a specified set of fields of the resource to be returned. """ api_utils.check_allow_specify_fields(fields) if fields is None: fields = _DEFAULT_RETURN_FIELDS return self._get_chassis_collection(marker, limit, sort_key, sort_dir, fields=fields) @expose.expose(ChassisCollection, types.uuid, int, wtypes.text, wtypes.text) def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'): """Retrieve a list of chassis with detail. :param marker: pagination marker for large data sets. :param limit: maximum number of resources to return in a single result. :param sort_key: column to sort results by. Default: id. :param sort_dir: direction to sort. "asc" or "desc". Default: asc. """ # /detail should only work against collections parent = pecan.request.path.split('/')[:-1][-1] if parent != "chassis": raise exception.HTTPNotFound() resource_url = '/'.join(['chassis', 'detail']) return self._get_chassis_collection(marker, limit, sort_key, sort_dir, resource_url) @expose.expose(Chassis, types.uuid, types.listtype) def get_one(self, chassis_uuid, fields=None): """Retrieve information about the given chassis. :param chassis_uuid: UUID of a chassis. :param fields: Optional, a list with a specified set of fields of the resource to be returned. """ api_utils.check_allow_specify_fields(fields) rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) return Chassis.convert_with_links(rpc_chassis, fields=fields) @expose.expose(Chassis, body=Chassis, status_code=http_client.CREATED) def post(self, chassis): """Create a new chassis. :param chassis: a chassis within the request body. """ new_chassis = objects.Chassis(pecan.request.context, **chassis.as_dict()) new_chassis.create() # Set the HTTP Location Header pecan.response.location = link.build_url('chassis', new_chassis.uuid) return Chassis.convert_with_links(new_chassis) @wsme.validate(types.uuid, [ChassisPatchType]) @expose.expose(Chassis, types.uuid, body=[ChassisPatchType]) def patch(self, chassis_uuid, patch): """Update an existing chassis. :param chassis_uuid: UUID of a chassis. :param patch: a json PATCH document to apply to this chassis. """ rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) try: chassis = Chassis( **api_utils.apply_jsonpatch(rpc_chassis.as_dict(), patch)) except api_utils.JSONPATCH_EXCEPTIONS as e: raise exception.PatchError(patch=patch, reason=e) # Update only the fields that have changed for field in objects.Chassis.fields: try: patch_val = getattr(chassis, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if rpc_chassis[field] != patch_val: rpc_chassis[field] = patch_val rpc_chassis.save() return Chassis.convert_with_links(rpc_chassis) @expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT) def delete(self, chassis_uuid): """Delete a chassis. :param chassis_uuid: UUID of a chassis. """ rpc_chassis = objects.Chassis.get_by_uuid(pecan.request.context, chassis_uuid) rpc_chassis.destroy()
class Controller(rest.RestController): """Version 1 API controller root.""" nodes = node.NodesController() ports = port.PortsController() portgroups = portgroup.PortgroupsController() chassis = chassis.ChassisController() drivers = driver.DriversController() volume = volume.VolumeController() lookup = ramdisk.LookupController() heartbeat = ramdisk.HeartbeatController() conductors = conductor.ConductorsController() allocations = allocation.AllocationsController() events = event.EventsController() deploy_templates = deploy_template.DeployTemplatesController() @expose.expose(V1) def get(self): # NOTE: The reason why convert() it's being called for every # request is because we need to get the host url from # the request object to make the links. return V1.convert() def _check_version(self, version, headers=None): if headers is None: headers = {} # ensure that major version in the URL matches the header if version.major != BASE_VERSION: raise exc.HTTPNotAcceptable( _("Mutually exclusive versions requested. Version %(ver)s " "requested but not supported by this service. The supported " "version range is: [%(min)s, %(max)s].") % { 'ver': version, 'min': versions.min_version_string(), 'max': versions.max_version_string() }, headers=headers) # ensure the minor version is within the supported range if version < min_version() or version > max_version(): raise exc.HTTPNotAcceptable( _("Version %(ver)s was requested but the minor version is not " "supported by this service. The supported version range is: " "[%(min)s, %(max)s].") % { 'ver': version, 'min': versions.min_version_string(), 'max': versions.max_version_string() }, headers=headers) @pecan.expose() def _route(self, args, request=None): v = base.Version(api.request.headers, versions.min_version_string(), versions.max_version_string()) # Always set the min and max headers api.response.headers[base.Version.min_string] = ( versions.min_version_string()) api.response.headers[base.Version.max_string] = ( versions.max_version_string()) # assert that requested version is supported self._check_version(v, api.response.headers) api.response.headers[base.Version.string] = str(v) api.request.version = v return super(Controller, self)._route(args, request)
class Controller(object): """Version 1 API controller root.""" _subcontroller_map = { 'nodes': node.NodesController(), 'ports': port.PortsController(), 'portgroups': portgroup.PortgroupsController(), 'chassis': chassis.ChassisController(), 'drivers': driver.DriversController(), 'volume': volume.VolumeController(), 'lookup': ramdisk.LookupController(), 'heartbeat': ramdisk.HeartbeatController(), 'conductors': conductor.ConductorsController(), 'allocations': allocation.AllocationsController(), 'events': event.EventsController(), 'deploy_templates': deploy_template.DeployTemplatesController() } @method.expose() def index(self): # NOTE: The reason why v1() it's being called for every # request is because we need to get the host url from # the request object to make the links. self._add_version_attributes() if api.request.method != "GET": pecan.abort(http_client.METHOD_NOT_ALLOWED) return v1() def _check_version(self, version, headers=None): if headers is None: headers = {} # ensure that major version in the URL matches the header if version.major != BASE_VERSION: raise exc.HTTPNotAcceptable( _("Mutually exclusive versions requested. Version %(ver)s " "requested but not supported by this service. The supported " "version range is: [%(min)s, %(max)s].") % { 'ver': version, 'min': versions.min_version_string(), 'max': versions.max_version_string() }, headers=headers) # ensure the minor version is within the supported range if version < min_version() or version > max_version(): raise exc.HTTPNotAcceptable( _("Version %(ver)s was requested but the minor version is not " "supported by this service. The supported version range is: " "[%(min)s, %(max)s].") % { 'ver': version, 'min': versions.min_version_string(), 'max': versions.max_version_string() }, headers=headers) def _add_version_attributes(self): v = base.Version(api.request.headers, versions.min_version_string(), versions.max_version_string()) # Always set the min and max headers api.response.headers[base.Version.min_string] = ( versions.min_version_string()) api.response.headers[base.Version.max_string] = ( versions.max_version_string()) # assert that requested version is supported self._check_version(v, api.response.headers) api.response.headers[base.Version.string] = str(v) api.request.version = v @pecan.expose() def _lookup(self, primary_key, *remainder): self._add_version_attributes() controller = self._subcontroller_map.get(primary_key) if not controller: pecan.abort(http_client.NOT_FOUND) return controller, remainder