summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTadeas Kot <tkot@redhat.com>2021-08-25 11:15:02 +0200
committerTadeas Kot <tkot@redhat.com>2021-08-30 09:45:54 +0200
commit920f5102cb28669f04730a18aa03210bb51e9703 (patch)
tree45da24b5732d185242c6d1fcdbf915b08925fb4c
parentb5df386022c686f1b1020253f0980e8b738091fd (diff)
downloadpython-ironicclient-920f5102cb28669f04730a18aa03210bb51e9703.tar.gz
Add support for fields in drivers CLI
This commit add support for fields selector to the driver CLI. * ``openstack baremetal driver list --fields <field> [<field> ...]`` * ``openstack baremetal driver show <driver_name> --fields <field> [<field> ...]`` Depends-On: https://review.opendev.org/c/openstack/ironic/+/804416 Story: 1674775 Task: 43043 Change-Id: I2d691feec876f6978d5075e779ea465ed660f09e
-rw-r--r--ironicclient/common/http.py2
-rw-r--r--ironicclient/osc/v1/baremetal_driver.py38
-rw-r--r--ironicclient/tests/unit/osc/v1/test_baremetal_driver.py116
-rw-r--r--ironicclient/tests/unit/v1/test_driver.py32
-rw-r--r--ironicclient/v1/driver.py15
-rw-r--r--releasenotes/notes/add-driver-cli-fields-selector-b0f527eb5f6fb2a9.yaml9
6 files changed, 203 insertions, 9 deletions
diff --git a/ironicclient/common/http.py b/ironicclient/common/http.py
index f2a1aef..b449599 100644
--- a/ironicclient/common/http.py
+++ b/ironicclient/common/http.py
@@ -37,7 +37,7 @@ from ironicclient import exc
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
# for full details.
DEFAULT_VER = '1.9'
-LAST_KNOWN_API_VERSION = 76
+LAST_KNOWN_API_VERSION = 77
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__)
diff --git a/ironicclient/osc/v1/baremetal_driver.py b/ironicclient/osc/v1/baremetal_driver.py
index 9c3323c..21c0929 100644
--- a/ironicclient/osc/v1/baremetal_driver.py
+++ b/ironicclient/osc/v1/baremetal_driver.py
@@ -12,8 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
-
-
+import itertools
import logging
from osc_lib.command import command
@@ -39,11 +38,23 @@ class ListBaremetalDriver(command.Lister):
help='Type of driver ("classic" or "dynamic"). '
'The default is to list all of them.'
)
- parser.add_argument(
+ display_group = parser.add_mutually_exclusive_group()
+ display_group.add_argument(
'--long',
action='store_true',
default=None,
help="Show detailed information about the drivers.")
+ display_group.add_argument(
+ '--fields',
+ nargs='+',
+ dest='fields',
+ metavar='<field>',
+ action='append',
+ default=[],
+ choices=res_fields.DRIVER_DETAILED_RESOURCE.fields,
+ help=_("One or more node fields. Only these fields will be "
+ "fetched from the server. Can not be used when '--long' "
+ "is specified."))
return parser
def take_action(self, parsed_args):
@@ -55,6 +66,12 @@ class ListBaremetalDriver(command.Lister):
if parsed_args.long:
labels = res_fields.DRIVER_DETAILED_RESOURCE.labels
columns = res_fields.DRIVER_DETAILED_RESOURCE.fields
+ elif parsed_args.fields:
+ fields = itertools.chain.from_iterable(parsed_args.fields)
+ resource = res_fields.Resource(list(fields))
+ columns = resource.fields
+ labels = resource.labels
+ params['fields'] = columns
else:
labels = res_fields.DRIVER_RESOURCE.labels
columns = res_fields.DRIVER_RESOURCE.fields
@@ -213,13 +230,26 @@ class ShowBaremetalDriver(command.ShowOne):
'driver',
metavar='<driver>',
help=_('Name of the driver.'))
+ parser.add_argument(
+ '--fields',
+ nargs='+',
+ dest='fields',
+ metavar='<field>',
+ action='append',
+ default=[],
+ choices=res_fields.DRIVER_DETAILED_RESOURCE.fields,
+ help=_("One or more node fields. Only these fields will be "
+ "fetched from the server."))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
- driver = baremetal_client.driver.get(parsed_args.driver)._info
+ fields = list(itertools.chain.from_iterable(parsed_args.fields))
+ fields = fields if fields else None
+ driver = baremetal_client.driver.get(parsed_args.driver,
+ fields=fields)._info
driver.pop("links", None)
driver.pop("properties", None)
# For list-type properties, show the values as comma separated
diff --git a/ironicclient/tests/unit/osc/v1/test_baremetal_driver.py b/ironicclient/tests/unit/osc/v1/test_baremetal_driver.py
index c6c935f..15ef919 100644
--- a/ironicclient/tests/unit/osc/v1/test_baremetal_driver.py
+++ b/ironicclient/tests/unit/osc/v1/test_baremetal_driver.py
@@ -146,6 +146,69 @@ class TestListBaremetalDriver(TestBaremetalDriver):
),)
self.assertEqual(datalist, tuple(data))
+ def test_baremetal_driver_list_fields(self):
+ arglist = [
+ '--fields', 'name', 'hosts'
+ ]
+ verifylist = [
+ ('fields', [['name', 'hosts']])
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ kwargs = {
+ 'driver_type': None,
+ 'detail': None,
+ 'fields': ('name', 'hosts')
+ }
+
+ self.baremetal_mock.driver.list.assert_called_with(**kwargs)
+
+ def test_baremetal_driver_list_fields_multiple(self):
+ arglist = [
+ '--fields', 'name',
+ '--fields', 'hosts', 'type'
+ ]
+ verifylist = [
+ ('fields', [['name'], ['hosts', 'type']])
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ kwargs = {
+ 'driver_type': None,
+ 'detail': None,
+ 'fields': ('name', 'hosts', 'type')
+ }
+
+ self.baremetal_mock.driver.list.assert_called_with(**kwargs)
+
+ def test_baremetal_driver_list_invalid_fields(self):
+ arglist = [
+ '--fields', 'name', 'invalid'
+ ]
+ verifylist = [
+ ('fields', [['name', 'invalid']])
+ ]
+ self.assertRaises(oscutils.ParserException,
+ self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_baremetal_driver_list_fields_with_long(self):
+ arglist = [
+ '--fields', 'name', 'hosts',
+ '--long'
+ ]
+ verifylist = [
+ ('fields', [['name', 'invalid']]),
+ ('long', True)
+ ]
+ self.assertRaises(oscutils.ParserException,
+ self.check_parser,
+ self.cmd, arglist, verifylist)
+
class TestListBaremetalDriverProperty(TestBaremetalDriver):
@@ -362,7 +425,7 @@ class TestShowBaremetalDriver(TestBaremetalDriver):
columns, data = self.cmd.take_action(parsed_args)
args = ['fakedrivername']
- self.baremetal_mock.driver.get.assert_called_with(*args)
+ self.baremetal_mock.driver.get.assert_called_with(*args, fields=None)
self.assertFalse(self.baremetal_mock.driver.properties.called)
collist = ('default_bios_interface', 'default_boot_interface',
@@ -420,3 +483,54 @@ class TestShowBaremetalDriver(TestBaremetalDriver):
self.assertRaises(oscutils.ParserException,
self.check_parser,
self.cmd, arglist, verifylist)
+
+ def test_baremetal_driver_show_fields(self):
+ arglist = [
+ 'fakedrivername',
+ '--fields', 'name', 'hosts'
+ ]
+ verifylist = [
+ ('driver', 'fakedrivername'),
+ ('fields', [['name', 'hosts']])
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ args = ['fakedrivername']
+ fields = ['name', 'hosts']
+ self.baremetal_mock.driver.get.assert_called_with(*args, fields=fields)
+ self.assertFalse(self.baremetal_mock.driver.properties.called)
+
+ def test_baremetal_driver_show_fields_multiple(self):
+ arglist = [
+ 'fakedrivername',
+ '--fields', 'name',
+ '--fields', 'hosts', 'type'
+ ]
+ verifylist = [
+ ('driver', 'fakedrivername'),
+ ('fields', [['name'], ['hosts', 'type']])
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ args = ['fakedrivername']
+ fields = ['name', 'hosts', 'type']
+ self.baremetal_mock.driver.get.assert_called_with(*args, fields=fields)
+ self.assertFalse(self.baremetal_mock.driver.properties.called)
+
+ def test_baremetal_driver_show_invalid_fields(self):
+ arglist = [
+ 'fakedrivername',
+ '--fields', 'name', 'invalid'
+ ]
+ verifylist = [
+ ('driver', 'fakedrivername'),
+ ('fields', [['name', 'invalid']])
+ ]
+
+ self.assertRaises(oscutils.ParserException,
+ self.check_parser,
+ self.cmd, arglist, verifylist)
diff --git a/ironicclient/tests/unit/v1/test_driver.py b/ironicclient/tests/unit/v1/test_driver.py
index 0d1d562..9a32d01 100644
--- a/ironicclient/tests/unit/v1/test_driver.py
+++ b/ironicclient/tests/unit/v1/test_driver.py
@@ -77,6 +77,13 @@ fake_responses = {
{'drivers': [DRIVER1]},
),
},
+ '/v1/drivers/?fields=name,hosts':
+ {
+ 'GET': (
+ {},
+ {'drivers': [DRIVER1]}
+ )
+ },
'/v1/drivers/%s' % DRIVER1['name']:
{
'GET': (
@@ -84,6 +91,13 @@ fake_responses = {
DRIVER1
),
},
+ '/v1/drivers/%s?fields=name,hosts' % DRIVER1['name']:
+ {
+ 'GET': (
+ {},
+ DRIVER1,
+ ),
+ },
'/v1/drivers/%s/properties' % DRIVER2['name']:
{
'GET': (
@@ -136,6 +150,24 @@ class DriverManagerTest(testtools.TestCase):
self.assertEqual(DRIVER1, driver_attr)
+ def test_driver_list_fields(self):
+ drivers = self.mgr.list(fields=['name', 'hosts'])
+ expect = [
+ ('GET', '/v1/drivers/?fields=name,hosts', {}, None),
+ ]
+ self.assertEqual(expect, self.api.calls)
+ self.assertThat(drivers, matchers.HasLength(1))
+
+ def test_driver_show_fields(self):
+ driver_ = self.mgr.get(DRIVER1['name'], fields=['name', 'hosts'])
+ expect = [
+ ('GET', '/v1/drivers/%s?fields=name,hosts' %
+ DRIVER1['name'], {}, None)
+ ]
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual(DRIVER1['name'], driver_.name)
+ self.assertEqual(DRIVER1['hosts'], driver_.hosts)
+
def test_driver_properties(self):
properties = self.mgr.properties(DRIVER2['name'])
expect = [
diff --git a/ironicclient/v1/driver.py b/ironicclient/v1/driver.py
index 289ee05..6b41be6 100644
--- a/ironicclient/v1/driver.py
+++ b/ironicclient/v1/driver.py
@@ -28,7 +28,7 @@ class DriverManager(base.Manager):
_resource_name = 'drivers'
def list(self, driver_type=None, detail=None, os_ironic_api_version=None,
- global_request_id=None):
+ global_request_id=None, fields=None):
"""Retrieve a list of drivers.
:param driver_type: Optional, string to filter the drivers by type.
@@ -41,13 +41,22 @@ class DriverManager(base.Manager):
the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
+ :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 drivers.
"""
+
filters = []
+ if detail and fields:
+ raise exc.InvalidAttribute(_("Can't fetch a subset of fields "
+ "with 'detail' set"))
if driver_type is not None:
filters.append('type=%s' % driver_type)
if detail is not None:
filters.append('detail=%s' % detail)
+ if fields is not None:
+ filters.append('fields=%s' % ','.join(fields))
path = ''
if filters:
@@ -57,8 +66,8 @@ class DriverManager(base.Manager):
global_request_id=global_request_id)
def get(self, driver_name, os_ironic_api_version=None,
- global_request_id=None):
- return self._get(resource_id=driver_name,
+ global_request_id=None, fields=None):
+ return self._get(resource_id=driver_name, fields=fields,
os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)
diff --git a/releasenotes/notes/add-driver-cli-fields-selector-b0f527eb5f6fb2a9.yaml b/releasenotes/notes/add-driver-cli-fields-selector-b0f527eb5f6fb2a9.yaml
new file mode 100644
index 0000000..4c86f57
--- /dev/null
+++ b/releasenotes/notes/add-driver-cli-fields-selector-b0f527eb5f6fb2a9.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Adds support for fields selector in driver cli.
+ See `story 1674775
+ <https://storyboard.openstack.org/#!/story/1674775>`_.
+
+ * ``openstack baremetal driver list --fields <field> [<field> ...]``
+ * ``openstack baremetal driver show <driver_name> --fields <field> [<field> ...]``