Merge "Allow specifying a set of fields of the Port and Chasis resources"

This commit is contained in:
Jenkins
2015-07-23 03:50:23 +00:00
committed by Gerrit Code Review
10 changed files with 339 additions and 33 deletions

View File

@@ -167,7 +167,6 @@ def common_params_for_list(args, fields, field_labels):
params['detail'] = args.detail params['detail'] = args.detail
if hasattr(args, 'fields'):
requested_fields = args.fields[0] if args.fields else None requested_fields = args.fields[0] if args.fields else None
if requested_fields is not None: if requested_fields is not None:
params['fields'] = requested_fields params['fields'] = requested_fields
@@ -299,7 +298,9 @@ def check_for_invalid_fields(fields, valid_fields):
if not fields: if not fields:
return return
invalid_attr = set(fields) - set(valid_fields) invalid_fields = set(fields) - set(valid_fields)
if invalid_attr: if invalid_fields:
raise exc.CommandError(_('Invalid field(s): %s') % raise exc.CommandError(
', '.join(invalid_attr)) _('Invalid field(s) requested: %(invalid)s. Valid fields '
'are: %(valid)s.') % {'invalid': ', '.join(invalid_fields),
'valid': ', '.join(valid_fields)})

View File

@@ -126,7 +126,8 @@ class CommonParamsForListTest(test_utils.BaseTestCase):
def setUp(self): def setUp(self):
super(CommonParamsForListTest, self).setUp() super(CommonParamsForListTest, self).setUp()
self.args = mock.Mock(marker=None, limit=None, sort_key=None, self.args = mock.Mock(marker=None, limit=None, sort_key=None,
sort_dir=None, detail=False, spec=True) sort_dir=None, detail=False, fields=None,
spec=True)
self.expected_params = {'detail': False} self.expected_params = {'detail': False}
def test_nothing_set(self): def test_nothing_set(self):

View File

@@ -18,6 +18,7 @@ import copy
import testtools import testtools
from testtools.matchers import HasLength from testtools.matchers import HasLength
from ironicclient import exc
from ironicclient.tests.unit import utils from ironicclient.tests.unit import utils
import ironicclient.v1.chassis import ironicclient.v1.chassis
@@ -67,6 +68,13 @@ fake_responses = {
{"chassis": [CHASSIS]}, {"chassis": [CHASSIS]},
), ),
}, },
'/v1/chassis/?fields=uuid,extra':
{
'GET': (
{},
{"chassis": [CHASSIS]},
),
},
'/v1/chassis/%s' % CHASSIS['uuid']: '/v1/chassis/%s' % CHASSIS['uuid']:
{ {
'GET': ( 'GET': (
@@ -82,6 +90,13 @@ fake_responses = {
UPDATED_CHASSIS, UPDATED_CHASSIS,
), ),
}, },
'/v1/chassis/%s?fields=uuid,description' % CHASSIS['uuid']:
{
'GET': (
{},
CHASSIS,
),
},
'/v1/chassis/%s/nodes' % CHASSIS['uuid']: '/v1/chassis/%s/nodes' % CHASSIS['uuid']:
{ {
'GET': ( 'GET': (
@@ -96,6 +111,13 @@ fake_responses = {
{"nodes": [NODE]}, {"nodes": [NODE]},
), ),
}, },
'/v1/chassis/%s/nodes?fields=uuid,extra' % CHASSIS['uuid']:
{
'GET': (
{},
{"nodes": [NODE]},
),
},
} }
fake_responses_pagination = { fake_responses_pagination = {
@@ -243,6 +265,18 @@ class ChassisManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(chassis)) self.assertEqual(1, len(chassis))
def test_chassis_list_fields(self):
nodes = self.mgr.list(fields=['uuid', 'extra'])
expect = [
('GET', '/v1/chassis/?fields=uuid,extra', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(nodes))
def test_chassis_list_detail_and_fields_fail(self):
self.assertRaises(exc.InvalidAttribute, self.mgr.list,
detail=True, fields=['uuid', 'extra'])
def test_chassis_show(self): def test_chassis_show(self):
chassis = self.mgr.get(CHASSIS['uuid']) chassis = self.mgr.get(CHASSIS['uuid'])
expect = [ expect = [
@@ -252,6 +286,16 @@ class ChassisManagerTest(testtools.TestCase):
self.assertEqual(CHASSIS['uuid'], chassis.uuid) self.assertEqual(CHASSIS['uuid'], chassis.uuid)
self.assertEqual(CHASSIS['description'], chassis.description) self.assertEqual(CHASSIS['description'], chassis.description)
def test_chassis_show_fields(self):
chassis = self.mgr.get(CHASSIS['uuid'], fields=['uuid', 'description'])
expect = [
('GET', '/v1/chassis/%s?fields=uuid,description' %
CHASSIS['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(CHASSIS['uuid'], chassis.uuid)
self.assertEqual(CHASSIS['description'], chassis.description)
def test_create(self): def test_create(self):
chassis = self.mgr.create(**CREATE_CHASSIS) chassis = self.mgr.create(**CREATE_CHASSIS)
expect = [ expect = [
@@ -297,6 +341,20 @@ class ChassisManagerTest(testtools.TestCase):
self.assertEqual(1, len(nodes)) self.assertEqual(1, len(nodes))
self.assertEqual(NODE['uuid'], nodes[0].uuid) self.assertEqual(NODE['uuid'], nodes[0].uuid)
def test_chassis_node_list_fields(self):
nodes = self.mgr.list_nodes(CHASSIS['uuid'], fields=['uuid', 'extra'])
expect = [
('GET', '/v1/chassis/%s/nodes?fields=uuid,extra' %
CHASSIS['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(nodes))
def test_chassis_node_list_detail_and_fields_fail(self):
self.assertRaises(exc.InvalidAttribute, self.mgr.list_nodes,
CHASSIS['uuid'], detail=True,
fields=['uuid', 'extra'])
def test_chassis_node_list_limit(self): def test_chassis_node_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination) self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api) self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)

View File

@@ -22,7 +22,8 @@ import ironicclient.v1.chassis_shell as c_shell
class ChassisShellTest(utils.BaseTestCase): class ChassisShellTest(utils.BaseTestCase):
def _get_client_mock_args(self, chassis=None, marker=None, limit=None, def _get_client_mock_args(self, chassis=None, marker=None, limit=None,
sort_dir=None, sort_key=None, detail=False): sort_dir=None, sort_key=None, detail=False,
fields=None):
args = mock.MagicMock(spec=True) args = mock.MagicMock(spec=True)
args.chassis = chassis args.chassis = chassis
args.marker = marker args.marker = marker
@@ -30,6 +31,7 @@ class ChassisShellTest(utils.BaseTestCase):
args.sort_dir = sort_dir args.sort_dir = sort_dir
args.sort_key = sort_key args.sort_key = sort_key
args.detail = detail args.detail = detail
args.fields = fields
return args return args
@@ -59,6 +61,24 @@ class ChassisShellTest(utils.BaseTestCase):
c_shell.do_chassis_show, c_shell.do_chassis_show,
client_mock, args) client_mock, args)
def test_do_chassis_show_fields(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.chassis = 'chassis_uuid'
args.fields = [['uuid', 'description']]
c_shell.do_chassis_show(client_mock, args)
client_mock.chassis.get.assert_called_once_with(
'chassis_uuid', fields=['uuid', 'description'])
def test_do_chassis_show_invalid_fields(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.chassis = 'chassis_uuid'
args.fields = [['foo', 'bar']]
self.assertRaises(exceptions.CommandError,
c_shell.do_chassis_show,
client_mock, args)
def test_do_chassis_list(self): def test_do_chassis_list(self):
client_mock = mock.MagicMock() client_mock = mock.MagicMock()
args = self._get_client_mock_args() args = self._get_client_mock_args()
@@ -111,6 +131,20 @@ class ChassisShellTest(utils.BaseTestCase):
client_mock, args) client_mock, args)
self.assertFalse(client_mock.chassis.list.called) self.assertFalse(client_mock.chassis.list.called)
def test_do_chassis_list_fields(self):
client_mock = mock.MagicMock()
args = self._get_client_mock_args(fields=[['uuid', 'description']])
c_shell.do_chassis_list(client_mock, args)
client_mock.chassis.list.assert_called_once_with(
fields=['uuid', 'description'], detail=False)
def test_do_chassis_list_invalid_fields(self):
client_mock = mock.MagicMock()
args = self._get_client_mock_args(fields=[['foo', 'bar']])
self.assertRaises(exceptions.CommandError,
c_shell.do_chassis_list,
client_mock, args)
def test_do_chassis_node_list(self): def test_do_chassis_node_list(self):
client_mock = mock.MagicMock() client_mock = mock.MagicMock()
chassis_mock = mock.MagicMock(spec_set=[]) chassis_mock = mock.MagicMock(spec_set=[])
@@ -128,3 +162,20 @@ class ChassisShellTest(utils.BaseTestCase):
c_shell.do_chassis_node_list(client_mock, args) c_shell.do_chassis_node_list(client_mock, args)
client_mock.chassis.list_nodes.assert_called_once_with( client_mock.chassis.list_nodes.assert_called_once_with(
chassis_mock, detail=True) chassis_mock, detail=True)
def test_do_chassis_node_list_fields(self):
client_mock = mock.MagicMock()
chassis_mock = mock.MagicMock(spec_set=[])
args = self._get_client_mock_args(chassis=chassis_mock,
fields=[['uuid', 'power_state']])
c_shell.do_chassis_node_list(client_mock, args)
client_mock.chassis.list_nodes.assert_called_once_with(
chassis_mock, fields=['uuid', 'power_state'], detail=False)
def test_do_chassis_node_list_invalid_fields(self):
client_mock = mock.MagicMock()
chassis_mock = mock.MagicMock(spec_set=[])
args = self._get_client_mock_args(chassis=chassis_mock,
fields=[['foo', 'bar']])
self.assertRaises(exceptions.CommandError,
c_shell.do_chassis_node_list, client_mock, args)

View File

@@ -18,6 +18,7 @@ import copy
import testtools import testtools
from testtools.matchers import HasLength from testtools.matchers import HasLength
from ironicclient import exc
from ironicclient.tests.unit import utils from ironicclient.tests.unit import utils
import ironicclient.v1.port import ironicclient.v1.port
@@ -60,6 +61,13 @@ fake_responses = {
{"ports": [PORT]}, {"ports": [PORT]},
), ),
}, },
'/v1/ports/?fields=uuid,address':
{
'GET': (
{},
{"ports": [PORT]},
),
},
'/v1/ports/%s' % PORT['uuid']: '/v1/ports/%s' % PORT['uuid']:
{ {
'GET': ( 'GET': (
@@ -75,6 +83,13 @@ fake_responses = {
UPDATED_PORT, UPDATED_PORT,
), ),
}, },
'/v1/ports/%s?fields=uuid,address' % PORT['uuid']:
{
'GET': (
{},
PORT,
),
},
'/v1/ports/detail?address=%s' % PORT['address']: '/v1/ports/detail?address=%s' % PORT['address']:
{ {
'GET': ( 'GET': (
@@ -173,6 +188,18 @@ class PortManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls) self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(ports)) self.assertEqual(1, len(ports))
def test_port_list_fields(self):
ports = self.mgr.list(fields=['uuid', 'address'])
expect = [
('GET', '/v1/ports/?fields=uuid,address', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(ports))
def test_port_list_detail_and_fields_fail(self):
self.assertRaises(exc.InvalidAttribute, self.mgr.list,
detail=True, fields=['uuid', 'address'])
def test_ports_list_limit(self): def test_ports_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination) self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.port.PortManager(self.api) self.mgr = ironicclient.v1.port.PortManager(self.api)
@@ -245,6 +272,16 @@ class PortManagerTest(testtools.TestCase):
self.assertEqual(PORT['address'], port.address) self.assertEqual(PORT['address'], port.address)
self.assertEqual(PORT['node_uuid'], port.node_uuid) self.assertEqual(PORT['node_uuid'], port.node_uuid)
def test_port_show_fields(self):
port = self.mgr.get(PORT['uuid'], fields=['uuid', 'address'])
expect = [
('GET', '/v1/ports/%s?fields=uuid,address' %
PORT['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(PORT['uuid'], port.uuid)
self.assertEqual(PORT['address'], port.address)
def test_create(self): def test_create(self):
port = self.mgr.create(**CREATE_PORT) port = self.mgr.create(**CREATE_PORT)
expect = [ expect = [

View File

@@ -39,9 +39,10 @@ class PortShellTest(utils.BaseTestCase):
args = mock.MagicMock() args = mock.MagicMock()
args.port = 'port_uuid' args.port = 'port_uuid'
args.address = False args.address = False
args.fields = None
p_shell.do_port_show(client_mock, args) p_shell.do_port_show(client_mock, args)
client_mock.port.get.assert_called_once_with('port_uuid') client_mock.port.get.assert_called_once_with('port_uuid', fields=None)
# assert get_by_address() wasn't called # assert get_by_address() wasn't called
self.assertFalse(client_mock.port.get_by_address.called) self.assertFalse(client_mock.port.get_by_address.called)
@@ -68,12 +69,34 @@ class PortShellTest(utils.BaseTestCase):
args = mock.MagicMock() args = mock.MagicMock()
args.port = 'port_address' args.port = 'port_address'
args.address = True args.address = True
args.fields = None
p_shell.do_port_show(client_mock, args) p_shell.do_port_show(client_mock, args)
client_mock.port.get_by_address.assert_called_once_with('port_address') client_mock.port.get_by_address.assert_called_once_with('port_address',
fields=None)
# assert get() wasn't called # assert get() wasn't called
self.assertFalse(client_mock.port.get.called) self.assertFalse(client_mock.port.get.called)
def test_do_port_show_fields(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.port = 'port_uuid'
args.address = False
args.fields = [['uuid', 'address']]
p_shell.do_port_show(client_mock, args)
client_mock.port.get.assert_called_once_with(
'port_uuid', fields=['uuid', 'address'])
def test_do_port_show_invalid_fields(self):
client_mock = mock.MagicMock()
args = mock.MagicMock()
args.port = 'port_uuid'
args.address = False
args.fields = [['foo', 'bar']]
self.assertRaises(exceptions.CommandError,
p_shell.do_port_show,
client_mock, args)
def test_do_port_update(self): def test_do_port_update(self):
client_mock = mock.MagicMock() client_mock = mock.MagicMock()
args = mock.MagicMock() args = mock.MagicMock()
@@ -86,7 +109,8 @@ class PortShellTest(utils.BaseTestCase):
client_mock.port.update.assert_called_once_with('port_uuid', patch) client_mock.port.update.assert_called_once_with('port_uuid', patch)
def _get_client_mock_args(self, address=None, marker=None, limit=None, def _get_client_mock_args(self, address=None, marker=None, limit=None,
sort_dir=None, sort_key=None, detail=False): sort_dir=None, sort_key=None, detail=False,
fields=None):
args = mock.MagicMock(spec=True) args = mock.MagicMock(spec=True)
args.address = address args.address = address
args.marker = marker args.marker = marker
@@ -94,6 +118,7 @@ class PortShellTest(utils.BaseTestCase):
args.sort_dir = sort_dir args.sort_dir = sort_dir
args.sort_key = sort_key args.sort_key = sort_key
args.detail = detail args.detail = detail
args.fields = fields
return args return args
@@ -148,3 +173,18 @@ class PortShellTest(utils.BaseTestCase):
p_shell.do_port_list, p_shell.do_port_list,
client_mock, args) client_mock, args)
self.assertFalse(client_mock.port.list.called) self.assertFalse(client_mock.port.list.called)
def test_do_port_list_fields(self):
client_mock = mock.MagicMock()
args = self._get_client_mock_args(fields=[['uuid', 'address']])
p_shell.do_port_list(client_mock, args)
client_mock.port.list.assert_called_once_with(
fields=['uuid', 'address'], detail=False)
def test_do_port_list_invalid_fields(self):
client_mock = mock.MagicMock()
args = self._get_client_mock_args(fields=[['foo', 'bar']])
self.assertRaises(exceptions.CommandError,
p_shell.do_port_list,
client_mock, args)

View File

@@ -15,6 +15,7 @@
# under the License. # under the License.
from ironicclient.common import base from ironicclient.common import base
from ironicclient.common.i18n import _
from ironicclient.common import utils from ironicclient.common import utils
from ironicclient import exc from ironicclient import exc
@@ -35,7 +36,7 @@ class ChassisManager(base.Manager):
return '/v1/chassis/%s' % id if id else '/v1/chassis' return '/v1/chassis/%s' % id if id else '/v1/chassis'
def list(self, marker=None, limit=None, sort_key=None, def list(self, marker=None, limit=None, sort_key=None,
sort_dir=None, detail=False): sort_dir=None, detail=False, fields=None):
"""Retrieve a list of chassis. """Retrieve a list of chassis.
:param marker: Optional, the UUID of a chassis, eg the last :param marker: Optional, the UUID of a chassis, eg the last
@@ -58,13 +59,22 @@ class ChassisManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information :param detail: Optional, boolean whether to return detailed information
about chassis. about chassis.
:param fields: Optional, a list with a specified set of fields
of the resource to be returned. Can not be used
when 'detail' is set.
:returns: A list of chassis. :returns: A list of chassis.
""" """
if limit is not None: if limit is not None:
limit = int(limit) limit = int(limit)
filters = utils.common_filters(marker, limit, sort_key, sort_dir) if detail and fields:
raise exc.InvalidAttribute(_("Can't fetch a subset of fields "
"with 'detail' set"))
filters = utils.common_filters(marker, limit, sort_key, sort_dir,
fields)
path = '' path = ''
if detail: if detail:
@@ -79,7 +89,7 @@ class ChassisManager(base.Manager):
limit=limit) limit=limit)
def list_nodes(self, chassis_id, marker=None, limit=None, def list_nodes(self, chassis_id, marker=None, limit=None,
sort_key=None, sort_dir=None, detail=False): sort_key=None, sort_dir=None, detail=False, fields=None):
"""List all the nodes for a given chassis. """List all the nodes for a given chassis.
:param chassis_id: The UUID of the chassis. :param chassis_id: The UUID of the chassis.
@@ -103,13 +113,22 @@ class ChassisManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information :param detail: Optional, boolean whether to return detailed information
about nodes. about nodes.
:param fields: Optional, a list with a specified set of fields
of the resource to be returned. Can not be used
when 'detail' is set.
:returns: A list of nodes. :returns: A list of nodes.
""" """
if limit is not None: if limit is not None:
limit = int(limit) limit = int(limit)
filters = utils.common_filters(marker, limit, sort_key, sort_dir) if detail and fields:
raise exc.InvalidAttribute(_("Can't fetch a subset of fields "
"with 'detail' set"))
filters = utils.common_filters(marker, limit, sort_key, sort_dir,
fields)
path = "%s/nodes" % chassis_id path = "%s/nodes" % chassis_id
if detail: if detail:
@@ -124,7 +143,11 @@ class ChassisManager(base.Manager):
return self._list_pagination(self._path(path), "nodes", return self._list_pagination(self._path(path), "nodes",
limit=limit) limit=limit)
def get(self, chassis_id): def get(self, chassis_id, fields=None):
if fields is not None:
chassis_id = '%s?fields=' % chassis_id
chassis_id += ','.join(fields)
try: try:
return self._list(self._path(chassis_id))[0] return self._list(self._path(chassis_id))[0]
except IndexError: except IndexError:

View File

@@ -18,18 +18,32 @@ from ironicclient.openstack.common import cliutils
from ironicclient.v1 import resource_fields as res_fields from ironicclient.v1 import resource_fields as res_fields
def _print_chassis_show(chassis): def _print_chassis_show(chassis, fields=None):
fields = ['uuid', 'description', 'created_at', 'updated_at', 'extra'] if fields is None:
fields = res_fields.CHASSIS_DETAILED_RESOURCE.fields
data = dict([(f, getattr(chassis, f, '')) for f in fields]) data = dict([(f, getattr(chassis, f, '')) for f in fields])
cliutils.print_dict(data, wrap=72) cliutils.print_dict(data, wrap=72)
@cliutils.arg('chassis', metavar='<chassis>', help="UUID of the chassis.") @cliutils.arg('chassis', metavar='<chassis>', help="UUID of the chassis.")
@cliutils.arg(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
help="One or more chassis fields. Only these fields will be fetched from "
"the server.")
def do_chassis_show(cc, args): def do_chassis_show(cc, args):
"""Show detailed information about a chassis.""" """Show detailed information about a chassis."""
utils.check_empty_arg(args.chassis, '<chassis>') utils.check_empty_arg(args.chassis, '<chassis>')
chassis = cc.chassis.get(args.chassis) fields = args.fields[0] if args.fields else None
_print_chassis_show(chassis) utils.check_for_invalid_fields(
fields, res_fields.CHASSIS_DETAILED_RESOURCE.fields)
chassis = cc.chassis.get(args.chassis, fields=fields)
_print_chassis_show(chassis, fields=fields)
@cliutils.arg( @cliutils.arg(
@@ -60,6 +74,15 @@ def do_chassis_show(cc, args):
metavar='<direction>', metavar='<direction>',
choices=['asc', 'desc'], choices=['asc', 'desc'],
help='Sort direction: "asc" (the default) or "desc".') help='Sort direction: "asc" (the default) or "desc".')
@cliutils.arg(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
help="One or more chassis fields. Only these fields will be fetched from "
"the server. Can not be used when '--detail' is specified.")
def do_chassis_list(cc, args): def do_chassis_list(cc, args):
"""List the chassis.""" """List the chassis."""
if args.detail: if args.detail:
@@ -67,6 +90,14 @@ def do_chassis_list(cc, args):
field_labels = res_fields.CHASSIS_DETAILED_RESOURCE.labels field_labels = res_fields.CHASSIS_DETAILED_RESOURCE.labels
sort_fields = res_fields.CHASSIS_DETAILED_RESOURCE.sort_fields sort_fields = res_fields.CHASSIS_DETAILED_RESOURCE.sort_fields
sort_field_labels = res_fields.CHASSIS_DETAILED_RESOURCE.sort_labels sort_field_labels = res_fields.CHASSIS_DETAILED_RESOURCE.sort_labels
elif args.fields:
utils.check_for_invalid_fields(
args.fields[0], res_fields.CHASSIS_DETAILED_RESOURCE.fields)
resource = res_fields.Resource(args.fields[0])
fields = resource.fields
field_labels = resource.labels
sort_fields = res_fields.CHASSIS_DETAILED_RESOURCE.sort_fields
sort_field_labels = res_fields.CHASSIS_DETAILED_RESOURCE.sort_labels
else: else:
fields = res_fields.CHASSIS_RESOURCE.fields fields = res_fields.CHASSIS_RESOURCE.fields
field_labels = res_fields.CHASSIS_RESOURCE.labels field_labels = res_fields.CHASSIS_RESOURCE.labels
@@ -166,11 +197,26 @@ def do_chassis_update(cc, args):
choices=['asc', 'desc'], choices=['asc', 'desc'],
help='Sort direction: "asc" (the default) or "desc".') help='Sort direction: "asc" (the default) or "desc".')
@cliutils.arg('chassis', metavar='<chassis>', help="UUID of the chassis.") @cliutils.arg('chassis', metavar='<chassis>', help="UUID of the chassis.")
@cliutils.arg(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
help="One or more node fields. Only these fields will be fetched from "
"the server. Can not be used when '--detail' is specified.")
def do_chassis_node_list(cc, args): def do_chassis_node_list(cc, args):
"""List the nodes contained in a chassis.""" """List the nodes contained in a chassis."""
if args.detail: if args.detail:
fields = res_fields.NODE_DETAILED_RESOURCE.fields fields = res_fields.NODE_DETAILED_RESOURCE.fields
field_labels = res_fields.NODE_DETAILED_RESOURCE.labels field_labels = res_fields.NODE_DETAILED_RESOURCE.labels
elif args.fields:
utils.check_for_invalid_fields(
args.fields[0], res_fields.NODE_DETAILED_RESOURCE.fields)
resource = res_fields.Resource(args.fields[0])
fields = resource.fields
field_labels = resource.labels
else: else:
fields = res_fields.NODE_RESOURCE.fields fields = res_fields.NODE_RESOURCE.fields
field_labels = res_fields.NODE_RESOURCE.labels field_labels = res_fields.NODE_RESOURCE.labels

View File

@@ -15,6 +15,7 @@
# under the License. # under the License.
from ironicclient.common import base from ironicclient.common import base
from ironicclient.common.i18n import _
from ironicclient.common import utils from ironicclient.common import utils
from ironicclient import exc from ironicclient import exc
@@ -34,7 +35,7 @@ class PortManager(base.Manager):
return '/v1/ports/%s' % id if id else '/v1/ports' return '/v1/ports/%s' % id if id else '/v1/ports'
def list(self, address=None, limit=None, marker=None, sort_key=None, def list(self, address=None, limit=None, marker=None, sort_key=None,
sort_dir=None, detail=False): sort_dir=None, detail=False, fields=None):
"""Retrieve a list of port. """Retrieve a list of port.
:param address: Optional, MAC address of a port, to get :param address: Optional, MAC address of a port, to get
@@ -59,13 +60,22 @@ class PortManager(base.Manager):
:param detail: Optional, boolean whether to return detailed information :param detail: Optional, boolean whether to return detailed information
about ports. about ports.
:param fields: Optional, a list with a specified set of fields
of the resource to be returned. Can not be used
when 'detail' is set.
:returns: A list of ports. :returns: A list of ports.
""" """
if limit is not None: if limit is not None:
limit = int(limit) limit = int(limit)
filters = utils.common_filters(marker, limit, sort_key, sort_dir) if detail and fields:
raise exc.InvalidAttribute(_("Can't fetch a subset of fields "
"with 'detail' set"))
filters = utils.common_filters(marker, limit, sort_key, sort_dir,
fields)
if address is not None: if address is not None:
filters.append('address=%s' % address) filters.append('address=%s' % address)
@@ -81,14 +91,23 @@ class PortManager(base.Manager):
return self._list_pagination(self._path(path), "ports", return self._list_pagination(self._path(path), "ports",
limit=limit) limit=limit)
def get(self, port_id): def get(self, port_id, fields=None):
if fields is not None:
port_id = '%s?fields=' % port_id
port_id += ','.join(fields)
try: try:
return self._list(self._path(port_id))[0] return self._list(self._path(port_id))[0]
except IndexError: except IndexError:
return None return None
def get_by_address(self, address): def get_by_address(self, address, fields=None):
path = "detail?address=%s" % address path = '?address=%s' % address
if fields is not None:
path += '&fields=' + ','.join(fields)
else:
path = 'detail' + path
ports = self._list(self._path(path), 'ports') ports = self._list(self._path(path), 'ports')
# get all the details of the port assuming that filtering by # get all the details of the port assuming that filtering by
# address returns a collection of one port if successful. # address returns a collection of one port if successful.

View File

@@ -18,9 +18,10 @@ from ironicclient.openstack.common import cliutils
from ironicclient.v1 import resource_fields as res_fields from ironicclient.v1 import resource_fields as res_fields
def _print_port_show(port): def _print_port_show(port, fields=None):
fields = ['address', 'created_at', 'extra', 'node_uuid', 'updated_at', if fields is None:
'uuid'] fields = res_fields.PORT_DETAILED_RESOURCE.fields
data = dict([(f, getattr(port, f, '')) for f in fields]) data = dict([(f, getattr(port, f, '')) for f in fields])
cliutils.print_dict(data, wrap=72) cliutils.print_dict(data, wrap=72)
@@ -35,14 +36,26 @@ def _print_port_show(port):
action='store_true', action='store_true',
default=False, default=False,
help='<id> is the MAC address (instead of the UUID) of the port.') help='<id> is the MAC address (instead of the UUID) of the port.')
@cliutils.arg(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
help="One or more port fields. Only these fields will be fetched from "
"the server.")
def do_port_show(cc, args): def do_port_show(cc, args):
"""Show detailed information about a port.""" """Show detailed information about a port."""
fields = args.fields[0] if args.fields else None
utils.check_for_invalid_fields(
fields, res_fields.PORT_DETAILED_RESOURCE.fields)
if args.address: if args.address:
port = cc.port.get_by_address(args.port) port = cc.port.get_by_address(args.port, fields=fields)
else: else:
utils.check_empty_arg(args.port, '<id>') utils.check_empty_arg(args.port, '<id>')
port = cc.port.get(args.port) port = cc.port.get(args.port, fields=fields)
_print_port_show(port) _print_port_show(port, fields=fields)
@cliutils.arg( @cliutils.arg(
@@ -76,6 +89,15 @@ def do_port_show(cc, args):
metavar='<direction>', metavar='<direction>',
choices=['asc', 'desc'], choices=['asc', 'desc'],
help='Sort direction: "asc" (the default) or "desc".') help='Sort direction: "asc" (the default) or "desc".')
@cliutils.arg(
'--fields',
nargs='+',
dest='fields',
metavar='<field>',
action='append',
default=[],
help="One or more port fields. Only these fields will be fetched from "
"the server. Can not be used when '--detail' is specified.")
def do_port_list(cc, args): def do_port_list(cc, args):
"""List the ports.""" """List the ports."""
params = {} params = {}
@@ -88,6 +110,14 @@ def do_port_list(cc, args):
field_labels = res_fields.PORT_DETAILED_RESOURCE.labels field_labels = res_fields.PORT_DETAILED_RESOURCE.labels
sort_fields = res_fields.PORT_DETAILED_RESOURCE.sort_fields sort_fields = res_fields.PORT_DETAILED_RESOURCE.sort_fields
sort_field_labels = res_fields.PORT_DETAILED_RESOURCE.sort_labels sort_field_labels = res_fields.PORT_DETAILED_RESOURCE.sort_labels
elif args.fields:
utils.check_for_invalid_fields(
args.fields[0], res_fields.PORT_DETAILED_RESOURCE.fields)
resource = res_fields.Resource(args.fields[0])
fields = resource.fields
field_labels = resource.labels
sort_fields = res_fields.PORT_DETAILED_RESOURCE.sort_fields
sort_field_labels = res_fields.PORT_DETAILED_RESOURCE.sort_labels
else: else:
fields = res_fields.PORT_RESOURCE.fields fields = res_fields.PORT_RESOURCE.fields
field_labels = res_fields.PORT_RESOURCE.labels field_labels = res_fields.PORT_RESOURCE.labels