summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Waldon <brian.waldon@rackspace.com>2011-07-12 14:35:09 -0400
committerBrian Waldon <brian.waldon@rackspace.com>2011-07-12 14:35:09 -0400
commit54be28647ac3ad401006bca3069b1dfc1a65d093 (patch)
treee03f42d2110d045c2d4b57fb0aa282181fa3a471
parent76fbcb9bfd88a56c5a3419bf227cca28334f6844 (diff)
downloadnova-54be28647ac3ad401006bca3069b1dfc1a65d093.tar.gz
nova-54be28647ac3ad401006bca3069b1dfc1a65d093.tar.xz
nova-54be28647ac3ad401006bca3069b1dfc1a65d093.zip
server create deserialization functional and tested
-rw-r--r--nova/api/openstack/create_instance_helper.py81
-rw-r--r--nova/api/openstack/servers.py63
-rw-r--r--nova/api/openstack/wsgi.py21
-rw-r--r--nova/tests/api/openstack/test_servers.py414
4 files changed, 508 insertions, 71 deletions
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 2654e3c40..eea973a56 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -180,7 +180,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())
@@ -295,9 +295,15 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
"""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"]:
+ for attr in ["name", "imageId", "flavorId"]:
if server_node.getAttribute(attr):
server[attr] = server_node.getAttribute(attr)
+ image = self._extract_image(server_node)
+ if image is not None:
+ server["image"] = image
+ flavor = self._extract_flavor(server_node)
+ if flavor is not None:
+ server["flavor"] = flavor
metadata = self._extract_metadata(server_node)
if metadata is not None:
server["metadata"] = metadata
@@ -306,6 +312,56 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
server["personality"] = personality
return server
+ def _extract_image(self, server_node):
+ """Retrieve an image entity from the server node"""
+ image_node = self._find_first_child_named(server_node, "image")
+ if image_node is None:
+ return None
+
+ image = {}
+ image_id = image_node.getAttribute('id')
+ if image_id:
+ image['id'] = image_id
+
+ image_links = self._extract_links_from_node(image_node)
+ if len(image_links) > 0:
+ image['links'] = image_links
+
+ return image
+
+ def _extract_flavor(self, server_node):
+ """Retrieve a flavor entity from the server node"""
+ flavor_node = self._find_first_child_named(server_node, "flavor")
+ if flavor_node is None:
+ return None
+
+ flavor = {}
+ flavor_id = flavor_node.getAttribute('id')
+ if flavor_id:
+ flavor['id'] = flavor_id
+
+ flavor_links = self._extract_links_from_node(flavor_node)
+ if len(flavor_links) > 0:
+ flavor['links'] = flavor_links
+
+ return flavor
+
+ def _extract_links_from_node(self, parent_node):
+ """Retrieve link entities from a links container provided node"""
+ links = []
+
+ for link_node in self._find_children_named(parent_node, 'atom:link'):
+ link = {}
+ link_rel = link_node.getAttribute('rel')
+ if link_rel is not None:
+ link['rel'] = link_rel
+ link_href = link_node.getAttribute('href')
+ if link_href is not None:
+ link['href'] = link_href
+ links.append(link)
+
+ return links
+
def _extract_metadata(self, server_node):
"""Marshal the metadata attribute of a parsed request"""
metadata_node = self._find_first_child_named(server_node, "metadata")
@@ -331,24 +387,3 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
item["contents"] = self._extract_text(file_node)
personality.append(item)
return personality
-
- def _find_first_child_named(self, parent, name):
- """Search a nodes children for the first child with a given name"""
- for node in parent.childNodes:
- if node.nodeName == name:
- return node
- return None
-
- def _find_children_named(self, parent, name):
- """Return all of a nodes children who have the given name"""
- for node in parent.childNodes:
- if node.nodeName == name:
- yield node
-
- def _extract_text(self, node):
- """Get the text field contained by the given node"""
- if len(node.childNodes) == 1:
- child = node.childNodes[0]
- if child.nodeType == child.TEXT_NODE:
- return child.nodeValue
- return ""
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 12af44a8d..f239044ff 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -492,11 +492,68 @@ class ControllerV11(Controller):
return faults.Fault(exc.HTTPNotFound())
def _image_ref_from_req_data(self, data):
- return data['server']['imageRef']
+ try:
+ image = data['server']['image']
+ except (AttributeError, KeyError):
+ msg = _("Missing image entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ links = image.get('links', [])
+ except AttributeError:
+ msg = _("Malformed image entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ image_ref = None
+ for link in links:
+ try:
+ if link.get('rel') == 'bookmark':
+ image_ref = link.get('href')
+ break
+ except AttributeError:
+ msg = _("Malformed image link")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ if image_ref is None:
+ try:
+ image_ref = image['id']
+ except KeyError:
+ msg = _("Missing id attribute on image entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ return image_ref
def _flavor_id_from_req_data(self, data):
- href = data['server']['flavorRef']
- return common.get_id_from_href(href)
+ try:
+ flavor = data['server']['flavor']
+ except (AttributeError, KeyError):
+ msg = _("Missing flavor entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ links = flavor.get('links', [])
+ except AttributeError:
+ msg = _("Malformed flavor entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ flavor_ref = None
+ for link in links:
+ try:
+ if link.get('rel') == 'bookmark':
+ flavor_ref = link.get('href')
+ break
+ except AttributeError:
+ msg = _("Malformed flavor link")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ if flavor_ref is None:
+ try:
+ return flavor['id']
+ except (KeyError, AttributeError):
+ msg = _("Missing id attribute in flavor entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+ else:
+ return common.get_id_from_href(flavor_ref)
def _get_view_builder(self, req):
base_url = req.application_url
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 8eff9e441..0ece2cff0 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -134,6 +134,27 @@ class XMLDeserializer(TextDeserializer):
listnames)
return result
+ def _find_first_child_named(self, parent, name):
+ """Search a nodes children for the first child with a given name"""
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ return node
+ return None
+
+ def _find_children_named(self, parent, name):
+ """Return all of a nodes children who have the given name"""
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ yield node
+
+ def _extract_text(self, node):
+ """Get the text field contained by the given node"""
+ if len(node.childNodes) == 1:
+ child = node.childNodes[0]
+ if child.nodeType == child.TEXT_NODE:
+ return child.nodeValue
+ return ""
+
def default(self, datastring):
return {'body': self._from_xml(datastring)}
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 775f66ad0..cb7e03934 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -612,7 +612,7 @@ class ServersTest(test.TestCase):
"_get_kernel_ramdisk_from_image", kernel_ramdisk_mapping)
self.stubs.Set(nova.compute.api.API, "_find_host", find_host)
- def _test_create_instance_helper(self):
+ def test_create_instance(self):
self._setup_for_create_instance()
body = dict(server=dict(
@@ -626,6 +626,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'])
@@ -633,10 +634,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()
def test_create_instance_has_uuid(self):
"""Tests at the db-layer instead of API layer since that's where the
@@ -692,7 +689,27 @@ class ServersTest(test.TestCase):
def test_create_instance_no_key_pair(self):
fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False)
- self._test_create_instance_helper()
+ self._setup_for_create_instance()
+
+ body = dict(server=dict(
+ name='server_test', imageId=3, flavorId=2,
+ metadata={'hello': 'world', 'open': 'stack'},
+ personality={}))
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+
+ server = json.loads(res.body)['server']
+ self.assertEqual(16, len(server['adminPass']))
+ self.assertEqual('server_test', server['name'])
+ self.assertEqual(1, server['id'])
+ 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_no_name(self):
self._setup_for_create_instance()
@@ -765,18 +782,34 @@ class ServersTest(test.TestCase):
def test_create_instance_v1_1(self):
self._setup_for_create_instance()
- image_href = 'http://localhost/v1.1/images/2'
- flavor_ref = 'http://localhost/v1.1/flavors/3'
+ image_href = 'http://localhost/v1.1/images/3'
+ flavor_href = 'http://localhost/v1.1/flavors/2'
+
body = {
'server': {
'name': 'server_test',
- 'imageRef': image_href,
- 'flavorRef': flavor_ref,
+ 'image': {
+ 'id': 3,
+ 'links': [
+ {'rel': 'bookmark', 'href': image_href},
+ ],
+ },
+ 'flavor': {
+ 'id': 2,
+ 'links': [
+ {'rel': 'bookmark', 'href': flavor_href},
+ ],
+ },
'metadata': {
'hello': 'world',
'open': 'stack',
},
- 'personality': {},
+ 'personality': [
+ {
+ "path" : "/etc/banner.txt",
+ "contents" : "MQ==",
+ },
+ ],
},
}
@@ -787,40 +820,102 @@ 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'])
self.assertEqual(1, server['id'])
- self.assertEqual(flavor_ref, server['flavorRef'])
+ self.assertEqual(flavor_href, server['flavorRef'])
self.assertEqual(image_href, server['imageRef'])
- self.assertEqual(res.status_int, 200)
+ self.assertFalse('personality' in server)
- def test_create_instance_v1_1_bad_href(self):
+ def test_create_instance_v1_1_image_id(self):
self._setup_for_create_instance()
- image_href = 'http://localhost/v1.1/images/asdf'
+ image_id = 2
flavor_ref = 'http://localhost/v1.1/flavors/3'
- body = dict(server=dict(
- name='server_test', imageRef=image_href, flavorRef=flavor_ref,
- metadata={'hello': 'world', 'open': 'stack'},
- personality={}))
+ body = {
+ 'server': {
+ 'name': 'server_test',
+ 'image': {'id': image_id},
+ 'flavor': {'id': 3},
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/servers')
+ req.method = 'POST'
+ 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(flavor_ref, server['flavorRef'])
+
+ def test_create_instance_v1_1_image_link(self):
+ self._setup_for_create_instance()
+
+ image_ref = 'http://localhost/v1.1/image/3'
+ body = {
+ 'server': {
+ 'name': 'server_test',
+ 'image': {
+ 'links':[
+ {'rel': 'self', 'href': 'http://google.com'},
+ {'rel': 'bookmark', 'href': image_ref},
+ ],
+ },
+ 'flavor': {'id': 3},
+ },
+ }
+
req = webob.Request.blank('/v1.1/servers')
req.method = 'POST'
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(image_ref, server['imageRef'])
+
+ def test_create_instance_v1_1_no_valid_image(self):
+ self._setup_for_create_instance()
+
+ body = {
+ 'server': {
+ 'name': 'server_test',
+ 'image': {},
+ 'flavor': {'id': 3},
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+
self.assertEqual(res.status_int, 400)
- def test_create_instance_v1_1_local_href(self):
+ def test_create_instance_v1_1_flavor_link(self):
self._setup_for_create_instance()
- image_id = 2
flavor_ref = 'http://localhost/v1.1/flavors/3'
+
body = {
'server': {
'name': 'server_test',
- 'imageRef': image_id,
- 'flavorRef': flavor_ref,
+ 'image': {'id': 3},
+ 'flavor': {
+ 'id': 2,
+ 'links': [
+ {'rel': 'bookmark', 'href': flavor_ref},
+ ],
+ },
},
}
@@ -831,11 +926,55 @@ 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(1, server['id'])
self.assertEqual(flavor_ref, server['flavorRef'])
- self.assertEqual(image_id, server['imageRef'])
+
+ def test_create_instance_v1_1_flavor_id(self):
+ self._setup_for_create_instance()
+
+ flavor_ref = 'http://localhost/v1.1/flavors/2'
+
+ body = {
+ 'server': {
+ 'name': 'server_test',
+ 'image': {'id': 3},
+ 'flavor': {'id': 2},
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/servers')
+ req.method = 'POST'
+ 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(flavor_ref, server['flavorRef'])
+
+ def test_create_instance_v1_1_no_valid_flavor(self):
+ self._setup_for_create_instance()
+
+ body = {
+ 'server': {
+ 'name': 'server_test',
+ 'image': {'id': 3},
+ 'flavor': {},
+ },
+ }
+
+ req = webob.Request.blank('/v1.1/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ req.headers["content-type"] = "application/json"
+
+ res = req.get_response(fakes.wsgi_app())
+
+ self.assertEqual(res.status_int, 400)
+
+
def test_create_instance_with_admin_pass_v1_0(self):
self._setup_for_create_instance()
@@ -858,7 +997,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'
@@ -866,8 +1005,8 @@ class ServersTest(test.TestCase):
body = {
'server': {
'name': 'server_test',
- 'imageRef': image_href,
- 'flavorRef': flavor_ref,
+ 'image': {'id': 3},
+ 'flavor': {'id': 3},
'adminPass': 'testpass',
},
}
@@ -876,20 +1015,22 @@ class ServersTest(test.TestCase):
req.method = 'POST'
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,
+ 'image': {'id': 3},
+ 'flavor': {'id': 3},
'adminPass': '',
},
}
@@ -1644,7 +1785,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()
@@ -1652,7 +1793,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_minimal_request(self):
serial_request = """
<server xmlns="http://docs.rackspacecloud.com/servers/api/v1.0"
- name="new-server-test" imageId="1" flavorId="1"/>"""
+ name="new-server-test" imageId="1" flavorId="1" />"""
request = self.deserializer.deserialize(serial_request, 'create')
expected = {"server": {
"name": "new-server-test",
@@ -1924,19 +2065,202 @@ 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="http://localhost:8774/v1.1/images/1"
- flavorRef="http://localhost:8774/v1.1/flavors/1">
- </server>"""
+<server name="new-server-test">
+ <image id="1"/>
+ <flavor id="2"/>
+</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",
+ "image": {"id": "1"},
+ "flavor": {"id": "2"},
+ },
+ }
+ self.assertEquals(request['body'], expected)
+ def test_image_link(self):
+ serial_request = """
+<server xmlns:atom="http://www.w3.org/2005/Atom" name="new-server-test">
+ <image id="1">
+ <atom:link rel="bookmark" href="http://localhost:8774/v1.1/images/2"/>
+ </image>
+ <flavor id="3"/>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "image": {
+ "id": "1",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://localhost:8774/v1.1/images/2",
+ },
+ ],
+ },
+ "flavor": {"id": "3"},
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_flavor_link(self):
+ serial_request = """
+<server xmlns:atom="http://www.w3.org/2005/Atom" name="new-server-test">
+ <image id="1"/>
+ <flavor id="2">
+ <atom:link rel="bookmark" href="http://localhost:8774/v1.1/flavors/3"/>
+ </flavor>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "image": {"id": "1"},
+ "flavor": {
+ "id": "2",
+ "links": [
+ {
+ "rel": "bookmark",
+ "href": "http://localhost:8774/v1.1/flavors/3",
+ },
+ ],
+ },
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_empty_metadata_personality(self):
+ serial_request = """
+<server name="new-server-test">
+ <image id="1"/>
+ <flavor id="2"/>
+ <metadata/>
+ <personality/>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "image": {"id": "1"},
+ "flavor": {"id": "2"},
+ "metadata": {},
+ "personality": [],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_multiple_metadata_items(self):
+ serial_request = """
+<server name="new-server-test">
+ <image id="1"/>
+ <flavor id="2"/>
+ <metadata>
+ <meta key="one">two</meta>
+ <meta key="open">snack</meta>
+ </metadata>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "image": {"id": "1"},
+ "flavor": {"id": "2"},
+ "metadata": {"one": "two", "open": "snack"},
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_multiple_personality_files(self):
+ serial_request = """
+<server name="new-server-test">
+ <image id="1"/>
+ <flavor id="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",
+ "image": {"id": "1"},
+ "flavor": {"id": "2"},
+ "personality": [
+ {"path": "/etc/banner.txt", "contents": "MQ=="},
+ {"path": "/etc/hosts", "contents": "Mg=="},
+ ],
+ },
+ }
+ self.assertEquals(request['body'], expected)
+
+ def test_spec_request(self):
+ serial_request = """
+<server xmlns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ name="new-server-test">
+ <image id="52415800-8b69-11e0-9b19-734f6f006e54"
+ name="CentOS 5.2"
+ updated="2010-10-10T12:00:00Z"
+ created="2010-08-10T12:00:00Z"
+ status="ACTIVE">
+ <atom:link
+ rel="self"
+ href="http://servers.api.openstack.org/v1.1/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"/>
+ <atom:link
+ rel="bookmark"
+ href="http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"/>
+ </image>
+ <flavor id="52415800-8b69-11e0-9b19-734f1195ff37" />
+ <metadata>
+ <meta key="My Server Name">Apache1</meta>
+ </metadata>
+ <personality>
+ <file path="/etc/banner.txt">Mg==</file>
+ </personality>
+</server>"""
+ request = self.deserializer.deserialize(serial_request, 'create')
+ expected = {
+ "server": {
+ "name": "new-server-test",
+ "image": {
+ "id": "52415800-8b69-11e0-9b19-734f6f006e54",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://servers.api.openstack.org/" + \
+ "v1.1/1234/images/52415800-8b69-11" + \
+ "e0-9b19-734f6f006e54",
+ },
+ {
+ "rel": "bookmark",
+ "href": "http://servers.api.openstack.org/" + \
+ "1234/images/52415800-8b69-11e0-9b" + \
+ "19-734f6f006e54",
+ },
+ ],
+ },
+ "flavor": {"id": "52415800-8b69-11e0-9b19-734f1195ff37"},
+ "metadata": {"My Server Name": "Apache1"},
+ "personality": [
+ {
+ "path": "/etc/banner.txt",
+ "contents": "Mg==",
+ },
+ ],
+ },
+ }
+ self.assertEquals(request['body'], expected)
class TestServerInstanceCreation(test.TestCase):