summaryrefslogtreecommitdiff
path: root/quantumclient
diff options
context:
space:
mode:
authorSalvatore Orlando <salv.orlando@gmail.com>2012-07-24 23:45:10 -0700
committerSalvatore Orlando <salv.orlando@gmail.com>2012-07-26 03:23:46 -0700
commitd16e00a056bbe7ba576c4b7195180bfc383bbfad (patch)
tree018f3cb8bb61a65eec4a9d13fc9bbcf79ba58561 /quantumclient
parentd70620ce9665738965f8d9953ba9c81ec1a4e1c2 (diff)
downloadpython-neutronclient-d16e00a056bbe7ba576c4b7195180bfc383bbfad.tar.gz
Allow to retrieve objects by name
Fixes bug 979527 xxx-show commands now can accept either an id or a name of the resource to retrieve, similarly to the "nova get" command. This has been preferred to using mutually exclusive keyword argument, in order to avoid confusion with other CLI tools. NOTE: the current patch allow search by name only for networks. The restriction will be lifted once name attributes for port and subnets are added. Change-Id: Id186139a01c9f2cfc36ca3405b4024bd7780622e
Diffstat (limited to 'quantumclient')
-rw-r--r--quantumclient/common/exceptions.py1
-rw-r--r--quantumclient/quantum/v2_0/__init__.py64
-rw-r--r--quantumclient/quantum/v2_0/port.py2
-rw-r--r--quantumclient/quantum/v2_0/subnet.py2
-rw-r--r--quantumclient/tests/unit/test_cli20.py42
-rw-r--r--quantumclient/tests/unit/test_cli20_network.py15
-rw-r--r--quantumclient/tests/unit/test_cli20_port.py15
-rw-r--r--quantumclient/tests/unit/test_cli20_subnet.py15
-rw-r--r--quantumclient/v2_0/client.py6
9 files changed, 134 insertions, 28 deletions
diff --git a/quantumclient/common/exceptions.py b/quantumclient/common/exceptions.py
index 80f9680..734a498 100644
--- a/quantumclient/common/exceptions.py
+++ b/quantumclient/common/exceptions.py
@@ -51,6 +51,7 @@ class QuantumClientException(QuantumException):
def __init__(self, **kwargs):
message = kwargs.get('message')
+ self.status_code = kwargs.get('status_code', 0)
if message:
self.message = message
super(QuantumClientException, self).__init__(**kwargs)
diff --git a/quantumclient/quantum/v2_0/__init__.py b/quantumclient/quantum/v2_0/__init__.py
index c761530..92dc496 100644
--- a/quantumclient/quantum/v2_0/__init__.py
+++ b/quantumclient/quantum/v2_0/__init__.py
@@ -17,6 +17,7 @@
import argparse
import logging
+import re
from cliff import lister
from cliff import show
@@ -326,7 +327,10 @@ class ShowCommand(QuantumCommand, show.ShowOne):
"""Show information of a given resource
"""
-
+ HEX_ELEM = '[0-9A-Fa-f]'
+ UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
+ HEX_ELEM + '{4}', HEX_ELEM + '{4}',
+ HEX_ELEM + '{12}'])
api = 'network'
resource = None
log = None
@@ -336,22 +340,70 @@ class ShowCommand(QuantumCommand, show.ShowOne):
add_show_list_common_argument(parser)
parser.add_argument(
'id', metavar='%s_id' % self.resource,
- help='ID of %s to look up' % self.resource)
-
+ help='ID or name of %s to look up' % self.resource)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)' % parsed_args)
quantum_client = self.get_client()
quantum_client.format = parsed_args.request_format
+
params = {}
if parsed_args.show_details:
params = {'verbose': 'True'}
if parsed_args.fields:
params = {'fields': parsed_args.fields}
- obj_showor = getattr(quantum_client,
- "show_%s" % self.resource)
- data = obj_showor(parsed_args.id, **params)
+
+ data = None
+ # Error message to be used in case both search by id and name are
+ # unsuccessful (if list by name fails it does not return an error)
+ not_found_message = "Unable to find resource:%s" % parsed_args.id
+
+ # perform search by id only if we are passing a valid UUID
+ match = re.match(self.UUID_PATTERN, parsed_args.id)
+ if match:
+ try:
+ obj_shower = getattr(quantum_client,
+ "show_%s" % self.resource)
+ data = obj_shower(parsed_args.id, **params)
+ except exceptions.QuantumClientException as ex:
+ logging.debug("Show operation failed with code:%s",
+ ex.status_code)
+ not_found_message = ex.message
+ if ex.status_code != 404:
+ logging.exception("Unable to perform show operation")
+ raise
+
+ # If data is empty, then we got a 404. Try to interpret Id as a name
+ if not data:
+ logging.debug("Trying to interpret %s as a %s name",
+ parsed_args.id,
+ self.resource)
+ # build search_opts for the name
+ search_opts = parse_args_to_dict(["--name=%s" % parsed_args.id])
+ search_opts.update(params)
+ obj_lister = getattr(quantum_client,
+ "list_%ss" % self.resource)
+ data = obj_lister(**search_opts)
+ info = []
+ collection = self.resource + "s"
+ if collection in data:
+ info = data[collection]
+ if len(info) > 1:
+ logging.info("Multiple occurrences found for: %s",
+ parsed_args.id)
+ _columns = ['id']
+ # put all ids in a single string as formatter for show
+ # command will print on record only
+ id_string = "\n".join(utils.get_item_properties(
+ s, _columns)[0] for s in info)
+ return (_columns, (id_string, ), )
+ elif len(info) == 0:
+ #Nothing was found
+ raise exceptions.QuantumClientException(
+ message=not_found_message)
+ else:
+ data = {self.resource: info[0]}
if self.resource in data:
for k, v in data[self.resource].iteritems():
if isinstance(v, list):
diff --git a/quantumclient/quantum/v2_0/port.py b/quantumclient/quantum/v2_0/port.py
index 54d831a..9d6a91a 100644
--- a/quantumclient/quantum/v2_0/port.py
+++ b/quantumclient/quantum/v2_0/port.py
@@ -72,7 +72,7 @@ class CreatePort(CreateCommand):
'can be repeated')
parser.add_argument(
'network_id',
- help='Network id of this port belongs to')
+ help='Network id this port belongs to')
def args2body(self, parsed_args):
body = {'port': {'admin_state_up': parsed_args.admin_state_down,
diff --git a/quantumclient/quantum/v2_0/subnet.py b/quantumclient/quantum/v2_0/subnet.py
index 280d918..a43ec27 100644
--- a/quantumclient/quantum/v2_0/subnet.py
+++ b/quantumclient/quantum/v2_0/subnet.py
@@ -69,7 +69,7 @@ class CreateSubnet(CreateCommand):
'can be repeated')
parser.add_argument(
'network_id',
- help='Network id of this subnet belongs to')
+ help='Network id this subnet belongs to')
parser.add_argument(
'cidr', metavar='cidr',
help='cidr of subnet to create')
diff --git a/quantumclient/tests/unit/test_cli20.py b/quantumclient/tests/unit/test_cli20.py
index 14a153e..b58d65c 100644
--- a/quantumclient/tests/unit/test_cli20.py
+++ b/quantumclient/tests/unit/test_cli20.py
@@ -107,6 +107,8 @@ class MyComparator(Comparator):
class CLITestV20Base(unittest.TestCase):
+ test_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
+
def _url(self, path, query=None):
_url_str = self.endurl + "/v" + API_VERSION + path + "." + FORMAT
return query and _url_str + "?" + query or _url_str
@@ -242,16 +244,11 @@ class CLITestV20Base(unittest.TestCase):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
- query = None
- for field in fields:
- if query:
- query += "&fields=" + field
- else:
- query = "fields=" + field
- resnetworks = {resource:
- {'id': myid,
+ query = "&".join(["fields=%s" % field for field in fields])
+ expected_res = {resource:
+ {'id': myid,
'name': 'myname', }, }
- resstr = self.client.serialize(resnetworks)
+ resstr = self.client.serialize(expected_res)
path = getattr(self.client, resource + "_path")
self.client.httpclient.request(
self._url(path % myid, query), 'GET',
@@ -269,6 +266,33 @@ class CLITestV20Base(unittest.TestCase):
self.assertTrue(myid in _str)
self.assertTrue('myname' in _str)
+ def _test_show_resource_by_name(self, resource, cmd, name,
+ args, fields=[]):
+ self.mox.StubOutWithMock(cmd, "get_client")
+ self.mox.StubOutWithMock(self.client.httpclient, "request")
+ cmd.get_client().MultipleTimes().AndReturn(self.client)
+ query = "&".join(["fields=%s" % field for field in fields])
+ expected_res = {"%ss" % resource:
+ [{'id': 'some_id',
+ 'name': name, }], }
+ resstr = self.client.serialize(expected_res)
+ list_path = getattr(self.client, resource + "s_path")
+ self.client.httpclient.request(
+ self._url(list_path, "%s&name=%s" % (query, name)), 'GET',
+ body=None,
+ headers=ContainsKeyValue('X-Auth-Token',
+ TOKEN)).AndReturn((MyResp(200), resstr))
+ self.mox.ReplayAll()
+ cmd_parser = cmd.get_parser("show_" + resource)
+
+ parsed_args = cmd_parser.parse_args(args)
+ cmd.run(parsed_args)
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+ _str = self.fake_stdout.make_string()
+ self.assertTrue(name in _str)
+ self.assertTrue('some_id' in _str)
+
def _test_delete_resource(self, resource, cmd, myid, args):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
diff --git a/quantumclient/tests/unit/test_cli20_network.py b/quantumclient/tests/unit/test_cli20_network.py
index 69fcd1d..ad85dc6 100644
--- a/quantumclient/tests/unit/test_cli20_network.py
+++ b/quantumclient/tests/unit/test_cli20_network.py
@@ -125,9 +125,18 @@ class CLITestV20Network(CLITestV20Base):
"""Show net: --fields id --fields name myid."""
resource = 'network'
cmd = ShowNetwork(MyApp(sys.stdout), None)
- myid = 'myid'
- args = ['--fields', 'id', '--fields', 'name', myid]
- self._test_show_resource(resource, cmd, myid, args, ['id', 'name'])
+ args = ['--fields', 'id', '--fields', 'name', self.test_id]
+ self._test_show_resource(resource, cmd, self.test_id, args,
+ ['id', 'name'])
+
+ def test_show_network_by_name(self):
+ """Show net: --fields id --fields name myname."""
+ resource = 'network'
+ cmd = ShowNetwork(MyApp(sys.stdout), None)
+ myname = 'myname'
+ args = ['--fields', 'id', '--fields', 'name', myname]
+ self._test_show_resource_by_name(resource, cmd, myname,
+ args, ['id', 'name'])
def test_delete_network(self):
"""Delete net: myid."""
diff --git a/quantumclient/tests/unit/test_cli20_port.py b/quantumclient/tests/unit/test_cli20_port.py
index 427d473..676aaea 100644
--- a/quantumclient/tests/unit/test_cli20_port.py
+++ b/quantumclient/tests/unit/test_cli20_port.py
@@ -124,9 +124,18 @@ class CLITestV20Port(CLITestV20Base):
"""Show port: --fields id --fields name myid."""
resource = 'port'
cmd = ShowPort(MyApp(sys.stdout), None)
- myid = 'myid'
- args = ['--fields', 'id', '--fields', 'name', myid]
- self._test_show_resource(resource, cmd, myid, args, ['id', 'name'])
+ args = ['--fields', 'id', '--fields', 'name', self.test_id]
+ self._test_show_resource(resource, cmd, self.test_id,
+ args, ['id', 'name'])
+
+ def test_show_port_by_name(self):
+ """Show port: --fields id --fields name myname."""
+ resource = 'port'
+ cmd = ShowPort(MyApp(sys.stdout), None)
+ myname = 'myname'
+ args = ['--fields', 'id', '--fields', 'name', myname]
+ self._test_show_resource_by_name(resource, cmd, myname,
+ args, ['id', 'name'])
def test_delete_port(self):
"""Delete port: myid."""
diff --git a/quantumclient/tests/unit/test_cli20_subnet.py b/quantumclient/tests/unit/test_cli20_subnet.py
index b649b74..8783ed0 100644
--- a/quantumclient/tests/unit/test_cli20_subnet.py
+++ b/quantumclient/tests/unit/test_cli20_subnet.py
@@ -157,9 +157,18 @@ class CLITestV20Subnet(CLITestV20Base):
"""Show subnet: --fields id --fields name myid."""
resource = 'subnet'
cmd = ShowSubnet(MyApp(sys.stdout), None)
- myid = 'myid'
- args = ['--fields', 'id', '--fields', 'name', myid]
- self._test_show_resource(resource, cmd, myid, args, ['id', 'name'])
+ args = ['--fields', 'id', '--fields', 'name', self.test_id]
+ self._test_show_resource(resource, cmd, self.test_id,
+ args, ['id', 'name'])
+
+ def test_show_subnet_by_name(self):
+ """Show subnet: --fields id --fields name myname."""
+ resource = 'subnet'
+ cmd = ShowSubnet(MyApp(sys.stdout), None)
+ myname = 'myname'
+ args = ['--fields', 'id', '--fields', 'name', myname]
+ self._test_show_resource_by_name(resource, cmd, myname,
+ args, ['id', 'name'])
def test_delete_subnet(self):
"""Delete subnet: subnetid."""
diff --git a/quantumclient/v2_0/client.py b/quantumclient/v2_0/client.py
index 4273d82..b906237 100644
--- a/quantumclient/v2_0/client.py
+++ b/quantumclient/v2_0/client.py
@@ -78,11 +78,13 @@ def exception_handler_v20(status_code, error_content):
if isinstance(error_content, dict):
message = error_content.get('message', None)
if message:
- raise exceptions.QuantumClientException(message=message)
+ raise exceptions.QuantumClientException(status_code=status_code,
+ message=message)
# If we end up here the exception was not a quantum error
msg = "%s-%s" % (status_code, error_content)
- raise exceptions.QuantumClientException(message=msg)
+ raise exceptions.QuantumClientException(status_code=status_code,
+ message=msg)
class APIParamsCall(object):