def patch(self, uuid, patch): """Update an existing chassis.""" chassis = objects.Chassis.get_by_uuid(pecan.request.context, uuid) chassis_dict = chassis.as_dict() utils.validate_patch(patch) try: patched_chassis = jsonpatch.apply_patch(chassis_dict, jsonpatch.JsonPatch(patch)) except jsonpatch.JsonPatchException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) defaults = objects.Chassis.get_defaults() for key in defaults: # Internal values that shouldn't be part of the patch if key in ['id', 'updated_at', 'created_at']: continue # In case of a remove operation, add the missing fields back # to the document with their default value if key in chassis_dict and key not in patched_chassis: patched_chassis[key] = defaults[key] # Update only the fields that have changed if chassis[key] != patched_chassis[key]: chassis[key] = patched_chassis[key] chassis.save() return Chassis.convert_with_links(chassis)
def patch(self, uuid, patch): """Update an existing node.""" if self._from_chassis: raise exception.OperationNotPermitted node = objects.Node.get_by_uuid(pecan.request.context, uuid) node_dict = node.as_dict() utils.validate_patch(patch) patch_obj = jsonpatch.JsonPatch(patch) # Prevent states from being updated state_rel_path = ['/power_state', '/target_power_state', '/provision_state', '/target_provision_state'] if any(p['path'] in state_rel_path for p in patch_obj): raise wsme.exc.ClientSideError(_("Changing states is not allowed " "here; You must use the " "nodes/%s/state interface.") % uuid) # Prevent node from being updated when there's a state # change in progress if any(node.get(tgt) for tgt in ["target_power_state", "target_provision_state"]): raise wsme.exc.ClientSideError(_("Can not update node %s while " "a state transition is in " "progress.") % uuid, status_code=409) try: patched_node = jsonpatch.apply_patch(node_dict, patch_obj) except jsonpatch.JsonPatchException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) try: self. _convert_chassis_uuid_to_id(patched_node) defaults = objects.Node.get_defaults() for key in defaults: # Internal values that shouldn't be part of the patch if key in ['id', 'updated_at', 'created_at']: continue # In case of a remove operation, add the missing fields back # to the document with their default value if key in node_dict and key not in patched_node: patched_node[key] = defaults[key] # Update only the fields that have changed if node[key] != patched_node[key]: node[key] = patched_node[key] node = pecan.request.rpcapi.update_node(pecan.request.context, node) except exception.IronicException as e: with excutils.save_and_reraise_exception(): LOG.exception(e) return Node.convert_with_links(node)
def patch(self, uuid, patch): """Update an existing port.""" if self._from_nodes: raise exception.OperationNotPermitted port = objects.Port.get_by_uuid(pecan.request.context, uuid) port_dict = port.as_dict() api_utils.validate_patch(patch) try: patched_port = jsonpatch.apply_patch(port_dict, jsonpatch.JsonPatch(patch)) except jsonpatch.JsonPatchException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) # Required fields missing_attr = [attr for attr in ['address', 'node_id'] if attr not in patched_port] if missing_attr: msg = _("Attribute(s): %s can not be removed") raise wsme.exc.ClientSideError(msg % ', '.join(missing_attr)) if port_dict['address'] != patched_port['address']: self._check_address(patched_port) self._convert_node_uuid_to_id(patched_port) defaults = objects.Port.get_defaults() for key in defaults: # Internal values that shouldn't be part of the patch if key in ['id', 'updated_at', 'created_at']: continue # In case of a remove operation, add the missing fields back # to the document with their default value if key in port_dict and key not in patched_port: patched_port[key] = defaults[key] # Update only the fields that have changed if port[key] != patched_port[key]: port[key] = patched_port[key] port.save() return Port.convert_with_links(port)
def test_validate_patch(self): patch = [{'op': 'remove', 'value': 'bar', 'path': '/foo'}] utils.validate_patch(patch) patch = [{'op': 'add', 'value': 'bar', 'path': '/extra/foo'}] utils.validate_patch(patch) patch = [{'op': 'replace', 'value': 'bar', 'path': '/foo'}] utils.validate_patch(patch)
def patch(self, uuid, patch): """Update an existing node. TODO(deva): add exception handling """ node = objects.Node.get_by_uuid(pecan.request.context, uuid) node_dict = node.as_dict() utils.validate_patch(patch) patch_obj = jsonpatch.JsonPatch(patch) # Prevent states from being updated state_rel_path = ['/power_state', '/target_power_state', '/provision_state', '/target_provision_state'] if any(p['path'] in state_rel_path for p in patch_obj): raise wsme.exc.ClientSideError(_("Changing states is not allowed " "here; You must use the " "nodes/%s/state interface.") % uuid) # Prevent node from being updated when there's a state # change in progress if any(node.get(tgt) for tgt in ["target_power_state", "target_provision_state"]): raise wsme.exc.ClientSideError(_("Can not update node %s because " "a state change is already in " "progress.") % uuid) try: patched_node = jsonpatch.apply_patch(node_dict, patch_obj) except jsonpatch.JsonPatchException as e: LOG.exception(e) raise wsme.exc.ClientSideError(_("Patching Error: %s") % e) response = wsme.api.Response(Node(), status_code=200) try: defaults = objects.Node.get_defaults() for key in defaults: # Internal values that shouldn't be part of the patch if key in ['id', 'updated_at', 'created_at']: continue # In case of a remove operation, add the missing fields back # to the document with their default value if key in node_dict and key not in patched_node: patched_node[key] = defaults[key] # Update only the fields that have changed if node[key] != patched_node[key]: node[key] = patched_node[key] node = pecan.request.rpcapi.update_node(pecan.request.context, node) response.obj = node except exception.InvalidParameterValue: response.status_code = 400 except exception.NodeInWrongPowerState: response.status_code = 409 except exception.IronicException as e: LOG.exception(e) response.status_code = 500 # TODO(deva): return the response object instead of raising # after wsme 0.5b3 is released if response.status_code not in [200, 202]: raise wsme.exc.ClientSideError(_( "Error updating node %s") % uuid) return Node.convert_with_links(response.obj)