Implement volume list availability zone filter

Currently volume list supports most of the filters since it
directly passes filter parameters to bottom pods. But
availability zone filter can not be implememted like that
because we don't require top pod and bottom pod to have
availability zones with the same name. Since each bottom pod
belongs to an availability zone defined in top pod, instead
of passing the availability zone filter to bottom pods, we
directly filter bottom pods by the availability zone filter
then send list request to these filtered bottom pods.

With change in this patch, tempest test test_volumes_list can
be passed.

Change-Id: I8e448a1262ca370ff3bfa9948fee636240a7fb78
This commit is contained in:
zhiyuan_cai 2016-04-27 10:07:23 +08:00
parent 353ebcddb1
commit fa9db37e64
3 changed files with 124 additions and 8 deletions

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import urlparse
import pecan
from pecan import expose
from pecan import request
@ -220,6 +222,16 @@ class VolumeController(rest.RestController):
if pod['pod_name'] == '':
continue
query = urlparse.urlsplit(request.url).query
query_filters = urlparse.parse_qsl(query)
skip_pod = False
for k, v in query_filters:
if k == 'availability_zone' and v != pod['az_name']:
skip_pod = True
break
if skip_pod:
continue
s_ctx = hclient.get_pod_service_ctx(
context,
request.url,

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import urllib
import urlparse
from requests import Request
@ -87,7 +88,17 @@ def get_bottom_url(t_ver, t_url, b_ver, b_endpoint):
path = '/' + b_ver + '/' + after_ver
if b_ver == '':
path = '/' + after_ver
query = t_parse.query
# Remove availability_zone filter since it is handled by VolumeController.
# VolumeController will send GET request only to bottom pods whose AZ
# is specified in availability_zone filter.
query_filters = []
for k, v in urlparse.parse_qsl(t_parse.query):
if k == 'availability_zone':
continue
query_filters.append((k, v))
query = urllib.urlencode(query_filters)
fragment = t_parse.fragment
b_url = urlparse.urlunsplit((scheme,

View File

@ -14,6 +14,7 @@
# under the License.
from mock import patch
import urlparse
import pecan
from pecan.configuration import set_config
@ -49,6 +50,7 @@ def fake_volumes_forward_req(ctx, action, b_header, b_url, b_req_body):
resp = Response()
resp.status_code = 404
parse = urlparse.urlsplit(b_url)
if action == 'POST':
b_body = jsonutils.loads(b_req_body)
if b_body.get('volume'):
@ -56,7 +58,7 @@ def fake_volumes_forward_req(ctx, action, b_header, b_url, b_req_body):
vol['id'] = uuidutils.generate_uuid()
stored_vol = {
'volume': vol,
'url': b_url
'host': parse.hostname
}
fake_volumes.append(stored_vol)
resp.status_code = 202
@ -66,17 +68,16 @@ def fake_volumes_forward_req(ctx, action, b_header, b_url, b_req_body):
# resp.json = vol_dict
return resp
pos = b_url.rfind('/volumes')
b_path = parse.path
pos = b_path.rfind('/volumes')
op = ''
cmp_url = b_url
if pos > 0:
op = b_url[pos:]
cmp_url = b_url[:pos] + '/volumes'
op = b_path[pos:]
op = op[len('/volumes'):]
if action == 'GET':
if op == '' or op == '/detail':
tenant_id = b_url[:pos]
tenant_id = b_path[:pos]
pos2 = tenant_id.rfind('/')
if pos2 > 0:
tenant_id = tenant_id[(pos2 + 1):]
@ -84,8 +85,9 @@ def fake_volumes_forward_req(ctx, action, b_header, b_url, b_req_body):
resp.status_code = 404
return resp
ret_vols = []
cmp_host = parse.hostname
for temp_vol in fake_volumes:
if temp_vol['url'] != cmp_url:
if temp_vol['host'] != cmp_host:
continue
if temp_vol['volume']['project_id'] == tenant_id:
@ -210,6 +212,7 @@ class CinderVolumeFunctionalTest(base.TestCase):
cfg.CONF.unregister_opts(app.common_opts)
pecan.set_config({}, overwrite=True)
core.ModelBase.metadata.drop_all(core.get_engine())
del fake_volumes[:]
class TestVolumeController(CinderVolumeFunctionalTest):
@ -396,6 +399,96 @@ class TestVolumeController(CinderVolumeFunctionalTest):
vols = json_body.get('volumes')
self.assertEqual(0, len(vols))
@patch.object(hclient, 'forward_req',
new=fake_volumes_forward_req)
def test_get_all(self):
update_dict = {'pod_az_name': 'fake_pod_az2'}
# update pod2 to set pod_az_name
db_api.update_pod(self.context, 'fake_pod_id2', update_dict)
volumes = [
# normal volume with correct parameter
{
"volume":
{
"name": 'vol_1',
"availability_zone": FAKE_AZ,
"source_volid": '',
"consistencygroup_id": '',
"snapshot_id": '',
"source_replica": '',
"size": 10,
"user_id": '',
"imageRef": '',
"attach_status": "detached",
"volume_type": '',
"project_id": 'my_tenant_id',
"metadata": {}
},
"expected_error": 202
},
# same tenant, multiple volumes
{
"volume":
{
"name": 'vol_2',
"availability_zone": FAKE_AZ,
"source_volid": '',
"consistencygroup_id": '',
"snapshot_id": '',
"source_replica": '',
"size": 20,
"user_id": '',
"imageRef": '',
"attach_status": "detached",
"volume_type": '',
"project_id": 'my_tenant_id',
"metadata": {}
},
"expected_error": 202
},
# same tenant, different az
{
"volume":
{
"name": 'vol_3',
"availability_zone": FAKE_AZ + '2',
"source_volid": '',
"consistencygroup_id": '',
"snapshot_id": '',
"source_replica": '',
"size": 20,
"user_id": '',
"imageRef": '',
"attach_status": "detached",
"volume_type": '',
"project_id": 'my_tenant_id',
"metadata": {}
},
"expected_error": 202
},
]
tenant_id = 'my_tenant_id'
for volume in volumes:
self.app.post_json('/v2/' + tenant_id + '/volumes',
dict(volume=volume['volume']),
expect_errors=True)
query_string = '?availability_zone=' + FAKE_AZ
resp = self.app.get('/v2/' + tenant_id + '/volumes' + query_string)
self.assertEqual(resp.status_int, 200)
json_body = jsonutils.loads(resp.body)
ret_vols = json_body.get('volumes')
self.assertEqual(len(ret_vols), 2)
query_string = '?availability_zone=' + FAKE_AZ + '2'
resp = self.app.get('/v2/' + tenant_id + '/volumes' + query_string)
self.assertEqual(resp.status_int, 200)
json_body = jsonutils.loads(resp.body)
ret_vols = json_body.get('volumes')
self.assertEqual(len(ret_vols), 1)
def _test_and_check(self, volumes, tenant_id):
for test_vol in volumes:
if test_vol.get('volume'):