diff options
| author | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2011-07-29 18:59:33 +0000 |
|---|---|---|
| committer | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2011-07-29 18:59:33 +0000 |
| commit | 62c7ca622a42aaed9a4f23e8fc2167655b2ff58f (patch) | |
| tree | 5d62b83e53098339f20f003ee99d8c0760aed7ce | |
| parent | c0355038b462cfd75b423a535601c4463c68f80f (diff) | |
| parent | ab0c38efa5440347c3c75171e703e010023affe2 (diff) | |
pull-up from trunk; fix problem obscuring context module with context param; fix conflicts and no-longer-skipped tests
29 files changed, 1449 insertions, 462 deletions
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index bd14a1389..efa4ab385 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -17,12 +17,14 @@ import re from urlparse import urlparse +from xml.dom import minidom import webob from nova import exception from nova import flags from nova import log as logging +from nova.api.openstack import wsgi LOG = logging.getLogger('nova.api.openstack.common') @@ -192,3 +194,83 @@ def get_version_from_href(href): except IndexError: version = '1.0' return version + + +class MetadataXMLDeserializer(wsgi.MetadataXMLDeserializer): + + def _extract_metadata_container(self, datastring): + dom = minidom.parseString(datastring) + metadata_node = self.find_first_child_named(dom, "metadata") + metadata = self.extract_metadata(metadata_node) + return {'body': {'metadata': metadata}} + + def create(self, datastring): + return self._extract_metadata_container(datastring) + + def update_all(self, datastring): + return self._extract_metadata_container(datastring) + + def update(self, datastring): + dom = minidom.parseString(datastring) + metadata_item = self.extract_metadata(dom) + return {'body': {'meta': metadata_item}} + + +class MetadataHeadersSerializer(wsgi.ResponseHeadersSerializer): + + def delete(self, response, data): + response.status_int = 204 + + +class MetadataXMLSerializer(wsgi.XMLDictSerializer): + def __init__(self, xmlns=wsgi.XMLNS_V11): + super(MetadataXMLSerializer, self).__init__(xmlns=xmlns) + + def _meta_item_to_xml(self, doc, key, value): + node = doc.createElement('meta') + doc.appendChild(node) + node.setAttribute('key', '%s' % key) + text = doc.createTextNode('%s' % value) + node.appendChild(text) + return node + + def meta_list_to_xml(self, xml_doc, meta_items): + container_node = xml_doc.createElement('metadata') + for (key, value) in meta_items: + item_node = self._meta_item_to_xml(xml_doc, key, value) + container_node.appendChild(item_node) + return container_node + + def _meta_list_to_xml_string(self, metadata_dict): + xml_doc = minidom.Document() + items = metadata_dict['metadata'].items() + container_node = self.meta_list_to_xml(xml_doc, items) + xml_doc.appendChild(container_node) + self._add_xmlns(container_node) + return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') + + def index(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def create(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def update_all(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def _meta_item_to_xml_string(self, meta_item_dict): + xml_doc = minidom.Document() + item_key, item_value = meta_item_dict.items()[0] + item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) + xml_doc.appendChild(item_node) + self._add_xmlns(item_node) + return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') + + def show(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + def update(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + def default(self, *args, **kwargs): + return '' diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index f8317565e..1342397c4 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -188,7 +188,7 @@ class CreateInstanceHelper(object): Overrides normal behavior in the case of xml content """ if request.content_type == "application/xml": - deserializer = ServerCreateRequestXMLDeserializer() + deserializer = ServerXMLDeserializer() return deserializer.deserialize(request.body) else: return self._deserialize(request.body, request.get_content_type()) @@ -303,29 +303,29 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): """Marshal the server attribute of a parsed request""" server = {} server_node = self.find_first_child_named(node, 'server') - for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]: + + attributes = ["name", "imageId", "flavorId", "imageRef", + "flavorRef", "adminPass"] + for attr in attributes: if server_node.getAttribute(attr): server[attr] = server_node.getAttribute(attr) + metadata_node = self.find_first_child_named(server_node, "metadata") - metadata = self.extract_metadata(metadata_node) - if metadata is not None: - server["metadata"] = metadata - personality = self._extract_personality(server_node) - if personality is not None: - server["personality"] = personality + server["metadata"] = self.extract_metadata(metadata_node) + + server["personality"] = self._extract_personality(server_node) + return server def _extract_personality(self, server_node): """Marshal the personality attribute of a parsed request""" - personality_node = \ - self.find_first_child_named(server_node, "personality") - if personality_node is None: - return None + node = self.find_first_child_named(server_node, "personality") personality = [] - for file_node in self.find_children_named(personality_node, "file"): - item = {} - if file_node.hasAttribute("path"): - item["path"] = file_node.getAttribute("path") - item["contents"] = self.extract_text(file_node) - personality.append(item) + if node is not None: + for file_node in self.find_children_named(node, "file"): + item = {} + if file_node.hasAttribute("path"): + item["path"] = file_node.getAttribute("path") + item["contents"] = self.extract_text(file_node) + personality.append(item) return personality diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index ee181c924..aaf64a123 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -16,12 +16,12 @@ # under the License. from webob import exc -from xml.dom import minidom from nova import flags from nova import image from nova import quota from nova import utils +from nova.api.openstack import common from nova.api.openstack import wsgi @@ -118,95 +118,15 @@ class Controller(object): self.image_service.update(context, image_id, img, None) -class ImageMetadataXMLDeserializer(wsgi.MetadataXMLDeserializer): - - def _extract_metadata_container(self, datastring): - dom = minidom.parseString(datastring) - metadata_node = self.find_first_child_named(dom, "metadata") - metadata = self.extract_metadata(metadata_node) - return {'body': {'metadata': metadata}} - - def create(self, datastring): - return self._extract_metadata_container(datastring) - - def update_all(self, datastring): - return self._extract_metadata_container(datastring) - - def update(self, datastring): - dom = minidom.parseString(datastring) - metadata_item = self.extract_metadata(dom) - return {'body': {'meta': metadata_item}} - - -class HeadersSerializer(wsgi.ResponseHeadersSerializer): - - def delete(self, response, data): - response.status_int = 204 - - -class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): - def __init__(self, xmlns=wsgi.XMLNS_V11): - super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns) - - def _meta_item_to_xml(self, doc, key, value): - node = doc.createElement('meta') - doc.appendChild(node) - node.setAttribute('key', '%s' % key) - text = doc.createTextNode('%s' % value) - node.appendChild(text) - return node - - def meta_list_to_xml(self, xml_doc, meta_items): - container_node = xml_doc.createElement('metadata') - for (key, value) in meta_items: - item_node = self._meta_item_to_xml(xml_doc, key, value) - container_node.appendChild(item_node) - return container_node - - def _meta_list_to_xml_string(self, metadata_dict): - xml_doc = minidom.Document() - items = metadata_dict['metadata'].items() - container_node = self.meta_list_to_xml(xml_doc, items) - xml_doc.appendChild(container_node) - self._add_xmlns(container_node) - return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') - - def index(self, metadata_dict): - return self._meta_list_to_xml_string(metadata_dict) - - def create(self, metadata_dict): - return self._meta_list_to_xml_string(metadata_dict) - - def update_all(self, metadata_dict): - return self._meta_list_to_xml_string(metadata_dict) - - def _meta_item_to_xml_string(self, meta_item_dict): - xml_doc = minidom.Document() - item_key, item_value = meta_item_dict.items()[0] - item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) - xml_doc.appendChild(item_node) - self._add_xmlns(item_node) - return xml_doc.toprettyxml(indent=' ', encoding='UTF-8') - - def show(self, meta_item_dict): - return self._meta_item_to_xml_string(meta_item_dict['meta']) - - def update(self, meta_item_dict): - return self._meta_item_to_xml_string(meta_item_dict['meta']) - - def default(self, *args, **kwargs): - return '' - - def create_resource(): - headers_serializer = HeadersSerializer() + headers_serializer = common.MetadataHeadersSerializer() body_deserializers = { - 'application/xml': ImageMetadataXMLDeserializer(), + 'application/xml': common.MetadataXMLDeserializer(), } body_serializers = { - 'application/xml': ImageMetadataXMLSerializer(), + 'application/xml': common.MetadataXMLSerializer(), } serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer) deserializer = wsgi.RequestDeserializer(body_deserializers) diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 30e4fd389..9ba8b639e 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -284,7 +284,7 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer): xmlns = wsgi.XMLNS_V11 def __init__(self): - self.metadata_serializer = image_metadata.ImageMetadataXMLSerializer() + self.metadata_serializer = common.MetadataXMLSerializer() def _image_to_xml(self, xml_doc, image): image_node = xml_doc.createElement('image') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index d7cabb067..f6841318d 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -18,6 +18,7 @@ import traceback from webob import exc import webob +from xml.dom import minidom from nova import compute from nova import db @@ -27,6 +28,7 @@ from nova import log as logging from nova import utils from nova.api.openstack import common from nova.api.openstack import create_instance_helper as helper +from nova.api.openstack import ips import nova.api.openstack.views.addresses import nova.api.openstack.views.flavors import nova.api.openstack.views.images @@ -480,11 +482,20 @@ class ControllerV11(Controller): raise exc.HTTPNotFound() def _image_ref_from_req_data(self, data): - return data['server']['imageRef'] + try: + return data['server']['imageRef'] + except (TypeError, KeyError): + msg = _("Missing imageRef attribute") + raise exc.HTTPBadRequest(explanation=msg) def _flavor_id_from_req_data(self, data): - href = data['server']['flavorRef'] - return common.get_id_from_href(href) + try: + flavor_ref = data['server']['flavorRef'] + except (TypeError, KeyError): + msg = _("Missing flavorRef attribute") + raise exc.HTTPBadRequest(explanation=msg) + + return common.get_id_from_href(flavor_ref) def _build_view(self, req, instance, is_detail=False): base_url = req.application_url @@ -599,6 +610,123 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer): response.status_int = 204 +class ServerXMLSerializer(wsgi.XMLDictSerializer): + + xmlns = wsgi.XMLNS_V11 + + def __init__(self): + self.metadata_serializer = common.MetadataXMLSerializer() + self.addresses_serializer = ips.IPXMLSerializer() + + def _create_basic_entity_node(self, xml_doc, id, links, name): + basic_node = xml_doc.createElement(name) + basic_node.setAttribute('id', str(id)) + link_nodes = self._create_link_nodes(xml_doc, links) + for link_node in link_nodes: + basic_node.appendChild(link_node) + return basic_node + + def _create_metadata_node(self, xml_doc, metadata): + return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata) + + def _create_addresses_node(self, xml_doc, addresses): + return self.addresses_serializer.networks_to_xml(xml_doc, addresses) + + def _add_server_attributes(self, node, server): + node.setAttribute('id', str(server['id'])) + node.setAttribute('uuid', str(server['uuid'])) + node.setAttribute('hostId', str(server['hostId'])) + node.setAttribute('name', server['name']) + node.setAttribute('created', str(server['created'])) + node.setAttribute('updated', str(server['updated'])) + node.setAttribute('status', server['status']) + if 'progress' in server: + node.setAttribute('progress', str(server['progress'])) + + def _server_to_xml(self, xml_doc, server): + server_node = xml_doc.createElement('server') + server_node.setAttribute('id', str(server['id'])) + server_node.setAttribute('name', server['name']) + link_nodes = self._create_link_nodes(xml_doc, + server['links']) + for link_node in link_nodes: + server_node.appendChild(link_node) + return server_node + + def _server_to_xml_detailed(self, xml_doc, server): + server_node = xml_doc.createElement('server') + self._add_server_attributes(server_node, server) + + link_nodes = self._create_link_nodes(xml_doc, + server['links']) + for link_node in link_nodes: + server_node.appendChild(link_node) + + if 'image' in server: + image_node = self._create_basic_entity_node(xml_doc, + server['image']['id'], + server['image']['links'], + 'image') + server_node.appendChild(image_node) + + if 'flavor' in server: + flavor_node = self._create_basic_entity_node(xml_doc, + server['flavor']['id'], + server['flavor']['links'], + 'flavor') + server_node.appendChild(flavor_node) + + metadata = server.get('metadata', {}).items() + if len(metadata) > 0: + metadata_node = self._create_metadata_node(xml_doc, metadata) + server_node.appendChild(metadata_node) + + addresses_node = self._create_addresses_node(xml_doc, + server['addresses']) + server_node.appendChild(addresses_node) + + return server_node + + def _server_list_to_xml(self, xml_doc, servers, detailed): + container_node = xml_doc.createElement('servers') + if detailed: + server_to_xml = self._server_to_xml_detailed + else: + server_to_xml = self._server_to_xml + + for server in servers: + item_node = server_to_xml(xml_doc, server) + container_node.appendChild(item_node) + return container_node + + def index(self, servers_dict): + xml_doc = minidom.Document() + node = self._server_list_to_xml(xml_doc, + servers_dict['servers'], + detailed=False) + return self.to_xml_string(node, True) + + def detail(self, servers_dict): + xml_doc = minidom.Document() + node = self._server_list_to_xml(xml_doc, + servers_dict['servers'], + detailed=True) + return self.to_xml_string(node, True) + + def show(self, server_dict): + xml_doc = minidom.Document() + node = self._server_to_xml_detailed(xml_doc, + server_dict['server']) + return self.to_xml_string(node, True) + + def create(self, server_dict): + xml_doc = minidom.Document() + node = self._server_to_xml_detailed(xml_doc, + server_dict['server']) + node.setAttribute('adminPass', server_dict['server']['adminPass']) + return self.to_xml_string(node, True) + + def create_resource(version='1.0'): controller = { '1.0': ControllerV10, @@ -628,9 +756,13 @@ def create_resource(version='1.0'): headers_serializer = HeadersSerializer() + xml_serializer = { + '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10), + '1.1': ServerXMLSerializer(), + }[version] + body_serializers = { - 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, - xmlns=xmlns), + 'application/xml': xml_serializer, } body_deserializers = { diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index be25e1e40..659a43522 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import hashlib import os @@ -149,8 +150,10 @@ class ViewBuilderV11(ViewBuilder): def _build_detail(self, inst): response = super(ViewBuilderV11, self)._build_detail(inst) - response['server']['created'] = inst['created_at'] - response['server']['updated'] = inst['updated_at'] + response['server']['created'] = \ + self._convert_timeformat(inst['created_at']) + response['server']['updated'] = \ + self._convert_timeformat(inst['updated_at']) if 'status' in response['server']: if response['server']['status'] == "ACTIVE": response['server']['progress'] = 100 @@ -221,3 +224,11 @@ class ViewBuilderV11(ViewBuilder): """Create an url that refers to a specific flavor id.""" return os.path.join(common.remove_version_from_href(self.base_url), "servers", str(server_id)) + + def _convert_timeformat(self, date_time): + """Converts the given time into the common time format + + :param date_time: the datetime object to convert + + """ + return date_time.strftime(utils.TIME_FORMAT) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index a28443d12..53dab22e8 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -165,12 +165,11 @@ class MetadataXMLDeserializer(XMLDeserializer): def extract_metadata(self, metadata_node): """Marshal the metadata attribute of a parsed request""" - if metadata_node is None: - return None metadata = {} - for meta_node in self.find_children_named(metadata_node, "meta"): - key = meta_node.getAttribute("key") - metadata[key] = self.extract_text(meta_node) + if metadata_node is not None: + for meta_node in self.find_children_named(metadata_node, "meta"): + key = meta_node.getAttribute("key") + metadata[key] = self.extract_text(meta_node) return metadata diff --git a/nova/auth/dbdriver.py b/nova/auth/dbdriver.py index 0158c4e23..c6d81ee04 100644 --- a/nova/auth/dbdriver.py +++ b/nova/auth/dbdriver.py @@ -127,7 +127,7 @@ class DbDriver(object): try: project = db.project_create(context.get_admin_context(), values) - except (exception.Duplicate, exception.DBError): + except exception.DBError: raise exception.ProjectExists(project=name) for member in members: diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 83a218580..56331f86b 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -747,7 +747,7 @@ class ComputeManager(manager.SchedulerDependentManager): local_gb=instance_type['local_gb'], instance_type_id=instance_type['id'])) - self.driver.revert_resize(instance_ref) + self.driver.revert_migration(instance_ref) self.db.migration_update(context, migration_id, {'status': 'reverted'}) usage_info = utils.usage_from_instance(instance_ref) @@ -845,21 +845,26 @@ class ComputeManager(manager.SchedulerDependentManager): """ migration_ref = self.db.migration_get(context, migration_id) + + resize_instance = False instance_ref = self.db.instance_get_by_uuid(context, migration_ref.instance_uuid) - instance_type = self.db.instance_type_get_by_flavor_id(context, - migration_ref['new_flavor_id']) - self.db.instance_update(context, instance_ref.uuid, - dict(instance_type_id=instance_type['id'], - memory_mb=instance_type['memory_mb'], - vcpus=instance_type['vcpus'], - local_gb=instance_type['local_gb'])) + if migration_ref['old_flavor_id'] != migration_ref['new_flavor_id']: + instance_type = self.db.instance_type_get_by_flavor_id(context, + migration_ref['new_flavor_id']) + self.db.instance_update(context, instance_ref.uuid, + dict(instance_type_id=instance_type['id'], + memory_mb=instance_type['memory_mb'], + vcpus=instance_type['vcpus'], + local_gb=instance_type['local_gb'])) + resize_instance = True instance_ref = self.db.instance_get_by_uuid(context, instance_ref.uuid) + network_info = self._get_instance_nw_info(context, instance_ref) - self.driver.finish_resize(context, instance_ref, disk_info, - network_info) + self.driver.finish_migration(context, instance_ref, disk_info, + network_info, resize_instance) self.db.migration_update(context, migration_id, {'status': 'finished', }) diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index f09270b34..0b76841f0 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -20,6 +20,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API. """ import webob.exc +import xml.dom.minidom as minidom from webob import Request @@ -265,3 +266,203 @@ class MiscFunctionsTest(test.TestCase): expected = '1.0' actual = common.get_version_from_href(fixture) self.assertEqual(actual, expected) + + +class MetadataXMLDeserializationTest(test.TestCase): + + deserializer = common.MetadataXMLDeserializer() + + def test_create(self): + request_body = """ + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key='123'>asdf</meta> + <meta key='567'>jkl;</meta> + </metadata>""" + output = self.deserializer.deserialize(request_body, 'create') + expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} + self.assertEquals(output, expected) + + def test_create_empty(self): + request_body = """ + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>""" + output = self.deserializer.deserialize(request_body, 'create') + expected = {"body": {"metadata": {}}} + self.assertEquals(output, expected) + + def test_update_all(self): + request_body = """ + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key='123'>asdf</meta> + <meta key='567'>jkl;</meta> + </metadata>""" + output = self.deserializer.deserialize(request_body, 'update_all') + expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} + self.assertEquals(output, expected) + + def test_update(self): + request_body = """ + <meta xmlns="http://docs.openstack.org/compute/api/v1.1" + key='123'>asdf</meta>""" + output = self.deserializer.deserialize(request_body, 'update') + expected = {"body": {"meta": {"123": "asdf"}}} + self.assertEquals(output, expected) + + +class MetadataXMLSerializationTest(test.TestCase): + + def test_index(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + 'one': 'two', + 'three': 'four', + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key="three"> + four + </meta> + <meta key="one"> + two + </meta> + </metadata> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index_null(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + None: None, + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key="None"> + None + </meta> + </metadata> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index_unicode(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + u'three': u'Jos\xe9', + }, + } + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(u""" + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key="three"> + Jos\xe9 + </meta> + </metadata> + """.encode("UTF-8").replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_show(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.serialize(fixture, 'show') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one"> + two + </meta> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_update_all(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + 'key6': 'value6', + 'key4': 'value4', + }, + } + output = serializer.serialize(fixture, 'update_all') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key="key6"> + value6 + </meta> + <meta key="key4"> + value4 + </meta> + </metadata> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_update_item(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.serialize(fixture, 'update') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one"> + two + </meta> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_create(self): + serializer = common.MetadataXMLSerializer() + fixture = { + 'metadata': { + 'key9': 'value9', + 'key2': 'value2', + 'key1': 'value1', + }, + } + output = serializer.serialize(fixture, 'create') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> + <meta key="key2"> + value2 + </meta> + <meta key="key9"> + value9 + </meta> + <meta key="key1"> + value1 + </meta> + </metadata> + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_delete(self): + serializer = common.MetadataXMLSerializer() + output = serializer.serialize(None, 'delete') + self.assertEqual(output, '') diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index d459c694f..bf2d0368f 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -109,8 +109,8 @@ class ExtensionControllerTest(unittest.TestCase): 'updated': '2011-01-22T13:25:27-06:00', 'description': 'The Fox In Socks Extension', 'alias': 'FOXNSOX', - 'links': [] - } + 'links': [], + }, ) def test_get_extension_json(self): @@ -127,8 +127,8 @@ class ExtensionControllerTest(unittest.TestCase): "updated": "2011-01-22T13:25:27-06:00", "description": "The Fox In Socks Extension", "alias": "FOXNSOX", - "links": [] - } + "links": [], + }, ) def test_list_extensions_xml(self): @@ -342,15 +342,15 @@ class ExtensionsXMLSerializerTest(unittest.TestCase): { 'rel': 'describedby', 'type': 'application/pdf', - 'href': 'http://docs.rack.com/servers/api/ext/cs.pdf' + 'href': 'http://docs.rack.com/servers/api/ext/cs.pdf', }, { 'rel': 'describedby', 'type': 'application/vnd.sun.wadl+xml', - 'href': 'http://docs.rack.com/servers/api/ext/cs.wadl' - } - ] - } + 'href': 'http://docs.rack.com/servers/api/ext/cs.wadl', + }, + ], + }, } xml = serializer.serialize(data, 'show') @@ -382,14 +382,14 @@ class ExtensionsXMLSerializerTest(unittest.TestCase): { "rel": "describedby", "type": "application/pdf", - "href": "http://foo.com/api/ext/cs-pie.pdf" + "href": "http://foo.com/api/ext/cs-pie.pdf", }, { "rel": "describedby", "type": "application/vnd.sun.wadl+xml", - "href": "http://foo.com/api/ext/cs-pie.wadl" - } - ] + "href": "http://foo.com/api/ext/cs-pie.wadl", + }, + ], }, { "name": "Cloud Block Storage", @@ -401,16 +401,16 @@ class ExtensionsXMLSerializerTest(unittest.TestCase): { "rel": "describedby", "type": "application/pdf", - "href": "http://foo.com/api/ext/cs-cbs.pdf" + "href": "http://foo.com/api/ext/cs-cbs.pdf", }, { "rel": "describedby", "type": "application/vnd.sun.wadl+xml", - "href": "http://foo.com/api/ext/cs-cbs.wadl" - } - ] - } - ] + "href": "http://foo.com/api/ext/cs-cbs.wadl", + }, + ], + }, + ], } xml = serializer.serialize(data, 'index') diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 31ca18497..c9137cc24 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -19,7 +19,6 @@ import json import stubout import unittest import webob -import xml.dom.minidom as minidom from nova import flags @@ -252,203 +251,3 @@ class ImageMetaDataTest(test.TestCase): req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) - - -class ImageMetadataXMLDeserializationTest(test.TestCase): - - deserializer = openstack.image_metadata.ImageMetadataXMLDeserializer() - - def test_create(self): - request_body = """ - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key='123'>asdf</meta> - <meta key='567'>jkl;</meta> - </metadata>""" - output = self.deserializer.deserialize(request_body, 'create') - expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} - self.assertEquals(output, expected) - - def test_create_empty(self): - request_body = """ - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>""" - output = self.deserializer.deserialize(request_body, 'create') - expected = {"body": {"metadata": {}}} - self.assertEquals(output, expected) - - def test_update_all(self): - request_body = """ - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key='123'>asdf</meta> - <meta key='567'>jkl;</meta> - </metadata>""" - output = self.deserializer.deserialize(request_body, 'update_all') - expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} - self.assertEquals(output, expected) - - def test_update(self): - request_body = """ - <meta xmlns="http://docs.openstack.org/compute/api/v1.1" - key='123'>asdf</meta>""" - output = self.deserializer.deserialize(request_body, 'update') - expected = {"body": {"meta": {"123": "asdf"}}} - self.assertEquals(output, expected) - - -class ImageMetadataXMLSerializationTest(test.TestCase): - - def test_index(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - 'one': 'two', - 'three': 'four', - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key="three"> - four - </meta> - <meta key="one"> - two - </meta> - </metadata> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_index_null(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - None: None, - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key="None"> - None - </meta> - </metadata> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_index_unicode(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - u'three': u'Jos\xe9', - }, - } - output = serializer.serialize(fixture, 'index') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(u""" - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key="three"> - Jos\xe9 - </meta> - </metadata> - """.encode("UTF-8").replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_show(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'meta': { - 'one': 'two', - }, - } - output = serializer.serialize(fixture, 'show') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one"> - two - </meta> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_update_all(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - 'key6': 'value6', - 'key4': 'value4', - }, - } - output = serializer.serialize(fixture, 'update_all') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key="key6"> - value6 - </meta> - <meta key="key4"> - value4 - </meta> - </metadata> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_update_item(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'meta': { - 'one': 'two', - }, - } - output = serializer.serialize(fixture, 'update') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one"> - two - </meta> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_create(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - fixture = { - 'metadata': { - 'key9': 'value9', - 'key2': 'value2', - 'key1': 'value1', - }, - } - output = serializer.serialize(fixture, 'create') - actual = minidom.parseString(output.replace(" ", "")) - - expected = minidom.parseString(""" - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> - <meta key="key2"> - value2 - </meta> - <meta key="key9"> - value9 - </meta> - <meta key="key1"> - value1 - </meta> - </metadata> - """.replace(" ", "")) - - self.assertEqual(expected.toxml(), actual.toxml()) - - def test_delete(self): - serializer = openstack.image_metadata.ImageMetadataXMLSerializer() - output = serializer.serialize(None, 'delete') - self.assertEqual(output, '') diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py index 8a3fe681a..6c3d531e3 100644 --- a/nova/tests/api/openstack/test_limits.py +++ b/nova/tests/api/openstack/test_limits.py @@ -920,7 +920,7 @@ class LimitsViewBuilderV11Test(test.TestCase): "verb": "POST", "remaining": 2, "unit": "MINUTE", - "resetTime": 1311272226 + "resetTime": 1311272226, }, { "URI": "*/servers", @@ -929,7 +929,7 @@ class LimitsViewBuilderV11Test(test.TestCase): "verb": "POST", "remaining": 10, "unit": "DAY", - "resetTime": 1311272226 + "resetTime": 1311272226, }, ] self.absolute_limits = { @@ -954,7 +954,7 @@ class LimitsViewBuilderV11Test(test.TestCase): "verb": "POST", "remaining": 2, "unit": "MINUTE", - "next-available": "2011-07-21T18:17:06Z" + "next-available": "2011-07-21T18:17:06Z", }, ] }, @@ -967,7 +967,7 @@ class LimitsViewBuilderV11Test(test.TestCase): "verb": "POST", "remaining": 10, "unit": "DAY", - "next-available": "2011-07-21T18:17:06Z" + "next-available": "2011-07-21T18:17:06Z", }, ] }, @@ -989,7 +989,7 @@ class LimitsViewBuilderV11Test(test.TestCase): expected_limits = { "limits": { "rate": [], - "absolute": {} + "absolute": {}, } } @@ -1022,7 +1022,7 @@ class LimitsXMLSerializationTest(test.TestCase): "verb": "POST", "remaining": 2, "unit": "MINUTE", - "next-available": "2011-12-15T22:42:45Z" + "next-available": "2011-12-15T22:42:45Z", }, ] }, @@ -1083,7 +1083,7 @@ class LimitsXMLSerializationTest(test.TestCase): fixture = { "limits": { "rate": [], - "absolute": {} + "absolute": {}, } } diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 4ca79434f..4027ef829 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -16,6 +16,7 @@ # under the License. import base64 +import datetime import json import unittest from xml.dom import minidom @@ -172,8 +173,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None, instance = { "id": int(id), - "created_at": "2010-10-10T12:00:00Z", - "updated_at": "2010-11-11T11:00:00Z", + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), "admin_pass": "", "user_id": user_id, "project_id": "", @@ -399,6 +400,78 @@ class ServersTest(test.TestCase): self.assertDictMatch(res_dict, expected_server) + def test_get_server_by_id_v1_1_xml(self): + image_bookmark = "http://localhost/images/10" + flavor_ref = "http://localhost/v1.1/flavors/1" + flavor_id = "1" + flavor_bookmark = "http://localhost/flavors/1" + server_href = "http://localhost/v1.1/servers/1" + server_bookmark = "http://localhost/servers/1" + + public_ip = '192.168.0.3' + private_ip = '172.19.0.1' + interfaces = [ + { + 'network': {'label': 'public'}, + 'fixed_ips': [ + {'address': public_ip}, + ], + }, + { + 'network': {'label': 'private'}, + 'fixed_ips': [ + {'address': private_ip}, + ], + }, + ] + new_return_server = return_server_with_attributes( + interfaces=interfaces) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + req = webob.Request.blank('/v1.1/servers/1') + req.headers['Accept'] = 'application/xml' + res = req.get_response(fakes.wsgi_app()) + actual = minidom.parseString(res.body.replace(' ', '')) + expected_uuid = FAKE_UUID + expected_updated = "2010-11-11T11:00:00Z" + expected_created = "2010-10-10T12:00:00Z" + expected = minidom.parseString(""" + <server id="1" + uuid="%(expected_uuid)s" + xmlns="http://docs.openstack.org/compute/api/v1.1" + xmlns:atom="http://www.w3.org/2005/Atom" + name="server1" + updated="%(expected_updated)s" + created="%(expected_created)s" + hostId="" + status="BUILD" + progress="0"> + <atom:link href="%(server_href)s" rel="self"/> + <atom:link href="%(server_bookmark)s" rel="bookmark"/> + <image id="10"> + <atom:link rel="bookmark" href="%(image_bookmark)s"/> + </image> + <flavor id="1"> + <atom:link rel="bookmark" href="%(flavor_bookmark)s"/> + </flavor> + <metadata> + <meta key="seq"> + 1 + </meta> + </metadata> + <addresses> + <network id="public"> + <ip version="4" addr="%(public_ip)s"/> + </network> + <network id="private"> + <ip version="4" addr="%(private_ip)s"/> + </network> + </addresses> + </server> + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_get_server_with_active_status_by_id_v1_1(self): image_bookmark = "http://localhost/images/10" flavor_ref = "http://localhost/v1.1/flavors/1" @@ -1048,8 +1121,8 @@ class ServersTest(test.TestCase): 'uuid': FAKE_UUID, 'instance_type': dict(inst_type), 'image_ref': image_ref, - 'created_at': '2010-10-10T12:00:00Z', - 'updated_at': '2010-11-11T11:00:00Z', + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), } def server_update(context, id, params): @@ -1099,6 +1172,7 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) self.assertEqual('server_test', server['name']) @@ -1106,7 +1180,6 @@ class ServersTest(test.TestCase): self.assertEqual(2, server['flavorId']) self.assertEqual(3, server['imageId']) self.assertEqual(FAKE_UUID, server['uuid']) - self.assertEqual(res.status_int, 200) def test_create_instance(self): self._test_create_instance_helper() @@ -1279,7 +1352,12 @@ class ServersTest(test.TestCase): 'hello': 'world', 'open': 'stack', }, - 'personality': {}, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "MQ==", + }, + ], }, } @@ -1293,11 +1371,11 @@ class ServersTest(test.TestCase): self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(16, len(server['adminPass'])) + self.assertEqual(1, server['id']) + self.assertEqual(0, server['progress']) self.assertEqual('server_test', server['name']) self.assertEqual(expected_flavor, server['flavor']) self.assertEqual(expected_image, server['image']) - self.assertEqual(res.status_int, 200) - #self.assertEqual(1, server['id']) def test_create_instance_v1_1_invalid_flavor_href(self): self._setup_for_create_instance() @@ -1351,7 +1429,7 @@ class ServersTest(test.TestCase): self._setup_for_create_instance() image_id = "2" - flavor_ref = 'http://localhost/flavors/3' + flavor_ref = 'http://localhost/v1.1/flavors/3' expected_flavor = { "id": "3", "links": [ @@ -1385,10 +1463,10 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(expected_flavor, server['flavor']) self.assertEqual(expected_image, server['image']) - self.assertEqual(res.status_int, 200) def test_create_instance_with_admin_pass_v1_0(self): self._setup_for_create_instance() @@ -1411,7 +1489,7 @@ class ServersTest(test.TestCase): self.assertNotEqual(res['server']['adminPass'], body['server']['adminPass']) - def test_create_instance_with_admin_pass_v1_1(self): + def test_create_instance_v1_1_admin_pass(self): self._setup_for_create_instance() image_href = 'http://localhost/v1.1/images/2' @@ -1419,8 +1497,8 @@ class ServersTest(test.TestCase): body = { 'server': { 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, + 'imageRef': 3, + 'flavorRef': 3, 'adminPass': 'testpass', }, } @@ -1430,19 +1508,18 @@ class ServersTest(test.TestCase): req.body = json.dumps(body) req.headers['content-type'] = "application/json" res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) server = json.loads(res.body)['server'] self.assertEqual(server['adminPass'], body['server']['adminPass']) - def test_create_instance_with_empty_admin_pass_v1_1(self): + def test_create_instance_v1_1_admin_pass_empty(self): self._setup_for_create_instance() - image_href = 'http://localhost/v1.1/images/2' - flavor_ref = 'http://localhost/v1.1/flavors/3' body = { 'server': { 'name': 'server_test', - 'imageRef': image_href, - 'flavorRef': flavor_ref, + 'imageRef': 3, + 'flavorRef': 3, 'adminPass': '', }, } @@ -2235,7 +2312,7 @@ class ServersTest(test.TestCase): self.assertEqual(res_dict['server']['status'], 'SHUTOFF') -class TestServerCreateRequestXMLDeserializer(unittest.TestCase): +class TestServerCreateRequestXMLDeserializerV10(unittest.TestCase): def setUp(self): self.deserializer = create_instance_helper.ServerXMLDeserializer() @@ -2249,6 +2326,8 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase): "name": "new-server-test", "imageId": "1", "flavorId": "1", + "metadata": {}, + "personality": [], }} self.assertEquals(request['body'], expected) @@ -2264,6 +2343,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase): "imageId": "1", "flavorId": "1", "metadata": {}, + "personality": [], }} self.assertEquals(request['body'], expected) @@ -2278,6 +2358,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase): "name": "new-server-test", "imageId": "1", "flavorId": "1", + "metadata": {}, "personality": [], }} self.assertEquals(request['body'], expected) @@ -2515,18 +2596,188 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""", request = self.deserializer.deserialize(serial_request, 'create') self.assertEqual(request['body'], expected) - def test_request_xmlser_with_flavor_image_href(self): + +class TestServerCreateRequestXMLDeserializerV11(unittest.TestCase): + + def setUp(self): + self.deserializer = create_instance_helper.ServerXMLDeserializer() + + def test_minimal_request(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_admin_pass(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2" + adminPass="1234"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "adminPass": "1234", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_image_link(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="http://localhost:8774/v1.1/images/2" + flavorRef="3"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "http://localhost:8774/v1.1/images/2", + "flavorRef": "3", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_flavor_link(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="http://localhost:8774/v1.1/flavors/3"/>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "http://localhost:8774/v1.1/flavors/3", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_empty_metadata_personality(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2"> + <metadata/> + <personality/> +</server>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_metadata_items(self): serial_request = """ - <server xmlns="http://docs.openstack.org/compute/api/v1.1" - name="new-server-test" - imageRef="http://localhost:8774/v1.1/images/1" - flavorRef="http://localhost:8774/v1.1/flavors/1"> - </server>""" +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2"> + <metadata> + <meta key="one">two</meta> + <meta key="open">snack</meta> + </metadata> +</server>""" request = self.deserializer.deserialize(serial_request, 'create') - self.assertEquals(request['body']["server"]["flavorRef"], - "http://localhost:8774/v1.1/flavors/1") - self.assertEquals(request['body']["server"]["imageRef"], - "http://localhost:8774/v1.1/images/1") + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {"one": "two", "open": "snack"}, + "personality": [], + }, + } + self.assertEquals(request['body'], expected) + + def test_multiple_personality_files(self): + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + name="new-server-test" + imageRef="1" + flavorRef="2"> + <personality> + <file path="/etc/banner.txt">MQ==</file> + <file path="/etc/hosts">Mg==</file> + </personality> +</server>""" + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "2", + "metadata": {}, + "personality": [ + {"path": "/etc/banner.txt", "contents": "MQ=="}, + {"path": "/etc/hosts", "contents": "Mg=="}, + ], + }, + } + self.assertEquals(request['body'], expected) + + def test_spec_request(self): + image_bookmark_link = "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54" + serial_request = """ +<server xmlns="http://docs.openstack.org/compute/api/v1.1" + imageRef="%s" + flavorRef="52415800-8b69-11e0-9b19-734f1195ff37" + name="new-server-test"> + <metadata> + <meta key="My Server Name">Apache1</meta> + </metadata> + <personality> + <file path="/etc/banner.txt">Mg==</file> + </personality> +</server>""" % (image_bookmark_link) + request = self.deserializer.deserialize(serial_request, 'create') + expected = { + "server": { + "name": "new-server-test", + "imageRef": "http://servers.api.openstack.org/1234/" + \ + "images/52415800-8b69-11e0-9b19-734f6f006e54", + "flavorRef": "52415800-8b69-11e0-9b19-734f1195ff37", + "metadata": {"My Server Name": "Apache1"}, + "personality": [ + { + "path": "/etc/banner.txt", + "contents": "Mg==", + }, + ], + }, + } + self.assertEquals(request['body'], expected) class TextAddressesXMLSerialization(test.TestCase): @@ -2899,10 +3150,12 @@ class ServersViewBuilderV11Test(test.TestCase): pass def _get_instance(self): + created_at = datetime.datetime(2010, 10, 10, 12, 0, 0) + updated_at = datetime.datetime(2010, 11, 11, 11, 0, 0) instance = { "id": 1, - "created_at": "2010-10-10T12:00:00Z", - "updated_at": "2010-11-11T11:00:00Z", + "created_at": created_at, + "updated_at": updated_at, "admin_pass": "", "user_id": "", "project_id": "", @@ -2950,7 +3203,7 @@ class ServersViewBuilderV11Test(test.TestCase): address_builder, flavor_builder, image_builder, - base_url + base_url, ) return view_builder @@ -3106,12 +3359,12 @@ class ServersViewBuilderV11Test(test.TestCase): }, "flavor": { "id": "1", - "links": [ - { - "rel": "bookmark", - "href": flavor_bookmark, - }, - ], + "links": [ + { + "rel": "bookmark", + "href": flavor_bookmark, + }, + ], }, "addresses": {}, "metadata": { @@ -3133,3 +3386,505 @@ class ServersViewBuilderV11Test(test.TestCase): output = self.view_builder.build(self.instance, True) self.assertDictMatch(output, expected_server) + + +class ServerXMLSerializationTest(test.TestCase): + + TIMESTAMP = "2010-10-11T10:30:22Z" + SERVER_HREF = 'http://localhost/v1.1/servers/123' + SERVER_BOOKMARK = 'http://localhost/servers/123' + IMAGE_BOOKMARK = 'http://localhost/images/5' + FLAVOR_BOOKMARK = 'http://localhost/flavors/1' + + def setUp(self): + self.maxDiff = None + test.TestCase.setUp(self) + + def test_show(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'show') + actual = minidom.parseString(output.replace(" ", "")) + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + expected = minidom.parseString(""" + <server id="1" + uuid="%(expected_uuid)s" + xmlns="http://docs.openstack.org/compute/api/v1.1" + xmlns:atom="http://www.w3.org/2005/Atom" + name="test_server" + updated="%(expected_now)s" + created="%(expected_now)s" + hostId="e4d909c290d0fb1ca068ffaddf22cbd0" + status="BUILD" + progress="0"> + <atom:link href="%(expected_server_href)s" rel="self"/> + <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> + <image id="5"> + <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> + </image> + <flavor id="1"> + <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> + </flavor> + <metadata> + <meta key="Open"> + Stack + </meta> + <meta key="Number"> + 1 + </meta> + </metadata> + <addresses> + <network id="network_one"> + <ip version="4" addr="67.23.10.138"/> + <ip version="6" addr="::babe:67.23.10.138"/> + </network> + <network id="network_two"> + <ip version="4" addr="67.23.10.139"/> + <ip version="6" addr="::babe:67.23.10.139"/> + </network> + </addresses> + </server> + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_create(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", + "adminPass": "test_password", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'create') + actual = minidom.parseString(output.replace(" ", "")) + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + expected = minidom.parseString(""" + <server id="1" + uuid="%(expected_uuid)s" + xmlns="http://docs.openstack.org/compute/api/v1.1" + xmlns:atom="http://www.w3.org/2005/Atom" + name="test_server" + updated="%(expected_now)s" + created="%(expected_now)s" + hostId="e4d909c290d0fb1ca068ffaddf22cbd0" + status="BUILD" + adminPass="test_password" + progress="0"> + <atom:link href="%(expected_server_href)s" rel="self"/> + <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> + <image id="5"> + <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> + </image> + <flavor id="1"> + <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> + </flavor> + <metadata> + <meta key="Open"> + Stack + </meta> + <meta key="Number"> + 1 + </meta> + </metadata> + <addresses> + <network id="network_one"> + <ip version="4" addr="67.23.10.138"/> + <ip version="6" addr="::babe:67.23.10.138"/> + </network> + <network id="network_two"> + <ip version="4" addr="67.23.10.139"/> + <ip version="6" addr="::babe:67.23.10.139"/> + </network> + </addresses> + </server> + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_index(self): + serializer = servers.ServerXMLSerializer() + + expected_server_href = 'http://localhost/v1.1/servers/1' + expected_server_bookmark = 'http://localhost/servers/1' + expected_server_href_2 = 'http://localhost/v1.1/servers/2' + expected_server_bookmark_2 = 'http://localhost/servers/2' + fixture = {"servers": [ + { + "id": 1, + "name": "test_server", + 'links': [ + { + 'href': expected_server_href, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark, + 'rel': 'bookmark', + }, + ], + }, + { + "id": 2, + "name": "test_server_2", + 'links': [ + { + 'href': expected_server_href_2, + 'rel': 'self', + }, + { + 'href': expected_server_bookmark_2, + 'rel': 'bookmark', + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'index') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <servers xmlns="http://docs.openstack.org/compute/api/v1.1" + xmlns:atom="http://www.w3.org/2005/Atom"> + <server id="1" name="test_server"> + <atom:link href="%(expected_server_href)s" rel="self"/> + <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> + </server> + <server id="2" name="test_server_2"> + <atom:link href="%(expected_server_href_2)s" rel="self"/> + <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/> + </server> + </servers> + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) + + def test_detail(self): + serializer = servers.ServerXMLSerializer() + + expected_server_href = 'http://localhost/v1.1/servers/1' + expected_server_bookmark = 'http://localhost/servers/1' + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + + expected_server_href_2 = 'http://localhost/v1.1/servers/2' + expected_server_bookmark_2 = 'http://localhost/servers/2' + fixture = {"servers": [ + { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "1", + }, + "links": [ + { + "href": expected_server_href, + "rel": "self", + }, + { + "href": expected_server_bookmark, + "rel": "bookmark", + }, + ], + }, + { + "id": 2, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 100, + "name": "test_server_2", + "status": "ACTIVE", + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": expected_image_bookmark, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": expected_flavor_bookmark, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + }, + "metadata": { + "Number": "2", + }, + "links": [ + { + "href": expected_server_href_2, + "rel": "self", + }, + { + "href": expected_server_bookmark_2, + "rel": "bookmark", + }, + ], + }, + ]} + + output = serializer.serialize(fixture, 'detail') + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + <servers xmlns="http://docs.openstack.org/compute/api/v1.1" + xmlns:atom="http://www.w3.org/2005/Atom"> + <server id="1" + uuid="%(expected_uuid)s" + name="test_server" + updated="%(expected_now)s" + created="%(expected_now)s" + hostId="e4d909c290d0fb1ca068ffaddf22cbd0" + status="BUILD" + progress="0"> + <atom:link href="%(expected_server_href)s" rel="self"/> + <atom:link href="%(expected_server_bookmark)s" rel="bookmark"/> + <image id="5"> + <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> + </image> + <flavor id="1"> + <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> + </flavor> + <metadata> + <meta key="Number"> + 1 + </meta> + </metadata> + <addresses> + <network id="network_one"> + <ip version="4" addr="67.23.10.138"/> + <ip version="6" addr="::babe:67.23.10.138"/> + </network> + </addresses> + </server> + <server id="2" + uuid="%(expected_uuid)s" + name="test_server_2" + updated="%(expected_now)s" + created="%(expected_now)s" + hostId="e4d909c290d0fb1ca068ffaddf22cbd0" + status="ACTIVE" + progress="100"> + <atom:link href="%(expected_server_href_2)s" rel="self"/> + <atom:link href="%(expected_server_bookmark_2)s" rel="bookmark"/> + <image id="5"> + <atom:link rel="bookmark" href="%(expected_image_bookmark)s"/> + </image> + <flavor id="1"> + <atom:link rel="bookmark" href="%(expected_flavor_bookmark)s"/> + </flavor> + <metadata> + <meta key="Number"> + 2 + </meta> + </metadata> + <addresses> + <network id="network_one"> + <ip version="4" addr="67.23.10.138"/> + <ip version="6" addr="::babe:67.23.10.138"/> + </network> + </addresses> + </server> + </servers> + """.replace(" ", "") % (locals())) + + self.assertEqual(expected.toxml(), actual.toxml()) diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 4e8e85c7b..67b3c485a 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -305,5 +305,6 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # Cleanup self._delete_server(server_id) + if __name__ == "__main__": unittest.main() diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 0ede4f469..d82666689 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -420,7 +420,7 @@ class ComputeTestCase(test.TestCase): def fake(*args, **kwargs): pass - self.stubs.Set(self.compute.driver, 'finish_resize', fake) + self.stubs.Set(self.compute.driver, 'finish_migration', fake) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) context = self.context.elevated() instance_id = self._create_instance() @@ -531,8 +531,8 @@ class ComputeTestCase(test.TestCase): def fake(*args, **kwargs): pass - self.stubs.Set(self.compute.driver, 'finish_resize', fake) - self.stubs.Set(self.compute.driver, 'revert_resize', fake) + self.stubs.Set(self.compute.driver, 'finish_migration', fake) + self.stubs.Set(self.compute.driver, 'revert_migration', fake) self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake) self.compute.run_instance(self.context, instance_id) diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 107fd03e3..5560b489b 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -66,7 +66,7 @@ class DbApiTestCase(test.TestCase): result = db.fixed_ip_get_all(self.context) values = {'instance_type_id': FLAGS.default_instance_type, 'image_ref': FLAGS.vpn_image_id, - 'project_id': self.project.id + 'project_id': self.project.id, } instance = db.instance_create(self.context, values) result = db.instance_get_project_vpn(self.context, self.project.id) @@ -76,7 +76,7 @@ class DbApiTestCase(test.TestCase): result = db.fixed_ip_get_all(self.context) values = {'instance_type_id': FLAGS.default_instance_type, 'image_ref': FLAGS.vpn_image_id, - 'project_id': self.project.id + 'project_id': self.project.id, } instance = db.instance_create(self.context, values) _setup_networking(instance.id) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d8462eb25..3cad9d1ed 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -239,7 +239,7 @@ class LibvirtConnTestCase(test.TestCase): 'mac_address': 'fake', 'ip_address': 'fake', 'dhcp_server': 'fake', - 'extra_params': 'fake' + 'extra_params': 'fake', } # Creating mocks diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index 7313508a6..909e65dd9 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -20,6 +20,7 @@ Test suite for VMWareAPI. """ from nova import db +from nova import context from nova import flags from nova import test from nova import utils @@ -40,6 +41,7 @@ class VMWareAPIVMTestCase(test.TestCase): def setUp(self): super(VMWareAPIVMTestCase, self).setUp() + self.context = context.RequestContext('fake', 'fake', False) self.flags(vmwareapi_host_ip='test_url', vmwareapi_host_username='test_username', vmwareapi_host_password='test_pass') @@ -97,7 +99,7 @@ class VMWareAPIVMTestCase(test.TestCase): """Create and spawn the VM.""" self._create_instance_in_the_db() self.type_data = db.instance_type_get_by_name(None, 'm1.large') - self.conn.spawn(self.instance, self.network_info) + self.conn.spawn(self.context, self.instance, self.network_info) self._check_vm_record() def _check_vm_record(self): @@ -159,14 +161,14 @@ class VMWareAPIVMTestCase(test.TestCase): self._create_vm() info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) - self.conn.snapshot(self.instance, "Test-Snapshot") + self.conn.snapshot(self.context, self.instance, "Test-Snapshot") info = self.conn.get_info(1) self._check_vm_info(info, power_state.RUNNING) def test_snapshot_non_existent(self): self._create_instance_in_the_db() - self.assertRaises(Exception, self.conn.snapshot, self.instance, - "Test-Snapshot") + self.assertRaises(Exception, self.conn.snapshot, self.context, + self.instance, "Test-Snapshot") def test_reboot(self): self._create_vm() diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index bb1b88421..1a8ddadc4 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -786,8 +786,48 @@ class XenAPIMigrateInstance(test.TestCase): conn = xenapi_conn.get_connection(False) conn.migrate_disk_and_power_off(instance, '127.0.0.1') - def test_finish_resize(self): + def test_finish_migrate(self): instance = db.instance_create(self.context, self.values) + self.called = False + + def fake_vdi_resize(*args, **kwargs): + self.called = True + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + stubs.stubout_loopingcall_start(self.stubs) + conn = xenapi_conn.get_connection(False) + network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False}, + {'broadcast': '192.168.0.255', + 'dns': ['192.168.0.1'], + 'gateway': '192.168.0.1', + 'gateway6': 'dead:beef::1', + 'ip6s': [{'enabled': '1', + 'ip': 'dead:beef::dcad:beff:feef:0', + 'netmask': '64'}], + 'ips': [{'enabled': '1', + 'ip': '192.168.0.100', + 'netmask': '255.255.255.0'}], + 'label': 'fake', + 'mac': 'DE:AD:BE:EF:00:00', + 'rxtx_cap': 3})] + conn.finish_migration(self.context, instance, + dict(base_copy='hurr', cow='durr'), + network_info, resize_instance=True) + self.assertEqual(self.called, True) + + def test_finish_migrate_no_local_storage(self): + tiny_type_id = \ + instance_types.get_instance_type_by_name('m1.tiny')['id'] + self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0}) + instance = db.instance_create(self.context, self.values) + + def fake_vdi_resize(*args, **kwargs): + raise Exception("This shouldn't be called") + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) stubs.stubout_loopingcall_start(self.stubs) conn = xenapi_conn.get_connection(False) @@ -805,9 +845,40 @@ class XenAPIMigrateInstance(test.TestCase): 'label': 'fake', 'mac': 'DE:AD:BE:EF:00:00', 'rxtx_cap': 3})] - conn.finish_resize(self.context, instance, - dict(base_copy='hurr', cow='durr'), - network_info) + conn.finish_migration(self.context, instance, + dict(base_copy='hurr', cow='durr'), + network_info, resize_instance=True) + + def test_finish_migrate_no_resize_vdi(self): + instance = db.instance_create(self.context, self.values) + + def fake_vdi_resize(*args, **kwargs): + raise Exception("This shouldn't be called") + + self.stubs.Set(stubs.FakeSessionForMigrationTests, + "VDI_resize_online", fake_vdi_resize) + stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests) + stubs.stubout_loopingcall_start(self.stubs) + conn = xenapi_conn.get_connection(False) + network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False}, + {'broadcast': '192.168.0.255', + 'dns': ['192.168.0.1'], + 'gateway': '192.168.0.1', + 'gateway6': 'dead:beef::1', + 'ip6s': [{'enabled': '1', + 'ip': 'dead:beef::dcad:beff:feef:0', + 'netmask': '64'}], + 'ips': [{'enabled': '1', + 'ip': '192.168.0.100', + 'netmask': '255.255.255.0'}], + 'label': 'fake', + 'mac': 'DE:AD:BE:EF:00:00', + 'rxtx_cap': 3})] + + # Resize instance would be determined by the compute call + conn.finish_migration(self.context, instance, + dict(base_copy='hurr', cow='durr'), + network_info, resize_instance=False) class XenAPIDetermineDiskImageTestCase(test.TestCase): diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 8e17d7540..4be847dc6 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -65,7 +65,7 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def spawn(self, context, instance, network_info, + def spawn(self, cxt, instance, network_info, block_device_mapping=None): """Launch a VM for the specified instance""" raise NotImplementedError() @@ -131,15 +131,16 @@ class ComputeDriver(object): # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() - def snapshot(self, context, instance, image_id): + def snapshot(self, cxt, instance, image_id): """Create snapshot from a running VM instance.""" raise NotImplementedError() - def finish_resize(self, context, instance, disk_info): + def finish_migration(self, cxt, instance, disk_info, network_info, + resize_instance): """Completes a resize, turning on the migrated instance""" raise NotImplementedError() - def revert_resize(self, instance): + def revert_migration(self, instance): """Reverts a resize, powering back on the instance""" # TODO(Vek): Need to pass context in for access to auth_token raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index f67c2309e..23aa603ef 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -129,7 +129,7 @@ class FakeConnection(driver.ComputeDriver): info_list.append(self._map_to_instance_info(instance)) return info_list - def spawn(self, context, instance, network_info, + def spawn(self, cxt, instance, network_info, block_device_mapping=None): """ Create a new instance/VM/domain on the virtualization platform. @@ -154,7 +154,7 @@ class FakeConnection(driver.ComputeDriver): fake_instance = FakeInstance(name, state) self.instances[name] = fake_instance - def snapshot(self, context, instance, name): + def snapshot(self, cxt, instance, name): """ Snapshots the specified instance. diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 8236b5a45..075594808 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -139,7 +139,7 @@ class HyperVConnection(driver.ComputeDriver): return instance_infos - def spawn(self, context, instance, network_info, + def spawn(self, cxt, instance, network_info, block_device_mapping=None): """ Create a new VM and start it.""" vm = self._lookup(instance.name) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 471811b06..1e291000c 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -396,7 +396,7 @@ class LibvirtConnection(driver.ComputeDriver): virt_dom.detachDevice(xml) @exception.wrap_exception() - def snapshot(self, context, instance, image_href): + def snapshot(self, cxt, instance, image_href): """Create snapshot from a running VM instance. This command only works with qemu 0.14+, the qemu_img flag is @@ -592,7 +592,7 @@ class LibvirtConnection(driver.ComputeDriver): # NOTE(ilyaalekseyev): Implementation like in multinics # for xenapi(tr3buchet) @exception.wrap_exception() - def spawn(self, context, instance, network_info, + def spawn(self, cxt, instance, network_info, block_device_mapping=None): xml = self.to_xml(instance, False, network_info=network_info, block_device_mapping=block_device_mapping) diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 777c15736..ad354bc3a 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -89,7 +89,7 @@ class VMWareVMOps(object): LOG.debug(_("Got total of %s instances") % str(len(lst_vm_names)))
return lst_vm_names
- def spawn(self, context, instance, network_info):
+ def spawn(self, cxt, instance, network_info):
"""
Creates a VM instance.
@@ -329,7 +329,7 @@ class VMWareVMOps(object): LOG.debug(_("Powered on the VM instance %s") % instance.name)
_power_on_vm()
- def snapshot(self, context, instance, snapshot_name):
+ def snapshot(self, cxt, instance, snapshot_name):
"""
Create snapshot from a running VM instance.
Steps followed are:
diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 3d209fa99..a26a02290 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -124,14 +124,14 @@ class VMWareESXConnection(driver.ComputeDriver): """List VM instances."""
return self._vmops.list_instances()
- def spawn(self, context, instance, network_info,
+ def spawn(self, cxt, instance, network_info,
block_device_mapping=None):
"""Create VM instance."""
- self._vmops.spawn(context, instance, network_info)
+ self._vmops.spawn(cxt, instance, network_info)
- def snapshot(self, context, instance, name):
+ def snapshot(self, cxt, instance, name):
"""Create snapshot from a running VM instance."""
- self._vmops.snapshot(context, instance, name)
+ self._vmops.snapshot(cxt, instance, name)
def reboot(self, instance, network_info):
"""Reboot VM instance."""
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index b1b0ddd0c..14465d8b4 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -359,7 +359,7 @@ class VMHelper(HelperBase): return os.path.join(FLAGS.xenapi_sr_base_path, sr_uuid) @classmethod - def upload_image(cls, context, session, instance, vdi_uuids, image_id): + def upload_image(cls, ctx, session, instance, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and push them into Glance using the specified human-friendly name. """ @@ -378,14 +378,14 @@ class VMHelper(HelperBase): 'glance_port': glance_port, 'sr_path': cls.get_sr_path(session), 'os_type': os_type, - 'auth_token': getattr(context, 'auth_token', None)} + 'auth_token': getattr(ctx, 'auth_token', None)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'upload_vhd', kwargs) session.wait_for_task(task, instance.id) @classmethod - def fetch_image(cls, context, session, instance_id, image, user, project, + def fetch_image(cls, ctx, session, instance_id, image, user, project, image_type): """ image_type is interpreted as an ImageType instance @@ -400,7 +400,7 @@ class VMHelper(HelperBase): access = AuthManager().get_access_key(user, project) if FLAGS.xenapi_image_service == 'glance': - return cls._fetch_image_glance(context, session, instance_id, + return cls._fetch_image_glance(ctx, session, instance_id, image, access, image_type) else: return cls._fetch_image_objectstore(session, instance_id, image, @@ -408,7 +408,7 @@ class VMHelper(HelperBase): image_type) @classmethod - def _fetch_image_glance_vhd(cls, context, session, instance_id, image, + def _fetch_image_glance_vhd(cls, ctx, session, instance_id, image, access, image_type): """Tell glance to download an image and put the VHDs into the SR @@ -431,7 +431,7 @@ class VMHelper(HelperBase): 'glance_port': glance_port, 'uuid_stack': uuid_stack, 'sr_path': cls.get_sr_path(session), - 'auth_token': getattr(context, 'auth_token', None)} + 'auth_token': getattr(ctx, 'auth_token', None)} kwargs = {'params': pickle.dumps(params)} task = session.async_call_plugin('glance', 'download_vhd', kwargs) @@ -457,7 +457,7 @@ class VMHelper(HelperBase): return vdis @classmethod - def _fetch_image_glance_disk(cls, context, session, instance_id, image, + def _fetch_image_glance_disk(cls, ctx, session, instance_id, image, access, image_type): """Fetch the image from Glance @@ -477,7 +477,7 @@ class VMHelper(HelperBase): sr_ref = safe_find_sr(session) glance_client, image_id = nova.image.get_glance_client(image) - glance_client.set_auth_token(getattr(context, 'auth_token', None)) + glance_client.set_auth_token(getattr(ctx, 'auth_token', None)) meta, image_file = glance_client.get_image(image_id) virtual_size = int(meta['size']) vdi_size = virtual_size @@ -592,7 +592,7 @@ class VMHelper(HelperBase): return image_type @classmethod - def _fetch_image_glance(cls, context, session, instance_id, image, access, + def _fetch_image_glance(cls, ctx, session, instance_id, image, access, image_type): """Fetch image from glance based on image type. @@ -600,10 +600,10 @@ class VMHelper(HelperBase): A list of dictionaries that describe VDIs, otherwise """ if image_type == ImageType.DISK_VHD: - return cls._fetch_image_glance_vhd(context, + return cls._fetch_image_glance_vhd(ctx, session, instance_id, image, access, image_type) else: - return cls._fetch_image_glance_disk(context, + return cls._fetch_image_glance_disk(ctx, session, instance_id, image, access, image_type) @classmethod diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index f7a800d58..e01ffa046 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -110,17 +110,19 @@ class VMOps(object): instance_infos.append(instance_info) return instance_infos - def revert_resize(self, instance): + def revert_migration(self, instance): vm_ref = VMHelper.lookup(self._session, instance.name) self._start(instance, vm_ref) - def finish_resize(self, context, instance, disk_info, network_info): + def finish_migration(self, ctx, instance, disk_info, network_info, + resize_instance): vdi_uuid = self.link_disks(instance, disk_info['base_copy'], disk_info['cow']) - vm_ref = self._create_vm(context, instance, + vm_ref = self._create_vm(ctx, instance, [dict(vdi_type='os', vdi_uuid=vdi_uuid)], network_info) - self.resize_instance(instance, vdi_uuid) + if resize_instance: + self.resize_instance(instance, vdi_uuid) self._spawn(instance, vm_ref) def _start(self, instance, vm_ref=None): @@ -133,20 +135,20 @@ class VMOps(object): LOG.debug(_("Starting instance %s"), instance.name) self._session.call_xenapi('VM.start', vm_ref, False, False) - def _create_disks(self, context, instance): + def _create_disks(self, ctx, instance): user = AuthManager().get_user(instance.user_id) project = AuthManager().get_project(instance.project_id) disk_image_type = VMHelper.determine_disk_image_type(instance) - vdis = VMHelper.fetch_image(context, self._session, + vdis = VMHelper.fetch_image(ctx, self._session, instance.id, instance.image_ref, user, project, disk_image_type) return vdis - def spawn(self, context, instance, network_info): + def spawn(self, ctx, instance, network_info): vdis = None try: - vdis = self._create_disks(context, instance) - vm_ref = self._create_vm(context, instance, vdis, network_info) + vdis = self._create_disks(ctx, instance) + vm_ref = self._create_vm(ctx, instance, vdis, network_info) self._spawn(instance, vm_ref) except (self.XenAPI.Failure, OSError, IOError) as spawn_error: LOG.exception(_("instance %s: Failed to spawn"), @@ -160,7 +162,7 @@ class VMOps(object): """Spawn a rescue instance.""" self.spawn(instance) - def _create_vm(self, context, instance, vdis, network_info): + def _create_vm(self, ctx, instance, vdis, network_info): """Create VM instance.""" instance_name = instance.name vm_ref = VMHelper.lookup(self._session, instance_name) @@ -184,11 +186,11 @@ class VMOps(object): ramdisk = None try: if instance.kernel_id: - kernel = VMHelper.fetch_image(context, self._session, + kernel = VMHelper.fetch_image(ctx, self._session, instance.id, instance.kernel_id, user, project, ImageType.KERNEL)[0] if instance.ramdisk_id: - ramdisk = VMHelper.fetch_image(context, self._session, + ramdisk = VMHelper.fetch_image(ctx, self._session, instance.id, instance.ramdisk_id, user, project, ImageType.RAMDISK)[0] # Create the VM ref and attach the first disk @@ -440,10 +442,10 @@ class VMOps(object): vm, "start") - def snapshot(self, context, instance, image_id): + def snapshot(self, ctx, instance, image_id): """Create snapshot from a running VM instance. - :param context: request context + :param ctx: request context :param instance: instance to be snapshotted :param image_id: id of image to upload to @@ -468,7 +470,7 @@ class VMOps(object): try: template_vm_ref, template_vdi_uuids = self._get_snapshot(instance) # call plugin to ship snapshot off to glance - VMHelper.upload_image(context, + VMHelper.upload_image(ctx, self._session, instance, template_vdi_uuids, image_id) finally: if template_vm_ref: @@ -569,18 +571,22 @@ class VMOps(object): return new_cow_uuid def resize_instance(self, instance, vdi_uuid): - """Resize a running instance by changing it's RAM and disk size.""" + """Resize a running instance by changing its RAM and disk size.""" #TODO(mdietz): this will need to be adjusted for swap later #The new disk size must be in bytes - new_disk_size = str(instance.local_gb * 1024 * 1024 * 1024) - instance_name = instance.name - instance_local_gb = instance.local_gb - LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance %(instance_name)s." - " Expanding to %(instance_local_gb)d GB") % locals()) - vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) - self._session.call_xenapi('VDI.resize_online', vdi_ref, new_disk_size) - LOG.debug(_("Resize instance %s complete") % (instance.name)) + new_disk_size = instance.local_gb * 1024 * 1024 * 1024 + if new_disk_size > 0: + instance_name = instance.name + instance_local_gb = instance.local_gb + LOG.debug(_("Resizing VDI %(vdi_uuid)s for instance" + "%(instance_name)s. Expanding to %(instance_local_gb)d" + " GB") % locals()) + vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) + # for an instance with no local storage + self._session.call_xenapi('VDI.resize_online', vdi_ref, + str(new_disk_size)) + LOG.debug(_("Resize instance %s complete") % (instance.name)) def reboot(self, instance): """Reboot VM instance.""" diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f191d13fa..9315c2f4a 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -187,22 +187,24 @@ class XenAPIConnection(driver.ComputeDriver): def list_instances_detail(self): return self._vmops.list_instances_detail() - def spawn(self, context, instance, network_info, + def spawn(self, cxt, instance, network_info, block_device_mapping=None): """Create VM instance""" - self._vmops.spawn(context, instance, network_info) + self._vmops.spawn(cxt, instance, network_info) - def revert_resize(self, instance): + def revert_migration(self, instance): """Reverts a resize, powering back on the instance""" self._vmops.revert_resize(instance) - def finish_resize(self, context, instance, disk_info, network_info): + def finish_migration(self, cxt, instance, disk_info, network_info, + resize_instance=False): """Completes a resize, turning on the migrated instance""" - self._vmops.finish_resize(context, instance, disk_info, network_info) + self._vmops.finish_migration(cxt, instance, disk_info, + network_info, resize_instance) - def snapshot(self, context, instance, image_id): + def snapshot(self, cxt, instance, image_id): """ Create snapshot from a running VM instance """ - self._vmops.snapshot(context, instance, image_id) + self._vmops.snapshot(cxt, instance, image_id) def reboot(self, instance, network_info): """Reboot VM instance""" |
