Add functional tests for new add-location API
Related blueprint new-location-apis Change-Id: If94d7d1fbec07a49222b65aa3e530748a95eaf5b
This commit is contained in:
parent
4281558dff
commit
b83f38cf25
@ -99,6 +99,8 @@ class BaseServer(metaclass=abc.ABCMeta):
|
|||||||
self.deployment_flavor = ''
|
self.deployment_flavor = ''
|
||||||
self.show_image_direct_url = False
|
self.show_image_direct_url = False
|
||||||
self.show_multiple_locations = False
|
self.show_multiple_locations = False
|
||||||
|
self.do_secure_hash = True
|
||||||
|
self.http_retries = '3'
|
||||||
self.property_protection_file = ''
|
self.property_protection_file = ''
|
||||||
self.needs_database = False
|
self.needs_database = False
|
||||||
self.log_file = None
|
self.log_file = None
|
||||||
@ -437,6 +439,8 @@ image_cache_dir = %(image_cache_dir)s
|
|||||||
image_cache_driver = %(image_cache_driver)s
|
image_cache_driver = %(image_cache_driver)s
|
||||||
show_image_direct_url = %(show_image_direct_url)s
|
show_image_direct_url = %(show_image_direct_url)s
|
||||||
show_multiple_locations = %(show_multiple_locations)s
|
show_multiple_locations = %(show_multiple_locations)s
|
||||||
|
do_secure_hash = %(do_secure_hash)s
|
||||||
|
http_retries = %(http_retries)s
|
||||||
user_storage_quota = %(user_storage_quota)s
|
user_storage_quota = %(user_storage_quota)s
|
||||||
lock_path = %(lock_path)s
|
lock_path = %(lock_path)s
|
||||||
property_protection_file = %(property_protection_file)s
|
property_protection_file = %(property_protection_file)s
|
||||||
@ -623,6 +627,8 @@ image_cache_dir = %(image_cache_dir)s
|
|||||||
image_cache_driver = %(image_cache_driver)s
|
image_cache_driver = %(image_cache_driver)s
|
||||||
show_image_direct_url = %(show_image_direct_url)s
|
show_image_direct_url = %(show_image_direct_url)s
|
||||||
show_multiple_locations = %(show_multiple_locations)s
|
show_multiple_locations = %(show_multiple_locations)s
|
||||||
|
do_secure_hash = %(do_secure_hash)s
|
||||||
|
http_retries = %(http_retries)s
|
||||||
user_storage_quota = %(user_storage_quota)s
|
user_storage_quota = %(user_storage_quota)s
|
||||||
lock_path = %(lock_path)s
|
lock_path = %(lock_path)s
|
||||||
property_protection_file = %(property_protection_file)s
|
property_protection_file = %(property_protection_file)s
|
||||||
|
@ -16,10 +16,13 @@
|
|||||||
import http.client as http
|
import http.client as http
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def verify_image_hashes_and_status(
|
def verify_image_hashes_and_status(
|
||||||
test_obj, image_id, checksum=None, os_hash_value=None, status=None,
|
test_obj, image_id, checksum=None, os_hash_value=None, status=None,
|
||||||
@ -48,12 +51,15 @@ def verify_image_hashes_and_status(
|
|||||||
test_obj.assertEqual(size, image['size'])
|
test_obj.assertEqual(size, image['size'])
|
||||||
|
|
||||||
|
|
||||||
def wait_for_status(request_path, request_headers, status='active',
|
def wait_for_status(test_obj, request_path, request_headers,
|
||||||
max_sec=10, delay_sec=0.2, start_delay_sec=None):
|
status=None, max_sec=10, delay_sec=0.2,
|
||||||
|
start_delay_sec=None, multistore=False):
|
||||||
"""
|
"""
|
||||||
Performs a time-bounded wait for the entity at the request_path to
|
Performs a time-bounded wait for the entity at the request_path to
|
||||||
reach the requested status.
|
reach the requested status.
|
||||||
|
|
||||||
|
:param test_obj: The test object; expected to have _url() and
|
||||||
|
_headers() defined on it
|
||||||
:param request_path: path to use to make the request
|
:param request_path: path to use to make the request
|
||||||
:param request_headers: headers to use when making the request
|
:param request_headers: headers to use when making the request
|
||||||
:param status: the status to wait for (default: 'active')
|
:param status: the status to wait for (default: 'active')
|
||||||
@ -62,6 +68,7 @@ def wait_for_status(request_path, request_headers, status='active',
|
|||||||
made (default: 0.2)
|
made (default: 0.2)
|
||||||
:param start_delay_sec: seconds to wait before making the first
|
:param start_delay_sec: seconds to wait before making the first
|
||||||
request (default: None)
|
request (default: None)
|
||||||
|
:multistore: Optional flag if multiple backends enabled
|
||||||
:raises Exception: if the entity fails to reach the status within
|
:raises Exception: if the entity fails to reach the status within
|
||||||
the requested time or if the server returns something
|
the requested time or if the server returns something
|
||||||
other than a 200 response
|
other than a 200 response
|
||||||
@ -71,11 +78,18 @@ def wait_for_status(request_path, request_headers, status='active',
|
|||||||
if start_delay_sec:
|
if start_delay_sec:
|
||||||
time.sleep(start_delay_sec)
|
time.sleep(start_delay_sec)
|
||||||
while time.time() <= done_time:
|
while time.time() <= done_time:
|
||||||
|
if multistore:
|
||||||
|
resp = test_obj.api_get(request_path, headers=request_headers)
|
||||||
|
else:
|
||||||
resp = requests.get(request_path, headers=request_headers)
|
resp = requests.get(request_path, headers=request_headers)
|
||||||
if resp.status_code != http.OK:
|
if resp.status_code != http.OK:
|
||||||
raise Exception("Received {} response from server".format(
|
raise Exception("Received {} response from server".format(
|
||||||
resp.status_code))
|
resp.status_code))
|
||||||
|
test_obj.assertEqual(http.OK, resp.status_code)
|
||||||
entity = jsonutils.loads(resp.text)
|
entity = jsonutils.loads(resp.text)
|
||||||
|
LOG.info('Image status is: %s', entity['status'])
|
||||||
|
if entity['checksum'] and entity['status'] == 'active':
|
||||||
|
return
|
||||||
if entity['status'] == status:
|
if entity['status'] == status:
|
||||||
return
|
return
|
||||||
time.sleep(delay_sec)
|
time.sleep(delay_sec)
|
||||||
|
@ -23,6 +23,9 @@ import urllib
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
|
import glance_store
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_limit import exception as ol_exc
|
from oslo_limit import exception as ol_exc
|
||||||
from oslo_limit import limit
|
from oslo_limit import limit
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
@ -30,12 +33,15 @@ from oslo_utils.secretutils import md5
|
|||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from glance.common import wsgi
|
||||||
from glance.quota import keystone as ks_quota
|
from glance.quota import keystone as ks_quota
|
||||||
from glance.tests import functional
|
from glance.tests import functional
|
||||||
from glance.tests.functional import ft_utils as func_utils
|
from glance.tests.functional import ft_utils as func_utils
|
||||||
from glance.tests import utils as test_utils
|
from glance.tests import utils as test_utils
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
TENANT1 = str(uuid.uuid4())
|
TENANT1 = str(uuid.uuid4())
|
||||||
TENANT2 = str(uuid.uuid4())
|
TENANT2 = str(uuid.uuid4())
|
||||||
TENANT3 = str(uuid.uuid4())
|
TENANT3 = str(uuid.uuid4())
|
||||||
@ -201,7 +207,7 @@ class TestImages(functional.FunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=10,
|
max_sec=10,
|
||||||
@ -342,7 +348,7 @@ class TestImages(functional.FunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=20,
|
max_sec=20,
|
||||||
@ -982,7 +988,7 @@ class TestImages(functional.FunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image['id'])
|
path = self._url('/v2/images/%s' % image['id'])
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=15,
|
max_sec=15,
|
||||||
@ -3692,6 +3698,286 @@ class TestImages(functional.FunctionalTest):
|
|||||||
response = requests.patch(path, headers=headers, data=data)
|
response = requests.patch(path, headers=headers, data=data)
|
||||||
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_true_negative(self):
|
||||||
|
self.start_servers(**self.__dict__.copy())
|
||||||
|
|
||||||
|
# Create an image
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
self.assertIsNone(image['size'])
|
||||||
|
|
||||||
|
# Add Location with non image owner
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT2})
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
|
||||||
|
data = jsonutils.dumps({'url': url})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.NOT_FOUND, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data
|
||||||
|
# Invalid os_hash_value
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': "sha512",
|
||||||
|
'os_hash_value': "dbc9e0f80d131e64b94913a7b40bb5"
|
||||||
|
}
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code,
|
||||||
|
response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data (without os_hash_algo)
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
validation_data = {'os_hash_value': expect_h}
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data &
|
||||||
|
# (invalid hash_algo)
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha123',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data
|
||||||
|
# (mismatch hash_value with hash algo)
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha256(r.content).hexdigest())
|
||||||
|
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
self.stop_servers()
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_true(self):
|
||||||
|
self.start_servers(**self.__dict__.copy())
|
||||||
|
|
||||||
|
# Create an image
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
# Add location with os_hash_algo other than sha512
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha256(r.content).hexdigest())
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha256',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=10,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1)
|
||||||
|
# Show Image
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
resp = requests.get(path, headers=headers)
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
|
||||||
|
# Add location with valid validation data
|
||||||
|
# os_hash_algo value sha512
|
||||||
|
# Create an image 2
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Show Image
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
resp = requests.get(path, headers=self._headers())
|
||||||
|
output = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual('queued', output['status'])
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=self._headers(),
|
||||||
|
status='active',
|
||||||
|
max_sec=10,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1)
|
||||||
|
# Show Image
|
||||||
|
resp = requests.get(path, headers=self._headers())
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = True
|
||||||
|
# without validation_data
|
||||||
|
# Create an image 3
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
self.assertIsNone(image['size'])
|
||||||
|
self.assertIsNone(image['virtual_size'])
|
||||||
|
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
data = jsonutils.dumps({'url': url})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=10,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1)
|
||||||
|
# Show Image
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
resp = requests.get(path, headers=headers)
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
|
||||||
|
self.stop_servers()
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_false(self):
|
||||||
|
self.api_server.do_secure_hash = False
|
||||||
|
self.start_servers(**self.__dict__.copy())
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = False
|
||||||
|
# with validation_data
|
||||||
|
# Create an image 1
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = jsonutils.dumps({'url': url,
|
||||||
|
'validation_data': validation_data})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=2,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1)
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = False
|
||||||
|
# without validation_data
|
||||||
|
# Create an image 2
|
||||||
|
path = self._url('/v2/images')
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = jsonutils.dumps({'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
self.assertIsNone(image['size'])
|
||||||
|
self.assertIsNone(image['virtual_size'])
|
||||||
|
|
||||||
|
url = 'http://127.0.0.1:%s/foo_image' % self.http_port0
|
||||||
|
path = self._url('/v2/images/%s/locations' % image_id)
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = jsonutils.dumps({'url': url})
|
||||||
|
response = requests.post(path, headers=headers, data=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=2,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1)
|
||||||
|
|
||||||
|
self.stop_servers()
|
||||||
|
|
||||||
|
|
||||||
class TestImagesIPv6(functional.FunctionalTest):
|
class TestImagesIPv6(functional.FunctionalTest):
|
||||||
"""Verify that API and REG servers running IPv6 can communicate"""
|
"""Verify that API and REG servers running IPv6 can communicate"""
|
||||||
@ -4640,7 +4926,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=15,
|
max_sec=15,
|
||||||
@ -4805,7 +5091,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=15,
|
max_sec=15,
|
||||||
@ -4967,7 +5253,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=20,
|
max_sec=20,
|
||||||
@ -5130,7 +5416,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=20,
|
max_sec=20,
|
||||||
@ -5292,7 +5578,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=40,
|
max_sec=40,
|
||||||
@ -5456,7 +5742,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=40,
|
max_sec=40,
|
||||||
@ -5680,7 +5966,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=40,
|
max_sec=40,
|
||||||
@ -5938,7 +6224,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=40,
|
max_sec=40,
|
||||||
@ -6790,7 +7076,7 @@ class TestCopyImagePermissions(functional.MultipleBackendFunctionalTest):
|
|||||||
# NOTE(abhishekk): As import is a async call we need to provide
|
# NOTE(abhishekk): As import is a async call we need to provide
|
||||||
# some timelap to complete the call.
|
# some timelap to complete the call.
|
||||||
path = self._url('/v2/images/%s' % image_id)
|
path = self._url('/v2/images/%s' % image_id)
|
||||||
func_utils.wait_for_status(request_path=path,
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
request_headers=self._headers(),
|
request_headers=self._headers(),
|
||||||
status='active',
|
status='active',
|
||||||
max_sec=40,
|
max_sec=40,
|
||||||
@ -7352,3 +7638,317 @@ class TestStoreWeight(functional.SynchronousAPIBase):
|
|||||||
# as store3,store1,store2
|
# as store3,store1,store2
|
||||||
image = self.api_get('/v2/images/%s' % image_id).json
|
image = self.api_get('/v2/images/%s' % image_id).json
|
||||||
self.assertEqual("store3,store1,store2", image['stores'])
|
self.assertEqual("store3,store1,store2", image['stores'])
|
||||||
|
|
||||||
|
|
||||||
|
class TestMultipleBackendsLocationApi(functional.SynchronousAPIBase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestMultipleBackendsLocationApi, self).setUp()
|
||||||
|
self.start_server()
|
||||||
|
for i in range(3):
|
||||||
|
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
||||||
|
"foo_image%d" % i)
|
||||||
|
setattr(self, 'http_server%d' % i, ret[1])
|
||||||
|
setattr(self, 'http_port%d' % i, ret[2])
|
||||||
|
|
||||||
|
def setup_stores(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _headers(self, custom_headers=None):
|
||||||
|
base_headers = {
|
||||||
|
'X-Identity-Status': 'Confirmed',
|
||||||
|
'X-Auth-Token': '932c5c84-02ac-4fe5-a9ba-620af0e2bb96',
|
||||||
|
'X-User-Id': 'f9a41d13-0c13-47e9-bee2-ce4e8bfe958e',
|
||||||
|
'X-Tenant-Id': TENANT1,
|
||||||
|
'X-Roles': 'reader,member',
|
||||||
|
}
|
||||||
|
base_headers.update(custom_headers or {})
|
||||||
|
return base_headers
|
||||||
|
|
||||||
|
def _setup_multiple_stores(self):
|
||||||
|
self.ksa_client = self.useFixture(
|
||||||
|
fixtures.MockPatch('glance.context.get_ksa_client')).mock
|
||||||
|
self.config(enabled_backends={'store1': 'http', 'store2': 'http'})
|
||||||
|
glance_store.register_store_opts(CONF,
|
||||||
|
reserved_stores=wsgi.RESERVED_STORES)
|
||||||
|
|
||||||
|
self.config(default_backend='store1',
|
||||||
|
group='glance_store')
|
||||||
|
self.config(filesystem_store_datadir=self._store_dir('staging'),
|
||||||
|
group='os_glance_staging_store')
|
||||||
|
self.config(filesystem_store_datadir='/tmp/foo',
|
||||||
|
group='os_glance_tasks_store')
|
||||||
|
|
||||||
|
glance_store.create_multi_stores(CONF,
|
||||||
|
reserved_stores=wsgi.RESERVED_STORES)
|
||||||
|
glance_store.verify_store()
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_false(self):
|
||||||
|
self.config(do_secure_hash=False)
|
||||||
|
self._setup_multiple_stores()
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = False
|
||||||
|
# with validation_data
|
||||||
|
# Create an image 1
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
self.assertIsNone(image['size'])
|
||||||
|
self.assertIsNone(image['virtual_size'])
|
||||||
|
|
||||||
|
url = 'http://127.0.0.1:%s/store1/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = {'url': url,
|
||||||
|
'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=5,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1,
|
||||||
|
multistore=True)
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = False
|
||||||
|
# without validation_data
|
||||||
|
# Create an image 2
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
self.assertIsNone(image['size'])
|
||||||
|
self.assertIsNone(image['virtual_size'])
|
||||||
|
|
||||||
|
url = 'http://127.0.0.1:%s/store1/foo_image' % self.http_port0
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = {'url': url}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=5,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1, multistore=True)
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_true_negative(self):
|
||||||
|
self._setup_multiple_stores()
|
||||||
|
|
||||||
|
# Create an image
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
# Add Location with non image owner
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT2})
|
||||||
|
url = 'http://127.0.0.1:%s/store1/foo_image' % self.http_port0
|
||||||
|
|
||||||
|
data = {'url': url}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.NOT_FOUND, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data
|
||||||
|
# Invalid os_hash_value
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': "sha512",
|
||||||
|
'os_hash_value': "dbc9e0f80d131e64b94913a7b40bb5"
|
||||||
|
}
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code,
|
||||||
|
response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data (without os_hash_algo)
|
||||||
|
url = 'http://127.0.0.1:%s/store1/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
validation_data = {'os_hash_value': expect_h}
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data &
|
||||||
|
# (invalid hash_algo)
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha123',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Add location with invalid validation_data
|
||||||
|
# (mismatch hash_value with hash algo)
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_h = str(hashlib.sha256(r.content).hexdigest())
|
||||||
|
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.BAD_REQUEST, response.status_code, response.text)
|
||||||
|
|
||||||
|
def test_add_location_with_do_secure_hash_true(self):
|
||||||
|
self._setup_multiple_stores()
|
||||||
|
|
||||||
|
# Create an image
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
# Add location with os_hash_algo other than sha512
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/store1/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha256(r.content).hexdigest())
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha256',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=20,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1, multistore=True)
|
||||||
|
# Show Image
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
resp = self.api_get(path, headers=self._headers())
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
|
||||||
|
# Add location with valid validation data
|
||||||
|
# os_hash_algo value sha512
|
||||||
|
# Create an image 3
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/store2/foo_image' % self.http_port0
|
||||||
|
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
validation_data = {
|
||||||
|
'os_hash_algo': 'sha512',
|
||||||
|
'os_hash_value': expect_h}
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
data = {'url': url, 'validation_data': validation_data}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
|
||||||
|
# Show Image
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
resp = self.api_get(path, headers=self._headers())
|
||||||
|
output = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual('queued', output['status'])
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=10,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1, multistore=True)
|
||||||
|
# Show Image
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
resp = self.api_get(path, headers=self._headers())
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
|
||||||
|
# Add Location with valid URL and do_secure_hash = True
|
||||||
|
# without validation_data
|
||||||
|
# Create an image 4
|
||||||
|
path = '/v2/images'
|
||||||
|
headers = self._headers({'content-type': 'application/json'})
|
||||||
|
data = {'name': 'image-1', 'disk_format': 'aki',
|
||||||
|
'container_format': 'aki'}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.CREATED, response.status_code)
|
||||||
|
|
||||||
|
# Returned image entity should have a generated id and status
|
||||||
|
image = jsonutils.loads(response.text)
|
||||||
|
image_id = image['id']
|
||||||
|
self.assertEqual('queued', image['status'])
|
||||||
|
|
||||||
|
path = '/v2/images/%s/locations' % image_id
|
||||||
|
headers = self._headers({'X-Tenant-Id': TENANT1})
|
||||||
|
url = 'http://127.0.0.1:%s/store2/foo_image' % self.http_port0
|
||||||
|
with requests.get(url) as r:
|
||||||
|
expect_c = str(md5(r.content, usedforsecurity=False).hexdigest())
|
||||||
|
expect_h = str(hashlib.sha512(r.content).hexdigest())
|
||||||
|
data = {'url': url}
|
||||||
|
response = self.api_post(path, headers=headers, json=data)
|
||||||
|
self.assertEqual(http.ACCEPTED, response.status_code, response.text)
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
func_utils.wait_for_status(self, request_path=path,
|
||||||
|
request_headers=headers,
|
||||||
|
status='active',
|
||||||
|
max_sec=10,
|
||||||
|
delay_sec=0.2,
|
||||||
|
start_delay_sec=1, multistore=True)
|
||||||
|
# Show Image
|
||||||
|
path = '/v2/images/%s' % image_id
|
||||||
|
resp = self.api_get(path, headers=self._headers())
|
||||||
|
image = jsonutils.loads(resp.text)
|
||||||
|
self.assertEqual(expect_c, image['checksum'])
|
||||||
|
self.assertEqual(expect_h, image['os_hash_value'])
|
||||||
|
Loading…
Reference in New Issue
Block a user