def test_is_path_updated_similar_subpath(self): patch = [{ 'path': '/properties2/switch_id', 'op': 'replace', 'value': 'spam' }] path = '/properties' value = utils.is_path_updated(patch, path) self.assertFalse(value)
def patch(self, portgroup_ident, patch): """Update an existing portgroup. :param portgroup_ident: UUID or logical name of a portgroup. :param patch: a json PATCH document to apply to this portgroup. """ if not api_utils.allow_portgroups(): raise exception.NotFound() context = pecan.request.context cdict = context.to_policy_values() policy.authorize('baremetal:portgroup:update', cdict, cdict) if self.parent_node_ident: raise exception.OperationNotPermitted() if (not api_utils.allow_portgroup_mode_properties() and (api_utils.is_path_updated(patch, '/mode') or api_utils.is_path_updated(patch, '/properties'))): raise exception.NotAcceptable() rpc_portgroup = api_utils.get_rpc_portgroup(portgroup_ident) names = api_utils.get_patch_values(patch, '/name') for name in names: if (name and not api_utils.is_valid_logical_name(name)): error_msg = _("Portgroup %(portgroup)s: Cannot change name to" " invalid name '%(name)s'") % {'portgroup': portgroup_ident, 'name': name} raise wsme.exc.ClientSideError( error_msg, status_code=http_client.BAD_REQUEST) try: portgroup_dict = rpc_portgroup.as_dict() # NOTE: # 1) Remove node_id because it's an internal value and # not present in the API object # 2) Add node_uuid portgroup_dict['node_uuid'] = portgroup_dict.pop('node_id', None) portgroup = Portgroup(**api_utils.apply_jsonpatch(portgroup_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.Portgroup.fields: try: patch_val = getattr(portgroup, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == wtypes.Unset: patch_val = None if rpc_portgroup[field] != patch_val: rpc_portgroup[field] = patch_val rpc_node = objects.Node.get_by_id(context, rpc_portgroup.node_id) notify.emit_start_notification(context, rpc_portgroup, 'update', node_uuid=rpc_node.uuid) with notify.handle_error_notification(context, rpc_portgroup, 'update', node_uuid=rpc_node.uuid): topic = pecan.request.rpcapi.get_topic_for(rpc_node) new_portgroup = pecan.request.rpcapi.update_portgroup( context, rpc_portgroup, topic) api_portgroup = Portgroup.convert_with_links(new_portgroup) notify.emit_end_notification(context, new_portgroup, 'update', node_uuid=api_portgroup.node_uuid) return api_portgroup
def patch(self, portgroup_ident, patch): """Update an existing portgroup. :param portgroup_ident: UUID or logical name of a portgroup. :param patch: a json PATCH document to apply to this portgroup. """ if not api_utils.allow_portgroups(): raise exception.NotFound() context = api.request.context rpc_portgroup, rpc_node = api_utils.check_port_policy_and_retrieve( 'baremetal:portgroup:update', portgroup_ident, portgroup=True) if self.parent_node_ident: raise exception.OperationNotPermitted() if (not api_utils.allow_portgroup_mode_properties() and (api_utils.is_path_updated(patch, '/mode') or api_utils.is_path_updated(patch, '/properties'))): raise exception.NotAcceptable() api_utils.patch_validate_allowed_fields(patch, PATCH_ALLOWED_FIELDS) names = api_utils.get_patch_values(patch, '/name') for name in names: if (name and not api_utils.is_valid_logical_name(name)): error_msg = _("Portgroup %(portgroup)s: Cannot change name to" " invalid name '%(name)s'") % { 'portgroup': portgroup_ident, 'name': name } raise exception.ClientSideError( error_msg, status_code=http_client.BAD_REQUEST) portgroup_dict = rpc_portgroup.as_dict() # NOTE: # 1) Remove node_id because it's an internal value and # not present in the API object # 2) Add node_uuid portgroup_dict.pop('node_id') portgroup_dict['node_uuid'] = rpc_node.uuid portgroup_dict = api_utils.apply_jsonpatch(portgroup_dict, patch) if 'mode' not in portgroup_dict: msg = _("'mode' is a mandatory attribute and can not be removed") raise exception.ClientSideError(msg) try: if portgroup_dict['node_uuid'] != rpc_node.uuid: rpc_node = objects.Node.get(api.request.context, portgroup_dict['node_uuid']) except exception.NodeNotFound as e: # Change error code because 404 (NotFound) is inappropriate # response for a POST request to patch a Portgroup e.code = http_client.BAD_REQUEST # BadRequest raise api_utils.patched_validate_with_schema(portgroup_dict, PORTGROUP_PATCH_SCHEMA, PORTGROUP_PATCH_VALIDATOR) api_utils.patch_update_changed_fields(portgroup_dict, rpc_portgroup, fields=objects.Portgroup.fields, schema=PORTGROUP_PATCH_SCHEMA, id_map={'node_id': rpc_node.id}) if (rpc_node.provision_state == ir_states.INSPECTING and api_utils.allow_inspect_wait_state()): msg = _('Cannot update portgroup "%(portgroup)s" on node ' '"%(node)s" while it is in state "%(state)s".') % { 'portgroup': rpc_portgroup.uuid, 'node': rpc_node.uuid, 'state': ir_states.INSPECTING } raise exception.ClientSideError(msg, status_code=http_client.CONFLICT) notify.emit_start_notification(context, rpc_portgroup, 'update', node_uuid=rpc_node.uuid) with notify.handle_error_notification(context, rpc_portgroup, 'update', node_uuid=rpc_node.uuid): topic = api.request.rpcapi.get_topic_for(rpc_node) new_portgroup = api.request.rpcapi.update_portgroup( context, rpc_portgroup, topic) api_portgroup = convert_with_links(new_portgroup) notify.emit_end_notification(context, new_portgroup, 'update', node_uuid=rpc_node.uuid) return api_portgroup
def patch(self, port_uuid, patch): """Update an existing port. :param port_uuid: UUID of a port. :param patch: a json PATCH document to apply to this port. :raises: NotAcceptable, HTTPNotFound """ if self.parent_node_ident or self.parent_portgroup_ident: raise exception.OperationNotPermitted() api_utils.patch_validate_allowed_fields(patch, PATCH_ALLOWED_FIELDS) context = api.request.context fields_to_check = set() for field in (self.advanced_net_fields + ['portgroup_uuid', 'physical_network', 'is_smartnic', 'local_link_connection/network_type']): field_path = '/%s' % field if (api_utils.get_patch_values(patch, field_path) or api_utils.is_path_removed(patch, field_path)): fields_to_check.add(field) self._check_allowed_port_fields(fields_to_check) rpc_port, rpc_node = api_utils.check_port_policy_and_retrieve( 'baremetal:port:update', port_uuid) port_dict = rpc_port.as_dict() # NOTE(lucasagomes): # 1) Remove node_id because it's an internal value and # not present in the API object # 2) Add node_uuid port_dict.pop('node_id', None) port_dict['node_uuid'] = rpc_node.uuid # NOTE(vsaienko): # 1) Remove portgroup_id because it's an internal value and # not present in the API object # 2) Add portgroup_uuid portgroup = None if port_dict.get('portgroup_id'): portgroup = objects.Portgroup.get_by_id( context, port_dict.pop('portgroup_id')) port_dict['portgroup_uuid'] = portgroup and portgroup.uuid or None port_dict = api_utils.apply_jsonpatch(port_dict, patch) api_utils.handle_patch_port_like_extra_vif( rpc_port, port_dict['internal_info'], patch) try: if api_utils.is_path_updated(patch, '/portgroup_uuid'): if port_dict.get('portgroup_uuid'): portgroup = objects.Portgroup.get_by_uuid( context, port_dict['portgroup_uuid']) else: portgroup = None except exception.PortGroupNotFound as e: # Change error code because 404 (NotFound) is inappropriate # response for a PATCH request to change a Port e.code = http_client.BAD_REQUEST # BadRequest raise try: if port_dict['node_uuid'] != rpc_node.uuid: rpc_node = objects.Node.get( api.request.context, port_dict['node_uuid']) except exception.NodeNotFound as e: # Change error code because 404 (NotFound) is inappropriate # response for a PATCH request to change a Port e.code = http_client.BAD_REQUEST # BadRequest raise api_utils.patched_validate_with_schema( port_dict, PORT_PATCH_SCHEMA, PORT_PATCH_VALIDATOR) api_utils.patch_update_changed_fields( port_dict, rpc_port, fields=objects.Port.fields, schema=PORT_PATCH_SCHEMA, id_map={ 'node_id': rpc_node.id, 'portgroup_id': portgroup and portgroup.id or None } ) if (rpc_node.provision_state == ir_states.INSPECTING and api_utils.allow_inspect_wait_state()): msg = _('Cannot update port "%(port)s" on "%(node)s" while it is ' 'in state "%(state)s".') % {'port': rpc_port.uuid, 'node': rpc_node.uuid, 'state': ir_states.INSPECTING} raise exception.ClientSideError(msg, status_code=http_client.CONFLICT) if (api_utils.is_path_updated(patch, '/physical_network') and rpc_port['physical_network'] is not None and not rpc_port['physical_network']): raise exception.Invalid('A non-empty value is required when ' 'setting physical_network') notify_extra = {'node_uuid': rpc_node.uuid, 'portgroup_uuid': portgroup and portgroup.uuid or None} notify.emit_start_notification(context, rpc_port, 'update', **notify_extra) with notify.handle_error_notification(context, rpc_port, 'update', **notify_extra): topic = api.request.rpcapi.get_topic_for(rpc_node) new_port = api.request.rpcapi.update_port(context, rpc_port, topic) api_port = convert_with_links(new_port) notify.emit_end_notification(context, new_port, 'update', **notify_extra) return api_port
def test_is_path_updated_subpath_success(self): patch = [{'path': '/properties/switch_id', 'op': 'add', 'value': 'id'}] path = '/properties' value = utils.is_path_updated(patch, path) self.assertTrue(value)
def test_is_path_updated_success(self): patch = [{'path': '/name', 'op': 'remove'}] path = '/name' value = utils.is_path_updated(patch, path) self.assertTrue(value)
def test_is_path_updated_similar_subpath(self): patch = [{'path': '/properties2/switch_id', 'op': 'replace', 'value': 'spam'}] path = '/properties' value = utils.is_path_updated(patch, path) self.assertFalse(value)
def patch(self, port_uuid, patch): """Update an existing port. :param port_uuid: UUID of a port. :param patch: a json PATCH document to apply to this port. :raises: NotAcceptable, HTTPNotFound """ if self.parent_node_ident or self.parent_portgroup_ident: raise exception.OperationNotPermitted() rpc_port, rpc_node = api_utils.check_port_policy_and_retrieve( 'baremetal:port:update', port_uuid) context = api.request.context fields_to_check = set() for field in (self.advanced_net_fields + [ 'portgroup_uuid', 'physical_network', 'is_smartnic', 'local_link_connection/network_type' ]): field_path = '/%s' % field if (api_utils.get_patch_values(patch, field_path) or api_utils.is_path_removed(patch, field_path)): fields_to_check.add(field) self._check_allowed_port_fields(fields_to_check) port_dict = rpc_port.as_dict() # NOTE(lucasagomes): # 1) Remove node_id because it's an internal value and # not present in the API object # 2) Add node_uuid port_dict['node_uuid'] = port_dict.pop('node_id', None) # NOTE(vsaienko): # 1) Remove portgroup_id because it's an internal value and # not present in the API object # 2) Add portgroup_uuid port_dict['portgroup_uuid'] = port_dict.pop('portgroup_id', None) port = Port(**api_utils.apply_jsonpatch(port_dict, patch)) api_utils.handle_patch_port_like_extra_vif(rpc_port, port, patch) if api_utils.is_path_removed(patch, '/portgroup_uuid'): rpc_port.portgroup_id = None # Update only the fields that have changed for field in objects.Port.fields: try: patch_val = getattr(port, field) except AttributeError: # Ignore fields that aren't exposed in the API continue if patch_val == atypes.Unset: patch_val = None if rpc_port[field] != patch_val: rpc_port[field] = patch_val if (rpc_node.provision_state == ir_states.INSPECTING and api_utils.allow_inspect_wait_state()): msg = _('Cannot update port "%(port)s" on "%(node)s" while it is ' 'in state "%(state)s".') % { 'port': rpc_port.uuid, 'node': rpc_node.uuid, 'state': ir_states.INSPECTING } raise exception.ClientSideError(msg, status_code=http_client.CONFLICT) if (api_utils.is_path_updated(patch, '/physical_network') and rpc_port['physical_network'] is not None and not rpc_port['physical_network']): raise exception.Invalid('A non-empty value is required when ' 'setting physical_network') notify_extra = { 'node_uuid': rpc_node.uuid, 'portgroup_uuid': port.portgroup_uuid } notify.emit_start_notification(context, rpc_port, 'update', **notify_extra) with notify.handle_error_notification(context, rpc_port, 'update', **notify_extra): topic = api.request.rpcapi.get_topic_for(rpc_node) new_port = api.request.rpcapi.update_port(context, rpc_port, topic) api_port = Port.convert_with_links(new_port) notify.emit_end_notification(context, new_port, 'update', **notify_extra) return api_port