276 lines
11 KiB
Python
276 lines
11 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2011 OpenStack, LLC
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""Functional test case that utilizes httplib2 against the API server"""
|
|
|
|
import hashlib
|
|
import httplib2
|
|
import json
|
|
import os
|
|
|
|
from tests import functional
|
|
from tests.utils import execute
|
|
|
|
FIVE_KB = 5 * 1024
|
|
FIVE_GB = 5 * 1024 * 1024 * 1024
|
|
|
|
|
|
class TestApiHttplib2(functional.FunctionalTest):
|
|
|
|
"""Functional tests using httplib2 against the API server"""
|
|
|
|
def test_001_get_head_simple_post(self):
|
|
"""
|
|
We test the following sequential series of actions:
|
|
|
|
0. GET /images
|
|
- Verify no public images
|
|
1. GET /images/detail
|
|
- Verify no public images
|
|
2. HEAD /images/1
|
|
- Verify 404 returned
|
|
3. POST /images with public image named Image1 with a location
|
|
attribute and no custom properties
|
|
- Verify 201 returned
|
|
4. HEAD /images/1
|
|
- Verify HTTP headers have correct information we just added
|
|
5. GET /images/1
|
|
- Verify all information on image we just added is correct
|
|
6. GET /images
|
|
- Verify the image we just added is returned
|
|
7. GET /images/detail
|
|
- Verify the image we just added is returned
|
|
8. PUT /images/1 with custom properties of "distro" and "arch"
|
|
- Verify 200 returned
|
|
9. GET /images/1
|
|
- Verify updated information about image was stored
|
|
10. PUT /images/1
|
|
- Remove a previously existing property.
|
|
11. PUT /images/1
|
|
- Add a previously deleted property.
|
|
"""
|
|
|
|
self.cleanup()
|
|
self.start_servers()
|
|
|
|
# 0. GET /images
|
|
# Verify no public images
|
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
self.assertEqual(content, '{"images": []}')
|
|
|
|
# 1. GET /images/detail
|
|
# Verify no public images
|
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
self.assertEqual(content, '{"images": []}')
|
|
|
|
# 2. HEAD /images/1
|
|
# Verify 404 returned
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'HEAD')
|
|
self.assertEqual(response.status, 404)
|
|
|
|
# 3. POST /images with public image named Image1
|
|
# attribute and no custom properties. Verify a 200 OK is returned
|
|
image_data = "*" * FIVE_KB
|
|
headers = {'Content-Type': 'application/octet-stream',
|
|
'X-Image-Meta-Name': 'Image1',
|
|
'X-Image-Meta-Is-Public': 'True'}
|
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'POST', headers=headers,
|
|
body=image_data)
|
|
self.assertEqual(response.status, 201)
|
|
data = json.loads(content)
|
|
self.assertEqual(data['image']['checksum'],
|
|
hashlib.md5(image_data).hexdigest())
|
|
self.assertEqual(data['image']['size'], FIVE_KB)
|
|
self.assertEqual(data['image']['name'], "Image1")
|
|
self.assertEqual(data['image']['is_public'], True)
|
|
|
|
# 4. HEAD /images/1
|
|
# Verify image found now
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'HEAD')
|
|
self.assertEqual(response.status, 200)
|
|
self.assertEqual(response['x-image-meta-name'], "Image1")
|
|
|
|
# 5. GET /images/1
|
|
# Verify all information on image we just added is correct
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
|
|
expected_image_headers = {
|
|
'x-image-meta-id': '1',
|
|
'x-image-meta-name': 'Image1',
|
|
'x-image-meta-is_public': 'True',
|
|
'x-image-meta-status': 'active',
|
|
'x-image-meta-disk_format': '',
|
|
'x-image-meta-container_format': '',
|
|
'x-image-meta-size': str(FIVE_KB),
|
|
'x-image-meta-location': 'file://%s/1' % self.api_server.image_dir}
|
|
|
|
expected_std_headers = {
|
|
'content-length': str(FIVE_KB),
|
|
'content-type':
|
|
'text/html; charset=UTF-8, application/octet-stream'}
|
|
|
|
for expected_key, expected_value in expected_image_headers.items():
|
|
self.assertEqual(response[expected_key], expected_value,
|
|
"For key '%s' expected header value '%s'. Got '%s'"
|
|
% (expected_key, expected_value,
|
|
response[expected_key]))
|
|
|
|
for expected_key, expected_value in expected_std_headers.items():
|
|
self.assertEqual(response[expected_key], expected_value,
|
|
"For key '%s' expected header value '%s'. Got '%s'"
|
|
% (expected_key,
|
|
expected_value,
|
|
response[expected_key]))
|
|
|
|
self.assertEqual(content, "*" * FIVE_KB)
|
|
self.assertEqual(hashlib.md5(content).hexdigest(),
|
|
hashlib.md5("*" * FIVE_KB).hexdigest())
|
|
|
|
# 6. GET /images
|
|
# Verify no public images
|
|
path = "http://%s:%d/v1/images" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
|
|
expected_result = {"images": [
|
|
{"container_format": None,
|
|
"disk_format": None,
|
|
"id": 1,
|
|
"name": "Image1",
|
|
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
|
"size": 5120}]}
|
|
self.assertEqual(json.loads(content), expected_result)
|
|
|
|
# 7. GET /images/detail
|
|
# Verify image and all its metadata
|
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
|
|
expected_image = {
|
|
"status": "active",
|
|
"name": "Image1",
|
|
"deleted": False,
|
|
"container_format": None,
|
|
"disk_format": None,
|
|
"id": 1,
|
|
"location": "file://%s/1" % self.api_server.image_dir,
|
|
"is_public": True,
|
|
"deleted_at": None,
|
|
"properties": {},
|
|
"size": 5120}
|
|
|
|
image = json.loads(content)
|
|
|
|
for expected_key, expected_value in expected_image.items():
|
|
self.assertEqual(expected_value, expected_image[expected_key],
|
|
"For key '%s' expected header value '%s'. Got '%s'"
|
|
% (expected_key,
|
|
expected_value,
|
|
image['images'][0][expected_key]))
|
|
|
|
# 8. PUT /images/1 with custom properties of "distro" and "arch"
|
|
# Verify 200 returned
|
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'PUT', headers=headers)
|
|
self.assertEqual(response.status, 200)
|
|
data = json.loads(content)
|
|
self.assertEqual(data['image']['properties']['arch'], "x86_64")
|
|
self.assertEqual(data['image']['properties']['distro'], "Ubuntu")
|
|
|
|
# 9. GET /images/detail
|
|
# Verify image and all its metadata
|
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
|
|
expected_image = {
|
|
"status": "active",
|
|
"name": "Image1",
|
|
"deleted": False,
|
|
"container_format": None,
|
|
"disk_format": None,
|
|
"id": 1,
|
|
"location": "file://%s/1" % self.api_server.image_dir,
|
|
"is_public": True,
|
|
"deleted_at": None,
|
|
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
|
"size": 5120}
|
|
|
|
image = json.loads(content)
|
|
|
|
for expected_key, expected_value in expected_image.items():
|
|
self.assertEqual(expected_value, expected_image[expected_key],
|
|
"For key '%s' expected header value '%s'. Got '%s'"
|
|
% (expected_key,
|
|
expected_value,
|
|
image['images'][0][expected_key]))
|
|
|
|
# 10. PUT /images/1 and remove a previously existing property.
|
|
headers = {'X-Image-Meta-Property-Arch': 'x86_64'}
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'PUT', headers=headers)
|
|
self.assertEqual(response.status, 200)
|
|
|
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
data = json.loads(content)['images'][0]
|
|
self.assertEqual(len(data['properties']), 1)
|
|
self.assertEqual(data['properties']['arch'], "x86_64")
|
|
|
|
# 11. PUT /images/1 and add a previously deleted property.
|
|
headers = {'X-Image-Meta-Property-Distro': 'Ubuntu',
|
|
'X-Image-Meta-Property-Arch': 'x86_64'}
|
|
path = "http://%s:%d/v1/images/1" % ("0.0.0.0", self.api_port)
|
|
http = httplib2.Http()
|
|
response, content = http.request(path, 'PUT', headers=headers)
|
|
self.assertEqual(response.status, 200)
|
|
data = json.loads(content)
|
|
|
|
path = "http://%s:%d/v1/images/detail" % ("0.0.0.0", self.api_port)
|
|
response, content = http.request(path, 'GET')
|
|
self.assertEqual(response.status, 200)
|
|
data = json.loads(content)['images'][0]
|
|
self.assertEqual(len(data['properties']), 2)
|
|
self.assertEqual(data['properties']['arch'], "x86_64")
|
|
self.assertEqual(data['properties']['distro'], "Ubuntu")
|
|
|
|
self.stop_servers()
|