Merge "Add is_smartnic to Port data model"
This commit is contained in:
commit
7794e29ed6
@ -138,7 +138,7 @@ RELEASE_MAPPING = {
|
|||||||
'Node': ['1.31', '1.30', '1.29', '1.28'],
|
'Node': ['1.31', '1.30', '1.29', '1.28'],
|
||||||
'Conductor': ['1.3'],
|
'Conductor': ['1.3'],
|
||||||
'Chassis': ['1.3'],
|
'Chassis': ['1.3'],
|
||||||
'Port': ['1.8'],
|
'Port': ['1.9'],
|
||||||
'Portgroup': ['1.4'],
|
'Portgroup': ['1.4'],
|
||||||
'Trait': ['1.0'],
|
'Trait': ['1.0'],
|
||||||
'TraitList': ['1.0'],
|
'TraitList': ['1.0'],
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
# 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 is_smartnic port attribute
|
||||||
|
|
||||||
|
Revision ID: 9cbeefa3763f
|
||||||
|
Revises: dd67b91a1981
|
||||||
|
Create Date: 2019-01-13 09:31:13.336479
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '9cbeefa3763f'
|
||||||
|
down_revision = 'dd67b91a1981'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('ports', sa.Column('is_smartnic', sa.Boolean(),
|
||||||
|
default=False))
|
@ -215,6 +215,7 @@ class Port(Base):
|
|||||||
pxe_enabled = Column(Boolean, default=True)
|
pxe_enabled = Column(Boolean, default=True)
|
||||||
internal_info = Column(db_types.JsonEncodedDict)
|
internal_info = Column(db_types.JsonEncodedDict)
|
||||||
physical_network = Column(String(64), nullable=True)
|
physical_network = Column(String(64), nullable=True)
|
||||||
|
is_smartnic = Column(Boolean, nullable=True, default=False)
|
||||||
|
|
||||||
|
|
||||||
class Portgroup(Base):
|
class Portgroup(Base):
|
||||||
|
@ -41,7 +41,7 @@ def migrate_vif_port_id(context, max_count):
|
|||||||
# NOTE(rloo): if we introduce newer port versions in the same cycle,
|
# NOTE(rloo): if we introduce newer port versions in the same cycle,
|
||||||
# we could add those versions along with 1.8. This is only so we don't
|
# we could add those versions along with 1.8. This is only so we don't
|
||||||
# duplicate work; it isn't necessary.
|
# duplicate work; it isn't necessary.
|
||||||
db_ports = Port.dbapi.get_not_versions('Port', ['1.8'])
|
db_ports = Port.dbapi.get_not_versions('Port', ['1.8', '1.9'])
|
||||||
total = len(db_ports)
|
total = len(db_ports)
|
||||||
max_count = max_count or total
|
max_count = max_count or total
|
||||||
done = 0
|
done = 0
|
||||||
@ -71,7 +71,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
# Version 1.8: Migrate/copy extra['vif_port_id'] to
|
# Version 1.8: Migrate/copy extra['vif_port_id'] to
|
||||||
# internal_info['tenant_vif_port_id'] (not an explicit db
|
# internal_info['tenant_vif_port_id'] (not an explicit db
|
||||||
# change)
|
# change)
|
||||||
VERSION = '1.8'
|
# Version 1.9: Add support for Smart NIC port
|
||||||
|
VERSION = '1.9'
|
||||||
|
|
||||||
dbapi = dbapi.get_instance()
|
dbapi = dbapi.get_instance()
|
||||||
|
|
||||||
@ -87,6 +88,8 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
'pxe_enabled': object_fields.BooleanField(),
|
'pxe_enabled': object_fields.BooleanField(),
|
||||||
'internal_info': object_fields.FlexibleDictField(nullable=True),
|
'internal_info': object_fields.FlexibleDictField(nullable=True),
|
||||||
'physical_network': object_fields.StringField(nullable=True),
|
'physical_network': object_fields.StringField(nullable=True),
|
||||||
|
'is_smartnic': object_fields.BooleanField(nullable=True,
|
||||||
|
default=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
def _convert_to_version(self, target_version,
|
def _convert_to_version(self, target_version,
|
||||||
@ -106,6 +109,9 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
.extra value to internal_info. There is nothing to do here when
|
.extra value to internal_info. There is nothing to do here when
|
||||||
downgrading to an older version.
|
downgrading to an older version.
|
||||||
|
|
||||||
|
Version 1.9: remove is_smartnic field for unsupported versions if
|
||||||
|
remove_unavailable_fields is True.
|
||||||
|
|
||||||
:param target_version: the desired version of the object
|
:param target_version: the desired version of the object
|
||||||
:param remove_unavailable_fields: True to remove fields that are
|
:param remove_unavailable_fields: True to remove fields that are
|
||||||
unavailable in the target version; set this to True when
|
unavailable in the target version; set this to True when
|
||||||
@ -140,6 +146,24 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
# DB: set unavailable fields to their default.
|
# DB: set unavailable fields to their default.
|
||||||
self.physical_network = None
|
self.physical_network = None
|
||||||
|
|
||||||
|
# Convert is_smartnic field.
|
||||||
|
is_smartnic_set = self.obj_attr_is_set('is_smartnic')
|
||||||
|
if target_version >= (1, 9):
|
||||||
|
# Target version supports is_smartnic. Set it to its default
|
||||||
|
# value if it is not set.
|
||||||
|
if not is_smartnic_set:
|
||||||
|
self.is_smartnic = False
|
||||||
|
|
||||||
|
# handle is_smartnic field in older version
|
||||||
|
elif is_smartnic_set:
|
||||||
|
# Target version does not support is_smartnic, and it is set.
|
||||||
|
if remove_unavailable_fields:
|
||||||
|
# (De)serialising: remove unavailable fields.
|
||||||
|
delattr(self, 'is_smartnic')
|
||||||
|
elif self.is_smartnic is not False:
|
||||||
|
# DB: set unavailable fields to their default.
|
||||||
|
self.is_smartnic = False
|
||||||
|
|
||||||
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
# NOTE(xek): We don't want to enable RPC on this call just yet. Remotable
|
||||||
# methods can be used in the future to replace current explicit RPC calls.
|
# methods can be used in the future to replace current explicit RPC calls.
|
||||||
# Implications of calling new remote procedures should be thought through.
|
# Implications of calling new remote procedures should be thought through.
|
||||||
@ -393,6 +417,15 @@ class Port(base.IronicObject, object_base.VersionedObjectDictCompat):
|
|||||||
"""
|
"""
|
||||||
return cls.supports_version((1, 7))
|
return cls.supports_version((1, 7))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def supports_is_smartnic(cls):
|
||||||
|
"""Return whether is_smartnic field is supported.
|
||||||
|
|
||||||
|
:returns: Whether is_smartnic field is supported
|
||||||
|
:raises: ovo_exception.IncompatibleObjectVersion
|
||||||
|
"""
|
||||||
|
return cls.supports_version((1, 9))
|
||||||
|
|
||||||
|
|
||||||
@base.IronicObjectRegistry.register
|
@base.IronicObjectRegistry.register
|
||||||
class PortCRUDNotification(notification.NotificationBase):
|
class PortCRUDNotification(notification.NotificationBase):
|
||||||
@ -410,7 +443,8 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
|
|||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
# Version 1.1: Add "portgroup_uuid" field
|
# Version 1.1: Add "portgroup_uuid" field
|
||||||
# Version 1.2: Add "physical_network" field
|
# Version 1.2: Add "physical_network" field
|
||||||
VERSION = '1.2'
|
# Version 1.3: Add "is_smartnic" field
|
||||||
|
VERSION = '1.3'
|
||||||
|
|
||||||
SCHEMA = {
|
SCHEMA = {
|
||||||
'address': ('port', 'address'),
|
'address': ('port', 'address'),
|
||||||
@ -420,7 +454,8 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
|
|||||||
'physical_network': ('port', 'physical_network'),
|
'physical_network': ('port', 'physical_network'),
|
||||||
'created_at': ('port', 'created_at'),
|
'created_at': ('port', 'created_at'),
|
||||||
'updated_at': ('port', 'updated_at'),
|
'updated_at': ('port', 'updated_at'),
|
||||||
'uuid': ('port', 'uuid')
|
'uuid': ('port', 'uuid'),
|
||||||
|
'is_smartnic': ('port', 'is_smartnic'),
|
||||||
}
|
}
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
@ -434,7 +469,9 @@ class PortCRUDPayload(notification.NotificationPayloadBase):
|
|||||||
'physical_network': object_fields.StringField(nullable=True),
|
'physical_network': object_fields.StringField(nullable=True),
|
||||||
'created_at': object_fields.DateTimeField(nullable=True),
|
'created_at': object_fields.DateTimeField(nullable=True),
|
||||||
'updated_at': object_fields.DateTimeField(nullable=True),
|
'updated_at': object_fields.DateTimeField(nullable=True),
|
||||||
'uuid': object_fields.UUIDField()
|
'uuid': object_fields.UUIDField(),
|
||||||
|
'is_smartnic': object_fields.BooleanField(nullable=True,
|
||||||
|
default=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, port, node_uuid, portgroup_uuid):
|
def __init__(self, port, node_uuid, portgroup_uuid):
|
||||||
|
@ -119,6 +119,9 @@ def port_post_data(**kw):
|
|||||||
port.pop('version')
|
port.pop('version')
|
||||||
port.pop('node_id')
|
port.pop('node_id')
|
||||||
port.pop('portgroup_id')
|
port.pop('portgroup_id')
|
||||||
|
|
||||||
|
# TODO(hamdyk): remove when port API can handle this attribute
|
||||||
|
port.pop('is_smartnic')
|
||||||
internal = port_controller.PortPatchType.internal_attrs()
|
internal = port_controller.PortPatchType.internal_attrs()
|
||||||
return remove_internal(port, internal)
|
return remove_internal(port, internal)
|
||||||
|
|
||||||
|
@ -842,6 +842,15 @@ class MigrationCheckersMixin(object):
|
|||||||
self.assertIsInstance(allocations.c.conductor_affinity.type,
|
self.assertIsInstance(allocations.c.conductor_affinity.type,
|
||||||
sqlalchemy.types.Integer)
|
sqlalchemy.types.Integer)
|
||||||
|
|
||||||
|
def _check_9cbeefa3763f(self, engine, data):
|
||||||
|
ports = db_utils.get_table(engine, 'ports')
|
||||||
|
col_names = [column.name for column in ports.c]
|
||||||
|
self.assertIn('is_smartnic', col_names)
|
||||||
|
# in some backends bool type is integer
|
||||||
|
self.assertIsInstance(ports.c.is_smartnic.type,
|
||||||
|
(sqlalchemy.types.Boolean,
|
||||||
|
sqlalchemy.types.Integer))
|
||||||
|
|
||||||
def test_upgrade_and_version(self):
|
def test_upgrade_and_version(self):
|
||||||
with patch_with_engine(self.engine):
|
with patch_with_engine(self.engine):
|
||||||
self.migration_api.upgrade('head')
|
self.migration_api.upgrade('head')
|
||||||
|
@ -269,6 +269,7 @@ def get_test_port(**kw):
|
|||||||
'pxe_enabled': kw.get('pxe_enabled', True),
|
'pxe_enabled': kw.get('pxe_enabled', True),
|
||||||
'internal_info': kw.get('internal_info', {"bar": "buzz"}),
|
'internal_info': kw.get('internal_info', {"bar": "buzz"}),
|
||||||
'physical_network': kw.get('physical_network'),
|
'physical_network': kw.get('physical_network'),
|
||||||
|
'is_smartnic': kw.get('is_smartnic', False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -680,7 +680,7 @@ expected_object_fingerprints = {
|
|||||||
'Node': '1.31-1b77c11e94f971a71c76f5f44fb5b3f4',
|
'Node': '1.31-1b77c11e94f971a71c76f5f44fb5b3f4',
|
||||||
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
'MyObj': '1.5-9459d30d6954bffc7a9afd347a807ca6',
|
||||||
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
|
||||||
'Port': '1.8-898a47921f4a1f53fcdddd4eeb179e0b',
|
'Port': '1.9-0cb9202a4ec442e8c0d87a324155eaaf',
|
||||||
'Portgroup': '1.4-71923a81a86743b313b190f5c675e258',
|
'Portgroup': '1.4-71923a81a86743b313b190f5c675e258',
|
||||||
'Conductor': '1.3-d3f53e853b4d58cae5bfbd9a8341af4a',
|
'Conductor': '1.3-d3f53e853b4d58cae5bfbd9a8341af4a',
|
||||||
'EventType': '1.1-aa2ba1afd38553e3880c267404e8d370',
|
'EventType': '1.1-aa2ba1afd38553e3880c267404e8d370',
|
||||||
@ -701,7 +701,7 @@ expected_object_fingerprints = {
|
|||||||
'NodeCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
'NodeCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
||||||
'NodeCRUDPayload': '1.10-49590dee863c5ed1193f5deae0a0a2f2',
|
'NodeCRUDPayload': '1.10-49590dee863c5ed1193f5deae0a0a2f2',
|
||||||
'PortCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
'PortCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
||||||
'PortCRUDPayload': '1.2-233d259df442eb15cc584fae1fe81504',
|
'PortCRUDPayload': '1.3-21235916ed54a91b2a122f59571194e7',
|
||||||
'NodeMaintenanceNotification': '1.0-59acc533c11d306f149846f922739c15',
|
'NodeMaintenanceNotification': '1.0-59acc533c11d306f149846f922739c15',
|
||||||
'NodeConsoleNotification': '1.0-59acc533c11d306f149846f922739c15',
|
'NodeConsoleNotification': '1.0-59acc533c11d306f149846f922739c15',
|
||||||
'PortgroupCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
'PortgroupCRUDNotification': '1.0-59acc533c11d306f149846f922739c15',
|
||||||
|
@ -165,6 +165,20 @@ class TestPortObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
|||||||
def test_payload_schemas(self):
|
def test_payload_schemas(self):
|
||||||
self._check_payload_schemas(objects.port, objects.Port.fields)
|
self._check_payload_schemas(objects.port, objects.Port.fields)
|
||||||
|
|
||||||
|
@mock.patch.object(obj_base.IronicObject, 'supports_version',
|
||||||
|
spec_set=types.FunctionType)
|
||||||
|
def test_supports_is_smartnic_supported(self, mock_sv):
|
||||||
|
mock_sv.return_value = True
|
||||||
|
self.assertTrue(objects.Port.supports_is_smartnic())
|
||||||
|
mock_sv.assert_called_once_with((1, 9))
|
||||||
|
|
||||||
|
@mock.patch.object(obj_base.IronicObject, 'supports_version',
|
||||||
|
spec_set=types.FunctionType)
|
||||||
|
def test_supports_is_smartnic_unsupported(self, mock_sv):
|
||||||
|
mock_sv.return_value = False
|
||||||
|
self.assertFalse(objects.Port.supports_is_smartnic())
|
||||||
|
mock_sv.assert_called_once_with((1, 9))
|
||||||
|
|
||||||
|
|
||||||
class TestConvertToVersion(db_base.DbTestCase):
|
class TestConvertToVersion(db_base.DbTestCase):
|
||||||
|
|
||||||
@ -255,6 +269,72 @@ class TestConvertToVersion(db_base.DbTestCase):
|
|||||||
# no change
|
# no change
|
||||||
self.assertEqual(vif2, port.internal_info['tenant_vif_port_id'])
|
self.assertEqual(vif2, port.internal_info['tenant_vif_port_id'])
|
||||||
|
|
||||||
|
def test_is_smartnic_unsupported(self):
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port._convert_to_version("1.8")
|
||||||
|
self.assertNotIn('is_smartnic', port)
|
||||||
|
|
||||||
|
def test_is_smartnic_supported(self):
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port._convert_to_version("1.9")
|
||||||
|
self.assertIn('is_smartnic', port)
|
||||||
|
|
||||||
|
def test_is_smartnic_supported_missing(self):
|
||||||
|
# is_smartnic is not set, should be set to default.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
delattr(port, 'is_smartnic')
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.9")
|
||||||
|
self.assertFalse(port.is_smartnic)
|
||||||
|
self.assertIn('is_smartnic', port.obj_get_changes())
|
||||||
|
self.assertFalse(port.obj_get_changes()['is_smartnic'])
|
||||||
|
|
||||||
|
def test_is_smartnic_supported_set(self):
|
||||||
|
# is_smartnic is set, no change required.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port.is_smartnic = True
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.9")
|
||||||
|
self.assertTrue(port.is_smartnic)
|
||||||
|
self.assertNotIn('is_smartnic', port.obj_get_changes())
|
||||||
|
|
||||||
|
def test_is_smartnic_unsupported_missing(self):
|
||||||
|
# is_smartnic is not set, no change required.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
delattr(port, 'is_smartnic')
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.8")
|
||||||
|
self.assertNotIn('is_smartnic', port)
|
||||||
|
self.assertNotIn('is_smartnic', port.obj_get_changes())
|
||||||
|
|
||||||
|
def test_is_smartnic_unsupported_set_remove(self):
|
||||||
|
# is_smartnic is set, should be removed.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port.is_smartnic = False
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.8")
|
||||||
|
self.assertNotIn('is_smartnic', port)
|
||||||
|
self.assertNotIn('is_smartnic', port.obj_get_changes())
|
||||||
|
|
||||||
|
def test_is_smartnic_unsupported_set_no_remove_non_default(self):
|
||||||
|
# is_smartnic is set, should be set to default.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port.is_smartnic = True
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.8", False)
|
||||||
|
self.assertFalse(port.is_smartnic)
|
||||||
|
self.assertIn('is_smartnic', port.obj_get_changes())
|
||||||
|
self.assertFalse(port.obj_get_changes()['is_smartnic'])
|
||||||
|
|
||||||
|
def test_is_smartnic_unsupported_set_no_remove_default(self):
|
||||||
|
# is_smartnic is set, no change required.
|
||||||
|
port = objects.Port(self.context, **self.fake_port)
|
||||||
|
port.is_smartnic = False
|
||||||
|
port.obj_reset_changes()
|
||||||
|
port._convert_to_version("1.8", False)
|
||||||
|
self.assertFalse(port.is_smartnic)
|
||||||
|
self.assertNotIn('is_smartnic', port.obj_get_changes())
|
||||||
|
|
||||||
|
|
||||||
class TestMigrateVifPortId(db_base.DbTestCase):
|
class TestMigrateVifPortId(db_base.DbTestCase):
|
||||||
|
|
||||||
@ -278,9 +358,10 @@ class TestMigrateVifPortId(db_base.DbTestCase):
|
|||||||
total, done = objects.port.migrate_vif_port_id(self.context, 0)
|
total, done = objects.port.migrate_vif_port_id(self.context, 0)
|
||||||
self.assertEqual(3, total)
|
self.assertEqual(3, total)
|
||||||
self.assertEqual(3, done)
|
self.assertEqual(3, done)
|
||||||
mock_get_not_versions.assert_called_once_with('Port', ['1.8'])
|
mock_get_not_versions.assert_called_once_with('Port', ['1.8',
|
||||||
|
'1.9'])
|
||||||
calls = 3 * [
|
calls = 3 * [
|
||||||
mock.call(mock.ANY, '1.8', remove_unavailable_fields=False),
|
mock.call(mock.ANY, '1.9', remove_unavailable_fields=False),
|
||||||
]
|
]
|
||||||
self.assertEqual(calls, mock_convert.call_args_list)
|
self.assertEqual(calls, mock_convert.call_args_list)
|
||||||
|
|
||||||
@ -292,8 +373,9 @@ class TestMigrateVifPortId(db_base.DbTestCase):
|
|||||||
total, done = objects.port.migrate_vif_port_id(self.context, 1)
|
total, done = objects.port.migrate_vif_port_id(self.context, 1)
|
||||||
self.assertEqual(3, total)
|
self.assertEqual(3, total)
|
||||||
self.assertEqual(1, done)
|
self.assertEqual(1, done)
|
||||||
mock_get_not_versions.assert_called_once_with('Port', ['1.8'])
|
mock_get_not_versions.assert_called_once_with('Port', ['1.8',
|
||||||
|
'1.9'])
|
||||||
calls = [
|
calls = [
|
||||||
mock.call(mock.ANY, '1.8', remove_unavailable_fields=False),
|
mock.call(mock.ANY, '1.9', remove_unavailable_fields=False),
|
||||||
]
|
]
|
||||||
self.assertEqual(calls, mock_convert.call_args_list)
|
self.assertEqual(calls, mock_convert.call_args_list)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user