From 92f076c981343e09c240533acf49a6fdd0384555 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 16:39:04 -0700 Subject: Set XML namespace when returning XML --- nova/wsgi.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/wsgi.py b/nova/wsgi.py index ba0819466..f0a60582d 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -478,6 +478,9 @@ class Serializer(object): root_key = data.keys()[0] doc = minidom.Document() node = self._to_xml_node(doc, metadata, root_key, data[root_key]) + node.setAttribute('xmlns', + 'http://docs.rackspacecloud.com/servers/api/v1.0') + return node.toprettyxml(indent=' ') def _to_xml_node(self, doc, metadata, nodename, data): -- cgit From 5c31b423ba5b5347aac62559c4e5c0a02f264213 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 16:59:03 -0700 Subject: Support setting the xmlns intelligently --- nova/wsgi.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/wsgi.py b/nova/wsgi.py index f0a60582d..5d286bb3b 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -355,7 +355,8 @@ class Controller(object): if type(result) is dict: content_type = req.best_match_content_type() - body = self._serialize(result, content_type) + default_xmlns = self.get_default_xmlns(req) + body = self._serialize(result, content_type, default_xmlns) response = webob.Response() response.headers["Content-Type"] = content_type @@ -365,14 +366,15 @@ class Controller(object): else: return result - def _serialize(self, data, content_type): + def _serialize(self, data, content_type, default_xmlns): """ Serialize the given dict to the provided content_type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(_metadata) + + serializer = Serializer(_metadata, default_xmlns) try: return serializer.serialize(data, content_type) except exception.InvalidContentType: @@ -388,19 +390,23 @@ class Controller(object): serializer = Serializer(_metadata) return serializer.deserialize(data, content_type) + def get_default_xmlns(self, req): + return 'http://docs.rackspacecloud.com/servers/api/v1.0' + class Serializer(object): """ Serializes and deserializes dictionaries to certain MIME types. """ - def __init__(self, metadata=None): + def __init__(self, metadata=None, default_xmlns=None): """ Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information needed to serialize a dictionary to that type. """ self.metadata = metadata or {} + self.default_xmlns = default_xmlns def _get_serialize_handler(self, content_type): handlers = { @@ -478,14 +484,23 @@ class Serializer(object): root_key = data.keys()[0] doc = minidom.Document() node = self._to_xml_node(doc, metadata, root_key, data[root_key]) - node.setAttribute('xmlns', - 'http://docs.rackspacecloud.com/servers/api/v1.0') + + xmlns = node.getAttribute('xmlns') + if not xmlns and self.default_xmlns: + node.setAttribute('xmlns', self.default_xmlns) return node.toprettyxml(indent=' ') def _to_xml_node(self, doc, metadata, nodename, data): """Recursive method to convert data members to XML nodes.""" result = doc.createElement(nodename) + + # Set the xml namespace if one is specified + # TODO(justinsb): We could also use prefixes on the keys + xmlns = metadata.get('xmlns', None) + if xmlns: + result.setAttribute('xmlns', xmlns) + if type(data) is list: singular = metadata.get('plurals', {}).get(nodename, None) if singular is None: -- cgit From 45d28dfb035b4e219845d44e00073d70211e8175 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 21:14:26 -0700 Subject: Fixed up unit tests and direct api that was also calling _serialize (naughty!) --- nova/api/direct.py | 4 +++- nova/tests/api/openstack/test_limits.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/direct.py b/nova/api/direct.py index dfca250e0..153871e9f 100644 --- a/nova/api/direct.py +++ b/nova/api/direct.py @@ -206,7 +206,9 @@ class ServiceWrapper(wsgi.Controller): params = dict([(str(k), v) for (k, v) in params.iteritems()]) result = method(context, **params) if type(result) is dict or type(result) is list: - return self._serialize(result, req.best_match_content_type()) + content_type = req.best_match_content_type() + default_xmlns = self.get_default_xmlns(req) + return self._serialize(result, content_type, default_xmlns) else: return result diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py index 05cfacc60..df367005d 100644 --- a/nova/tests/api/openstack/test_limits.py +++ b/nova/tests/api/openstack/test_limits.py @@ -136,10 +136,17 @@ class LimitsControllerTest(BaseLimitTestSuite): request = self._get_index_request("application/xml") response = request.get_response(self.controller) - expected = "" - body = response.body.replace("\n", "").replace(" ", "") + expected = parseString(""" + + + + + """.replace(" ", "")) - self.assertEqual(expected, body) + body = parseString(response.body.replace(" ", "")) + + self.assertEqual(expected.toxml(), body.toxml()) def test_index_xml(self): """Test getting limit details in XML.""" @@ -148,7 +155,8 @@ class LimitsControllerTest(BaseLimitTestSuite): response = request.get_response(self.controller) expected = parseString(""" - + -- cgit From 9686b3a296c53486a64a949ae2f7430e25df2dcb Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 21:18:31 -0700 Subject: Added note agreeing with Brian Lamar that the namespace doesn't belong in wsgi --- nova/wsgi.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/wsgi.py b/nova/wsgi.py index 5d286bb3b..1bcc08f7f 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -391,6 +391,10 @@ class Controller(object): return serializer.deserialize(data, content_type) def get_default_xmlns(self, req): + # NOTE(justinsb): This doesn't really belong here.. + # We'll probably end up moving this into a new OpenstackApiController + # class or something like that, once we know what's going to happen + # with v1.1 return 'http://docs.rackspacecloud.com/servers/api/v1.0' -- cgit From 5e6c69bc7a7e5ddaa1bf0fa83f64da116343dba8 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 29 Mar 2011 16:35:39 -0700 Subject: Narrowly focused bugfix - don't lose libvirt instances on host reboot or if they crash --- nova/compute/manager.py | 14 ++++++----- nova/virt/libvirt_conn.py | 59 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 18 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index e0a5e2b3f..72f04ecb1 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1088,12 +1088,14 @@ class ComputeManager(manager.SchedulerDependentManager): db_instance['id'], vm_state) - if vm_state == power_state.SHUTOFF: - # TODO(soren): This is what the compute manager does when you - # terminate an instance. At some point I figure we'll have a - # "terminated" state and some sort of cleanup job that runs - # occasionally, cleaning them out. - self.db.instance_destroy(context, db_instance['id']) + # NOTE(justinsb): We no longer auto-remove SHUTOFF instances + # It's quite hard to get them back when we do. + #if vm_state == power_state.SHUTOFF: + # # TODO(soren): This is what the compute manager does when you + # # terminate an instance. At some point I figure we'll have a + # # "terminated" state and some sort of cleanup job that runs + # # occasionally, cleaning them out. + # self.db.instance_destroy(context, db_instance['id']) # Are there VMs not in the DB? for vm_not_found_in_db in vms_not_found_in_db: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c144e827e..533ff9394 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -116,6 +116,8 @@ flags.DEFINE_integer('live_migration_bandwidth', 0, 'Define live migration behavior') flags.DEFINE_string('qemu_img', 'qemu-img', 'binary to use for qemu-img commands') +flags.DEFINE_bool('start_guests_on_host_boot', False, + 'Whether to restart guests when the host reboots') def get_connection(read_only): @@ -230,12 +232,14 @@ class LibvirtConnection(driver.ComputeDriver): {'name': instance['name'], 'state': state}) db.instance_set_state(ctxt, instance['id'], state) - if state == power_state.SHUTOFF: - # TODO(soren): This is what the compute manager does when you - # terminate # an instance. At some point I figure we'll have a - # "terminated" state and some sort of cleanup job that runs - # occasionally, cleaning them out. - db.instance_destroy(ctxt, instance['id']) + # NOTE(justinsb): We no longer delete these instances, + # the user may want to power them back on + #if state == power_state.SHUTOFF: + # # TODO(soren): This is what the compute manager does when you + # # terminate # an instance. At some point I figure we'll have a + # # "terminated" state and some sort of cleanup job that runs + # # occasionally, cleaning them out. + # db.instance_destroy(ctxt, instance['id']) if state != power_state.RUNNING: continue @@ -474,7 +478,7 @@ class LibvirtConnection(driver.ComputeDriver): xml = self.to_xml(instance) self.firewall_driver.setup_basic_filtering(instance) self.firewall_driver.prepare_instance_filter(instance) - self._conn.createXML(xml, 0) + self._create_new_domain(xml) self.firewall_driver.apply_instance_filter(instance) timer = utils.LoopingCall(f=None) @@ -522,7 +526,7 @@ class LibvirtConnection(driver.ComputeDriver): 'kernel_id': FLAGS.rescue_kernel_id, 'ramdisk_id': FLAGS.rescue_ramdisk_id} self._create_image(instance, xml, '.rescue', rescue_images) - self._conn.createXML(xml, 0) + self._create_new_domain(xml) timer = utils.LoopingCall(f=None) @@ -565,10 +569,15 @@ class LibvirtConnection(driver.ComputeDriver): self.firewall_driver.setup_basic_filtering(instance, network_info) self.firewall_driver.prepare_instance_filter(instance, network_info) self._create_image(instance, xml, network_info) - self._conn.createXML(xml, 0) + domain = self._create_new_domain(xml) LOG.debug(_("instance %s: is running"), instance['name']) self.firewall_driver.apply_instance_filter(instance) + if FLAGS.start_guests_on_host_boot: + LOG.debug(_("instance %s: setting autostart ON") % + instance['name']) + domain.setAutostart(1) + timer = utils.LoopingCall(f=None) def _wait_for_boot(): @@ -964,11 +973,19 @@ class LibvirtConnection(driver.ComputeDriver): return xml def get_info(self, instance_name): + # NOTE(justinsb): When libvirt isn't running / can't connect, we get: + # libvir: Remote error : unable to connect to + # '/var/run/libvirt/libvirt-sock', libvirtd may need to be started: + # No such file or directory try: virt_dom = self._conn.lookupByName(instance_name) - except: - raise exception.NotFound(_("Instance %s not found") - % instance_name) + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_UNKNOWN_HOST: + raise exception.NotFound(_("Instance %s not found") + % instance_name) + LOG.warning(_("Error from libvirt during lookup: %s") % e) + raise + (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() return {'state': state, 'max_mem': max_mem, @@ -976,6 +993,24 @@ class LibvirtConnection(driver.ComputeDriver): 'num_cpu': num_cpu, 'cpu_time': cpu_time} + def _create_new_domain(self, xml, persistent=True, launch_flags=0): + # NOTE(justinsb): libvirt has two types of domain: + # * a transient domain disappears when the guest is shutdown + # or the host is rebooted. + # * a permanent domain is not automatically deleted + # NOTE(justinsb): Even for ephemeral instances, transient seems risky + + if persistent: + # To create a persistent domain, first define it, then launch it. + domain = self._conn.defineXML(xml) + + domain.createWithFlags(launch_flags) + else: + # createXML call creates a transient domain + domain = self._conn.createXML(xml, launch_flags) + + return domain + def get_diagnostics(self, instance_name): raise exception.ApiError(_("diagnostics are not supported " "for libvirt")) -- cgit From 399056300a0be228ec4c56587ec0d9c0d09d927c Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 29 Mar 2011 16:54:37 -0700 Subject: Fix unit test to reflect fact that instance is no longer deleted, just marked SHUTOFF --- nova/tests/test_compute.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index d1ef68de4..b7f08dfbe 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -654,4 +654,5 @@ class ComputeTestCase(test.TestCase): instances = db.instance_get_all(context.get_admin_context()) LOG.info(_("After force-killing instances: %s"), instances) - self.assertEqual(len(instances), 0) + self.assertEqual(len(instances), 1) + self.assertEqual(power_state.SHUTOFF, instances[0]['state']) -- cgit From 12505c6bae61e72b6c75ac647323c3ec15997df1 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 10:05:06 -0700 Subject: Add XML namespaces to the OpenStack API --- nova/api/openstack/accounts.py | 5 ++- nova/api/openstack/backup_schedules.py | 4 +-- nova/api/openstack/common.py | 11 +++++++ nova/api/openstack/consoles.py | 4 +-- nova/api/openstack/extensions.py | 7 +++-- nova/api/openstack/flavors.py | 7 +++-- nova/api/openstack/image_metadata.py | 3 +- nova/api/openstack/images.py | 5 ++- nova/api/openstack/limits.py | 4 +-- nova/api/openstack/server_metadata.py | 3 +- nova/api/openstack/servers.py | 5 ++- nova/api/openstack/shared_ip_groups.py | 4 +-- nova/api/openstack/users.py | 3 +- nova/api/openstack/zones.py | 6 ++-- nova/tests/integrated/test_xml.py | 56 ++++++++++++++++++++++++++++++++++ nova/wsgi.py | 8 ++--- 16 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 nova/tests/integrated/test_xml.py (limited to 'nova') diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py index 86066fa20..6e3763e47 100644 --- a/nova/api/openstack/accounts.py +++ b/nova/api/openstack/accounts.py @@ -13,15 +13,14 @@ # License for the specific language governing permissions and limitations # under the License. -import common import webob.exc from nova import exception from nova import flags from nova import log as logging -from nova import wsgi from nova.auth import manager +from nova.api.openstack import common from nova.api.openstack import faults FLAGS = flags.FLAGS @@ -35,7 +34,7 @@ def _translate_keys(account): manager=account.project_manager_id) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py index f2d2d86e8..4bf744046 100644 --- a/nova/api/openstack/backup_schedules.py +++ b/nova/api/openstack/backup_schedules.py @@ -19,7 +19,7 @@ import time from webob import exc -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults import nova.image.service @@ -29,7 +29,7 @@ def _translate_keys(inst): return dict(backupSchedule=inst) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The backup schedule API controller for the Openstack API """ _serialization_metadata = { diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 75aeb0a5f..234f921ab 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -22,6 +22,7 @@ import webob from nova import exception from nova import flags from nova import log as logging +from nova import wsgi LOG = logging.getLogger('common') @@ -30,6 +31,10 @@ LOG = logging.getLogger('common') FLAGS = flags.FLAGS +XML_NS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0' +XML_NS_V11 = 'http://docs.openstack.org/compute/api/v1.1' + + def limited(items, request, max_limit=FLAGS.osapi_max_limit): """ Return a slice of items according to requested offset and limit. @@ -128,3 +133,9 @@ def get_id_from_href(href): except: LOG.debug(_("Error extracting id from href: %s") % href) raise webob.exc.HTTPBadRequest(_('could not parse id from href')) + + +class OpenstackController(wsgi.Controller): + def get_default_xmlns(self, req): + # Use V10 by default + return XML_NS_V10 diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py index 8c291c2eb..1a77f25d7 100644 --- a/nova/api/openstack/consoles.py +++ b/nova/api/openstack/consoles.py @@ -19,7 +19,7 @@ from webob import exc from nova import console from nova import exception -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -43,7 +43,7 @@ def _translate_detail_keys(cons): return dict(console=info) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """The Consoles Controller for the Openstack API""" _serialization_metadata = { diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index fb1dccb28..75f369cac 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -28,6 +28,7 @@ from nova import exception from nova import flags from nova import log as logging from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -115,7 +116,7 @@ class ExtensionDescriptor(object): return response_exts -class ActionExtensionController(wsgi.Controller): +class ActionExtensionController(common.OpenstackController): def __init__(self, application): @@ -136,7 +137,7 @@ class ActionExtensionController(wsgi.Controller): return res -class ResponseExtensionController(wsgi.Controller): +class ResponseExtensionController(common.OpenstackController): def __init__(self, application): self.application = application @@ -163,7 +164,7 @@ class ResponseExtensionController(wsgi.Controller): return res -class ExtensionController(wsgi.Controller): +class ExtensionController(common.OpenstackController): def __init__(self, extension_manager): self.extension_manager = extension_manager diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py index 5b99b5a6f..40787bd17 100644 --- a/nova/api/openstack/flavors.py +++ b/nova/api/openstack/flavors.py @@ -19,11 +19,11 @@ import webob from nova import db from nova import exception -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import views -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """Flavor controller for the OpenStack API.""" _serialization_metadata = { @@ -76,3 +76,6 @@ class ControllerV11(Controller): def _get_view_builder(self, req): base_url = req.application_url return views.flavors.ViewBuilderV11(base_url) + + def get_default_xmlns(self, req): + return common.XML_NS_V11 diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index c9d6ac532..e673e5f7b 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -20,13 +20,14 @@ from webob import exc from nova import flags from nova import utils from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """The image metadata API controller for the Openstack API""" def __init__(self): diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index e77100d7b..5550ee532 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -32,7 +32,7 @@ LOG = log.getLogger('nova.api.openstack.images') FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """Base `wsgi.Controller` for retrieving/displaying images.""" _serialization_metadata = { @@ -153,3 +153,6 @@ class ControllerV11(Controller): """Property to get the ViewBuilder class we need to use.""" base_url = request.application_url return images_view.ViewBuilderV11(base_url) + + def get_default_xmlns(self, req): + return common.XML_NS_V11 diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py index efc7d193d..9877af191 100644 --- a/nova/api/openstack/limits.py +++ b/nova/api/openstack/limits.py @@ -31,8 +31,8 @@ from collections import defaultdict from webob.dec import wsgify from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults -from nova.wsgi import Controller from nova.wsgi import Middleware @@ -43,7 +43,7 @@ PER_HOUR = 60 * 60 PER_DAY = 60 * 60 * 24 -class LimitsController(Controller): +class LimitsController(common.OpenstackController): """ Controller for accessing limits in the OpenStack API. """ diff --git a/nova/api/openstack/server_metadata.py b/nova/api/openstack/server_metadata.py index 45bbac99d..5c1390b9c 100644 --- a/nova/api/openstack/server_metadata.py +++ b/nova/api/openstack/server_metadata.py @@ -19,10 +19,11 @@ from webob import exc from nova import compute from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The server metadata API controller for the Openstack API """ def __init__(self): diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f7696d918..64090d830 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -44,7 +44,7 @@ LOG = logging.getLogger('server') FLAGS = flags.FLAGS -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The Server API controller for the OpenStack API """ _serialization_metadata = { @@ -632,6 +632,9 @@ class ControllerV11(Controller): def _limit_items(self, items, req): return common.limited_by_marker(items, req) + def get_default_xmlns(self, req): + return common.XML_NS_V11 + class ServerCreateRequestXMLDeserializer(object): """ diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py index ee7991d7f..996db3648 100644 --- a/nova/api/openstack/shared_ip_groups.py +++ b/nova/api/openstack/shared_ip_groups.py @@ -17,7 +17,7 @@ from webob import exc -from nova import wsgi +from nova.api.openstack import common from nova.api.openstack import faults @@ -32,7 +32,7 @@ def _translate_detail_keys(inst): return dict(sharedIpGroups=inst) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): """ The Shared IP Groups Controller for the Openstack API """ _serialization_metadata = { diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py index d3ab3d553..077ccfc79 100644 --- a/nova/api/openstack/users.py +++ b/nova/api/openstack/users.py @@ -18,7 +18,6 @@ from webob import exc from nova import exception from nova import flags from nova import log as logging -from nova import wsgi from nova.api.openstack import common from nova.api.openstack import faults from nova.auth import manager @@ -35,7 +34,7 @@ def _translate_keys(user): admin=user.admin) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py index 846cb48a1..227ffecdc 100644 --- a/nova/api/openstack/zones.py +++ b/nova/api/openstack/zones.py @@ -13,12 +13,10 @@ # License for the specific language governing permissions and limitations # under the License. -import common - from nova import db from nova import flags from nova import log as logging -from nova import wsgi +from nova.api.openstack import common from nova.scheduler import api @@ -43,7 +41,7 @@ def _scrub_zone(zone): 'deleted', 'deleted_at', 'updated_at')) -class Controller(wsgi.Controller): +class Controller(common.OpenstackController): _serialization_metadata = { 'application/xml': { diff --git a/nova/tests/integrated/test_xml.py b/nova/tests/integrated/test_xml.py new file mode 100644 index 000000000..8a9754777 --- /dev/null +++ b/nova/tests/integrated/test_xml.py @@ -0,0 +1,56 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Justin Santa Barbara +# 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 import flags +from nova.log import logging +from nova.tests.integrated import integrated_helpers +from nova.api.openstack import common + + +LOG = logging.getLogger('nova.tests.integrated') + + +FLAGS = flags.FLAGS +FLAGS.verbose = True + + +class XmlTests(integrated_helpers._IntegratedTestBase): + """"Some basic XML sanity checks.""" + + def test_namespace_limits(self): + """/limits should have v1.0 namespace (hasn't changed in 1.1).""" + headers = {} + headers['Accept'] = 'application/xml' + + response = self.api.api_request('/limits', headers=headers) + data = response.read() + LOG.debug("data: %s" % data) + + prefix = ' Date: Wed, 30 Mar 2011 10:41:22 -0700 Subject: Some tests actually tested for the lack of a namespace :-) --- nova/api/openstack/extensions.py | 3 ++- nova/api/openstack/faults.py | 5 +++-- nova/api/openstack/images.py | 3 --- nova/tests/api/openstack/test_images.py | 16 +++++++++++----- 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 75f369cac..7ea7afef6 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -156,7 +156,8 @@ class ResponseExtensionController(common.OpenstackController): body = res.body headers = res.headers except AttributeError: - body = self._serialize(res, content_type) + default_xmlns = None + body = self._serialize(res, content_type, default_xmlns) headers = {"Content-Type": content_type} res = webob.Response() res.body = body diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 0e9c4b26f..a05e97021 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -20,10 +20,10 @@ import webob.dec import webob.exc from nova import wsgi +from nova.api.openstack import common class Fault(webob.exc.HTTPException): - """An RS API fault response.""" _fault_names = { @@ -57,7 +57,8 @@ class Fault(webob.exc.HTTPException): fault_data[fault_name]['retryAfter'] = retry # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} - serializer = wsgi.Serializer(metadata) + default_xmlns = common.XML_NS_V10 + serializer = wsgi.Serializer(metadata, default_xmlns) content_type = req.best_match_content_type() self.wrapped_exc.body = serializer.serialize(fault_data, content_type) return self.wrapped_exc diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 5550ee532..77baf5947 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime - import webob.exc from nova import compute @@ -22,7 +20,6 @@ from nova import exception from nova import flags from nova import log from nova import utils -from nova import wsgi from nova.api.openstack import common from nova.api.openstack import faults from nova.api.openstack.views import images as images_view diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 57e447dce..ff3fe8976 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -146,7 +146,7 @@ class LocalImageServiceTest(_BaseImageServiceTests): for x in [1, 2, 3]: tempfile.mkstemp(prefix='ami-', dir=self.tempdir) # create some valid image directories names - for x in ["1485baed", "1a60f0ee", "3123a73d"]: + for x in ["1485baed", "1a60f0ee", "3123a73d"]: os.makedirs(os.path.join(self.tempdir, x)) found_image_ids = self.service._ids() self.assertEqual(True, isinstance(found_image_ids, list)) @@ -334,7 +334,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): name="public image" updated="%(expected_now)s" created="%(expected_now)s" - status="ACTIVE" /> + status="ACTIVE" + xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" /> """ % (locals())) self.assertEqual(expected_image.toxml(), actual_image.toxml()) @@ -353,7 +354,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): name="public image" updated="%(expected_now)s" created="%(expected_now)s" - status="ACTIVE"> + status="ACTIVE" + xmlns="http://docs.openstack.org/compute/api/v1.1"> + Image not found. @@ -422,8 +425,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): response = request.get_response(fakes.wsgi_app()) self.assertEqual(404, response.status_int) + # NOTE(justinsb): I believe this should still use the v1.0 XSD, + # because the element hasn't changed definition expected = minidom.parseString(""" - + Image not found. -- cgit From d2aa73efed6f4eb731c4fe8dca3f4ceb5b36caeb Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 10:49:09 -0700 Subject: More tests that were checking for no-namespace --- nova/tests/api/openstack/test_faults.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index 7667753f4..505e7e308 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -20,6 +20,7 @@ import webob.dec import webob.exc from nova import test +from nova.api.openstack import common from nova.api.openstack import faults @@ -30,8 +31,11 @@ class TestFaults(test.TestCase): f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) resp = req.get_response(f) - first_two_words = resp.body.strip().split()[:2] - self.assertEqual(first_two_words, ['']) + first_three_words = resp.body.strip().split()[:3] + self.assertEqual(first_three_words, + ['' % common.XML_NS_V10]) body_without_spaces = ''.join(resp.body.split()) self.assertTrue('scram' in body_without_spaces) @@ -41,8 +45,11 @@ class TestFaults(test.TestCase): headers={'Retry-After': 4}) f = faults.Fault(exc) resp = req.get_response(f) - first_two_words = resp.body.strip().split()[:2] - self.assertEqual(first_two_words, ['']) + first_three_words = resp.body.strip().split()[:3] + self.assertEqual(first_three_words, + ['' % common.XML_NS_V10]) body_sans_spaces = ''.join(resp.body.split()) self.assertTrue('sorry' in body_sans_spaces) self.assertTrue('4' in body_sans_spaces) -- cgit From 168fdb09f369287f3ec8c19ef73915280ff79d20 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 11:15:16 -0700 Subject: Refixed unit test to check XML ns --- nova/tests/api/openstack/test_faults.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_faults.py b/nova/tests/api/openstack/test_faults.py index b3433f45f..4d86ffb26 100644 --- a/nova/tests/api/openstack/test_faults.py +++ b/nova/tests/api/openstack/test_faults.py @@ -48,10 +48,10 @@ class TestFaults(test.TestCase): response = request.get_response(fault) expected = self._prepare_xml(""" - + scram - """) + """ % common.XML_NS_V10) actual = self._prepare_xml(response.body) self.assertEqual(response.content_type, "application/xml") @@ -92,11 +92,11 @@ class TestFaults(test.TestCase): response = request.get_response(fault) expected = self._prepare_xml(""" - + sorry 4 - """) + """ % common.XML_NS_V10) actual = self._prepare_xml(response.body) self.assertEqual(expected, actual) -- cgit From 323eb60884cf8736448d997068d32f252d22e7f3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 12:05:25 -0700 Subject: Key type values in ec2_api off of container format --- nova/api/ec2/cloud.py | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 7ba8dfbea..1e6c4d1db 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -146,7 +146,7 @@ class CloudController(object): floating_ip = db.instance_get_floating_address(ctxt, instance_ref['id']) ec2_id = ec2utils.id_to_ec2_id(instance_ref['id']) - image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'machine') + image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'ami') data = { 'user-data': base64.b64decode(instance_ref['user_data']), 'meta-data': { @@ -176,7 +176,7 @@ class CloudController(object): for image_type in ['kernel', 'ramdisk']: if '%s_id' % image_type in instance_ref: ec2_id = self._image_ec2_id(instance_ref['%s_id' % image_type], - image_type) + self._image_type(image_type)) data['meta-data']['%s-id' % image_type] = ec2_id if False: # TODO(vish): store ancestor ids @@ -862,13 +862,27 @@ class CloudController(object): self.compute_api.update(context, instance_id=instance_id, **kwargs) return True - _type_prefix_map = {'machine': 'ami', - 'kernel': 'aki', - 'ramdisk': 'ari'} + @staticmethod + def _image_type(image_type): + """Converts to a three letter image type. - def _image_ec2_id(self, image_id, image_type='machine'): - prefix = self._type_prefix_map[image_type] - template = prefix + '-%08x' + aki, kernel => aki + ari, ramdisk => ari + anything else => ami + + """ + if image_type == 'kernel': + return 'aki' + if image_type == 'ramdisk': + return 'ari' + if image_type not in ['aki', 'ari']: + return 'ami' + return image_type + + @staticmethod + def _image_ec2_id(image_id, image_type='ami'): + """Returns image ec2_id using id and three letter type""" + template = image_type + '-%08x' return ec2utils.id_to_ec2_id(int(image_id), template=template) def _get_image(self, context, ec2_id): @@ -881,7 +895,7 @@ class CloudController(object): def _format_image(self, image): """Convert from format defined by BaseImageService to S3 format.""" i = {} - image_type = image['properties'].get('type') + image_type = self._image_type(image.get('container_format')) ec2_id = self._image_ec2_id(image.get('id'), image_type) name = image.get('name') if name: @@ -890,16 +904,19 @@ class CloudController(object): i['imageId'] = ec2_id kernel_id = image['properties'].get('kernel_id') if kernel_id: - i['kernelId'] = self._image_ec2_id(kernel_id, 'kernel') + i['kernelId'] = self._image_ec2_id(kernel_id, 'aki') ramdisk_id = image['properties'].get('ramdisk_id') if ramdisk_id: - i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ramdisk') + i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ari') i['imageOwnerId'] = image['properties'].get('owner_id') i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') i['displayName'] = image.get('name') i['description'] = image.get('description') - i['type'] = image_type + display_mapping = {'aki': 'kernel', + 'ari': 'ramdisk', + 'ami': 'machine'} + i['type'] = display_mapping.get(image_type) i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' i['architecture'] = image['properties'].get('architecture') return i @@ -932,8 +949,9 @@ class CloudController(object): image_location = kwargs['name'] metadata = {'properties': {'image_location': image_location}} image = self.image_service.create(context, metadata) + image_type = self._image_type(image.get('container_format')) image_id = self._image_ec2_id(image['id'], - image['properties']['type']) + image_type) msg = _("Registered image %(image_location)s with" " id %(image_id)s") % locals() LOG.audit(msg, context=context) -- cgit From 1703592992ebdd5bbf19952f79f05022a4cdc849 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 12:34:10 -0700 Subject: remove all references to image_type and change nova-manage upload to set container format more intelligently --- nova/api/openstack/servers.py | 2 +- nova/image/fake.py | 6 +++--- nova/image/s3.py | 1 - nova/tests/api/openstack/test_image_metadata.py | 2 -- nova/virt/libvirt_conn.py | 1 - 5 files changed, 4 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index f7696d918..d640e8684 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -562,7 +562,7 @@ class Controller(wsgi.Controller): _("Cannot build from image %(image_id)s, status not active") % locals()) - if image_meta['properties']['disk_format'] != 'ami': + if image_meta.get('container_format') != 'ami': return None, None try: diff --git a/nova/image/fake.py b/nova/image/fake.py index 08302d6eb..d1c62757f 100644 --- a/nova/image/fake.py +++ b/nova/image/fake.py @@ -44,10 +44,10 @@ class FakeImageService(service.BaseImageService): 'created_at': timestamp, 'updated_at': timestamp, 'status': 'active', - 'type': 'machine', + 'container_format': 'ami', + 'disk_format': 'raw', 'properties': {'kernel_id': FLAGS.null_kernel, - 'ramdisk_id': FLAGS.null_kernel, - 'disk_format': 'ami'} + 'ramdisk_id': FLAGS.null_kernel} } self.create(None, image) super(FakeImageService, self).__init__() diff --git a/nova/image/s3.py b/nova/image/s3.py index ddec5f3aa..203bedc49 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -177,7 +177,6 @@ class S3ImageService(service.BaseImageService): properties['ramdisk_id'] = ec2utils.ec2_id_to_id(ramdisk_id) properties['is_public'] = False - properties['type'] = image_type metadata.update({'disk_format': image_format, 'container_format': image_format, 'status': 'queued', diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 9be753f84..7c3287006 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -45,7 +45,6 @@ class ImageMetaDataTest(unittest.TestCase): 'is_public': True, 'deleted_at': None, 'properties': { - 'type': 'ramdisk', 'key1': 'value1', 'key2': 'value2' }, @@ -62,7 +61,6 @@ class ImageMetaDataTest(unittest.TestCase): 'is_public': True, 'deleted_at': None, 'properties': { - 'type': 'ramdisk', 'key1': 'value1', 'key2': 'value2' }, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..adcb2ffa3 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -424,7 +424,6 @@ class LibvirtConnection(driver.ComputeDriver): 'container_format': base['container_format'], 'is_public': False, 'properties': {'architecture': base['architecture'], - 'type': base['type'], 'name': '%s.%s' % (base['name'], image_id), 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', -- cgit From b54b6c200092054e38af1fa1e5885fe915e53149 Mon Sep 17 00:00:00 2001 From: John Tran Date: Wed, 30 Mar 2011 13:10:11 -0700 Subject: submitting a unit test for terminate_instance --- nova/tests/test_cloud.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 00803d0ad..be1939193 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -36,6 +36,7 @@ from nova import rpc from nova import service from nova import test from nova import utils +from nova import exception from nova.auth import manager from nova.compute import power_state from nova.api.ec2 import cloud @@ -310,6 +311,19 @@ class CloudTestCase(test.TestCase): LOG.debug(_("Terminating instance %s"), instance_id) rv = self.compute.terminate_instance(instance_id) + def test_terminate_instances(self): + inst1 = db.instance_create(self.context, {'reservation_id': 'a', + 'image_id': 1, + 'host': 'host1'}) + terminate_instances = self.cloud.terminate_instances + # valid instance_id + result = terminate_instances(self.context, ['i-00000001']) + self.assertTrue(result) + # non-existing instance_id + self.assertRaises(exception.InstanceNotFound, terminate_instances, + self.context, ['i-2']) + db.instance_destroy(self.context, inst1['id']) + def test_update_of_instance_display_fields(self): inst = db.instance_create(self.context, {}) ec2_id = ec2utils.id_to_ec2_id(inst['id']) -- cgit From b8173ba9fe80045665064208e66f46cca75fd3ba Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 30 Mar 2011 13:48:07 -0700 Subject: review cleanup --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 1e6c4d1db..3541e49ca 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -881,7 +881,7 @@ class CloudController(object): @staticmethod def _image_ec2_id(image_id, image_type='ami'): - """Returns image ec2_id using id and three letter type""" + """Returns image ec2_id using id and three letter type.""" template = image_type + '-%08x' return ec2utils.id_to_ec2_id(int(image_id), template=template) -- cgit From 7f98416fdba59c4305f88a30a457bbf6c68ef372 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 31 Mar 2011 09:20:47 -0400 Subject: Limit image metadata to the configured metadata quota for a project. --- nova/api/openstack/image_metadata.py | 12 ++++++++ nova/tests/api/openstack/test_image_metadata.py | 39 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index c9d6ac532..76abdaa7e 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -18,6 +18,7 @@ from webob import exc from nova import flags +from nova import quota from nova import utils from nova import wsgi from nova.api.openstack import faults @@ -39,6 +40,15 @@ class Controller(wsgi.Controller): metadata = image.get('properties', {}) return metadata + def _check_quota_limit(self, context, metadata): + if metadata is None: + return + num_metadata = len(metadata) + quota_metadata = quota.allowed_metadata_items(context, num_metadata) + if quota_metadata < num_metadata: + expl = ("Image metadata limit exceeded") + raise exc.HTTPBadRequest(explanation=expl) + def index(self, req, image_id): """Returns the list of metadata for a given instance""" context = req.environ['nova.context'] @@ -61,6 +71,7 @@ class Controller(wsgi.Controller): if 'metadata' in body: for key, value in body['metadata'].iteritems(): metadata[key] = value + self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) return dict(metadata=metadata) @@ -77,6 +88,7 @@ class Controller(wsgi.Controller): img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) metadata[id] = body[id] + self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 9be753f84..51f64680b 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -67,6 +67,19 @@ class ImageMetaDataTest(unittest.TestCase): 'key2': 'value2' }, 'size': 5882349}, + {'status': 'active', + 'name': 'image3', + 'deleted': False, + 'container_format': None, + 'created_at': '2011-03-22T17:40:15', + 'disk_format': None, + 'updated_at': '2011-03-22T17:40:15', + 'id': '3', + 'location': 'file:///var/lib/glance/images/2', + 'is_public': True, + 'deleted_at': None, + 'properties': {}, + 'size': 5882349}, ] def setUp(self): @@ -77,6 +90,10 @@ class ImageMetaDataTest(unittest.TestCase): fakes.FakeAuthManager.auth_data = {} fakes.FakeAuthDatabase.data = {} fakes.stub_out_auth(self.stubs) + # NOTE(dprince) max out properties/metadata in image 3 for testing + img3 = self.IMAGE_FIXTURES[2] + for num in range(FLAGS.quota_metadata_items): + img3['properties']['key%i' % num] = "blah" fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES) def tearDown(self): @@ -164,3 +181,25 @@ class ImageMetaDataTest(unittest.TestCase): req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) self.assertEqual(404, res.status_int) + + def test_too_many_metadata_items_on_create(self): + data = {"metadata": {}} + for num in range(FLAGS.quota_metadata_items + 1): + data['metadata']['key%i' % num] = "blah" + json_string = str(data).replace("\'", "\"") + req = webob.Request.blank('/v1.1/images/2/meta') + req.environ['api.version'] = '1.1' + req.method = 'POST' + req.body = json_string + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) + + def test_too_many_metadata_items_on_put(self): + req = webob.Request.blank('/v1.1/images/3/meta/blah') + req.environ['api.version'] = '1.1' + req.method = 'PUT' + req.body = '{"blah": "blah"}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) -- cgit From 1654afaabba498ab690c07ccc6de858783dc1742 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 31 Mar 2011 12:38:05 -0700 Subject: makes sure s3 filtering works even without metadata set properly --- nova/image/s3.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/image/s3.py b/nova/image/s3.py index 203bedc49..def49f682 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -80,9 +80,10 @@ class S3ImageService(service.BaseImageService): @classmethod def _is_visible(cls, context, image): + return (context.is_admin - or context.project_id == image['properties']['owner_id'] - or image['properties']['is_public'] == 'True') + or context.project_id == image['properties'].get('owner_id') + or str(image['properties'].get('is_public')) == 'True') @classmethod def _filter(cls, context, images): -- cgit From 38b4cd9e68d7e1c262b08474b277573440ec3e87 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 4 Apr 2011 16:17:04 -0400 Subject: Refactor so that instances.instance_type is now instances.instance_type_id. Update the Openstack API to return the correct flavor_id. --- nova/api/ec2/cloud.py | 7 +- nova/api/openstack/servers.py | 6 +- nova/api/openstack/views/servers.py | 4 +- nova/compute/api.py | 18 +++-- nova/compute/instance_types.py | 46 ++++++------ nova/db/api.py | 5 ++ nova/db/sqlalchemy/api.py | 22 ++++++ .../014_add_instance_type_id_to_instances.py | 86 ++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 11 ++- nova/tests/api/openstack/test_servers.py | 24 +++--- nova/tests/db/fakes.py | 22 ++++-- nova/tests/test_compute.py | 18 +++-- nova/tests/test_console.py | 2 +- nova/tests/test_instance_types.py | 6 +- nova/tests/test_quota.py | 17 +++-- nova/tests/test_scheduler.py | 2 +- nova/tests/test_virt.py | 2 +- nova/tests/test_volume.py | 2 +- nova/tests/test_xenapi.py | 12 +-- nova/virt/libvirt_conn.py | 22 +++--- nova/virt/xenapi/vm_utils.py | 8 +- nova/virt/xenapi/vmops.py | 8 +- 22 files changed, 255 insertions(+), 95 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 7ba8dfbea..cd1195502 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -722,7 +722,10 @@ class CloudController(object): instance['project_id'], instance['host']) i['productCodesSet'] = self._convert_to_set([], 'product_codes') - i['instanceType'] = instance['instance_type'] + if instance['instance_type']: + i['instanceType'] = instance['instance_type'].get('name', None) + else: + i['instanceType'] = None i['launchTime'] = instance['created_at'] i['amiLaunchIndex'] = instance['launch_index'] i['displayName'] = instance['display_name'] @@ -805,7 +808,7 @@ class CloudController(object): ramdisk = self._get_image(context, kwargs['ramdisk_id']) kwargs['ramdisk_id'] = ramdisk['id'] instances = self.compute_api.create(context, - instance_type=instance_types.get_by_type( + instance_type=instance_types.get_instance_type_by_name( kwargs.get('instance_type', None)), image_id=self._get_image(context, kwargs['image_id'])['id'], min_count=int(kwargs.get('min_count', max_count)), diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 4e2ebb2bd..327911f6f 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -160,9 +160,11 @@ class Controller(wsgi.Controller): name = name.strip() try: + inst_type = \ + instance_types.get_instance_type_by_flavor_id(flavor_id) (inst,) = self.compute_api.create( context, - instance_types.get_by_flavor_id(flavor_id), + inst_type, image_id, kernel_id=kernel_id, ramdisk_id=ramdisk_id, @@ -175,7 +177,7 @@ class Controller(wsgi.Controller): except quota.QuotaError as error: self._handle_quota_error(error) - inst['instance_type'] = flavor_id + inst['instance_type'] = inst_type inst['image_id'] = requested_image_id builder = self._get_view_builder(req) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 4e7f62eb3..2f400eef6 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -115,7 +115,7 @@ class ViewBuilderV10(ViewBuilder): def _build_flavor(self, response, inst): if 'instance_type' in dict(inst): - response['flavorId'] = inst['instance_type'] + response['flavorId'] = inst['instance_type']['flavorid'] class ViewBuilderV11(ViewBuilder): @@ -134,7 +134,7 @@ class ViewBuilderV11(ViewBuilder): def _build_flavor(self, response, inst): if "instance_type" in dict(inst): - flavor_id = inst["instance_type"] + flavor_id = inst["instance_type"]['flavorid'] flavor_ref = self.flavor_builder.generate_href(flavor_id) response["flavorRef"] = flavor_ref diff --git a/nova/compute/api.py b/nova/compute/api.py index 1dbd73f8f..363d61f29 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -110,8 +110,11 @@ class API(base.Base): """Create the number of instances requested if quota and other arguments check out ok.""" - type_data = instance_types.get_instance_type(instance_type) - num_instances = quota.allowed_instances(context, max_count, type_data) + if not instance_type: + instance_type = instance_types.get_default_instance_type() + + num_instances = quota.allowed_instances(context, max_count, + instance_type) if num_instances < min_count: pid = context.project_id LOG.warn(_("Quota exceeeded for %(pid)s," @@ -197,10 +200,10 @@ class API(base.Base): 'user_id': context.user_id, 'project_id': context.project_id, 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), - 'instance_type': instance_type, - 'memory_mb': type_data['memory_mb'], - 'vcpus': type_data['vcpus'], - 'local_gb': type_data['local_gb'], + 'instance_type_id': instance_type['id'], + 'memory_mb': instance_type['memory_mb'], + 'vcpus': instance_type['vcpus'], + 'local_gb': instance_type['local_gb'], 'display_name': display_name, 'display_description': display_description, 'user_data': user_data or '', @@ -517,8 +520,7 @@ class API(base.Base): def resize(self, context, instance_id, flavor_id): """Resize a running instance.""" instance = self.db.instance_get(context, instance_id) - current_instance_type = self.db.instance_type_get_by_name( - context, instance['instance_type']) + current_instance_type = instance['instance_type'] new_instance_type = self.db.instance_type_get_by_flavor_id( context, flavor_id) diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index fa02a5dfa..5b1d92e29 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -101,41 +101,43 @@ def get_all_flavors(): return get_all_types(context.get_admin_context()) -def get_instance_type(name): - """Retrieves single instance type by name""" - if name is None: - return FLAGS.default_instance_type +def get_default_instance_type(): + name = FLAGS.default_instance_type try: - ctxt = context.get_admin_context() - inst_type = db.instance_type_get_by_name(ctxt, name) - return inst_type + return get_instance_type_by_name(name) except exception.DBError: raise exception.ApiError(_("Unknown instance type: %s" % name)) -def get_by_type(instance_type): - """retrieve instance type name""" - if instance_type is None: - return FLAGS.default_instance_type +def get_instance_type(id): + """Retrieves single instance type by id""" + if id is None: + return get_default_instance_type() + try: + ctxt = context.get_admin_context() + return db.instance_type_get_by_id(ctxt, id) + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s" % name)) + +def get_instance_type_by_name(name): + """Retrieves single instance type by name""" + if name is None: + return get_default_instance_type() try: ctxt = context.get_admin_context() - inst_type = db.instance_type_get_by_name(ctxt, instance_type) - return inst_type['name'] - except exception.DBError, e: - LOG.exception(_('DB error: %s' % e)) - raise exception.ApiError(_("Unknown instance type: %s" %\ - instance_type)) + return db.instance_type_get_by_name(ctxt, name) + except exception.DBError: + raise exception.ApiError(_("Unknown instance type: %s" % name)) -def get_by_flavor_id(flavor_id): - """retrieve instance type's name by flavor_id""" +def get_instance_type_by_flavor_id(flavor_id): + """retrieve instance type by flavor_id""" if flavor_id is None: - return FLAGS.default_instance_type + return get_default_instance_type() try: ctxt = context.get_admin_context() - flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id) - return flavor['name'] + return db.instance_type_get_by_flavor_id(ctxt, flavor_id) except exception.DBError, e: LOG.exception(_('DB error: %s' % e)) raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) diff --git a/nova/db/api.py b/nova/db/api.py index fd3c63b76..63901e94d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1124,6 +1124,11 @@ def instance_type_get_all(context, inactive=False): return IMPL.instance_type_get_all(context, inactive) +def instance_type_get_by_id(context, id): + """Get instance type by id""" + return IMPL.instance_type_get_by_id(context, id) + + def instance_type_get_by_name(context, name): """Get instance type by name""" return IMPL.instance_type_get_by_name(context, name) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b2a13a01b..9f600b236 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -829,6 +829,7 @@ def instance_get(context, instance_id, session=None): options(joinedload('volumes')).\ options(joinedload_all('fixed_ip.network')).\ options(joinedload('metadata')).\ + options(joinedload('instance_type')).\ filter_by(id=instance_id).\ filter_by(deleted=can_read_deleted(context)).\ first() @@ -838,6 +839,7 @@ def instance_get(context, instance_id, session=None): options(joinedload_all('security_groups.rules')).\ options(joinedload('volumes')).\ options(joinedload('metadata')).\ + options(joinedload('instance_type')).\ filter_by(project_id=context.project_id).\ filter_by(id=instance_id).\ filter_by(deleted=False).\ @@ -857,6 +859,7 @@ def instance_get_all(context): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -868,6 +871,7 @@ def instance_get_all_by_user(context, user_id): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(deleted=can_read_deleted(context)).\ filter_by(user_id=user_id).\ all() @@ -880,6 +884,7 @@ def instance_get_all_by_host(context, host): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(host=host).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -894,6 +899,7 @@ def instance_get_all_by_project(context, project_id): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(project_id=project_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -908,6 +914,7 @@ def instance_get_all_by_reservation(context, reservation_id): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(reservation_id=reservation_id).\ filter_by(deleted=can_read_deleted(context)).\ all() @@ -916,6 +923,7 @@ def instance_get_all_by_reservation(context, reservation_id): options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ options(joinedload_all('fixed_ip.network')).\ + options(joinedload('instance_type')).\ filter_by(project_id=context.project_id).\ filter_by(reservation_id=reservation_id).\ filter_by(deleted=False).\ @@ -928,6 +936,7 @@ def instance_get_project_vpn(context, project_id): return session.query(models.Instance).\ options(joinedload_all('fixed_ip.floating_ips')).\ options(joinedload('security_groups')).\ + options(joinedload('instance_type')).\ filter_by(project_id=project_id).\ filter_by(image_id=FLAGS.vpn_image_id).\ filter_by(deleted=can_read_deleted(context)).\ @@ -2368,6 +2377,19 @@ def instance_type_get_all(context, inactive=False): raise exception.NotFound +@require_context +def instance_type_get_by_id(context, id): + """Returns a dict describing specific instance_type""" + session = get_session() + inst_type = session.query(models.InstanceTypes).\ + filter_by(id=id).\ + first() + if not inst_type: + raise exception.NotFound(_("No instance type with id %s") % id) + else: + return dict(inst_type) + + @require_context def instance_type_get_by_name(context, name): """Returns a dict describing specific instance_type""" diff --git a/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py new file mode 100644 index 000000000..813e57e10 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py @@ -0,0 +1,86 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 OpenStack LLC. +# +# 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 sqlalchemy import * +from sqlalchemy.sql import text +from migrate import * + +#from nova import log as logging + + +meta = MetaData() + + +c_instance_type = Column('instance_type', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False), + nullable=True) + +c_instance_type_id = Column('instance_type_id', + String(length=255, convert_unicode=False, + assert_unicode=None, unicode_error=None, + _warn_on_bytestring=False), + nullable=True) + +instance_types = Table('instance_types', meta, + Column('id', Integer(), primary_key=True, nullable=False), + Column('name', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False), + unique=True)) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + + instances = Table('instances', meta, autoload=True, + autoload_with=migrate_engine) + + instances.create_column(c_instance_type_id) + + recs = migrate_engine.execute(instance_types.select()) + for row in recs: + type_id = row[0] + type_name = row[1] + migrate_engine.execute(instances.update()\ + .where(instances.c.instance_type == type_name)\ + .values(instance_type_id=type_id)) + + instances.c.instance_type.drop() + #instances.c.instance_type_id.alter(nullable=False) + + +def downgrade(migrate_engine): + meta.bind = migrate_engine + + instances = Table('instances', meta, autoload=True, + autoload_with=migrate_engine) + + instances.create_column(c_instance_type) + + recs = migrate_engine.execute(instance_types.select()) + for row in recs: + type_id = row[0] + type_name = row[1] + migrate_engine.execute(instances.update()\ + .where(instances.c.instance_type_id == type_id)\ + .values(instance_type=type_name)) + + instances.c.instance_type_id.drop() + #instances.c.instance_type.alter(nullable=False) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 3b95ac23e..9d4c6cdef 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -209,7 +209,7 @@ class Instance(BASE, NovaBase): hostname = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) - instance_type = Column(String(255)) + instance_type_id = Column(String(255)) user_data = Column(Text) @@ -258,7 +258,8 @@ class InstanceActions(BASE, NovaBase): class InstanceTypes(BASE, NovaBase): """Represent possible instance_types or flavor of VM offered""" __tablename__ = "instance_types" - id = Column(Integer, primary_key=True) + id = Column(Integer, ForeignKey('instances.instance_type_id'), + primary_key=True) name = Column(String(255), unique=True) memory_mb = Column(Integer) vcpus = Column(Integer) @@ -268,6 +269,12 @@ class InstanceTypes(BASE, NovaBase): rxtx_quota = Column(Integer, nullable=False, default=0) rxtx_cap = Column(Integer, nullable=False, default=0) + instances = relationship(Instance, + backref=backref('instance_type', uselist=False), + foreign_keys=id, + primaryjoin='and_(Instance.instance_type_id == ' + 'InstanceTypes.id)') + class Volume(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index cb5047da9..899dcf7f7 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -32,6 +32,7 @@ from nova import test import nova.api.openstack from nova.api.openstack import servers import nova.compute.api +from nova.compute import instance_types import nova.db.api from nova.db.sqlalchemy.models import Instance from nova.db.sqlalchemy.models import InstanceMetadata @@ -71,13 +72,19 @@ def instance_address(context, instance_id): return None -def stub_instance(id, user_id=1, private_address=None, public_addresses=None): +def stub_instance(id, user_id=1, private_address=None, public_addresses=None, + host=None): metadata = [] metadata.append(InstanceMetadata(key='seq', value=id)) + inst_type = instance_types.get_instance_type_by_flavor_id(1) + if public_addresses == None: public_addresses = list() + if host != None: + host = str(host) + instance = { "id": id, "admin_pass": "", @@ -95,8 +102,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None): "vcpus": 0, "local_gb": 0, "hostname": "", - "host": None, - "instance_type": "1", + "host": host, + "instance_type": dict(inst_type), "user_data": "", "reservation_id": "", "mac_address": "", @@ -630,7 +637,7 @@ class ServersTest(test.TestCase): self.assertEqual(s['hostId'], '') self.assertEqual(s['name'], 'server%d' % i) self.assertEqual(s['imageId'], '10') - self.assertEqual(s['flavorId'], '1') + self.assertEqual(s['flavorId'], 1) self.assertEqual(s['metadata']['seq'], i) def test_get_all_server_details_v1_1(self): @@ -654,12 +661,8 @@ class ServersTest(test.TestCase): instances - 2 on one host and 3 on another. ''' - def stub_instance(id, user_id=1): - return Instance(id=id, state=0, image_id=10, user_id=user_id, - display_name='server%s' % id, host='host%s' % (id % 2)) - def return_servers_with_host(context, user_id=1): - return [stub_instance(i) for i in xrange(5)] + return [stub_instance(i, 1, None, None, i % 2) for i in xrange(5)] self.stubs.Set(nova.db.api, 'instance_get_all_by_user', return_servers_with_host) @@ -677,7 +680,8 @@ class ServersTest(test.TestCase): self.assertEqual(s['id'], i) self.assertEqual(s['hostId'], host_ids[i % 2]) self.assertEqual(s['name'], 'server%d' % i) - self.assertEqual(s['imageId'], 10) + self.assertEqual(s['imageId'], '10') + self.assertEqual(s['flavorId'], 1) def test_server_pause(self): FLAGS.allow_admin_api = True diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 7ddfe377a..58d251b1e 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -28,29 +28,34 @@ def stub_out_db_instance_api(stubs, injected=True): """Stubs out the db API for creating Instances.""" INSTANCE_TYPES = { - 'm1.tiny': dict(memory_mb=512, + 'm1.tiny': dict(id=2, + memory_mb=512, vcpus=1, local_gb=0, flavorid=1, rxtx_cap=1), - 'm1.small': dict(memory_mb=2048, + 'm1.small': dict(id=5, + memory_mb=2048, vcpus=1, local_gb=20, flavorid=2, rxtx_cap=2), 'm1.medium': - dict(memory_mb=4096, + dict(id=1, + memory_mb=4096, vcpus=2, local_gb=40, flavorid=3, rxtx_cap=3), - 'm1.large': dict(memory_mb=8192, + 'm1.large': dict(id=3, + memory_mb=8192, vcpus=4, local_gb=80, flavorid=4, rxtx_cap=4), 'm1.xlarge': - dict(memory_mb=16384, + dict(id=4, + memory_mb=16384, vcpus=8, local_gb=160, flavorid=5, @@ -107,6 +112,12 @@ def stub_out_db_instance_api(stubs, injected=True): def fake_instance_type_get_by_name(context, name): return INSTANCE_TYPES[name] + def fake_instance_type_get_by_id(context, id): + for name, inst_type in INSTANCE_TYPES.iteritems(): + if str(inst_type['id']) == str(id): + return inst_type + return None + def fake_network_get_by_instance(context, instance_id): # Even instance numbers are on vlan networks if instance_id % 2 == 0: @@ -136,6 +147,7 @@ def stub_out_db_instance_api(stubs, injected=True): fake_network_get_all_by_instance) stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) + stubs.Set(db, 'instance_type_get_by_id', fake_instance_type_get_by_id) stubs.Set(db, 'instance_get_fixed_address', fake_instance_get_fixed_address) stubs.Set(db, 'instance_get_fixed_address_v6', diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 1b0f426d2..1917dff3e 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -84,7 +84,8 @@ class ComputeTestCase(test.TestCase): inst['launch_time'] = '10' inst['user_id'] = self.user.id inst['project_id'] = self.project.id - inst['instance_type'] = 'm1.tiny' + type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] + inst['instance_type_id'] = type_id inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = 0 inst.update(params) @@ -132,7 +133,7 @@ class ComputeTestCase(test.TestCase): cases = [dict(), dict(display_name=None)] for instance in cases: ref = self.compute_api.create(self.context, - FLAGS.default_instance_type, None, **instance) + instance_types.get_default_instance_type(), None, **instance) try: self.assertNotEqual(ref[0]['display_name'], None) finally: @@ -143,7 +144,7 @@ class ComputeTestCase(test.TestCase): group = self._create_group() ref = self.compute_api.create( self.context, - instance_type=FLAGS.default_instance_type, + instance_type=instance_types.get_default_instance_type(), image_id=None, security_group=['testgroup']) try: @@ -161,7 +162,7 @@ class ComputeTestCase(test.TestCase): ref = self.compute_api.create( self.context, - instance_type=FLAGS.default_instance_type, + instance_type=instance_types.get_default_instance_type(), image_id=None, security_group=['testgroup']) try: @@ -177,7 +178,7 @@ class ComputeTestCase(test.TestCase): ref = self.compute_api.create( self.context, - instance_type=FLAGS.default_instance_type, + instance_type=instance_types.get_default_instance_type(), image_id=None, security_group=['testgroup']) @@ -359,8 +360,9 @@ class ComputeTestCase(test.TestCase): instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) + inst_type = instance_types.get_instance_type_by_name('m1.xlarge') db.instance_update(self.context, instance_id, - {'instance_type': 'm1.xlarge'}) + {'instance_type_id': inst_type['id']}) self.assertRaises(exception.ApiError, self.compute_api.resize, context, instance_id, 1) @@ -380,8 +382,8 @@ class ComputeTestCase(test.TestCase): self.compute.terminate_instance(context, instance_id) def test_get_by_flavor_id(self): - type = instance_types.get_by_flavor_id(1) - self.assertEqual(type, 'm1.tiny') + type = instance_types.get_instance_type_by_flavor_id(1) + self.assertEqual(type['name'], 'm1.tiny') def test_resize_same_source_fails(self): """Ensure instance fails to migrate when source and destination are diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py index d47c70d88..1a9a867ee 100644 --- a/nova/tests/test_console.py +++ b/nova/tests/test_console.py @@ -62,7 +62,7 @@ class ConsoleTestCase(test.TestCase): inst['launch_time'] = '10' inst['user_id'] = self.user.id inst['project_id'] = self.project.id - inst['instance_type'] = 'm1.tiny' + inst['instance_type_id'] = 1 inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = 0 return db.instance_create(self.context, inst)['id'] diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index edc538879..5d6d5e1f4 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -40,7 +40,11 @@ class InstanceTypeTestCase(test.TestCase): max_flavorid = session.query(models.InstanceTypes).\ order_by("flavorid desc").\ first() + max_id = session.query(models.InstanceTypes).\ + order_by("id desc").\ + first() self.flavorid = max_flavorid["flavorid"] + 1 + self.id = max_id["id"] + 1 self.name = str(int(time.time())) def test_instance_type_create_then_delete(self): @@ -53,7 +57,7 @@ class InstanceTypeTestCase(test.TestCase): 'instance type was not created') instance_types.destroy(self.name) self.assertEqual(1, - instance_types.get_instance_type(self.name)["deleted"]) + instance_types.get_instance_type(self.id)["deleted"]) self.assertEqual(starting_inst_list, instance_types.get_all_types()) instance_types.purge(self.name) self.assertEqual(len(starting_inst_list), diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py index c65bc459d..39a123158 100644 --- a/nova/tests/test_quota.py +++ b/nova/tests/test_quota.py @@ -67,7 +67,7 @@ class QuotaTestCase(test.TestCase): inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user.id inst['project_id'] = self.project.id - inst['instance_type'] = 'm1.large' + inst['instance_type_id'] = '3' # m1.large inst['vcpus'] = cores inst['mac_address'] = utils.generate_mac() return db.instance_create(self.context, inst)['id'] @@ -124,11 +124,12 @@ class QuotaTestCase(test.TestCase): for i in range(FLAGS.quota_instances): instance_id = self._create_instance() instance_ids.append(instance_id) + inst_type = instance_types.get_instance_type_by_name('m1.small') self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, - instance_type='m1.small', + instance_type=inst_type, image_id=1) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -137,11 +138,12 @@ class QuotaTestCase(test.TestCase): instance_ids = [] instance_id = self._create_instance(cores=4) instance_ids.append(instance_id) + inst_type = instance_types.get_instance_type_by_name('m1.small') self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, - instance_type='m1.small', + instance_type=inst_type, image_id=1) for instance_id in instance_ids: db.instance_destroy(self.context, instance_id) @@ -192,11 +194,12 @@ class QuotaTestCase(test.TestCase): metadata = {} for i in range(FLAGS.quota_metadata_items + 1): metadata['key%s' % i] = 'value%s' % i + inst_type = instance_types.get_instance_type_by_name('m1.small') self.assertRaises(quota.QuotaError, compute.API().create, self.context, min_count=1, max_count=1, - instance_type='m1.small', + instance_type=inst_type, image_id='fake', metadata=metadata) @@ -207,13 +210,15 @@ class QuotaTestCase(test.TestCase): def _create_with_injected_files(self, files): api = compute.API(image_service=self.StubImageService()) + inst_type = instance_types.get_instance_type_by_name('m1.small') api.create(self.context, min_count=1, max_count=1, - instance_type='m1.small', image_id='fake', + instance_type=inst_type, image_id='fake', injected_files=files) def test_no_injected_files(self): api = compute.API(image_service=self.StubImageService()) - api.create(self.context, instance_type='m1.small', image_id='fake') + inst_type = instance_types.get_instance_type_by_name('m1.small') + api.create(self.context, instance_type=inst_type, image_id='fake') def test_max_injected_files(self): files = [] diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py index 6df74dd61..ae56a1a16 100644 --- a/nova/tests/test_scheduler.py +++ b/nova/tests/test_scheduler.py @@ -263,7 +263,7 @@ class SimpleDriverTestCase(test.TestCase): inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user.id inst['project_id'] = self.project.id - inst['instance_type'] = 'm1.tiny' + inst['instance_type_id'] = '1' inst['mac_address'] = utils.generate_mac() inst['vcpus'] = kwargs.get('vcpus', 1) inst['ami_launch_index'] = 0 diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 958c8e3e2..c13cbf043 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -140,7 +140,7 @@ class LibvirtConnTestCase(test.TestCase): 'vcpus': 2, 'project_id': 'fake', 'bridge': 'br101', - 'instance_type': 'm1.small'} + 'instance_type_id': '5'} # m1.small def lazy_load_library_exists(self): """check if libvirt is available.""" diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py index d71b75f3f..e9d8289aa 100644 --- a/nova/tests/test_volume.py +++ b/nova/tests/test_volume.py @@ -106,7 +106,7 @@ class VolumeTestCase(test.TestCase): inst['launch_time'] = '10' inst['user_id'] = 'fake' inst['project_id'] = 'fake' - inst['instance_type'] = 'm1.tiny' + inst['instance_type_id'] = '2' # m1.tiny inst['mac_address'] = utils.generate_mac() inst['ami_launch_index'] = 0 instance_id = db.instance_create(self.context, inst)['id'] diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 17e3f55e9..665ec068e 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -80,7 +80,7 @@ class XenAPIVolumeTestCase(test.TestCase): 'image_id': 1, 'kernel_id': 2, 'ramdisk_id': 3, - 'instance_type': 'm1.large', + 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': 'linux'} @@ -328,7 +328,7 @@ class XenAPIVMTestCase(test.TestCase): self.assertEquals(self.vm['HVM_boot_policy'], '') def _test_spawn(self, image_id, kernel_id, ramdisk_id, - instance_type="m1.large", os_type="linux", + instance_type_id="3", os_type="linux", instance_id=1, check_injection=False): stubs.stubout_loopingcall_start(self.stubs) values = {'id': instance_id, @@ -337,7 +337,7 @@ class XenAPIVMTestCase(test.TestCase): 'image_id': image_id, 'kernel_id': kernel_id, 'ramdisk_id': ramdisk_id, - 'instance_type': instance_type, + 'instance_type_id': instance_type_id, 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': os_type} instance = db.instance_create(self.context, values) @@ -349,7 +349,7 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self.assertRaises(Exception, self._test_spawn, - 1, 2, 3, "m1.xlarge") + 1, 2, 3, "4") # m1.xlarge def test_spawn_raw_objectstore(self): FLAGS.xenapi_image_service = 'objectstore' @@ -523,7 +523,7 @@ class XenAPIVMTestCase(test.TestCase): 'image_id': 1, 'kernel_id': 2, 'ramdisk_id': 3, - 'instance_type': 'm1.large', + 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': 'linux'} instance = db.instance_create(self.context, values) @@ -580,7 +580,7 @@ class XenAPIMigrateInstance(test.TestCase): 'kernel_id': None, 'ramdisk_id': None, 'local_gb': 5, - 'instance_type': 'm1.large', + 'instance_type_id': '3', # m1.large 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': 'linux'} diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f34ea7225..6b7fce634 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -797,7 +797,10 @@ class LibvirtConnection(driver.ComputeDriver): root_fname = '%08x' % int(disk_images['image_id']) size = FLAGS.minimum_root_size - if inst['instance_type'] == 'm1.tiny' or suffix == '.rescue': + + inst_type_id = instance['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) + if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': size = None root_fname += "_sm" @@ -809,14 +812,13 @@ class LibvirtConnection(driver.ComputeDriver): user=user, project=project, size=size) - type_data = instance_types.get_instance_type(inst['instance_type']) - if type_data['local_gb']: + if inst_type['local_gb']: self._cache_image(fn=self._create_local, target=basepath('disk.local'), - fname="local_%s" % type_data['local_gb'], + fname="local_%s" % inst_type['local_gb'], cow=FLAGS.use_cow_images, - local_gb=type_data['local_gb']) + local_gb=inst_type['local_gb']) # For now, we assume that if we're not using a kernel, we're using a # partitioned disk image where the target partition is the first @@ -950,8 +952,8 @@ class LibvirtConnection(driver.ComputeDriver): nics.append(self._get_nic_for_xml(network, mapping)) # FIXME(vish): stick this in db - instance_type_name = instance['instance_type'] - instance_type = instance_types.get_instance_type(instance_type_name) + inst_type_id = instance['instance_type_id'] + inst_type = instance_types.get_instance_type(inst_type_id) if FLAGS.use_cow_images: driver_type = 'qcow2' @@ -962,10 +964,10 @@ class LibvirtConnection(driver.ComputeDriver): 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, instance['name']), - 'memory_kb': instance_type['memory_mb'] * 1024, - 'vcpus': instance_type['vcpus'], + 'memory_kb': inst_type['memory_mb'] * 1024, + 'vcpus': inst_type['vcpus'], 'rescue': rescue, - 'local': instance_type['local_gb'], + 'local': inst_type['local_gb'], 'driver_type': driver_type, 'nics': nics} diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..fd4f3705a 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -101,8 +101,8 @@ class VMHelper(HelperBase): 3. Using hardware virtualization """ - instance_type = instance_types.\ - get_instance_type(instance.instance_type) + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) mem = str(long(instance_type['memory_mb']) * 1024 * 1024) vcpus = str(instance_type['vcpus']) rec = { @@ -169,8 +169,8 @@ class VMHelper(HelperBase): @classmethod def ensure_free_mem(cls, session, instance): - instance_type = instance_types.get_instance_type( - instance.instance_type) + inst_type_id = instance.instance_type_id + instance_type = instance_types.get_instance_type(inst_type_id) mem = long(instance_type['memory_mb']) * 1024 * 1024 #get free memory from host host = session.get_xenapi_host() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c96c35a6e..c26965c9a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -802,8 +802,10 @@ class VMOps(object): instance['id']) networks = db.network_get_all_by_instance(admin_context, instance['id']) - flavor = db.instance_type_get_by_name(admin_context, - instance['instance_type']) + + inst_type = db.instance_type_get_by_id(admin_context, + instance['instance_type_id']) + network_info = [] for network in networks: network_IPs = [ip for ip in IPs if ip.network_id == network.id] @@ -827,7 +829,7 @@ class VMOps(object): 'gateway': network['gateway'], 'broadcast': network['broadcast'], 'mac': instance.mac_address, - 'rxtx_cap': flavor['rxtx_cap'], + 'rxtx_cap': inst_type['rxtx_cap'], 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_IPs]} if network['cidr_v6']: -- cgit From 5c67809e4b9a1546c48316ea52676dfeba8f1a75 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 4 Apr 2011 15:58:41 -0700 Subject: openstack api requires uppercase image format status responses --- nova/api/openstack/views/images.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 16195b050..9dec8a355 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -34,11 +34,11 @@ class ViewBuilder(object): def _format_status(self, image): """Update the status field to standardize format.""" status_mapping = { - 'pending': 'queued', - 'decrypting': 'preparing', - 'untarring': 'saving', - 'available': 'active', - 'killed': 'failed', + 'pending': 'QUEUED', + 'decrypting': 'PREPARING', + 'untarring': 'SAVING', + 'available': 'ACTIVE', + 'killed': 'FAILED', } try: -- cgit From a6c283f00d67a5172d8271d7e48bd58484ac6b96 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 4 Apr 2011 16:00:59 -0700 Subject: openstack api metadata responses must be strings --- nova/api/openstack/views/servers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index d24c025be..baa911590 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -82,7 +82,8 @@ class ViewBuilder(object): # Return the metadata as a dictionary metadata = {} for item in inst.get('metadata', []): - metadata[item['key']] = item['value'] + # metadata values must be strings per API + metadata[item['key']] = str(item['value']) inst_dict['metadata'] = metadata inst_dict['hostId'] = '' -- cgit From 868288b9705ea765b5515fd0ac9e0858f37239d0 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Mon, 4 Apr 2011 16:07:39 -0700 Subject: correct test for numeric/string metadata value conversion --- nova/tests/api/openstack/test_servers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 313676e72..08a3f5d5a 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -630,7 +630,7 @@ class ServersTest(test.TestCase): self.assertEqual(s['imageId'], '10') self.assertEqual(s['flavorId'], '1') self.assertEqual(s['status'], 'BUILD') - self.assertEqual(s['metadata']['seq'], i) + self.assertEqual(s['metadata']['seq'], str(i)) def test_get_all_server_details_v1_1(self): req = webob.Request.blank('/v1.1/servers/detail') @@ -644,7 +644,7 @@ class ServersTest(test.TestCase): self.assertEqual(s['imageRef'], 'http://localhost/v1.1/images/10') self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1') self.assertEqual(s['status'], 'BUILD') - self.assertEqual(s['metadata']['seq'], i) + self.assertEqual(s['metadata']['seq'], str(i)) def test_get_all_server_details_with_host(self): ''' -- cgit From a18fece993c21f2ae1cbb44d8a0dea92d58d3b44 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 4 Apr 2011 22:16:53 -0400 Subject: Correct variable name. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d4cef8d7c..099ef647c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -798,7 +798,7 @@ class LibvirtConnection(driver.ComputeDriver): root_fname = '%08x' % int(disk_images['image_id']) size = FLAGS.minimum_root_size - inst_type_id = instance['instance_type_id'] + inst_type_id = inst['instance_type_id'] inst_type = instance_types.get_instance_type(inst_type_id) if inst_type['name'] == 'm1.tiny' or suffix == '.rescue': size = None -- cgit From 7702affdbf09127e3e7cdfafcb6382673914438e Mon Sep 17 00:00:00 2001 From: Masanori Itoh Date: Tue, 5 Apr 2011 18:43:11 +0900 Subject: Remove and from AllocateAddress response, and fix bug #751176. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 425784e8a..3b333437b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -777,7 +777,7 @@ class CloudController(object): def allocate_address(self, context, **kwargs): LOG.audit(_("Allocate address"), context=context) public_ip = self.network_api.allocate_floating_ip(context) - return {'addressSet': [{'publicIp': public_ip}]} + return {'publicIp': public_ip} def release_address(self, context, public_ip, **kwargs): LOG.audit(_("Release address %s"), public_ip, context=context) -- cgit From 2b3aea4be35f370c68ac3c24ab15d4851aa28e94 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Tue, 5 Apr 2011 20:10:10 +0900 Subject: fix bug 746821 --- nova/tests/test_virt.py | 3 ++- nova/virt/libvirt_conn.py | 38 +++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 958c8e3e2..2e9eac0d5 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -479,7 +479,7 @@ class LibvirtConnTestCase(test.TestCase): fake_timer = FakeTime() - self.create_fake_libvirt_mock(nwfilterLookupByName=fake_raise) + self.create_fake_libvirt_mock() instance_ref = db.instance_create(self.context, self.test_instance) # Start test @@ -488,6 +488,7 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_conn.LibvirtConnection(False) conn.firewall_driver.setattr('setup_basic_filtering', fake_none) conn.firewall_driver.setattr('prepare_instance_filter', fake_none) + conn.firewall_driver.setattr('instance_filter_exists', fake_none) conn.ensure_filtering_rules_for_instance(instance_ref, time=fake_timer) except exception.Error, e: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index babbc610d..bdf577825 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1401,18 +1401,13 @@ class LibvirtConnection(driver.ComputeDriver): # wait for completion timeout_count = range(FLAGS.live_migration_retry_count) while timeout_count: - try: - filter_name = 'nova-instance-%s' % instance_ref.name - self._conn.nwfilterLookupByName(filter_name) + if self.firewall_driver.instance_filter_exists(instance_ref): break - except libvirt.libvirtError: - timeout_count.pop() - if len(timeout_count) == 0: - ec2_id = instance_ref['hostname'] - iname = instance_ref.name - msg = _('Timeout migrating for %(ec2_id)s(%(iname)s)') - raise exception.Error(msg % locals()) - time.sleep(1) + timeout_count.pop() + if len(timeout_count) == 0: + msg = _('Timeout migrating for %s. nwfilter not found.') + raise exception.Error(msg % instance_ref.name) + time.sleep(1) def live_migration(self, ctxt, instance_ref, dest, post_method, recover_method): @@ -1541,6 +1536,10 @@ class FirewallDriver(object): """ raise NotImplementedError() + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + raise NotImplementedError() + class NWFilterFirewall(FirewallDriver): """ @@ -1848,6 +1847,19 @@ class NWFilterFirewall(FirewallDriver): return 'nova-instance-%s' % (instance['name']) return 'nova-instance-%s-%s' % (instance['name'], nic_id) + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + + network_info = _get_network_info(instance) + for (network, mapping) in network_info: + nic_id = mapping['mac'].replace(':', '') + instance_filter_name = self._instance_filter_name(instance, nic_id) + try: + self._conn.nwfilterLookupByName(instance_filter_name) + except libvirt.libvirtError: + return False + return True + class IptablesFirewallDriver(FirewallDriver): def __init__(self, execute=None, **kwargs): @@ -2037,6 +2049,10 @@ class IptablesFirewallDriver(FirewallDriver): return ipv4_rules, ipv6_rules + def instance_filter_exists(self, instance): + """Check nova-instance-instance-xxx exists""" + return self.nwfilter.instance_filter_exists(instance) + def refresh_security_group_members(self, security_group): pass -- cgit From 4d1f60df049e11623df7d6174eda19e5e27d004e Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Tue, 5 Apr 2011 20:20:32 +0900 Subject: fix bug lp751231 --- nova/compute/manager.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 08b772517..37a904b1e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1090,6 +1090,15 @@ class ComputeManager(manager.SchedulerDependentManager): vm_state = vm_instance.state vms_not_found_in_db.remove(name) + if db_instance['state_description'] == 'migrating': + # A situation which db record exists, but no instance" + # sometimes occurs while live-migration at src compute, + # this case should be ignored. + LOG.info(_("the instance '%(name)s' is not found in hypervisor" + ", while db record is found. But not synchronize " + "since it is migrating." % locals())) + continue + if vm_state != db_state: LOG.info(_("DB/VM state mismatch. Changing state from " "'%(db_state)s' to '%(vm_state)s'") % locals()) -- cgit From ca2ce6dc4a96c7bded2c30c258b7becaee5a1ed7 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Tue, 5 Apr 2011 20:39:22 +0900 Subject: fix bug lp751242 --- nova/compute/api.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 996955fe3..f67aa47e8 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -372,6 +372,10 @@ class API(base.Base): instance_id) return + if (instance['state_description'] == 'migrating'): + LOG.warning(_("Instance %s is being migrated"), instance_id) + return + self.update(context, instance['id'], state_description='terminating', -- cgit From d8ab404fd74facd181eaaee7cbc78b39a82afd1c Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 5 Apr 2011 08:26:16 -0400 Subject: Add missing underscore. --- nova/api/openstack/image_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 76abdaa7e..c733172ae 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -46,7 +46,7 @@ class Controller(wsgi.Controller): num_metadata = len(metadata) quota_metadata = quota.allowed_metadata_items(context, num_metadata) if quota_metadata < num_metadata: - expl = ("Image metadata limit exceeded") + expl = _("Image metadata limit exceeded") raise exc.HTTPBadRequest(explanation=expl) def index(self, req, image_id): -- cgit From e6505fd78cc28a5e91ec6fbf55e2e8de07679c7c Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Tue, 5 Apr 2011 10:56:24 -0400 Subject: Fixed the addresses and metadata collections in xml responses. Added corresponding tests. --- nova/api/openstack/servers.py | 7 +++++++ nova/tests/api/openstack/test_servers.py | 36 ++++++++++++++++++++++++++++++++ nova/wsgi.py | 18 ++++++++++++++++ 3 files changed, 61 insertions(+) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 6704a68ae..cada92813 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -55,6 +55,13 @@ class Controller(wsgi.Controller): "imageRef"], "link": ["rel", "type", "href"], }, + "dict_collections": { + "metadata": {"item_name": "meta", "item_key": "key"}, + }, + "list_collections": { + "public": {"item_name": "ip", "item_key": "addr"}, + "private": {"item_name": "ip", "item_key": "addr"}, + }, }, } diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 313676e72..ce9821587 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -192,6 +192,26 @@ class ServersTest(test.TestCase): print res_dict['server'] self.assertEqual(res_dict['server']['links'], expected_links) + def test_get_server_by_id_with_addresses_xml(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + dom = minidom.parseString(res.body) + server = dom.childNodes[0] + self.assertEquals(server.nodeName, 'server') + self.assertEquals(server.getAttribute('id'), '1') + self.assertEquals(server.getAttribute('name'), 'server1') + (public,) = server.getElementsByTagName('public'); + (ip,) = public.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), '1.2.3.4') + (private,) = server.getElementsByTagName('private') + (ip,) = private.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), '192.168.0.3') + def test_get_server_by_id_with_addresses(self): private = "192.168.0.3" public = ["1.2.3.4"] @@ -618,6 +638,22 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 404) + def test_get_all_server_details_xml_v1_0(self): + req = webob.Request.blank('/v1.0/servers/detail') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + print res.body + dom = minidom.parseString(res.body) + for i, server in enumerate(dom.getElementsByTagName('server')): + self.assertEqual(server.getAttribute('id'), str(i)) + self.assertEqual(server.getAttribute('hostId'), '') + self.assertEqual(server.getAttribute('name'), 'server%d' % i) + self.assertEqual(server.getAttribute('imageId'), '10') + self.assertEqual(server.getAttribute('status'), 'BUILD') + (meta,) = server.getElementsByTagName('meta') + self.assertEqual(meta.getAttribute('key'), 'seq') + self.assertEqual(meta.firstChild.data.strip(), str(i)) + def test_get_all_server_details_v1_0(self): req = webob.Request.blank('/v1.0/servers/detail') res = req.get_response(fakes.wsgi_app()) diff --git a/nova/wsgi.py b/nova/wsgi.py index ba0819466..a550a33b0 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -484,6 +484,14 @@ class Serializer(object): """Recursive method to convert data members to XML nodes.""" result = doc.createElement(nodename) if type(data) is list: + collections = metadata.get('list_collections', {}) + if nodename in collections: + metadata = collections[nodename] + for item in data: + node = doc.createElement(metadata['item_name']) + node.setAttribute(metadata['item_key'], str(item)) + result.appendChild(node) + return result singular = metadata.get('plurals', {}).get(nodename, None) if singular is None: if nodename.endswith('s'): @@ -494,6 +502,16 @@ class Serializer(object): node = self._to_xml_node(doc, metadata, singular, item) result.appendChild(node) elif type(data) is dict: + collections = metadata.get('dict_collections', {}) + if nodename in collections: + metadata = collections[nodename] + for k, v in data.items(): + node = doc.createElement(metadata['item_name']) + node.setAttribute(metadata['item_key'], str(k)) + text = doc.createTextNode(str(v)) + node.appendChild(text) + result.appendChild(node) + return result attrs = metadata.get('attributes', {}).get(nodename, {}) for k, v in data.items(): if k in attrs: -- cgit From 3bb6e627fd99a307825f88ff8882e974bcf1b365 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 5 Apr 2011 13:02:42 -0400 Subject: Remove comments. --- .../migrate_repo/versions/014_add_instance_type_id_to_instances.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py index 813e57e10..b12a0a801 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py @@ -63,7 +63,6 @@ def upgrade(migrate_engine): .values(instance_type_id=type_id)) instances.c.instance_type.drop() - #instances.c.instance_type_id.alter(nullable=False) def downgrade(migrate_engine): @@ -83,4 +82,3 @@ def downgrade(migrate_engine): .values(instance_type=type_name)) instances.c.instance_type_id.drop() - #instances.c.instance_type.alter(nullable=False) -- cgit From 3c487493de2a0f827780d54080797bfe70183ba6 Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Tue, 5 Apr 2011 17:49:38 -0400 Subject: Removing naughty semicolon. --- nova/tests/api/openstack/test_servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index ce9821587..a424a8105 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -205,7 +205,7 @@ class ServersTest(test.TestCase): self.assertEquals(server.nodeName, 'server') self.assertEquals(server.getAttribute('id'), '1') self.assertEquals(server.getAttribute('name'), 'server1') - (public,) = server.getElementsByTagName('public'); + (public,) = server.getElementsByTagName('public') (ip,) = public.getElementsByTagName('ip') self.assertEquals(ip.getAttribute('addr'), '1.2.3.4') (private,) = server.getElementsByTagName('private') -- cgit From 56eb682a169c20a9a8275b3b9bfd423f0d562cbc Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:01:33 -0700 Subject: unite the filtering done by glance client and s3 --- nova/api/ec2/cloud.py | 2 +- nova/image/glance.py | 28 ---------------------------- nova/image/local.py | 4 +++- nova/image/s3.py | 45 +++------------------------------------------ nova/image/service.py | 27 +++++++++++++++++++++++++++ 5 files changed, 34 insertions(+), 72 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 02a30220a..b4705820e 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -927,7 +927,7 @@ class CloudController(object): 'ari': 'ramdisk', 'ami': 'machine'} i['imageType'] = display_mapping.get(image_type) - i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' + i['isPublic'] = image.get('is_public') == True i['architecture'] = image['properties'].get('architecture') return i diff --git a/nova/image/glance.py b/nova/image/glance.py index fdf468594..e583c504d 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -186,34 +186,6 @@ class GlanceImageService(service.BaseImageService): image_meta = _convert_timestamps_to_datetimes(image_meta) return image_meta - @staticmethod - def _is_image_available(context, image_meta): - """ - Images are always available if they are public or if the user is an - admin. - - Otherwise, we filter by project_id (if present) and then fall-back to - images owned by user. - """ - # FIXME(sirp): We should be filtering by user_id on the Glance side - # for security; however, we can't do that until we get authn/authz - # sorted out. Until then, filtering in Nova. - if image_meta['is_public'] or context.is_admin: - return True - - properties = image_meta['properties'] - - if context.project_id and ('project_id' in properties): - return str(properties['project_id']) == str(project_id) - - try: - user_id = properties['user_id'] - except KeyError: - return False - - return str(user_id) == str(context.user_id) - - # utility functions def _convert_timestamps_to_datetimes(image_meta): """ diff --git a/nova/image/local.py b/nova/image/local.py index 1fb6e1f13..92d3dabd4 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -84,7 +84,9 @@ class LocalImageService(service.BaseImageService): def show(self, context, image_id): try: with open(self._path_to(image_id)) as metadata_file: - return json.load(metadata_file) + image_meta = json.load(metadata_file) + if not self._is_image_available(context, image_meta): + raise exception.NotFound except (IOError, ValueError): raise exception.NotFound diff --git a/nova/image/s3.py b/nova/image/s3.py index def49f682..93af21022 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -58,54 +58,16 @@ class S3ImageService(service.BaseImageService): return image def delete(self, context, image_id): - # FIXME(vish): call to show is to check filter + # FIXME(vish): call to show is to check visibility self.show(context, image_id) self.service.delete(context, image_id) def update(self, context, image_id, metadata, data=None): - # FIXME(vish): call to show is to check filter + # FIXME(vish): call to show is to check visibility self.show(context, image_id) image = self.service.update(context, image_id, metadata, data) return image - def index(self, context): - images = self.service.index(context) - # FIXME(vish): index doesn't filter so we do it manually - return self._filter(context, images) - - def detail(self, context): - images = self.service.detail(context) - # FIXME(vish): detail doesn't filter so we do it manually - return self._filter(context, images) - - @classmethod - def _is_visible(cls, context, image): - - return (context.is_admin - or context.project_id == image['properties'].get('owner_id') - or str(image['properties'].get('is_public')) == 'True') - - @classmethod - def _filter(cls, context, images): - filtered = [] - for image in images: - if not cls._is_visible(context, image): - continue - filtered.append(image) - return filtered - - def show(self, context, image_id): - image = self.service.show(context, image_id) - if not self._is_visible(context, image): - raise exception.NotFound - return image - - def show_by_name(self, context, name): - image = self.service.show_by_name(context, name) - if not self._is_visible(context, image): - raise exception.NotFound - return image - @staticmethod def _conn(context): # TODO(vish): is there a better way to get creds to sign @@ -168,7 +130,7 @@ class S3ImageService(service.BaseImageService): arch = 'x86_64' properties = metadata['properties'] - properties['owner_id'] = context.project_id + properties['project_id'] = context.project_id properties['architecture'] = arch if kernel_id: @@ -177,7 +139,6 @@ class S3ImageService(service.BaseImageService): if ramdisk_id: properties['ramdisk_id'] = ec2utils.ec2_id_to_id(ramdisk_id) - properties['is_public'] = False metadata.update({'disk_format': image_format, 'container_format': image_format, 'status': 'queued', diff --git a/nova/image/service.py b/nova/image/service.py index b9897ecae..fddc72409 100644 --- a/nova/image/service.py +++ b/nova/image/service.py @@ -136,6 +136,33 @@ class BaseImageService(object): """ raise NotImplementedError + @staticmethod + def _is_image_available(context, image_meta): + """ + Images are always available if they are public or if the user is an + admin. + + Otherwise, we filter by project_id (if present) and then fall-back to + images owned by user. + """ + # FIXME(sirp): We should be filtering by user_id on the Glance side + # for security; however, we can't do that until we get authn/authz + # sorted out. Until then, filtering in Nova. + if image_meta['is_public'] or context.is_admin: + return True + + properties = image_meta['properties'] + + if context.project_id and ('project_id' in properties): + return str(properties['project_id']) == str(context.project_id) + + try: + user_id = properties['user_id'] + except KeyError: + return False + + return str(user_id) == str(context.user_id) + @classmethod def _translate_to_base(cls, metadata): """Return a metadata dictionary that is BaseImageService compliant. -- cgit From 8eff0a91bbc475d8559b39fb81e1a62beda841f3 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:35:17 -0700 Subject: update and fix tests --- nova/image/local.py | 1 + nova/image/s3.py | 13 +++++++++++++ nova/tests/api/openstack/test_servers.py | 16 +++++++--------- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/image/local.py b/nova/image/local.py index 92d3dabd4..bad4c2d85 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -87,6 +87,7 @@ class LocalImageService(service.BaseImageService): image_meta = json.load(metadata_file) if not self._is_image_available(context, image_meta): raise exception.NotFound + return image_meta except (IOError, ValueError): raise exception.NotFound diff --git a/nova/image/s3.py b/nova/image/s3.py index 93af21022..a7301eefd 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -46,6 +46,7 @@ flags.DEFINE_string('image_decryption_dir', '/tmp', class S3ImageService(service.BaseImageService): + """Wraps an existing image service to support s3 based register""" def __init__(self, service=None, *args, **kwargs): if service == None: service = utils.import_object(FLAGS.image_service) @@ -68,6 +69,18 @@ class S3ImageService(service.BaseImageService): image = self.service.update(context, image_id, metadata, data) return image + def index(self, context): + return self.service.index(context) + + def detail(self, context): + return self.service.detail(context) + + def show(self, context, image_id): + return self.service.show(context, image_id) + + def show_by_name(self, context, name): + return self.service.show(context, name) + @staticmethod def _conn(context): # TODO(vish): is there a better way to get creds to sign diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 313676e72..1ec01bb4c 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -1525,29 +1525,27 @@ class TestGetKernelRamdiskFromImage(test.TestCase): def test_not_ami(self): """Anything other than ami should return no kernel and no ramdisk""" - image_meta = {'id': 1, 'status': 'active', - 'properties': {'disk_format': 'vhd'}} + image_meta = {'id': 1, 'status': 'active', 'container_format': 'vhd'} kernel_id, ramdisk_id = self._get_k_r(image_meta) self.assertEqual(kernel_id, None) self.assertEqual(ramdisk_id, None) def test_ami_no_kernel(self): """If an ami is missing a kernel it should raise NotFound""" - image_meta = {'id': 1, 'status': 'active', - 'properties': {'disk_format': 'ami', 'ramdisk_id': 1}} + image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', + 'properties': {'ramdisk_id': 1}} self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_no_ramdisk(self): """If an ami is missing a ramdisk it should raise NotFound""" - image_meta = {'id': 1, 'status': 'active', - 'properties': {'disk_format': 'ami', 'kernel_id': 1}} + image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', + 'properties': {'kernel_id': 1}} self.assertRaises(exception.NotFound, self._get_k_r, image_meta) def test_ami_kernel_ramdisk_present(self): """Return IDs if both kernel and ramdisk are present""" - image_meta = {'id': 1, 'status': 'active', - 'properties': {'disk_format': 'ami', 'kernel_id': 1, - 'ramdisk_id': 2}} + image_meta = {'id': 1, 'status': 'active', 'container_format': 'ami', + 'properties': {'kernel_id': 1, 'ramdisk_id': 2}} kernel_id, ramdisk_id = self._get_k_r(image_meta) self.assertEqual(kernel_id, 1) self.assertEqual(ramdisk_id, 2) -- cgit From 5f414311fb581ba612cf152f3ec9983d5f39c2e4 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:45:01 -0700 Subject: fallback to status if image_state is not set --- nova/api/ec2/cloud.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b4705820e..4e5b8bfc6 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -920,7 +920,11 @@ class CloudController(object): get('image_location'), name) else: i['imageLocation'] = image['properties'].get('image_location') - i['imageState'] = image['properties'].get('image_state') + # NOTE(vish): fallback status if image_state isn't set + state = image.get('status') + if state == 'active': + state = 'available' + i['imageState'] = image['properties'].get('image_state', state) i['displayName'] = name i['description'] = image.get('description') display_mapping = {'aki': 'kernel', -- cgit From 75082c44cdc0319c56805b5558927760636eaf4b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 5 Apr 2011 15:49:43 -0700 Subject: pep8 --- nova/image/glance.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index e583c504d..f848f40d8 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -186,6 +186,7 @@ class GlanceImageService(service.BaseImageService): image_meta = _convert_timestamps_to_datetimes(image_meta) return image_meta + # utility functions def _convert_timestamps_to_datetimes(image_meta): """ -- cgit From a254fd9b63c48f64a62fd38df3a2caae81ce63c7 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Tue, 5 Apr 2011 18:29:53 -0500 Subject: typo --- nova/virt/xenapi/vmops.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c96c35a6e..ef2ab09b9 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -814,12 +814,11 @@ class VMOps(object): "netmask": network["netmask"], "enabled": "1"} - def ip6_dict(ip6): + def ip6_dict(): return { "ip": utils.to_global_ipv6(network['cidr_v6'], instance['mac_address']), "netmask": network['netmask_v6'], - "gateway": network['gateway_v6'], "enabled": "1"} info = { @@ -831,7 +830,9 @@ class VMOps(object): 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_IPs]} if network['cidr_v6']: - info['ip6s'] = [ip6_dict(ip) for ip in network_IPs] + info['ip6s'] = [ip6_dict()] + if network['gateway_v6']: + info['gateway6'] = network['gateway_v6'], network_info.append((network, info)) return network_info -- cgit From 44eefb1eefda5e42a286ee1aa689c1c93e72aae4 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Wed, 6 Apr 2011 14:44:19 +0900 Subject: fixed based on reviwer's comment --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index f67aa47e8..db04143c1 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -367,12 +367,12 @@ class API(base.Base): instance_id) raise - if (instance['state_description'] == 'terminating'): + if instance['state_description'] == 'terminating': LOG.warning(_("Instance %s is already being terminated"), instance_id) return - if (instance['state_description'] == 'migrating'): + if instance['state_description'] == 'migrating': LOG.warning(_("Instance %s is being migrated"), instance_id) return -- cgit From dbeab47bbdeceab0bef896c9d7646ae346c9dd3a Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 6 Apr 2011 09:05:12 -0700 Subject: Wait for device node to be created after mounting image VDI --- nova/virt/xenapi/vm_utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..d29456ec6 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -49,6 +49,8 @@ LOG = logging.getLogger("nova.virt.xenapi.vm_utils") FLAGS = flags.FLAGS flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') +flags.DEFINE_integer('timeout_block', 10, + 'time to wait for a block device to be created') XENAPI_POWER_STATE = { 'Halted': power_state.SHUTDOWN, @@ -896,6 +898,16 @@ def remap_vbd_dev(dev): return remapped_dev +def _wait_for_device(dev): + """Wait for device node to appear""" + for i in xrange(0, FLAGS.timeout_block): + if os.path.exists('/dev/%s' % dev): + return + time.sleep(1) + + raise StorageError(_('Timeout waiting for device %s to be created') % dev) + + def with_vdi_attached_here(session, vdi_ref, read_only, f): this_vm_ref = get_this_vm_ref(session) vbd_rec = {} @@ -924,6 +936,7 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f): if dev != orig_dev: LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, ' 'remapping to %(dev)s') % locals()) + _wait_for_device(dev) return f(dev) finally: LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref) -- cgit From 0a8ca1bb7f123718ae48bb842b1c532b07f03890 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 6 Apr 2011 18:10:42 +0200 Subject: Create ca_folder if it does not already exist. --- nova/crypto.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/crypto.py b/nova/crypto.py index 2b122e560..9b1897926 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -269,6 +269,8 @@ def _sign_csr(csr_text, ca_folder): LOG.debug(_("Flags path: %s"), ca_folder) start = os.getcwd() # Change working dir to CA + if not os.path.exists(ca_folder): + os.makedirs(ca_folder) os.chdir(ca_folder) utils.execute('openssl', 'ca', '-batch', '-out', outbound, '-config', './openssl.cnf', '-infiles', inbound) -- cgit From 481a77134a4e0e1d668fa488d7c5b1d7e1bc5429 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 11:15:35 -0500 Subject: modified behavior of inject_network_info and reset_network related to a vm_ref not being passed in --- nova/virt/xenapi/vmops.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ef2ab09b9..f02beda24 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -176,7 +176,7 @@ class VMOps(object): vdi_ref, network_info) self.create_vifs(vm_ref, network_info) - self.inject_network_info(instance, vm_ref, network_info) + self.inject_network_info(instance, network_info, vm_ref) return vm_ref def _spawn(self, instance, vm_ref): @@ -836,15 +836,31 @@ class VMOps(object): network_info.append((network, info)) return network_info - def inject_network_info(self, instance, vm_ref, network_info): + #TODO{tr3buchet) remove this shim with nova-multi-nic + def inject_network_info(self, instance, network_info=None, vm_ref=None): + """ + shim in place which makes inject_network_info work without being + passed network_info. + shim goes away after nova-multi-nic + """ + if not network_info: + network_info = self._get_network_info(instance) + self._inject_network_info(instance, network_info, vm_ref) + + def _inject_network_info(self, instance, network_info, vm_ref=None): """ Generate the network info and make calls to place it into the xenstore and the xenstore param list. + vm_ref can be passed in because it will sometimes be different than + what VMHelper.lookup(session, instance.name) will find (ex: rescue) """ logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref) - # this function raises if vm_ref is not a vm_opaque_ref - self._session.get_xenapi().VM.get_record(vm_ref) + if vm_ref: + # this function raises if vm_ref is not a vm_opaque_ref + self._session.get_xenapi().VM.get_record(vm_ref) + else: + vm_ref = VMHelper.lookup(self._session, instance.name) for (network, info) in network_info: location = 'vm-data/networking/%s' % info['mac'].replace(':', '') @@ -876,8 +892,10 @@ class VMOps(object): VMHelper.create_vif(self._session, vm_ref, network_ref, mac_address, device, rxtx_cap) - def reset_network(self, instance, vm_ref): + def reset_network(self, instance, vm_ref=None): """Creates uuid arg to pass to make_agent_call and calls it.""" + if not vm_ref: + vm_ref = VMHelper.lookup(self._session, instance.name) args = {'id': str(uuid.uuid4())} # TODO(tr3buchet): fix function call after refactor #resp = self._make_agent_call('resetnetwork', instance, '', args) -- cgit From e46d78218eec77f8502579496ee0922ce401e84a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 12:33:07 -0500 Subject: updated _prepare_injectables() to use info[gateway6] instead of looking inside the ip6 address dict for the gateway6 information --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d07d60800..886f1ec88 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -1130,7 +1130,7 @@ def _prepare_injectables(inst, networks_info): 'dns': dns, 'address_v6': ip_v6 and ip_v6['ip'] or '', 'netmask_v6': ip_v6 and ip_v6['netmask'] or '', - 'gateway_v6': ip_v6 and ip_v6['gateway'] or '', + 'gateway_v6': ip_v6 and info['gateway6'] or '', 'use_ipv6': FLAGS.use_ipv6} interfaces_info.append(interface_info) -- cgit From c18bf716f08e6b9fbdc259755cf172b5a6cf096a Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 12:52:25 -0500 Subject: updated get_network_info in libvirt_conn to correctly insert ip6s and gateway6 into the network info, also small style fixes --- nova/virt/libvirt_conn.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2be190256..f1fa859ed 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -169,34 +169,34 @@ def _get_network_info(instance): instance['id']) network_info = [] - def ip_dict(ip): - return { - "ip": ip.address, - "netmask": network["netmask"], - "enabled": "1"} - - def ip6_dict(ip6): - prefix = ip6.network.cidr_v6 - mac = instance.mac_address - return { - "ip": utils.to_global_ipv6(prefix, mac), - "netmask": ip6.network.netmask_v6, - "gateway": ip6.network.gateway_v6, - "enabled": "1"} - for network in networks: network_ips = [ip for ip in ip_addresses - if ip.network_id == network.id] + if ip['network_id'] == network['id']] + + def ip_dict(ip): + return { + 'ip': ip['address'], + 'netmask': network['netmask'], + 'enabled': '1'} + + def ip6_dict(): + prefix = network['cidr_v6'] + mac = instance['mac_address'] + return { + 'ip': utils.to_global_ipv6(prefix, mac), + 'netmask': network['netmask_v6'], + 'enabled': '1'} mapping = { 'label': network['label'], 'gateway': network['gateway'], - 'mac': instance.mac_address, + 'mac': instance['mac_address'], 'dns': [network['dns']], 'ips': [ip_dict(ip) for ip in network_ips]} if FLAGS.use_ipv6: - mapping['ip6s'] = [ip6_dict(ip) for ip in network_ips] + mapping['ip6s'] = [ip6_dict()] + mapping['gateway6'] = network['gateway_v6'], network_info.append((network, mapping)) return network_info -- cgit From d5c077131e00f41a38fa03fdbea46aa4351f95b5 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 13:05:39 -0500 Subject: updated check_vm_record in test_xenapi to check the gateway6 correctly --- nova/tests/test_xenapi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 17e3f55e9..42fe8f3fa 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -289,11 +289,11 @@ class XenAPIVMTestCase(test.TestCase): 'enabled':'1'}], 'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff', 'netmask': '120', - 'enabled': '1', - 'gateway': 'fe80::a00:1'}], + 'enabled': '1'}], 'mac': 'aa:bb:cc:dd:ee:ff', 'dns': ['10.0.0.2'], - 'gateway': '10.0.0.1'}) + 'gateway': '10.0.0.1', + 'gateway6': 'fe80::a00:1'}) def check_vm_params_for_windows(self): self.assertEquals(self.vm['platform']['nx'], 'true') -- cgit From d3fec5c2c3de2d3a1ef0fd1fd809ff248b6df5a8 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 13:31:51 -0500 Subject: syntax error --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f1fa859ed..50b09d19b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -196,7 +196,7 @@ def _get_network_info(instance): if FLAGS.use_ipv6: mapping['ip6s'] = [ip6_dict()] - mapping['gateway6'] = network['gateway_v6'], + mapping['gateway6'] = network['gateway_v6'] network_info.append((network, mapping)) return network_info -- cgit From c649c8d5febab7d0dfa329bc5d78f0147383c5ee Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Wed, 6 Apr 2011 11:36:08 -0700 Subject: Use a more descriptive name for the flag to make it easier to understand the purpose --- nova/virt/xenapi/vm_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d29456ec6..cf2adb44c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -49,7 +49,7 @@ LOG = logging.getLogger("nova.virt.xenapi.vm_utils") FLAGS = flags.FLAGS flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') -flags.DEFINE_integer('timeout_block', 10, +flags.DEFINE_integer('block_device_creation_timeout', 10, 'time to wait for a block device to be created') XENAPI_POWER_STATE = { @@ -900,7 +900,7 @@ def remap_vbd_dev(dev): def _wait_for_device(dev): """Wait for device node to appear""" - for i in xrange(0, FLAGS.timeout_block): + for i in xrange(0, FLAGS.block_device_creation_timeout): if os.path.exists('/dev/%s' % dev): return time.sleep(1) -- cgit From 560d36e7ad87ca7e8f8619e146ed4965f33dd391 Mon Sep 17 00:00:00 2001 From: Trey Morris Date: Wed, 6 Apr 2011 13:43:02 -0500 Subject: another syntax error --- nova/virt/xenapi/vmops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f02beda24..30754b7b6 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -832,7 +832,7 @@ class VMOps(object): if network['cidr_v6']: info['ip6s'] = [ip6_dict()] if network['gateway_v6']: - info['gateway6'] = network['gateway_v6'], + info['gateway6'] = network['gateway_v6'] network_info.append((network, info)) return network_info -- cgit From cd279bec6391ef40d278f377f0039b507b14904b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 6 Apr 2011 12:58:57 -0700 Subject: check visibility on delete and update --- nova/image/glance.py | 4 ++++ nova/image/local.py | 6 +++++- nova/image/s3.py | 4 ---- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index f848f40d8..bf49ca96c 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -151,6 +151,8 @@ class GlanceImageService(service.BaseImageService): :raises NotFound if the image does not exist. """ + # NOTE(vish): show is to check if image is available + self.show(context, image_id) try: image_meta = self.client.update_image(image_id, image_meta, data) except glance_exception.NotFound: @@ -165,6 +167,8 @@ class GlanceImageService(service.BaseImageService): :raises NotFound if the image does not exist. """ + # NOTE(vish): show is to check if image is available + self.show(context, image_id) try: result = self.client.delete_image(image_id) except glance_exception.NotFound: diff --git a/nova/image/local.py b/nova/image/local.py index bad4c2d85..8bf78b4c9 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -126,6 +126,8 @@ class LocalImageService(service.BaseImageService): def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data.""" + # NOTE(vish): show is to check if image is available + self.show(context, image_id) metadata['id'] = image_id try: if data: @@ -143,9 +145,11 @@ class LocalImageService(service.BaseImageService): def delete(self, context, image_id): """Delete the given image. - Raises OSError if the image does not exist. + Raises NotFound if the image does not exist. """ + # NOTE(vish): show is to check if image is available + self.show(context, image_id) try: shutil.rmtree(self._path_to(image_id, None)) except (IOError, ValueError): diff --git a/nova/image/s3.py b/nova/image/s3.py index a7301eefd..554760d53 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -59,13 +59,9 @@ class S3ImageService(service.BaseImageService): return image def delete(self, context, image_id): - # FIXME(vish): call to show is to check visibility - self.show(context, image_id) self.service.delete(context, image_id) def update(self, context, image_id, metadata, data=None): - # FIXME(vish): call to show is to check visibility - self.show(context, image_id) image = self.service.update(context, image_id, metadata, data) return image -- cgit From 5534113f65cc578dcdf93432981b836bf4e3dfaf Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 6 Apr 2011 16:12:32 -0400 Subject: Added support for listing addresses of a server in the openstack api. Now you can GET * /servers/1/ips * /servers/1/ips/public * /servers/1/ips/private Supports v1.0 json and xml. Added corresponding tests. --- nova/api/openstack/__init__.py | 6 +++ nova/api/openstack/ips.py | 71 +++++++++++++++++++++++++++++ nova/api/openstack/servers.py | 9 ---- nova/api/openstack/views/addresses.py | 10 +++- nova/tests/api/openstack/test_servers.py | 78 ++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 nova/api/openstack/ips.py (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7545eb0c9..5e76a06f7 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -34,6 +34,7 @@ from nova.api.openstack import consoles from nova.api.openstack import flavors from nova.api.openstack import images from nova.api.openstack import image_metadata +from nova.api.openstack import ips from nova.api.openstack import limits from nova.api.openstack import servers from nova.api.openstack import server_metadata @@ -144,6 +145,11 @@ class APIRouterV10(APIRouter): parent_resource=dict(member_name='server', collection_name='servers')) + mapper.resource("ip", "ips", controller=ips.Controller(), + collection=dict(public='GET', private='GET'), + parent_resource=dict(member_name='server', + collection_name='servers')) + class APIRouterV11(APIRouter): """Define routes specific to OpenStack API V1.1.""" diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py new file mode 100644 index 000000000..89b0936d5 --- /dev/null +++ b/nova/api/openstack/ips.py @@ -0,0 +1,71 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 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. + +import time + +from webob import exc + +import nova +import nova.api.openstack.views.addresses +from nova.api.openstack import faults + + +class Controller(nova.wsgi.Controller): + """The servers addresses API controller for the Openstack API.""" + + _serialization_metadata = { + 'application/xml': { + 'list_collections': { + 'public': {'item_name': 'ip', 'item_key': 'addr'}, + 'private': {'item_name': 'ip', 'item_key': 'addr'}, + }, + }, + } + + def __init__(self): + self.compute_api = nova.compute.API() + self.builder = nova.api.openstack.views.addresses.ViewBuilderV10() + + def index(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'addresses': self.builder.build(instance)} + + def public(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'public': self.builder.build_public_parts(instance)} + + def private(self, req, server_id): + try: + instance = self.compute_api.get(req.environ['nova.context'], id) + except nova.exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + return {'private': self.builder.build_private_parts(instance)} + + def show(self, req, server_id, id): + return faults.Fault(exc.HTTPNotImplemented()) + + def create(self, req, server_id): + return faults.Fault(exc.HTTPNotImplemented()) + + def delete(self, req, server_id, id): + return faults.Fault(exc.HTTPNotImplemented()) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index cada92813..67e6eefd8 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -70,15 +70,6 @@ class Controller(wsgi.Controller): self._image_service = utils.import_object(FLAGS.image_service) super(Controller, self).__init__() - def ips(self, req, id): - try: - instance = self.compute_api.get(req.environ['nova.context'], id) - except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) - - builder = self._get_addresses_view_builder(req) - return builder.build(instance) - def index(self, req): """ Returns a list of server names and ids for a given user """ return self._items(req, is_detail=False) diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py index 90c77855b..2810cce39 100644 --- a/nova/api/openstack/views/addresses.py +++ b/nova/api/openstack/views/addresses.py @@ -28,10 +28,16 @@ class ViewBuilder(object): class ViewBuilderV10(ViewBuilder): def build(self, inst): - private_ips = utils.get_from_path(inst, 'fixed_ip/address') - public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address') + private_ips = self.build_private_parts(inst) + public_ips = self.build_public_parts(inst) return dict(public=public_ips, private=private_ips) + def build_public_parts(self, inst): + return utils.get_from_path(inst, 'fixed_ip/floating_ips/address') + + def build_private_parts(self, inst): + return utils.get_from_path(inst, 'fixed_ip/address') + class ViewBuilderV11(ViewBuilder): def build(self, inst): diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index a424a8105..640418cfa 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -228,6 +228,84 @@ class ServersTest(test.TestCase): self.assertEqual(len(addresses["private"]), 1) self.assertEqual(addresses["private"][0], private) + def test_get_server_addresses_V10(self): + private = '192.168.0.3' + public = ['1.2.3.4'] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict, { + 'addresses': {'public': public, 'private': [private]}}) + + def test_get_server_addresses_xml_V10(self): + private_expected = "192.168.0.3" + public_expected = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private_expected, + public_expected) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + dom = minidom.parseString(res.body) + (addresses,) = dom.childNodes + self.assertEquals(addresses.nodeName, 'addresses') + (public,) = addresses.getElementsByTagName('public') + (ip,) = public.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), public_expected[0]) + (private,) = addresses.getElementsByTagName('private') + (ip,) = private.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), private_expected) + + def test_get_server_addresses_public_V10(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips/public') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict, {'public': public}) + + def test_get_server_addresses_private_V10(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips/private') + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict, {'private': [private]}) + + def test_get_server_addresses_public_xml_V10(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips/public') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + dom = minidom.parseString(res.body) + (public_node,) = dom.childNodes + self.assertEquals(public_node.nodeName, 'public') + (ip,) = public_node.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), public[0]) + + def test_get_server_addresses_private_xml_V10(self): + private = "192.168.0.3" + public = ["1.2.3.4"] + new_return_server = return_server_with_addresses(private, public) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + req = webob.Request.blank('/v1.0/servers/1/ips/private') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + dom = minidom.parseString(res.body) + (private_node,) = dom.childNodes + self.assertEquals(private_node.nodeName, 'private') + (ip,) = private_node.getElementsByTagName('ip') + self.assertEquals(ip.getAttribute('addr'), private) + def test_get_server_by_id_with_addresses_v11(self): private = "192.168.0.3" public = ["1.2.3.4"] -- cgit From e16571671e4baab50521870ec64e4ab954c7d165 Mon Sep 17 00:00:00 2001 From: Naveed Massjouni Date: Wed, 6 Apr 2011 17:50:11 -0400 Subject: Controllers now inherit from nova.api.openstack.common.OpenstackController. --- nova/api/openstack/ips.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py index 89b0936d5..778e9ba1a 100644 --- a/nova/api/openstack/ips.py +++ b/nova/api/openstack/ips.py @@ -21,10 +21,11 @@ from webob import exc import nova import nova.api.openstack.views.addresses +from nova.api.openstack import common from nova.api.openstack import faults -class Controller(nova.wsgi.Controller): +class Controller(common.OpenstackController): """The servers addresses API controller for the Openstack API.""" _serialization_metadata = { -- cgit From 040428cc35aa046de4bcf744c95b7c507df94550 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 6 Apr 2011 14:53:35 -0700 Subject: Add automatic metadata ip to network host on start. Also fix race where gw is readded twice --- nova/network/linux_net.py | 13 ++++++++++++- nova/network/manager.py | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index d11d21dad..6a7051d10 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -391,6 +391,13 @@ def unbind_floating_ip(floating_ip): 'dev', FLAGS.public_interface) +def ensure_metadata_ip(interface): + """Sets up local metadata ip""" + _execute('sudo', 'ip', 'addr', 'add', '169.254.169.254/32', + 'scope', 'link', 'dev', interface, + check_exit_code=False) + + def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" iptables_manager.ipv4['filter'].add_rule("FORWARD", @@ -504,7 +511,11 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute(*_ip_bridge_cmd('del', params, fields[-1])) _execute(*_ip_bridge_cmd('add', params, bridge)) if gateway: - _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', gateway) + # NOTE(vish): If the gateway already exists we are fine + out, err = _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', + gateway, check_exit_code=False) + if err and err != "SIOCADDRT: File exists\n": + raise exception.Error("Failed to reset gateway: %s" % err) out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, check_exit_code=False) diff --git a/nova/network/manager.py b/nova/network/manager.py index 86ee4fc00..a3c345b69 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -100,6 +100,8 @@ flags.DEFINE_string('network_host', socket.gethostname(), 'Network host to use for ip allocation in flat modes') flags.DEFINE_bool('fake_call', False, 'If True, skip using the queue and make local calls') +flags.DEFINE_string('metadata_interface', 'eth0', + 'interface to add the metadata ip to') class AddressAlreadyAllocated(exception.Error): @@ -128,6 +130,7 @@ class NetworkManager(manager.SchedulerDependentManager): self.driver.init_host() # Set up networking for the projects for which we're already # the designated network host. + self.driver.ensure_metadata_ip(FLAGS.metadata_interface) ctxt = context.get_admin_context() for network in self.db.host_get_networks(ctxt, self.host): self._on_set_network_host(ctxt, network['id']) -- cgit From dc8b3cb3fa124755ed3e5282b2d11a811f1db2d5 Mon Sep 17 00:00:00 2001 From: Ken Pepple Date: Wed, 6 Apr 2011 15:39:44 -0700 Subject: removed comment on API compliance --- nova/api/openstack/views/servers.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index baa911590..096bf70c6 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -82,7 +82,6 @@ class ViewBuilder(object): # Return the metadata as a dictionary metadata = {} for item in inst.get('metadata', []): - # metadata values must be strings per API metadata[item['key']] = str(item['value']) inst_dict['metadata'] = metadata -- cgit From 3831008b4e8aeec08b58afb49e40428ad5ece5b1 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 6 Apr 2011 16:10:08 -0700 Subject: if we delete the old route when we move it we don't need to check for exists --- nova/network/linux_net.py | 9 ++++----- nova/network/manager.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6a7051d10..a4d312f02 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -449,6 +449,7 @@ def ensure_vlan(vlan_num): return interface +@utils.synchronized('ensure_bridge', external=True) def ensure_bridge(bridge, interface, net_attrs=None): """Create a bridge unless it already exists. @@ -502,6 +503,8 @@ def ensure_bridge(bridge, interface, net_attrs=None): fields = line.split() if fields and fields[0] == "0.0.0.0" and fields[-1] == interface: gateway = fields[1] + _execute('sudo', 'route', 'del', 'default', 'gw', gateway, + 'dev', interface) out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, 'scope', 'global') for line in out.split("\n"): @@ -511,11 +514,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute(*_ip_bridge_cmd('del', params, fields[-1])) _execute(*_ip_bridge_cmd('add', params, bridge)) if gateway: - # NOTE(vish): If the gateway already exists we are fine - out, err = _execute('sudo', 'route', 'add', '0.0.0.0', 'gw', - gateway, check_exit_code=False) - if err and err != "SIOCADDRT: File exists\n": - raise exception.Error("Failed to reset gateway: %s" % err) + _execute('sudo', 'route', 'add', 'default', 'gw', gateway) out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, check_exit_code=False) diff --git a/nova/network/manager.py b/nova/network/manager.py index a3c345b69..b80560bdf 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -128,9 +128,9 @@ class NetworkManager(manager.SchedulerDependentManager): standalone service. """ self.driver.init_host() + self.driver.ensure_metadata_ip(FLAGS.metadata_interface) # Set up networking for the projects for which we're already # the designated network host. - self.driver.ensure_metadata_ip(FLAGS.metadata_interface) ctxt = context.get_admin_context() for network in self.db.host_get_networks(ctxt, self.host): self._on_set_network_host(ctxt, network['id']) -- cgit From d7e8d91d6bc4954f2d256f57e34444b5bd170ab0 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Thu, 7 Apr 2011 13:08:14 +0900 Subject: fixed based on reviewer's comment - 'locals() should be off from _() --- nova/compute/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 37a904b1e..847c3655a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1096,7 +1096,7 @@ class ComputeManager(manager.SchedulerDependentManager): # this case should be ignored. LOG.info(_("the instance '%(name)s' is not found in hypervisor" ", while db record is found. But not synchronize " - "since it is migrating." % locals())) + "since it is migrating.") % locals()) continue if vm_state != db_state: -- cgit From 2bc0e744162276048ddd9c1a1eeacbd647cda6f4 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Thu, 7 Apr 2011 13:32:19 +0900 Subject: fixed based on reviewer's comment - 1. erase unnecessary blank line, 2. adding LOG.debug --- nova/virt/libvirt_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bdf577825..eab54c53e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1849,7 +1849,6 @@ class NWFilterFirewall(FirewallDriver): def instance_filter_exists(self, instance): """Check nova-instance-instance-xxx exists""" - network_info = _get_network_info(instance) for (network, mapping) in network_info: nic_id = mapping['mac'].replace(':', '') @@ -1857,6 +1856,9 @@ class NWFilterFirewall(FirewallDriver): try: self._conn.nwfilterLookupByName(instance_filter_name) except libvirt.libvirtError: + name = instance.name + LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' + '%(name)s is not found.') % locals()) return False return True -- cgit From 7cf0deda8f7ab410005c556779353d599c8e8a63 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 10:34:14 -0300 Subject: adds timeout to login_with_password --- nova/virt/xenapi_conn.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 99fd35c61..6dfe0b9a9 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -63,6 +63,7 @@ import xmlrpclib from eventlet import event from eventlet import tpool +from eventlet import timeout from nova import context from nova import db @@ -140,7 +141,9 @@ flags.DEFINE_bool('xenapi_remap_vbd_dev', False, flags.DEFINE_string('xenapi_remap_vbd_dev_prefix', 'sd', 'Specify prefix to remap VBD dev to ' '(ex. /dev/xvdb -> /dev/sdb)') - +flags.DEFINE_integer('xenapi_login_timeout', + 10, + 'Timeout in seconds for XenAPI login.') def get_connection(_): """Note that XenAPI doesn't have a read-only connection mode, so @@ -318,7 +321,9 @@ class XenAPISession(object): def __init__(self, url, user, pw): self.XenAPI = self.get_imported_xenapi() self._session = self._create_session(url) - self._session.login_with_password(user, pw) + exception = self.XenAPI.Failure(_("Unable to log in to XenAPI.")) + with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): + self._session.login_with_password(user, pw) self.loop = None def get_imported_xenapi(self): -- cgit From 9b24c399c5689a1492b96dcd6725590c2a97c6e3 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 10:42:29 -0300 Subject: pep8 --- nova/virt/xenapi_conn.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 6dfe0b9a9..f10aa6eb5 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -145,6 +145,7 @@ flags.DEFINE_integer('xenapi_login_timeout', 10, 'Timeout in seconds for XenAPI login.') + def get_connection(_): """Note that XenAPI doesn't have a read-only connection mode, so the read_only parameter is ignored.""" -- cgit From 9f57f78efab4a31bfe29e2edab1e86eedf4352fd Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 7 Apr 2011 11:59:40 -0300 Subject: better error message --- nova/virt/xenapi_conn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f10aa6eb5..0cabccf08 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -322,7 +322,8 @@ class XenAPISession(object): def __init__(self, url, user, pw): self.XenAPI = self.get_imported_xenapi() self._session = self._create_session(url) - exception = self.XenAPI.Failure(_("Unable to log in to XenAPI.")) + exception = self.XenAPI.Failure(_("Unable to log in to XenAPI " + "(is the Dom0 disk full?)")) with timeout.Timeout(FLAGS.xenapi_login_timeout, exception): self._session.login_with_password(user, pw) self.loop = None -- cgit From 8da3e6c19b97ab7cd08e69fb0df114653c0b90db Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 7 Apr 2011 10:37:40 -0700 Subject: Simplify by always adding to loopback --- nova/network/linux_net.py | 5 ++--- nova/network/manager.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index a4d312f02..ed6c943c7 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -391,11 +391,10 @@ def unbind_floating_ip(floating_ip): 'dev', FLAGS.public_interface) -def ensure_metadata_ip(interface): +def ensure_metadata_ip(): """Sets up local metadata ip""" _execute('sudo', 'ip', 'addr', 'add', '169.254.169.254/32', - 'scope', 'link', 'dev', interface, - check_exit_code=False) + 'scope', 'link', 'dev', 'lo', check_exit_code=False) def ensure_vlan_forward(public_ip, port, private_ip): diff --git a/nova/network/manager.py b/nova/network/manager.py index b80560bdf..0dd7f2360 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -100,8 +100,6 @@ flags.DEFINE_string('network_host', socket.gethostname(), 'Network host to use for ip allocation in flat modes') flags.DEFINE_bool('fake_call', False, 'If True, skip using the queue and make local calls') -flags.DEFINE_string('metadata_interface', 'eth0', - 'interface to add the metadata ip to') class AddressAlreadyAllocated(exception.Error): @@ -128,7 +126,7 @@ class NetworkManager(manager.SchedulerDependentManager): standalone service. """ self.driver.init_host() - self.driver.ensure_metadata_ip(FLAGS.metadata_interface) + self.driver.ensure_metadata_ip() # Set up networking for the projects for which we're already # the designated network host. ctxt = context.get_admin_context() -- cgit From 404feb59a829e24e026f793a362e54aad1aaa03f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 7 Apr 2011 13:55:42 -0500 Subject: Renamed computeFault to cloudServersFault --- nova/api/openstack/faults.py | 2 +- nova/tests/api/openstack/test_api.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index bc97639a0..87118ce19 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -47,7 +47,7 @@ class Fault(webob.exc.HTTPException): """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. code = self.wrapped_exc.status_int - fault_name = self._fault_names.get(code, "computeFault") + fault_name = self._fault_names.get(code, "cloudServersFault") fault_data = { fault_name: { 'code': code, diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index 5112c486f..c63431a45 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -53,13 +53,13 @@ class APITest(test.TestCase): #api.application = succeed api = self._wsgi_app(succeed) resp = Request.blank('/').get_response(api) - self.assertFalse('computeFault' in resp.body, resp.body) + self.assertFalse('cloudServersFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 200, resp.body) #api.application = raise_webob_exc api = self._wsgi_app(raise_webob_exc) resp = Request.blank('/').get_response(api) - self.assertFalse('computeFault' in resp.body, resp.body) + self.assertFalse('cloudServersFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 404, resp.body) #api.application = raise_api_fault @@ -71,11 +71,11 @@ class APITest(test.TestCase): #api.application = fail api = self._wsgi_app(fail) resp = Request.blank('/').get_response(api) - self.assertTrue('{"computeFault' in resp.body, resp.body) + self.assertTrue('{"cloudServersFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 500, resp.body) #api.application = fail api = self._wsgi_app(fail) resp = Request.blank('/.xml').get_response(api) - self.assertTrue(' Date: Thu, 7 Apr 2011 15:09:10 -0400 Subject: Drop unneeded Fkey on InstanceTypes.id. --- nova/db/sqlalchemy/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 9d4c6cdef..f79d0f16c 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -258,8 +258,7 @@ class InstanceActions(BASE, NovaBase): class InstanceTypes(BASE, NovaBase): """Represent possible instance_types or flavor of VM offered""" __tablename__ = "instance_types" - id = Column(Integer, ForeignKey('instances.instance_type_id'), - primary_key=True) + id = Column(Integer, primary_key=True) name = Column(String(255), unique=True) memory_mb = Column(Integer) vcpus = Column(Integer) -- cgit From 99e8335e9b07b1cbf9c28cda2dfb2496d955c72c Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 15:43:51 -0400 Subject: Some i18n fixes to instance_types. --- nova/compute/instance_types.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index 5b1d92e29..b3a5ab0ac 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -59,8 +59,8 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0, rxtx_quota=rxtx_quota, rxtx_cap=rxtx_cap)) except exception.DBError, e: - LOG.exception(_('DB error: %s' % e)) - raise exception.ApiError(_("Cannot create instance type: %s" % name)) + LOG.exception(_('DB error: %s') % e) + raise exception.ApiError(_("Cannot create instance type: %s") % name) def destroy(name): @@ -72,8 +72,8 @@ def destroy(name): try: db.instance_type_destroy(context.get_admin_context(), name) except exception.NotFound: - LOG.exception(_('Instance type %s not found for deletion' % name)) - raise exception.ApiError(_("Unknown instance type: %s" % name)) + LOG.exception(_('Instance type %s not found for deletion') % name) + raise exception.ApiError(_("Unknown instance type: %s") % name) def purge(name): @@ -85,8 +85,8 @@ def purge(name): try: db.instance_type_purge(context.get_admin_context(), name) except exception.NotFound: - LOG.exception(_('Instance type %s not found for purge' % name)) - raise exception.ApiError(_("Unknown instance type: %s" % name)) + LOG.exception(_('Instance type %s not found for purge') % name) + raise exception.ApiError(_("Unknown instance type: %s") % name) def get_all_types(inactive=0): @@ -106,7 +106,7 @@ def get_default_instance_type(): try: return get_instance_type_by_name(name) except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s" % name)) + raise exception.ApiError(_("Unknown instance type: %s") % name) def get_instance_type(id): @@ -117,7 +117,7 @@ def get_instance_type(id): ctxt = context.get_admin_context() return db.instance_type_get_by_id(ctxt, id) except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s" % name)) + raise exception.ApiError(_("Unknown instance type: %s") % name) def get_instance_type_by_name(name): @@ -128,7 +128,7 @@ def get_instance_type_by_name(name): ctxt = context.get_admin_context() return db.instance_type_get_by_name(ctxt, name) except exception.DBError: - raise exception.ApiError(_("Unknown instance type: %s" % name)) + raise exception.ApiError(_("Unknown instance type: %s") % name) def get_instance_type_by_flavor_id(flavor_id): @@ -139,5 +139,5 @@ def get_instance_type_by_flavor_id(flavor_id): ctxt = context.get_admin_context() return db.instance_type_get_by_flavor_id(ctxt, flavor_id) except exception.DBError, e: - LOG.exception(_('DB error: %s' % e)) - raise exception.ApiError(_("Unknown flavor: %s" % flavor_id)) + LOG.exception(_('DB error: %s') % e) + raise exception.ApiError(_("Unknown flavor: %s") % flavor_id) -- cgit From c7fd470d7ff0df4b23664b6599e5ae5acdb21511 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 15:52:14 -0400 Subject: Drop extra 'None' arg from dict.get call. --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e4bcccb2b..c3124b89d 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -731,7 +731,7 @@ class CloudController(object): instance['host']) i['productCodesSet'] = self._convert_to_set([], 'product_codes') if instance['instance_type']: - i['instanceType'] = instance['instance_type'].get('name', None) + i['instanceType'] = instance['instance_type'].get('name') else: i['instanceType'] = None i['launchTime'] = instance['created_at'] -- cgit From b5310d58d418f123b2d5d2953d6b4082a70120cd Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Fri, 8 Apr 2011 06:32:53 +0900 Subject: fix pep8 violation --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index eab54c53e..256e6e635 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1857,7 +1857,7 @@ class NWFilterFirewall(FirewallDriver): self._conn.nwfilterLookupByName(instance_filter_name) except libvirt.libvirtError: name = instance.name - LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' + LOG.debug(_('The nwfilter(%(instance_filter_name)s) for' '%(name)s is not found.') % locals()) return False return True -- cgit From b54be0e29cdcd91e3d106fb587b89c39ca3a0bff Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 15:52:27 -0700 Subject: Removed commented-out old 'delete instance on SHUTOFF' code --- nova/compute/manager.py | 6 ------ nova/virt/libvirt_conn.py | 8 +------- 2 files changed, 1 insertion(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 72f04ecb1..d3cd2e51a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1090,12 +1090,6 @@ class ComputeManager(manager.SchedulerDependentManager): # NOTE(justinsb): We no longer auto-remove SHUTOFF instances # It's quite hard to get them back when we do. - #if vm_state == power_state.SHUTOFF: - # # TODO(soren): This is what the compute manager does when you - # # terminate an instance. At some point I figure we'll have a - # # "terminated" state and some sort of cleanup job that runs - # # occasionally, cleaning them out. - # self.db.instance_destroy(context, db_instance['id']) # Are there VMs not in the DB? for vm_not_found_in_db in vms_not_found_in_db: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 533ff9394..4523cdd2f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -232,14 +232,8 @@ class LibvirtConnection(driver.ComputeDriver): {'name': instance['name'], 'state': state}) db.instance_set_state(ctxt, instance['id'], state) - # NOTE(justinsb): We no longer delete these instances, + # NOTE(justinsb): We no longer delete SHUTOFF instances, # the user may want to power them back on - #if state == power_state.SHUTOFF: - # # TODO(soren): This is what the compute manager does when you - # # terminate # an instance. At some point I figure we'll have a - # # "terminated" state and some sort of cleanup job that runs - # # occasionally, cleaning them out. - # db.instance_destroy(ctxt, instance['id']) if state != power_state.RUNNING: continue -- cgit From 52478e039b094861e7d783b7995b9cafa68e32b9 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 15:56:16 -0700 Subject: Fix to correct libvirt error code when the domain is not found --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4523cdd2f..a7a8a14b1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -974,7 +974,7 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom = self._conn.lookupByName(instance_name) except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_UNKNOWN_HOST: + if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: raise exception.NotFound(_("Instance %s not found") % instance_name) LOG.warning(_("Error from libvirt during lookup: %s") % e) -- cgit From 0c5f70c0bcf9395fb25a231057d997b075d04fda Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Thu, 7 Apr 2011 16:00:55 -0700 Subject: Log libvirt errcode on exception --- nova/virt/libvirt_conn.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a7a8a14b1..5f1c12ab3 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -974,10 +974,13 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom = self._conn.lookupByName(instance_name) except libvirt.libvirtError as e: - if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN: + errcode = e.get_error_code() + if errcode == libvirt.VIR_ERR_NO_DOMAIN: raise exception.NotFound(_("Instance %s not found") % instance_name) - LOG.warning(_("Error from libvirt during lookup: %s") % e) + LOG.warning(_("Error from libvirt during lookup. " + "Code=%(errcode)s Error=%(e)s") % + locals()) raise (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() -- cgit From b0ad20c796bd25dad0538ab85b1a56f421e16039 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 7 Apr 2011 16:42:33 -0700 Subject: fix tests from moving access check into update and delete --- nova/image/local.py | 5 ++++- nova/tests/image/test_glance.py | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/image/local.py b/nova/image/local.py index 8bf78b4c9..d4fd62156 100644 --- a/nova/image/local.py +++ b/nova/image/local.py @@ -122,12 +122,15 @@ class LocalImageService(service.BaseImageService): image_path = self._path_to(image_id, None) if not os.path.exists(image_path): os.mkdir(image_path) - return self.update(context, image_id, metadata, data) + return self._store(context, image_id, metadata, data) def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data.""" # NOTE(vish): show is to check if image is available self.show(context, image_id) + return self._store(context, image_id, metadata, data) + + def _store(self, context, image_id, metadata, data=None): metadata['id'] = image_id try: if data: diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 9d0b14613..109905ded 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -209,17 +209,17 @@ class TestMutatorDateTimeTests(BaseGlanceTest): self.assertDateTimesEmpty(image_meta) def test_update_handles_datetimes(self): + self.client.images = {'image1': self._make_datetime_fixture()} self.client.update_response = self._make_datetime_fixture() - dummy_id = 'dummy_id' dummy_meta = {} - image_meta = self.service.update(self.context, 'dummy_id', dummy_meta) + image_meta = self.service.update(self.context, 'image1', dummy_meta) self.assertDateTimesFilled(image_meta) def test_update_handles_none_datetimes(self): + self.client.images = {'image1': self._make_datetime_fixture()} self.client.update_response = self._make_none_datetime_fixture() - dummy_id = 'dummy_id' dummy_meta = {} - image_meta = self.service.update(self.context, 'dummy_id', dummy_meta) + image_meta = self.service.update(self.context, 'image1', dummy_meta) self.assertDateTimesEmpty(image_meta) def _make_datetime_fixture(self): -- cgit From 479c95b8e855c5f07b75883a1f55b4657e886a92 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 21:44:41 -0400 Subject: Ignore errors when deleting the default route in the ensure_bridge function. --- nova/network/linux_net.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ed6c943c7..ec5579dee 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -503,7 +503,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): if fields and fields[0] == "0.0.0.0" and fields[-1] == interface: gateway = fields[1] _execute('sudo', 'route', 'del', 'default', 'gw', gateway, - 'dev', interface) + 'dev', interface, check_exit_code=False) out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, 'scope', 'global') for line in out.split("\n"): -- cgit From 7badb6c0278c8cc51fc3e870fd3810ea3706f494 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Thu, 7 Apr 2011 23:39:29 -0400 Subject: Update the describe_image_attribute and modify_image_attribute functions in the ec2 API so they use the top level 'is_public' attribute of image objects. This brings these functions in line with the base image service. Added missing EC2 unit tests on describing and modifying image attributes. --- nova/api/ec2/cloud.py | 4 ++-- nova/tests/test_cloud.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4ed8a9ecf..651ec47f9 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -984,7 +984,7 @@ class CloudController(object): except exception.NotFound: raise exception.NotFound(_('Image %s not found') % image_id) result = {'imageId': image_id, 'launchPermission': []} - if image['properties']['is_public']: + if image['is_public']: result['launchPermission'].append({'group': 'all'}) return result @@ -1009,7 +1009,7 @@ class CloudController(object): internal_id = image['id'] del(image['id']) - image['properties']['is_public'] = (operation_type == 'add') + image['is_public'] = (operation_type == 'add') return self.image_service.update(context, internal_id, image) def update_image(self, context, image_id, **kwargs): diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 5cb969979..5f76a9005 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -247,6 +247,37 @@ class CloudTestCase(test.TestCase): self.assertRaises(NotFound, describe_images, self.context, ['ami-fake']) + def test_describe_image_attribute(self): + describe_image_attribute = self.cloud.describe_image_attribute + + def fake_show(meh, context, id): + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type': 'machine'}, 'is_public': True} + + self.stubs.Set(local.LocalImageService, 'show', fake_show) + self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) + result = describe_image_attribute(self.context, 'ami-00000001', + 'launchPermission') + self.assertEqual([{'group': 'all'}], result['launchPermission']) + + def test_modify_image_attribute(self): + modify_image_attribute = self.cloud.modify_image_attribute + + def fake_show(meh, context, id): + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type': 'machine'}, 'is_public': False} + + def fake_update(meh, context, image_id, metadata, data=None): + return metadata + + self.stubs.Set(local.LocalImageService, 'show', fake_show) + self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) + self.stubs.Set(local.LocalImageService, 'update', fake_update) + result = modify_image_attribute(self.context, 'ami-00000001', + 'launchPermission', 'add', + user_group=['all']) + self.assertEqual(True, result['is_public']) + def test_console_output(self): instance_type = FLAGS.default_instance_type max_count = 1 -- cgit From decdaa30acb15e088eb6a0ca3ebc8ea6f377cbfe Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Fri, 8 Apr 2011 12:22:09 -0400 Subject: Set default stateOrProvice to 'supplied' in openssl.cnf.tmpl. This resolves a stateOrProvince printable string UTF8 mismatch on RHEL 6 and Fedora 14 (using openssl-1.0.0-4.el6.x86_64 or openssl-1.0.0d-1.fc14.x86_64). Fixes x509 certificate generation on Fedora 14 and Redhat 6. --- nova/CA/openssl.cnf.tmpl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/CA/openssl.cnf.tmpl b/nova/CA/openssl.cnf.tmpl index dd81f1c2b..b80fadf40 100644 --- a/nova/CA/openssl.cnf.tmpl +++ b/nova/CA/openssl.cnf.tmpl @@ -41,9 +41,13 @@ nameopt = default_ca certopt = default_ca policy = policy_match +# NOTE(dprince): stateOrProvinceName must be 'supplied' or 'optional' to +# work around a stateOrProvince printable string UTF8 mismatch on +# RHEL 6 and Fedora 14 (using openssl-1.0.0-4.el6.x86_64 or +# openssl-1.0.0d-1.fc14.x86_64) [ policy_match ] countryName = match -stateOrProvinceName = match +stateOrProvinceName = supplied organizationName = optional organizationalUnitName = optional commonName = supplied -- cgit From 0cf2a52218fbb801a35e5dd73e146c6c37e218e2 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Sat, 9 Apr 2011 02:39:18 +0900 Subject: fixed LOG level and log message phrase --- nova/compute/manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 847c3655a..c7d86e27b 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1094,9 +1094,8 @@ class ComputeManager(manager.SchedulerDependentManager): # A situation which db record exists, but no instance" # sometimes occurs while live-migration at src compute, # this case should be ignored. - LOG.info(_("the instance '%(name)s' is not found in hypervisor" - ", while db record is found. But not synchronize " - "since it is migrating.") % locals()) + LOG.debug(_("Ignoring %(name)s, as it's currently being " + "migrated.") % locals()) continue if vm_state != db_state: -- cgit From 5deb4796bc26d98eeea94065c5098f7ce30ac2af Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Fri, 8 Apr 2011 11:21:36 -0700 Subject: Short circuit non-existant device during unit tests. It won't ever be created because of the stubs used during the unit tests --- nova/virt/xenapi/vm_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 73a1e2a3a..d2045a557 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -936,7 +936,11 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f): if dev != orig_dev: LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, ' 'remapping to %(dev)s') % locals()) - _wait_for_device(dev) + if dev != 'autodetect': + # NOTE(johannes): Unit tests will end up with a device called + # 'autodetect' which obviously won't exist. It's not ideal, + # but the alternatives were much messier + _wait_for_device(dev) return f(dev) finally: LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref) -- cgit From 5ea0991db9526969f100f3361661731aaf4d24d5 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 8 Apr 2011 15:22:15 -0400 Subject: Fixes bug which hangs nova-compute when terminating an instance when using libvirt backend. --- nova/virt/libvirt_conn.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b949e6c92..51a0a3380 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -325,12 +325,13 @@ class LibvirtConnection(driver.ComputeDriver): state = self.get_info(instance['name'])['state'] db.instance_set_state(context.get_admin_context(), instance['id'], state) - if state == power_state.SHUTDOWN: + if state == power_state.SHUTOFF: break - except Exception: + except Exception as ex: + LOG.debug(ex) db.instance_set_state(context.get_admin_context(), instance['id'], - power_state.SHUTDOWN) + power_state.SHUTOFF) break self.firewall_driver.unfilter_instance(instance) -- cgit From a1c40feb0cd592829b63df1cf19109bc322f81a7 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Fri, 8 Apr 2011 15:54:17 -0400 Subject: Added error message to exception logging. --- nova/virt/libvirt_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 51a0a3380..bfa9ff688 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -328,7 +328,9 @@ class LibvirtConnection(driver.ComputeDriver): if state == power_state.SHUTOFF: break except Exception as ex: - LOG.debug(ex) + msg = _("Error encountered when destroying instance '%(id)s': " + "%(ex)s") % locals().update({"id": instance["id"]}) + LOG.debug(msg) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTOFF) -- cgit From 79ebe165f255037b0d5eaad7afe81b51cf85ed63 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Sat, 9 Apr 2011 11:08:47 -0400 Subject: Fixed log message gaffe. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index bfa9ff688..9c665ab15 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -329,7 +329,7 @@ class LibvirtConnection(driver.ComputeDriver): break except Exception as ex: msg = _("Error encountered when destroying instance '%(id)s': " - "%(ex)s") % locals().update({"id": instance["id"]}) + "%(ex)s") % {"id": instance["id"], "ex": ex} LOG.debug(msg) db.instance_set_state(context.get_admin_context(), instance['id'], -- cgit From a572b49e376cd6da4265c2807eaed8f0a2daf954 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 11:57:14 -0700 Subject: Remove the XML definition when we destroy a machine --- nova/virt/libvirt_conn.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b949e6c92..1ad6f8b32 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,10 +309,18 @@ class LibvirtConnection(driver.ComputeDriver): return infos def destroy(self, instance, cleanup=True): + name = instance['name'] try: - virt_dom = self._conn.lookupByName(instance['name']) + virt_dom = self._conn.lookupByName(name) virt_dom.destroy() - except Exception as _err: + # NOTE(justinsb): We remove the domain definition. We probably + # would do better to keep it if cleanup=False (e.g. volumes?) + # (e.g. #2 - not losing machines on failure) + virt_dom.undefine() + except Exception as e: + # TODO(justinsb): We really should check the error is 'not found' + LOG.warn(_("Ignoring error destroying domain %(name)s: %(e)s") % + locals()) pass # If the instance is already terminated, we're still happy -- cgit From be386ee614777212da2a14ebd8211f4b3d90ce66 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:33:24 -0700 Subject: Split logic on shutdown and undefine, so that even if the machine is already shutdown we will be able to proceed --- nova/virt/libvirt_conn.py | 53 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1ad6f8b32..47eb17abb 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -309,20 +309,47 @@ class LibvirtConnection(driver.ComputeDriver): return infos def destroy(self, instance, cleanup=True): - name = instance['name'] + instance_name = instance['name'] + + # TODO(justinsb): Refactor all lookupByName calls for error-handling try: - virt_dom = self._conn.lookupByName(name) - virt_dom.destroy() - # NOTE(justinsb): We remove the domain definition. We probably - # would do better to keep it if cleanup=False (e.g. volumes?) - # (e.g. #2 - not losing machines on failure) - virt_dom.undefine() - except Exception as e: - # TODO(justinsb): We really should check the error is 'not found' - LOG.warn(_("Ignoring error destroying domain %(name)s: %(e)s") % - locals()) - pass - # If the instance is already terminated, we're still happy + virt_dom = self._conn.lookupByName(instance_name) + except libvirt.libvirtError as e: + errcode = e.get_error_code() + if errcode == libvirt.VIR_ERR_NO_DOMAIN: + virt_dom = None + else: + LOG.warning(_("Error from libvirt during lookup of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + # If the instance is already terminated, we're still happy + # Otherwise, destroy it + if virt_dom is not None: + try: + virt_dom.destroy() + except libvirt.libvirtError as e: + errcode = e.get_error_code() + LOG.warning(_("Error from libvirt during destroy of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise + + try: + # NOTE(justinsb): We remove the domain definition. We probably + # would do better to keep it if cleanup=False (e.g. volumes?) + # (e.g. #2 - not losing machines on failure) + virt_dom.undefine() + except libvirt.libvirtError as e: + errcode = e.get_error_code() + LOG.warning(_("Error from libvirt during undefine of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise # We'll save this for when we do shutdown, # instead of destroy - but destroy returns immediately -- cgit From c6923ec603288e1d46fdb80e874c8e71361442f5 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:41:30 -0700 Subject: Handle the case when the machine is already SHUTOFF --- nova/virt/libvirt_conn.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 47eb17abb..7771aad7a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -331,12 +331,22 @@ class LibvirtConnection(driver.ComputeDriver): try: virt_dom.destroy() except libvirt.libvirtError as e: + is_okay = False errcode = e.get_error_code() - LOG.warning(_("Error from libvirt during destroy of " - "%(instance_name)s. Code=%(errcode)s " - "Error=%(e)s") % - locals()) - raise + if errcode == libvirt.VIR_ERR_OPERATION_INVALID: + # If the instance if already shut off, we get this: + # Code=55 Error=Requested operation is not valid: + # domain is not running + (state, _, _, _, _) = virt_dom.info() + if state == power_state.SHUTOFF: + is_okay = True + + if not is_okay: + LOG.warning(_("Error from libvirt during destroy of " + "%(instance_name)s. Code=%(errcode)s " + "Error=%(e)s") % + locals()) + raise try: # NOTE(justinsb): We remove the domain definition. We probably -- cgit From f59f8e8fcbde6f0d8d4c19b00bfc5f4141287772 Mon Sep 17 00:00:00 2001 From: Justin SB Date: Sat, 9 Apr 2011 12:57:32 -0700 Subject: Ooops - redefining the _ variable seems like a _really_ bad idea --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 7771aad7a..22b0e1103 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -337,7 +337,7 @@ class LibvirtConnection(driver.ComputeDriver): # If the instance if already shut off, we get this: # Code=55 Error=Requested operation is not valid: # domain is not running - (state, _, _, _, _) = virt_dom.info() + (state, _max_mem, _mem, _cpus, _t) = virt_dom.info() if state == power_state.SHUTOFF: is_okay = True -- cgit From 5752838917237e7b86a64117f46c71d1c2a356f3 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 11 Apr 2011 16:18:18 +0200 Subject: Replace instance ref from compute.api.get_all with one from instance_get. This should ensure it gets fully populated with all the relevant attributes. --- nova/api/ec2/cloud.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 651ec47f9..83c894bea 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -142,6 +142,11 @@ class CloudController(object): instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address) if instance_ref is None: return None + + # This ensures that all attributes of the instance + # are populated. + instance_ref = db.instance_get(ctxt, instance_ref['id']) + mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) if instance_ref['key_name']: keys = {'0': {'_name': instance_ref['key_name'], -- cgit From 21fd04c34487b97f7d1ed199773cf80e9ab60839 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 11 Apr 2011 15:56:36 +0000 Subject: fix reference to genvpn to point to the right shell script --- nova/crypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/crypto.py b/nova/crypto.py index 9b1897926..605be2a32 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -232,7 +232,7 @@ def generate_vpn_files(project_id): genvpn_sh_path = os.path.join(os.path.dirname(__file__), 'CA', - 'geninter.sh') + 'genvpn.sh') if os.path.exists(crt_fn): return _ensure_project_folder(project_id) -- cgit From 07c1f30225fb27cbc8e7cfeebc6a73ec67a7f2e5 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 12 Apr 2011 09:41:42 +0200 Subject: pep8 --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 83c894bea..5e81878c4 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -144,7 +144,7 @@ class CloudController(object): return None # This ensures that all attributes of the instance - # are populated. + # are populated. instance_ref = db.instance_get(ctxt, instance_ref['id']) mpi = self._get_mpi_data(ctxt, instance_ref['project_id']) -- cgit