diff options
author | Jenkins <jenkins@review.openstack.org> | 2012-12-20 19:23:30 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2012-12-20 19:23:30 +0000 |
commit | 6085602f1b266d31d08b4a4b5bd33f7768d53a81 (patch) | |
tree | d9ffc4ee1d47f5c80d7e7ba18fe98a5cf1c34907 | |
parent | e979dbce43bf13bedff6264c4b4302cf7a9126a4 (diff) | |
parent | c846477f4bf9a615b251b0038163fe1a5d088880 (diff) | |
download | nova-6085602f1b266d31d08b4a4b5bd33f7768d53a81.tar.gz nova-6085602f1b266d31d08b4a4b5bd33f7768d53a81.tar.xz nova-6085602f1b266d31d08b4a4b5bd33f7768d53a81.zip |
Merge "New Baremetal provisioning framework."
-rw-r--r-- | etc/nova/rootwrap.d/compute.filters | 3 | ||||
-rw-r--r-- | nova/tests/baremetal/__init__.py | 2 | ||||
-rw-r--r-- | nova/tests/baremetal/db/__init__.py | 1 | ||||
-rw-r--r-- | nova/tests/baremetal/db/test_bm_interface.py | 17 | ||||
-rw-r--r-- | nova/tests/baremetal/db/utils.py | 1 | ||||
-rw-r--r-- | nova/tests/baremetal/test_driver.py | 173 | ||||
-rw-r--r-- | nova/virt/baremetal/__init__.py | 1 | ||||
-rw-r--r-- | nova/virt/baremetal/baremetal_states.py | 32 | ||||
-rw-r--r-- | nova/virt/baremetal/base.py | 75 | ||||
-rw-r--r-- | nova/virt/baremetal/db/api.py | 8 | ||||
-rw-r--r-- | nova/virt/baremetal/db/sqlalchemy/api.py | 40 | ||||
-rw-r--r-- | nova/virt/baremetal/driver.py | 368 | ||||
-rw-r--r-- | nova/virt/baremetal/fake.py | 75 | ||||
-rw-r--r-- | nova/virt/baremetal/interfaces.template | 31 | ||||
-rw-r--r-- | nova/virt/baremetal/utils.py | 37 |
15 files changed, 797 insertions, 67 deletions
diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters index cb7ad7487..baf4f1c92 100644 --- a/etc/nova/rootwrap.d/compute.filters +++ b/etc/nova/rootwrap.d/compute.filters @@ -166,3 +166,6 @@ lvs: CommandFilter, /sbin/lvs, root # nova/virt/libvirt/utils.py: vgs: CommandFilter, /sbin/vgs, root + +# nova/virt/baremetal/volume_driver.py: 'tgtadm', '--lld', 'iscsi', ... +tgtadm: CommandFilter, /usr/sbin/tgtadm, root diff --git a/nova/tests/baremetal/__init__.py b/nova/tests/baremetal/__init__.py index eef64faeb..f15d84efc 100644 --- a/nova/tests/baremetal/__init__.py +++ b/nova/tests/baremetal/__init__.py @@ -12,4 +12,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from nova.tests import * +from nova.tests.baremetal import * diff --git a/nova/tests/baremetal/db/__init__.py b/nova/tests/baremetal/db/__init__.py index 19071662c..543dfc1ae 100644 --- a/nova/tests/baremetal/db/__init__.py +++ b/nova/tests/baremetal/db/__init__.py @@ -12,3 +12,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from nova.tests.baremetal.db import * diff --git a/nova/tests/baremetal/db/test_bm_interface.py b/nova/tests/baremetal/db/test_bm_interface.py index 9f051ac9b..a9168bff6 100644 --- a/nova/tests/baremetal/db/test_bm_interface.py +++ b/nova/tests/baremetal/db/test_bm_interface.py @@ -35,20 +35,3 @@ class BareMetalInterfaceTestCase(base.BMDBTestCase): pif2_id = db.bm_interface_create(self.context, 2, '11:11:11:11:11:11', '0x2', 2) self.assertTrue(pif2_id is not None) - - def test_unique_vif_uuid(self): - pif1_id = db.bm_interface_create(self.context, 1, '11:11:11:11:11:11', - '0x1', 1) - pif2_id = db.bm_interface_create(self.context, 2, '22:22:22:22:22:22', - '0x2', 2) - db.bm_interface_set_vif_uuid(self.context, pif1_id, 'AAAA') - self.assertRaises(exception.NovaException, - db.bm_interface_set_vif_uuid, - self.context, pif2_id, 'AAAA') - - def test_vif_not_found(self): - pif_id = db.bm_interface_create(self.context, 1, '11:11:11:11:11:11', - '0x1', 1) - self.assertRaises(exception.NovaException, - db.bm_interface_set_vif_uuid, - self.context, pif_id + 1, 'AAAA') diff --git a/nova/tests/baremetal/db/utils.py b/nova/tests/baremetal/db/utils.py index 800305402..034f2f25a 100644 --- a/nova/tests/baremetal/db/utils.py +++ b/nova/tests/baremetal/db/utils.py @@ -60,7 +60,6 @@ def new_bm_interface(**kwargs): x.address = kwargs.pop('address', None) x.datapath_id = kwargs.pop('datapath_id', None) x.port_no = kwargs.pop('port_no', None) - x.vif_uuid = kwargs.pop('vif_uuid', None) if len(kwargs) > 0: raise test.TestingException("unknown field: %s" % ','.join(kwargs.keys())) diff --git a/nova/tests/baremetal/test_driver.py b/nova/tests/baremetal/test_driver.py new file mode 100644 index 000000000..117520e94 --- /dev/null +++ b/nova/tests/baremetal/test_driver.py @@ -0,0 +1,173 @@ +# Copyright (c) 2012 NTT DOCOMO, INC. +# Copyright (c) 2011 University of Southern California / ISI +# 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. + +""" +Tests for baremetal driver. +""" + +import mox + +from nova import exception +from nova.openstack.common import cfg +from nova import test +from nova.tests.baremetal.db import base +from nova.tests.baremetal.db import utils +from nova.tests.image import fake as fake_image +from nova.tests import test_virt_drivers +from nova.tests import utils as test_utils +from nova.virt.baremetal import baremetal_states +from nova.virt.baremetal import db +from nova.virt.baremetal import driver as bm_driver +from nova.virt.firewall import NoopFirewallDriver + + +CONF = cfg.CONF + + +FakeFirewallDriver = NoopFirewallDriver + + +NODE = utils.new_bm_node(cpus=2, memory_mb=4096, service_host="host1") +NICS = [ + {'address': '01:23:45:67:89:01', 'datapath_id': '0x1', 'port_no': 1, }, + {'address': '01:23:45:67:89:02', 'datapath_id': '0x2', 'port_no': 2, }, + ] + + +def class_path(class_): + return class_.__module__ + '.' + class_.__name__ + + +COMMON_FLAGS = dict( + baremetal_sql_connection='sqlite:///:memory:', + baremetal_driver='nova.virt.baremetal.fake.Fake', + power_manager='nova.virt.baremetal.fake.FakePowerManager', + firewall_driver=class_path(FakeFirewallDriver), + instance_type_extra_specs=['cpu_arch:test'], + host=NODE['service_host'], +) + + +def _create_baremetal_stuff(): + context = test_utils.get_test_admin_context() + node = db.bm_node_create(context, NODE) + for nic in NICS: + db.bm_interface_create(context, + node['id'], + nic['address'], + nic['datapath_id'], + nic['port_no']) + return node + + +class BaremetalDriverSpawnTestCase(base.Database): + + def setUp(self): + super(BaremetalDriverSpawnTestCase, self).setUp() + self.flags(**COMMON_FLAGS) + fake_image.stub_out_image_service(self.stubs) + + self.node = _create_baremetal_stuff() + self.node_id = self.node['id'] + + self.context = test_utils.get_test_admin_context() + self.instance = test_utils.get_test_instance() + self.network_info = test_utils.get_test_network_info() + self.block_device_info = None + self.image_meta = test_utils.get_test_image_info(None, self.instance) + self.driver = bm_driver.BareMetalDriver(None) + self.kwargs = dict( + context=self.context, + instance=self.instance, + image_meta=self.image_meta, + injected_files=[('/foo', 'bar'), ('/abc', 'xyz')], + admin_password='testpass', + network_info=self.network_info, + block_device_info=self.block_device_info) + self.addCleanup(fake_image.FakeImageService_reset) + + def test_ok(self): + self.instance['node'] = str(self.node_id) + self.driver.spawn(**self.kwargs) + node = db.bm_node_get(self.context, self.node_id) + self.assertEqual(node['instance_uuid'], self.instance['uuid']) + self.assertEqual(node['task_state'], baremetal_states.ACTIVE) + + def test_without_node(self): + self.assertRaises( + exception.NovaException, + self.driver.spawn, + **self.kwargs) + + def test_node_not_found(self): + self.instance['node'] = "123456789" + self.assertRaises( + exception.InstanceNotFound, + self.driver.spawn, + **self.kwargs) + + def test_node_in_use(self): + self.instance['node'] = str(self.node_id) + db.bm_node_update(self.context, self.node_id, + {'instance_uuid': 'something'}) + self.assertRaises( + exception.NovaException, + self.driver.spawn, + **self.kwargs) + + +class BaremetalDriverTestCase(test_virt_drivers._VirtDriverTestCase, + base.Database): + + def setUp(self): + super(BaremetalDriverTestCase, self).setUp() + self.driver_module = 'nova.virt.baremetal.BareMetalDriver' + self.flags(**COMMON_FLAGS) + self.node = _create_baremetal_stuff() + self.node_id = self.node['id'] + fake_image.stub_out_image_service(self.stubs) + self.addCleanup(fake_image.FakeImageService_reset) + + def _get_running_instance(self): + instance_ref = test_utils.get_test_instance() + instance_ref['node'] = str(self.node_id) + network_info = test_utils.get_test_network_info() + image_info = test_utils.get_test_image_info(None, instance_ref) + self.connection.spawn(self.ctxt, instance_ref, image_info, + [], 'herp', network_info=network_info) + return instance_ref, network_info + + def test_loading_baremetal_drivers(self): + from nova.virt.baremetal import fake + drv = bm_driver.BareMetalDriver(None) + self.assertTrue(isinstance(drv.baremetal_nodes, fake.Fake)) + self.assertTrue(isinstance(drv._firewall_driver, FakeFirewallDriver)) + + def test_get_host_stats(self): + self.flags(instance_type_extra_specs=['cpu_arch:x86_64', + 'x:123', + 'y:456', ]) + drv = bm_driver.BareMetalDriver(None) + cap_list = drv.get_host_stats() + self.assertTrue(isinstance(cap_list, list)) + self.assertEqual(len(cap_list), 1) + cap = cap_list[0] + self.assertEqual(cap['cpu_arch'], 'x86_64') + self.assertEqual(cap['x'], '123') + self.assertEqual(cap['y'], '456') + self.assertEqual(cap['hypervisor_type'], 'baremetal') + self.assertEqual(cap['baremetal_driver'], + 'nova.virt.baremetal.fake.Fake') diff --git a/nova/virt/baremetal/__init__.py b/nova/virt/baremetal/__init__.py index 19071662c..e3ecef821 100644 --- a/nova/virt/baremetal/__init__.py +++ b/nova/virt/baremetal/__init__.py @@ -12,3 +12,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from nova.virt.baremetal.driver import BareMetalDriver diff --git a/nova/virt/baremetal/baremetal_states.py b/nova/virt/baremetal/baremetal_states.py new file mode 100644 index 000000000..28a41ab47 --- /dev/null +++ b/nova/virt/baremetal/baremetal_states.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 NTT DOCOMO, INC. +# Copyright 2010 OpenStack LLC. +# 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. + +""" +Possible baremetal node states for instances. + +Compute instance baremetal states represent the state of an instance as it +pertains to a user or administrator. When combined with task states +(task_states.py), a better picture can be formed regarding the instance's +health. + +""" + +ACTIVE = 'active' +BUILDING = 'building' +DELETED = 'deleted' +ERROR = 'error' diff --git a/nova/virt/baremetal/base.py b/nova/virt/baremetal/base.py new file mode 100644 index 000000000..4b0640885 --- /dev/null +++ b/nova/virt/baremetal/base.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 NTT DOCOMO, INC. +# Copyright (c) 2011 University of Southern California / ISI +# 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. + +from nova.virt.baremetal import baremetal_states + + +class NodeDriver(object): + + def define_vars(self, instance, network_info, block_device_info): + raise NotImplementedError() + + def create_image(self, var, context, image_meta, node, instance, + injected_files=None, admin_password=None): + raise NotImplementedError() + + def destroy_images(self, var, context, node, instance): + raise NotImplementedError() + + def activate_bootloader(self, var, context, node, instance, image_meta): + raise NotImplementedError() + + def deactivate_bootloader(self, var, context, node, instance): + raise NotImplementedError() + + def activate_node(self, var, context, node, instance): + """For operations after power on.""" + raise NotImplementedError() + + def deactivate_node(self, var, context, node, instance): + """For operations before power off.""" + raise NotImplementedError() + + def get_console_output(self, node, instance): + raise NotImplementedError() + + +class PowerManager(object): + + def __init__(self, node): + pass + + def activate_node(self): + return baremetal_states.ACTIVE + + def reboot_node(self): + return baremetal_states.ACTIVE + + def deactivate_node(self): + return baremetal_states.DELETED + + def is_power_on(self): + """Returns True or False according as the node's power state""" + return True + + # TODO(NTTdocomo): split out console methods to its own class + def start_console(self): + pass + + def stop_console(self): + pass diff --git a/nova/virt/baremetal/db/api.py b/nova/virt/baremetal/db/api.py index 0b8cf781c..15d50dd66 100644 --- a/nova/virt/baremetal/db/api.py +++ b/nova/virt/baremetal/db/api.py @@ -148,14 +148,6 @@ def bm_interface_create(context, bm_node_id, address, datapath_id, port_no): datapath_id, port_no) -def bm_interface_set_vif_uuid(context, if_id, vif_uuid): - return IMPL.bm_interface_set_vif_uuid(context, if_id, vif_uuid) - - -def bm_interface_get_by_vif_uuid(context, vif_uuid): - return IMPL.bm_interface_get_by_vif_uuid(context, vif_uuid) - - def bm_interface_get_all_by_bm_node_id(context, bm_node_id): return IMPL.bm_interface_get_all_by_bm_node_id(context, bm_node_id) diff --git a/nova/virt/baremetal/db/sqlalchemy/api.py b/nova/virt/baremetal/db/sqlalchemy/api.py index 48606ac44..bb85d677f 100644 --- a/nova/virt/baremetal/db/sqlalchemy/api.py +++ b/nova/virt/baremetal/db/sqlalchemy/api.py @@ -311,46 +311,6 @@ def bm_interface_create(context, bm_node_id, address, datapath_id, port_no): @require_admin_context -def bm_interface_set_vif_uuid(context, if_id, vif_uuid): - session = get_session() - with session.begin(): - bm_interface = model_query(context, models.BareMetalInterface, - read_deleted="no", session=session).\ - filter_by(id=if_id).\ - with_lockmode('update').\ - first() - if not bm_interface: - raise exception.NovaException(_("Baremetal interface %s " - "not found") % if_id) - - bm_interface.vif_uuid = vif_uuid - try: - session.add(bm_interface) - session.flush() - except exception.DBError, e: - # TODO(deva): clean up when db layer raises DuplicateKeyError - if str(e).find('IntegrityError') != -1: - raise exception.NovaException(_("Baremetal interface %s " - "already in use") % vif_uuid) - else: - raise e - - -@require_admin_context -def bm_interface_get_by_vif_uuid(context, vif_uuid): - result = model_query(context, models.BareMetalInterface, - read_deleted="no").\ - filter_by(vif_uuid=vif_uuid).\ - first() - - if not result: - raise exception.NovaException(_("Baremetal virtual interface %s " - "not found") % vif_uuid) - - return result - - -@require_admin_context def bm_interface_get_all_by_bm_node_id(context, bm_node_id): result = model_query(context, models.BareMetalInterface, read_deleted="no").\ diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py new file mode 100644 index 000000000..043fba421 --- /dev/null +++ b/nova/virt/baremetal/driver.py @@ -0,0 +1,368 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# coding=utf-8 +# +# Copyright (c) 2012 NTT DOCOMO, INC +# Copyright (c) 2011 University of Southern California / ISI +# 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. + +""" +A driver for Bare-metal platform. +""" + +from nova.compute import power_state +from nova import context as nova_context +from nova import exception +from nova.openstack.common import cfg +from nova.openstack.common import importutils +from nova.openstack.common import log as logging +from nova.virt.baremetal import baremetal_states +from nova.virt.baremetal import db as bmdb +from nova.virt import driver +from nova.virt import firewall +from nova.virt.libvirt import imagecache + +opts = [ + cfg.BoolOpt('baremetal_inject_password', + default=True, + help='Whether baremetal compute injects password or not'), + cfg.StrOpt('baremetal_injected_network_template', + default='$pybasedir/nova/virt/baremetal/interfaces.template', + help='Template file for injected network'), + cfg.ListOpt('instance_type_extra_specs', + default=[], + help='a list of additional capabilities corresponding to ' + 'instance_type_extra_specs for this compute ' + 'host to advertise. Valid entries are name=value, pairs ' + 'For example, "key1:val1, key2:val2"'), + cfg.StrOpt('baremetal_driver', + default='nova.virt.baremetal.pxe.PXE', + help='Baremetal driver back-end (pxe or tilera)'), + cfg.StrOpt('power_manager', + default='nova.virt.baremetal.ipmi.Ipmi', + help='Baremetal power management method'), + cfg.StrOpt('baremetal_tftp_root', + default='/tftpboot', + help='Baremetal compute node\'s tftp root path'), + ] + + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF +CONF.register_opts(opts) + +DEFAULT_FIREWALL_DRIVER = "%s.%s" % ( + firewall.__name__, + firewall.NoopFirewallDriver.__name__) + + +def _get_baremetal_nodes(context): + nodes = bmdb.bm_node_get_all(context, service_host=CONF.host) + return nodes + + +def _get_baremetal_node_by_instance_uuid(instance_uuid): + ctx = nova_context.get_admin_context() + node = bmdb.bm_node_get_by_instance_uuid(ctx, instance_uuid) + if node['service_host'] != CONF.host: + LOG.error(_("Request for baremetal node %s " + "sent to wrong service host") % instance_uuid) + raise exception.InstanceNotFound(instance_id=instance_uuid) + return node + + +def _update_baremetal_state(context, node, instance, state): + instance_uuid = None + if instance: + instance_uuid = instance['uuid'] + bmdb.bm_node_update(context, node['id'], + {'instance_uuid': instance_uuid, + 'task_state': state, + }) + + +def get_power_manager(node, **kwargs): + cls = importutils.import_class(CONF.power_manager) + return cls(node, **kwargs) + + +class BareMetalDriver(driver.ComputeDriver): + """BareMetal hypervisor driver.""" + + capabilities = { + "has_imagecache": True, + } + + def __init__(self, virtapi, read_only=False): + super(BareMetalDriver, self).__init__(virtapi) + + self.baremetal_nodes = importutils.import_object( + CONF.baremetal_driver) + self._firewall_driver = firewall.load_driver( + default=DEFAULT_FIREWALL_DRIVER) + self._image_cache_manager = imagecache.ImageCacheManager() + + extra_specs = {} + extra_specs["baremetal_driver"] = CONF.baremetal_driver + for pair in CONF.instance_type_extra_specs: + keyval = pair.split(':', 1) + keyval[0] = keyval[0].strip() + keyval[1] = keyval[1].strip() + extra_specs[keyval[0]] = keyval[1] + if not 'cpu_arch' in extra_specs: + LOG.warning( + _('cpu_arch is not found in instance_type_extra_specs')) + extra_specs['cpu_arch'] = '' + self._extra_specs = extra_specs + + self._supported_instances = [ + (extra_specs['cpu_arch'], 'baremetal', 'baremetal'), + ] + + @classmethod + def instance(cls): + if not hasattr(cls, '_instance'): + cls._instance = cls() + return cls._instance + + def init_host(self, host): + return + + def get_hypervisor_type(self): + return 'baremetal' + + def get_hypervisor_version(self): + # TODO(deva): define the version properly elsewhere + return 1 + + def list_instances(self): + l = [] + ctx = nova_context.get_admin_context() + for node in _get_baremetal_nodes(ctx): + if node['instance_uuid']: + inst = self.virtapi.instance_get_by_uuid(ctx, + node['instance_uuid']) + if inst: + l.append(inst['name']) + return l + + def spawn(self, context, instance, image_meta, injected_files, + admin_password, network_info=None, block_device_info=None): + nodename = instance.get('node') + if not nodename: + raise exception.NovaException(_("Baremetal node id not supplied" + " to driver")) + node = bmdb.bm_node_get(context, nodename) + if node['instance_uuid']: + raise exception.NovaException(_("Baremetal node %s already" + " in use") % nodename) + + # TODO(deva): split this huge try: block into manageable parts + try: + _update_baremetal_state(context, node, instance, + baremetal_states.BUILDING) + + var = self.baremetal_nodes.define_vars(instance, network_info, + block_device_info) + + self._firewall_driver.setup_basic_filtering(instance, network_info) + self._firewall_driver.prepare_instance_filter(instance, + network_info) + + self.baremetal_nodes.create_image(var, context, image_meta, node, + instance, + injected_files=injected_files, + admin_password=admin_password) + self.baremetal_nodes.activate_bootloader(var, context, node, + instance, image_meta) + pm = get_power_manager(node) + state = pm.activate_node() + + _update_baremetal_state(context, node, instance, state) + + self.baremetal_nodes.activate_node(var, context, node, instance) + self._firewall_driver.apply_instance_filter(instance, network_info) + + pm.start_console() + + except Exception, e: + # TODO(deva): add tooling that can revert a failed spawn + _update_baremetal_state(context, node, instance, + baremetal_states.ERROR) + raise e + + def reboot(self, instance, network_info, reboot_type, + block_device_info=None): + node = _get_baremetal_node_by_instance_uuid(instance['uuid']) + ctx = nova_context.get_admin_context() + pm = get_power_manager(node) + state = pm.reboot_node() + _update_baremetal_state(ctx, node, instance, state) + + def destroy(self, instance, network_info, block_device_info=None): + ctx = nova_context.get_admin_context() + + try: + node = _get_baremetal_node_by_instance_uuid(instance['uuid']) + except exception.InstanceNotFound: + # TODO(deva): refactor so that dangling files can be cleaned + # up even after a failed boot or delete + LOG.warning(_("Delete called on non-existing instance %s") + % instance['uuid']) + return + + var = self.baremetal_nodes.define_vars(instance, network_info, + block_device_info) + + self.baremetal_nodes.deactivate_node(var, ctx, node, instance) + + pm = get_power_manager(node) + + pm.stop_console() + + ## power off the node + state = pm.deactivate_node() + + self.baremetal_nodes.deactivate_bootloader(var, ctx, node, instance) + + self.baremetal_nodes.destroy_images(var, ctx, node, instance) + + # stop firewall + self._firewall_driver.unfilter_instance(instance, + network_info=network_info) + + _update_baremetal_state(ctx, node, None, state) + + def power_off(self, instance): + """Power off the specified instance.""" + node = _get_baremetal_node_by_instance_uuid(instance['uuid']) + pm = get_power_manager(node) + pm.deactivate_node() + + def power_on(self, instance): + """Power on the specified instance""" + node = _get_baremetal_node_by_instance_uuid(instance['uuid']) + pm = get_power_manager(node) + pm.activate_node() + + def get_info(self, instance): + # NOTE(deva): compute/manager.py expects to get NotFound exception + # so we convert from InstanceNotFound + inst_uuid = instance.get('uuid') + node = _get_baremetal_node_by_instance_uuid(inst_uuid) + pm = get_power_manager(node) + ps = power_state.SHUTDOWN + if pm.is_power_on(): + ps = power_state.RUNNING + return {'state': ps, + 'max_mem': node['memory_mb'], + 'mem': node['memory_mb'], + 'num_cpu': node['cpus'], + 'cpu_time': 0} + + def refresh_security_group_rules(self, security_group_id): + self._firewall_driver.refresh_security_group_rules(security_group_id) + return True + + def refresh_security_group_members(self, security_group_id): + self._firewall_driver.refresh_security_group_members(security_group_id) + return True + + def refresh_provider_fw_rules(self): + self._firewall_driver.refresh_provider_fw_rules() + + def _node_resource(self, node): + vcpus_used = 0 + memory_mb_used = 0 + local_gb_used = 0 + + vcpus = node['cpus'] + memory_mb = node['memory_mb'] + local_gb = node['local_gb'] + if node['registration_status'] != 'done' or node['instance_uuid']: + vcpus_used = node['cpus'] + memory_mb_used = node['memory_mb'] + local_gb_used = node['local_gb'] + + dic = {'vcpus': vcpus, + 'memory_mb': memory_mb, + 'local_gb': local_gb, + 'vcpus_used': vcpus_used, + 'memory_mb_used': memory_mb_used, + 'local_gb_used': local_gb_used, + 'hypervisor_type': self.get_hypervisor_type(), + 'hypervisor_version': self.get_hypervisor_version(), + 'hypervisor_hostname': str(node['id']), + 'cpu_info': 'baremetal cpu', + } + return dic + + def refresh_instance_security_rules(self, instance): + self._firewall_driver.refresh_instance_security_rules(instance) + + def get_available_resource(self, nodename): + context = nova_context.get_admin_context() + node = bmdb.bm_node_get(context, nodename) + dic = self._node_resource(node) + return dic + + def ensure_filtering_rules_for_instance(self, instance_ref, network_info): + self._firewall_driver.setup_basic_filtering(instance_ref, network_info) + self._firewall_driver.prepare_instance_filter(instance_ref, + network_info) + + def unfilter_instance(self, instance_ref, network_info): + self._firewall_driver.unfilter_instance(instance_ref, + network_info=network_info) + + def get_host_stats(self, refresh=False): + caps = [] + context = nova_context.get_admin_context() + nodes = bmdb.bm_node_get_all(context, + service_host=CONF.host) + for node in nodes: + res = self._node_resource(node) + nodename = str(node['id']) + data = {} + data['vcpus'] = res['vcpus'] + data['vcpus_used'] = res['vcpus_used'] + data['cpu_info'] = res['cpu_info'] + data['disk_total'] = res['local_gb'] + data['disk_used'] = res['local_gb_used'] + data['disk_available'] = res['local_gb'] - res['local_gb_used'] + data['host_memory_total'] = res['memory_mb'] + data['host_memory_free'] = res['memory_mb'] - res['memory_mb_used'] + data['hypervisor_type'] = res['hypervisor_type'] + data['hypervisor_version'] = res['hypervisor_version'] + data['hypervisor_hostname'] = nodename + data['supported_instances'] = self._supported_instances + data.update(self._extra_specs) + data['host'] = CONF.host + data['node'] = nodename + # TODO(NTTdocomo): put node's extra specs here + caps.append(data) + return caps + + def manage_image_cache(self, context, all_instances): + """Manage the local cache of images.""" + self._image_cache_manager.verify_base_images(context, all_instances) + + def get_console_output(self, instance): + node = _get_baremetal_node_by_instance_uuid(instance['uuid']) + return self.baremetal_nodes.get_console_output(node, instance) + + def get_available_nodes(self): + context = nova_context.get_admin_context() + return [str(n['id']) for n in _get_baremetal_nodes(context)] diff --git a/nova/virt/baremetal/fake.py b/nova/virt/baremetal/fake.py new file mode 100644 index 000000000..9df964c39 --- /dev/null +++ b/nova/virt/baremetal/fake.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 NTT DOCOMO, INC. +# Copyright (c) 2011 University of Southern California / ISI +# 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. + +from nova.virt.baremetal import baremetal_states +from nova.virt.baremetal import base + + +def get_baremetal_nodes(): + return Fake() + + +class Fake(base.NodeDriver): + + def define_vars(self, instance, network_info, block_device_info): + return {} + + def create_image(self, var, context, image_meta, node, instance, + injected_files=None, admin_password=None): + pass + + def destroy_images(self, var, context, node, instance): + pass + + def activate_bootloader(self, var, context, node, instance, image_meta): + pass + + def deactivate_bootloader(self, var, context, node, instance): + pass + + def activate_node(self, var, context, node, instance): + """For operations after power on.""" + pass + + def deactivate_node(self, var, context, node, instance): + """For operations before power off.""" + pass + + def get_console_output(self, node, instance): + return 'fake\nconsole\noutput for instance %s' % instance['id'] + + +class FakePowerManager(base.PowerManager): + + def activate_node(self): + return baremetal_states.ACTIVE + + def reboot_node(self): + return baremetal_states.ACTIVE + + def deactivate_node(self): + return baremetal_states.DELETED + + def is_power_on(self): + return True + + def start_console(self): + pass + + def stop_console(self): + pass diff --git a/nova/virt/baremetal/interfaces.template b/nova/virt/baremetal/interfaces.template new file mode 100644 index 000000000..94776ed49 --- /dev/null +++ b/nova/virt/baremetal/interfaces.template @@ -0,0 +1,31 @@ +# Injected by Nova on instance boot +# +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +# The loopback network interface +auto lo +iface lo inet loopback + +#for $ifc in $interfaces +auto ${ifc.name} +iface ${ifc.name} inet static + address ${ifc.address} + netmask ${ifc.netmask} + broadcast ${ifc.broadcast} + gateway ${ifc.gateway} +#if $ifc.dns + dns-nameservers ${ifc.dns} +#end if +#if $ifc.hwaddress + hwaddress ether ${ifc.hwaddress} +#end if + +#if $use_ipv6 +iface ${ifc.name} inet6 static + address ${ifc.address_v6} + netmask ${ifc.netmask_v6} + gateway ${ifc.gateway_v6} +#end if + +#end for diff --git a/nova/virt/baremetal/utils.py b/nova/virt/baremetal/utils.py new file mode 100644 index 000000000..e34ca60f3 --- /dev/null +++ b/nova/virt/baremetal/utils.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 NTT DOCOMO, 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. + +import os + +from nova.openstack.common import log as logging +from nova.virt.libvirt import utils as libvirt_utils + + +LOG = logging.getLogger(__name__) + + +def cache_image(context, target, image_id, user_id, project_id): + if not os.path.exists(target): + libvirt_utils.fetch_image(context, target, image_id, + user_id, project_id) + + +def unlink_without_raise(path): + try: + libvirt_utils.file_delete(path) + except OSError: + LOG.exception(_("failed to unlink %s") % path) |