diff options
Diffstat (limited to 'nova/tests/api/openstack/compute')
7 files changed, 354 insertions, 47 deletions
diff --git a/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py index bb0eafe66..97b78f81e 100644 --- a/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py +++ b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py @@ -152,25 +152,24 @@ class CloudpipesXMLSerializerTest(test.TestCase): def test_index_serializer(self): serializer = cloudpipe.CloudpipesTemplate() exemplar = dict(cloudpipes=[ - dict(cloudpipe=dict( + dict( project_id='1234', public_ip='1.2.3.4', public_port='321', instance_id='1234-1234-1234-1234', created_at=timeutils.isotime(), - state='running')), - dict(cloudpipe=dict( + state='running'), + dict( project_id='4321', public_ip='4.3.2.1', public_port='123', - state='pending'))]) + state='pending')]) text = serializer.serialize(exemplar) tree = etree.fromstring(text) self.assertEqual('cloudpipes', tree.tag) self.assertEqual(len(exemplar['cloudpipes']), len(tree)) for idx, cl_pipe in enumerate(tree): - self.assertEqual('cloudpipe', cl_pipe.tag) - kp_data = exemplar['cloudpipes'][idx]['cloudpipe'] + kp_data = exemplar['cloudpipes'][idx] for child in cl_pipe: self.assertTrue(child.tag in kp_data) self.assertEqual(child.text, kp_data[child.tag]) diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py index e9cfc2b9e..a789bbe96 100644 --- a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py @@ -197,6 +197,21 @@ class FloatingIpTest(test.TestCase): 'id': 2}]} self.assertEqual(res_dict, response) + def test_floating_ip_release_nonexisting(self): + def fake_get_floating_ip(*args, **kwargs): + raise exception.FloatingIpNotFound(id=id) + + self.stubs.Set(network.api.API, "get_floating_ip", + fake_get_floating_ip) + + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/9876') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 404) + expected_msg = ('{"itemNotFound": {"message": "Floating ip not found ' + 'for id 9876", "code": 404}}') + self.assertEqual(res.body, expected_msg) + def test_floating_ip_show(self): req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/1') res_dict = self.controller.show(req, 1) @@ -214,8 +229,11 @@ class FloatingIpTest(test.TestCase): req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips/9876') - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.show, req, 9876) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 404) + expected_msg = ('{"itemNotFound": {"message": "Floating ip not found ' + 'for id 9876", "code": 404}}') + self.assertEqual(res.body, expected_msg) def test_show_associated_floating_ip(self): def get_floating_ip(self, context, id): diff --git a/nova/tests/api/openstack/compute/contrib/test_volume_types.py b/nova/tests/api/openstack/compute/contrib/test_volume_types.py index 4ad6297b8..af88cf601 100644 --- a/nova/tests/api/openstack/compute/contrib/test_volume_types.py +++ b/nova/tests/api/openstack/compute/contrib/test_volume_types.py @@ -151,16 +151,6 @@ class VolumeTypesApiTest(test.TestCase): self.assertEqual(1, len(res_dict)) self.assertEqual('vol_type_1', res_dict['volume_type']['name']) - def test_create_empty_body(self): - self.stubs.Set(volume_types, 'create', - return_volume_types_create) - self.stubs.Set(volume_types, 'get_volume_type_by_name', - return_volume_types_get_by_name) - - req = fakes.HTTPRequest.blank('/v2/fake/os-volume-types') - self.assertRaises(webob.exc.HTTPUnprocessableEntity, - self.controller.create, req, '') - class VolumeTypesSerializerTest(test.TestCase): def _verify_volume_type(self, vtype, tree): @@ -204,3 +194,31 @@ class VolumeTypesSerializerTest(test.TestCase): tree = etree.fromstring(text) self._verify_volume_type(vtype, tree) + + +class VolumeTypesUnprocessableEntityTestCase(test.TestCase): + """ + Tests of places we throw 422 Unprocessable Entity from + """ + + def setUp(self): + super(VolumeTypesUnprocessableEntityTestCase, self).setUp() + self.controller = volumetypes.VolumeTypesController() + + def _unprocessable_volume_type_create(self, body): + req = fakes.HTTPRequest.blank('/v2/fake/os-volume-types') + req.method = 'POST' + + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, body) + + def test_create_volume_type_no_body(self): + self._unprocessable_volume_type_create(body=None) + + def test_create_volume_type_missing_volume_type(self): + body = {'foo': {'a': 'b'}} + self._unprocessable_volume_type_create(body=body) + + def test_create_volume_type_malformed_entity(self): + body = {'volume_type': 'string'} + self._unprocessable_volume_type_create(body=body) diff --git a/nova/tests/api/openstack/compute/contrib/test_volumes.py b/nova/tests/api/openstack/compute/contrib/test_volumes.py index 108ec8964..7ed04f1ad 100644 --- a/nova/tests/api/openstack/compute/contrib/test_volumes.py +++ b/nova/tests/api/openstack/compute/contrib/test_volumes.py @@ -175,15 +175,6 @@ class VolumeApiTest(test.TestCase): self.assertEqual(resp_dict['volume']['availabilityZone'], vol['availability_zone']) - def test_volume_create_no_body(self): - req = webob.Request.blank('/v2/fake/os-volumes') - req.method = 'POST' - req.body = jsonutils.dumps({}) - req.headers['content-type'] = 'application/json' - - resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 422) - def test_volume_index(self): req = webob.Request.blank('/v2/fake/os-volumes') resp = req.get_response(fakes.wsgi_app()) @@ -566,3 +557,64 @@ class TestVolumeCreateRequestXMLDeserializer(test.TestCase): } self.maxDiff = None self.assertEquals(request['body'], expected) + + +class CommonUnprocessableEntityTestCase(object): + + resource = None + entity_name = None + controller_cls = None + kwargs = {} + + """ + Tests of places we throw 422 Unprocessable Entity from + """ + + def setUp(self): + super(CommonUnprocessableEntityTestCase, self).setUp() + self.controller = self.controller_cls() + + def _unprocessable_create(self, body): + req = fakes.HTTPRequest.blank('/v2/fake/' + self.resource) + req.method = 'POST' + + kwargs = self.kwargs.copy() + kwargs['body'] = body + self.assertRaises(webob.exc.HTTPUnprocessableEntity, + self.controller.create, req, **kwargs) + + def test_create_no_body(self): + self._unprocessable_create(body=None) + + def test_create_missing_volume(self): + body = {'foo': {'a': 'b'}} + self._unprocessable_create(body=body) + + def test_create_malformed_entity(self): + body = {self.entity_name: 'string'} + self._unprocessable_create(body=body) + + +class UnprocessableVolumeTestCase(CommonUnprocessableEntityTestCase, + test.TestCase): + + resource = 'os-volumes' + entity_name = 'volume' + controller_cls = volumes.VolumeController + + +class UnprocessableAttachmentTestCase(CommonUnprocessableEntityTestCase, + test.TestCase): + + resource = 'servers/' + FAKE_UUID + '/os-volume_attachments' + entity_name = 'volumeAttachment' + controller_cls = volumes.VolumeAttachmentController + kwargs = {'server_id': FAKE_UUID} + + +class UnprocessableSnapshotTestCase(CommonUnprocessableEntityTestCase, + test.TestCase): + + resource = 'os-snapshots' + entity_name = 'snapshot' + controller_cls = volumes.SnapshotController diff --git a/nova/tests/api/openstack/compute/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py index 2a3038267..84c000035 100644 --- a/nova/tests/api/openstack/compute/test_limits.py +++ b/nova/tests/api/openstack/compute/test_limits.py @@ -124,6 +124,8 @@ class LimitsControllerTest(BaseLimitTestSuite): 'volumes': 5, 'key_pairs': 10, 'floating_ips': 10, + 'security_groups': 10, + 'security_group_rules': 20, } response = request.get_response(self.controller) expected = { @@ -172,6 +174,8 @@ class LimitsControllerTest(BaseLimitTestSuite): "maxTotalVolumes": 5, "maxTotalKeypairs": 10, "maxTotalFloatingIps": 10, + "maxSecurityGroups": 10, + "maxSecurityGroupRules": 20, }, }, } @@ -272,6 +276,17 @@ class LimitsControllerTest(BaseLimitTestSuite): } self._test_index_absolute_limits_json(expected) + def test_index_absolute_security_groups(self): + self.absolute_limits = { + 'security_groups': 8, + 'security_group_rules': 16, + } + expected = { + 'maxSecurityGroups': 8, + 'maxSecurityGroupRules': 16, + } + self._test_index_absolute_limits_json(expected) + class TestLimiter(limits.Limiter): pass @@ -321,9 +336,13 @@ class LimitMiddlewareTest(BaseLimitTestSuite): body = jsonutils.loads(response.body) expected = "Only 1 GET request(s) can be made to * every minute." - value = body["overLimitFault"]["details"].strip() + value = body["overLimit"]["details"].strip() self.assertEqual(value, expected) + self.assertTrue("retryAfter" in body["overLimit"]) + retryAfter = body["overLimit"]["retryAfter"] + self.assertEqual(retryAfter, "60") + def test_limited_request_xml(self): """Test a rate-limited (413) response as XML""" request = webob.Request.blank("/") @@ -338,6 +357,10 @@ class LimitMiddlewareTest(BaseLimitTestSuite): root = minidom.parseString(response.body).childNodes[0] expected = "Only 1 GET request(s) can be made to * every minute." + self.assertNotEqual(root.attributes.getNamedItem("retryAfter"), None) + retryAfter = root.attributes.getNamedItem("retryAfter").value + self.assertEqual(retryAfter, "60") + details = root.getElementsByTagName("details") self.assertEqual(details.length, 1) diff --git a/nova/tests/api/openstack/compute/test_server_actions.py b/nova/tests/api/openstack/compute/test_server_actions.py index 1c7366caa..c765f1dd8 100644 --- a/nova/tests/api/openstack/compute/test_server_actions.py +++ b/nova/tests/api/openstack/compute/test_server_actions.py @@ -630,13 +630,13 @@ class ServerActionsControllerTest(test.TestCase): image_service = nova.image.glance.get_default_image_service() - bdm = [dict(snapshot_id=_fake_id('a'), + bdm = [dict(volume_id=_fake_id('a'), volume_size=1, - device_name='sda1', + device_name='vda', delete_on_termination=False)] props = dict(kernel_id=_fake_id('b'), ramdisk_id=_fake_id('c'), - root_device_name='/dev/sda1', + root_device_name='/dev/vda', block_device_mapping=bdm) original_image = dict(properties=props, container_format='ami', @@ -649,11 +649,10 @@ class ServerActionsControllerTest(test.TestCase): class BDM(object): def __init__(self): self.no_device = None - self.values = dict(snapshot_id=_fake_id('a'), - volume_id=_fake_id('d'), + self.values = dict(volume_id=_fake_id('a'), virtual_name=None, volume_size=1, - device_name='sda1', + device_name='vda', delete_on_termination=False) def __getattr__(self, name): @@ -669,32 +668,38 @@ class ServerActionsControllerTest(test.TestCase): instance = fakes.fake_instance_get(image_ref=original_image['id'], vm_state=vm_states.ACTIVE, - root_device_name='/dev/sda1') + root_device_name='/dev/vda') self.stubs.Set(nova.db, 'instance_get_by_uuid', instance) - def fake_volume_get(context, volume_id): - return dict(id=volume_id, - size=1, - host='fake', - display_description='fake') + volume = dict(id=_fake_id('a'), + size=1, + host='fake', + display_description='fake') + snapshot = dict(id=_fake_id('d')) + self.mox.StubOutWithMock(self.controller.compute_api, 'volume_api') + volume_api = self.controller.compute_api.volume_api + volume_api.get(mox.IgnoreArg(), volume['id']).AndReturn(volume) + volume_api.create_snapshot_force(mox.IgnoreArg(), volume, + mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(snapshot) - self.stubs.Set(nova.db, 'volume_get', fake_volume_get) + self.mox.ReplayAll() req = fakes.HTTPRequest.blank(self.url) response = self.controller._action_create_image(req, FAKE_UUID, body) location = response.headers['Location'] image_id = location.replace('http://localhost/v2/fake/images/', '') - snapshot = image_service.show(None, image_id) + image = image_service.show(None, image_id) - self.assertEquals(snapshot['name'], 'snapshot_of_volume_backed') - properties = snapshot['properties'] + self.assertEquals(image['name'], 'snapshot_of_volume_backed') + properties = image['properties'] self.assertEquals(properties['kernel_id'], _fake_id('b')) self.assertEquals(properties['ramdisk_id'], _fake_id('c')) - self.assertEquals(properties['root_device_name'], '/dev/sda1') + self.assertEquals(properties['root_device_name'], '/dev/vda') bdms = properties['block_device_mapping'] self.assertEquals(len(bdms), 1) - self.assertEquals(bdms[0]['device_name'], 'sda1') + self.assertEquals(bdms[0]['device_name'], 'vda') + self.assertEquals(bdms[0]['snapshot_id'], snapshot['id']) for k in extra_properties.keys(): self.assertEquals(properties[k], extra_properties[k]) diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index 1a2026900..b90866813 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 import datetime import urlparse @@ -94,6 +95,43 @@ class MockSetAdminPassword(object): self.password = password +class Base64ValidationTest(test.TestCase): + def setUp(self): + super(Base64ValidationTest, self).setUp() + self.ext_mgr = extensions.ExtensionManager() + self.ext_mgr.extensions = {} + self.controller = servers.Controller(self.ext_mgr) + + def test_decode_base64(self): + value = "A random string" + result = self.controller._decode_base64(base64.b64encode(value)) + self.assertEqual(result, value) + + def test_decode_base64_binary(self): + value = "\x00\x12\x75\x99" + result = self.controller._decode_base64(base64.b64encode(value)) + self.assertEqual(result, value) + + def test_decode_base64_whitespace(self): + value = "A random string" + encoded = base64.b64encode(value) + white = "\n \n%s\t%s\n" % (encoded[:2], encoded[2:]) + result = self.controller._decode_base64(white) + self.assertEqual(result, value) + + def test_decode_base64_invalid(self): + invalid = "A random string" + result = self.controller._decode_base64(invalid) + self.assertEqual(result, None) + + def test_decode_base64_illegal_bytes(self): + value = "A random string" + encoded = base64.b64encode(value) + white = ">\x01%s*%s()" % (encoded[:2], encoded[2:]) + result = self.controller._decode_base64(white) + self.assertEqual(result, None) + + class ServersControllerTest(test.TestCase): def setUp(self): @@ -1934,7 +1972,7 @@ class ServersControllerCreateTest(test.TestCase): self._test_create_extra(params) def test_create_instance_with_scheduler_hints_enabled(self): - self.ext_mgr.extensions = {'os-scheduler-hints': 'fake'} + self.ext_mgr.extensions = {'OS-SCH-HNT': 'fake'} hints = {'a': 'b'} params = {'scheduler_hints': hints} old_create = nova.compute.api.API.create @@ -2765,6 +2803,43 @@ class ServersControllerCreateTest(test.TestCase): # The fact that the action doesn't raise is enough validation self.controller.create(req, body) + def test_create_instance_invalid_personality(self): + + def fake_create(*args, **kwargs): + codec = 'utf8' + content = 'b25zLiINCg0KLVJpY2hhcmQgQ$$%QQmFjaA==' + start_position = 19 + end_position = 20 + msg = 'invalid start byte' + raise UnicodeDecodeError(codec, content, start_position, + end_position, msg) + + self.stubs.Set(nova.compute.api.API, + 'create', + fake_create) + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + flavor_ref = 'http://localhost/v2/flavors/3' + body = { + 'server': { + 'name': 'server_test', + 'imageRef': image_uuid, + 'flavorRef': flavor_ref, + 'personality': [ + { + "path": "/etc/banner.txt", + "contents": "b25zLiINCg0KLVJpY2hhcmQgQ$$%QQmFjaA==", + }, + ], + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers') + req.method = 'POST' + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, body) + def test_create_location(self): selfhref = 'http://localhost/v2/fake/servers/%s' % FAKE_UUID bookhref = 'http://localhost/fake/servers/%s' % FAKE_UUID @@ -3218,6 +3293,123 @@ class TestServerCreateRequestXMLDeserializer(test.TestCase): }} self.assertEquals(request['body'], expected) + def test_request_with_availability_zone(self): + serial_request = """ + <server xmlns="http://docs.openstack.org/compute/api/v2" + name="new-server-test" imageRef="1" flavorRef="1" + availability_zone="some_zone:some_host"> + </server>""" + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "availability_zone": "some_zone:some_host", + }} + self.assertEquals(request['body'], expected) + + def test_request_with_multiple_create_args(self): + serial_request = """ + <server xmlns="http://docs.openstack.org/compute/api/v2" + name="new-server-test" imageRef="1" flavorRef="1" + min_count="1" max_count="3" return_reservation_id="True"> + </server>""" + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "min_count": "1", + "max_count": "3", + "return_reservation_id": True, + }} + self.assertEquals(request['body'], expected) + + def test_request_with_disk_config(self): + serial_request = """ + <server xmlns="http://docs.openstack.org/compute/api/v2" + xmlns:OS-DCF="http://docs.openstack.org/compute/ext/disk_config/api/v1.1" + name="new-server-test" imageRef="1" flavorRef="1" + OS-DCF:diskConfig="True"> + </server>""" + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "OS-DCF:diskConfig": True, + }} + self.assertEquals(request['body'], expected) + + def test_request_with_scheduler_hints(self): + serial_request = """ + <server xmlns="http://docs.openstack.org/compute/api/v2" + xmlns:OS-SCH-HNT= + "http://docs.openstack.org/compute/ext/scheduler-hints/api/v2" + name="new-server-test" imageRef="1" flavorRef="1"> + <OS-SCH-HNT:scheduler_hints> + <different_host> + 7329b667-50c7-46a6-b913-cb2a09dfeee0 + </different_host> + <different_host> + f31efb24-34d2-43e1-8b44-316052956a39 + </different_host> + </OS-SCH-HNT:scheduler_hints> + </server>""" + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "OS-SCH-HNT:scheduler_hints": { + "different_host": [ + "7329b667-50c7-46a6-b913-cb2a09dfeee0", + "f31efb24-34d2-43e1-8b44-316052956a39", + ] + } + }} + self.assertEquals(request['body'], expected) + + def test_request_with_block_device_mapping(self): + serial_request = """ + <server xmlns="http://docs.openstack.org/compute/api/v2" + name="new-server-test" imageRef="1" flavorRef="1"> + <block_device_mapping> + <mapping volume_id="7329b667-50c7-46a6-b913-cb2a09dfeee0" + device_name="/dev/vda" virtual_name="root" + delete_on_termination="False" /> + <mapping snapshot_id="f31efb24-34d2-43e1-8b44-316052956a39" + device_name="/dev/vdb" virtual_name="ephemeral0" + delete_on_termination="False" /> + <mapping device_name="/dev/vdc" no_device="True" /> + </block_device_mapping> + </server>""" + request = self.deserializer.deserialize(serial_request) + expected = {"server": { + "name": "new-server-test", + "imageRef": "1", + "flavorRef": "1", + "block_device_mapping": [ + { + "volume_id": "7329b667-50c7-46a6-b913-cb2a09dfeee0", + "device_name": "/dev/vda", + "virtual_name": "root", + "delete_on_termination": False, + }, + { + "snapshot_id": "f31efb24-34d2-43e1-8b44-316052956a39", + "device_name": "/dev/vdb", + "virtual_name": "ephemeral0", + "delete_on_termination": False, + }, + { + "device_name": "/dev/vdc", + "no_device": True, + }, + ] + }} + self.assertEquals(request['body'], expected) + class TestAddressesXMLSerialization(test.TestCase): |