summaryrefslogtreecommitdiff
path: root/ironicclient/osc
diff options
context:
space:
mode:
authorDmitry Tantsur <divius.inside@gmail.com>2019-02-12 16:24:05 +0100
committerDmitry Tantsur <divius.inside@gmail.com>2019-02-16 15:51:38 +0100
commite0708a16efa48d872a9f088af856809f523f03dc (patch)
treeb456e4c82c8402ccaa5a68e4dd268ed82ab07430 /ironicclient/osc
parente8a6d447f803c115ed57064e6fada3e9d6f30794 (diff)
downloadpython-ironicclient-e0708a16efa48d872a9f088af856809f523f03dc.tar.gz
Allocation API: client API and CLI
Adds the Python API to create/list/view/delete allocations, as well as the OpenStackClient commands. Change-Id: Ib97ee888c4a7b6dfa38934f02372284aa4c781a0 Story: #2004341 Task: #28028
Diffstat (limited to 'ironicclient/osc')
-rw-r--r--ironicclient/osc/v1/baremetal_allocation.py269
1 files changed, 269 insertions, 0 deletions
diff --git a/ironicclient/osc/v1/baremetal_allocation.py b/ironicclient/osc/v1/baremetal_allocation.py
new file mode 100644
index 0000000..31de0cf
--- /dev/null
+++ b/ironicclient/osc/v1/baremetal_allocation.py
@@ -0,0 +1,269 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import itertools
+import logging
+
+from osc_lib.command import command
+from osc_lib import utils as oscutils
+
+from ironicclient.common.i18n import _
+from ironicclient.common import utils
+from ironicclient import exc
+from ironicclient.v1 import resource_fields as res_fields
+
+
+class CreateBaremetalAllocation(command.ShowOne):
+ """Create a new baremetal allocation."""
+
+ log = logging.getLogger(__name__ + ".CreateBaremetalAllocation")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateBaremetalAllocation, self).get_parser(prog_name)
+
+ parser.add_argument(
+ '--resource-class',
+ dest='resource_class',
+ required=True,
+ help=_('Resource class to request.'))
+ parser.add_argument(
+ '--trait',
+ action='append',
+ dest='traits',
+ help=_('A trait to request. Can be specified multiple times.'))
+ parser.add_argument(
+ '--candidate-node',
+ action='append',
+ dest='candidate_nodes',
+ help=_('A candidate node for this allocation. Can be specified '
+ 'multiple times. If at least one is specified, only the '
+ 'provided candidate nodes are considered for the '
+ 'allocation.'))
+ parser.add_argument(
+ '--name',
+ dest='name',
+ help=_('Unique name of the allocation.'))
+ parser.add_argument(
+ '--uuid',
+ dest='uuid',
+ help=_('UUID of the allocation.'))
+ parser.add_argument(
+ '--extra',
+ metavar="<key=value>",
+ action='append',
+ help=_("Record arbitrary key/value metadata. "
+ "Can be specified multiple times."))
+ parser.add_argument(
+ '--wait',
+ type=int,
+ dest='wait_timeout',
+ default=None,
+ metavar='<time-out>',
+ const=0,
+ nargs='?',
+ help=_("Wait for the new allocation to become active. An error "
+ "is returned if allocation fails and --wait is used. "
+ "Optionally takes a timeout value (in seconds). The "
+ "default value is 0, meaning it will wait indefinitely."))
+
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+ baremetal_client = self.app.client_manager.baremetal
+
+ field_list = ['name', 'uuid', 'extra', 'resource_class', 'traits',
+ 'candidate_nodes']
+ fields = dict((k, v) for (k, v) in vars(parsed_args).items()
+ if k in field_list and v is not None)
+
+ fields = utils.args_array_to_dict(fields, 'extra')
+ allocation = baremetal_client.allocation.create(**fields)
+ if parsed_args.wait_timeout is not None:
+ allocation = baremetal_client.allocation.wait(
+ allocation.uuid, timeout=parsed_args.wait_timeout)
+
+ data = dict([(f, getattr(allocation, f, '')) for f in
+ res_fields.ALLOCATION_DETAILED_RESOURCE.fields])
+
+ return self.dict2columns(data)
+
+
+class ShowBaremetalAllocation(command.ShowOne):
+ """Show baremetal allocation details."""
+
+ log = logging.getLogger(__name__ + ".ShowBaremetalAllocation")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowBaremetalAllocation, self).get_parser(prog_name)
+ parser.add_argument(
+ "allocation",
+ metavar="<id>",
+ help=_("UUID or name of the allocation"))
+ parser.add_argument(
+ '--fields',
+ nargs='+',
+ dest='fields',
+ metavar='<field>',
+ action='append',
+ choices=res_fields.ALLOCATION_DETAILED_RESOURCE.fields,
+ default=[],
+ help=_("One or more allocation 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
+ fields = list(itertools.chain.from_iterable(parsed_args.fields))
+ fields = fields if fields else None
+
+ allocation = baremetal_client.allocation.get(
+ parsed_args.allocation, fields=fields)._info
+
+ allocation.pop("links", None)
+ return zip(*sorted(allocation.items()))
+
+
+class ListBaremetalAllocation(command.Lister):
+ """List baremetal allocations."""
+
+ log = logging.getLogger(__name__ + ".ListBaremetalAllocation")
+
+ def get_parser(self, prog_name):
+ parser = super(ListBaremetalAllocation, self).get_parser(prog_name)
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ type=int,
+ help=_('Maximum number of allocations to return per request, '
+ '0 for no limit. Default is the maximum number used '
+ 'by the Baremetal API Service.'))
+ parser.add_argument(
+ '--marker',
+ metavar='<allocation>',
+ help=_('Port group UUID (for example, of the last allocation in '
+ 'the list from a previous request). Returns the list of '
+ 'allocations after this UUID.'))
+ parser.add_argument(
+ '--sort',
+ metavar="<key>[:<direction>]",
+ help=_('Sort output by specified allocation fields and directions '
+ '(asc or desc) (default: asc). Multiple fields and '
+ 'directions can be specified, separated by comma.'))
+ parser.add_argument(
+ '--node',
+ metavar='<node>',
+ help=_("Only list allocations of this node (name or UUID)."))
+ parser.add_argument(
+ '--resource-class',
+ metavar='<resource_class>',
+ help=_("Only list allocations with this resource class."))
+ parser.add_argument(
+ '--state',
+ metavar='<state>',
+ help=_("Only list allocations in this state."))
+
+ # NOTE(dtantsur): the allocation API does not expose the 'detail' flag,
+ # but some fields are inconvenient to display in a table, so we emulate
+ # it on the client side.
+ display_group = parser.add_mutually_exclusive_group(required=False)
+ display_group.add_argument(
+ '--long',
+ default=False,
+ help=_("Show detailed information about the allocations."),
+ action='store_true')
+ display_group.add_argument(
+ '--fields',
+ nargs='+',
+ dest='fields',
+ metavar='<field>',
+ action='append',
+ default=[],
+ choices=res_fields.ALLOCATION_DETAILED_RESOURCE.fields,
+ help=_("One or more allocation 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):
+ self.log.debug("take_action(%s)", parsed_args)
+ client = self.app.client_manager.baremetal
+
+ params = {}
+ if parsed_args.limit is not None and parsed_args.limit < 0:
+ raise exc.CommandError(
+ _('Expected non-negative --limit, got %s') %
+ parsed_args.limit)
+ params['limit'] = parsed_args.limit
+ params['marker'] = parsed_args.marker
+ for field in ('node', 'resource_class', 'state'):
+ value = getattr(parsed_args, field)
+ if value is not None:
+ params[field] = value
+
+ if parsed_args.long:
+ columns = res_fields.ALLOCATION_DETAILED_RESOURCE.fields
+ labels = res_fields.ALLOCATION_DETAILED_RESOURCE.labels
+ 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:
+ columns = res_fields.ALLOCATION_RESOURCE.fields
+ labels = res_fields.ALLOCATION_RESOURCE.labels
+
+ self.log.debug("params(%s)", params)
+ data = client.allocation.list(**params)
+
+ data = oscutils.sort_items(data, parsed_args.sort)
+
+ return (labels,
+ (oscutils.get_item_properties(s, columns) for s in data))
+
+
+class DeleteBaremetalAllocation(command.Command):
+ """Unregister baremetal allocation(s)."""
+
+ log = logging.getLogger(__name__ + ".DeleteBaremetalAllocation")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteBaremetalAllocation, self).get_parser(prog_name)
+ parser.add_argument(
+ "allocations",
+ metavar="<allocation>",
+ nargs="+",
+ help=_("Allocations(s) to delete (name or UUID)."))
+
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+
+ baremetal_client = self.app.client_manager.baremetal
+
+ failures = []
+ for allocation in parsed_args.allocations:
+ try:
+ baremetal_client.allocation.delete(allocation)
+ print(_('Deleted allocation %s') % allocation)
+ except exc.ClientException as e:
+ failures.append(_("Failed to delete allocation "
+ "%(allocation)s: %(error)s")
+ % {'allocation': allocation, 'error': e})
+
+ if failures:
+ raise exc.ClientException("\n".join(failures))