Merge "Avoid error 414 when retrieving subnet cidrs for ListNetworks"
This commit is contained in:
commit
5011a21cb3
|
@ -140,12 +140,14 @@ class HTTPClient(httplib2.Http):
|
|||
raise exceptions.Forbidden(message=body)
|
||||
return resp, body
|
||||
|
||||
def do_request(self, url, method, **kwargs):
|
||||
def authenticate_and_fetch_endpoint_url(self):
|
||||
if not self.auth_token:
|
||||
self.authenticate()
|
||||
elif not self.endpoint_url:
|
||||
self.endpoint_url = self._get_endpoint_url()
|
||||
|
||||
def do_request(self, url, method, **kwargs):
|
||||
self.authenticate_and_fetch_endpoint_url()
|
||||
# Perform the request once. If we get a 401 back then it
|
||||
# might be because the auth token expired, so try to
|
||||
# re-authenticate and try again. If it still fails, bail.
|
||||
|
|
|
@ -131,6 +131,14 @@ class QuantumCLIError(QuantumClientException):
|
|||
pass
|
||||
|
||||
|
||||
class RequestURITooLong(QuantumClientException):
|
||||
"""Raised when a request fails with HTTP error 414."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.excess = kwargs.get('excess', 0)
|
||||
super(RequestURITooLong, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class ConnectionFailed(QuantumClientException):
|
||||
message = _("Connection to quantum failed: %(reason)s")
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import argparse
|
||||
import logging
|
||||
|
||||
from quantumclient.common import exceptions
|
||||
from quantumclient.quantum.v2_0 import CreateCommand
|
||||
from quantumclient.quantum.v2_0 import DeleteCommand
|
||||
from quantumclient.quantum.v2_0 import ListCommand
|
||||
|
@ -36,6 +37,9 @@ def _format_subnets(network):
|
|||
class ListNetwork(ListCommand):
|
||||
"""List networks that belong to a given tenant."""
|
||||
|
||||
# Length of a query filter on subnet id
|
||||
# id=<uuid>& (with len(uuid)=36)
|
||||
subnet_id_filter_len = 40
|
||||
resource = 'network'
|
||||
log = logging.getLogger(__name__ + '.ListNetwork')
|
||||
_formatters = {'subnets': _format_subnets, }
|
||||
|
@ -55,8 +59,27 @@ class ListNetwork(ListCommand):
|
|||
for n in data:
|
||||
if 'subnets' in n:
|
||||
subnet_ids.extend(n['subnets'])
|
||||
search_opts.update({'id': subnet_ids})
|
||||
subnets = quantum_client.list_subnets(**search_opts).get('subnets', [])
|
||||
|
||||
def _get_subnet_list(sub_ids):
|
||||
search_opts['id'] = sub_ids
|
||||
return quantum_client.list_subnets(
|
||||
**search_opts).get('subnets', [])
|
||||
|
||||
try:
|
||||
subnets = _get_subnet_list(subnet_ids)
|
||||
except exceptions.RequestURITooLong as uri_len_exc:
|
||||
# The URI is too long because of too many subnet_id filters
|
||||
# Use the excess attribute of the exception to know how many
|
||||
# subnet_id filters can be inserted into a single request
|
||||
subnet_count = len(subnet_ids)
|
||||
max_size = ((self.subnet_id_filter_len * subnet_count) -
|
||||
uri_len_exc.excess)
|
||||
chunk_size = max_size / self.subnet_id_filter_len
|
||||
subnets = []
|
||||
for i in xrange(0, subnet_count, chunk_size):
|
||||
subnets.extend(
|
||||
_get_subnet_list(subnet_ids[i: i + chunk_size]))
|
||||
|
||||
subnet_dict = dict([(s['id'], s) for s in subnets])
|
||||
for n in data:
|
||||
if 'subnets' in n:
|
||||
|
|
|
@ -199,6 +199,8 @@ class Client(object):
|
|||
'health_monitors': 'health_monitor',
|
||||
'quotas': 'quota',
|
||||
}
|
||||
# 8192 Is the default max URI len for eventlet.wsgi.server
|
||||
MAX_URI_LEN = 8192
|
||||
|
||||
def get_attr_metadata(self):
|
||||
if self.format == 'json':
|
||||
|
@ -912,6 +914,12 @@ class Client(object):
|
|||
if type(params) is dict and params:
|
||||
params = utils.safe_encode_dict(params)
|
||||
action += '?' + urllib.urlencode(params, doseq=1)
|
||||
# Ensure client always has correct uri - do not guesstimate anything
|
||||
self.httpclient.authenticate_and_fetch_endpoint_url()
|
||||
uri_len = len(self.httpclient.endpoint_url) + len(action)
|
||||
if uri_len > self.MAX_URI_LEN:
|
||||
raise exceptions.RequestURITooLong(
|
||||
excess=uri_len - self.MAX_URI_LEN)
|
||||
if body:
|
||||
body = self.serialize(body)
|
||||
self.httpclient.content_type = self.content_type()
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# Copyright 2012 OpenStack LLC.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -473,6 +472,72 @@ class CLITestV20NetworkJSON(CLITestV20Base):
|
|||
args = [myid]
|
||||
self._test_delete_resource(resource, cmd, myid, args)
|
||||
|
||||
def _test_extend_list(self, mox_calls):
|
||||
data = [{'id': 'netid%d' % i, 'name': 'net%d' % i,
|
||||
'subnets': ['mysubid%d' % i]}
|
||||
for i in range(0, 10)]
|
||||
self.mox.StubOutWithMock(self.client.httpclient, "request")
|
||||
path = getattr(self.client, 'subnets_path')
|
||||
cmd = ListNetwork(MyApp(sys.stdout), None)
|
||||
self.mox.StubOutWithMock(cmd, "get_client")
|
||||
cmd.get_client().MultipleTimes().AndReturn(self.client)
|
||||
mox_calls(path, data)
|
||||
self.mox.ReplayAll()
|
||||
known_args, _vs = cmd.get_parser('create_subnets').parse_known_args()
|
||||
cmd.extend_list(data, known_args)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def _build_test_data(self, data):
|
||||
subnet_ids = []
|
||||
response = []
|
||||
filters = ""
|
||||
for n in data:
|
||||
if 'subnets' in n:
|
||||
subnet_ids.extend(n['subnets'])
|
||||
for subnet_id in n['subnets']:
|
||||
filters = "%s&id=%s" % (filters, subnet_id)
|
||||
response.append({'id': subnet_id,
|
||||
'cidr': '192.168.0.0/16'})
|
||||
resp_str = self.client.serialize({'subnets': response})
|
||||
resp = (test_cli20.MyResp(200), resp_str)
|
||||
return filters, resp
|
||||
|
||||
def test_extend_list(self):
|
||||
def mox_calls(path, data):
|
||||
filters, response = self._build_test_data(data)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.end_url(path, 'fields=id&fields=cidr' + filters),
|
||||
'GET',
|
||||
body=None,
|
||||
headers=ContainsKeyValue(
|
||||
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
|
||||
|
||||
self._test_extend_list(mox_calls)
|
||||
|
||||
def test_extend_list_exceed_max_uri_len(self):
|
||||
def mox_calls(path, data):
|
||||
sub_data_lists = [data[:len(data) - 1], data[len(data) - 1:]]
|
||||
filters, response = self._build_test_data(data)
|
||||
# 1 char of extra URI len will cause a split in 2 requests
|
||||
self.client.httpclient.request(
|
||||
test_cli20.end_url(path, 'fields=id&fields=cidr%s' % filters),
|
||||
'GET',
|
||||
body=None,
|
||||
headers=ContainsKeyValue(
|
||||
'X-Auth-Token', test_cli20.TOKEN)).AndRaise(
|
||||
exceptions.RequestURITooLong(excess=1))
|
||||
for data in sub_data_lists:
|
||||
filters, response = self._build_test_data(data)
|
||||
self.client.httpclient.request(
|
||||
test_cli20.end_url(path,
|
||||
'fields=id&fields=cidr%s' % filters),
|
||||
'GET',
|
||||
body=None,
|
||||
headers=ContainsKeyValue(
|
||||
'X-Auth-Token', test_cli20.TOKEN)).AndReturn(response)
|
||||
|
||||
self._test_extend_list(mox_calls)
|
||||
|
||||
|
||||
class CLITestV20NetworkXML(CLITestV20NetworkJSON):
|
||||
format = 'xml'
|
||||
|
|
Loading…
Reference in New Issue