Make location API compatible with multiple store
In case of Multiple store while adding location to image if backend store is not provided it returns 400 BadRequest saying 'Invalid location'. Made changes to loaction API to findout the store from location URI and add it as a backend store to the location metadata. DocImpact Closes-Bug: #1802587 Co-Authored-By: Victor Coutellier <victor.coutellier@gmail.com> Change-Id: If6d0348346d2086a2500b0012a0e81e80cea7395
This commit is contained in:
parent
0e5984edc7
commit
4e070fd6e0
|
@ -31,6 +31,7 @@ from glance.api import common
|
|||
from glance.api import policy
|
||||
from glance.common import exception
|
||||
from glance.common import location_strategy
|
||||
from glance.common import store_utils
|
||||
from glance.common import timeutils
|
||||
from glance.common import utils
|
||||
from glance.common import wsgi
|
||||
|
@ -475,6 +476,9 @@ class ImagesController(object):
|
|||
raise webob.exc.HTTPConflict(explanation=msg)
|
||||
|
||||
val_data = self._validate_validation_data(image, value)
|
||||
# NOTE(abhishekk): get glance store based on location uri
|
||||
if CONF.enabled_backends:
|
||||
store_utils.update_store_in_locations(value, image.image_id)
|
||||
|
||||
try:
|
||||
# NOTE(flwang): _locations_proxy's setattr method will check if
|
||||
|
@ -502,6 +506,9 @@ class ImagesController(object):
|
|||
raise webob.exc.HTTPConflict(explanation=msg)
|
||||
|
||||
val_data = self._validate_validation_data(image, [value])
|
||||
# NOTE(abhishekk): get glance store based on location uri
|
||||
if CONF.enabled_backends:
|
||||
store_utils.update_store_in_locations([value], image.image_id)
|
||||
|
||||
pos = self._get_locations_op_pos(path_pos,
|
||||
len(image.locations), True)
|
||||
|
|
|
@ -23,10 +23,38 @@ import six
|
|||
import webob
|
||||
|
||||
from glance.common import exception
|
||||
from glance.common import store_utils
|
||||
from glance.common import utils
|
||||
from glance.tests import utils as test_utils
|
||||
|
||||
|
||||
class TestStoreUtils(test_utils.BaseTestCase):
|
||||
"""Test glance.common.store_utils module"""
|
||||
|
||||
def _test_update_store_in_location(self, metadata, store_id, expected):
|
||||
locations = [{
|
||||
'url': 'rbd://aaaaaaaa/images/id',
|
||||
'metadata': metadata
|
||||
}]
|
||||
with mock.patch.object(
|
||||
store_utils, '_get_store_id_from_uri') as mock_get_store_id:
|
||||
mock_get_store_id.return_value = store_id
|
||||
store_utils.update_store_in_locations(locations, mock.Mock())
|
||||
self.assertEqual(locations[0]['metadata'].get('store'), expected)
|
||||
|
||||
def test_update_store_location_with_no_store(self):
|
||||
self._test_update_store_in_location({}, 'rbd1', 'rbd1')
|
||||
|
||||
def test_update_store_location_with_different_store(self):
|
||||
self._test_update_store_in_location({'store': 'rbd2'}, 'rbd1', 'rbd1')
|
||||
|
||||
def test_update_store_location_with_same_store(self):
|
||||
self._test_update_store_in_location({'store': 'rbd1'}, 'rbd1', 'rbd1')
|
||||
|
||||
def test_update_store_location_with_store_none(self):
|
||||
self._test_update_store_in_location({}, None, None)
|
||||
|
||||
|
||||
class TestUtils(test_utils.BaseTestCase):
|
||||
"""Test routines in glance.utils"""
|
||||
|
||||
|
|
|
@ -1780,6 +1780,112 @@ class TestImagesController(base.IsolatedUnitTest):
|
|||
self.assertEqual('sha512', output.os_hash_algo)
|
||||
self.assertEqual(MULTIHASH1, output.os_hash_value)
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||
@mock.patch.object(store, 'get_size_from_backend')
|
||||
def test_replace_locations_identify_associated_store(
|
||||
self, mock_get_size, mock_get_size_uri, mock_set_acls,
|
||||
mock_check_loc, mock_calc):
|
||||
mock_calc.return_value = 1
|
||||
mock_get_size.return_value = 1
|
||||
mock_get_size_uri.return_value = 1
|
||||
self.config(show_multiple_locations=True)
|
||||
self.config(enabled_backends={'fake-store': 'http'})
|
||||
image_id = str(uuid.uuid4())
|
||||
self.images = [
|
||||
_db_fixture(image_id, owner=TENANT1,
|
||||
name='1',
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='queued',
|
||||
checksum=None,
|
||||
os_hash_algo=None,
|
||||
os_hash_value=None),
|
||||
]
|
||||
self.db.image_create(None, self.images[0])
|
||||
request = unit_test_utils.get_fake_request()
|
||||
new_location1 = {'url': '%s/fake_location_1' % BASE_URI,
|
||||
'metadata': {},
|
||||
'validation_data': {'checksum': CHKSUM,
|
||||
'os_hash_algo': 'sha512',
|
||||
'os_hash_value': MULTIHASH1}}
|
||||
new_location2 = {'url': '%s/fake_location_2' % BASE_URI,
|
||||
'metadata': {},
|
||||
'validation_data': {'checksum': CHKSUM,
|
||||
'os_hash_algo': 'sha512',
|
||||
'os_hash_value': MULTIHASH1}}
|
||||
changes = [{'op': 'replace', 'path': ['locations'],
|
||||
'value': [new_location1, new_location2]}]
|
||||
|
||||
with mock.patch.object(store_utils,
|
||||
'_get_store_id_from_uri') as mock_store:
|
||||
mock_store.return_value = 'fake-store'
|
||||
# ensure location metadata is updated
|
||||
new_location1['metadata']['store'] = 'fake-store'
|
||||
new_location1['metadata']['store'] = 'fake-store'
|
||||
|
||||
output = self.controller.update(request, image_id, changes)
|
||||
self.assertEqual(2, len(output.locations))
|
||||
self.assertEqual(image_id, output.image_id)
|
||||
self.assertEqual(new_location1, output.locations[0])
|
||||
self.assertEqual(new_location2, output.locations[1])
|
||||
self.assertEqual('active', output.status)
|
||||
self.assertEqual(CHKSUM, output.checksum)
|
||||
self.assertEqual('sha512', output.os_hash_algo)
|
||||
self.assertEqual(MULTIHASH1, output.os_hash_value)
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||
@mock.patch.object(store, 'get_size_from_backend')
|
||||
def test_replace_locations_unknon_locations(
|
||||
self, mock_get_size, mock_get_size_uri, mock_set_acls,
|
||||
mock_check_loc, mock_calc):
|
||||
mock_calc.return_value = 1
|
||||
mock_get_size.return_value = 1
|
||||
mock_get_size_uri.return_value = 1
|
||||
self.config(show_multiple_locations=True)
|
||||
self.config(enabled_backends={'fake-store': 'http'})
|
||||
image_id = str(uuid.uuid4())
|
||||
self.images = [
|
||||
_db_fixture(image_id, owner=TENANT1,
|
||||
name='1',
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='queued',
|
||||
checksum=None,
|
||||
os_hash_algo=None,
|
||||
os_hash_value=None),
|
||||
]
|
||||
self.db.image_create(None, self.images[0])
|
||||
request = unit_test_utils.get_fake_request()
|
||||
new_location1 = {'url': 'unknown://whocares',
|
||||
'metadata': {},
|
||||
'validation_data': {'checksum': CHKSUM,
|
||||
'os_hash_algo': 'sha512',
|
||||
'os_hash_value': MULTIHASH1}}
|
||||
new_location2 = {'url': 'unknown://whatever',
|
||||
'metadata': {'store': 'unkstore'},
|
||||
'validation_data': {'checksum': CHKSUM,
|
||||
'os_hash_algo': 'sha512',
|
||||
'os_hash_value': MULTIHASH1}}
|
||||
changes = [{'op': 'replace', 'path': ['locations'],
|
||||
'value': [new_location1, new_location2]}]
|
||||
|
||||
output = self.controller.update(request, image_id, changes)
|
||||
self.assertEqual(2, len(output.locations))
|
||||
self.assertEqual(image_id, output.image_id)
|
||||
self.assertEqual('active', output.status)
|
||||
self.assertEqual(CHKSUM, output.checksum)
|
||||
self.assertEqual('sha512', output.os_hash_algo)
|
||||
self.assertEqual(MULTIHASH1, output.os_hash_value)
|
||||
# ensure location metadata is same
|
||||
self.assertEqual(new_location1, output.locations[0])
|
||||
self.assertEqual(new_location2, output.locations[1])
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
|
@ -1898,6 +2004,82 @@ class TestImagesController(base.IsolatedUnitTest):
|
|||
self.assertEqual(new_location, output.locations[0])
|
||||
self.assertEqual('active', output.status)
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||
@mock.patch.object(store, 'get_size_from_backend')
|
||||
def test_add_location_identify_associated_store(
|
||||
self, mock_get_size, mock_get_size_uri, mock_set_acls,
|
||||
mock_check_loc, mock_calc):
|
||||
mock_calc.return_value = 1
|
||||
mock_get_size.return_value = 1
|
||||
mock_get_size_uri.return_value = 1
|
||||
self.config(show_multiple_locations=True)
|
||||
self.config(enabled_backends={'fake-store': 'http'})
|
||||
image_id = str(uuid.uuid4())
|
||||
self.images = [
|
||||
_db_fixture(image_id, owner=TENANT1, checksum=CHKSUM,
|
||||
name='1',
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='queued'),
|
||||
]
|
||||
self.db.image_create(None, self.images[0])
|
||||
request = unit_test_utils.get_fake_request()
|
||||
new_location = {'url': '%s/fake_location_1' % BASE_URI,
|
||||
'metadata': {}}
|
||||
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||
'value': new_location}]
|
||||
with mock.patch.object(store_utils,
|
||||
'_get_store_id_from_uri') as mock_store:
|
||||
mock_store.return_value = 'fake-store'
|
||||
output = self.controller.update(request, image_id, changes)
|
||||
|
||||
self.assertEqual(image_id, output.image_id)
|
||||
self.assertEqual(1, len(output.locations))
|
||||
self.assertEqual('active', output.status)
|
||||
# ensure location metadata is updated
|
||||
new_location['metadata']['store'] = 'fake-store'
|
||||
self.assertEqual(new_location, output.locations[0])
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
@mock.patch.object(store, 'get_size_from_uri_and_backend')
|
||||
@mock.patch.object(store, 'get_size_from_backend')
|
||||
def test_add_location_unknown_locations(
|
||||
self, mock_get_size, mock_get_size_uri, mock_set_acls,
|
||||
mock_check_loc, mock_calc):
|
||||
mock_calc.return_value = 1
|
||||
mock_get_size.return_value = 1
|
||||
mock_get_size_uri.return_value = 1
|
||||
self.config(show_multiple_locations=True)
|
||||
self.config(enabled_backends={'fake-store': 'http'})
|
||||
image_id = str(uuid.uuid4())
|
||||
|
||||
self.images = [
|
||||
_db_fixture(image_id, owner=TENANT1, checksum=CHKSUM,
|
||||
name='1',
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='queued'),
|
||||
]
|
||||
self.db.image_create(None, self.images[0])
|
||||
|
||||
new_location = {'url': 'unknown://whocares', 'metadata': {}}
|
||||
request = unit_test_utils.get_fake_request()
|
||||
changes = [{'op': 'add', 'path': ['locations', '-'],
|
||||
'value': new_location}]
|
||||
|
||||
output = self.controller.update(request, image_id, changes)
|
||||
|
||||
self.assertEqual(image_id, output.image_id)
|
||||
self.assertEqual('active', output.status)
|
||||
self.assertEqual(1, len(output.locations))
|
||||
# ensure location metadata is same
|
||||
self.assertEqual(new_location, output.locations[0])
|
||||
|
||||
@mock.patch.object(glance.quota, '_calc_required_size')
|
||||
@mock.patch.object(glance.location, '_check_image_location')
|
||||
@mock.patch.object(glance.location.ImageRepoProxy, '_set_acls')
|
||||
|
|
Loading…
Reference in New Issue