diff --git a/drydock_provisioner/drivers/node/maasdriver/actions/node.py b/drydock_provisioner/drivers/node/maasdriver/actions/node.py
index cab39191..57de8bce 100644
--- a/drydock_provisioner/drivers/node/maasdriver/actions/node.py
+++ b/drydock_provisioner/drivers/node/maasdriver/actions/node.py
@@ -1001,9 +1001,12 @@ class ApplyNodeNetworking(BaseMaasAction):
ctx=n.name,
ctx_type='node')
hw_iface_list = i.get_hw_slaves()
+ hw_iface_logicalname_list = []
+ for hw_iface in hw_iface_list:
+ hw_iface_logicalname_list.append(n.get_logicalname(hw_iface))
iface = machine.interfaces.create_bond(
device_name=i.device_name,
- parent_names=hw_iface_list,
+ parent_names=hw_iface_logicalname_list,
mtu=nl.mtu,
fabric=fabric.resource_id,
mode=nl.bonding_mode,
@@ -1012,7 +1015,6 @@ class ApplyNodeNetworking(BaseMaasAction):
updelay=nl.bonding_up_delay,
lacp_rate=nl.bonding_peer_rate,
hash_policy=nl.bonding_xmit_hash)
- self.task.success(focus=n.name)
else:
msg = "Network link %s indicates bonding, " \
"interface %s has less than 2 slaves." % \
@@ -1059,8 +1061,7 @@ class ApplyNodeNetworking(BaseMaasAction):
hw_iface = i.get_hw_slaves()[0]
# TODO(sh8121att): HardwareProfile device alias integration
iface = machine.get_network_interface(
- hw_iface)
- self.task.success(focus=n.name)
+ n.get_logicalname(hw_iface))
if iface is None:
msg = "Interface %s not found on node %s, skipping configuration" % (
@@ -1448,7 +1449,7 @@ class ApplyNodeStorage(BaseMaasAction):
storage_layout = dict()
if isinstance(root_block, hostprofile.HostPartition):
storage_layout['layout_type'] = 'flat'
- storage_layout['root_device'] = root_dev.name
+ storage_layout['root_device'] = n.get_logicalname(root_dev.name)
storage_layout['root_size'] = root_block.size
elif isinstance(root_block, hostprofile.HostVolume):
storage_layout['layout_type'] = 'lvm'
@@ -1460,8 +1461,7 @@ class ApplyNodeStorage(BaseMaasAction):
msg=msg, error=True, ctx=n.name, ctx_type='node')
self.task.failure(focus=n.get_id())
continue
- storage_layout['root_device'] = root_dev.physical_devices[
- 0]
+ storage_layout['root_device'] = n.get_logicalname(root_dev.physical_devices[0])
storage_layout['root_lv_size'] = root_block.size
storage_layout['root_lv_name'] = root_block.name
storage_layout['root_vg_name'] = root_dev.name
@@ -1479,24 +1479,24 @@ class ApplyNodeStorage(BaseMaasAction):
for d in n.storage_devices:
maas_dev = machine.block_devices.singleton({
- 'name': d.name
+ 'name': n.get_logicalname(d.name)
})
if maas_dev is None:
- self.logger.warning("Dev %s not found on node %s" %
- (d.name, n.name))
+ self.logger.warning("Dev %s (%s) not found on node %s" %
+ (d.name, n.get_logicalname(d.name), n.name))
continue
if d.volume_group is not None:
- self.logger.debug("Adding dev %s to volume group %s" %
- (d.name, d.volume_group))
+ self.logger.debug("Adding dev %s (%s) to volume group %s" %
+ (d.name, n.get_logicalname(d.name), d.volume_group))
if d.volume_group not in vg_devs:
vg_devs[d.volume_group] = {'b': [], 'p': []}
vg_devs[d.volume_group]['b'].append(
maas_dev.resource_id)
continue
- self.logger.debug("Partitioning dev %s on node %s" %
- (d.name, n.name))
+ self.logger.debug("Partitioning dev %s (%s) on node %s" %
+ (d.name, n.get_logicalname(d.name), n.name))
for p in d.partitions:
if p.is_sys():
self.logger.debug(
@@ -1510,8 +1510,9 @@ class ApplyNodeStorage(BaseMaasAction):
self.maas_client, size=size, bootable=p.bootable)
if p.part_uuid is not None:
part.uuid = p.part_uuid
- msg = "Creating partition %s on dev %s" % (p.name,
- d.name)
+ msg = "Creating partition %s on dev %s (%s)" % (p.name,
+ d.name,
+ n.get_logicalname(d.name))
self.logger.debug(msg)
part = maas_dev.create_partition(part)
self.task.add_status_msg(
diff --git a/drydock_provisioner/objects/node.py b/drydock_provisioner/objects/node.py
index ae4a5b31..e782c05f 100644
--- a/drydock_provisioner/objects/node.py
+++ b/drydock_provisioner/objects/node.py
@@ -16,8 +16,12 @@
#
"""Drydock model of a baremetal node."""
+from defusedxml.ElementTree import fromstring
+import logging
from oslo_versionedobjects import fields as ovo_fields
+import drydock_provisioner.config as config
+import drydock_provisioner.error as errors
import drydock_provisioner.objects as objects
import drydock_provisioner.objects.hostprofile
import drydock_provisioner.objects.base as base
@@ -39,20 +43,24 @@ class BaremetalNode(drydock_provisioner.objects.hostprofile.HostProfile):
# the same set of CIs
def __init__(self, **kwargs):
super(BaremetalNode, self).__init__(**kwargs)
+ self.logicalnames = {}
+ self.logger = logging.getLogger(
+ config.config_mgr.conf.logging.global_logger_name)
# Compile the applied version of this model sourcing referenced
# data from the passed site design
- def compile_applied_model(self, site_design):
+ def compile_applied_model(self, site_design, state_manager):
self.apply_host_profile(site_design)
self.apply_hardware_profile(site_design)
self.source = hd_fields.ModelSource.Compiled
+ self.apply_logicalnames(site_design, state_manager)
return
def apply_host_profile(self, site_design):
self.apply_inheritance(site_design)
return
- # Translate device alises to physical selectors and copy
+ # Translate device aliases to physical selectors and copy
# other hardware attributes into this object
def apply_hardware_profile(self, site_design):
if self.hardware_profile is None:
@@ -112,6 +120,64 @@ class BaremetalNode(drydock_provisioner.objects.hostprofile.HostProfile):
return (sd, p)
return (None, None)
+ def _apply_logicalname(self, xml_root, alias_name, bus_type, address):
+ """Given xml_data, checks for a matching businfo and returns the logicalname
+
+ :param xml_root: Parsed ElementTree, it is searched for the logicalname.
+ :param alias_name: String value of the current device alias, it is returned
+ if a logicalname is not found.
+ :param bus_type: String value that is used to find the logicalname.
+ :param address: String value that is used to find the logicalname.
+ :return: String value of the logicalname or the alias_name if logicalname is not found.
+ """
+ nodes = xml_root.findall(".//node[businfo='" + bus_type + "@" + address + "'].logicalname")
+ if len(nodes) >= 1 and nodes[0].text:
+ # TODO (as1452): log.info() when more than one result after the logger refactor.
+ for logicalname in reversed(nodes[0].text.split("/")):
+ self.logger.debug("Logicalname build dict: alias_name = %s, bus_type = %s, address = %s, "
+ "to logicalname = %s" % (alias_name, bus_type, address, logicalname))
+ return logicalname
+ self.logger.debug("Logicalname build dict: alias_name = %s, bus_type = %s, address = %s, not found" %
+ (alias_name, bus_type, address))
+ return alias_name
+
+ def apply_logicalnames(self, site_design, state_manager):
+ """Gets the logicalnames for devices from lshw.
+
+ :param site_design: SiteDesign object.
+ :param state_manager: DrydockState object.
+ :return: Returns sets a dictionary of aliases that map to logicalnames in self.logicalnames.
+ """
+ logicalnames = {}
+
+ results = state_manager.get_build_data(node_name=self.get_name(), latest=True)
+ xml_data = None
+ for result in results:
+ if result.generator == "lshw":
+ xml_data = result.data_element
+ break
+
+ if xml_data:
+ xml_root = fromstring(xml_data)
+ for hardware_profile in site_design.hardware_profiles:
+ for device in hardware_profile.devices:
+ logicalname = self._apply_logicalname(xml_root, device.alias, device.bus_type,
+ device.address)
+ logicalnames[device.alias] = logicalname
+ else:
+ raise errors.BuildDataError("No Build Data found for node_name %s" % (self.get_name()))
+
+ self.logicalnames = logicalnames
+
+ def get_logicalname(self, alias):
+ """Gets the logicalname from self.logicalnames for an alias or returns the alias if not in the dictionary.
+ """
+ if (self.logicalnames and self.logicalnames.get(alias)):
+ self.logger.debug("Logicalname input = %s with output %s." % (alias, self.logicalnames[alias]))
+ return self.logicalnames[alias]
+ else:
+ self.logger.debug("Logicalname input = %s not in logicalnames dictionary." % alias)
+ return alias
@base.DrydockObjectRegistry.register
class BaremetalNodeList(base.DrydockObjectListBase, base.DrydockObject):
diff --git a/drydock_provisioner/orchestrator/orchestrator.py b/drydock_provisioner/orchestrator/orchestrator.py
index e4de73a0..b5deb7cd 100644
--- a/drydock_provisioner/orchestrator/orchestrator.py
+++ b/drydock_provisioner/orchestrator/orchestrator.py
@@ -244,17 +244,19 @@ class Orchestrator(object):
def compute_model_inheritance(self, site_design):
"""Compute inheritance of the design model.
- Given a fully populated Site model, compute the effecitve
+ Given a fully populated Site model, compute the effective
design by applying inheritance and references
"""
try:
nodes = site_design.baremetal_nodes
for n in nodes or []:
- n.compile_applied_model(site_design)
+ n.compile_applied_model(site_design, state_manager=self.state_manager)
except AttributeError:
self.logger.debug(
"Model inheritance skipped, no node definitions in site design."
)
+ except errors.BuildDataError:
+ self.logger.info("No Build Data found.")
return
diff --git a/requirements-direct.txt b/requirements-direct.txt
index 88172fcf..8465992e 100644
--- a/requirements-direct.txt
+++ b/requirements-direct.txt
@@ -21,3 +21,4 @@ psycopg2==2.7.3.1
jsonschema==2.6.0
jinja2==2.9.6
ulid2==0.1.1
+defusedxml===0.5.0
diff --git a/requirements-lock.txt b/requirements-lock.txt
index 006f6e41..90dbe01d 100644
--- a/requirements-lock.txt
+++ b/requirements-lock.txt
@@ -1,18 +1,19 @@
alembic==0.8.2
amqp==2.2.2
-Babel==2.5.1
+Babel==2.5.3
bson==0.4.7
cachetools==2.0.1
certifi==2017.11.5
chardet==3.0.4
click==6.7
contextlib2==0.5.5
-debtcollector==1.18.0
+debtcollector==1.19.0
+defusedxml==0.5.0
enum-compat==0.0.2
eventlet==0.20.0
-falcon==1.3.0
+falcon==1.4.1
fasteners==0.14.1
-futurist==1.4.0
+futurist==1.6.0
greenlet==0.4.12
idna==2.6
iso8601==0.1.11
@@ -24,26 +25,26 @@ kombu==4.1.0
Mako==1.0.7
MarkupSafe==1.0
monotonic==1.4
-msgpack-python==0.4.8
+msgpack-python==0.5.1
netaddr==0.7.19
netifaces==0.10.6
oauthlib==2.0.6
-oslo.concurrency==3.23.0
+oslo.concurrency==3.25.0
oslo.config==3.16.0
-oslo.context==2.19.3
-oslo.i18n==3.18.0
-oslo.log==3.33.0
-oslo.messaging==5.33.1
-oslo.middleware==3.32.1
+oslo.context==2.20.0
+oslo.i18n==3.19.0
+oslo.log==3.36.0
+oslo.messaging==5.35.0
+oslo.middleware==3.34.0
oslo.policy==1.22.1
-oslo.serialization==2.21.2
-oslo.service==1.27.0
-oslo.utils==3.31.0
+oslo.serialization==2.23.0
+oslo.service==1.29.0
+oslo.utils==3.35.0
oslo.versionedobjects==1.23.0
Paste==2.0.3
PasteDeploy==1.5.2
pbr==3.1.1
-pika==0.11.0
+pika==0.11.2
pika-pool==0.1.3
pip==9.0.1
positional==1.2.1
@@ -53,11 +54,10 @@ PTable==0.9.2
pycadf==2.6.0
pycrypto==2.6.1
pyghmi==1.0.18
-pyinotify==0.9.6
pyparsing==2.2.0
python-dateutil==2.6.1
python-editor==1.0.3
-python-keystoneclient==3.13.0
+python-keystoneclient==3.14.0
python-mimeparse==1.6.0
pytz==2017.3
PyYAML==3.12
@@ -65,16 +65,16 @@ repoze.lru==0.7
requests==2.18.4
rfc3986==1.1.0
Routes==2.4.1
-setuptools==36.7.2
+setuptools==38.4.0
six==1.11.0
SQLAlchemy==1.1.14
-statsd==3.2.1
-stevedore==1.27.1
-tenacity==4.7.0
+statsd==3.2.2
+stevedore==1.28.0
+tenacity==4.8.0
ulid2==0.1.1
urllib3==1.22
uWSGI==2.0.15
vine==1.1.4
-WebOb==1.7.3
+WebOb==1.7.4
wheel==0.30.0
wrapt==1.10.11
diff --git a/tests/conftest.py b/tests/conftest.py
index e764533a..4a9808e4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -15,6 +15,7 @@
import logging
import os
import shutil
+from unittest.mock import Mock
import drydock_provisioner.config as config
import drydock_provisioner.objects as objects
@@ -128,3 +129,20 @@ def setup_logging():
ch = logging.StreamHandler()
ch.setFormatter(formatter)
logger.addHandler(ch)
+
+@pytest.fixture(scope='module')
+def mock_get_build_data(drydock_state):
+ def side_effect(**kwargs):
+ build_data = objects.BuildData(
+ node_name="test",
+ task_id="tid",
+ generator="lshw",
+ data_format="text/plain",
+ data_element="")
+ return [build_data]
+ drydock_state.real_get_build_data = drydock_state.get_build_data
+ drydock_state.get_build_data = Mock(side_effect=side_effect)
+
+ yield
+ drydock_state.get_build_data = Mock(wraps=None, side_effect=None)
+ drydock_state.get_build_data = drydock_state.real_get_build_data
diff --git a/tests/integration/postgres/test_action_prepare_nodes.py b/tests/integration/postgres/test_action_prepare_nodes.py
index ed1f4a61..40077d96 100644
--- a/tests/integration/postgres/test_action_prepare_nodes.py
+++ b/tests/integration/postgres/test_action_prepare_nodes.py
@@ -20,7 +20,7 @@ from drydock_provisioner.orchestrator.actions.orchestrator import PrepareNodes
class TestActionPrepareNodes(object):
def test_preparenodes(self, mocker, input_files, deckhand_ingester, setup,
- drydock_state):
+ drydock_state, mock_get_build_data):
mock_images = mocker.patch("drydock_provisioner.drivers.node.driver.NodeDriver"
".get_available_images")
mock_images.return_value = ['xenial']
diff --git a/tests/integration/postgres/test_api_bootaction.py b/tests/integration/postgres/test_api_bootaction.py
index 56a3dd88..acc3802b 100644
--- a/tests/integration/postgres/test_api_bootaction.py
+++ b/tests/integration/postgres/test_api_bootaction.py
@@ -66,7 +66,8 @@ class TestClass(object):
assert result.status == falcon.HTTP_403
@pytest.fixture()
- def seed_bootaction(self, blank_state, yaml_orchestrator, input_files):
+ def seed_bootaction(self, blank_state, yaml_orchestrator, input_files,
+ mock_get_build_data):
"""Add a task and boot action to the database for testing."""
input_file = input_files.join("fullsite.yaml")
design_ref = "file://%s" % input_file
@@ -83,7 +84,8 @@ class TestClass(object):
return ba_ctx
@pytest.fixture()
- def falcontest(self, drydock_state, yaml_ingester, yaml_orchestrator):
+ def falcontest(self, drydock_state, yaml_ingester, yaml_orchestrator,
+ mock_get_build_data):
"""Create a test harness for the the Falcon API framework."""
return testing.TestClient(
start_api(
diff --git a/tests/integration/postgres/test_bootaction_signalling.py b/tests/integration/postgres/test_bootaction_signalling.py
index 9f5df2d0..81803154 100644
--- a/tests/integration/postgres/test_bootaction_signalling.py
+++ b/tests/integration/postgres/test_bootaction_signalling.py
@@ -18,7 +18,7 @@ from drydock_provisioner.objects import fields as hd_fields
class TestBootActionSignal(object):
def test_bootaction_signal_disable(self, deckhand_orchestrator,
- drydock_state, input_files):
+ drydock_state, input_files, mock_get_build_data):
"""Test that disabled signaling omits a status entry in the DB."""
input_file = input_files.join("deckhand_fullsite.yaml")
design_ref = "file://%s" % str(input_file)
diff --git a/tests/integration/postgres/test_orch_generic.py b/tests/integration/postgres/test_orch_generic.py
index 950cb0fe..afdfc567 100644
--- a/tests/integration/postgres/test_orch_generic.py
+++ b/tests/integration/postgres/test_orch_generic.py
@@ -21,7 +21,7 @@ import drydock_provisioner.objects.fields as hd_fields
class TestClass(object):
def test_task_complete(self, yaml_ingester, input_files, setup,
- blank_state):
+ blank_state, mock_get_build_data):
input_file = input_files.join("fullsite.yaml")
design_ref = "file://%s" % str(input_file)
diff --git a/tests/unit/test_api_validation.py b/tests/unit/test_api_validation.py
index 2fd37bd0..974cc699 100644
--- a/tests/unit/test_api_validation.py
+++ b/tests/unit/test_api_validation.py
@@ -24,7 +24,8 @@ import falcon
class TestValidationApi(object):
- def test_post_validation_resp(self, input_files, falcontest):
+ def test_post_validation_resp(self, input_files, falcontest, drydock_state,
+ mock_get_build_data):
input_file = input_files.join("deckhand_fullsite.yaml")
design_ref = "file://%s" % str(input_file)
@@ -81,7 +82,7 @@ class TestValidationApi(object):
assert result.status == falcon.HTTP_400
- def test_invalid_post_resp(self, input_files, falcontest):
+ def test_invalid_post_resp(self, input_files, falcontest, drydock_state, mock_get_build_data):
input_file = input_files.join("invalid_validation.yaml")
design_ref = "file://%s" % str(input_file)
@@ -105,7 +106,7 @@ class TestValidationApi(object):
@pytest.fixture()
def falcontest(self, drydock_state, deckhand_ingester,
- deckhand_orchestrator):
+ deckhand_orchestrator, mock_get_build_data):
"""Create a test harness for the the Falcon API framework."""
policy.policy_engine = policy.DrydockPolicy()
policy.policy_engine.register_policy()
diff --git a/tests/unit/test_bootaction_scoping.py b/tests/unit/test_bootaction_scoping.py
index 4296478e..67e626e1 100644
--- a/tests/unit/test_bootaction_scoping.py
+++ b/tests/unit/test_bootaction_scoping.py
@@ -17,7 +17,8 @@ import drydock_provisioner.objects as objects
class TestClass(object):
def test_bootaction_scoping_blankfilter(self, input_files,
- deckhand_orchestrator):
+ deckhand_orchestrator, drydock_state,
+ mock_get_build_data):
"""Test a boot action with no node filter scopes correctly."""
input_file = input_files.join("deckhand_fullsite.yaml")
@@ -36,7 +37,8 @@ class TestClass(object):
assert 'controller01' in ba.target_nodes
def test_bootaction_scoping_unionfilter(self, input_files,
- deckhand_orchestrator):
+ deckhand_orchestrator, drydock_state,
+ mock_get_build_data):
"""Test a boot action with a union node filter scopes correctly."""
input_file = input_files.join("deckhand_fullsite.yaml")
diff --git a/tests/unit/test_node_logicalnames.py b/tests/unit/test_node_logicalnames.py
new file mode 100644
index 00000000..3ec01e1c
--- /dev/null
+++ b/tests/unit/test_node_logicalnames.py
@@ -0,0 +1,158 @@
+# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
+#
+# 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.
+
+import pytest
+from unittest.mock import Mock
+
+import drydock_provisioner.objects as objects
+import drydock_provisioner.error as errors
+
+
+class TestClass(object):
+ def test_apply_logicalnames_exception(self, input_files, deckhand_orchestrator,
+ drydock_state, mock_get_build_data):
+ """Test node apply_logicalnames to get an exception"""
+ input_file = input_files.join("deckhand_fullsite.yaml")
+
+ design_ref = "file://%s" % str(input_file)
+
+ design_status, design_data = deckhand_orchestrator.get_effective_site(
+ design_ref)
+
+ def side_effect(**kwargs):
+ return []
+ drydock_state.get_build_data = Mock(side_effect=side_effect)
+
+ nodes = design_data.baremetal_nodes
+ with pytest.raises(errors.BuildDataError):
+ for n in nodes or []:
+ n.apply_logicalnames(design_data, state_manager=drydock_state)
+
+ def test_apply_logicalnames_success(self, input_files, deckhand_orchestrator,
+ drydock_state, mock_get_build_data):
+ """Test node apply_logicalnames to get the proper dictionary"""
+ input_file = input_files.join("deckhand_fullsite.yaml")
+
+ design_ref = "file://%s" % str(input_file)
+
+ xml_example = """
+
+
+
+
+
+
+
+ Rack Mount Chassis
+ PowerEdge R720xd (SKU=NotProvided;ModelName=PowerEdge R720xd)
+ Dell Inc.
+ 6H5LBY1
+ 64
+
+
+
+
+
+
+
+ SMBIOS version 2.7
+ DMI version 2.7
+ 32-bit processes
+
+
+
+ PCI bridge
+ Xeon E5/Core i7 IIO PCI Express Root Port 2a
+ Intel Corporation
+ 2
+ pci@0000:00:02.0
+ 07
+ 32
+ 33000000
+
+
+
+
+
+ Message Signalled Interrupts
+ PCI Express
+ Power Management
+
+ bus mastering
+ PCI capabilities listing
+
+
+
+
+
+ Ethernet interface
+ I350 Gigabit Network Connection
+ Intel Corporation
+ 0
+ pci@0000:00:03.0
+ eno1
+ 01
+ b8:ca:3a:65:7d:d8
+ 1000000000
+ 1000000000
+ 32
+ 33000000
+
+
+
+
+ SCSI Disk
+ PERC H710P
+ DELL
+ 2.0.0
+ scsi@2:0.0.0
+ /dev/sda
+ 8:0
+ 3.13
+ 0044016c12771be71900034cfba0a38c
+ 299439751168
+
+
+
+"""
+ xml_example = xml_example.replace('\n', '')
+
+ def side_effect(**kwargs):
+ build_data = objects.BuildData(
+ node_name="controller01",
+ task_id="tid",
+ generator="lshw",
+ data_format="text/plain",
+ data_element=xml_example)
+ return [build_data]
+ drydock_state.get_build_data = Mock(side_effect=side_effect)
+
+ design_status, design_data = deckhand_orchestrator.get_effective_site(
+ design_ref)
+
+ nodes = design_data.baremetal_nodes
+ nodes[0].apply_logicalnames(design_data, state_manager=drydock_state)
+
+ expected = {'primary_boot': 'sda', 'prim_nic02': 'prim_nic02', 'prim_nic01': 'eno1'}
+ # Tests the whole dictionary
+ assert nodes[0].logicalnames == expected
+ # Makes sure the path and / are both removed from primary_boot
+ assert nodes[0].logicalnames['primary_boot'] == 'sda'
+ assert nodes[0].get_logicalname('primary_boot') == 'sda'
+ # A simple logicalname
+ assert nodes[0].logicalnames['prim_nic01'] == 'eno1'
+ assert nodes[0].get_logicalname('prim_nic01') == 'eno1'
+ # Logicalname is not found, returns the alias
+ assert nodes[0].logicalnames['prim_nic02'] == 'prim_nic02'
+ assert nodes[0].get_logicalname('prim_nic02') == 'prim_nic02'
diff --git a/tests/unit/test_validate_design.py b/tests/unit/test_validate_design.py
index 3a71e813..d0269fff 100644
--- a/tests/unit/test_validate_design.py
+++ b/tests/unit/test_validate_design.py
@@ -19,7 +19,7 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
class TestDesignValidator(object):
def test_validate_design(self, deckhand_ingester, drydock_state,
- input_files):
+ input_files, mock_get_build_data):
"""Test the basic validation engine."""
input_file = input_files.join("deckhand_fullsite.yaml")
diff --git a/tests/unit/test_validation_rule_boot_storage.py b/tests/unit/test_validation_rule_boot_storage.py
index 08a117e6..92d8b89e 100644
--- a/tests/unit/test_validation_rule_boot_storage.py
+++ b/tests/unit/test_validation_rule_boot_storage.py
@@ -21,7 +21,7 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
class TestRationalBootStorage(object):
def test_boot_storage_rational(self, deckhand_ingester, drydock_state,
- input_files):
+ input_files, mock_get_build_data):
input_file = input_files.join("validation.yaml")
design_ref = "file://%s" % str(input_file)
@@ -39,7 +39,7 @@ class TestRationalBootStorage(object):
assert len(message_list) == 1
def test_invalid_boot_storage_small(self, deckhand_ingester, drydock_state,
- input_files):
+ input_files, mock_get_build_data):
input_file = input_files.join("invalid_boot_storage_small.yaml")
design_ref = "file://%s" % str(input_file)
diff --git a/tests/unit/test_validation_rule_storage_partitioning.py b/tests/unit/test_validation_rule_storage_partitioning.py
index 78200ec1..9bfa92fe 100644
--- a/tests/unit/test_validation_rule_storage_partitioning.py
+++ b/tests/unit/test_validation_rule_storage_partitioning.py
@@ -56,7 +56,7 @@ class TestRationalNetworkTrunking(object):
assert msg.get('error') is False
def test_invalid_storage_partitioning(self, deckhand_ingester,
- drydock_state, input_files):
+ drydock_state, input_files, mock_get_build_data):
input_file = input_files.join("invalid_validation.yaml")
design_ref = "file://%s" % str(input_file)
diff --git a/tests/unit/test_validation_rule_storage_sizing.py b/tests/unit/test_validation_rule_storage_sizing.py
index f91ebc69..ac6d0c0a 100644
--- a/tests/unit/test_validation_rule_storage_sizing.py
+++ b/tests/unit/test_validation_rule_storage_sizing.py
@@ -39,7 +39,7 @@ class TestStorageSizing(object):
assert msg.get('error') is False
def test_invalid_storage_sizing(self, deckhand_ingester, drydock_state,
- input_files):
+ input_files, mock_get_build_data):
input_file = input_files.join("invalid_validation.yaml")
design_ref = "file://%s" % str(input_file)
diff --git a/tests/unit/test_validation_rule_valid_platform.py b/tests/unit/test_validation_rule_valid_platform.py
index 569188f4..3c3091fc 100644
--- a/tests/unit/test_validation_rule_valid_platform.py
+++ b/tests/unit/test_validation_rule_valid_platform.py
@@ -21,7 +21,7 @@ from drydock_provisioner.orchestrator.validations.validator import Validator
class TestValidPlatform(object):
def test_valid_platform(self, mocker, deckhand_ingester, drydock_state,
- input_files):
+ input_files, mock_get_build_data):
mock_images = mocker.patch(
"drydock_provisioner.drivers.node.maasdriver.driver."
"MaasNodeDriver.get_available_images")