From 714047e8ebe83726d11deb561351e3c9267994a9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sun, 12 Aug 2012 17:47:47 -0700 Subject: Adds integration testing for api samples We have no validation the the samples at api.openstack.org are actually correct. This creates a framework to validate the request and response samples through integration tests. Adds a bit of stubbing in fake_network to return simple network_info along with instance records and adds a call in image/fake to get av valid image. It will create non-templated versions of the samples when the tests are running if GENERATE_SAMPLES is set in the environment when running nova/tests/integrated/test_api_samples.py Change-Id: Id134af0b23f0d0932c8789697439ce94bbe2834b --- nova/tests/fake_network.py | 51 ++++++ nova/tests/image/fake.py | 4 + nova/tests/integrated/api_samples/README.rst | 11 ++ .../all_extensions/server-get-resp.json | 68 ++++++++ .../all_extensions/server-get-resp.json.tpl | 68 ++++++++ .../api_samples/all_extensions/server-get-resp.xml | 22 +++ .../all_extensions/server-get-resp.xml.tpl | 22 +++ .../all_extensions/server-post-req.json | 16 ++ .../all_extensions/server-post-req.json.tpl | 16 ++ .../api_samples/all_extensions/server-post-req.xml | 19 +++ .../all_extensions/server-post-req.xml.tpl | 19 +++ .../all_extensions/server-post-resp.json | 22 +++ .../all_extensions/server-post-resp.json.tpl | 22 +++ .../all_extensions/server-post-resp.xml | 9 ++ .../all_extensions/server-post-resp.xml.tpl | 9 ++ .../integrated/api_samples/server-get-resp.json | 54 +++++++ .../api_samples/server-get-resp.json.tpl | 54 +++++++ .../integrated/api_samples/server-get-resp.xml | 19 +++ .../integrated/api_samples/server-get-resp.xml.tpl | 19 +++ .../integrated/api_samples/server-post-req.json | 16 ++ .../api_samples/server-post-req.json.tpl | 16 ++ .../integrated/api_samples/server-post-req.xml | 19 +++ .../integrated/api_samples/server-post-req.xml.tpl | 19 +++ .../integrated/api_samples/server-post-resp.json | 16 ++ .../api_samples/server-post-resp.json.tpl | 16 ++ .../integrated/api_samples/server-post-resp.xml | 6 + .../api_samples/server-post-resp.xml.tpl | 6 + nova/tests/integrated/test_api_samples.py | 174 +++++++++++++++++++++ 28 files changed, 812 insertions(+) create mode 100644 nova/tests/integrated/api_samples/README.rst create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-get-resp.json create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-get-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml.tpl create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-req.json create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-req.json.tpl create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-req.xml create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-req.xml.tpl create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-resp.json create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml create mode 100644 nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml.tpl create mode 100644 nova/tests/integrated/api_samples/server-get-resp.json create mode 100644 nova/tests/integrated/api_samples/server-get-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/server-get-resp.xml create mode 100644 nova/tests/integrated/api_samples/server-get-resp.xml.tpl create mode 100644 nova/tests/integrated/api_samples/server-post-req.json create mode 100644 nova/tests/integrated/api_samples/server-post-req.json.tpl create mode 100644 nova/tests/integrated/api_samples/server-post-req.xml create mode 100644 nova/tests/integrated/api_samples/server-post-req.xml.tpl create mode 100644 nova/tests/integrated/api_samples/server-post-resp.json create mode 100644 nova/tests/integrated/api_samples/server-post-resp.json.tpl create mode 100644 nova/tests/integrated/api_samples/server-post-resp.xml create mode 100644 nova/tests/integrated/api_samples/server-post-resp.xml.tpl create mode 100644 nova/tests/integrated/test_api_samples.py diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 580768d9d..ede005840 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +from nova.compute import api as compute_api from nova.compute import manager as compute_manager import nova.context from nova import db @@ -396,3 +397,53 @@ def unset_stub_network_methods(stubs): cm = compute_manager.ComputeManager for name in _real_functions: stubs.Set(cm, name, _real_functions[name]) + + +def stub_compute_with_ips(stubs): + orig_get = compute_api.API.get + orig_get_all = compute_api.API.get_all + + def fake_get(*args, **kwargs): + return _get_instances_with_cached_ips(orig_get, *args, **kwargs) + + def fake_get_all(*args, **kwargs): + return _get_instances_with_cached_ips(orig_get_all, *args, **kwargs) + + stubs.Set(compute_api.API, 'get', fake_get) + stubs.Set(compute_api.API, 'get_all', fake_get_all) + + +def _get_fake_cache(): + def _ip(ip, fixed=True, floats=None): + ip_dict = {'address': ip, 'type': 'fixed'} + if not fixed: + ip_dict['type'] = 'floating' + if fixed and floats: + ip_dict['floating_ips'] = [_ip(f, fixed=False) for f in floats] + return ip_dict + + info = [{'address': 'aa:bb:cc:dd:ee:ff', + 'id': 1, + 'network': {'bridge': 'br0', + 'id': 1, + 'label': 'private', + 'subnets': [{'cidr': '192.168.0.0/24', + 'ips': [_ip('192.168.0.3')]}]}}] + if FLAGS.use_ipv6: + ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff' + info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64', + 'ips': [_ip(ipv6_addr)]}) + return info + + +def _get_instances_with_cached_ips(orig_func, *args, **kwargs): + """Kludge the cache into instance(s) without having to create DB + entries + """ + instances = orig_func(*args, **kwargs) + if isinstance(instances, list): + for instance in instances: + instance['info_cache'] = {'network_info': _get_fake_cache()} + else: + instances['info_cache'] = {'network_info': _get_fake_cache()} + return instances diff --git a/nova/tests/image/fake.py b/nova/tests/image/fake.py index 9d07f4c0d..47b10ce76 100644 --- a/nova/tests/image/fake.py +++ b/nova/tests/image/fake.py @@ -230,6 +230,10 @@ def FakeImageService_reset(): _fakeImageService = _FakeImageService() +def get_valid_image_id(): + return _fakeImageService.images.keys()[0] + + def stub_out_image_service(stubs): def fake_get_remote_image_service(context, image_href): return (FakeImageService(), image_href) diff --git a/nova/tests/integrated/api_samples/README.rst b/nova/tests/integrated/api_samples/README.rst new file mode 100644 index 000000000..b4e274334 --- /dev/null +++ b/nova/tests/integrated/api_samples/README.rst @@ -0,0 +1,11 @@ +Api Samples +=========== + +Samples in this directory are automatically generated from the api samples +integration tests. To regenerate the samples, simply set GENERATE_SAMPLES +in the environment before running the tests. For example: + + GENERATE_SAMPLES=True ./run_tests.py nova.tests.integrated + +If new tests are added or the .tpl files are changed due to bug fixes, the +samples should be regenerated so they are in sync with the templates. diff --git a/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json new file mode 100644 index 000000000..3eb1870f9 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json @@ -0,0 +1,68 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-SRV-ATTR:host": "3972bc79fa2a4754b7559153a5a48422", + "OS-EXT-SRV-ATTR:hypervisor_hostname": null, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "accessIPv4": "", + "accessIPv6": "", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "2012-08-20T21:11:06Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/openstack/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "dc23873c80c22f14705d190b645b59398cbc8ed3cdf6145468051c0d", + "id": "7dc62bde-85f0-45b5-8c74-5fb209314807", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/7dc62bde-85f0-45b5-8c74-5fb209314807", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/7dc62bde-85f0-45b5-8c74-5fb209314807", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tenant_id": "openstack", + "updated": "2012-08-20T21:11:06Z", + "user_id": "fake" + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json.tpl new file mode 100644 index 000000000..c4e2ecf78 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.json.tpl @@ -0,0 +1,68 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "OS-EXT-SRV-ATTR:host": "%(compute_host)s", + "OS-EXT-SRV-ATTR:hypervisor_hostname": null, + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-STS:power_state": 1, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "accessIPv4": "", + "accessIPv6": "", + "addresses": { + "private": [ + { + "addr": "%(ip)s", + "version": 4 + } + ] + }, + "config_drive": "", + "created": "%(timestamp)s", + "flavor": { + "id": "1", + "links": [ + { + "href": "%(host)s/openstack/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "%(hostid)s", + "id": "%(uuid)s", + "image": { + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/openstack/images/%(uuid)s", + "rel": "bookmark" + } + ] + }, + "key_name": null, + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "security_groups": [ + { + "name": "default" + } + ], + "status": "ACTIVE", + "tenant_id": "openstack", + "updated": "%(timestamp)s", + "user_id": "fake" + } +} diff --git a/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml new file mode 100644 index 000000000..d5adf7806 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml @@ -0,0 +1,22 @@ + + + + + + + + + + Apache1 + + + + + + + + + + + + \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml.tpl new file mode 100644 index 000000000..6c73bf60f --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-get-resp.xml.tpl @@ -0,0 +1,22 @@ + + + + + + + + + + Apache1 + + + + + + + + + + + + diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-req.json b/nova/tests/integrated/api_samples/all_extensions/server-post-req.json new file mode 100644 index 000000000..2269848f4 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-req.json @@ -0,0 +1,16 @@ +{ + "server": { + "flavorRef": "http://openstack.example.com/openstack/flavors/1", + "imageRef": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "personality": [ + { + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==", + "path": "/etc/banner.txt" + } + ] + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-req.json.tpl b/nova/tests/integrated/api_samples/all_extensions/server-post-req.json.tpl new file mode 100644 index 000000000..d3916d1aa --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-req.json.tpl @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "%(host)s/openstack/images/%(image_id)s", + "flavorRef" : "%(host)s/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml b/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml new file mode 100644 index 000000000..2dbbb4438 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml.tpl new file mode 100644 index 000000000..f92614984 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-req.xml.tpl @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json new file mode 100644 index 000000000..a8dc8258a --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json @@ -0,0 +1,22 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "adminPass": "ahW3PjQNAu6r", + "id": "e726f5ae-8179-44ba-bbfb-8a0c52b33095", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/e726f5ae-8179-44ba-bbfb-8a0c52b33095", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/e726f5ae-8179-44ba-bbfb-8a0c52b33095", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json.tpl new file mode 100644 index 000000000..c931eb3fd --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.json.tpl @@ -0,0 +1,22 @@ +{ + "server": { + "OS-DCF:diskConfig": "AUTO", + "adminPass": "%(password)s", + "id": "%(id)s", + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ], + "security_groups": [ + { + "name": "default" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml new file mode 100644 index 000000000..3edac38e4 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml.tpl new file mode 100644 index 000000000..d7dc31655 --- /dev/null +++ b/nova/tests/integrated/api_samples/all_extensions/server-post-resp.xml.tpl @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/nova/tests/integrated/api_samples/server-get-resp.json b/nova/tests/integrated/api_samples/server-get-resp.json new file mode 100644 index 000000000..c6ebd4195 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-get-resp.json @@ -0,0 +1,54 @@ +{ + "server": { + "accessIPv4": "", + "accessIPv6": "", + "addresses": { + "private": [ + { + "addr": "192.168.0.3", + "version": 4 + } + ] + }, + "created": "2012-08-20T21:11:09Z", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://openstack.example.com/openstack/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "65201c14a29663e06d0748e561207d998b343e1d164bfa0aafa9c45d", + "id": "893c7791-f1df-4c3d-8383-3caae9656c62", + "image": { + "id": "70a599e0-31e7-49b7-b260-868f441e862b", + "links": [ + { + "href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/893c7791-f1df-4c3d-8383-3caae9656c62", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/893c7791-f1df-4c3d-8383-3caae9656c62", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "status": "ACTIVE", + "tenant_id": "openstack", + "updated": "2012-08-20T21:11:09Z", + "user_id": "fake" + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/server-get-resp.json.tpl b/nova/tests/integrated/api_samples/server-get-resp.json.tpl new file mode 100644 index 000000000..76907e6c3 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-get-resp.json.tpl @@ -0,0 +1,54 @@ +{ + "server": { + "accessIPv4": "", + "accessIPv6": "", + "addresses": { + "private": [ + { + "addr": "%(ip)s", + "version": 4 + } + ] + }, + "created": "%(timestamp)s", + "flavor": { + "id": "1", + "links": [ + { + "href": "%(host)s/openstack/flavors/1", + "rel": "bookmark" + } + ] + }, + "hostId": "%(hostid)s", + "id": "%(uuid)s", + "image": { + "id": "%(uuid)s", + "links": [ + { + "href": "%(host)s/openstack/images/%(uuid)s", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ], + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "progress": 0, + "status": "ACTIVE", + "tenant_id": "openstack", + "updated": "%(timestamp)s", + "user_id": "fake" + } +} diff --git a/nova/tests/integrated/api_samples/server-get-resp.xml b/nova/tests/integrated/api_samples/server-get-resp.xml new file mode 100644 index 000000000..e59319e42 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-get-resp.xml @@ -0,0 +1,19 @@ + + + + + + + + + + Apache1 + + + + + + + + + \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/server-get-resp.xml.tpl b/nova/tests/integrated/api_samples/server-get-resp.xml.tpl new file mode 100644 index 000000000..f3e4b4bd0 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-get-resp.xml.tpl @@ -0,0 +1,19 @@ + + + + + + + + + + Apache1 + + + + + + + + + diff --git a/nova/tests/integrated/api_samples/server-post-req.json b/nova/tests/integrated/api_samples/server-post-req.json new file mode 100644 index 000000000..2269848f4 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-req.json @@ -0,0 +1,16 @@ +{ + "server": { + "flavorRef": "http://openstack.example.com/openstack/flavors/1", + "imageRef": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "metadata": { + "My Server Name": "Apache1" + }, + "name": "new-server-test", + "personality": [ + { + "contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==", + "path": "/etc/banner.txt" + } + ] + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/server-post-req.json.tpl b/nova/tests/integrated/api_samples/server-post-req.json.tpl new file mode 100644 index 000000000..d3916d1aa --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-req.json.tpl @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "%(host)s/openstack/images/%(image_id)s", + "flavorRef" : "%(host)s/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/server-post-req.xml b/nova/tests/integrated/api_samples/server-post-req.xml new file mode 100644 index 000000000..2dbbb4438 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-req.xml @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/server-post-req.xml.tpl new file mode 100644 index 000000000..f92614984 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-req.xml.tpl @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/server-post-resp.json b/nova/tests/integrated/api_samples/server-post-resp.json new file mode 100644 index 000000000..29ce13717 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-resp.json @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "MVk5HPrazHcG", + "id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "rel": "bookmark" + } + ] + } +} \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/server-post-resp.json.tpl new file mode 100644 index 000000000..d5f030c87 --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "%(password)s", + "id": "%(id)s", + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/server-post-resp.xml b/nova/tests/integrated/api_samples/server-post-resp.xml new file mode 100644 index 000000000..9725f33bf --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-resp.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/nova/tests/integrated/api_samples/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/server-post-resp.xml.tpl new file mode 100644 index 000000000..3bb13e69b --- /dev/null +++ b/nova/tests/integrated/api_samples/server-post-resp.xml.tpl @@ -0,0 +1,6 @@ + + + + + + diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py new file mode 100644 index 000000000..0c454cb60 --- /dev/null +++ b/nova/tests/integrated/test_api_samples.py @@ -0,0 +1,174 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2012 Nebula, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import re + +from lxml import etree + +from nova import flags +from nova.openstack.common import jsonutils +from nova.openstack.common.log import logging +from nova.tests import fake_network +from nova.tests.image import fake +from nova.tests.integrated import integrated_helpers + +FLAGS = flags.FLAGS +LOG = logging.getLogger(__name__) + + +class ApiSampleTestBase(integrated_helpers._IntegratedTestBase): + ctype = 'json' + all_extensions = False + extension_name = None + + def setUp(self): + self.flags(use_ipv6=False, + osapi_compute_link_prefix=self._get_host()) + if not self.all_extensions: + ext = [self.extension_name] if self.extension_name else [] + self.flags(osapi_compute_extension=ext) + super(ApiSampleTestBase, self).setUp() + fake_network.stub_compute_with_ips(self.stubs) + self.generate_samples = os.getenv('GENERATE_SAMPLES') is not None + + def _pretty_data(self, data): + if self.ctype == 'json': + data = jsonutils.dumps(jsonutils.loads(data), sort_keys=True, + indent=4) + + else: + data = etree.tostring(etree.XML(data), encoding="UTF-8", + xml_declaration=True, pretty_print=True) + return '\n'.join(line.rstrip() for line in data.split('\n')) + + @classmethod + def _get_sample(cls, name, suffix=''): + parts = [os.path.dirname(os.path.abspath(__file__))] + parts.append('api_samples') + if cls.all_extensions: + parts.append('all_extensions') + if cls.extension_name: + parts.append(cls.extension_name) + parts.append(name + "." + cls.ctype + suffix) + return os.path.join(*parts) + + @classmethod + def _get_template(cls, name): + return cls._get_sample(name, suffix='.tpl') + + def _read_template(self, name): + with open(self._get_template(name)) as inf: + return inf.read().strip() + + def _write_sample(self, name, data): + with open(self._get_sample(name), 'w') as outf: + outf.write(data) + + def _verify_response(self, name, subs, response): + expected = self._read_template(name) + + # NOTE(vish): escape stuff for regex + for char in ['[', ']', '<', '>', '?']: + expected = expected.replace(char, '\%s' % char) + + expected = expected % subs + data = response.read() + result = self._pretty_data(data).strip() + if self.generate_samples: + self._write_sample(name, result) + result_lines = result.split('\n') + expected_lines = expected.split('\n') + if len(result_lines) != len(expected_lines): + LOG.info(expected) + LOG.info(result) + self.fail('Number of lines (%s) incorrect' % (len(expected_lines))) + result = None + for line, result_line in zip(expected_lines, result_lines): + try: + match = re.match(line, result_line) + except Exception as exc: + self.fail(_('Response error on line %(i)s:\n' + '%(line)s\n%(result_line)s') % locals()) + if not match: + self.fail(_('Response error on line %(i)s:\n' + '%(line)s\n%(result_line)s') % locals()) + if match.groups(): + result = match.groups()[0] + return result + + def _get_host(self): + return 'http://openstack.example.com' + + def _get_regexes(self): + return { + 'timestamp': '[0-9]{4}-[0,1][0-9]-[0-3][0-9]T' + '[0-9]{2}:[0-9]{2}:[0-9]{2}Z', + 'password': '[0-9a-zA-Z]{12}', + 'ip': '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}', + 'id': '([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}' + '-[0-9a-f]{4}-[0-9a-f]{12})', + 'uuid': '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}' + '-[0-9a-f]{4}-[0-9a-f]{12}', + 'host': self._get_host(), + 'compute_host': self.compute.host, + } + + def _get_response(self, url, method, body=None): + headers = {} + headers['Content-Type'] = 'application/' + self.ctype + headers['Accept'] = 'application/' + self.ctype + return self.api.api_request(url, body=body, method=method, + headers=headers) + + def _do_get(self, url): + return self._get_response(url, 'GET') + + def _do_post(self, url, name, subs): + body = self._read_template(name) % subs + if self.generate_samples: + self._write_sample(name, self._pretty_data(body)) + return self._get_response(url, 'POST', body) + + +class ServersSampleJsonTest(ApiSampleTestBase): + def test_servers_post(self): + subs = { + 'image_id': fake.get_valid_image_id(), + 'host': self._get_host(), + } + response = self._do_post('servers', 'server-post-req', subs) + self.assertEqual(response.status, 202) + subs = self._get_regexes() + return self._verify_response('server-post-resp', subs, response) + + def test_servers_get(self): + uuid = self.test_servers_post() + response = self._do_get('servers/%s' % uuid) + subs = self._get_regexes() + subs['hostid'] = '[a-f0-9]+' + return self._verify_response('server-get-resp', subs, response) + + +class ServersSampleXmlTest(ServersSampleJsonTest): + ctype = 'xml' + + +class ServersSampleAllExtensionJsonTest(ServersSampleJsonTest): + all_extensions = True + + +class ServersSampleAllExtensionXmlTest(ServersSampleXmlTest): + all_extensions = True -- cgit