Adds client commands for listing a VNF resources

Support now for being able list sub resources of a vnf. REST path is
/vnf/<vnf_id>/resources/.  Resources will contain 'name', 'id', and
'type'.

APIImpact
Partial-Bug: 1602112

Change-Id: Ib9f0163c0c86df2a4d17630a5e6f7ca2d2fb22de
Signed-off-by: Tim Rozet <trozet@redhat.com>
This commit is contained in:
Tim Rozet 2016-08-24 23:05:49 -04:00
parent 7f829587b0
commit 144408331e
6 changed files with 235 additions and 1 deletions

View File

@ -0,0 +1,4 @@
---
features:
- Adds new CLI command 'vnf-resource-list' to view VNF
resources, such as VDU, CP, etc.

View File

@ -117,6 +117,7 @@ COMMAND_V1 = {
'vnf-list': vnf.ListVNF,
'vnf-show': vnf.ShowVNF,
'vnf-scale': vnf.ScaleVNF,
'vnf-resource-list': vnf.ListVNFResources,
# 'vnf-config-create'
# 'vnf-config-push'

View File

@ -15,10 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from tackerclient.i18n import _
from tackerclient.tacker import v1_0 as tackerV10
_VNF = 'vnf'
_RESOURCE = 'resource'
class ListVNF(tackerV10.ListCommand):
@ -149,6 +151,73 @@ class DeleteVNF(tackerV10.DeleteCommand):
resource = _VNF
class ListVNFResources(tackerV10.ListCommand):
"""List resources of a VNF like VDU, CP, etc."""
list_columns = ['name', 'id', 'type']
allow_names = True
resource = _VNF
def get_id(self):
if self.resource:
return self.resource.upper()
def get_parser(self, prog_name):
parser = super(ListVNFResources, self).get_parser(prog_name)
if self.allow_names:
help_str = _('ID or name of %s to look up')
else:
help_str = _('ID of %s to look up')
parser.add_argument(
'id', metavar=self.get_id(),
help=help_str % self.resource)
return parser
def get_data(self, parsed_args):
self.log.debug('get_data(%s)', parsed_args)
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
if self.allow_names:
_id = tackerV10.find_resourceid_by_name_or_id(tacker_client,
self.resource,
parsed_args.id)
else:
_id = parsed_args.id
data = self.retrieve_list_by_id(_id, parsed_args)
self.extend_list(data, parsed_args)
return self.setup_columns(data, parsed_args)
def retrieve_list_by_id(self, id, parsed_args):
"""Retrieve a list of sub resources from Tacker server"""
tacker_client = self.get_client()
tacker_client.format = parsed_args.request_format
_extra_values = tackerV10.parse_args_to_dict(self.values_specs)
tackerV10._merge_args(self, parsed_args, _extra_values,
self.values_specs)
search_opts = self.args2search_opts(parsed_args)
search_opts.update(_extra_values)
if self.pagination_support:
page_size = parsed_args.page_size
if page_size:
search_opts.update({'limit': page_size})
if self.sorting_support:
keys = parsed_args.sort_key
if keys:
search_opts.update({'sort_key': keys})
dirs = parsed_args.sort_dir
len_diff = len(keys) - len(dirs)
if len_diff > 0:
dirs += ['asc'] * len_diff
elif len_diff < 0:
dirs = dirs[:len(keys)]
if dirs:
search_opts.update({'sort_dir': dirs})
obj_lister = getattr(tacker_client, "list_vnf_resources")
data = obj_lister(id, **search_opts)
return data.get('resources', [])
class ScaleVNF(tackerV10.TackerCommand):
"""Scale a VNF."""

View File

@ -369,6 +369,139 @@ class CLITestV10Base(testtools.TestCase):
self.assertIn('myid1', _str)
return _str
def _test_list_sub_resources(self, resources, api_resource, cmd, myid,
detail=False,
tags=[], fields_1=[], fields_2=[],
page_size=None, sort_key=[], sort_dir=[],
response_contents=None, base_args=None,
path=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
if response_contents is None:
contents = [{self.id_field: 'myid1', },
{self.id_field: 'myid2', }, ]
else:
contents = response_contents
reses = {api_resource: contents}
self.client.format = self.format
resstr = self.client.serialize(reses)
# url method body
query = ""
args = base_args if base_args is not None else []
if detail:
args.append('-D')
args.extend(['--request-format', self.format])
if fields_1:
for field in fields_1:
args.append('--fields')
args.append(field)
if tags:
args.append('--')
args.append("--tag")
for tag in tags:
args.append(tag)
if isinstance(tag, six.string_types):
tag = urllib.quote(tag.encode('utf-8'))
if query:
query += "&tag=" + tag
else:
query = "tag=" + tag
if (not tags) and fields_2:
args.append('--')
if fields_2:
args.append("--fields")
for field in fields_2:
args.append(field)
if detail:
query = query and query + '&verbose=True' or 'verbose=True'
fields_1.extend(fields_2)
for field in fields_1:
if query:
query += "&fields=" + field
else:
query = "fields=" + field
if page_size:
args.append("--page-size")
args.append(str(page_size))
if query:
query += "&limit=%s" % page_size
else:
query = "limit=%s" % page_size
if sort_key:
for key in sort_key:
args.append('--sort-key')
args.append(key)
if query:
query += '&'
query += 'sort_key=%s' % key
if sort_dir:
len_diff = len(sort_key) - len(sort_dir)
if len_diff > 0:
sort_dir += ['asc'] * len_diff
elif len_diff < 0:
sort_dir = sort_dir[:len(sort_key)]
for dir in sort_dir:
args.append('--sort-dir')
args.append(dir)
if query:
query += '&'
query += 'sort_dir=%s' % dir
if path is None:
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
MyUrlComparator(end_url(path % myid, query, format=self.format),
self.client),
'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
if response_contents is None:
self.assertIn('myid1', _str)
return _str
def _test_list_sub_resources_with_pagination(self, resources, api_resource,
cmd, myid):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
path = getattr(self.client, resources + "_path")
fake_query = "marker=myid2&limit=2"
reses1 = {api_resource: [{'id': 'myid1', },
{'id': 'myid2', }],
'%s_links' % api_resource: [
{'href': end_url(path % myid, fake_query),
'rel': 'next'}]
}
reses2 = {api_resource: [{'id': 'myid3', },
{'id': 'myid4', }]}
self.client.format = self.format
resstr1 = self.client.serialize(reses1)
resstr2 = self.client.serialize(reses2)
self.client.httpclient.request(
end_url(path % myid, "", format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr1))
self.client.httpclient.request(
end_url(path % myid, fake_query, format=self.format), 'GET',
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(200), resstr2))
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("list_" + resources)
args = [myid, '--request-format', self.format]
shell.run_command(cmd, cmd_parser, args)
self.mox.VerifyAll()
self.mox.UnsetStubs()
def _test_list_resources_with_pagination(self, resources, cmd):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")

View File

@ -32,9 +32,11 @@ ENDURL = 'localurl'
class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base):
_RESOURCE = 'vnf'
_RESOURCES = 'vnfs'
_VNF_RESOURCES = 'vnf_resources'
def setUp(self):
plurals = {'vnfs': 'vnf'}
plurals = {'vnfs': 'vnf',
'resources': 'resource'}
super(CLITestV10VmVNFJSON, self).setUp(plurals=plurals)
def _test_create_resource(self, resource, cmd,
@ -192,3 +194,22 @@ class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base):
my_id = 'my-id'
args = [my_id]
self._test_delete_resource(self._RESOURCE, cmd, my_id, args)
def test_list_vnf_resources(self):
cmd = vnf.ListVNFResources(test_cli10.MyApp(sys.stdout), None)
base_args = [self.test_id]
response = [{'name': 'CP11', 'id': 'id1', 'type': 'NeutronPort'},
{'name': 'CP12', 'id': 'id2', 'type': 'NeutronPort'}]
val = self._test_list_sub_resources(self._VNF_RESOURCES, 'resources',
cmd, self.test_id,
response_contents=response,
detail=True, base_args=base_args)
self.assertIn('id1', val)
self.assertIn('NeutronPort', val)
self.assertIn('CP11', val)
def test_list_vnf_resources_pagination(self):
cmd = vnf.ListVNFResources(test_cli10.MyApp(sys.stdout), None)
self._test_list_sub_resources_with_pagination(self._VNF_RESOURCES,
'resources', cmd,
self.test_id)

View File

@ -339,6 +339,7 @@ class Client(ClientBase):
vnfs_path = '/vnfs'
vnf_path = '/vnfs/%s'
vnf_scale_path = '/vnfs/%s/actions'
vnf_resources_path = '/vnfs/%s/resources'
vims_path = '/vims'
vim_path = '/vims/%s'
@ -425,6 +426,11 @@ class Client(ClientBase):
def update_vnf(self, vnf, body=None):
return self.put(self.vnf_path % vnf, body=body)
@APIParamsCall
def list_vnf_resources(self, vnf, retrieve_all=True, **_params):
return self.list('resources', self.vnf_resources_path % vnf,
retrieve_all, **_params)
@APIParamsCall
def scale_vnf(self, vnf, body=None):
return self.post(self.vnf_scale_path % vnf, body=body)