summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/nova/policy.json1
-rw-r--r--nova/api/openstack/common.py3
-rw-r--r--nova/api/openstack/compute/contrib/certificates.py13
-rw-r--r--nova/api/openstack/compute/plugins/v3/certificates.py38
-rw-r--r--nova/cells/scheduler.py3
-rw-r--r--nova/compute/api.py31
-rwxr-xr-xnova/compute/manager.py4
-rw-r--r--nova/conductor/manager.py8
-rw-r--r--nova/conductor/rpcapi.py6
-rw-r--r--nova/objects/__init__.py1
-rw-r--r--nova/scheduler/utils.py11
-rw-r--r--nova/tests/api/ec2/test_cloud.py6
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_admin_actions.py4
-rw-r--r--nova/tests/api/openstack/compute/plugins/v3/test_certificates.py6
-rw-r--r--nova/tests/api/openstack/compute/test_server_metadata.py3
-rw-r--r--nova/tests/cells/test_cells_scheduler.py8
-rw-r--r--nova/tests/compute/test_compute.py144
-rw-r--r--nova/tests/conductor/test_conductor.py18
-rw-r--r--nova/tests/fake_policy.py1
-rw-r--r--nova/tests/virt/baremetal/test_pxe.py26
-rwxr-xr-xnova/tests/virt/baremetal/test_tilera.py7
-rw-r--r--nova/virt/baremetal/base.py2
-rwxr-xr-xnova/virt/baremetal/driver.py3
-rw-r--r--nova/virt/baremetal/fake.py2
-rw-r--r--nova/virt/baremetal/pxe.py56
-rw-r--r--nova/virt/baremetal/pxe_config.template2
-rwxr-xr-xnova/virt/baremetal/tilera.py28
-rw-r--r--nova/virt/baremetal/utils.py29
-rw-r--r--setup.cfg1
29 files changed, 317 insertions, 148 deletions
diff --git a/etc/nova/policy.json b/etc/nova/policy.json
index a9a584237..056a36b3b 100644
--- a/etc/nova/policy.json
+++ b/etc/nova/policy.json
@@ -34,6 +34,7 @@
"compute_extension:baremetal_nodes": "rule:admin_api",
"compute_extension:cells": "rule:admin_api",
"compute_extension:certificates": "",
+ "compute_extension:v3:os-certificates": "",
"compute_extension:cloudpipe": "rule:admin_api",
"compute_extension:cloudpipe_update": "rule:admin_api",
"compute_extension:console_output": "",
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index bec919f4b..dd746e23d 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -358,9 +358,12 @@ def raise_http_conflict_for_instance_invalid_state(exc, action):
"""
attr = exc.kwargs.get('attr')
state = exc.kwargs.get('state')
+ not_launched = exc.kwargs.get('not_launched')
if attr and state:
msg = _("Cannot '%(action)s' while instance is in %(attr)s "
"%(state)s") % {'action': action, 'attr': attr, 'state': state}
+ elif not_launched:
+ msg = _("Cannot '%s' an instance which has never been active") % action
else:
# At least give some meaningful message
msg = _("Instance is in an invalid state for '%s'") % action
diff --git a/nova/api/openstack/compute/contrib/certificates.py b/nova/api/openstack/compute/contrib/certificates.py
index 64a6e26fe..4fe49aadf 100644
--- a/nova/api/openstack/compute/contrib/certificates.py
+++ b/nova/api/openstack/compute/contrib/certificates.py
@@ -38,15 +38,6 @@ class CertificateTemplate(xmlutil.TemplateBuilder):
return xmlutil.MasterTemplate(root, 1)
-class CertificatesTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('certificates')
- elem = xmlutil.SubTemplateElement(root, 'certificate',
- selector='certificates')
- make_certificate(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
def _translate_certificate_view(certificate, private_key=None):
return {
'data': certificate,
@@ -64,7 +55,7 @@ class CertificatesController(object):
@wsgi.serializers(xml=CertificateTemplate)
def show(self, req, id):
- """Return a list of certificates."""
+ """Return certificate information."""
context = req.environ['nova.context']
authorize(context)
if id != 'root':
@@ -76,7 +67,7 @@ class CertificatesController(object):
@wsgi.serializers(xml=CertificateTemplate)
def create(self, req, body=None):
- """Return a list of certificates."""
+ """Create a certificate."""
context = req.environ['nova.context']
authorize(context)
pk, cert = self.cert_rpcapi.generate_x509_cert(context,
diff --git a/nova/api/openstack/compute/plugins/v3/certificates.py b/nova/api/openstack/compute/plugins/v3/certificates.py
index 64a6e26fe..175780f9c 100644
--- a/nova/api/openstack/compute/plugins/v3/certificates.py
+++ b/nova/api/openstack/compute/plugins/v3/certificates.py
@@ -22,7 +22,8 @@ from nova.api.openstack import xmlutil
import nova.cert.rpcapi
from nova import network
-authorize = extensions.extension_authorizer('compute', 'certificates')
+ALIAS = "os-certificates"
+authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS)
def make_certificate(elem):
@@ -38,15 +39,6 @@ class CertificateTemplate(xmlutil.TemplateBuilder):
return xmlutil.MasterTemplate(root, 1)
-class CertificatesTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('certificates')
- elem = xmlutil.SubTemplateElement(root, 'certificate',
- selector='certificates')
- make_certificate(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
def _translate_certificate_view(certificate, private_key=None):
return {
'data': certificate,
@@ -64,7 +56,7 @@ class CertificatesController(object):
@wsgi.serializers(xml=CertificateTemplate)
def show(self, req, id):
- """Return a list of certificates."""
+ """Return certificate information."""
context = req.environ['nova.context']
authorize(context)
if id != 'root':
@@ -76,7 +68,7 @@ class CertificatesController(object):
@wsgi.serializers(xml=CertificateTemplate)
def create(self, req, body=None):
- """Return a list of certificates."""
+ """Create a certificate."""
context = req.environ['nova.context']
authorize(context)
pk, cert = self.cert_rpcapi.generate_x509_cert(context,
@@ -85,21 +77,21 @@ class CertificatesController(object):
return {'certificate': _translate_certificate_view(cert, pk)}
-class Certificates(extensions.ExtensionDescriptor):
+class Certificates(extensions.V3APIExtensionBase):
"""Certificates support."""
name = "Certificates"
- alias = "os-certificates"
+ alias = ALIAS
namespace = ("http://docs.openstack.org/compute/ext/"
- "certificates/api/v1.1")
- updated = "2012-01-19T00:00:00+00:00"
+ "certificates/api/v3")
+ version = 1
def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension('os-certificates',
- CertificatesController(),
- member_actions={})
- resources.append(res)
-
+ resources = [
+ extensions.ResourceExtension('os-certificates',
+ CertificatesController(),
+ member_actions={})]
return resources
+
+ def get_controller_extensions(self):
+ return []
diff --git a/nova/cells/scheduler.py b/nova/cells/scheduler.py
index c54b9b578..aa081a726 100644
--- a/nova/cells/scheduler.py
+++ b/nova/cells/scheduler.py
@@ -214,7 +214,8 @@ class CellsScheduler(base.Base):
instance_uuids = [inst['uuid'] for inst in
build_inst_kwargs['instances']]
instances = build_inst_kwargs['instances']
- request_spec = scheduler_utils.build_request_spec(image, instances)
+ request_spec = scheduler_utils.build_request_spec(message.ctxt,
+ image, instances)
filter_properties = copy.copy(build_inst_kwargs['filter_properties'])
filter_properties.update({'context': message.ctxt,
'scheduler': self,
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 1e24e8ce5..0a9b0e67b 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -110,10 +110,12 @@ RO_SECURITY_GROUPS = ['default']
SM_IMAGE_PROP_PREFIX = "image_"
-def check_instance_state(vm_state=None, task_state=(None,)):
+def check_instance_state(vm_state=None, task_state=(None,),
+ must_have_launched=True):
"""Decorator to check VM and/or task state before entry to API functions.
- If the instance is in the wrong state, the wrapper will raise an exception.
+ If the instance is in the wrong state, or has not been sucessfully started
+ at least once the wrapper will raise an exception.
"""
if vm_state is not None and not isinstance(vm_state, set):
@@ -137,6 +139,13 @@ def check_instance_state(vm_state=None, task_state=(None,)):
instance_uuid=instance['uuid'],
state=instance['task_state'],
method=f.__name__)
+ if must_have_launched and not instance['launched_at']:
+ raise exception.InstanceInvalidState(
+ attr=None,
+ not_launched=True,
+ instance_uuid=instance['uuid'],
+ state=instance['vm_state'],
+ method=f.__name__)
return f(self, context, instance, *args, **kw)
return inner
@@ -1305,7 +1314,8 @@ class API(base.Base):
# NOTE(maoy): we allow delete to be called no matter what vm_state says.
@wrap_check_policy
@check_instance_lock
- @check_instance_state(vm_state=None, task_state=None)
+ @check_instance_state(vm_state=None, task_state=None,
+ must_have_launched=True)
def soft_delete(self, context, instance):
"""Terminate an instance."""
LOG.debug(_('Going to try to soft delete instance'),
@@ -1329,7 +1339,8 @@ class API(base.Base):
@wrap_check_policy
@check_instance_lock
- @check_instance_state(vm_state=None, task_state=None)
+ @check_instance_state(vm_state=None, task_state=None,
+ must_have_launched=False)
def delete(self, context, instance):
"""Terminate an instance."""
LOG.debug(_("Going to try to terminate instance"), instance=instance)
@@ -1369,7 +1380,8 @@ class API(base.Base):
@wrap_check_policy
@check_instance_lock
- @check_instance_state(vm_state=[vm_states.SOFT_DELETED])
+ @check_instance_state(vm_state=[vm_states.SOFT_DELETED],
+ must_have_launched=False)
def force_delete(self, context, instance):
"""Force delete a previously deleted (but not reclaimed) instance."""
self._delete_instance(context, instance)
@@ -1790,7 +1802,8 @@ class API(base.Base):
@wrap_check_policy
@check_instance_lock
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
- vm_states.PAUSED, vm_states.SUSPENDED],
+ vm_states.PAUSED, vm_states.SUSPENDED,
+ vm_states.ERROR],
task_state=[None, task_states.REBOOTING,
task_states.REBOOTING_HARD,
task_states.RESUMING,
@@ -1826,7 +1839,8 @@ class API(base.Base):
@wrap_check_policy
@check_instance_lock
- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED],
+ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
+ vm_states.ERROR],
task_state=[None])
def rebuild(self, context, instance, image_href, admin_password, **kwargs):
"""Rebuild the given instance with the provided attributes."""
@@ -2224,7 +2238,8 @@ class API(base.Base):
@wrap_check_policy
@check_instance_lock
- @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
+ @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED,
+ vm_states.ERROR])
def rescue(self, context, instance, rescue_password=None):
"""Rescue the given instance."""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 0a6a9d08d..237831cd1 100755
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -3707,8 +3707,8 @@ class ComputeManager(manager.SchedulerDependentManager):
{'migration_id': migration['id'],
'instance_uuid': instance_uuid})
try:
- instance = self.conductor_api.instance_get_by_uuid(
- context, instance_uuid)
+ instance = instance_obj.Instance.get_by_uuid(context,
+ instance_uuid)
except exception.InstanceNotFound:
reason = (_("Instance %s not found") %
instance_uuid)
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index 3b8c5f55d..cc1b05cc4 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -66,7 +66,7 @@ class ConductorManager(manager.Manager):
namespace. See the ComputeTaskManager class for details.
"""
- RPC_API_VERSION = '1.51'
+ RPC_API_VERSION = '1.52'
def __init__(self, *args, **kwargs):
super(ConductorManager, self).__init__(service_name='conductor',
@@ -475,6 +475,9 @@ class ConductorManager(manager.Manager):
self.compute_api.stop(context, instance, do_cast)
def compute_confirm_resize(self, context, instance, migration_ref):
+ if isinstance(instance, nova_object.NovaObject):
+ # NOTE(danms): Remove this at RPC API v2.0
+ instance = dict(instance.items())
self.compute_api.confirm_resize(context, instance, migration_ref)
def compute_unrescue(self, context, instance):
@@ -541,7 +544,8 @@ class ComputeTaskManager(object):
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping):
- request_spec = scheduler_utils.build_request_spec(image, instances)
+ request_spec = scheduler_utils.build_request_spec(context, image,
+ instances)
# NOTE(alaski): For compatibility until a new scheduler method is used.
request_spec.update({'block_device_mapping': block_device_mapping,
'security_group': security_groups})
diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py
index bb66ca8b2..fcbb87f0e 100644
--- a/nova/conductor/rpcapi.py
+++ b/nova/conductor/rpcapi.py
@@ -102,6 +102,7 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
1.50 - Added object_action() and object_class_action()
1.51 - Added the 'legacy' argument to
block_device_mapping_get_all_by_instance
+ 1.52 - Pass instance objects for compute_confirm_resize
"""
BASE_RPC_API_VERSION = '1.0'
@@ -459,11 +460,10 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
return self.call(context, msg, version='1.43')
def compute_confirm_resize(self, context, instance, migration_ref):
- instance_p = jsonutils.to_primitive(instance)
migration_p = jsonutils.to_primitive(migration_ref)
- msg = self.make_msg('compute_confirm_resize', instance=instance_p,
+ msg = self.make_msg('compute_confirm_resize', instance=instance,
migration_ref=migration_p)
- return self.call(context, msg, version='1.46')
+ return self.call(context, msg, version='1.52')
def compute_unrescue(self, context, instance):
instance_p = jsonutils.to_primitive(instance)
diff --git a/nova/objects/__init__.py b/nova/objects/__init__.py
index e39f0154c..00f8240f1 100644
--- a/nova/objects/__init__.py
+++ b/nova/objects/__init__.py
@@ -18,3 +18,4 @@ def register_all():
# function in order for it to be registered by services that may
# need to receive it via RPC.
__import__('nova.objects.instance')
+ __import__('nova.objects.instance_info_cache')
diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py
index 028c72ff7..b707f424c 100644
--- a/nova/scheduler/utils.py
+++ b/nova/scheduler/utils.py
@@ -15,19 +15,26 @@
"""Utility methods for scheduling."""
from nova.compute import flavors
+from nova import db
from nova.openstack.common import jsonutils
-def build_request_spec(image, instances):
+def build_request_spec(ctxt, image, instances):
"""Build a request_spec for the scheduler.
The request_spec assumes that all instances to be scheduled are the same
type.
"""
instance = instances[0]
+ instance_type = flavors.extract_flavor(instance)
+ # NOTE(comstud): This is a bit ugly, but will get cleaned up when
+ # we're passing an InstanceType internal object.
+ extra_specs = db.instance_type_extra_specs_get(ctxt,
+ instance_type['flavorid'])
+ instance_type['extra_specs'] = extra_specs
request_spec = {
'image': image,
'instance_properties': instance,
- 'instance_type': flavors.extract_flavor(instance),
+ 'instance_type': instance_type,
'instance_uuids': [inst['uuid'] for inst in instances]}
return jsonutils.to_primitive(request_spec)
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index 543bf4a62..22a6947f2 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -46,6 +46,7 @@ from nova.network import api as network_api
from nova.network import quantumv2
from nova.openstack.common import log as logging
from nova.openstack.common import rpc
+from nova.openstack.common import timeutils
from nova import test
from nova.tests.api.openstack.compute.contrib import (
test_quantum_security_groups as test_quantum)
@@ -880,6 +881,7 @@ class CloudTestCase(test.TestCase):
'instance_type_id': 1,
'host': 'host1',
'vm_state': 'active',
+ 'launched_at': timeutils.utcnow(),
'hostname': 'server-1111',
'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1),
'system_metadata': sys_meta
@@ -891,6 +893,7 @@ class CloudTestCase(test.TestCase):
'instance_type_id': 1,
'host': 'host2',
'vm_state': 'active',
+ 'launched_at': timeutils.utcnow(),
'hostname': 'server-1112',
'created_at': datetime.datetime(2012, 5, 1, 1, 1, 2),
'system_metadata': sys_meta
@@ -2442,6 +2445,7 @@ class CloudTestCase(test.TestCase):
'image_ref': image_uuid,
'instance_type_id': 1,
'vm_state': 'active',
+ 'launched_at': timeutils.utcnow(),
'hostname': 'server-1111',
'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1)
}
@@ -2492,6 +2496,7 @@ class CloudTestCase(test.TestCase):
'image_ref': image_uuid,
'instance_type_id': 1,
'vm_state': 'active',
+ 'launched_at': timeutils.utcnow(),
'hostname': 'server-1111',
'created_at': datetime.datetime(2012, 5, 1, 1, 1, 1)
}
@@ -2501,6 +2506,7 @@ class CloudTestCase(test.TestCase):
'image_ref': image_uuid,
'instance_type_id': 1,
'vm_state': 'active',
+ 'launched_at': timeutils.utcnow(),
'hostname': 'server-1112',
'created_at': datetime.datetime(2012, 5, 1, 1, 1, 2)
}
diff --git a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py
index 5e64bdf94..39eebbcd1 100644
--- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py
+++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py
@@ -26,6 +26,7 @@ from nova.conductor import api as conductor_api
from nova import context
from nova import exception
from nova.openstack.common import jsonutils
+from nova.openstack.common import timeutils
from nova import test
from nova.tests.api.openstack import fakes
@@ -41,6 +42,7 @@ INSTANCE = {
"tenant_id": 'fake_tenant_id',
"created_at": datetime.datetime(2010, 10, 10, 12, 0, 0),
"updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
+ "launched_at": datetime.datetime(2010, 11, 11, 11, 0, 0),
"security_groups": [{"id": 1, "name": "test"}],
"progress": 0,
"image_ref": 'http://foo.com/123',
@@ -61,7 +63,7 @@ def fake_compute_api_raises_invalid_state(*args, **kwargs):
def fake_compute_api_get(self, context, instance_id):
return {'id': 1, 'uuid': instance_id, 'vm_state': vm_states.ACTIVE,
- 'task_state': None}
+ 'task_state': None, 'launched_at': timeutils.utcnow()}
class AdminActionsTest(test.TestCase):
diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py b/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py
index df5a7e9a1..222087872 100644
--- a/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py
+++ b/nova/tests/api/openstack/compute/plugins/v3/test_certificates.py
@@ -15,7 +15,7 @@
from lxml import etree
-from nova.api.openstack.compute.contrib import certificates
+from nova.api.openstack.compute.plugins.v3 import certificates
from nova import context
from nova.openstack.common import rpc
from nova import test
@@ -44,7 +44,7 @@ class CertificatesTest(test.TestCase):
def test_certificates_show_root(self):
self.stubs.Set(rpc, 'call', fake_get_root_cert)
- req = fakes.HTTPRequest.blank('/v2/fake/os-certificates/root')
+ req = fakes.HTTPRequestV3.blank('/os-certificates/root')
res_dict = self.controller.show(req, 'root')
cert = fake_get_root_cert(self.context)
@@ -53,7 +53,7 @@ class CertificatesTest(test.TestCase):
def test_certificates_create_certificate(self):
self.stubs.Set(rpc, 'call', fake_create_cert)
- req = fakes.HTTPRequest.blank('/v2/fake/os-certificates/')
+ req = fakes.HTTPRequestV3.blank('/os-certificates/')
res_dict = self.controller.create(req)
pk, cert = fake_create_cert(self.context)
diff --git a/nova/tests/api/openstack/compute/test_server_metadata.py b/nova/tests/api/openstack/compute/test_server_metadata.py
index fa25ad4a3..f0548ffa0 100644
--- a/nova/tests/api/openstack/compute/test_server_metadata.py
+++ b/nova/tests/api/openstack/compute/test_server_metadata.py
@@ -26,6 +26,7 @@ from nova.compute import vm_states
import nova.db
from nova import exception
from nova.openstack.common import jsonutils
+from nova.openstack.common import timeutils
from nova import test
from nova.tests.api.openstack import fakes
@@ -77,6 +78,7 @@ def return_server(context, server_id):
'uuid': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
'name': 'fake',
'locked': False,
+ 'launched_at': timeutils.utcnow(),
'vm_state': vm_states.ACTIVE}
@@ -85,6 +87,7 @@ def return_server_by_uuid(context, server_uuid):
'uuid': '0cc3346e-9fef-4445-abe6-5d2b2690ec64',
'name': 'fake',
'locked': False,
+ 'launched_at': timeutils.utcnow(),
'vm_state': vm_states.ACTIVE}
diff --git a/nova/tests/cells/test_cells_scheduler.py b/nova/tests/cells/test_cells_scheduler.py
index 9cd637cdf..600867f43 100644
--- a/nova/tests/cells/test_cells_scheduler.py
+++ b/nova/tests/cells/test_cells_scheduler.py
@@ -178,7 +178,7 @@ class CellsSchedulerTestCase(test.TestCase):
call_info['target_cell'] = target_cell
call_info['build_inst_kwargs'] = build_inst_kwargs
- def fake_build_request_spec(image, instances):
+ def fake_build_request_spec(ctxt, image, instances):
request_spec = {
'instance_uuids': [inst['uuid'] for inst in instances],
'image': image}
@@ -264,7 +264,7 @@ class CellsSchedulerTestCase(test.TestCase):
def fake_rpc_build_instances(ctxt, **build_inst_kwargs):
call_info['build_inst_kwargs'] = build_inst_kwargs
- def fake_build_request_spec(image, instances):
+ def fake_build_request_spec(ctxt, image, instances):
request_spec = {
'instance_uuids': [inst['uuid'] for inst in instances],
'image': image}
@@ -341,7 +341,7 @@ class CellsSchedulerTestCase(test.TestCase):
self.assertEqual(vm_states.ERROR, values['vm_state'])
call_info['errored_uuids'].append(instance_uuid)
- def fake_build_request_spec(image, instances):
+ def fake_build_request_spec(ctxt, image, instances):
request_spec = {
'instance_uuids': [inst['uuid'] for inst in instances],
'image': image}
@@ -385,7 +385,7 @@ class CellsSchedulerTestCase(test.TestCase):
self.assertEqual(vm_states.ERROR, instance['vm_state'])
call_info['errored_uuids2'].append(instance['uuid'])
- def fake_build_request_spec(image, instances):
+ def fake_build_request_spec(ctxt, image, instances):
request_spec = {
'instance_uuids': [inst['uuid'] for inst in instances],
'image': image}
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index dc828cef8..c887041f8 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -259,6 +259,9 @@ class BaseTestCase(test.TestCase):
inst['os_type'] = 'Linux'
inst['system_metadata'] = make_fake_sys_meta()
inst['locked'] = False
+ inst['created_at'] = timeutils.utcnow()
+ inst['updated_at'] = timeutils.utcnow()
+ inst['launched_at'] = timeutils.utcnow()
inst.update(params)
_create_service_entries(self.context.elevated(),
{'fake_zone': [inst['host']]})
@@ -1248,6 +1251,7 @@ class ComputeTestCase(BaseTestCase):
def test_run_terminate_timestamps(self):
# Make sure timestamps are set for launched and destroyed.
instance = jsonutils.to_primitive(self._create_fake_instance())
+ instance['launched_at'] = None
self.assertEqual(instance['launched_at'], None)
self.assertEqual(instance['deleted_at'], None)
launch = timeutils.utcnow()
@@ -4441,19 +4445,27 @@ class ComputeTestCase(BaseTestCase):
self.assertTrue(instance)
def test_poll_unconfirmed_resizes(self):
- instances = [{'uuid': 'fake_uuid1', 'vm_state': vm_states.RESIZED,
- 'task_state': None},
- {'uuid': 'noexist'},
- {'uuid': 'fake_uuid2', 'vm_state': vm_states.ERROR,
- 'task_state': None},
- {'uuid': 'fake_uuid3', 'vm_state': vm_states.ACTIVE,
- 'task_state': task_states.REBOOTING},
- {'uuid': 'fake_uuid4', 'vm_state': vm_states.RESIZED,
- 'task_state': None},
- {'uuid': 'fake_uuid5', 'vm_state': vm_states.ACTIVE,
- 'task_state': None},
- {'uuid': 'fake_uuid6', 'vm_state': vm_states.RESIZED,
- 'task_state': 'deleting'}]
+ instances = [
+ fake_instance.fake_db_instance(uuid='fake_uuid1',
+ vm_state=vm_states.RESIZED,
+ task_state=None),
+ fake_instance.fake_db_instance(uuid='noexist'),
+ fake_instance.fake_db_instance(uuid='fake_uuid2',
+ vm_state=vm_states.ERROR,
+ task_state=None),
+ fake_instance.fake_db_instance(uuid='fake_uuid3',
+ vm_state=vm_states.ACTIVE,
+ task_state=
+ task_states.REBOOTING),
+ fake_instance.fake_db_instance(uuid='fake_uuid4',
+ vm_state=vm_states.RESIZED,
+ task_state=None),
+ fake_instance.fake_db_instance(uuid='fake_uuid5',
+ vm_state=vm_states.ACTIVE,
+ task_state=None),
+ fake_instance.fake_db_instance(uuid='fake_uuid6',
+ vm_state=vm_states.RESIZED,
+ task_state='deleting')]
expected_migration_status = {'fake_uuid1': 'confirmed',
'noexist': 'error',
'fake_uuid2': 'error',
@@ -5714,6 +5726,21 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['uuid'])
+ def test_delete_if_not_launched(self):
+ instance, instance_uuid = self._run_instance(params={
+ 'host': CONF.host})
+
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_states.ERROR,
+ "launched_at": None})
+
+ self.compute_api.delete(self.context, instance)
+
+ instance = db.instance_get_by_uuid(self.context, instance_uuid)
+ self.assertEqual(instance['task_state'], task_states.DELETING)
+
+ db.instance_destroy(self.context, instance['uuid'])
+
def test_delete_in_resizing(self):
def fake_quotas_reserve(context, expire=None, project_id=None,
**deltas):
@@ -5988,7 +6015,7 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['uuid'])
- def test_rebuild(self):
+ def _test_rebuild(self, vm_state):
instance = jsonutils.to_primitive(self._create_fake_instance())
instance_uuid = instance['uuid']
self.compute.run_instance(self.context, instance=instance)
@@ -6019,6 +6046,10 @@ class ComputeAPITestCase(BaseTestCase):
image_ref = instance["image_ref"] + '-new_image_ref'
password = "new_password"
+
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_state})
+
self.compute_api.rebuild(self.context, instance, image_ref, password)
self.assertEqual(info['image_ref'], image_ref)
@@ -6033,6 +6064,34 @@ class ComputeAPITestCase(BaseTestCase):
'preserved': 'preserve this!'})
db.instance_destroy(self.context, instance['uuid'])
+ def test_rebuild(self):
+ # Test we can rebuild an instance in the Error State
+ self._test_rebuild(vm_state=vm_states.ACTIVE)
+
+ def test_rebuild_in_error_state(self):
+ # Test we can rebuild an instance in the Error State
+ self._test_rebuild(vm_state=vm_states.ERROR)
+
+ def test_rebuild_in_error_not_launched(self):
+ instance = jsonutils.to_primitive(
+ self._create_fake_instance(params={'image_ref': ''}))
+ instance_uuid = instance['uuid']
+ self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show)
+ self.compute.run_instance(self.context, instance=instance)
+
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_states.ERROR,
+ "launched_at": None})
+
+ instance = db.instance_get_by_uuid(self.context, instance['uuid'])
+
+ self.assertRaises(exception.InstanceInvalidState,
+ self.compute_api.rebuild,
+ self.context,
+ instance,
+ instance['image_ref'],
+ "new password")
+
def test_rebuild_no_image(self):
instance = jsonutils.to_primitive(
self._create_fake_instance(params={'image_ref': ''}))
@@ -6179,7 +6238,7 @@ class ComputeAPITestCase(BaseTestCase):
self.stubs.Set(nova.virt.fake.FakeDriver, 'legacy_nwinfo',
lambda x: False)
- def test_reboot_soft(self):
+ def _test_reboot_soft(self, vm_state):
# Ensure instance can be soft rebooted.
instance = jsonutils.to_primitive(self._create_fake_instance())
self.compute.run_instance(self.context, instance=instance)
@@ -6196,6 +6255,9 @@ class ComputeAPITestCase(BaseTestCase):
inst_ref = db.instance_get_by_uuid(self.context, instance['uuid'])
self.assertEqual(inst_ref['task_state'], None)
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_state})
+
reboot_type = "SOFT"
self._stub_out_reboot(device_name)
self.compute_api.reboot(self.context, inst_ref, reboot_type)
@@ -6205,7 +6267,15 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, inst_ref['uuid'])
- def test_reboot_hard(self):
+ def test_soft_reboot(self):
+ # Ensure instance can be rebooted while in error state.
+ self._test_reboot_soft(vm_state=vm_states.ACTIVE)
+
+ def test_soft_reboot_of_instance_in_error(self):
+ # Ensure instance can be rebooted while in error state.
+ self._test_reboot_soft(vm_state=vm_states.ERROR)
+
+ def test_reboot_hard(self, vm_state=vm_states.ACTIVE):
# Ensure instance can be hard rebooted.
instance = jsonutils.to_primitive(self._create_fake_instance())
self.compute.run_instance(self.context, instance=instance)
@@ -6222,6 +6292,9 @@ class ComputeAPITestCase(BaseTestCase):
inst_ref = db.instance_get_by_uuid(self.context, instance['uuid'])
self.assertEqual(inst_ref['task_state'], None)
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_state})
+
reboot_type = "HARD"
self._stub_out_reboot(device_name)
self.compute_api.reboot(self.context, inst_ref, reboot_type)
@@ -6231,6 +6304,10 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, inst_ref['uuid'])
+ def test_hard_reboot_of_instance_in_error(self):
+ # Ensure instance can be rebooted while in error state.
+ self.test_reboot_hard(vm_state=vm_states.ERROR)
+
def test_hard_reboot_of_soft_rebooting_instance(self):
# Ensure instance can be hard rebooted while soft rebooting.
instance = jsonutils.to_primitive(self._create_fake_instance())
@@ -6267,7 +6344,7 @@ class ComputeAPITestCase(BaseTestCase):
inst_ref,
reboot_type)
- def test_soft_reboot_of_rescued_instance(self):
+ def test_reboot_of_rescued_instance(self):
# Ensure instance can't be rebooted while in rescued state.
instance = jsonutils.to_primitive(self._create_fake_instance())
self.compute.run_instance(self.context, instance=instance)
@@ -6291,6 +6368,33 @@ class ComputeAPITestCase(BaseTestCase):
inst_ref,
'HARD')
+ def test_reboot_of_instance_in_error_not_launched(self):
+ # Ensure instance can be not rebooted while in error states
+ # if they have never been booted at least once.
+ instance = jsonutils.to_primitive(self._create_fake_instance())
+ self.compute.run_instance(self.context, instance=instance)
+
+ inst_ref = db.instance_get_by_uuid(self.context, instance['uuid'])
+ self.assertEqual(inst_ref['task_state'], None)
+
+ db.instance_update(self.context, instance['uuid'],
+ {"vm_state": vm_states.ERROR,
+ "launched_at": None})
+
+ inst_ref = db.instance_get_by_uuid(self.context, inst_ref['uuid'])
+
+ self.assertRaises(exception.InstanceInvalidState,
+ self.compute_api.reboot,
+ self.context,
+ inst_ref,
+ 'SOFT')
+
+ self.assertRaises(exception.InstanceInvalidState,
+ self.compute_api.reboot,
+ self.context,
+ inst_ref,
+ 'HARD')
+
def test_hostname_create(self):
# Ensure instance hostname is set during creation.
inst_type = flavors.get_flavor_by_name('m1.tiny')
@@ -7730,7 +7834,8 @@ class ComputeAPITestCase(BaseTestCase):
self.assertRaises(exception.InvalidDevicePath,
self.compute_api.attach_volume,
self.context,
- {'locked': False, 'vm_state': vm_states.ACTIVE},
+ {'locked': False, 'vm_state': vm_states.ACTIVE,
+ 'launched_at': timeutils.utcnow()},
None,
'/invalid')
@@ -8037,6 +8142,7 @@ class ComputeAPITestCase(BaseTestCase):
# Ensure exception is raised while detaching an un-attached volume
instance = {'uuid': 'uuid1',
'locked': False,
+ 'launched_at': timeutils.utcnow(),
'vm_state': vm_states.ACTIVE}
volume = {'id': 1, 'attach_status': 'detached'}
@@ -8049,6 +8155,7 @@ class ComputeAPITestCase(BaseTestCase):
# instance doesn't match.
instance = {'uuid': 'uuid1',
'locked': False,
+ 'launched_at': timeutils.utcnow(),
'vm_state': vm_states.ACTIVE}
volume = {'id': 1, 'attach_status': 'in-use',
'instance_uuid': 'uuid2'}
@@ -8363,6 +8470,7 @@ class ComputeAPITestCase(BaseTestCase):
def test_fail_evacuate_from_non_existing_host(self):
inst = {}
inst['vm_state'] = vm_states.ACTIVE
+ inst['launched_at'] = timeutils.utcnow()
inst['image_ref'] = FAKE_IMAGE_REF
inst['reservation_id'] = 'r-fakeres'
inst['user_id'] = self.user_id
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index 9bb2c00ec..a2a015313 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -1183,18 +1183,28 @@ class _BaseTaskTestCase(object):
def test_build_instances(self):
instance_type = flavors.get_default_flavor()
system_metadata = flavors.save_flavor_info({}, instance_type)
- # NOTE(alaski): instance_type -> system_metadata -> instance_type loses
- # some data (extra_specs) so we need both for testing.
- instance_type_extract = flavors.extract_flavor(
+ # NOTE(alaski): instance_type -> system_metadata -> instance_type
+ # loses some data (extra_specs). This build process is using
+ # scheduler/utils:build_request_spec() which extracts flavor from
+ # system_metadata and will re-query the DB for extra_specs.. so
+ # we need to test this properly
+ expected_instance_type = flavors.extract_flavor(
{'system_metadata': system_metadata})
+ expected_instance_type['extra_specs'] = 'fake-specs'
+
+ self.mox.StubOutWithMock(db, 'instance_type_extra_specs_get')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi,
'run_instance')
+
+ db.instance_type_extra_specs_get(
+ self.context,
+ instance_type['flavorid']).AndReturn('fake-specs')
self.conductor_manager.scheduler_rpcapi.run_instance(self.context,
request_spec={
'image': {'fake_data': 'should_pass_silently'},
'instance_properties': {'system_metadata': system_metadata,
'uuid': 'fakeuuid'},
- 'instance_type': instance_type_extract,
+ 'instance_type': expected_instance_type,
'instance_uuids': ['fakeuuid', 'fakeuuid2'],
'block_device_mapping': 'block_device_mapping',
'security_group': 'security_groups'},
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py
index ed8cc7424..70f84499a 100644
--- a/nova/tests/fake_policy.py
+++ b/nova/tests/fake_policy.py
@@ -112,6 +112,7 @@ policy_data = """
"compute_extension:baremetal_nodes": "",
"compute_extension:cells": "",
"compute_extension:certificates": "",
+ "compute_extension:v3:os-certificates": "",
"compute_extension:cloudpipe": "",
"compute_extension:cloudpipe_update": "",
"compute_extension:config_drive": "",
diff --git a/nova/tests/virt/baremetal/test_pxe.py b/nova/tests/virt/baremetal/test_pxe.py
index 022f9c692..cd4e5c143 100644
--- a/nova/tests/virt/baremetal/test_pxe.py
+++ b/nova/tests/virt/baremetal/test_pxe.py
@@ -116,6 +116,7 @@ class PXEClassMethodsTestCase(BareMetalPXETestCase):
'deployment_ari_path': 'eee',
'aki_path': 'fff',
'ari_path': 'ggg',
+ 'network_info': self.test_network_info,
}
config = pxe.build_pxe_config(**args)
self.assertThat(config, matchers.StartsWith('default deploy'))
@@ -140,6 +141,21 @@ class PXEClassMethodsTestCase(BareMetalPXETestCase):
matchers.Not(matchers.Contains('kernel ddd')),
))
+ def test_build_pxe_network_config(self):
+ self.flags(
+ pxe_network_config=True,
+ group='baremetal',
+ )
+ net = utils.get_test_network_info(1)
+ config = pxe.build_pxe_network_config(net)
+ self.assertIn('eth0:off', config)
+ self.assertNotIn('eth1', config)
+
+ net = utils.get_test_network_info(2)
+ config = pxe.build_pxe_network_config(net)
+ self.assertIn('eth0:off', config)
+ self.assertIn('eth1:off', config)
+
def test_build_network_config(self):
net = utils.get_test_network_info(1)
config = pxe.build_network_config(net)
@@ -458,7 +474,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
bm_utils.random_alnum(32).AndReturn('alnum')
pxe.build_pxe_config(
self.node['id'], 'alnum', iqn,
- 'aaaa', 'bbbb', 'cccc', 'dddd').AndReturn(pxe_config)
+ 'aaaa', 'bbbb', 'cccc', 'dddd',
+ self.test_network_info).AndReturn(pxe_config)
bm_utils.write_to_file(pxe_path, pxe_config)
for mac in macs:
bm_utils.create_link_without_raise(
@@ -466,7 +483,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
self.mox.ReplayAll()
- self.driver.activate_bootloader(self.context, self.node, self.instance)
+ self.driver.activate_bootloader(self.context, self.node, self.instance,
+ network_info=self.test_network_info)
self.mox.VerifyAll()
@@ -515,8 +533,8 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
row = db.bm_node_get(self.context, 1)
self.assertTrue(row['deploy_key'] is None)
- self.driver.activate_bootloader(self.context, self.node,
- self.instance)
+ self.driver.activate_bootloader(self.context, self.node, self.instance,
+ network_info=self.test_network_info)
row = db.bm_node_get(self.context, 1)
self.assertTrue(row['deploy_key'] is not None)
diff --git a/nova/tests/virt/baremetal/test_tilera.py b/nova/tests/virt/baremetal/test_tilera.py
index 488cba4df..7ad5c4b6a 100755
--- a/nova/tests/virt/baremetal/test_tilera.py
+++ b/nova/tests/virt/baremetal/test_tilera.py
@@ -317,7 +317,8 @@ class TileraPublicMethodsTestCase(BareMetalTileraTestCase):
self.mox.ReplayAll()
- self.driver.activate_bootloader(self.context, self.node, self.instance)
+ self.driver.activate_bootloader(self.context, self.node, self.instance,
+ network_info=self.test_network_info)
self.mox.VerifyAll()
@@ -334,8 +335,8 @@ class TileraPublicMethodsTestCase(BareMetalTileraTestCase):
row = db.bm_node_get(self.context, 1)
self.assertTrue(row['deploy_key'] is None)
- self.driver.activate_bootloader(self.context, self.node,
- self.instance)
+ self.driver.activate_bootloader(self.context, self.node, self.instance,
+ network_info=self.test_network_info)
row = db.bm_node_get(self.context, 1)
self.assertTrue(row['deploy_key'] is not None)
diff --git a/nova/virt/baremetal/base.py b/nova/virt/baremetal/base.py
index 2029400ba..876c70b23 100644
--- a/nova/virt/baremetal/base.py
+++ b/nova/virt/baremetal/base.py
@@ -30,7 +30,7 @@ class NodeDriver(object):
def destroy_images(self, context, node, instance):
raise NotImplementedError()
- def activate_bootloader(self, context, node, instance):
+ def activate_bootloader(self, context, node, instance, **kwargs):
raise NotImplementedError()
def deactivate_bootloader(self, context, node, instance):
diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py
index 376921360..4e8543c3e 100755
--- a/nova/virt/baremetal/driver.py
+++ b/nova/virt/baremetal/driver.py
@@ -248,7 +248,8 @@ class BareMetalDriver(driver.ComputeDriver):
injected_files=injected_files,
network_info=network_info,
)
- self.driver.activate_bootloader(context, node, instance)
+ self.driver.activate_bootloader(context, node, instance,
+ network_info=network_info)
self.power_on(instance, node)
self.driver.activate_node(context, node, instance)
_update_state(context, node, instance, baremetal_states.ACTIVE)
diff --git a/nova/virt/baremetal/fake.py b/nova/virt/baremetal/fake.py
index b3f39fdc3..76586ab74 100644
--- a/nova/virt/baremetal/fake.py
+++ b/nova/virt/baremetal/fake.py
@@ -28,7 +28,7 @@ class FakeDriver(base.NodeDriver):
def destroy_images(self, context, node, instance):
pass
- def activate_bootloader(self, context, node, instance):
+ def activate_bootloader(self, context, node, instance, **kwargs):
pass
def deactivate_bootloader(self, context, node, instance):
diff --git a/nova/virt/baremetal/pxe.py b/nova/virt/baremetal/pxe.py
index 6a5a5ece5..f44a5f87a 100644
--- a/nova/virt/baremetal/pxe.py
+++ b/nova/virt/baremetal/pxe.py
@@ -54,6 +54,10 @@ pxe_opts = [
cfg.IntOpt('pxe_deploy_timeout',
help='Timeout for PXE deployments. Default: 0 (unlimited)',
default=0),
+ cfg.BoolOpt('pxe_network_config',
+ help='If set, pass the network configuration details to the '
+ 'initramfs via cmdline.',
+ default=False),
]
LOG = logging.getLogger(__name__)
@@ -77,9 +81,22 @@ def _get_cheetah():
return CHEETAH
+def build_pxe_network_config(network_info):
+ interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6)
+ template = None
+ if not CONF.use_ipv6:
+ template = "ip=%(address)s::%(gateway)s:%(netmask)s::%(name)s:off"
+ else:
+ template = ("ip=[%(address_v6)s]::[%(gateway_v6)s]:"
+ "[%(netmask_v6)s]::%(name)s:off")
+
+ net_config = [template % iface for iface in interfaces]
+ return ' '.join(net_config)
+
+
def build_pxe_config(deployment_id, deployment_key, deployment_iscsi_iqn,
deployment_aki_path, deployment_ari_path,
- aki_path, ari_path):
+ aki_path, ari_path, network_info):
"""Build the PXE config file for a node
This method builds the PXE boot configuration file for a node,
@@ -90,6 +107,11 @@ def build_pxe_config(deployment_id, deployment_key, deployment_iscsi_iqn,
"""
LOG.debug(_("Building PXE config for deployment %s.") % deployment_id)
+
+ network_config = None
+ if network_info and CONF.baremetal.pxe_network_config:
+ network_config = build_pxe_network_config(network_info)
+
pxe_options = {
'deployment_id': deployment_id,
'deployment_key': deployment_key,
@@ -99,6 +121,7 @@ def build_pxe_config(deployment_id, deployment_key, deployment_iscsi_iqn,
'aki_path': aki_path,
'ari_path': ari_path,
'pxe_append_params': CONF.baremetal.pxe_append_params,
+ 'pxe_network_config': network_config,
}
cheetah = _get_cheetah()
pxe_config = str(cheetah(
@@ -110,33 +133,7 @@ def build_pxe_config(deployment_id, deployment_key, deployment_iscsi_iqn,
def build_network_config(network_info):
- # TODO(deva): fix assumption that device names begin with "eth"
- # and fix assumption about ordering
- try:
- assert isinstance(network_info, list)
- except AssertionError:
- network_info = [network_info]
- interfaces = []
- for id, (network, mapping) in enumerate(network_info):
- address_v6 = None
- gateway_v6 = None
- netmask_v6 = None
- if CONF.use_ipv6:
- address_v6 = mapping['ip6s'][0]['ip']
- netmask_v6 = mapping['ip6s'][0]['netmask']
- gateway_v6 = mapping['gateway_v6']
- interface = {
- 'name': 'eth%d' % id,
- 'address': mapping['ips'][0]['ip'],
- 'gateway': mapping['gateway'],
- 'netmask': mapping['ips'][0]['netmask'],
- 'dns': ' '.join(mapping['dns']),
- 'address_v6': address_v6,
- 'gateway_v6': gateway_v6,
- 'netmask_v6': netmask_v6,
- }
- interfaces.append(interface)
-
+ interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6)
cheetah = _get_cheetah()
network_config = str(cheetah(
open(CONF.baremetal.net_config_template).read(),
@@ -354,7 +351,7 @@ class PXE(base.NodeDriver):
bm_utils.unlink_without_raise(get_image_file_path(instance))
bm_utils.rmtree_without_raise(get_image_dir_path(instance))
- def activate_bootloader(self, context, node, instance):
+ def activate_bootloader(self, context, node, instance, network_info):
"""Configure PXE boot loader for an instance
Kernel and ramdisk images are downloaded by cache_tftp_images,
@@ -398,6 +395,7 @@ class PXE(base.NodeDriver):
image_info['deploy_ramdisk'][1],
image_info['kernel'][1],
image_info['ramdisk'][1],
+ network_info,
)
bm_utils.write_to_file(pxe_config_file_path, pxe_config)
diff --git a/nova/virt/baremetal/pxe_config.template b/nova/virt/baremetal/pxe_config.template
index f2fcc9b14..54dd98baf 100644
--- a/nova/virt/baremetal/pxe_config.template
+++ b/nova/virt/baremetal/pxe_config.template
@@ -8,4 +8,4 @@ ipappend 3
label boot
kernel ${pxe_options.aki_path}
-append initrd=${pxe_options.ari_path} root=${ROOT} ro ${pxe_options.pxe_append_params}
+append initrd=${pxe_options.ari_path} root=${ROOT} ro ${pxe_options.pxe_append_params} ${pxe_options.pxe_network_config}
diff --git a/nova/virt/baremetal/tilera.py b/nova/virt/baremetal/tilera.py
index 64335298c..bb89a5f94 100755
--- a/nova/virt/baremetal/tilera.py
+++ b/nova/virt/baremetal/tilera.py
@@ -55,31 +55,7 @@ def _get_cheetah():
def build_network_config(network_info):
- try:
- assert isinstance(network_info, list)
- except AssertionError:
- network_info = [network_info]
- interfaces = []
- for id, (network, mapping) in enumerate(network_info):
- address_v6 = None
- gateway_v6 = None
- netmask_v6 = None
- if CONF.use_ipv6:
- address_v6 = mapping['ip6s'][0]['ip']
- netmask_v6 = mapping['ip6s'][0]['netmask']
- gateway_v6 = mapping['gateway_v6']
- interface = {
- 'name': 'eth%d' % id,
- 'address': mapping['ips'][0]['ip'],
- 'gateway': mapping['gateway'],
- 'netmask': mapping['ips'][0]['netmask'],
- 'dns': ' '.join(mapping['dns']),
- 'address_v6': address_v6,
- 'gateway_v6': gateway_v6,
- 'netmask_v6': netmask_v6,
- }
- interfaces.append(interface)
-
+ interfaces = bm_utils.map_network_interfaces(network_info, CONF.use_ipv6)
cheetah = _get_cheetah()
network_config = str(cheetah(
open(CONF.baremetal.net_config_template).read(),
@@ -262,7 +238,7 @@ class Tilera(base.NodeDriver):
bm_utils.unlink_without_raise(get_image_file_path(instance))
bm_utils.rmtree_without_raise(get_image_dir_path(instance))
- def activate_bootloader(self, context, node, instance):
+ def activate_bootloader(self, context, node, instance, network_info):
"""Configure Tilera boot loader for an instance
Kernel and ramdisk images are downloaded by cache_tftp_images,
diff --git a/nova/virt/baremetal/utils.py b/nova/virt/baremetal/utils.py
index b18bfac85..96abcd41b 100644
--- a/nova/virt/baremetal/utils.py
+++ b/nova/virt/baremetal/utils.py
@@ -81,3 +81,32 @@ def random_alnum(count):
import string
chars = string.ascii_uppercase + string.digits
return "".join(random.choice(chars) for _ in range(count))
+
+
+def map_network_interfaces(network_info, use_ipv6=False):
+ # TODO(deva): fix assumption that device names begin with "eth"
+ # and fix assumption about ordering
+ if not isinstance(network_info, list):
+ network_info = [network_info]
+
+ interfaces = []
+ for id, (network, mapping) in enumerate(network_info):
+ address_v6 = None
+ gateway_v6 = None
+ netmask_v6 = None
+ if use_ipv6:
+ address_v6 = mapping['ip6s'][0]['ip']
+ netmask_v6 = mapping['ip6s'][0]['netmask']
+ gateway_v6 = mapping['gateway_v6']
+ interface = {
+ 'name': 'eth%d' % id,
+ 'address': mapping['ips'][0]['ip'],
+ 'gateway': mapping['gateway'],
+ 'netmask': mapping['ips'][0]['netmask'],
+ 'dns': ' '.join(mapping['dns']),
+ 'address_v6': address_v6,
+ 'gateway_v6': gateway_v6,
+ 'netmask_v6': netmask_v6,
+ }
+ interfaces.append(interface)
+ return interfaces
diff --git a/setup.cfg b/setup.cfg
index 1baa22940..5b850f622 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -54,6 +54,7 @@ console_scripts =
nova-xvpvncproxy = nova.cmd.xvpvncproxy:main
nova.api.v3.extensions =
+ certificates = nova.api.openstack.compute.plugins.v3.certificates:Certificates
consoles = nova.api.openstack.compute.plugins.v3.consoles:Consoles
extension_info = nova.api.openstack.compute.plugins.v3.extension_info:ExtensionInfo
fixed_ips = nova.api.openstack.compute.plugins.v3.fixed_ips:FixedIPs