summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-07-23 13:31:38 +0000
committerGerrit Code Review <review@openstack.org>2014-07-23 13:31:38 +0000
commit401eae8a89cee131198325b89a2c7208d74d94c2 (patch)
tree46f7a9ced1db88ddf916adc8776ff9e55f873263
parentb2a65a90c67d8133e80f5d06aa8294feb7d56187 (diff)
parentc75a070520b02deafe61343157d3a4219374f6d3 (diff)
downloadironic-401eae8a89cee131198325b89a2c7208d74d94c2.tar.gz
Merge "Add drivers.base.BaseDriver.get_properties()"
-rw-r--r--ironic/drivers/base.py57
-rw-r--r--ironic/drivers/modules/fake.py20
-rw-r--r--ironic/drivers/modules/ilo/common.py17
-rw-r--r--ironic/drivers/modules/ilo/power.py3
-rw-r--r--ironic/drivers/modules/ipminative.py24
-rw-r--r--ironic/drivers/modules/ipmitool.py42
-rw-r--r--ironic/drivers/modules/pxe.py14
-rw-r--r--ironic/drivers/modules/seamicro.py40
-rw-r--r--ironic/drivers/modules/ssh.py38
-rw-r--r--ironic/drivers/utils.py12
-rw-r--r--ironic/tests/drivers/ilo/test_power.py6
-rw-r--r--ironic/tests/drivers/test_fake.py11
-rw-r--r--ironic/tests/drivers/test_ipminative.py4
-rw-r--r--ironic/tests/drivers/test_ipmitool.py11
-rw-r--r--ironic/tests/drivers/test_pxe.py6
-rw-r--r--ironic/tests/drivers/test_seamicro.py6
-rw-r--r--ironic/tests/drivers/test_ssh.py7
-rw-r--r--ironic/tests/drivers/test_utils.py8
18 files changed, 294 insertions, 32 deletions
diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py
index ceb48703c..4ba16a804 100644
--- a/ironic/drivers/base.py
+++ b/ironic/drivers/base.py
@@ -88,12 +88,34 @@ class BaseDriver(object):
def __init__(self):
pass
+ def get_properties(self):
+ """Get the properties of the driver.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ properties = {}
+ for iface_name in (self.core_interfaces +
+ self.standard_interfaces +
+ ['vendor']):
+ iface = getattr(self, iface_name, None)
+ if iface:
+ properties.update(iface.get_properties())
+ return properties
+
@six.add_metaclass(abc.ABCMeta)
class DeployInterface(object):
"""Interface for deploy-related actions."""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task):
"""Validate the driver-specific Node deployment info.
@@ -190,6 +212,13 @@ class PowerInterface(object):
"""Interface for power-related actions."""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task):
"""Validate the driver-specific Node power info.
@@ -231,6 +260,13 @@ class ConsoleInterface(object):
"""Interface for console-related actions."""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task):
"""Validate the driver-specific Node console info.
@@ -274,6 +310,13 @@ class RescueInterface(object):
"""Interface for rescue-related actions."""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task):
"""Validate the rescue info stored in the node' properties.
@@ -311,6 +354,13 @@ class VendorInterface(object):
"""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task, **kwargs):
"""Validate vendor-specific actions.
@@ -358,6 +408,13 @@ class ManagementInterface(object):
"""Interface for management related actions."""
@abc.abstractmethod
+ def get_properties(self):
+ """Return the properties of the interface.
+
+ :returns: dictionary of <property name>:<property description> entries.
+ """
+
+ @abc.abstractmethod
def validate(self, task):
"""Validate the driver-specific management information.
diff --git a/ironic/drivers/modules/fake.py b/ironic/drivers/modules/fake.py
index 95c3bf530..1ae10f7f6 100644
--- a/ironic/drivers/modules/fake.py
+++ b/ironic/drivers/modules/fake.py
@@ -42,6 +42,9 @@ def _raise_unsupported_error(method=None):
class FakePower(base.PowerInterface):
"""Example implementation of a simple power interface."""
+ def get_properties(self):
+ return {}
+
def validate(self, task):
pass
@@ -63,6 +66,9 @@ class FakeDeploy(base.DeployInterface):
separate power interface.
"""
+ def get_properties(self):
+ return {}
+
def validate(self, task):
pass
@@ -85,6 +91,10 @@ class FakeDeploy(base.DeployInterface):
class FakeVendorA(base.VendorInterface):
"""Example implementation of a vendor passthru interface."""
+ def get_properties(self):
+ return {'A1': 'A1 description. Required.',
+ 'A2': 'A2 description. Optional.'}
+
def validate(self, task, **kwargs):
method = kwargs.get('method')
if method == 'first_method':
@@ -109,6 +119,10 @@ class FakeVendorA(base.VendorInterface):
class FakeVendorB(base.VendorInterface):
"""Example implementation of a secondary vendor passthru."""
+ def get_properties(self):
+ return {'B1': 'B1 description. Required.',
+ 'B2': 'B2 description. Required.'}
+
def validate(self, task, **kwargs):
method = kwargs.get('method')
if method == 'second_method':
@@ -133,6 +147,9 @@ class FakeVendorB(base.VendorInterface):
class FakeConsole(base.ConsoleInterface):
"""Example implementation of a simple console interface."""
+ def get_properties(self):
+ return {}
+
def validate(self, task):
pass
@@ -149,6 +166,9 @@ class FakeConsole(base.ConsoleInterface):
class FakeManagement(base.ManagementInterface):
"""Example implementation of a simple management interface."""
+ def get_properties(self):
+ return {}
+
def validate(self, task):
pass
diff --git a/ironic/drivers/modules/ilo/common.py b/ironic/drivers/modules/ilo/common.py
index f645139ae..80d503bd4 100644
--- a/ironic/drivers/modules/ilo/common.py
+++ b/ironic/drivers/modules/ilo/common.py
@@ -44,6 +44,19 @@ CONF.register_opts(opts, group='ilo')
LOG = logging.getLogger(__name__)
+REQUIRED_PROPERTIES = {
+ 'ilo_address': _("IP address or hostname of the iLO. Required."),
+ 'ilo_username': _("username for the iLO with administrator privileges. "
+ "Required."),
+ 'ilo_password': _("password for ilo_username. Required.")
+}
+OPTIONAL_PROPERTIES = {
+ 'client_port': _("port to be used for iLO operations. Optional."),
+ 'client_timeout': _("timeout (in seconds) for iLO operations. Optional.")
+}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
+COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
+
def parse_driver_info(node):
"""Gets the driver specific Node deployment info.
@@ -61,13 +74,13 @@ def parse_driver_info(node):
d_info = {}
error_msgs = []
- for param in ('ilo_address', 'ilo_username', 'ilo_password'):
+ for param in REQUIRED_PROPERTIES:
try:
d_info[param] = info[param]
except KeyError:
error_msgs.append(_("'%s' not supplied to IloDriver.") % param)
- for param in ('client_port', 'client_timeout'):
+ for param in OPTIONAL_PROPERTIES:
value = info.get(param, CONF.ilo.get(param))
try:
value = int(value)
diff --git a/ironic/drivers/modules/ilo/power.py b/ironic/drivers/modules/ilo/power.py
index cabf51193..5faf74282 100644
--- a/ironic/drivers/modules/ilo/power.py
+++ b/ironic/drivers/modules/ilo/power.py
@@ -152,6 +152,9 @@ def _set_power_state(node, target_state):
class IloPower(base.PowerInterface):
+ def get_properties(self):
+ return ilo_common.COMMON_PROPERTIES
+
def validate(self, task):
"""Check if node.driver_info contains the required iLO credentials.
diff --git a/ironic/drivers/modules/ipminative.py b/ironic/drivers/modules/ipminative.py
index 19c572fa2..4be2b943b 100644
--- a/ironic/drivers/modules/ipminative.py
+++ b/ironic/drivers/modules/ipminative.py
@@ -50,6 +50,11 @@ CONF.register_opts(opts, group='ipmi')
LOG = logging.getLogger(__name__)
+REQUIRED_PROPERTIES = {'ipmi_address': _("IP of the node's BMC. Required."),
+ 'ipmi_password': _("IPMI password. Required."),
+ 'ipmi_username': _("IPMI username. Required.")}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES
+
def _parse_driver_info(node):
"""Gets the bmc access info for the given node.
@@ -58,19 +63,18 @@ def _parse_driver_info(node):
"""
info = node.driver_info or {}
- bmc_info = {}
- bmc_info['address'] = info.get('ipmi_address')
- bmc_info['username'] = info.get('ipmi_username')
- bmc_info['password'] = info.get('ipmi_password')
-
- # address, username and password must be present
- missing_info = [key for key in bmc_info if not bmc_info[key]]
+ missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
if missing_info:
raise exception.InvalidParameterValue(_(
"The following IPMI credentials are not supplied"
" to IPMI driver: %s."
) % missing_info)
+ bmc_info = {}
+ bmc_info['address'] = info.get('ipmi_address')
+ bmc_info['username'] = info.get('ipmi_username')
+ bmc_info['password'] = info.get('ipmi_password')
+
# get additional info
bmc_info['uuid'] = node.uuid
@@ -207,6 +211,9 @@ def _power_status(driver_info):
class NativeIPMIPower(base.PowerInterface):
"""The power driver using native python-ipmi library."""
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Check that node['driver_info'] contains IPMI credentials.
@@ -299,6 +306,9 @@ class VendorPassthru(base.VendorInterface):
% {'node_id': driver_info['uuid'], 'error': str(e)})
raise exception.IPMIFailure(cmd=str(e))
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task, **kwargs):
"""Validate vendor-specific actions.
:param task: a TaskManager instance.
diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py
index e4239c327..9060eb68a 100644
--- a/ironic/drivers/modules/ipmitool.py
+++ b/ironic/drivers/modules/ipmitool.py
@@ -65,6 +65,23 @@ CONF.import_opt('min_command_interval',
LOG = logging.getLogger(__name__)
VALID_PRIV_LEVELS = ['ADMINISTRATOR', 'CALLBACK', 'OPERATOR', 'USER']
+
+REQUIRED_PROPERTIES = {
+ 'ipmi_address': _("IP address or hostname of the node. Required.")
+}
+OPTIONAL_PROPERTIES = {
+ 'ipmi_password': _("password. Optional."),
+ 'ipmi_priv_level': _("privilege level; default is ADMINISTRATOR. One of "
+ "%s. Optional.") % ', '.join(VALID_PRIV_LEVELS),
+ 'ipmi_username': _("username; default is NULL user. Optional.")
+}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
+COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
+CONSOLE_PROPERTIES = {
+ 'ipmi_terminal_port': _("node's UDP port to connect to. Only required for "
+ "console access.")
+}
+
LAST_CMD_TIME = {}
TIMING_SUPPORT = None
@@ -145,6 +162,13 @@ def _parse_driver_info(node):
"""
info = node.driver_info or {}
+ missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
+ if missing_info:
+ raise exception.InvalidParameterValue(_(
+ "The following IPMI credentials are not supplied"
+ " to IPMI driver: %s."
+ ) % missing_info)
+
address = info.get('ipmi_address')
username = info.get('ipmi_username')
password = info.get('ipmi_password')
@@ -158,10 +182,6 @@ def _parse_driver_info(node):
raise exception.InvalidParameterValue(_(
"IPMI terminal port is not an integer."))
- if not address:
- raise exception.InvalidParameterValue(_(
- "IPMI address not supplied to IPMI driver."))
-
if priv_level not in VALID_PRIV_LEVELS:
valid_priv_lvls = ', '.join(VALID_PRIV_LEVELS)
raise exception.InvalidParameterValue(_(
@@ -365,6 +385,9 @@ class IPMIPower(base.PowerInterface):
reason="Unable to locate usable ipmitool command in "
"the system path when checking ipmitool version")
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Validate driver_info for ipmitool driver.
@@ -438,6 +461,9 @@ class IPMIPower(base.PowerInterface):
class IPMIManagement(base.ManagementInterface):
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Check that 'driver_info' contains IPMI credentials.
@@ -602,6 +628,9 @@ class VendorPassthru(base.VendorInterface):
{'node_id': node_uuid, 'error': e})
raise exception.IPMIFailure(cmd=cmd)
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task, **kwargs):
"""Validate vendor-specific actions.
@@ -668,6 +697,11 @@ class IPMIShellinaboxConsole(base.ConsoleInterface):
reason="Unable to locate usable ipmitool command in "
"the system path when checking ipmitool version")
+ def get_properties(self):
+ d = COMMON_PROPERTIES.copy()
+ d.update(CONSOLE_PROPERTIES)
+ return d
+
def validate(self, task):
"""Validate the Node console info.
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index 4af3c4aa1..07a3bc3f4 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -90,6 +90,14 @@ CONF = cfg.CONF
CONF.register_opts(pxe_opts, group='pxe')
CONF.import_opt('use_ipv6', 'ironic.netconf')
+REQUIRED_PROPERTIES = {
+ 'pxe_deploy_kernel': _("UUID (from Glance) of the deployment kernel. "
+ "Required."),
+ 'pxe_deploy_ramdisk': _("UUID (from Glance) of the ramdisk that is "
+ "mounted at boot time. Required."),
+}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES
+
def _check_for_missing_params(info_dict, param_prefix=''):
missing_info = []
@@ -463,6 +471,9 @@ def _validate_glance_image(ctx, deploy_info):
class PXEDeploy(base.DeployInterface):
"""PXE Deploy Interface: just a stub until the real driver is ported."""
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Validate the deployment information for the task's node.
@@ -609,6 +620,9 @@ class VendorPassthru(base.VendorInterface):
return params
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task, **kwargs):
method = kwargs['method']
if method == 'pass_deploy_info':
diff --git a/ironic/drivers/modules/seamicro.py b/ironic/drivers/modules/seamicro.py
index 491c0b293..265131aa6 100644
--- a/ironic/drivers/modules/seamicro.py
+++ b/ironic/drivers/modules/seamicro.py
@@ -62,6 +62,19 @@ _BOOT_DEVICES_MAP = {
boot_devices.PXE: 'pxe',
}
+REQUIRED_PROPERTIES = {
+ 'seamicro_api_endpoint': _("API endpoint. Required."),
+ 'seamicro_password': _("password. Required."),
+ 'seamicro_server_id': _("server ID. Required."),
+ 'seamicro_username': _("username. Required."),
+}
+OPTIONAL_PROPERTIES = {
+ 'seamicro_api_version': _("version of SeaMicro API client; default is 2. "
+ "Optional.")
+}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
+COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
+
def _get_client(*args, **kwargs):
"""Creates the python-seamicro_client
@@ -87,24 +100,18 @@ def _parse_driver_info(node):
"""
info = node.driver_info or {}
+ missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
+ if missing_info:
+ raise exception.InvalidParameterValue(_(
+ "SeaMicro driver requires the following to be set: %s.")
+ % missing_info)
+
api_endpoint = info.get('seamicro_api_endpoint')
username = info.get('seamicro_username')
password = info.get('seamicro_password')
server_id = info.get('seamicro_server_id')
api_version = info.get('seamicro_api_version', "2")
- if not api_endpoint:
- raise exception.InvalidParameterValue(_(
- "SeaMicro driver requires api_endpoint be set"))
-
- if not username or not password:
- raise exception.InvalidParameterValue(_(
- "SeaMicro driver requires both username and password be set"))
-
- if not server_id:
- raise exception.InvalidParameterValue(_(
- "SeaMicro driver requires server_id be set"))
-
res = {'username': username,
'password': password,
'api_endpoint': api_endpoint,
@@ -322,6 +329,9 @@ class Power(base.PowerInterface):
state of servers in a seamicro chassis.
"""
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Check that node 'driver_info' is valid.
@@ -389,6 +399,9 @@ class Power(base.PowerInterface):
class VendorPassthru(base.VendorInterface):
"""SeaMicro vendor-specific methods."""
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task, **kwargs):
method = kwargs['method']
if method not in VENDOR_PASSTHRU_METHODS:
@@ -470,6 +483,9 @@ class VendorPassthru(base.VendorInterface):
class Management(base.ManagementInterface):
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Check that 'driver_info' contains SeaMicro credentials.
diff --git a/ironic/drivers/modules/ssh.py b/ironic/drivers/modules/ssh.py
index 2d8d470f8..50cc0270a 100644
--- a/ironic/drivers/modules/ssh.py
+++ b/ironic/drivers/modules/ssh.py
@@ -35,6 +35,7 @@ from ironic.common import utils
from ironic.conductor import task_manager
from ironic.drivers import base
from ironic.drivers import utils as driver_utils
+from ironic.openstack.common.gettextutils import _
from ironic.openstack.common import log as logging
from ironic.openstack.common import processutils
@@ -49,6 +50,27 @@ CONF.register_opts(libvirt_opts, group='ssh')
LOG = logging.getLogger(__name__)
+REQUIRED_PROPERTIES = {
+ 'ssh_address': _("IP address or hostname of the node to ssh into. "
+ "Required."),
+ 'ssh_username': _("username to authenticate as. Required."),
+ 'ssh_virt_type': _("virtualization software to use; one of vbox, virsh, "
+ "vmware. Required.")
+}
+OTHER_PROPERTIES = {
+ 'ssh_key_contents': _("private key(s). One of this, ssh_key_filename, "
+ "or ssh_password must be specified."),
+ 'ssh_key_filename': _("(list of) filename(s) of optional private key(s) "
+ "for authentication. One of this, ssh_key_contents, "
+ "or ssh_password must be specified."),
+ 'ssh_password': _("password to use for authentication or for unlocking a "
+ "private key. One of this, ssh_key_contents, or "
+ "ssh_key_filename must be specified."),
+ 'ssh_port': _("port on the node to connect to; default is 22. Optional.")
+}
+COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
+COMMON_PROPERTIES.update(OTHER_PROPERTIES)
+
def _get_command_sets(virt_type):
if virt_type == 'vbox':
@@ -155,6 +177,12 @@ def _parse_driver_info(node):
"""
info = node.driver_info or {}
+ missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
+ if missing_info:
+ raise exception.InvalidParameterValue(_(
+ "SSHPowerDriver requires the following to be set: %s.")
+ % missing_info)
+
address = info.get('ssh_address')
username = info.get('ssh_username')
password = info.get('ssh_password')
@@ -176,16 +204,9 @@ def _parse_driver_info(node):
'uuid': node.uuid
}
- if not virt_type:
- raise exception.InvalidParameterValue(_(
- "SSHPowerDriver requires virt_type be set."))
-
cmd_set = _get_command_sets(virt_type)
res['cmd_set'] = cmd_set
- if not address or not username:
- raise exception.InvalidParameterValue(_(
- "SSHPowerDriver requires both address and username be set."))
# Only one credential may be set (avoids complexity around having
# precedence etc).
if len(filter(None, (password, key_filename, key_contents))) != 1:
@@ -354,6 +375,9 @@ class SSHPower(base.PowerInterface):
NOTE: This driver does not currently support multi-node operations.
"""
+ def get_properties(self):
+ return COMMON_PROPERTIES
+
def validate(self, task):
"""Check that the node's 'driver_info' is valid.
diff --git a/ironic/drivers/utils.py b/ironic/drivers/utils.py
index d0435fc81..47ceef275 100644
--- a/ironic/drivers/utils.py
+++ b/ironic/drivers/utils.py
@@ -46,6 +46,18 @@ class MixinVendorInterface(base.VendorInterface):
method = kwargs.get('method')
return self.mapping.get(method) or _raise_unsupported_error(method)
+ def get_properties(self):
+ """Return the properties from all the VendorInterfaces.
+
+ :returns: a dictionary of <property_name>:<property_description>
+ entries.
+ """
+ properties = {}
+ interfaces = set(self.mapping.values())
+ for interface in interfaces:
+ properties.update(interface.get_properties())
+ return properties
+
def validate(self, *args, **kwargs):
"""Call validate on the appropriate interface only.
diff --git a/ironic/tests/drivers/ilo/test_power.py b/ironic/tests/drivers/ilo/test_power.py
index 61a221f8a..2dbfe72ae 100644
--- a/ironic/tests/drivers/ilo/test_power.py
+++ b/ironic/tests/drivers/ilo/test_power.py
@@ -149,6 +149,12 @@ class IloPowerTestCase(base.TestCase):
driver='ilo',
driver_info=driver_info)
+ def test_get_properties(self):
+ expected = ilo_common.COMMON_PROPERTIES
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ self.assertEqual(expected, task.driver.get_properties())
+
@mock.patch.object(ilo_common, 'parse_driver_info')
def test_validate(self, mock_drvinfo):
with task_manager.acquire(self.context, self.node.uuid,
diff --git a/ironic/tests/drivers/test_fake.py b/ironic/tests/drivers/test_fake.py
index 1e401c7d0..0f9cfb13c 100644
--- a/ironic/tests/drivers/test_fake.py
+++ b/ironic/tests/drivers/test_fake.py
@@ -53,7 +53,13 @@ class FakeDriverTestCase(base.TestCase):
driver_base.ConsoleInterface)
self.assertIsNone(self.driver.rescue)
+ def test_get_properties(self):
+ expected = ['A1', 'A2', 'B1', 'B2']
+ properties = self.driver.get_properties()
+ self.assertEqual(sorted(expected), sorted(properties.keys()))
+
def test_power_interface(self):
+ self.assertEqual({}, self.driver.power.get_properties())
self.driver.power.validate(self.task)
self.driver.power.get_power_state(self.task)
self.assertRaises(exception.InvalidParameterValue,
@@ -63,6 +69,7 @@ class FakeDriverTestCase(base.TestCase):
self.driver.power.reboot(self.task)
def test_deploy_interface(self):
+ self.assertEqual({}, self.driver.deploy.get_properties())
self.driver.deploy.validate(None)
self.driver.deploy.prepare(None)
@@ -74,11 +81,15 @@ class FakeDriverTestCase(base.TestCase):
self.driver.deploy.tear_down(None)
def test_console_interface(self):
+ self.assertEqual({}, self.driver.console.get_properties())
self.driver.console.validate(self.task)
self.driver.console.start_console(self.task)
self.driver.console.stop_console(self.task)
self.driver.console.get_console(self.task)
+ def test_management_interface_get_properties(self):
+ self.assertEqual({}, self.driver.management.get_properties())
+
def test_management_interface_validate(self):
self.driver.management.validate(self.task)
diff --git a/ironic/tests/drivers/test_ipminative.py b/ironic/tests/drivers/test_ipminative.py
index 5340a8132..d0649513c 100644
--- a/ironic/tests/drivers/test_ipminative.py
+++ b/ironic/tests/drivers/test_ipminative.py
@@ -143,6 +143,10 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
self.dbapi = db_api.get_instance()
self.info = ipminative._parse_driver_info(self.node)
+ def test_get_properties(self):
+ expected = ipminative.COMMON_PROPERTIES
+ self.assertEqual(expected, self.driver.get_properties())
+
@mock.patch('pyghmi.ipmi.command.Command')
def test_get_power_state(self, ipmi_mock):
# Getting the mocked command.
diff --git a/ironic/tests/drivers/test_ipmitool.py b/ironic/tests/drivers/test_ipmitool.py
index ae29af36c..55751a987 100644
--- a/ironic/tests/drivers/test_ipmitool.py
+++ b/ironic/tests/drivers/test_ipmitool.py
@@ -484,6 +484,17 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
driver_info=INFO_DICT)
self.info = ipmi._parse_driver_info(self.node)
+ def test_get_properties(self):
+ expected = ipmi.COMMON_PROPERTIES
+ self.assertEqual(expected, self.driver.power.get_properties())
+
+ expected = ipmi.COMMON_PROPERTIES.keys()
+ expected += ipmi.CONSOLE_PROPERTIES.keys()
+ self.assertEqual(sorted(expected),
+ sorted(self.driver.console.get_properties().keys()))
+ self.assertEqual(sorted(expected),
+ sorted(self.driver.get_properties().keys()))
+
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
def test_get_power_state(self, mock_exec):
returns = iter([["Chassis Power is off\n", None],
diff --git a/ironic/tests/drivers/test_pxe.py b/ironic/tests/drivers/test_pxe.py
index cd1ff9a7e..f870eca7d 100644
--- a/ironic/tests/drivers/test_pxe.py
+++ b/ironic/tests/drivers/test_pxe.py
@@ -519,6 +519,12 @@ class PXEDriverTestCase(db_base.DbTestCase):
open(token_path, 'w').close()
return token_path
+ def test_get_properties(self):
+ expected = pxe.COMMON_PROPERTIES
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ self.assertEqual(expected, task.driver.get_properties())
+
@mock.patch.object(base_image_service.BaseImageService, '_show')
def test_validate_good(self, mock_glance):
mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
diff --git a/ironic/tests/drivers/test_seamicro.py b/ironic/tests/drivers/test_seamicro.py
index 56a537087..42c8f4ced 100644
--- a/ironic/tests/drivers/test_seamicro.py
+++ b/ironic/tests/drivers/test_seamicro.py
@@ -274,6 +274,12 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
self.Server = Fake_Server
self.Volume = Fake_Volume
+ def test_get_properties(self):
+ expected = seamicro.COMMON_PROPERTIES
+ with task_manager.acquire(self.context, self.node['uuid'],
+ shared=True) as task:
+ self.assertEqual(expected, task.driver.get_properties())
+
@mock.patch.object(seamicro, '_parse_driver_info')
def test_power_interface_validate_good(self, parse_drv_info_mock):
with task_manager.acquire(self.context, self.node['uuid'],
diff --git a/ironic/tests/drivers/test_ssh.py b/ironic/tests/drivers/test_ssh.py
index 6f09f0dc4..ff8ea22ae 100644
--- a/ironic/tests/drivers/test_ssh.py
+++ b/ironic/tests/drivers/test_ssh.py
@@ -580,6 +580,13 @@ class SSHDriverTestCase(db_base.DbTestCase):
driver_info = ssh._parse_driver_info(task.node)
ssh_connect_mock.assert_called_once_with(driver_info)
+ def test_get_properties(self):
+ expected = ssh.COMMON_PROPERTIES
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+ self.assertEqual(expected, task.driver.power.get_properties())
+ self.assertEqual(expected, task.driver.get_properties())
+
def test_validate_fail_no_port(self):
new_node = obj_utils.create_test_node(
self.context,
diff --git a/ironic/tests/drivers/test_utils.py b/ironic/tests/drivers/test_utils.py
index 799d3ca09..6a1f94e91 100644
--- a/ironic/tests/drivers/test_utils.py
+++ b/ironic/tests/drivers/test_utils.py
@@ -38,6 +38,14 @@ class UtilsTestCase(base.TestCase):
self.driver = driver_factory.get_driver("fake")
self.node = obj_utils.create_test_node(self.context)
+ def test_vendor_interface_get_properties(self):
+ expected = {'A1': 'A1 description. Required.',
+ 'A2': 'A2 description. Optional.',
+ 'B1': 'B1 description. Required.',
+ 'B2': 'B2 description. Required.'}
+ props = self.driver.vendor.get_properties()
+ self.assertEqual(expected, props)
+
@mock.patch.object(fake.FakeVendorA, 'validate')
def test_vendor_interface_validate_valid_methods(self,
mock_fakea_validate):