diff --git a/ironic/drivers/modules/noop.py b/ironic/drivers/modules/noop.py new file mode 100644 index 0000000000..b7c7eb98ca --- /dev/null +++ b/ironic/drivers/modules/noop.py @@ -0,0 +1,67 @@ +# Copyright 2016 Red Hat, Inc. +# +# 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. + +""" +Dummy interface implementations for use as defaults with optional interfaces. + +Note that unlike fake implementatios, these do not pass validation and raise +exceptions for user-accessible actions. +""" + +from ironic.common import exception +from ironic.drivers import base + + +def _fail(iface, task, *args, **kwargs): + # TODO(dtanstur): support hardware types + driver = task.node.driver + raise exception.UnsupportedDriverExtension( + driver=driver, extension=iface.interface_type) + + +class FailMixin(object): + """Mixin to add to an interface to make it fail validation.""" + + def get_properties(self): + return {} + + validate = _fail + + +class NoConsole(FailMixin, base.ConsoleInterface): + """Console interface implementation that raises errors on all requests.""" + stop_console = get_console = start_console = _fail + + +class NoRescue(FailMixin, base.RescueInterface): + """Rescue interface implementation that raises errors on all requests.""" + rescue = unrescue = _fail + + +class NoVendor(FailMixin, base.VendorInterface): + """Vendor interface implementation that raises errors on all requests.""" + + def driver_validate(self, method, **kwargs): + raise exception.UnsupportedDriverExtension( + driver=type(self).__name__, extension=self.interface_type) + + +class NoInspect(FailMixin, base.InspectInterface): + """Inspect interface implementation that raises errors on all requests.""" + inspect_hardware = _fail + + +class NoRAID(FailMixin, base.RAIDInterface): + """RAID interface implementation that raises errors on all requests.""" + create_configuration = delete_configuration = _fail diff --git a/ironic/tests/unit/drivers/modules/test_noop.py b/ironic/tests/unit/drivers/modules/test_noop.py new file mode 100644 index 0000000000..ec6f21776e --- /dev/null +++ b/ironic/tests/unit/drivers/modules/test_noop.py @@ -0,0 +1,66 @@ +# Copyright 2016 Red Hat, Inc. +# +# 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. + +import mock +import stevedore + +from ironic.common import exception +from ironic.drivers.modules import noop +from ironic.tests import base + + +# TODO(dtantsur): move to ironic.common.driver_factory +def hardware_interface_extension_manager(interface): + """Get a Stevedore extension manager for given hardware interface.""" + return stevedore.extension.ExtensionManager( + 'ironic.hardware.interfaces.%s' % interface, + invoke_on_load=True) + + +class NoInterfacesTestCase(base.TestCase): + iface_types = ['console', 'inspect', 'raid', 'rescue', 'vendor'] + task = mock.Mock(node=mock.Mock(driver='pxe_foobar', spec=['driver']), + spec=['node']) + + def test_console(self): + for method in ('start_console', 'stop_console', 'get_console'): + self.assertRaises(exception.UnsupportedDriverExtension, + getattr(noop.NoConsole(), method), + self.task) + + def test_rescue(self): + for method in ('rescue', 'unrescue'): + self.assertRaises(exception.UnsupportedDriverExtension, + getattr(noop.NoRescue(), method), + self.task) + + def test_vendor(self): + self.assertRaises(exception.UnsupportedDriverExtension, + noop.NoVendor().validate, + self.task, 'method') + self.assertRaises(exception.UnsupportedDriverExtension, + noop.NoVendor().driver_validate, + 'method') + + def test_inspect(self): + self.assertRaises(exception.UnsupportedDriverExtension, + noop.NoInspect().inspect_hardware, self.task) + + def test_load_by_name(self): + for iface_type in self.iface_types: + mgr = hardware_interface_extension_manager(iface_type) + inst = mgr['no-%s' % iface_type].obj + self.assertEqual({}, inst.get_properties()) + self.assertRaises(exception.UnsupportedDriverExtension, + inst.validate, self.task) diff --git a/setup.cfg b/setup.cfg index 1a29d88edc..5369d86a41 100644 --- a/setup.cfg +++ b/setup.cfg @@ -97,11 +97,26 @@ ironic.drivers = pxe_iscsi_cimc = ironic.drivers.pxe:PXEAndCIMCDriver pxe_agent_cimc = ironic.drivers.agent:AgentAndCIMCDriver +ironic.hardware.interfaces.console = + no-console = ironic.drivers.modules.noop:NoConsole + +ironic.hardware.interfaces.inspect = + no-inspect = ironic.drivers.modules.noop:NoInspect + ironic.hardware.interfaces.network = flat = ironic.drivers.modules.network.flat:FlatNetwork noop = ironic.drivers.modules.network.noop:NoopNetwork neutron = ironic.drivers.modules.network.neutron:NeutronNetwork +ironic.hardware.interfaces.raid = + no-raid = ironic.drivers.modules.noop:NoRAID + +ironic.hardware.interfaces.rescue = + no-rescue = ironic.drivers.modules.noop:NoRescue + +ironic.hardware.interfaces.vendor = + no-vendor = ironic.drivers.modules.noop:NoVendor + ironic.database.migration_backend = sqlalchemy = ironic.db.sqlalchemy.migration