Example #1
0
def handle_post_port_like_extra_vif(p_dict):
    """Handle a Post request that sets .extra['vif_port_id'].

    This handles attach of VIFs via specifying the VIF port ID
    in a port or port group's extra['vif_port_id'] field.

    :param p_dict: a dictionary with field names/values for the port or
                   port group
    :return: VIF or None
    """
    vif = p_dict.get('extra', {}).get('vif_port_id')
    if vif:
        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove
        #             warning and support for extra[]; else (< 1.28)
        #             still support it; continue copying to internal_info
        #             (see bug 1722850). i.e., change the 7 lines of code
        #             below to something like:
        #                 if not api_utils.allow_vifs_subcontroller():
        #                     internal_info = {'tenant_vif_port_id': vif}
        #                     pg_dict['internal_info'] = internal_info
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): this value should really be in .internal_info[..]
        #             which is what would happen if they had used the
        #             POST /v1/nodes/<node>/vifs API.
        internal_info = {'tenant_vif_port_id': vif}
        p_dict['internal_info'] = internal_info
    return vif
Example #2
0
def handle_post_port_like_extra_vif(p_dict):
    """Handle a Post request that sets .extra['vif_port_id'].

    This handles attach of VIFs via specifying the VIF port ID
    in a port or port group's extra['vif_port_id'] field.

    :param p_dict: a dictionary with field names/values for the port or
                   port group
    :return: VIF or None
    """
    vif = p_dict.get('extra', {}).get('vif_port_id')
    if vif:
        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove
        #             warning and support for extra[]; else (< 1.28)
        #             still support it; continue copying to internal_info
        #             (see bug 1722850). i.e., change the 7 lines of code
        #             below to something like:
        #                 if not api_utils.allow_vifs_subcontroller():
        #                     internal_info = {'tenant_vif_port_id': vif}
        #                     pg_dict['internal_info'] = internal_info
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): this value should really be in .internal_info[..]
        #             which is what would happen if they had used the
        #             POST /v1/nodes/<node>/vifs API.
        internal_info = {'tenant_vif_port_id': vif}
        p_dict['internal_info'] = internal_info
    return vif
Example #3
0
    def post(self, port):
        """Create a new port.

        :param port: a port within the request body.
        :raises: NotAcceptable, HTTPNotFound, Conflict
        """
        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:port:create', cdict, cdict)

        if self.parent_node_ident or self.parent_portgroup_ident:
            raise exception.OperationNotPermitted()

        pdict = port.as_dict()
        self._check_allowed_port_fields(pdict)

        extra = pdict.get('extra')
        vif = extra.get('vif_port_id') if extra else None
        if vif:
            common_utils.warn_about_deprecated_extra_vif_port_id()
        if (pdict.get('portgroup_uuid') and
                (pdict.get('pxe_enabled') or vif)):
            rpc_pg = objects.Portgroup.get_by_uuid(context,
                                                   pdict['portgroup_uuid'])
            if not rpc_pg.standalone_ports_supported:
                msg = _("Port group %s doesn't support standalone ports. "
                        "This port cannot be created as a member of that "
                        "port group because either 'extra/vif_port_id' "
                        "was specified or 'pxe_enabled' was set to True.")
                raise exception.Conflict(
                    msg % pdict['portgroup_uuid'])

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not pdict.get('uuid'):
            pdict['uuid'] = uuidutils.generate_uuid()

        rpc_port = objects.Port(context, **pdict)
        rpc_node = objects.Node.get_by_id(context, rpc_port.node_id)

        notify_extra = {'node_uuid': port.node_uuid,
                        'portgroup_uuid': port.portgroup_uuid}
        notify.emit_start_notification(context, rpc_port, 'create',
                                       **notify_extra)
        with notify.handle_error_notification(context, rpc_port, 'create',
                                              **notify_extra):
            # TODO(mgoddard): In RPC API v1.41, port creation was moved to the
            # conductor service to facilitate validation of the physical
            # network field of ports in portgroups. Further consideration is
            # required determine how best to support rolling upgrades from a
            # release in which ports are created by the API service to one in
            # which they are created by the conductor service, while ensuring
            # that all required validation is performed.
            topic = pecan.request.rpcapi.get_topic_for(rpc_node)
            new_port = pecan.request.rpcapi.create_port(context, rpc_port,
                                                        topic)
        notify.emit_end_notification(context, new_port, 'create',
                                     **notify_extra)
        # Set the HTTP Location Header
        pecan.response.location = link.build_url('ports', new_port.uuid)
        return Port.convert_with_links(new_port)
Example #4
0
    def post(self, port):
        """Create a new port.

        :param port: a port within the request body.
        :raises: NotAcceptable, HTTPNotFound, Conflict
        """
        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:port:create', cdict, cdict)

        if self.parent_node_ident or self.parent_portgroup_ident:
            raise exception.OperationNotPermitted()

        pdict = port.as_dict()
        self._check_allowed_port_fields(pdict)

        extra = pdict.get('extra')
        vif = extra.get('vif_port_id') if extra else None
        if vif:
            common_utils.warn_about_deprecated_extra_vif_port_id()
        if (pdict.get('portgroup_uuid') and (pdict.get('pxe_enabled') or vif)):
            rpc_pg = objects.Portgroup.get_by_uuid(context,
                                                   pdict['portgroup_uuid'])
            if not rpc_pg.standalone_ports_supported:
                msg = _("Port group %s doesn't support standalone ports. "
                        "This port cannot be created as a member of that "
                        "port group because either 'extra/vif_port_id' "
                        "was specified or 'pxe_enabled' was set to True.")
                raise exception.Conflict(msg % pdict['portgroup_uuid'])

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not pdict.get('uuid'):
            pdict['uuid'] = uuidutils.generate_uuid()

        rpc_port = objects.Port(context, **pdict)
        rpc_node = objects.Node.get_by_id(context, rpc_port.node_id)

        notify_extra = {
            'node_uuid': port.node_uuid,
            'portgroup_uuid': port.portgroup_uuid
        }
        notify.emit_start_notification(context, rpc_port, 'create',
                                       **notify_extra)
        with notify.handle_error_notification(context, rpc_port, 'create',
                                              **notify_extra):
            # TODO(mgoddard): In RPC API v1.41, port creation was moved to the
            # conductor service to facilitate validation of the physical
            # network field of ports in portgroups. Further consideration is
            # required determine how best to support rolling upgrades from a
            # release in which ports are created by the API service to one in
            # which they are created by the conductor service, while ensuring
            # that all required validation is performed.
            topic = pecan.request.rpcapi.get_topic_for(rpc_node)
            new_port = pecan.request.rpcapi.create_port(
                context, rpc_port, topic)
        notify.emit_end_notification(context, new_port, 'create',
                                     **notify_extra)
        # Set the HTTP Location Header
        pecan.response.location = link.build_url('ports', new_port.uuid)
        return Port.convert_with_links(new_port)
Example #5
0
 def test_warn_about_deprecated_extra_vif_port_id(self, mock_log):
     # Set variable to default value
     utils.warn_deprecated_extra_vif_port_id = False
     utils.warn_about_deprecated_extra_vif_port_id()
     utils.warn_about_deprecated_extra_vif_port_id()
     self.assertEqual(1, mock_log.warning.call_count)
     self.assertIn("extra['vif_port_id'] is deprecated and will not",
                   mock_log.warning.call_args[0][0])
Example #6
0
 def test_warn_about_deprecated_extra_vif_port_id(self, mock_log):
     # Set variable to default value
     utils.warn_deprecated_extra_vif_port_id = False
     utils.warn_about_deprecated_extra_vif_port_id()
     utils.warn_about_deprecated_extra_vif_port_id()
     self.assertEqual(1, mock_log.warning.call_count)
     self.assertIn("extra['vif_port_id'] is deprecated and will not",
                   mock_log.warning.call_args[0][0])
Example #7
0
    def post(self, portgroup):
        """Create a new portgroup.

        :param portgroup: a portgroup within the request body.
        """
        if not api_utils.allow_portgroups():
            raise exception.NotFound()

        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:portgroup:create', cdict, cdict)

        if self.parent_node_ident:
            raise exception.OperationNotPermitted()

        if (not api_utils.allow_portgroup_mode_properties()
                and (portgroup.mode is not wtypes.Unset
                     or portgroup.properties is not wtypes.Unset)):
            raise exception.NotAcceptable()

        if (portgroup.name
                and not api_utils.is_valid_logical_name(portgroup.name)):
            error_msg = _("Cannot create portgroup with invalid name "
                          "'%(name)s'") % {
                              'name': portgroup.name
                          }
            raise wsme.exc.ClientSideError(error_msg,
                                           status_code=http_client.BAD_REQUEST)

        pg_dict = portgroup.as_dict()
        vif = pg_dict.get('extra', {}).get('vif_port_id')
        if vif:
            common_utils.warn_about_deprecated_extra_vif_port_id()

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not pg_dict.get('uuid'):
            pg_dict['uuid'] = uuidutils.generate_uuid()

        new_portgroup = objects.Portgroup(context, **pg_dict)

        notify.emit_start_notification(context,
                                       new_portgroup,
                                       'create',
                                       node_uuid=portgroup.node_uuid)
        with notify.handle_error_notification(context,
                                              new_portgroup,
                                              'create',
                                              node_uuid=portgroup.node_uuid):
            new_portgroup.create()
        notify.emit_end_notification(context,
                                     new_portgroup,
                                     'create',
                                     node_uuid=portgroup.node_uuid)

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('portgroups',
                                                 new_portgroup.uuid)
        return Portgroup.convert_with_links(new_portgroup)
Example #8
0
    def post(self, port):
        """Create a new port.

        :param port: a port within the request body.
        :raises: NotAcceptable, HTTPNotFound, Conflict
        """
        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:port:create', cdict, cdict)

        if self.parent_node_ident or self.parent_portgroup_ident:
            raise exception.OperationNotPermitted()

        pdict = port.as_dict()
        if (not api_utils.allow_port_advanced_net_fields()
                and set(pdict).intersection(self.advanced_net_fields)):
            raise exception.NotAcceptable()
        if (not api_utils.allow_portgroups_subcontrollers()
                and 'portgroup_uuid' in pdict):
            raise exception.NotAcceptable()

        extra = pdict.get('extra')
        vif = extra.get('vif_port_id') if extra else None
        if vif:
            common_utils.warn_about_deprecated_extra_vif_port_id()
        if (pdict.get('portgroup_uuid') and (pdict.get('pxe_enabled') or vif)):
            rpc_pg = objects.Portgroup.get_by_uuid(context,
                                                   pdict['portgroup_uuid'])
            if not rpc_pg.standalone_ports_supported:
                msg = _("Port group %s doesn't support standalone ports. "
                        "This port cannot be created as a member of that "
                        "port group because either 'extra/vif_port_id' "
                        "was specified or 'pxe_enabled' was set to True.")
                raise exception.Conflict(msg % pdict['portgroup_uuid'])

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not pdict.get('uuid'):
            pdict['uuid'] = uuidutils.generate_uuid()

        new_port = objects.Port(context, **pdict)

        notify.emit_start_notification(context,
                                       new_port,
                                       'create',
                                       node_uuid=port.node_uuid)
        with notify.handle_error_notification(context,
                                              new_port,
                                              'create',
                                              node_uuid=port.node_uuid):
            new_port.create()
        notify.emit_end_notification(context,
                                     new_port,
                                     'create',
                                     node_uuid=port.node_uuid)
        # Set the HTTP Location Header
        pecan.response.location = link.build_url('ports', new_port.uuid)
        return Port.convert_with_links(new_port)
Example #9
0
def handle_patch_port_like_extra_vif(rpc_object, api_object, patch):
    """Handle a Patch request that modifies .extra['vif_port_id'].

    This handles attach/detach of VIFs via the VIF port ID
    in a port or port group's extra['vif_port_id'] field.

    :param rpc_object: a Port or Portgroup RPC object
    :param api_object: the corresponding Port or Portgroup API object
    :param patch: the JSON patch in the API request
    """
    vif_list = get_patch_values(patch, '/extra/vif_port_id')
    vif = None
    if vif_list:
        # if specified more than once, use the last value
        vif = vif_list[-1]

        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove this
        # warning and don't copy to internal_info; else (<1.28) still
        # support it; continue copying to internal_info (see bug 1722850).
        # i.e., change the 8 lines of code below to something like:
        #   if not allow_vifs_subcontroller():
        #       int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        #       if (not int_info or
        #           int_info == rpc_object.extra.get('vif_port_id')):
        #           api_object.internal_info['tenant_vif_port_id'] = vif
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): if the user isn't also using the REST API
        #             'POST nodes/<node>/vifs', we are safe to copy the
        #             .extra[] value to the .internal_info location
        int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        if (not int_info or int_info == rpc_object.extra.get('vif_port_id')):
            api_object.internal_info['tenant_vif_port_id'] = vif

    elif is_path_removed(patch, '/extra/vif_port_id'):
        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove this
        # warning and don't remove from internal_info; else (<1.28) still
        # support it; remove from internal_info (see bug 1722850).
        # i.e., change the 8 lines of code below to something like:
        #   if not allow_vifs_subcontroller():
        #     int_info = rpc_object.internal_info.get('tenant_vif...')
        #     if (int_info and int_info==rpc_object.extra.get('vif_port_id')):
        #         api_object.internal_info['tenant_vif_port_id'] = None
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): if the user isn't also using the REST API
        #             'POST nodes/<node>/vifs', we are safe to remove the
        #             .extra[] value from the .internal_info location
        int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        if (int_info and int_info == rpc_object.extra.get('vif_port_id')):
            api_object.internal_info.pop('tenant_vif_port_id')
Example #10
0
def handle_patch_port_like_extra_vif(rpc_object, api_object, patch):
    """Handle a Patch request that modifies .extra['vif_port_id'].

    This handles attach/detach of VIFs via the VIF port ID
    in a port or port group's extra['vif_port_id'] field.

    :param rpc_object: a Port or Portgroup RPC object
    :param api_object: the corresponding Port or Portgroup API object
    :param patch: the JSON patch in the API request
    """
    vif_list = get_patch_values(patch, '/extra/vif_port_id')
    vif = None
    if vif_list:
        # if specified more than once, use the last value
        vif = vif_list[-1]

        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove this
        # warning and don't copy to internal_info; else (<1.28) still
        # support it; continue copying to internal_info (see bug 1722850).
        # i.e., change the 8 lines of code below to something like:
        #   if not allow_vifs_subcontroller():
        #       int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        #       if (not int_info or
        #           int_info == rpc_object.extra.get('vif_port_id')):
        #           api_object.internal_info['tenant_vif_port_id'] = vif
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): if the user isn't also using the REST API
        #             'POST nodes/<node>/vifs', we are safe to copy the
        #             .extra[] value to the .internal_info location
        int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        if (not int_info or int_info == rpc_object.extra.get('vif_port_id')):
            api_object.internal_info['tenant_vif_port_id'] = vif

    elif is_path_removed(patch, '/extra/vif_port_id'):
        # TODO(rloo): in Stein cycle: if API version >= 1.28, remove this
        # warning and don't remove from internal_info; else (<1.28) still
        # support it; remove from internal_info (see bug 1722850).
        # i.e., change the 8 lines of code below to something like:
        #   if not allow_vifs_subcontroller():
        #     int_info = rpc_object.internal_info.get('tenant_vif...')
        #     if (int_info and int_info==rpc_object.extra.get('vif_port_id')):
        #         api_object.internal_info['tenant_vif_port_id'] = None
        if allow_vifs_subcontroller():
            utils.warn_about_deprecated_extra_vif_port_id()
        # NOTE(rloo): if the user isn't also using the REST API
        #             'POST nodes/<node>/vifs', we are safe to remove the
        #             .extra[] value from the .internal_info location
        int_info = rpc_object.internal_info.get('tenant_vif_port_id')
        if (int_info and int_info == rpc_object.extra.get('vif_port_id')):
            api_object.internal_info.pop('tenant_vif_port_id')
Example #11
0
    def portgroup_changed(self, task, portgroup_obj):
        """Handle any actions required when a portgroup changes

        :param task: a TaskManager instance.
        :param portgroup_obj: a changed Portgroup object from the API before
            it is saved to database.
        :raises: FailedToUpdateDHCPOptOnPort, Conflict
        """

        context = task.context
        portgroup_uuid = portgroup_obj.uuid
        # NOTE(vsaienko) address is not mandatory field in portgroup.
        # Do not touch neutron port if we removed address on portgroup.
        if ('address' in portgroup_obj.obj_what_changed()
                and portgroup_obj.address):
            pg_vif = (portgroup_obj.internal_info.get(TENANT_VIF_KEY)
                      or portgroup_obj.extra.get('vif_port_id'))
            if pg_vif:
                neutron.update_port_address(pg_vif, portgroup_obj.address)

        if 'extra' in portgroup_obj.obj_what_changed():
            original_portgroup = objects.Portgroup.get_by_id(
                context, portgroup_obj.id)
            if (portgroup_obj.extra.get('vif_port_id')
                    and portgroup_obj.extra['vif_port_id'] !=
                    original_portgroup.extra.get('vif_port_id')):
                utils.warn_about_deprecated_extra_vif_port_id()

        if ('standalone_ports_supported' in portgroup_obj.obj_what_changed()):
            if not portgroup_obj.standalone_ports_supported:
                ports = [
                    p for p in task.ports if p.portgroup_id == portgroup_obj.id
                ]
                for p in ports:
                    vif = p.internal_info.get(TENANT_VIF_KEY,
                                              p.extra.get('vif_port_id'))
                    reason = []
                    if p.pxe_enabled:
                        reason.append("'pxe_enabled' is set to True")
                    if vif:
                        reason.append('VIF %s is attached to this port' % vif)

                    if reason:
                        msg = (_("standalone_ports_supported can not be set "
                                 "to False, because the port group %(pg_id)s "
                                 "contains port with %(reason)s") % {
                                     'pg_id': portgroup_uuid,
                                     'reason': ', '.join(reason)
                                 })
                        raise exception.Conflict(msg)
Example #12
0
    def post(self, portgroup):
        """Create a new portgroup.

        :param portgroup: a portgroup within the request body.
        """
        if not api_utils.allow_portgroups():
            raise exception.NotFound()

        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:portgroup:create', cdict, cdict)

        if self.parent_node_ident:
            raise exception.OperationNotPermitted()

        if (not api_utils.allow_portgroup_mode_properties() and
                (portgroup.mode is not wtypes.Unset or
                 portgroup.properties is not wtypes.Unset)):
            raise exception.NotAcceptable()

        if (portgroup.name and
                not api_utils.is_valid_logical_name(portgroup.name)):
            error_msg = _("Cannot create portgroup with invalid name "
                          "'%(name)s'") % {'name': portgroup.name}
            raise wsme.exc.ClientSideError(
                error_msg, status_code=http_client.BAD_REQUEST)

        pg_dict = portgroup.as_dict()
        vif = pg_dict.get('extra', {}).get('vif_port_id')
        if vif:
            common_utils.warn_about_deprecated_extra_vif_port_id()

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not pg_dict.get('uuid'):
            pg_dict['uuid'] = uuidutils.generate_uuid()

        new_portgroup = objects.Portgroup(context, **pg_dict)

        notify.emit_start_notification(context, new_portgroup, 'create',
                                       node_uuid=portgroup.node_uuid)
        with notify.handle_error_notification(context, new_portgroup, 'create',
                                              node_uuid=portgroup.node_uuid):
            new_portgroup.create()
        notify.emit_end_notification(context, new_portgroup, 'create',
                                     node_uuid=portgroup.node_uuid)

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('portgroups',
                                                 new_portgroup.uuid)
        return Portgroup.convert_with_links(new_portgroup)
Example #13
0
    def portgroup_changed(self, task, portgroup_obj):
        """Handle any actions required when a portgroup changes

        :param task: a TaskManager instance.
        :param portgroup_obj: a changed Portgroup object from the API before
            it is saved to database.
        :raises: FailedToUpdateDHCPOptOnPort, Conflict
        """

        context = task.context
        portgroup_uuid = portgroup_obj.uuid
        # NOTE(vsaienko) address is not mandatory field in portgroup.
        # Do not touch neutron port if we removed address on portgroup.
        if ('address' in portgroup_obj.obj_what_changed() and
                portgroup_obj.address):
            pg_vif = (portgroup_obj.internal_info.get(TENANT_VIF_KEY) or
                      portgroup_obj.extra.get('vif_port_id'))
            if pg_vif:
                neutron.update_port_address(pg_vif, portgroup_obj.address)

        if 'extra' in portgroup_obj.obj_what_changed():
            original_portgroup = objects.Portgroup.get_by_id(context,
                                                             portgroup_obj.id)
            if (portgroup_obj.extra.get('vif_port_id') and
                    portgroup_obj.extra['vif_port_id'] !=
                    original_portgroup.extra.get('vif_port_id')):
                utils.warn_about_deprecated_extra_vif_port_id()

        if ('standalone_ports_supported' in
                portgroup_obj.obj_what_changed()):
            if not portgroup_obj.standalone_ports_supported:
                ports = [p for p in task.ports if
                         p.portgroup_id == portgroup_obj.id]
                for p in ports:
                    vif = p.internal_info.get(
                        TENANT_VIF_KEY, p.extra.get('vif_port_id'))
                    reason = []
                    if p.pxe_enabled:
                        reason.append("'pxe_enabled' is set to True")
                    if vif:
                        reason.append('VIF %s is attached to this port' % vif)

                    if reason:
                        msg = (_("standalone_ports_supported can not be set "
                                 "to False, because the port group %(pg_id)s "
                                 "contains port with %(reason)s") % {
                               'pg_id': portgroup_uuid,
                               'reason': ', '.join(reason)})
                        raise exception.Conflict(msg)
Example #14
0
    def port_changed(self, task, port_obj):
        """Handle any actions required when a port changes

        :param task: a TaskManager instance.
        :param port_obj: a changed Port object from the API before it is saved
            to database.
        :raises: FailedToUpdateDHCPOptOnPort, Conflict
        """
        context = task.context
        node = task.node
        port_uuid = port_obj.uuid
        portgroup_obj = None
        if port_obj.portgroup_id:
            portgroup_obj = [
                pg for pg in task.portgroups if pg.id == port_obj.portgroup_id
            ][0]
        vif = (port_obj.internal_info.get(TENANT_VIF_KEY)
               or port_obj.extra.get('vif_port_id'))
        if 'address' in port_obj.obj_what_changed():
            if vif:
                neutron.update_port_address(vif, port_obj.address)

        if 'extra' in port_obj.obj_what_changed():
            original_port = objects.Port.get_by_id(context, port_obj.id)
            updated_client_id = port_obj.extra.get('client-id')
            if (port_obj.extra.get('vif_port_id')
                    and (port_obj.extra['vif_port_id'] !=
                         original_port.extra.get('vif_port_id'))):
                utils.warn_about_deprecated_extra_vif_port_id()
            if (original_port.extra.get('client-id') != updated_client_id):
                # DHCP Option with opt_value=None will remove it
                # from the neutron port
                if vif:
                    api = dhcp_factory.DHCPFactory()
                    client_id_opt = {
                        'opt_name': 'client-id',
                        'opt_value': updated_client_id
                    }

                    api.provider.update_port_dhcp_opts(vif, [client_id_opt])
                # Log warning if there is no VIF and an instance
                # is associated with the node.
                elif node.instance_uuid:
                    LOG.warning(
                        _LW("No VIF found for instance %(instance)s "
                            "port %(port)s when attempting to update port "
                            "client-id."), {
                                'port': port_uuid,
                                'instance': node.instance_uuid
                            })

        if portgroup_obj and ((set(port_obj.obj_what_changed())
                               & {'pxe_enabled', 'portgroup_id'}) or vif):
            if not portgroup_obj.standalone_ports_supported:
                reason = []
                if port_obj.pxe_enabled:
                    reason.append("'pxe_enabled' was set to True")
                if vif:
                    reason.append('VIF %s is attached to the port' % vif)

                if reason:
                    msg = (_("Port group %(portgroup)s doesn't support "
                             "standalone ports. This port %(port)s cannot be "
                             " a member of that port group because of: "
                             "%(reason)s") % {
                                 "reason": ', '.join(reason),
                                 "portgroup": portgroup_obj.uuid,
                                 "port": port_uuid
                             })
                    raise exception.Conflict(msg)
Example #15
0
    def port_changed(self, task, port_obj):
        """Handle any actions required when a port changes

        :param task: a TaskManager instance.
        :param port_obj: a changed Port object from the API before it is saved
            to database.
        :raises: FailedToUpdateDHCPOptOnPort, Conflict
        """
        context = task.context
        node = task.node
        port_uuid = port_obj.uuid
        portgroup_obj = None
        if port_obj.portgroup_id:
            portgroup_obj = [pg for pg in task.portgroups if
                             pg.id == port_obj.portgroup_id][0]
        vif = (port_obj.internal_info.get(TENANT_VIF_KEY) or
               port_obj.extra.get('vif_port_id'))
        if 'address' in port_obj.obj_what_changed():
            if vif:
                neutron.update_port_address(vif, port_obj.address)

        if 'extra' in port_obj.obj_what_changed():
            original_port = objects.Port.get_by_id(context, port_obj.id)
            updated_client_id = port_obj.extra.get('client-id')
            if (port_obj.extra.get('vif_port_id') and
                    (port_obj.extra['vif_port_id'] !=
                     original_port.extra.get('vif_port_id'))):
                utils.warn_about_deprecated_extra_vif_port_id()
            if (original_port.extra.get('client-id') !=
                updated_client_id):
                # DHCP Option with opt_value=None will remove it
                # from the neutron port
                if vif:
                    api = dhcp_factory.DHCPFactory()
                    client_id_opt = {'opt_name': 'client-id',
                                     'opt_value': updated_client_id}

                    api.provider.update_port_dhcp_opts(
                        vif, [client_id_opt])
                # Log warning if there is no VIF and an instance
                # is associated with the node.
                elif node.instance_uuid:
                    LOG.warning(
                        "No VIF found for instance %(instance)s "
                        "port %(port)s when attempting to update port "
                        "client-id.",
                        {'port': port_uuid,
                         'instance': node.instance_uuid})

        if portgroup_obj and ((set(port_obj.obj_what_changed()) &
                              {'pxe_enabled', 'portgroup_id'}) or vif):
            if not portgroup_obj.standalone_ports_supported:
                reason = []
                if port_obj.pxe_enabled:
                    reason.append("'pxe_enabled' was set to True")
                if vif:
                    reason.append('VIF %s is attached to the port' % vif)

                if reason:
                    msg = (_("Port group %(portgroup)s doesn't support "
                             "standalone ports. This port %(port)s cannot be "
                             " a member of that port group because of: "
                             "%(reason)s") % {"reason": ', '.join(reason),
                                              "portgroup": portgroup_obj.uuid,
                                              "port": port_uuid})
                    raise exception.Conflict(msg)