Add a mechanism to route vendor methods
Each vendor class had to implement a custom method
to invoke their own vendor functionalities, the commit
e129faef17
added a decorator @passthru()
to decorate those vendor methods and guarantee that the errors and
exceptions are being handled accordingly. This patch is extending that
decorator to add some metadata to each vendor method and from that be
able to generically build a way to invoke those without requiring vendor
drivers to implement a custom method for that.
The vendor_passthru() and driver_vendor_passthru() methods were removed
from the VendorInterface base class. Now vendors that wants to implement
a vendor method should now only care about implementing the method itself
and decorating them with the @passthru() or @driver_passthru decorator.
This patch also adds a backward compatibility layer for existing drivers
out of the tree that may be using the old [driver_]vendor_passthru()
methods. If the vendor class contains those methods we are going to
call them just like before, otherwise we are going to use the new
mechanism. All drivers in tree have been ported to the new mechanism.
Implements: blueprint extended-vendor-passthru
Partial-Bug: #1382457
Change-Id: I7770ccd9668d9c03e02f769aff7c54b33734a770
This commit is contained in:
parent
8dba76ee7f
commit
b5a531aa4d
@ -392,11 +392,34 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
driver=task.node.driver,
|
||||
extension='vendor passthru')
|
||||
|
||||
task.driver.vendor.validate(task, method=driver_method,
|
||||
vendor_iface = task.driver.vendor
|
||||
|
||||
# NOTE(lucasagomes): Before the vendor_passthru() method was
|
||||
# a self-contained method and each driver implemented their own
|
||||
# version of it, now we have a common mechanism that drivers
|
||||
# should use to expose their vendor methods. If a driver still
|
||||
# have their own vendor_passthru() method we call it to be
|
||||
# backward compat. This code should be removed once L opens.
|
||||
if hasattr(vendor_iface, 'vendor_passthru'):
|
||||
LOG.warning(_LW("Drivers implementing their own version "
|
||||
"of vendor_passthru() has been deprecated. "
|
||||
"Please update the code to use the "
|
||||
"@passthru decorator."))
|
||||
vendor_iface.validate(task, method=driver_method,
|
||||
**info)
|
||||
task.spawn_after(self._spawn_worker,
|
||||
task.driver.vendor.vendor_passthru, task,
|
||||
vendor_iface.vendor_passthru, task,
|
||||
method=driver_method, **info)
|
||||
return
|
||||
|
||||
try:
|
||||
vendor_func = vendor_iface.vendor_routes[driver_method]['func']
|
||||
except KeyError:
|
||||
raise exception.InvalidParameterValue(
|
||||
_('No handler for method %s') % driver_method)
|
||||
|
||||
vendor_iface.validate(task, method=driver_method, **info)
|
||||
task.spawn_after(self._spawn_worker, vendor_func, task, **info)
|
||||
|
||||
@messaging.expected_exceptions(exception.InvalidParameterValue,
|
||||
exception.MissingParameterValue,
|
||||
@ -432,9 +455,28 @@ class ConductorManager(periodic_task.PeriodicTasks):
|
||||
driver=driver_name,
|
||||
extension='vendor interface')
|
||||
|
||||
return driver.vendor.driver_vendor_passthru(context,
|
||||
method=driver_method,
|
||||
**info)
|
||||
# NOTE(lucasagomes): Before the driver_vendor_passthru()
|
||||
# method was a self-contained method and each driver implemented
|
||||
# their own version of it, now we have a common mechanism that
|
||||
# drivers should use to expose their vendor methods. If a driver
|
||||
# still have their own driver_vendor_passthru() method we call
|
||||
# it to be backward compat. This code should be removed
|
||||
# once L opens.
|
||||
if hasattr(driver.vendor, 'driver_vendor_passthru'):
|
||||
LOG.warning(_LW("Drivers implementing their own version "
|
||||
"of driver_vendor_passthru() has been "
|
||||
"deprecated. Please update the code to use "
|
||||
"the @driver_passthru decorator."))
|
||||
return driver.vendor.driver_vendor_passthru(
|
||||
context, method=driver_method, **info)
|
||||
|
||||
try:
|
||||
vendor_func = driver.vendor.driver_routes[driver_method]['func']
|
||||
except KeyError:
|
||||
raise exception.InvalidParameterValue(
|
||||
_('No handler for method %s') % driver_method)
|
||||
|
||||
return vendor_func(context, **info)
|
||||
|
||||
def _provisioning_error_handler(self, e, node, provision_state,
|
||||
target_provision_state):
|
||||
|
@ -18,13 +18,14 @@ Abstract base classes for drivers.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import functools
|
||||
import inspect
|
||||
|
||||
from oslo.utils import excutils
|
||||
import six
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.openstack.common import log as logging
|
||||
|
||||
@ -355,7 +356,12 @@ class RescueInterface(object):
|
||||
"""
|
||||
|
||||
|
||||
def passthru(method=None):
|
||||
# Representation of a single vendor method metadata
|
||||
VendorMetadata = collections.namedtuple('VendorMetadata', ['method',
|
||||
'metadata'])
|
||||
|
||||
|
||||
def _passthru(method=None, driver_passthru=False):
|
||||
"""A decorator for registering a function as a passthru function.
|
||||
|
||||
Decorator ensures function is ready to catch any ironic exceptions
|
||||
@ -367,11 +373,26 @@ def passthru(method=None):
|
||||
reraised, it won't be handled if it is an async. call.
|
||||
|
||||
:param method: an arbitrary string describing the action to be taken.
|
||||
:param driver_passthru: Boolean value. Whether this is a driver
|
||||
vendor passthru or a node vendor passthru
|
||||
method.
|
||||
|
||||
"""
|
||||
def handle_passthru(func):
|
||||
api_method = method
|
||||
if api_method is None:
|
||||
api_method = func.__name__
|
||||
|
||||
# NOTE(lucasagomes): It's adding an empty dictionary for now but
|
||||
# in the following patches this is going to have more metadata
|
||||
# about the vendor method. For e.g the supported HTTP methods and
|
||||
# whether it should run asynchrounously or not.
|
||||
metadata = VendorMetadata(api_method, {})
|
||||
if driver_passthru:
|
||||
func._driver_metadata = metadata
|
||||
else:
|
||||
func._vendor_metadata = metadata
|
||||
|
||||
passthru_logmessage = _LE('vendor_passthru failed with method %s')
|
||||
|
||||
@functools.wraps(func)
|
||||
@ -389,18 +410,46 @@ def passthru(method=None):
|
||||
return handle_passthru
|
||||
|
||||
|
||||
def passthru(method=None):
|
||||
return _passthru(method, driver_passthru=False)
|
||||
|
||||
|
||||
def driver_passthru(method=None):
|
||||
return _passthru(method, driver_passthru=True)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class VendorInterface(object):
|
||||
"""Interface for all vendor passthru functionality.
|
||||
|
||||
Additional vendor- or driver-specific capabilities should be implemented as
|
||||
private methods and invoked from vendor_passthru() or
|
||||
driver_vendor_passthru().
|
||||
Additional vendor- or driver-specific capabilities should be
|
||||
implemented as a method in the class inheriting from this class and
|
||||
use the @passthru or @driver_passthru decorators.
|
||||
|
||||
driver_vendor_passthru() is a blocking call - methods implemented here
|
||||
should be short-lived.
|
||||
Methods decorated with @driver_passthru should be short-lived because
|
||||
it is a blocking call.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
inst = super(VendorInterface, cls).__new__(cls, *args, **kwargs)
|
||||
|
||||
inst.vendor_routes = {}
|
||||
inst.driver_routes = {}
|
||||
|
||||
for name, ref in inspect.getmembers(inst, predicate=inspect.ismethod):
|
||||
vmeta = getattr(ref, '_vendor_metadata', None)
|
||||
dmeta = getattr(ref, '_driver_metadata', None)
|
||||
|
||||
if vmeta is not None:
|
||||
vmeta.metadata['func'] = ref
|
||||
inst.vendor_routes.update({vmeta.method: vmeta.metadata})
|
||||
|
||||
if dmeta is not None:
|
||||
dmeta.metadata['func'] = ref
|
||||
inst.driver_routes.update({dmeta.method: dmeta.metadata})
|
||||
|
||||
return inst
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_properties(self):
|
||||
"""Return the properties of the interface.
|
||||
@ -422,37 +471,6 @@ class VendorInterface(object):
|
||||
:raises: MissingParameterValue
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Receive requests for vendor-specific actions.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param kwargs: info for action.
|
||||
|
||||
:raises: UnsupportedDriverExtension if 'method' can not be mapped to
|
||||
the supported interfaces.
|
||||
:raises: InvalidParameterValue if kwargs does not contain 'method'.
|
||||
:raises: MissingParameterValue when a required parameter is missing
|
||||
"""
|
||||
|
||||
def driver_vendor_passthru(self, context, method, **kwargs):
|
||||
"""Handle top-level (ie, no node is specified) vendor actions.
|
||||
|
||||
These allow a vendor interface to expose additional cross-node API
|
||||
functionality.
|
||||
|
||||
VendorInterface subclasses are explicitly not required to implement
|
||||
this in order to maintain backwards compatibility with existing
|
||||
drivers.
|
||||
|
||||
:param context: a context for this action.
|
||||
:param method: an arbitrary string describing the action to be taken.
|
||||
:param kwargs: arbitrary parameters to the passthru method.
|
||||
"""
|
||||
raise exception.UnsupportedDriverExtension(
|
||||
_('Vendor interface does not support driver vendor_passthru '
|
||||
'method: %s') % method)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ManagementInterface(object):
|
||||
|
@ -305,13 +305,8 @@ class AgentDeploy(base.DeployInterface):
|
||||
|
||||
|
||||
class AgentVendorInterface(base.VendorInterface):
|
||||
|
||||
def __init__(self):
|
||||
self.vendor_routes = {
|
||||
'heartbeat': self.heartbeat
|
||||
}
|
||||
self.driver_routes = {
|
||||
'lookup': self._lookup,
|
||||
}
|
||||
self.supported_payload_versions = ['2']
|
||||
self._client = _get_client()
|
||||
|
||||
@ -333,31 +328,6 @@ class AgentVendorInterface(base.VendorInterface):
|
||||
"""
|
||||
pass
|
||||
|
||||
def driver_vendor_passthru(self, task, method, **kwargs):
|
||||
"""Handle top-level vendor actions.
|
||||
|
||||
A node that does not know its UUID should POST to this method.
|
||||
Given method, route the command to the appropriate private function.
|
||||
"""
|
||||
if method not in self.driver_routes:
|
||||
raise exception.InvalidParameterValue(_('No handler for method %s')
|
||||
% method)
|
||||
func = self.driver_routes[method]
|
||||
return func(task, **kwargs)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""A node that knows its UUID should heartbeat to this passthru.
|
||||
|
||||
It will get its node object back, with what Ironic thinks its provision
|
||||
state is and the target provision state is.
|
||||
"""
|
||||
method = kwargs['method'] # Existence checked in mixin
|
||||
if method not in self.vendor_routes:
|
||||
raise exception.InvalidParameterValue(_('No handler for method '
|
||||
'%s') % method)
|
||||
func = self.vendor_routes[method]
|
||||
return func(task, **kwargs)
|
||||
|
||||
@base.passthru()
|
||||
def heartbeat(self, task, **kwargs):
|
||||
"""Method for agent to periodically check in.
|
||||
@ -465,7 +435,8 @@ class AgentVendorInterface(base.VendorInterface):
|
||||
node.target_provision_state = states.NOSTATE
|
||||
node.save()
|
||||
|
||||
def _lookup(self, context, **kwargs):
|
||||
@base.driver_passthru()
|
||||
def lookup(self, context, **kwargs):
|
||||
"""Find a matching node for the agent.
|
||||
|
||||
Method to be called the first time a ramdisk agent checks in. This
|
||||
|
@ -112,13 +112,6 @@ class FakeVendorA(base.VendorInterface):
|
||||
def first_method(self, task, bar):
|
||||
return True if bar == 'baz' else False
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
method = kwargs.get('method')
|
||||
if method == 'first_method':
|
||||
bar = kwargs.get('bar')
|
||||
return self._private_method(task, bar)
|
||||
_raise_unsupported_error(method)
|
||||
|
||||
|
||||
class FakeVendorB(base.VendorInterface):
|
||||
"""Example implementation of a secondary vendor passthru."""
|
||||
@ -141,13 +134,6 @@ class FakeVendorB(base.VendorInterface):
|
||||
def second_method(self, task, bar):
|
||||
return True if bar == 'kazoo' else False
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
method = kwargs.get('method')
|
||||
if method == 'second_method':
|
||||
bar = kwargs.get('bar')
|
||||
return self._private_method(task, bar)
|
||||
_raise_unsupported_error(method)
|
||||
|
||||
|
||||
class FakeConsole(base.ConsoleInterface):
|
||||
"""Example implementation of a simple console interface."""
|
||||
|
@ -565,22 +565,10 @@ class IloConsoleInterface(ipmitool.IPMIShellinaboxConsole):
|
||||
|
||||
class IloPXEVendorPassthru(pxe.VendorPassthru):
|
||||
|
||||
@base.passthru()
|
||||
def pass_deploy_info(self, *args, **kwargs):
|
||||
ilo_common.set_boot_device(*args, **kwargs)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Calls a valid vendor passthru method.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param kwargs: kwargs containing the vendor passthru method and its
|
||||
parameters.
|
||||
"""
|
||||
method = kwargs['method']
|
||||
if method == 'pass_deploy_info':
|
||||
self.pass_deploy_info(task.node, 'NETWORK', True)
|
||||
return super(IloPXEVendorPassthru, self).vendor_passthru(task,
|
||||
**kwargs)
|
||||
@base.passthru(method='pass_deploy_info')
|
||||
def _continue_deploy(self, task, **kwargs):
|
||||
ilo_common.set_boot_device(task.node, 'NETWORK', True)
|
||||
super(IloPXEVendorPassthru, self)._continue_deploy(task, **kwargs)
|
||||
|
||||
|
||||
class VendorPassthru(base.VendorInterface):
|
||||
@ -611,7 +599,7 @@ class VendorPassthru(base.VendorInterface):
|
||||
"Unsupported method (%s) passed to iLO driver.")
|
||||
% method)
|
||||
|
||||
@base.passthru('pass_deploy_info')
|
||||
@base.passthru(method='pass_deploy_info')
|
||||
@task_manager.require_exclusive_lock
|
||||
def _continue_deploy(self, task, **kwargs):
|
||||
"""Continues the iSCSI deployment from where ramdisk left off.
|
||||
@ -660,14 +648,3 @@ class VendorPassthru(base.VendorInterface):
|
||||
{'instance': node.instance_uuid, 'error': e})
|
||||
msg = _('Failed to continue iSCSI deployment.')
|
||||
iscsi_deploy.set_failed_state(task, msg)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Calls a valid vendor passthru method.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param kwargs: kwargs containing the vendor passthru method and its
|
||||
parameters.
|
||||
"""
|
||||
method = kwargs['method']
|
||||
if method == 'pass_deploy_info':
|
||||
self._continue_deploy(task, **kwargs)
|
||||
|
@ -891,29 +891,6 @@ class VendorPassthru(base.VendorInterface):
|
||||
% method)
|
||||
_parse_driver_info(task.node)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Receive requests for vendor-specific actions.
|
||||
|
||||
Valid methods:
|
||||
* send_raw
|
||||
* bmc_reset
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:param kwargs: info for action.
|
||||
|
||||
:raises: InvalidParameterValue if required IPMI credentials
|
||||
are missing.
|
||||
:raises: IPMIFailure if ipmitool fails for any method.
|
||||
:raises: MissingParameterValue when a required parameter is missing
|
||||
|
||||
"""
|
||||
|
||||
method = kwargs['method']
|
||||
if method == 'send_raw':
|
||||
return self.send_raw(task, kwargs.get('raw_bytes'))
|
||||
elif method == 'bmc_reset':
|
||||
return self.bmc_reset(task, warm=kwargs.get('warm', True))
|
||||
|
||||
|
||||
class IPMIShellinaboxConsole(base.ConsoleInterface):
|
||||
"""A ConsoleInterface that uses ipmitool and shellinabox."""
|
||||
|
@ -452,7 +452,7 @@ class VendorPassthru(base.VendorInterface):
|
||||
"Unsupported method (%s) passed to PXE driver.")
|
||||
% method)
|
||||
|
||||
@base.passthru('pass_deploy_info')
|
||||
@base.passthru(method='pass_deploy_info')
|
||||
@task_manager.require_exclusive_lock
|
||||
def _continue_deploy(self, task, **kwargs):
|
||||
"""Continues the deployment of baremetal node over iSCSI.
|
||||
@ -494,13 +494,3 @@ class VendorPassthru(base.VendorInterface):
|
||||
{'instance': node.instance_uuid, 'error': e})
|
||||
msg = _('Failed to continue iSCSI deployment.')
|
||||
iscsi_deploy.set_failed_state(task, msg)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Invokes a vendor passthru method.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param kwargs: kwargs containins the method name and its parameters.
|
||||
"""
|
||||
method = kwargs['method']
|
||||
if method == 'pass_deploy_info':
|
||||
self._continue_deploy(task, **kwargs)
|
||||
|
@ -55,8 +55,6 @@ CONF.register_opts(opts, opt_group)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
VENDOR_PASSTHRU_METHODS = ['attach_volume', 'set_node_vlan_id']
|
||||
|
||||
_BOOT_DEVICES_MAP = {
|
||||
boot_devices.DISK: 'hd0',
|
||||
boot_devices.PXE: 'pxe',
|
||||
@ -416,19 +414,8 @@ class VendorPassthru(base.VendorInterface):
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task, **kwargs):
|
||||
method = kwargs['method']
|
||||
if method not in VENDOR_PASSTHRU_METHODS:
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"Unsupported method (%s) passed to SeaMicro driver.")
|
||||
% method)
|
||||
_parse_driver_info(task.node)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Dispatch vendor specific method calls."""
|
||||
method = kwargs['method']
|
||||
if method in VENDOR_PASSTHRU_METHODS:
|
||||
return getattr(self, method)(task, **kwargs)
|
||||
|
||||
@base.passthru()
|
||||
def set_node_vlan_id(self, task, **kwargs):
|
||||
"""Sets a untagged vlan id for NIC 0 of node.
|
||||
|
@ -22,15 +22,6 @@ from ironic.openstack.common import log as logging
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _raise_unsupported_error(method=None):
|
||||
if method:
|
||||
raise exception.UnsupportedDriverExtension(_(
|
||||
"Unsupported method (%s) passed through to vendor extension.")
|
||||
% method)
|
||||
raise exception.MissingParameterValue(_(
|
||||
"Method not specified when calling vendor extension."))
|
||||
|
||||
|
||||
class MixinVendorInterface(base.VendorInterface):
|
||||
"""Wrapper around multiple VendorInterfaces."""
|
||||
|
||||
@ -47,10 +38,53 @@ class MixinVendorInterface(base.VendorInterface):
|
||||
"""
|
||||
self.mapping = mapping
|
||||
self.driver_level_mapping = driver_passthru_mapping or {}
|
||||
self.vendor_routes = self._build_routes(self.mapping)
|
||||
self.driver_routes = self._build_routes(self.driver_level_mapping,
|
||||
driver_passthru=True)
|
||||
|
||||
def _map(self, **kwargs):
|
||||
def _build_routes(self, map_dict, driver_passthru=False):
|
||||
"""Build the mapping for the vendor calls.
|
||||
|
||||
Build the mapping between the given methods and the corresponding
|
||||
method metadata.
|
||||
|
||||
:param map_dict: dict of {'method': interface} specifying how
|
||||
to map multiple vendor calls to interfaces.
|
||||
:param driver_passthru: Boolean value. Whether build the mapping
|
||||
to the node vendor passthru or driver
|
||||
vendor passthru.
|
||||
"""
|
||||
d = {}
|
||||
for method_name in map_dict:
|
||||
iface = map_dict[method_name]
|
||||
if driver_passthru:
|
||||
driver_methods = iface.driver_routes
|
||||
else:
|
||||
driver_methods = iface.vendor_routes
|
||||
|
||||
try:
|
||||
d.update({method_name: driver_methods[method_name]})
|
||||
except KeyError:
|
||||
pass
|
||||
return d
|
||||
|
||||
def _get_route(self, **kwargs):
|
||||
"""Return the driver interface which contains the given method.
|
||||
|
||||
:param method: The name of the vendor method.
|
||||
"""
|
||||
method = kwargs.get('method')
|
||||
return self.mapping.get(method) or _raise_unsupported_error(method)
|
||||
if not method:
|
||||
raise exception.MissingParameterValue(
|
||||
_("Method not specified when calling vendor extension."))
|
||||
|
||||
try:
|
||||
route = self.mapping[method]
|
||||
except KeyError:
|
||||
raise exception.InvalidParameterValue(
|
||||
_('No handler for method %s') % method)
|
||||
|
||||
return route
|
||||
|
||||
def get_properties(self):
|
||||
"""Return the properties from all the VendorInterfaces.
|
||||
@ -73,39 +107,9 @@ class MixinVendorInterface(base.VendorInterface):
|
||||
:raisee: MissingParameterValue if missing parameters in kwargs.
|
||||
|
||||
"""
|
||||
route = self._map(**kwargs)
|
||||
route = self._get_route(**kwargs)
|
||||
route.validate(*args, **kwargs)
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
"""Call vendor_passthru on the appropriate interface only.
|
||||
|
||||
Returns or raises according to the requested vendor_passthru method.
|
||||
|
||||
:raises: UnsupportedDriverExtension if 'method' can not be mapped to
|
||||
the supported interfaces.
|
||||
:raises: MissingParameterValue if kwargs does not contain 'method'.
|
||||
|
||||
"""
|
||||
route = self._map(**kwargs)
|
||||
return route.vendor_passthru(task, **kwargs)
|
||||
|
||||
def driver_vendor_passthru(self, context, method, **kwargs):
|
||||
"""Handle top-level vendor actions.
|
||||
|
||||
Call driver_vendor_passthru on a mapped interface based on the
|
||||
specified method.
|
||||
|
||||
Returns or raises according to the requested driver_vendor_passthru
|
||||
|
||||
:raises: UnsupportedDriverExtension if 'method' cannot be mapped to
|
||||
a supported interface.
|
||||
"""
|
||||
iface = self.driver_level_mapping.get(method)
|
||||
if iface is None:
|
||||
_raise_unsupported_error(method)
|
||||
|
||||
return iface.driver_vendor_passthru(context, method, **kwargs)
|
||||
|
||||
|
||||
def get_node_mac_addresses(task):
|
||||
"""Get all MAC addresses for the ports belonging to this task's node.
|
||||
|
@ -538,7 +538,7 @@ class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
self.context,
|
||||
node.uuid, 'unsupported_method', info)
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.UnsupportedDriverExtension,
|
||||
self.assertEqual(exception.InvalidParameterValue,
|
||||
exc.exc_info[0])
|
||||
|
||||
node.refresh()
|
||||
@ -604,20 +604,45 @@ class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
# Verify reservation has been cleared.
|
||||
self.assertIsNone(node.reservation)
|
||||
|
||||
@mock.patch.object(task_manager, 'acquire')
|
||||
def test_vendor_passthru_backwards_compat(self, acquire_mock):
|
||||
node = obj_utils.create_test_node(self.context, driver='fake')
|
||||
vendor_passthru_ref = mock.Mock()
|
||||
self._start_service()
|
||||
|
||||
driver = mock.Mock()
|
||||
driver.vendor.vendor_routes = {}
|
||||
driver.vendor.vendor_passthru = vendor_passthru_ref
|
||||
|
||||
task = mock.Mock()
|
||||
task.node = node
|
||||
task.driver = driver
|
||||
|
||||
acquire_mock.return_value.__enter__.return_value = task
|
||||
|
||||
self.service.vendor_passthru(
|
||||
self.context, node.uuid, 'test_method', {'bar': 'baz'})
|
||||
|
||||
task.spawn_after.assert_called_once_with(mock.ANY, vendor_passthru_ref,
|
||||
task, bar='baz', method='test_method')
|
||||
|
||||
def test_driver_vendor_passthru_success(self):
|
||||
expected = {'foo': 'bar'}
|
||||
self.driver.vendor = vendor = mock.Mock()
|
||||
vendor.driver_vendor_passthru.return_value = expected
|
||||
self.driver.vendor = mock.Mock(spec=drivers_base.VendorInterface)
|
||||
test_method = mock.MagicMock(return_value=expected)
|
||||
self.driver.vendor.driver_routes = {'test_method':
|
||||
{'func': test_method}}
|
||||
self.service.init_host()
|
||||
got = self.service.driver_vendor_passthru(self.context,
|
||||
'fake',
|
||||
'test_method',
|
||||
{'test': 'arg'})
|
||||
|
||||
# Assert that the vendor interface has no custom
|
||||
# driver_vendor_passthru()
|
||||
self.assertFalse(hasattr(self.driver.vendor, 'driver_vendor_passthru'))
|
||||
self.assertEqual(expected, got)
|
||||
vendor.driver_vendor_passthru.assert_called_once_with(
|
||||
mock.ANY,
|
||||
method='test_method',
|
||||
test='arg')
|
||||
test_method.assert_called_once_with(mock.ANY, test='arg')
|
||||
|
||||
def test_driver_vendor_passthru_vendor_interface_not_supported(self):
|
||||
# Test for when no vendor interface is set at all
|
||||
@ -633,7 +658,7 @@ class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
self.assertEqual(exception.UnsupportedDriverExtension,
|
||||
exc.exc_info[0])
|
||||
|
||||
def test_driver_vendor_passthru_not_supported(self):
|
||||
def test_driver_vendor_passthru_method_not_supported(self):
|
||||
# Test for when the vendor interface is set, but hasn't passed a
|
||||
# driver_passthru_mapping to MixinVendorInterface
|
||||
self.service.init_host()
|
||||
@ -644,7 +669,7 @@ class VendorPassthruTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
'test_method',
|
||||
{})
|
||||
# Compare true exception hidden by @messaging.expected_exceptions
|
||||
self.assertEqual(exception.UnsupportedDriverExtension,
|
||||
self.assertEqual(exception.InvalidParameterValue,
|
||||
exc.exc_info[0])
|
||||
|
||||
def test_driver_vendor_passthru_driver_not_found(self):
|
||||
|
@ -473,14 +473,29 @@ class IloPXEVendorPassthruTestCase(db_base.DbTestCase):
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='pxe_ilo', driver_info=INFO_DICT)
|
||||
|
||||
@mock.patch.object(pxe.VendorPassthru, 'vendor_passthru')
|
||||
def test_vendor_routes(self):
|
||||
expected = ['pass_deploy_info']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
vendor_routes = task.driver.vendor.vendor_routes
|
||||
self.assertIsInstance(vendor_routes, dict)
|
||||
self.assertEqual(expected, list(vendor_routes))
|
||||
|
||||
def test_driver_routes(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
driver_routes = task.driver.vendor.driver_routes
|
||||
self.assertIsInstance(driver_routes, dict)
|
||||
self.assertEqual({}, driver_routes)
|
||||
|
||||
@mock.patch.object(pxe.VendorPassthru, '_continue_deploy')
|
||||
@mock.patch.object(ilo_common, 'set_boot_device')
|
||||
def test_vendorpassthru(self, set_persistent_mock,
|
||||
pxe_vendorpassthru_mock):
|
||||
kwargs = {'method': 'pass_deploy_info', 'address': '123456'}
|
||||
kwargs = {'address': '123456'}
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.provision_state = states.DEPLOYWAIT
|
||||
task.driver.vendor.vendor_passthru(task, **kwargs)
|
||||
task.driver.vendor._continue_deploy(task, **kwargs)
|
||||
set_persistent_mock.assert_called_with(task.node, 'NETWORK', True)
|
||||
pxe_vendorpassthru_mock.assert_called_once_with(task, **kwargs)
|
||||
|
@ -21,7 +21,6 @@ from ironic.common import keystone
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base as driver_base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic import objects
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
@ -168,7 +167,7 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
}
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.passthru._lookup,
|
||||
self.passthru.lookup,
|
||||
task.context,
|
||||
**kwargs)
|
||||
|
||||
@ -193,26 +192,26 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
}
|
||||
find_mock.return_value = self.node
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
node = self.passthru._lookup(task.context, **kwargs)
|
||||
node = self.passthru.lookup(task.context, **kwargs)
|
||||
self.assertEqual(self.node, node['node'])
|
||||
|
||||
def test_lookup_v2_missing_inventory(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.passthru._lookup,
|
||||
self.passthru.lookup,
|
||||
task.context)
|
||||
|
||||
def test_lookup_v2_empty_inventory(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.passthru._lookup,
|
||||
self.passthru.lookup,
|
||||
task.context,
|
||||
inventory={})
|
||||
|
||||
def test_lookup_v2_empty_interfaces(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.NodeNotFound,
|
||||
self.passthru._lookup,
|
||||
self.passthru.lookup,
|
||||
task.context,
|
||||
version='2',
|
||||
inventory={'interfaces': []})
|
||||
@ -348,45 +347,18 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
self.passthru.heartbeat(task, **kwargs)
|
||||
failed_mock.assert_called_once_with(task, mock.ANY)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.heartbeat', autospec=True)
|
||||
def test_vendor_passthru_heartbeat(self, mock_heartbeat):
|
||||
kwargs = {
|
||||
'method': 'heartbeat',
|
||||
}
|
||||
self.passthru.vendor_routes['heartbeat'] = (
|
||||
driver_base.passthru('heartbeat')(mock_heartbeat))
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=True) as task:
|
||||
self.passthru.vendor_passthru(task, **kwargs)
|
||||
mock_heartbeat.assert_called_once_with(task, **kwargs)
|
||||
def test_vendor_passthru_vendor_routes(self):
|
||||
expected = ['heartbeat']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
vendor_routes = task.driver.vendor.vendor_routes
|
||||
self.assertIsInstance(vendor_routes, dict)
|
||||
self.assertEqual(expected, list(vendor_routes))
|
||||
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.heartbeat', autospec=True)
|
||||
def test_vendor_passthru_heartbeat_ironic_exc(self, mock_heartbeat):
|
||||
mock_heartbeat.side_effect = exception.IronicException()
|
||||
kwargs = {
|
||||
'method': 'heartbeat',
|
||||
}
|
||||
self.passthru.vendor_routes['heartbeat'] = (
|
||||
driver_base.passthru('heartbeat')(mock_heartbeat))
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=True) as task:
|
||||
self.assertRaises(exception.IronicException,
|
||||
self.passthru.vendor_passthru, task, **kwargs)
|
||||
mock_heartbeat.assert_called_once_with(task, **kwargs)
|
||||
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'.heartbeat', autospec=True)
|
||||
def test_vendor_passthru_heartbeat_exception(self, mock_heartbeat):
|
||||
mock_heartbeat.side_effect = KeyError()
|
||||
kwargs = {
|
||||
'method': 'heartbeat',
|
||||
}
|
||||
self.passthru.vendor_routes['heartbeat'] = (
|
||||
driver_base.passthru('heartbeat')(mock_heartbeat))
|
||||
with task_manager.acquire(
|
||||
self.context, self.node['uuid'], shared=True) as task:
|
||||
self.assertRaises(exception.VendorPassthruException,
|
||||
self.passthru.vendor_passthru, task, **kwargs)
|
||||
mock_heartbeat.assert_called_once_with(task, **kwargs)
|
||||
def test_vendor_passthru_driver_routes(self):
|
||||
expected = ['lookup']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
driver_routes = task.driver.vendor.driver_routes
|
||||
self.assertIsInstance(driver_routes, dict)
|
||||
self.assertEqual(expected, list(driver_routes))
|
||||
|
@ -24,30 +24,21 @@ class FakeVendorInterface(driver_base.VendorInterface):
|
||||
def get_properties(self):
|
||||
pass
|
||||
|
||||
@driver_base.passthru('noexception')
|
||||
def _noexception(self):
|
||||
@driver_base.passthru()
|
||||
def noexception(self):
|
||||
return "Fake"
|
||||
|
||||
@driver_base.passthru('ironicexception')
|
||||
def _ironicexception(self):
|
||||
@driver_base.passthru()
|
||||
def ironicexception(self):
|
||||
raise exception.IronicException("Fake!")
|
||||
|
||||
@driver_base.passthru('normalexception')
|
||||
def _normalexception(self):
|
||||
@driver_base.passthru()
|
||||
def normalexception(self):
|
||||
raise Exception("Fake!")
|
||||
|
||||
def validate(self, task, **kwargs):
|
||||
pass
|
||||
|
||||
def vendor_passthru(self, task, **kwargs):
|
||||
method = kwargs['method']
|
||||
if method == "noexception":
|
||||
self._noexception()
|
||||
elif method == "ironicexception":
|
||||
self._ironicexception()
|
||||
elif method == "normalexception":
|
||||
self._normalexception()
|
||||
|
||||
|
||||
class PassthruDecoratorTestCase(base.TestCase):
|
||||
|
||||
@ -57,17 +48,17 @@ class PassthruDecoratorTestCase(base.TestCase):
|
||||
driver_base.LOG = mock.Mock()
|
||||
|
||||
def test_passthru_noexception(self):
|
||||
result = self.fvi._noexception()
|
||||
result = self.fvi.noexception()
|
||||
self.assertEqual("Fake", result)
|
||||
|
||||
def test_passthru_ironicexception(self):
|
||||
self.assertRaises(exception.IronicException,
|
||||
self.fvi.vendor_passthru, mock.ANY, method="ironicexception")
|
||||
self.fvi.ironicexception, mock.ANY)
|
||||
driver_base.LOG.exception.assert_called_with(
|
||||
mock.ANY, 'ironicexception')
|
||||
|
||||
def test_passthru_nonironicexception(self):
|
||||
self.assertRaises(exception.VendorPassthruException,
|
||||
self.fvi.vendor_passthru, mock.ANY, method="normalexception")
|
||||
self.fvi.normalexception, mock.ANY)
|
||||
driver_base.LOG.exception.assert_called_with(
|
||||
mock.ANY, 'normalexception')
|
||||
|
@ -1074,10 +1074,8 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
||||
def test_vendor_passthru_call_send_raw_bytes(self, raw_bytes_mock):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.driver.vendor.vendor_passthru(task,
|
||||
method='send_raw',
|
||||
raw_bytes='0x00 0x01')
|
||||
raw_bytes_mock.assert_called_once_with(task, '0x00 0x01')
|
||||
self.driver.vendor.send_raw(task, raw_bytes='0x00 0x01')
|
||||
raw_bytes_mock.assert_called_once_with(task, raw_bytes='0x00 0x01')
|
||||
|
||||
def test_vendor_passthru_validate__bmc_reset_good(self):
|
||||
with task_manager.acquire(self.context, self.node['uuid']) as task:
|
||||
@ -1096,32 +1094,35 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
||||
method='bmc_reset',
|
||||
warm=False)
|
||||
|
||||
@mock.patch.object(ipmi.VendorPassthru, 'bmc_reset')
|
||||
def test_vendor_passthru_call_bmc_reset(self, bmc_mock):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.driver.vendor.vendor_passthru(task,
|
||||
method='bmc_reset')
|
||||
bmc_mock.assert_called_once_with(task, warm=True)
|
||||
|
||||
@mock.patch.object(ipmi.VendorPassthru, 'bmc_reset')
|
||||
def test_vendor_passthru_call_bmc_reset_warm(self, bmc_mock):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.driver.vendor.vendor_passthru(task,
|
||||
method='bmc_reset',
|
||||
warm=True)
|
||||
self.driver.vendor.bmc_reset(task, warm=True)
|
||||
bmc_mock.assert_called_once_with(task, warm=True)
|
||||
|
||||
@mock.patch.object(ipmi.VendorPassthru, 'bmc_reset')
|
||||
def test_vendor_passthru_call_bmc_reset_cold(self, bmc_mock):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=False) as task:
|
||||
self.driver.vendor.vendor_passthru(task,
|
||||
method='bmc_reset',
|
||||
warm=False)
|
||||
self.driver.vendor.bmc_reset(task, warm=False)
|
||||
bmc_mock.assert_called_once_with(task, warm=False)
|
||||
|
||||
def test_vendor_passthru_vendor_routes(self):
|
||||
expected = ['send_raw', 'bmc_reset']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
vendor_routes = task.driver.vendor.vendor_routes
|
||||
self.assertIsInstance(vendor_routes, dict)
|
||||
self.assertEqual(sorted(expected), sorted(vendor_routes))
|
||||
|
||||
def test_vendor_passthru_driver_routes(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
driver_routes = task.driver.vendor.driver_routes
|
||||
self.assertIsInstance(driver_routes, dict)
|
||||
self.assertEqual({}, driver_routes)
|
||||
|
||||
@mock.patch.object(console_utils, 'start_shellinabox_console',
|
||||
autospec=True)
|
||||
def test_start_console(self, mock_exec):
|
||||
|
@ -606,9 +606,9 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
fake_deploy))
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.vendor.vendor_passthru(
|
||||
task, method='pass_deploy_info', address='123456',
|
||||
iqn='aaa-bbb', key='fake-56789')
|
||||
task.driver.vendor._continue_deploy(
|
||||
task, address='123456', iqn='aaa-bbb', key='fake-56789')
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual(states.ACTIVE, self.node.provision_state)
|
||||
self.assertEqual(states.POWER_ON, self.node.power_state)
|
||||
@ -636,9 +636,9 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
fake_deploy))
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.vendor.vendor_passthru(
|
||||
task, method='pass_deploy_info', address='123456',
|
||||
iqn='aaa-bbb', key='fake-56789')
|
||||
task.driver.vendor._continue_deploy(
|
||||
task, address='123456', iqn='aaa-bbb', key='fake-56789')
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual(states.DEPLOYFAIL, self.node.provision_state)
|
||||
self.assertEqual(states.POWER_OFF, self.node.power_state)
|
||||
@ -662,10 +662,10 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
fake_deploy))
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.vendor.vendor_passthru(
|
||||
task, method='pass_deploy_info', address='123456',
|
||||
iqn='aaa-bbb', key='fake-56789',
|
||||
error='test ramdisk error')
|
||||
task.driver.vendor._continue_deploy(
|
||||
task, address='123456', iqn='aaa-bbb',
|
||||
key='fake-56789', error='test ramdisk error')
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual(states.DEPLOYFAIL, self.node.provision_state)
|
||||
self.assertEqual(states.POWER_OFF, self.node.power_state)
|
||||
@ -680,10 +680,10 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
self.node.save()
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.vendor.vendor_passthru(
|
||||
task, method='pass_deploy_info', address='123456',
|
||||
iqn='aaa-bbb', key='fake-56789',
|
||||
error='test ramdisk error')
|
||||
task.driver.vendor._continue_deploy(
|
||||
task, address='123456', iqn='aaa-bbb',
|
||||
key='fake-56789', error='test ramdisk error')
|
||||
|
||||
self.node.refresh()
|
||||
self.assertEqual('FAKE', self.node.provision_state)
|
||||
self.assertEqual(states.POWER_ON, self.node.power_state)
|
||||
@ -692,13 +692,28 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
with mock.patch.object(task.driver.vendor,
|
||||
'_continue_deploy') as _cont_deploy_mock:
|
||||
task.driver.vendor.vendor_passthru(task,
|
||||
method='pass_deploy_info', address='123456', iqn='aaa-bbb',
|
||||
key='fake-56789')
|
||||
task.driver.vendor._continue_deploy(
|
||||
task, address='123456', iqn='aaa-bbb', key='fake-56789')
|
||||
|
||||
# lock elevated w/o exception
|
||||
self.assertEqual(1, _cont_deploy_mock.call_count,
|
||||
"_continue_deploy was not called once.")
|
||||
|
||||
def test_vendor_routes(self):
|
||||
expected = ['pass_deploy_info']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
vendor_routes = task.driver.vendor.vendor_routes
|
||||
self.assertIsInstance(vendor_routes, dict)
|
||||
self.assertEqual(expected, list(vendor_routes))
|
||||
|
||||
def test_driver_routes(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
driver_routes = task.driver.vendor.driver_routes
|
||||
self.assertIsInstance(driver_routes, dict)
|
||||
self.assertEqual({}, driver_routes)
|
||||
|
||||
|
||||
@mock.patch.object(utils, 'unlink_without_raise')
|
||||
@mock.patch.object(iscsi_deploy, 'destroy_images')
|
||||
|
@ -293,6 +293,21 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
self.assertEqual(expected, task.driver.get_properties())
|
||||
|
||||
def test_vendor_routes(self):
|
||||
expected = ['set_node_vlan_id', 'attach_volume']
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
vendor_routes = task.driver.vendor.vendor_routes
|
||||
self.assertIsInstance(vendor_routes, dict)
|
||||
self.assertEqual(sorted(expected), sorted(vendor_routes))
|
||||
|
||||
def test_driver_routes(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
driver_routes = task.driver.vendor.driver_routes
|
||||
self.assertIsInstance(driver_routes, dict)
|
||||
self.assertEqual({}, driver_routes)
|
||||
|
||||
@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'],
|
||||
@ -390,26 +405,17 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
def test_vendor_passthru_validate_good(self, mock_info):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=True) as task:
|
||||
for method in seamicro.VENDOR_PASSTHRU_METHODS:
|
||||
for method in task.driver.vendor.vendor_routes:
|
||||
task.driver.vendor.validate(task, **{'method': method})
|
||||
self.assertEqual(len(seamicro.VENDOR_PASSTHRU_METHODS),
|
||||
self.assertEqual(len(task.driver.vendor.vendor_routes),
|
||||
mock_info.call_count)
|
||||
|
||||
@mock.patch.object(seamicro, '_parse_driver_info')
|
||||
def test_vendor_passthru_validate_fail(self, mock_info):
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=True) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.vendor.validate,
|
||||
task, **{'method': 'invalid_method'})
|
||||
self.assertFalse(mock_info.called)
|
||||
|
||||
@mock.patch.object(seamicro, '_parse_driver_info')
|
||||
def test_vendor_passthru_validate_parse_driver_info_fail(self, mock_info):
|
||||
mock_info.side_effect = exception.InvalidParameterValue("bad")
|
||||
with task_manager.acquire(self.context, self.node['uuid'],
|
||||
shared=True) as task:
|
||||
method = seamicro.VENDOR_PASSTHRU_METHODS[0]
|
||||
method = list(task.driver.vendor.vendor_routes)[0]
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.vendor.validate,
|
||||
task, **{'method': method})
|
||||
@ -422,8 +428,8 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = self.Server(active="true")
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'vlan_id': vlan_id, 'method': 'set_node_vlan_id'}
|
||||
task.driver.vendor.vendor_passthru(task, **kwargs)
|
||||
kwargs = {'vlan_id': vlan_id}
|
||||
task.driver.vendor.set_node_vlan_id(task, **kwargs)
|
||||
mock_get_server.assert_called_once_with(info)
|
||||
|
||||
def test_set_node_vlan_id_no_input(self):
|
||||
@ -431,9 +437,8 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.vendor.vendor_passthru,
|
||||
task,
|
||||
**{'method': 'set_node_vlan_id'})
|
||||
task.driver.vendor.set_node_vlan_id,
|
||||
task, **{})
|
||||
|
||||
@mock.patch.object(seamicro, '_get_server')
|
||||
def test_set_node_vlan_id_fail(self, mock_get_server):
|
||||
@ -447,11 +452,10 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = server
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'vlan_id': vlan_id, 'method': 'set_node_vlan_id'}
|
||||
kwargs = {'vlan_id': vlan_id}
|
||||
self.assertRaises(exception.IronicException,
|
||||
task.driver.vendor.vendor_passthru,
|
||||
task,
|
||||
**kwargs)
|
||||
task.driver.vendor.set_node_vlan_id,
|
||||
task, **kwargs)
|
||||
|
||||
mock_get_server.assert_called_once_with(info)
|
||||
|
||||
@ -465,8 +469,8 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = self.Server(active="true")
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'volume_id': volume_id, 'method': 'attach_volume'}
|
||||
task.driver.vendor.vendor_passthru(task, **kwargs)
|
||||
kwargs = {'volume_id': volume_id}
|
||||
task.driver.vendor.attach_volume(task, **kwargs)
|
||||
mock_get_server.assert_called_once_with(info)
|
||||
|
||||
@mock.patch.object(seamicro, '_get_server')
|
||||
@ -480,11 +484,10 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = self.Server(active="true")
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'volume_id': volume_id, 'method': 'attach_volume'}
|
||||
kwargs = {'volume_id': volume_id}
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.vendor.vendor_passthru,
|
||||
task,
|
||||
**kwargs)
|
||||
task.driver.vendor.attach_volume,
|
||||
task, **kwargs)
|
||||
|
||||
@mock.patch.object(seamicro, '_get_server')
|
||||
@mock.patch.object(seamicro, '_validate_volume')
|
||||
@ -501,11 +504,10 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = server
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'volume_id': volume_id, 'method': 'attach_volume'}
|
||||
kwargs = {'volume_id': volume_id}
|
||||
self.assertRaises(exception.IronicException,
|
||||
task.driver.vendor.vendor_passthru,
|
||||
task,
|
||||
**kwargs)
|
||||
task.driver.vendor.attach_volume,
|
||||
task, **kwargs)
|
||||
|
||||
mock_get_server.assert_called_once_with(info)
|
||||
|
||||
@ -523,8 +525,8 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
mock_get_server.return_value = self.Server(active="true")
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
kwargs = {'volume_size': volume_size, 'method': "attach_volume"}
|
||||
task.driver.vendor.vendor_passthru(task, **kwargs)
|
||||
kwargs = {'volume_size': volume_size}
|
||||
task.driver.vendor.attach_volume(task, **kwargs)
|
||||
mock_get_server.assert_called_once_with(info)
|
||||
mock_create_volume.assert_called_once_with(info, volume_size)
|
||||
|
||||
@ -533,8 +535,8 @@ class SeaMicroPowerDriverTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, info['uuid'],
|
||||
shared=False) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
task.driver.vendor.vendor_passthru, task,
|
||||
**{'method': 'attach_volume'})
|
||||
task.driver.vendor.attach_volume, task,
|
||||
**{})
|
||||
|
||||
@mock.patch.object(seamicro, '_get_server')
|
||||
def test_set_boot_device_good(self, mock_get_server):
|
||||
|
@ -48,66 +48,13 @@ class UtilsTestCase(db_base.DbTestCase):
|
||||
mock_fakea_validate.assert_called_once_with(method='first_method')
|
||||
|
||||
def test_vendor_interface_validate_bad_method(self):
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.driver.vendor.validate, method='fake_method')
|
||||
|
||||
def test_vendor_interface_validate_none_method(self):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.assertRaises(exception.MissingParameterValue,
|
||||
self.driver.vendor.validate)
|
||||
|
||||
@mock.patch.object(fake.FakeVendorA, 'vendor_passthru')
|
||||
@mock.patch.object(fake.FakeVendorB, 'vendor_passthru')
|
||||
def test_vendor_interface_route_valid_method(self, mock_fakeb_vendor,
|
||||
mock_fakea_vendor):
|
||||
self.driver.vendor.vendor_passthru('task',
|
||||
method='first_method',
|
||||
param1='fake1', param2='fake2')
|
||||
mock_fakea_vendor.assert_called_once_with('task',
|
||||
method='first_method',
|
||||
param1='fake1', param2='fake2')
|
||||
self.driver.vendor.vendor_passthru('task',
|
||||
method='second_method',
|
||||
param1='fake1', param2='fake2')
|
||||
mock_fakeb_vendor.assert_called_once_with('task',
|
||||
method='second_method',
|
||||
param1='fake1', param2='fake2')
|
||||
|
||||
def test_driver_passthru_mixin_success(self):
|
||||
vendor_a = fake.FakeVendorA()
|
||||
vendor_a.driver_vendor_passthru = mock.Mock()
|
||||
vendor_b = fake.FakeVendorB()
|
||||
vendor_b.driver_vendor_passthru = mock.Mock()
|
||||
driver_vendor_mapping = {
|
||||
'method_a': vendor_a,
|
||||
'method_b': vendor_b,
|
||||
}
|
||||
mixed_vendor = driver_utils.MixinVendorInterface(
|
||||
{},
|
||||
driver_vendor_mapping)
|
||||
mixed_vendor.driver_vendor_passthru('context',
|
||||
'method_a',
|
||||
param1='p1')
|
||||
vendor_a.driver_vendor_passthru.assert_called_once_with(
|
||||
'context',
|
||||
'method_a',
|
||||
param1='p1')
|
||||
|
||||
def test_driver_passthru_mixin_unsupported(self):
|
||||
mixed_vendor = driver_utils.MixinVendorInterface({}, {})
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
mixed_vendor.driver_vendor_passthru,
|
||||
'context',
|
||||
'fake_method',
|
||||
param='p1')
|
||||
|
||||
def test_driver_passthru_mixin_unspecified(self):
|
||||
mixed_vendor = driver_utils.MixinVendorInterface({})
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
mixed_vendor.driver_vendor_passthru,
|
||||
'context',
|
||||
'fake_method',
|
||||
param='p1')
|
||||
|
||||
def test_get_node_mac_addresses(self):
|
||||
ports = []
|
||||
ports.append(
|
||||
|
Loading…
Reference in New Issue
Block a user