Merge "Implements node inventory: database"
This commit is contained in:
commit
e91b59c47e
@ -828,6 +828,10 @@ class NodeHistoryNotFound(NotFound):
|
|||||||
_msg_fmt = _("Node history record %(history)s could not be found.")
|
_msg_fmt = _("Node history record %(history)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
|
class NodeInventoryNotFound(NotFound):
|
||||||
|
_msg_fmt = _("Node inventory record %(inventory)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
class IncorrectConfiguration(IronicException):
|
class IncorrectConfiguration(IronicException):
|
||||||
_msg_fmt = _("Supplied configuration is incorrect and must be fixed. "
|
_msg_fmt = _("Supplied configuration is incorrect and must be fixed. "
|
||||||
"Error: %(error)s")
|
"Error: %(error)s")
|
||||||
|
@ -518,6 +518,7 @@ RELEASE_MAPPING = {
|
|||||||
'BIOSSetting': ['1.1'],
|
'BIOSSetting': ['1.1'],
|
||||||
'Node': ['1.36'],
|
'Node': ['1.36'],
|
||||||
'NodeHistory': ['1.0'],
|
'NodeHistory': ['1.0'],
|
||||||
|
'NodeInventory': ['1.0'],
|
||||||
'Conductor': ['1.3'],
|
'Conductor': ['1.3'],
|
||||||
'Chassis': ['1.3'],
|
'Chassis': ['1.3'],
|
||||||
'Deployment': ['1.0'],
|
'Deployment': ['1.0'],
|
||||||
|
@ -1425,3 +1425,33 @@ class Connection(object, metaclass=abc.ABCMeta):
|
|||||||
count operation. This can be a single provision
|
count operation. This can be a single provision
|
||||||
state value or a list of values.
|
state value or a list of values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_node_inventory(self, values):
|
||||||
|
"""Create a new inventory record.
|
||||||
|
|
||||||
|
:param values: Dict of values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def destroy_node_inventory_by_node_id(self, inventory_node_id):
|
||||||
|
"""Destroy a inventory record.
|
||||||
|
|
||||||
|
:param inventory_uuid: The uuid of a inventory record
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_node_inventory_by_id(self, inventory_id):
|
||||||
|
"""Return a node inventory representation.
|
||||||
|
|
||||||
|
:param inventory_id: The id of a inventory record.
|
||||||
|
:returns: An inventory of a node.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_node_inventory_by_node_id(self, node_id):
|
||||||
|
"""Get the node inventory for a given node.
|
||||||
|
|
||||||
|
:param node_id: The integer node ID.
|
||||||
|
:returns: An inventory of a node.
|
||||||
|
"""
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
# 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 node inventory table
|
||||||
|
|
||||||
|
Revision ID: 0ac0f39bc5aa
|
||||||
|
Revises: 9ef41f07cb58
|
||||||
|
Create Date: 2022-10-25 17:15:38.181544
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
from oslo_db.sqlalchemy import types as db_types
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '0ac0f39bc5aa'
|
||||||
|
down_revision = '9ef41f07cb58'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table('node_inventory',
|
||||||
|
sa.Column('version', sa.String(length=15), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('inventory_data', db_types.JsonEncodedDict(
|
||||||
|
mysql_as_long=True).impl, nullable=True),
|
||||||
|
sa.Column('plugin_data', db_types.JsonEncodedDict(
|
||||||
|
mysql_as_long=True).impl, nullable=True),
|
||||||
|
sa.Column('node_id', sa.Integer(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.ForeignKeyConstraint(['node_id'], ['nodes.id'], ),
|
||||||
|
sa.Index('inventory_node_id_idx', 'node_id'),
|
||||||
|
mysql_engine='InnoDB',
|
||||||
|
mysql_charset='UTF8MB3')
|
@ -859,6 +859,11 @@ class Connection(api.Connection):
|
|||||||
models.NodeHistory).filter_by(node_id=node_id)
|
models.NodeHistory).filter_by(node_id=node_id)
|
||||||
history_query.delete()
|
history_query.delete()
|
||||||
|
|
||||||
|
# delete all inventory for this node
|
||||||
|
inventory_query = session.query(
|
||||||
|
models.NodeInventory).filter_by(node_id=node_id)
|
||||||
|
inventory_query.delete()
|
||||||
|
|
||||||
query.delete()
|
query.delete()
|
||||||
|
|
||||||
def update_node(self, node_id, values):
|
def update_node(self, node_id, values):
|
||||||
@ -2548,3 +2553,40 @@ class Connection(api.Connection):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@oslo_db_api.retry_on_deadlock
|
||||||
|
def create_node_inventory(self, values):
|
||||||
|
inventory = models.NodeInventory()
|
||||||
|
inventory.update(values)
|
||||||
|
with _session_for_write() as session:
|
||||||
|
try:
|
||||||
|
session.add(inventory)
|
||||||
|
session.flush()
|
||||||
|
except db_exc.DBDuplicateEntry:
|
||||||
|
raise exception.NodeInventoryAlreadyExists(
|
||||||
|
id=values['id'])
|
||||||
|
return inventory
|
||||||
|
|
||||||
|
@oslo_db_api.retry_on_deadlock
|
||||||
|
def destroy_node_inventory_by_node_id(self, node_id):
|
||||||
|
with _session_for_write() as session:
|
||||||
|
query = session.query(models.NodeInventory).filter_by(
|
||||||
|
node_id=node_id)
|
||||||
|
count = query.delete()
|
||||||
|
if count == 0:
|
||||||
|
raise exception.NodeInventoryNotFound(
|
||||||
|
node_id=node_id)
|
||||||
|
|
||||||
|
def get_node_inventory_by_id(self, inventory_id):
|
||||||
|
query = model_query(models.NodeInventory).filter_by(id=inventory_id)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.NodeInventoryNotFound(inventory=inventory_id)
|
||||||
|
|
||||||
|
def get_node_inventory_by_node_id(self, node_id):
|
||||||
|
query = model_query(models.NodeInventory).filter_by(node_id=node_id)
|
||||||
|
try:
|
||||||
|
return query.one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.NodeInventoryNotFound(node_id=node_id)
|
||||||
|
@ -465,6 +465,18 @@ class NodeHistory(Base):
|
|||||||
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
|
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class NodeInventory(Base):
|
||||||
|
"""Represents an inventory of a baremetal node."""
|
||||||
|
__tablename__ = 'node_inventory'
|
||||||
|
__table_args__ = (
|
||||||
|
Index('inventory_node_id_idx', 'node_id'),
|
||||||
|
table_args())
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
inventory_data = Column(db_types.JsonEncodedDict(mysql_as_long=True))
|
||||||
|
plugin_data = Column(db_types.JsonEncodedDict(mysql_as_long=True))
|
||||||
|
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
def get_class(model_name):
|
def get_class(model_name):
|
||||||
"""Returns the model class with the specified name.
|
"""Returns the model class with the specified name.
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ def register_all():
|
|||||||
__import__('ironic.objects.deployment')
|
__import__('ironic.objects.deployment')
|
||||||
__import__('ironic.objects.node')
|
__import__('ironic.objects.node')
|
||||||
__import__('ironic.objects.node_history')
|
__import__('ironic.objects.node_history')
|
||||||
|
__import__('ironic.objects.node_inventory')
|
||||||
__import__('ironic.objects.port')
|
__import__('ironic.objects.port')
|
||||||
__import__('ironic.objects.portgroup')
|
__import__('ironic.objects.portgroup')
|
||||||
__import__('ironic.objects.trait')
|
__import__('ironic.objects.trait')
|
||||||
|
104
ironic/objects/node_inventory.py
Normal file
104
ironic/objects/node_inventory.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_versionedobjects import base as object_base
|
||||||
|
|
||||||
|
from ironic.db import api as dbapi
|
||||||
|
from ironic.objects import base
|
||||||
|
from ironic.objects import fields as object_fields
|
||||||
|
|
||||||
|
|
||||||
|
@base.IronicObjectRegistry.register
|
||||||
|
class NodeInventory(base.IronicObject, object_base.VersionedObjectDictCompat):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
|
||||||
|
dbapi = dbapi.get_instance()
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
'id': object_fields.IntegerField(),
|
||||||
|
'node_id': object_fields.IntegerField(nullable=True),
|
||||||
|
'inventory_data': object_fields.FlexibleDictField(nullable=True),
|
||||||
|
'plugin_data': object_fields.FlexibleDictField(nullable=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_node_object(cls, context, node):
|
||||||
|
"""Convert a node into a virtual `NodeInventory` object."""
|
||||||
|
result = cls(context)
|
||||||
|
result._update_from_node_object(node)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _update_from_node_object(self, node):
|
||||||
|
"""Update the NodeInventory object from the node."""
|
||||||
|
for src, dest in self.node_mapping.items():
|
||||||
|
setattr(self, dest, getattr(node, src, None))
|
||||||
|
for src, dest in self.instance_info_mapping.items():
|
||||||
|
setattr(self, dest, node.instance_info.get(src))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_id(cls, context, inventory_id):
|
||||||
|
"""Get a NodeInventory object by its integer ID.
|
||||||
|
|
||||||
|
:param cls: the :class:`NodeInventory`
|
||||||
|
:param context: Security context
|
||||||
|
:param history_id: The ID of a inventory.
|
||||||
|
:returns: A :class:`NodeInventory` object.
|
||||||
|
:raises: NodeInventoryNotFound
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_inventory = cls.dbapi.get_node_inventory_by_id(inventory_id)
|
||||||
|
inventory = cls._from_db_object(context, cls(), db_inventory)
|
||||||
|
return inventory
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_node_id(cls, context, node_id):
|
||||||
|
"""Get a NodeInventory object by its node ID.
|
||||||
|
|
||||||
|
:param cls: the :class:`NodeInventory`
|
||||||
|
:param context: Security context
|
||||||
|
:param uuid: The UUID of a NodeInventory.
|
||||||
|
:returns: A :class:`NodeInventory` object.
|
||||||
|
:raises: NodeInventoryNotFound
|
||||||
|
|
||||||
|
"""
|
||||||
|
db_inventory = cls.dbapi.get_node_inventory_by_node_id(node_id)
|
||||||
|
inventory = cls._from_db_object(context, cls(), db_inventory)
|
||||||
|
return inventory
|
||||||
|
|
||||||
|
def create(self, context=None):
|
||||||
|
"""Create a NodeInventory record in the DB.
|
||||||
|
|
||||||
|
:param context: Security context. NOTE: This should only
|
||||||
|
be used internally by the indirection_api.
|
||||||
|
Unfortunately, RPC requires context as the first
|
||||||
|
argument, even though we don't use it.
|
||||||
|
A context should be set when instantiating the
|
||||||
|
object, e.g.: NodeHistory(context)
|
||||||
|
"""
|
||||||
|
values = self.do_version_changes_for_db()
|
||||||
|
db_inventory = self.dbapi.create_node_inventory(values)
|
||||||
|
self._from_db_object(self._context, self, db_inventory)
|
||||||
|
|
||||||
|
def destroy(self, context=None):
|
||||||
|
"""Delete the NodeHistory from the DB.
|
||||||
|
|
||||||
|
:param context: Security context. NOTE: This should only
|
||||||
|
be used internally by the indirection_api.
|
||||||
|
Unfortunately, RPC requires context as the first
|
||||||
|
argument, even though we don't use it.
|
||||||
|
A context should be set when instantiating the
|
||||||
|
object, e.g.: NodeInventory(context)
|
||||||
|
:raises: NodeInventoryNotFound
|
||||||
|
"""
|
||||||
|
self.dbapi.destroy_node_inventory_by_node_id(self.node_id)
|
||||||
|
self.obj_reset_changes()
|
@ -1240,6 +1240,23 @@ class MigrationCheckersMixin(object):
|
|||||||
self.assertIsInstance(node_history.c.user.type,
|
self.assertIsInstance(node_history.c.user.type,
|
||||||
sqlalchemy.types.String)
|
sqlalchemy.types.String)
|
||||||
|
|
||||||
|
def _check_0ac0f39bc5aa(self, engine, data):
|
||||||
|
node_inventory = db_utils.get_table(engine, 'node_inventory')
|
||||||
|
col_names = [column.name for column in node_inventory.c]
|
||||||
|
|
||||||
|
expected_names = ['version', 'created_at', 'updated_at', 'id',
|
||||||
|
'node_id', 'inventory_data', 'plugin_data']
|
||||||
|
self.assertEqual(sorted(expected_names), sorted(col_names))
|
||||||
|
|
||||||
|
self.assertIsInstance(node_inventory.c.created_at.type,
|
||||||
|
sqlalchemy.types.DateTime)
|
||||||
|
self.assertIsInstance(node_inventory.c.updated_at.type,
|
||||||
|
sqlalchemy.types.DateTime)
|
||||||
|
self.assertIsInstance(node_inventory.c.id.type,
|
||||||
|
sqlalchemy.types.Integer)
|
||||||
|
self.assertIsInstance(node_inventory.c.node_id.type,
|
||||||
|
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')
|
||||||
@ -1326,6 +1343,13 @@ class TestMigrationsMySQL(MigrationCheckersMixin,
|
|||||||
"'%s'" % data['uuid'])).one()
|
"'%s'" % data['uuid'])).one()
|
||||||
self.assertEqual(test_text, i_info[0])
|
self.assertEqual(test_text, i_info[0])
|
||||||
|
|
||||||
|
def _check_0ac0f39bc5aa(self, engine, data):
|
||||||
|
node_inventory = db_utils.get_table(engine, 'node_inventory')
|
||||||
|
self.assertIsInstance(node_inventory.c.inventory_data.type,
|
||||||
|
sqlalchemy.dialects.mysql.LONGTEXT)
|
||||||
|
self.assertIsInstance(node_inventory.c.plugin_data.type,
|
||||||
|
sqlalchemy.dialects.mysql.LONGTEXT)
|
||||||
|
|
||||||
|
|
||||||
class TestMigrationsPostgreSQL(MigrationCheckersMixin,
|
class TestMigrationsPostgreSQL(MigrationCheckersMixin,
|
||||||
WalkVersionsMixin,
|
WalkVersionsMixin,
|
||||||
@ -1333,6 +1357,13 @@ class TestMigrationsPostgreSQL(MigrationCheckersMixin,
|
|||||||
test_base.BaseTestCase):
|
test_base.BaseTestCase):
|
||||||
FIXTURE = test_fixtures.PostgresqlOpportunisticFixture
|
FIXTURE = test_fixtures.PostgresqlOpportunisticFixture
|
||||||
|
|
||||||
|
def _check_0ac0f39bc5aa(self, engine, data):
|
||||||
|
node_inventory = db_utils.get_table(engine, 'node_inventory')
|
||||||
|
self.assertIsInstance(node_inventory.c.inventory_data.type,
|
||||||
|
sqlalchemy.types.Text)
|
||||||
|
self.assertIsInstance(node_inventory.c.plugin_data.type,
|
||||||
|
sqlalchemy.types.Text)
|
||||||
|
|
||||||
|
|
||||||
class ModelsMigrationSyncMixin(object):
|
class ModelsMigrationSyncMixin(object):
|
||||||
|
|
||||||
|
47
ironic/tests/unit/db/test_node_inventory.py
Normal file
47
ironic/tests/unit/db/test_node_inventory.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from ironic.common import exception
|
||||||
|
from ironic.tests.unit.db import base
|
||||||
|
from ironic.tests.unit.db import utils as db_utils
|
||||||
|
|
||||||
|
|
||||||
|
class DBNodeInventoryTestCase(base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(DBNodeInventoryTestCase, self).setUp()
|
||||||
|
self.node = db_utils.create_test_node()
|
||||||
|
self.inventory = db_utils.create_test_inventory(
|
||||||
|
id=0, node_id=self.node.id,
|
||||||
|
inventory_data={"inventory": "test_inventory"},
|
||||||
|
plugin_data={"plugin_data": "test_plugin_data"})
|
||||||
|
|
||||||
|
def test_destroy_node_inventory_by_node_id(self):
|
||||||
|
self.dbapi.destroy_node_inventory_by_node_id(self.inventory.node_id)
|
||||||
|
self.assertRaises(exception.NodeInventoryNotFound,
|
||||||
|
self.dbapi.get_node_inventory_by_id,
|
||||||
|
self.inventory.id)
|
||||||
|
|
||||||
|
def test_get_inventory_by_id(self):
|
||||||
|
res = self.dbapi.get_node_inventory_by_id(self.inventory.id)
|
||||||
|
self.assertEqual(self.inventory.inventory_data, res.inventory_data)
|
||||||
|
|
||||||
|
def test_get_inventory_by_id_not_found(self):
|
||||||
|
self.assertRaises(exception.NodeInventoryNotFound,
|
||||||
|
self.dbapi.get_node_inventory_by_id, -1)
|
||||||
|
|
||||||
|
def test_get_inventory_by_node_id(self):
|
||||||
|
res = self.dbapi.get_node_inventory_by_node_id(self.inventory.node_id)
|
||||||
|
self.assertEqual(self.inventory.id, res.id)
|
||||||
|
|
||||||
|
def test_get_history_by_node_id_empty(self):
|
||||||
|
self.assertEqual([], self.dbapi.get_node_history_by_node_id(10))
|
@ -763,6 +763,15 @@ class DbNodeTestCase(base.DbTestCase):
|
|||||||
self.assertRaises(exception.NodeHistoryNotFound,
|
self.assertRaises(exception.NodeHistoryNotFound,
|
||||||
self.dbapi.get_node_history_by_id, history.id)
|
self.dbapi.get_node_history_by_id, history.id)
|
||||||
|
|
||||||
|
def test_inventory_get_destroyed_after_destroying_a_node_by_uuid(self):
|
||||||
|
node = utils.create_test_node()
|
||||||
|
|
||||||
|
inventory = utils.create_test_inventory(node_id=node.id)
|
||||||
|
|
||||||
|
self.dbapi.destroy_node(node.uuid)
|
||||||
|
self.assertRaises(exception.NodeInventoryNotFound,
|
||||||
|
self.dbapi.get_node_inventory_by_id, inventory.id)
|
||||||
|
|
||||||
def test_update_node(self):
|
def test_update_node(self):
|
||||||
node = utils.create_test_node()
|
node = utils.create_test_node()
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from ironic.objects import conductor
|
|||||||
from ironic.objects import deploy_template
|
from ironic.objects import deploy_template
|
||||||
from ironic.objects import node
|
from ironic.objects import node
|
||||||
from ironic.objects import node_history
|
from ironic.objects import node_history
|
||||||
|
from ironic.objects import node_inventory
|
||||||
from ironic.objects import port
|
from ironic.objects import port
|
||||||
from ironic.objects import portgroup
|
from ironic.objects import portgroup
|
||||||
from ironic.objects import trait
|
from ironic.objects import trait
|
||||||
@ -721,3 +722,29 @@ def create_test_history(**kw):
|
|||||||
del history['id']
|
del history['id']
|
||||||
dbapi = db_api.get_instance()
|
dbapi = db_api.get_instance()
|
||||||
return dbapi.create_node_history(history)
|
return dbapi.create_node_history(history)
|
||||||
|
|
||||||
|
|
||||||
|
def get_test_inventory(**kw):
|
||||||
|
return {
|
||||||
|
'id': kw.get('id', 345),
|
||||||
|
'version': kw.get('version', node_inventory.NodeInventory.VERSION),
|
||||||
|
'node_id': kw.get('node_id', 123),
|
||||||
|
'inventory_data': kw.get('inventory', {"inventory": "test"}),
|
||||||
|
'plugin_data': kw.get('plugin_data', {"pdata": {"plugin": "data"}}),
|
||||||
|
'created_at': kw.get('created_at'),
|
||||||
|
'updated_at': kw.get('updated_at'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_inventory(**kw):
|
||||||
|
"""Create test inventory entry in DB and return NodeInventory DB object.
|
||||||
|
|
||||||
|
:param kw: kwargs with overriding values for port's attributes.
|
||||||
|
:returns: Test NodeInventory DB object.
|
||||||
|
"""
|
||||||
|
inventory = get_test_inventory(**kw)
|
||||||
|
# Let DB generate ID if it isn't specified explicitly
|
||||||
|
if 'id' not in kw:
|
||||||
|
del inventory['id']
|
||||||
|
dbapi = db_api.get_instance()
|
||||||
|
return dbapi.create_node_inventory(inventory)
|
||||||
|
61
ironic/tests/unit/objects/test_node_inventory.py
Normal file
61
ironic/tests/unit/objects/test_node_inventory.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from ironic import objects
|
||||||
|
from ironic.tests.unit.db import base as db_base
|
||||||
|
from ironic.tests.unit.db import utils as db_utils
|
||||||
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
|
class TestNodeInventoryObject(db_base.DbTestCase, obj_utils.SchemasTestMixIn):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNodeInventoryObject, self).setUp()
|
||||||
|
self.fake_inventory = db_utils.get_test_inventory()
|
||||||
|
|
||||||
|
def test_get_by_id(self):
|
||||||
|
with mock.patch.object(self.dbapi, 'get_node_inventory_by_id',
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
id_ = self.fake_inventory['id']
|
||||||
|
mock_get.return_value = self.fake_inventory
|
||||||
|
|
||||||
|
inventory = objects.NodeInventory.get_by_id(self.context, id_)
|
||||||
|
|
||||||
|
mock_get.assert_called_once_with(id_)
|
||||||
|
self.assertIsInstance(inventory, objects.NodeInventory)
|
||||||
|
self.assertEqual(self.context, inventory._context)
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
with mock.patch.object(self.dbapi, 'create_node_inventory',
|
||||||
|
autospec=True) as mock_db_create:
|
||||||
|
mock_db_create.return_value = self.fake_inventory
|
||||||
|
new_inventory = objects.NodeInventory(
|
||||||
|
self.context, **self.fake_inventory)
|
||||||
|
new_inventory.create()
|
||||||
|
|
||||||
|
mock_db_create.assert_called_once_with(self.fake_inventory)
|
||||||
|
|
||||||
|
def test_destroy(self):
|
||||||
|
node_id = self.fake_inventory['node_id']
|
||||||
|
with mock.patch.object(self.dbapi, 'get_node_inventory_by_node_id',
|
||||||
|
autospec=True) as mock_get:
|
||||||
|
mock_get.return_value = self.fake_inventory
|
||||||
|
with mock.patch.object(self.dbapi,
|
||||||
|
'destroy_node_inventory_by_node_id',
|
||||||
|
autospec=True) as mock_db_destroy:
|
||||||
|
inventory = objects.NodeInventory.get_by_node_id(self.context,
|
||||||
|
node_id)
|
||||||
|
inventory.destroy()
|
||||||
|
|
||||||
|
mock_db_destroy.assert_called_once_with(node_id)
|
@ -721,6 +721,7 @@ expected_object_fingerprints = {
|
|||||||
'DeployTemplateCRUDPayload': '1.0-200857e7e715f58a5b6d6b700ab73a3b',
|
'DeployTemplateCRUDPayload': '1.0-200857e7e715f58a5b6d6b700ab73a3b',
|
||||||
'Deployment': '1.0-ff10ae028c5968f1596131d85d7f5f9d',
|
'Deployment': '1.0-ff10ae028c5968f1596131d85d7f5f9d',
|
||||||
'NodeHistory': '1.0-9b576c6481071e7f7eac97317fa29418',
|
'NodeHistory': '1.0-9b576c6481071e7f7eac97317fa29418',
|
||||||
|
'NodeInventory': '1.0-97692fec24e20ab02022b9db54e8f539',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user