New Baremetal provisioning framework.
This is a minimal patch for the new baremetal driver. With this driver, nova compute registers multiple entries of baremetal nodes. It periodically updates the capabilities of the multiple baremetal nodes and reports it as a list of capabilities. It does not include Tilera or PXE back-ends, which will be provided by subsequent patches. It also does not include VIF or volume components. Part 4 of 7: blueprint general-bare-metal-provisioning-framework. Change-Id: I55617a8da52d20d4df727b8bbde8e5f72d3bf130 Co-authored-by: Mikyung Kang <mkkang@isi.edu> Co-authored-by: David Kang <dkang@isi.edu> Co-authored-by: Ken Igarashi <igarashik@nttdocomo.co.jp> Co-authored-by: Arata Notsu <notsu@virtualtech.jp> Co-authored-by: Chris Krelle <NobodyCam@gmail.com> Co-authored-by: Devananda van der Veen <devananda.vdv@gmail.com>
This commit is contained in:
parent
63f55af401
commit
aaeb899f98
|
@ -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 *
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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')
|
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -310,46 +310,6 @@ def bm_interface_create(context, bm_node_id, address, datapath_id, port_no):
|
|||
return ref.id
|
||||
|
||||
|
||||
@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,
|
||||
|
|
|
@ -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)]
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
Loading…
Reference in New Issue