Merge "Add ManagementInterface"
This commit is contained in:
commit
6be126ec98
42
ironic/common/boot_devices.py
Normal file
42
ironic/common/boot_devices.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Copyright 2014 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Mapping of boot devices used when requesting the system to boot
|
||||||
|
from an alternate device.
|
||||||
|
|
||||||
|
The options presented were based on the IPMItool chassis
|
||||||
|
bootdev command. You can find the documentation at:
|
||||||
|
http://linux.die.net/man/1/ipmitool
|
||||||
|
|
||||||
|
NOTE: This module does not include all the options from ipmitool because
|
||||||
|
they don't make sense in the limited context of Ironic right now.
|
||||||
|
"""
|
||||||
|
|
||||||
|
PXE = 'pxe'
|
||||||
|
"Boot from PXE boot"
|
||||||
|
|
||||||
|
DISK = 'disk'
|
||||||
|
"Boot from default Hard-drive"
|
||||||
|
|
||||||
|
CDROM = 'cdrom'
|
||||||
|
"Boot from CD/DVD"
|
||||||
|
|
||||||
|
BIOS = 'bios'
|
||||||
|
"Boot into BIOS setup"
|
||||||
|
|
||||||
|
SAFE = 'safe'
|
||||||
|
"Boot from default Hard-drive, request Safe Mode"
|
@ -69,6 +69,14 @@ class BaseDriver(object):
|
|||||||
May be None, if unsupported by a driver.
|
May be None, if unsupported by a driver.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
management = None
|
||||||
|
"""`Standard` attribute for management related features.
|
||||||
|
|
||||||
|
A reference to an instance of :class:ManagementInterface.
|
||||||
|
May be None, if unsupported by a driver.
|
||||||
|
"""
|
||||||
|
standard_interfaces.append('management')
|
||||||
|
|
||||||
vendor = None
|
vendor = None
|
||||||
"""Attribute for accessing any vendor-specific extensions.
|
"""Attribute for accessing any vendor-specific extensions.
|
||||||
|
|
||||||
@ -348,3 +356,55 @@ class VendorInterface(object):
|
|||||||
raise exception.UnsupportedDriverExtension(
|
raise exception.UnsupportedDriverExtension(
|
||||||
_('Vendor interface does not support driver vendor_passthru '
|
_('Vendor interface does not support driver vendor_passthru '
|
||||||
'method: %s') % method)
|
'method: %s') % method)
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class ManagementInterface(object):
|
||||||
|
"""Interface for management related actions."""
|
||||||
|
|
||||||
|
# TODO(lucasagomes): The 'node' parameter
|
||||||
|
# needs to be passed to validate() because of the
|
||||||
|
# ConductorManager.validate_driver_interfaces(). Remove it as part of
|
||||||
|
# https://bugs.launchpad.net/ironic/+bug/1312632.
|
||||||
|
@abc.abstractmethod
|
||||||
|
def validate(self, task, node):
|
||||||
|
"""Validate the driver-specific management information.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:param node: a single Node to validate.
|
||||||
|
:raises: InvalidParameterValue
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_supported_boot_devices(self):
|
||||||
|
"""Get a list of the supported boot devices.
|
||||||
|
|
||||||
|
:returns: A list with the supported boot devices defined
|
||||||
|
in :mod:`ironic.common.boot_devices`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def set_boot_device(self, task, device, **kwargs):
|
||||||
|
"""Set the boot device for a node.
|
||||||
|
|
||||||
|
Set the boot device to use on next reboot of the node.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:param device: the boot device, one of
|
||||||
|
:mod:`ironic.common.boot_devices`.
|
||||||
|
:param kwargs: extra driver-specific parameters.
|
||||||
|
:raises: InvalidParameterValue if an invalid boot device is
|
||||||
|
specified.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_boot_device(self, task):
|
||||||
|
"""Get the current boot device for a node.
|
||||||
|
|
||||||
|
Provides the current boot device of the node. Be aware that not
|
||||||
|
all drivers support this.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:returns: the boot device, one of :mod:`ironic.common.boot_devices`
|
||||||
|
or None if it is unknown.
|
||||||
|
"""
|
||||||
|
@ -42,6 +42,7 @@ class FakeDriver(base.BaseDriver):
|
|||||||
'second_method': self.b}
|
'second_method': self.b}
|
||||||
self.vendor = utils.MixinVendorInterface(self.mapping)
|
self.vendor = utils.MixinVendorInterface(self.mapping)
|
||||||
self.console = fake.FakeConsole()
|
self.console = fake.FakeConsole()
|
||||||
|
self.management = fake.FakeManagement()
|
||||||
|
|
||||||
|
|
||||||
class FakeIPMIToolDriver(base.BaseDriver):
|
class FakeIPMIToolDriver(base.BaseDriver):
|
||||||
|
@ -24,6 +24,7 @@ functionality between a power interface and a deploy interface, when both rely
|
|||||||
on seprate vendor_passthru methods.
|
on seprate vendor_passthru methods.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from ironic.common import boot_devices
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common import states
|
from ironic.common import states
|
||||||
from ironic.drivers import base
|
from ironic.drivers import base
|
||||||
@ -143,3 +144,21 @@ class FakeConsole(base.ConsoleInterface):
|
|||||||
|
|
||||||
def get_console(self, task, node):
|
def get_console(self, task, node):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeManagement(base.ManagementInterface):
|
||||||
|
"""Example implementation of a simple management interface."""
|
||||||
|
|
||||||
|
def validate(self, task, node):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_supported_boot_devices(self):
|
||||||
|
return [boot_devices.PXE]
|
||||||
|
|
||||||
|
def set_boot_device(self, task, device, **kwargs):
|
||||||
|
if device not in self.get_supported_boot_devices():
|
||||||
|
raise exception.InvalidParameterValue(_(
|
||||||
|
"Invalid boot device %s specified.") % device)
|
||||||
|
|
||||||
|
def get_boot_device(self, task):
|
||||||
|
return boot_devices.PXE
|
||||||
|
@ -752,6 +752,7 @@ class ManagerTestCase(tests_db_base.DbTestCase):
|
|||||||
node['uuid'])
|
node['uuid'])
|
||||||
expected = {'console': {'result': True},
|
expected = {'console': {'result': True},
|
||||||
'power': {'result': True},
|
'power': {'result': True},
|
||||||
|
'management': {'result': True},
|
||||||
'deploy': {'result': True}}
|
'deploy': {'result': True}}
|
||||||
self.assertEqual(expected, ret)
|
self.assertEqual(expected, ret)
|
||||||
|
|
||||||
|
@ -17,16 +17,18 @@
|
|||||||
|
|
||||||
"""Test class for Fake driver."""
|
"""Test class for Fake driver."""
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from ironic.common import boot_devices
|
||||||
from ironic.common import driver_factory
|
from ironic.common import driver_factory
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common import states
|
from ironic.common import states
|
||||||
from ironic.conductor import task_manager
|
from ironic.conductor import task_manager
|
||||||
from ironic.db import api as db_api
|
|
||||||
from ironic.drivers import base as driver_base
|
from ironic.drivers import base as driver_base
|
||||||
from ironic.openstack.common import context
|
from ironic.openstack.common import context
|
||||||
from ironic.tests import base
|
from ironic.tests import base
|
||||||
from ironic.tests.conductor import utils as mgr_utils
|
from ironic.tests.conductor import utils as mgr_utils
|
||||||
from ironic.tests.db import utils as db_utils
|
from ironic.tests.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
class FakeDriverTestCase(base.TestCase):
|
class FakeDriverTestCase(base.TestCase):
|
||||||
@ -34,11 +36,13 @@ class FakeDriverTestCase(base.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(FakeDriverTestCase, self).setUp()
|
super(FakeDriverTestCase, self).setUp()
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
self.dbapi = db_api.get_instance()
|
|
||||||
mgr_utils.mock_the_extension_manager()
|
mgr_utils.mock_the_extension_manager()
|
||||||
self.driver = driver_factory.get_driver("fake")
|
self.driver = driver_factory.get_driver("fake")
|
||||||
db_node = db_utils.get_test_node()
|
self.node = obj_utils.get_test_node(self.context)
|
||||||
self.node = self.dbapi.create_node(db_node)
|
self.task = mock.Mock(spec=task_manager.TaskManager)
|
||||||
|
self.task.shared = False
|
||||||
|
self.task.node = self.node
|
||||||
|
self.task.driver = self.driver
|
||||||
|
|
||||||
def test_driver_interfaces(self):
|
def test_driver_interfaces(self):
|
||||||
# fake driver implements only 3 out of 5 interfaces
|
# fake driver implements only 3 out of 5 interfaces
|
||||||
@ -50,15 +54,14 @@ class FakeDriverTestCase(base.TestCase):
|
|||||||
self.assertIsNone(self.driver.rescue)
|
self.assertIsNone(self.driver.rescue)
|
||||||
|
|
||||||
def test_power_interface(self):
|
def test_power_interface(self):
|
||||||
with task_manager.acquire(self.context,
|
self.driver.power.validate(self.task, self.node)
|
||||||
[self.node.uuid]) as task:
|
self.driver.power.get_power_state(self.task, self.node)
|
||||||
self.driver.power.validate(task, self.node)
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
self.driver.power.get_power_state(task, self.node)
|
self.driver.power.set_power_state,
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
self.task, self.node, states.NOSTATE)
|
||||||
self.driver.power.set_power_state,
|
self.driver.power.set_power_state(self.task, self.node,
|
||||||
task, self.node, states.NOSTATE)
|
states.POWER_ON)
|
||||||
self.driver.power.set_power_state(task, self.node, states.POWER_ON)
|
self.driver.power.reboot(self.task, self.node)
|
||||||
self.driver.power.reboot(task, self.node)
|
|
||||||
|
|
||||||
def test_deploy_interface(self):
|
def test_deploy_interface(self):
|
||||||
self.driver.deploy.validate(None, self.node)
|
self.driver.deploy.validate(None, self.node)
|
||||||
@ -70,3 +73,23 @@ class FakeDriverTestCase(base.TestCase):
|
|||||||
|
|
||||||
self.driver.deploy.clean_up(None, None)
|
self.driver.deploy.clean_up(None, None)
|
||||||
self.driver.deploy.tear_down(None, None)
|
self.driver.deploy.tear_down(None, None)
|
||||||
|
|
||||||
|
def test_management_interface_validate(self):
|
||||||
|
self.driver.management.validate(self.task, self.node)
|
||||||
|
|
||||||
|
def test_management_interface_set_boot_device_good(self):
|
||||||
|
self.driver.management.set_boot_device(self.task, boot_devices.PXE)
|
||||||
|
|
||||||
|
def test_management_interface_set_boot_device_fail(self):
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
self.driver.management.set_boot_device, self.task,
|
||||||
|
'not-supported')
|
||||||
|
|
||||||
|
def test_management_interface_get_supported_boot_devices(self):
|
||||||
|
expected = [boot_devices.PXE]
|
||||||
|
self.assertEqual(expected,
|
||||||
|
self.driver.management.get_supported_boot_devices())
|
||||||
|
|
||||||
|
def test_management_interface_get_boot_device(self):
|
||||||
|
self.assertEqual(boot_devices.PXE,
|
||||||
|
self.driver.management.get_boot_device(self.task))
|
||||||
|
Loading…
Reference in New Issue
Block a user