Simplify base interfaces in ironic.drivers.base

Move abstract definitions of get_properties and validate to BaseInterface
and make all interfaces inherit it. This has a side effect of them getting
clean steps methods, but they anyway won't be wired in due to a whitelist
of interfaces in the conductor.

The RAIDInterface contained a mistake: it didn't have an ABCMeta metaclass,
so its @abstractmethod definitions didn't work. I've implemented get_properties
for it to avoid breaking implementers.

Also added interface_type to all interfaces for consistency and for future
use in the driver composition work.

Change-Id: Ia6708247a99ecdcf73d83e66350e8c309090b7d4
Partial-Bug: #1524745
This commit is contained in:
Dmitry Tantsur 2016-11-04 17:40:42 +01:00
parent 8c6b8e2625
commit 565a0ed6b9
2 changed files with 46 additions and 163 deletions

View File

@ -171,6 +171,7 @@ class BareDriver(BaseDriver):
self.core_interfaces.append('network') self.core_interfaces.append('network')
@six.add_metaclass(abc.ABCMeta)
class BaseInterface(object): class BaseInterface(object):
"""A base interface implementing common functions for Driver Interfaces.""" """A base interface implementing common functions for Driver Interfaces."""
@ -182,6 +183,30 @@ class BaseInterface(object):
""" """
interface_type = 'base' interface_type = 'base'
"""Interface type, used for clean steps and logging."""
@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.
This method validates whether the 'driver_info' and/or 'instance_info'
properties of the task's node contains the required information for
this interface to function.
This method is often executed synchronously in API requests, so it
should not conduct long-running checks.
:param task: a TaskManager instance containing the node to act on.
:raises: InvalidParameterValue on malformed parameter(s)
:raises: MissingParameterValue on missing parameter(s)
"""
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
# Get the list of clean steps when the interface is initialized by # Get the list of clean steps when the interface is initialized by
@ -252,32 +277,10 @@ class BaseInterface(object):
return getattr(self, step['step'])(task) return getattr(self, step['step'])(task)
@six.add_metaclass(abc.ABCMeta)
class DeployInterface(BaseInterface): class DeployInterface(BaseInterface):
"""Interface for deploy-related actions.""" """Interface for deploy-related actions."""
interface_type = 'deploy' interface_type = 'deploy'
@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.
This method validates whether the 'driver_info' property of the
task's node contains the required information for this driver to
deploy images to the node. If invalid, raises an exception; otherwise
returns None.
:param task: a TaskManager instance containing the node to act on.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def deploy(self, task): def deploy(self, task):
"""Perform a deployment to the task's node. """Perform a deployment to the task's node.
@ -402,30 +405,9 @@ class DeployInterface(BaseInterface):
{'node': task.node.uuid, 'driver': task.node.driver}) {'node': task.node.uuid, 'driver': task.node.driver})
@six.add_metaclass(abc.ABCMeta) class BootInterface(BaseInterface):
class BootInterface(object):
"""Interface for boot-related actions.""" """Interface for boot-related actions."""
interface_type = 'boot'
@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 info for booting.
This method validates the driver-specific info for booting the
ramdisk and instance on the node. If invalid, raises an
exception; otherwise returns None.
:param task: a task from TaskManager.
:returns: None
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def prepare_ramdisk(self, task, ramdisk_params): def prepare_ramdisk(self, task, ramdisk_params):
@ -482,32 +464,10 @@ class BootInterface(object):
""" """
@six.add_metaclass(abc.ABCMeta)
class PowerInterface(BaseInterface): class PowerInterface(BaseInterface):
"""Interface for power-related actions.""" """Interface for power-related actions."""
interface_type = 'power' interface_type = 'power'
@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.
This method validates whether the 'driver_info' property of the
supplied node contains the required information for this driver to
manage the power state of the node. If invalid, raises an exception;
otherwise, returns None.
:param task: a TaskManager instance containing the node to act on.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def get_power_state(self, task): def get_power_state(self, task):
"""Return the power state of the task's node. """Return the power state of the task's node.
@ -538,30 +498,9 @@ class PowerInterface(BaseInterface):
""" """
@six.add_metaclass(abc.ABCMeta) class ConsoleInterface(BaseInterface):
class ConsoleInterface(object):
"""Interface for console-related actions.""" """Interface for console-related actions."""
interface_type = "console"
@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.
This method validates whether the 'driver_info' property of the
supplied node contains the required information for this driver to
provide console access to the Node. If invalid, raises an exception;
otherwise returns None.
:param task: a TaskManager instance containing the node to act on.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def start_console(self, task): def start_console(self, task):
@ -591,27 +530,9 @@ class ConsoleInterface(object):
""" """
@six.add_metaclass(abc.ABCMeta) class RescueInterface(BaseInterface):
class RescueInterface(object):
"""Interface for rescue-related actions.""" """Interface for rescue-related actions."""
interface_type = "rescue"
@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.
If invalid, raises an exception; otherwise returns None.
:param task: a TaskManager instance containing the node to act on.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def rescue(self, task): def rescue(self, task):
@ -714,8 +635,7 @@ def driver_passthru(http_methods, method=None, async=True, description=None,
description=description, attach=attach) description=description, attach=attach)
@six.add_metaclass(abc.ABCMeta) class VendorInterface(BaseInterface):
class VendorInterface(object):
"""Interface for all vendor passthru functionality. """Interface for all vendor passthru functionality.
Additional vendor- or driver-specific capabilities should be Additional vendor- or driver-specific capabilities should be
@ -725,6 +645,7 @@ class VendorInterface(object):
Methods decorated with @driver_passthru should be short-lived because Methods decorated with @driver_passthru should be short-lived because
it is a blocking call. it is a blocking call.
""" """
interface_type = "vendor"
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
super_new = super(VendorInterface, cls).__new__ super_new = super(VendorInterface, cls).__new__
@ -752,13 +673,6 @@ class VendorInterface(object):
return inst return inst
@abc.abstractmethod
def get_properties(self):
"""Return the properties of the interface.
:returns: dictionary of <property name>:<property description> entries.
"""
@abc.abstractmethod @abc.abstractmethod
def validate(self, task, method=None, **kwargs): def validate(self, task, method=None, **kwargs):
"""Validate vendor-specific actions. """Validate vendor-specific actions.
@ -788,29 +702,10 @@ class VendorInterface(object):
pass pass
@six.add_metaclass(abc.ABCMeta)
class ManagementInterface(BaseInterface): class ManagementInterface(BaseInterface):
"""Interface for management related actions.""" """Interface for management related actions."""
interface_type = 'management' interface_type = 'management'
@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.
If invalid, raises an exception; otherwise returns None.
:param task: a task from TaskManager.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def get_supported_boot_devices(self, task): def get_supported_boot_devices(self, task):
"""Get a list of the supported boot devices. """Get a list of the supported boot devices.
@ -899,31 +794,13 @@ class ManagementInterface(BaseInterface):
""" """
@six.add_metaclass(abc.ABCMeta) class InspectInterface(BaseInterface):
class InspectInterface(object):
"""Interface for inspection-related actions.""" """Interface for inspection-related actions."""
interface_type = 'inspect'
ESSENTIAL_PROPERTIES = {'memory_mb', 'local_gb', 'cpus', 'cpu_arch'} ESSENTIAL_PROPERTIES = {'memory_mb', 'local_gb', 'cpus', 'cpu_arch'}
"""The properties required by scheduler/deploy.""" """The properties required by scheduler/deploy."""
@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 inspection information.
If invalid, raises an exception; otherwise returns None.
:param task: a task from TaskManager.
:raises: InvalidParameterValue
:raises: MissingParameterValue
"""
@abc.abstractmethod @abc.abstractmethod
def inspect_hardware(self, task): def inspect_hardware(self, task):
"""Inspect hardware. """Inspect hardware.
@ -947,12 +824,12 @@ class RAIDInterface(BaseInterface):
with open(RAID_CONFIG_SCHEMA, 'r') as raid_schema_fobj: with open(RAID_CONFIG_SCHEMA, 'r') as raid_schema_fobj:
self.raid_schema = json.load(raid_schema_fobj) self.raid_schema = json.load(raid_schema_fobj)
@abc.abstractmethod
def get_properties(self): def get_properties(self):
"""Return the properties of the interface. """Return the properties of the interface.
:returns: dictionary of <property name>:<property description> entries. :returns: dictionary of <property name>:<property description> entries.
""" """
return {}
def validate(self, task): def validate(self, task):
"""Validates the RAID Interface. """Validates the RAID Interface.
@ -1035,7 +912,6 @@ class RAIDInterface(BaseInterface):
return raid.get_logical_disk_properties(self.raid_schema) return raid.get_logical_disk_properties(self.raid_schema)
@six.add_metaclass(abc.ABCMeta)
class NetworkInterface(BaseInterface): class NetworkInterface(BaseInterface):
"""Base class for network interfaces.""" """Base class for network interfaces."""

View File

@ -226,7 +226,14 @@ class CleanStepTestCase(base.TestCase):
method_args_mock = mock.MagicMock(spec_set=[]) method_args_mock = mock.MagicMock(spec_set=[])
task_mock = mock.MagicMock(spec_set=[]) task_mock = mock.MagicMock(spec_set=[])
class TestClass(driver_base.BaseInterface): class BaseTestClass(driver_base.BaseInterface):
def get_properties(self):
return {}
def validate(self, task):
pass
class TestClass(BaseTestClass):
interface_type = 'test' interface_type = 'test'
@driver_base.clean_step(priority=0) @driver_base.clean_step(priority=0)
@ -240,7 +247,7 @@ class CleanStepTestCase(base.TestCase):
def not_clean_method(self, task): def not_clean_method(self, task):
pass pass
class TestClass2(driver_base.BaseInterface): class TestClass2(BaseTestClass):
interface_type = 'test2' interface_type = 'test2'
@driver_base.clean_step(priority=0) @driver_base.clean_step(priority=0)
@ -254,7 +261,7 @@ class CleanStepTestCase(base.TestCase):
def not_clean_method2(self, task): def not_clean_method2(self, task):
pass pass
class TestClass3(driver_base.BaseInterface): class TestClass3(BaseTestClass):
interface_type = 'test3' interface_type = 'test3'
@driver_base.clean_step(priority=0, abortable=True, argsinfo={ @driver_base.clean_step(priority=0, abortable=True, argsinfo={