@wsme.validate(types.uuid, [NodePatchType])
    @expose.expose(Node, types.uuid_or_name, body=[NodePatchType])
    def patch(self, node_ident, patch):
        if self.from_chassis:
            raise exception.OperationNotPermitted

        rpc_node = api_utils.get_rpc_node(node_ident)

        # TODO(lucasagomes): This code is here for backward compatibility
        # with old nova Ironic drivers that will attempt to remove the
        # instance even if it's already deleted in Ironic. This conditional
        # should be removed in the next cycle (Mitaka).
        remove_inst_uuid_patch = [{'op': 'remove', 'path': '/instance_uuid'}]
        if (rpc_node.provision_state in (ir_states.CLEANING,
                                         ir_states.CLEANWAIT)
            and patch == remove_inst_uuid_patch):
            # The instance_uuid is already removed as part of the node's
            # tear down, skip this update.
            return Node.convert_with_links(rpc_node)
        elif rpc_node.maintenance and patch == remove_inst_uuid_patch:
            LOG.debug('Removing instance uuid %(instance)s from node %(node)s',
                      {'instance': rpc_node.instance_uuid,
                       'node': rpc_node.uuid})
        # Check if node is transitioning state, although nodes in some states
        # can be updated.
        elif (rpc_node.target_provision_state and rpc_node.provision_state
              not in ir_states.UPDATE_ALLOWED_STATES):
            msg = _("Node %s can not be updated while a state transition "
                    "is in progress.")
            raise wsme.exc.ClientSideError(
                msg % node_ident, status_code=http_client.CONFLICT)

        name = api_utils.get_patch_value(patch, '/name')
        if name is not None:
            error_msg = _("Node %(node)s: Cannot change name to invalid "
                          "name '%(name)s'") % {'node': node_ident,
                                                'name': name}
            self._check_name_acceptable(name, error_msg)
        try:
            node_dict = rpc_node.as_dict()
            # NOTE(lucasagomes):
            # 1) Remove chassis_id because it's an internal value and
            #    not present in the API object
            # 2) Add chassis_uuid
            node_dict['chassis_uuid'] = node_dict.pop('chassis_id', None)
            node = Node(**api_utils.apply_jsonpatch(node_dict, patch))
        except api_utils.JSONPATCH_EXCEPTIONS as e:
            raise exception.PatchError(patch=patch, reason=e)
        self._update_changed_fields(node, rpc_node)
        # NOTE(deva): we calculate the rpc topic here in case node.driver
        #             has changed, so that update is sent to the
        #             new conductor, not the old one which may fail to
        #             load the new driver.
        try:
            topic = pecan.request.rpcapi.get_topic_for(rpc_node)
        except exception.NoValidHost as e:
            # NOTE(deva): convert from 404 to 400 because client can see
            #             list of available drivers and shouldn't request
            #             one that doesn't exist.
            e.code = http_client.BAD_REQUEST
            raise e
        self._check_driver_changed_and_console_enabled(rpc_node, node_ident)
        new_node = pecan.request.rpcapi.update_node(
            pecan.request.context, rpc_node, topic)

        return Node.convert_with_links(new_node)
Check loosely all functions are called with right args.
dataset/ETHPy150Open openstack/ironic/ironic/api/controllers/v1/node.py/NodesController.patch--crossover with--dataset/ETHPy150Open openstack/ironic/ironic/tests/unit/drivers/modules/test_deploy_utils.py/PhysicalWorkTestCase.test_deploy_partition_image_without_swap