Add node (database and objects) fields for all interfaces

For the driver composition reform we need nodes to record hardware
interfaces they're using. This change adds necessary fields.

The new fields will have a default value of None, which is the expected
value for nodes using classic drivers.

The new fields are not added to notifications yet, as they're not wired in.

Change-Id: Id7697de0276e9b2730ea62f9c562ed6e1d0f8a06
Partial-Bug: #1524745
This commit is contained in:
Dmitry Tantsur 2016-11-11 16:40:46 +01:00
parent c73b5d0d3c
commit 9e938f2466
9 changed files with 100 additions and 6 deletions

View File

@ -0,0 +1,45 @@
# 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 fields for all interfaces
Revision ID: bcdd431ba0bf
Revises: 60cf717201bc
Create Date: 2016-11-11 16:44:52.823881
"""
# revision identifiers, used by Alembic.
revision = 'bcdd431ba0bf'
down_revision = '60cf717201bc'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('nodes', sa.Column('boot_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('console_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('deploy_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('inspect_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('management_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('power_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('raid_interface',
sa.String(length=255), nullable=True))
op.add_column('nodes', sa.Column('vendor_interface',
sa.String(length=255), nullable=True))

View File

@ -144,7 +144,15 @@ class Node(Base):
inspection_started_at = Column(DateTime, nullable=True)
extra = Column(db_types.JsonEncodedDict)
boot_interface = Column(String(255), nullable=True)
console_interface = Column(String(255), nullable=True)
deploy_interface = Column(String(255), nullable=True)
inspect_interface = Column(String(255), nullable=True)
management_interface = Column(String(255), nullable=True)
network_interface = Column(String(255), nullable=True)
raid_interface = Column(String(255), nullable=True)
power_interface = Column(String(255), nullable=True)
vendor_interface = Column(String(255), nullable=True)
class Port(Base):

View File

@ -171,6 +171,13 @@ class BareDriver(BaseDriver):
self.core_interfaces.append('network')
ALL_INTERFACES = set(BareDriver().all_interfaces)
"""Constant holding all known interfaces.
Includes interfaces not exposed via BaseDriver.all_interfaces.
"""
@six.add_metaclass(abc.ABCMeta)
class BaseInterface(object):
"""A base interface implementing common functions for Driver Interfaces."""

View File

@ -58,7 +58,10 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
# Version 1.16: Add network_interface field
# Version 1.17: Add resource_class field
# Version 1.18: Add default setting for network_interface
VERSION = '1.18'
# Version 1.19: Add fields: boot_interface, console_interface,
# deploy_interface, inspect_interface, management_interface,
# power_interface, raid_interface, vendor_interface
VERSION = '1.19'
dbapi = db_api.get_instance()
@ -118,8 +121,16 @@ class Node(base.IronicObject, object_base.VersionedObjectDictCompat):
'extra': object_fields.FlexibleDictField(nullable=True),
'boot_interface': object_fields.StringField(nullable=True),
'console_interface': object_fields.StringField(nullable=True),
'deploy_interface': object_fields.StringField(nullable=True),
'inspect_interface': object_fields.StringField(nullable=True),
'management_interface': object_fields.StringField(nullable=True),
'network_interface': object_fields.StringFieldThatAcceptsCallable(
nullable=False, default=_default_network_interface),
'power_interface': object_fields.StringField(nullable=True),
'raid_interface': object_fields.StringField(nullable=True),
'vendor_interface': object_fields.StringField(nullable=True),
}
def _validate_property_values(self, properties):

View File

@ -23,6 +23,7 @@ from ironic.api.controllers.v1 import chassis as chassis_controller
from ironic.api.controllers.v1 import node as node_controller
from ironic.api.controllers.v1 import port as port_controller
from ironic.api.controllers.v1 import portgroup as portgroup_controller
from ironic.drivers import base as drivers_base
from ironic.tests.unit.db import utils
ADMIN_TOKEN = '4562138218392831'
@ -99,8 +100,10 @@ def node_post_data(**kw):
# NOTE(jroll): pop out fields that were introduced in later API versions,
# unless explicitly requested. Otherwise, these will cause tests using
# older API versions to fail.
if 'network_interface' not in kw:
node.pop('network_interface')
for iface in drivers_base.ALL_INTERFACES:
name = '%s_interface' % iface
if name not in kw:
node.pop(name)
if 'resource_class' not in kw:
node.pop('resource_class')

View File

@ -53,6 +53,7 @@ import sqlalchemy.exc
from ironic.common.i18n import _LE
from ironic.db.sqlalchemy import migration
from ironic.db.sqlalchemy import models
from ironic.drivers import base as base_driver
from ironic.tests import base
LOG = logging.getLogger(__name__)
@ -537,6 +538,15 @@ class MigrationCheckersMixin(object):
(sqlalchemy.types.Boolean,
sqlalchemy.types.Integer))
def _check_bcdd431ba0bf(self, engine, data):
nodes = db_utils.get_table(engine, 'nodes')
col_names = [column.name for column in nodes.c]
for iface in base_driver.ALL_INTERFACES:
name = '%s_interface' % iface
self.assertIn(name, col_names)
self.assertIsInstance(getattr(nodes.c, name).type,
sqlalchemy.types.String)
def test_upgrade_and_version(self):
with patch_with_engine(self.engine):
self.migration_api.upgrade('head')

View File

@ -19,6 +19,7 @@ from oslo_utils import timeutils
from ironic.common import states
from ironic.db import api as db_api
from ironic.drivers import base as drivers_base
def get_test_ipmi_info():
@ -214,7 +215,7 @@ def get_test_node(**kw):
fake_internal_info = {
"private_state": "secret value"
}
return {
result = {
'id': kw.get('id', 123),
'name': kw.get('name', None),
'uuid': kw.get('uuid', '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'),
@ -248,9 +249,14 @@ def get_test_node(**kw):
'target_raid_config': kw.get('target_raid_config'),
'tags': kw.get('tags', []),
'resource_class': kw.get('resource_class'),
'network_interface': kw.get('network_interface'),
}
for iface in drivers_base.ALL_INTERFACES:
name = '%s_interface' % iface
result[name] = kw.get(name)
return result
def create_test_node(**kw):
"""Create test node entry in DB and return Node DB object.

View File

@ -404,7 +404,7 @@ class TestObject(_LocalTest, _TestObject):
# version bump. It is md5 hash of object fields and remotable methods.
# The fingerprint values should only be changed if there is a version bump.
expected_object_fingerprints = {
'Node': '1.18-37a1d39ba8a4957f505dda936ac9146b',
'Node': '1.19-e8b294016d8d5b322df813f790d092b4',
'MyObj': '1.5-4f5efe8f0fcaf182bbe1c7fe3ba858db',
'Chassis': '1.3-d656e039fd8ae9f34efc232ab3980905',
'Port': '1.6-609504503d68982a10f495659990084b',

View File

@ -0,0 +1,4 @@
---
upgrade:
- Add database migration to add new fields corresponding to all interfaces
to the node table.