예제 #1
0
 def test_get_patch_value_remove(self):
     patch = [{'path': '/name', 'op': 'remove'}]
     path = '/name'
     value = utils.get_patch_value(patch, path)
     self.assertIsNone(value)
예제 #2
0
 def test_get_patch_value_success(self):
     patch = [{'path': '/name', 'op': 'replace', 'value': 'node-x'}]
     path = '/name'
     value = utils.get_patch_value(patch, path)
     self.assertEqual('node-x', value)
예제 #3
0
파일: node.py 프로젝트: stendulker/ironic
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        if self.from_chassis:
            raise exception.OperationNotPermitted

        rpc_node = api_utils.get_rpc_node(node_ident)

        # Check if node is transitioning state, although nodes in some states
        # can be updated.
        if ((rpc_node.maintenance or
                rpc_node.provision_state == ir_states.CLEANING) and
                patch == [{'op': 'remove', 'path': '/instance_uuid'}]):
            # Allow node.instance_uuid removal during cleaning, but not other
            # operations. Also allow it during maintenance, to break
            # association with Nova in case of serious problems.
            # TODO(JoshNang) remove node.instance_uuid when removing
            # instance_info and stop removing node.instance_uuid in the Nova
            # Ironic driver. Bug: 1436568
            LOG.debug('Removing instance uuid %(instance)s from node %(node)s',
                      {'instance': rpc_node.instance_uuid,
                       'node': rpc_node.uuid})
        elif ((rpc_node.target_power_state or 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=409)

        # Verify that if we're patching 'name' that it is a valid
        name = api_utils.get_patch_value(patch, '/name')
        if name:
            if not api_utils.allow_node_logical_names():
                raise exception.NotAcceptable()
            if not api_utils.is_valid_node_name(name):
                msg = _("Node %(node)s: Cannot change name to invalid "
                        "name '%(name)s'")
                raise wsme.exc.ClientSideError(msg % {'node': node_ident,
                                                      'name': name},
                                               status_code=400)

        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)

        # Update only the fields that have changed
        for field in objects.Node.fields:
            try:
                patch_val = getattr(node, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if rpc_node[field] != patch_val:
                rpc_node[field] = patch_val

        # 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 = 400
            raise e

        # NOTE(lucasagomes): If it's changing the driver and the console
        # is enabled we prevent updating it because the new driver will
        # not be able to stop a console started by the previous one.
        delta = rpc_node.obj_what_changed()
        if 'driver' in delta and rpc_node.console_enabled:
            raise wsme.exc.ClientSideError(
                _("Node %s can not update the driver while the console is "
                  "enabled. Please stop the console first.") % node_ident,
                status_code=409)

        new_node = pecan.request.rpcapi.update_node(
            pecan.request.context, rpc_node, topic)

        return Node.convert_with_links(new_node)
예제 #4
0
 def test_get_patch_value_no_path(self):
     patch = [{'path': '/name', 'op': 'update', 'value': 'node-0'}]
     path = '/invalid'
     value = utils.get_patch_value(patch, path)
     self.assertIsNone(value)
예제 #5
0
파일: node.py 프로젝트: Codixis/ironic
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        if self.from_chassis:
            raise exception.OperationNotPermitted

        rpc_node = api_utils.get_rpc_node(node_ident)

        # Check if node is transitioning state, although nodes in some states
        # can be updated.
        if ((rpc_node.maintenance or
                rpc_node.provision_state == ir_states.CLEANING) and
                patch == [{'op': 'remove', 'path': '/instance_uuid'}]):
            # Allow node.instance_uuid removal during cleaning, but not other
            # operations. Also allow it during maintenance, to break
            # association with Nova in case of serious problems.
            # TODO(JoshNang) remove node.instance_uuid when removing
            # instance_info and stop removing node.instance_uuid in the Nova
            # Ironic driver. Bug: 1436568
            LOG.debug('Removing instance uuid %(instance)s from node %(node)s',
                      {'instance': rpc_node.instance_uuid,
                       'node': rpc_node.uuid})
        elif ((rpc_node.target_power_state or 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=409)

        # Verify that if we're patching 'name' that it is a valid
        name = api_utils.get_patch_value(patch, '/name')
        if name:
            if not api_utils.allow_node_logical_names():
                raise exception.NotAcceptable()
            if not api_utils.is_valid_node_name(name):
                msg = _("Node %(node)s: Cannot change name to invalid "
                        "name '%(name)s'")
                raise wsme.exc.ClientSideError(msg % {'node': node_ident,
                                                      'name': name},
                                               status_code=400)

        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)

        # Update only the fields that have changed
        for field in objects.Node.fields:
            try:
                patch_val = getattr(node, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if rpc_node[field] != patch_val:
                rpc_node[field] = patch_val

        # 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 = 400
            raise e

        # NOTE(lucasagomes): If it's changing the driver and the console
        # is enabled we prevent updating it because the new driver will
        # not be able to stop a console started by the previous one.
        delta = rpc_node.obj_what_changed()
        if 'driver' in delta and rpc_node.console_enabled:
            raise wsme.exc.ClientSideError(
                _("Node %s can not update the driver while the console is "
                  "enabled. Please stop the console first.") % node_ident,
                status_code=409)

        new_node = pecan.request.rpcapi.update_node(
            pecan.request.context, rpc_node, topic)

        return Node.convert_with_links(new_node)
예제 #6
0
파일: node.py 프로젝트: ubuntu3/ironic
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        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=409)

        name = api_utils.get_patch_value(patch, '/name')
        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 = 400
            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)
예제 #7
0
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        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')
        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)
예제 #8
0
파일: node.py 프로젝트: infraredgirl/ironic
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        if self.from_chassis:
            raise exception.OperationNotPermitted

        rpc_node = _get_rpc_node(node_ident)

        # Check if node is transitioning state, although nodes in DEPLOYFAIL
        # can be updated.
        if ((rpc_node.target_power_state or rpc_node.target_provision_state)
                and rpc_node.provision_state != ir_states.DEPLOYFAIL):
            msg = _("Node %s can not be updated while a state transition "
                    "is in progress.")
            raise wsme.exc.ClientSideError(msg % node_ident, status_code=409)

        # Verify that if we're patching 'name' that it is a valid
        name = api_utils.get_patch_value(patch, '/name')
        if name:
            if not allow_logical_names():
                raise exception.NotAcceptable()
            if not is_valid_name(name):
                msg = _("Node %(node)s: Cannot change name to invalid "
                        "name '%(name)s'")
                raise wsme.exc.ClientSideError(msg % {
                    'node': node_ident,
                    'name': name
                },
                                               status_code=400)

        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)

        # Update only the fields that have changed
        for field in objects.Node.fields:
            try:
                patch_val = getattr(node, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if rpc_node[field] != patch_val:
                rpc_node[field] = patch_val

        # 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 = 400
            raise e

        new_node = pecan.request.rpcapi.update_node(pecan.request.context,
                                                    rpc_node, topic)

        return Node.convert_with_links(new_node)
예제 #9
0
파일: node.py 프로젝트: ramineni/myironic
    def patch(self, node_ident, patch):
        """Update an existing node.

        :param node_ident: UUID or logical name of a node.
        :param patch: a json PATCH document to apply to this node.
        """
        if self.from_chassis:
            raise exception.OperationNotPermitted

        rpc_node = _get_rpc_node(node_ident)

        # Check if node is transitioning state, although nodes in DEPLOYFAIL
        # can be updated.
        if ((rpc_node.target_power_state or rpc_node.target_provision_state)
                and rpc_node.provision_state != ir_states.DEPLOYFAIL):
            msg = _("Node %s can not be updated while a state transition "
                    "is in progress.")
            raise wsme.exc.ClientSideError(msg % node_ident, status_code=409)

        # Verify that if we're patching 'name' that it is a valid
        name = api_utils.get_patch_value(patch, '/name')
        if name:
            if not allow_logical_names():
                raise exception.NotAcceptable()
            if not is_valid_name(name):
                msg = _("Node %(node)s: Cannot change name to invalid "
                        "name '%(name)s'")
                raise wsme.exc.ClientSideError(msg % {'node': node_ident,
                                                      'name': name},
                                               status_code=400)

        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)

        # Update only the fields that have changed
        for field in objects.Node.fields:
            try:
                patch_val = getattr(node, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if rpc_node[field] != patch_val:
                rpc_node[field] = patch_val

        # 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 = 400
            raise e

        new_node = pecan.request.rpcapi.update_node(
                         pecan.request.context, rpc_node, topic)

        return Node.convert_with_links(new_node)