Merge "Add a new 'category' field to the Port object"

This commit is contained in:
Zuul
2025-08-22 19:58:19 +00:00
committed by Gerrit Code Review
21 changed files with 144 additions and 8 deletions

View File

@@ -37,6 +37,9 @@ Response to include only the specified fields, rather than the default set.
.. versionadded:: 1.100
Added the ``vendor`` field.
.. versionadded:: 1.101
Added the ``category`` field.
Normal response code: 200
Error codes: 400,401,403,404
@@ -90,6 +93,9 @@ Return a detailed list of bare metal Ports associated with ``portgroup_ident``.
.. versionadded:: 1.100
Added the ``vendor`` field.
.. versionadded:: 1.101
Added the ``category`` field.
Normal response code: 200
Error codes: 400,401,403,404
@@ -127,6 +133,7 @@ Response
- name: port_name
- description: port_description
- vendor: port_vendor
- category: port_category
**Example details of a Portgroup's Ports:**

View File

@@ -59,6 +59,9 @@ By default, this query will return the uuid and address for each Port.
.. versionadded:: 1.100
Added the ``vendor`` field.
.. versionadded:: 1.101
Added the ``category`` field.
Normal response code: 200
Request
@@ -134,6 +137,9 @@ This method requires a Node UUID and the physical hardware address for the Port
.. versionadded:: 1.100
Added the ``vendor`` field.
.. versionadded:: 1.101
Added the ``category`` field.
Normal response code: 201
Request
@@ -153,6 +159,7 @@ Request
- uuid: req_uuid
- description: req_port_description
- vendor: req_port_vendor
- category: req_port_category
.. note::
Either `node_ident` or `node_uuid` is a valid parameter.
@@ -183,6 +190,7 @@ Response
- is_smartnic: is_smartnic
- description: port_description
- vendor: port_vendor
- category: port_category
**Example Port creation response:**
@@ -227,6 +235,9 @@ Return a list of bare metal Ports, with detailed information.
.. versionadded:: 1.100
Added the ``vendor`` field.
.. versionadded:: 1.101
Added the ``category`` field.
Normal response code: 200
Request
@@ -266,6 +277,7 @@ Response
- is_smartnic: is_smartnic
- description: port_description
- vendor: port_vendor
- category: port_category
**Example detailed Port list response:**

View File

@@ -1570,6 +1570,12 @@ port_address:
in: body
required: true
type: string
port_category:
description: |
Category of the network Port. Helps to further differentiate the Port.
in: body
required: false
type: string
port_description:
description: |
Descriptive text about the network Port.
@@ -1977,6 +1983,12 @@ req_port_address:
in: body
required: true
type: string
req_port_category:
description: |
Category of the network Port. Helps to further differentiate the Port.
in: body
required: false
type: string
req_port_description:
description: |
Descriptive text about the network Port.

View File

@@ -24,6 +24,7 @@
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
"physical_network": "physnet1",
"vendor": "splitrock",
"category": "hupernet",
"portgroup_uuid": "e43c722c-248e-4c6e-8ce8-0d8ff129387a",
"pxe_enabled": true,
"updated_at": "2016-08-18T22:28:49.653974+00:00",

View File

@@ -4,6 +4,7 @@
"name": "port1",
"description": "Physical Network",
"vendor": "splitrock",
"category": "hypernet",
"address": "11:11:11:11:11:11",
"is_smartnic": true,
"local_link_connection": {

View File

@@ -22,6 +22,7 @@
"name": "port1",
"description": "Physical Network",
"vendor": "splitrock",
"category": "hypernet",
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
"physical_network": "physnet1",
"portgroup_uuid": "e43c722c-248e-4c6e-8ce8-0d8ff129387a",

View File

@@ -24,6 +24,7 @@
"name": "port1",
"description": "Physical Network",
"vendor": "splitrock",
"category": "hypernet",
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
"physical_network": "physnet1",
"portgroup_uuid": "e43c722c-248e-4c6e-8ce8-0d8ff129387a",

View File

@@ -22,6 +22,7 @@
"name": "port1",
"description": "Physical Network",
"vendor": "splitrock",
"category": "hypernet",
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
"physical_network": "physnet1",
"portgroup_uuid": "e43c722c-248e-4c6e-8ce8-0d8ff129387a",

View File

@@ -2,6 +2,11 @@
REST API Version History
========================
1.101 (Flamingo)
-----------------------
Add a 'category' field to the Port object.
1.100 (Flamingo)
-----------------------

View File

@@ -55,6 +55,7 @@ PORT_SCHEMA = {
'name': {'type': ['string', 'null']},
'description': {'type': ['string', 'null'], 'maxLength': 255},
'vendor': {'type': ['string', 'null'], 'maxLength': 32},
'category': {'type': ['string', 'null'], 'maxLength': 80},
},
'required': ['address'],
'oneOf': [
@@ -80,6 +81,7 @@ PATCH_ALLOWED_FIELDS = [
'name',
'description',
'vendor',
'category',
]
PORT_VALIDATOR_EXTRA = args.dict_valid(
@@ -144,6 +146,9 @@ def hide_fields_in_newer_versions(port):
# if requested version is < 1.100, hide vendor field.
if not api_utils.allow_port_vendor():
port.pop('vendor', None)
# if requested version is < 1.101, hide category field.
if not api_utils.allow_port_category():
port.pop('category', None)
def convert_with_links(rpc_port, fields=None, sanitize=True):
@@ -413,6 +418,9 @@ class PortsController(rest.RestController):
if ('vendor' in fields
and not api_utils.allow_port_vendor()):
raise exception.NotAcceptable()
if ('category' in fields
and not api_utils.allow_port_category()):
raise exception.NotAcceptable()
@METRICS.timer('PortsController.get_all')
@method.expose()

View File

@@ -2248,6 +2248,14 @@ def allow_port_description():
def allow_port_vendor():
"""Check if vendor is allowed for ports.
Version 1.100 of the API added description field to the port object.
Version 1.100 of the API added vendor field to the port object.
"""
return api.request.version.minor >= versions.MINOR_100_PORT_VENDOR
def allow_port_category():
"""Check if category is allowed for ports.
Version 1.101 of the API added category field to the port object.
"""
return api.request.version.minor >= versions.MINOR_101_PORT_CATEGORY

View File

@@ -138,6 +138,7 @@ BASE_VERSION = 1
# v1.98: Add support for object attributes with keys containing ~ or /.
# v1.99: Add conductor group filtering to port and portgroup list
# v1.100: Add vendor field to port.
# v1.101: Add category field to port.
MINOR_0_JUNO = 0
MINOR_1_INITIAL_VERSION = 1
@@ -240,6 +241,7 @@ MINOR_97_PORT_DESCRIPTION = 97
MINOR_98_SUPPORT_SPECIAL_CHAR_IN_ATTRIBUTES = 98
MINOR_99_PORT_PORTGROUP_CONDUCTOR_GROUP_FILTER = 99
MINOR_100_PORT_VENDOR = 100
MINOR_101_PORT_CATEGORY = 101
# When adding another version, update:
# - MINOR_MAX_VERSION
@@ -248,7 +250,7 @@ MINOR_100_PORT_VENDOR = 100
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
MINOR_MAX_VERSION = MINOR_100_PORT_VENDOR
MINOR_MAX_VERSION = MINOR_101_PORT_CATEGORY
# String representations of the minor and maximum versions
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)

View File

@@ -896,7 +896,7 @@ RELEASE_MAPPING = {
# make it below. To release, we will preserve a version matching
# the release as a separate block of text, like above.
'master': {
'api': '1.100',
'api': '1.101',
'rpc': '1.61',
'objects': {
'Allocation': ['1.3', '1.2', '1.1'],
@@ -908,7 +908,7 @@ RELEASE_MAPPING = {
'Chassis': ['1.4', '1.3'],
'Deployment': ['1.1', '1.0'],
'DeployTemplate': ['1.2', '1.1'],
'Port': ['1.14', '1.13', '1.12'],
'Port': ['1.15', '1.14', '1.13', '1.12'],
'Portgroup': ['1.6', '1.5'],
'Trait': ['1.1', '1.0'],
'TraitList': ['1.1', '1.0'],

View File

@@ -0,0 +1,31 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""add category attribute to port object
Revision ID: 3ef27505c9fb
Revises: e4827561979d
Create Date: 2025-07-21 01:33:47.215396
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3ef27505c9fb'
down_revision = 'e4827561979d'
def upgrade():
op.add_column('ports', sa.Column('category', sa.String(length=80),
nullable=True))

View File

@@ -271,6 +271,7 @@ class Port(Base):
name = Column(String(255), nullable=True)
description = Column(String(255), nullable=True)
vendor = Column(String(32), nullable=True)
category = Column(String(80), nullable=True)
_node_uuid = orm.relationship(
"Node",

View File

@@ -48,7 +48,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
# Version 1.12: Add description field
# Version 1.13: Add vendor field
# Version 1.14: Mark multiple methods as remotable methods.
VERSION = '1.14'
# Version 1.15: Add category field
VERSION = '1.15'
dbapi = dbapi.get_instance()
@@ -70,6 +71,7 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
'name': object_fields.StringField(nullable=True),
'description': object_fields.StringField(nullable=True),
'vendor': object_fields.StringField(nullable=True),
'category': object_fields.StringField(nullable=True),
}
def _convert_field_by_version(self, field_name, introduced_version,
@@ -129,6 +131,9 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
Version 1.13: remove vendor for unsupported versions if
remove_unavailable_fields is True.
Version 1.15: remove category for unsupported versions if
remove_unavailable_fields is True.
:param target_version: the desired version of the object
:param remove_unavailable_fields: True to remove fields that are
unavailable in the target version; set this to True when
@@ -163,6 +168,9 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
# Convert the vendor field.
self._convert_field_by_version('vendor', (1, 13), target_version,
remove_unavailable_fields)
# Convert the category field.
self._convert_field_by_version('category', (1, 15), target_version,
remove_unavailable_fields)
@object_base.remotable_classmethod
def get(cls, context, port_id):
@@ -495,7 +503,8 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
# Version 1.4: Add "name" field
# Version 1.5: Add "description" field
# Version 1.6: Add "vendor" field
VERSION = '1.6'
# Version 1.7: Add "category" field
VERSION = '1.7'
SCHEMA = {
'address': ('port', 'address'),
@@ -510,6 +519,7 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
'name': ('port', 'name'),
'description': ('port', 'description'),
'vendor': ('port', 'vendor'),
'category': ('port', 'category'),
}
fields = {
@@ -529,6 +539,7 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
'name': object_fields.StringField(nullable=True),
'description': object_fields.StringField(nullable=True),
'vendor': object_fields.StringField(nullable=True),
'category': object_fields.StringField(nullable=True),
}
def __init__(self, port, node_uuid, portgroup_uuid):

View File

@@ -2001,6 +2001,7 @@ class TestPost(test_api_base.BaseApiTest):
pdict.pop('name')
pdict.pop('description')
pdict.pop('vendor')
pdict.pop('category')
headers = {api_base.Version.string: str(api_v1.min_version())}
response = self.post_json('/ports', pdict, headers=headers)
self.assertEqual('application/json', response.content_type)

View File

@@ -368,3 +368,29 @@ class DbPortTestCase(base.DbTestCase):
retrieved_port1 = self.dbapi.get_port_by_uuid(port1.uuid)
self.assertEqual(new_vendor, retrieved_port1.vendor)
def test_create_port_with_category(self):
category = 'hypernet'
port1 = db_utils.create_test_port(
uuid=uuidutils.generate_uuid(),
node_id=self.node.id,
address='52:54:00:cf:2d:42',
category=category)
port2 = db_utils.create_test_port(
uuid=uuidutils.generate_uuid(),
node_id=self.node.id,
address='52:54:00:cf:2d:45',
category=category)
self.assertEqual(category, port1.category)
self.assertEqual(category, port2.category)
new_category = 'ultranet'
updated_port = self.dbapi.update_port(
port1.id, {'category': new_category})
self.assertEqual(new_category, updated_port.category)
retrieved_port1 = self.dbapi.get_port_by_uuid(port1.uuid)
self.assertEqual(new_category, retrieved_port1.category)

View File

@@ -290,6 +290,7 @@ def get_test_port(**kw):
'name': kw.get('name'),
'description': kw.get('description'),
'vendor': kw.get('vendor'),
'category': kw.get('category'),
}

View File

@@ -678,7 +678,7 @@ expected_object_fingerprints = {
'Node': '1.42-a1d3e6011e3cdb27aafa9353b7c0b6d4',
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
'Chassis': '1.4-fe427272d8bad232a8d46e996a5ca42a',
'Port': '1.14-684faad7173c1d9e8a2d630381c51903',
'Port': '1.15-013610c0fe2e370b14f4304e0d8aeb3a',
'Portgroup': '1.6-ada5300518c2262766121a4333d92df3',
'Conductor': '1.6-ed00540fae97aa1c9982f9017c6e8b68',
'EventType': '1.1-aa2ba1afd38553e3880c267404e8d370',
@@ -699,7 +699,7 @@ expected_object_fingerprints = {
'NodeCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
'NodeCRUDPayload': '1.15-9168946f843edd5859464aaa40ad70e0',
'PortCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
'PortCRUDPayload': '1.6-72323673fb8b869200d91bcd6886acae',
'PortCRUDPayload': '1.7-aaefef8ba3a94030753c1e3b9a29741b',
'NodeMaintenanceNotification': '1.0-59acc533c11d306f149846f922739c15',
'NodeConsoleNotification': '1.0-59acc533c11d306f149846f922739c15',
'PortgroupCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',

View File

@@ -0,0 +1,6 @@
---
features:
- |
A new "category" field has been added to the Port object. This field is meant
to help distinguish between different types of Ports. Relevant to trait
based port scheduling feature.