Beispiel #1
0
 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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
 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)
Beispiel #6
0
 def test_is_path_updated_success(self):
     patch = [{'path': '/name', 'op': 'remove'}]
     path = '/name'
     value = utils.is_path_updated(patch, path)
     self.assertTrue(value)
Beispiel #7
0
 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)
Beispiel #8
0
 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)
Beispiel #9
0
 def test_is_path_updated_success(self):
     patch = [{'path': '/name', 'op': 'remove'}]
     path = '/name'
     value = utils.is_path_updated(patch, path)
     self.assertTrue(value)
Beispiel #10
0
    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