ironic/ironic/drivers/base.py
Devananda van der Veen 59d5bea14a Restructuring driver API and inheritance.
Based on discussions during and after the Ironic team meeting on June
03, regarding support for substantially different driver work flows,
this is a re-working of the internal driver API.

tl;dr: The strict separation of "control" and "deploy" driver was an
       artefact of the ipmi + pxe implementation used in nova-baremetal,
       and does not map on to all drivers. Furthermore, the prior
       implementation did not accurately represent the separation of
       "core", "standard", and "vendor-specific" driver functionality.

These changes impact the v1 API structure, but since that is largely not
implemented yet, this change does not attempt to affect the public API
itself.

Highlights:
- No more deploy + control driver; nodes have one and only one driver.
  This drops the deploy_driver and deploy_info parameters,
  and renames control_driver -> driver, and control_info -> driver_info.
- Interfaces for core, standard, and vendor functionality now clearly
  defined in the driver API.
- Improve Fake driver to demonstrate use of interfaces.
- Convert IPMI and SSH driver classes into interfaces, and move to
  drivers/modules/ directory.
- Stub for the pxe interfaces.
- Stub implementations of pxe+ipmi and pxe+ssh drivers.
- driver_info field uses more standard names, but requires
  driver-specific data to be in a nested object. Examples in
  tests/db/utils.py as before.

A separate doc change will follow this to update the API v1 spec.

Also includes some cosmetic cleanup of test_ssh.py and test_ipmi.py.

Change-Id: I057ede8e07b1b57010e81ef58415debe0ba8b934
2013-06-11 17:12:21 -07:00

241 lines
6.5 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- encoding: utf-8 -*-
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
"""
Abstract base classes for drivers.
"""
import abc
class BaseDriver(object):
"""Base class for all drivers.
Defines the `core`, `standardized`, and `vendor-specific` interfaces for
drivers. Any loadable driver must implement all `core` interfaces.
Actual implementation may instantiate one or more classes, as long as
the interfaces are appropriate.
"""
__metaclass__ = abc.ABCMeta
power = None
"""`Core` attribute for managing power state.
A reference to an instance of :class:PowerInterface.
"""
deploy = None
"""`Core` attribute for managing deployments.
A reference to an instance of :class:DeployInterface.
"""
console = None
"""`Standard` attribute for managing console access.
A reference to an instance of :class:ConsoleInterface.
May be None, if unsupported by a driver.
"""
rescue = None
"""`Standard` attribute for accessing rescue features.
A reference to an instance of :class:RescueInterface.
May be None, if unsupported by a driver.
"""
vendor = None
"""Attribute for accessing any vendor-specific extensions.
A reference to an instance of :class:VendorInterface.
May be None, if the driver does not implement any vendor extensions.
"""
@abc.abstractmethod
def __init__(self):
pass
class DeployInterface(object):
"""Interface for deploy-related actions."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, node):
"""Validate the driver-specific Node deployment info.
This method validates whether the 'driver_info' property of the
supplied node contains the required information for this driver to
deploy images to the node.
:param node: a single Node to validate.
:returns: True or False, depending on capabilities.
"""
@abc.abstractmethod
def deploy(self, task, node):
"""Perform a deployment to a node.
Given a node with complete metadata, deploy the indicated image
to the node.
:param task: a TaskManager instance.
:param node: the Node to act upon.
"""
@abc.abstractmethod
def tear_down(self, task, node):
"""Tear down a previous deployment.
Given a node that has been previously deployed to,
do all cleanup and tear down necessary to "un-deploy" that node.
:param task: a TaskManager instance.
:param node: the Node to act upon.
"""
class PowerInterface(object):
"""Interface for power-related actions."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, node):
"""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.
:param node: a single Node to validate.
:returns: True or False, depending on capabilities.
"""
@abc.abstractmethod
def get_power_state(self, task, node):
"""Return the power state of the node.
TODO
"""
@abc.abstractmethod
def set_power_state(self, task, node, power_state):
"""Set the power state of the node.
TODO
"""
@abc.abstractmethod
def reboot(self, task, node):
"""Perform a hard reboot of the node.
TODO
"""
class ConsoleInterface(object):
"""Interface for console-related actions."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, node):
"""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.
:param node: a single Node to validate.
:returns: True or False, depending on capabilities.
"""
@abc.abstractmethod
def start_console(self, task, node):
"""Start a remote console for the node.
TODO
"""
@abc.abstractmethod
def stop_console(self, task, node):
"""Stop the remote console session for the node.
TODO
"""
class RescueInterface(object):
"""Interface for rescue-related actions."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, node):
"""Validate the rescue info stored in the node' properties."""
@abc.abstractmethod
def rescue(self, task, node):
"""Boot the node into a rescue environment.
TODO
"""
@abc.abstractmethod
def unrescue(self, task, node):
"""Tear down the rescue environment, and return to normal.
TODO
"""
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().
"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def validate(self, node):
"""Validate vendor-specific info stored in the node' properties."""
@abc.abstractmethod
def vendor_passthru(self, task, node, *args, **kwargs):
"""Receive RPC requests for vendor-specific actions.
TODO: Mechanism for vendor_passthru and, in particular, where the
the interpretation of API requests will live, is still being
discussed.
One possible implementation of this method might look like::
method = args.get('method')
if not method:
raise ...
if hasattr(self, method):
callable = getattr(self, method)
self.callable(task, node, *args, **kwargs)
else:
raise ...
"""