From cf2de7cb63f190b2483e4b0ec398ba7d0d84afba Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Tue, 21 Sep 2021 20:01:46 +0000 Subject: [PATCH] Add commands for resource property API This adds commands for interacting with the resource property API that is specified in blueprint resource-properties-discovery-api. Change-Id: Ibe024dc9b2d02d4061ab7eb68a4607d1356a6318 --- blazarclient/base.py | 8 +++ blazarclient/command.py | 66 +++++++++++++++++++ blazarclient/exception.py | 6 ++ blazarclient/shell.py | 3 + blazarclient/v1/hosts.py | 41 ++++++++++++ blazarclient/v1/shell_commands/hosts.py | 66 +++++++++++++++++++ ...st-resource-property-9ac5c21bd3ca6699.yaml | 9 +++ setup.cfg | 3 + 8 files changed, 202 insertions(+) create mode 100644 releasenotes/notes/host-resource-property-9ac5c21bd3ca6699.yaml diff --git a/blazarclient/base.py b/blazarclient/base.py index 8861f87..1a095eb 100644 --- a/blazarclient/base.py +++ b/blazarclient/base.py @@ -67,6 +67,14 @@ class RequestManager(object): """ return self.request(url, 'PUT', body=body) + def patch(self, url, body): + """Sends patch request to Blazar. + + :param url: URL to the wanted Blazar resource. + :type url: str + """ + return self.request(url, 'PATCH', body=body) + def request(self, url, method, **kwargs): """Base request method. diff --git a/blazarclient/command.py b/blazarclient/command.py index 0de9c3e..459b090 100644 --- a/blazarclient/command.py +++ b/blazarclient/command.py @@ -307,3 +307,69 @@ class ShowCommand(BlazarCommand, show.ShowOne): data = resource_manager.get(res_id) self.format_output_data(data) return list(zip(*sorted(data.items()))) + + +class ShowPropertyCommand(BlazarCommand, show.ShowOne): + """Show information of a given resource property.""" + + api = 'reservation' + resource = None + log = None + + def get_parser(self, prog_name): + parser = super(ShowPropertyCommand, self).get_parser(prog_name) + parser.add_argument('property_name', metavar='PROPERTY_NAME', + help='Name of property.') + return parser + + def get_data(self, parsed_args): + self.log.debug('get_data(%s)' % parsed_args) + blazar_client = self.get_client() + resource_manager = getattr(blazar_client, self.resource) + data = resource_manager.get_property(parsed_args.property_name) + if parsed_args.formatter == 'table': + self.format_output_data(data) + return list(zip(*sorted(data.items()))) + + +class UpdatePropertyCommand(BlazarCommand): + api = 'reservation' + resource = None + log = None + + def run(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + blazar_client = self.get_client() + body = self.args2body(parsed_args) + resource_manager = getattr(blazar_client, self.resource) + resource_manager.set_property(**body) + print( + 'Updated %s property: %s' % ( + self.resource, parsed_args.property_name), + file=self.app.stdout) + return + + def get_parser(self, prog_name): + parser = super(UpdatePropertyCommand, self).get_parser(prog_name) + parser.add_argument( + 'property_name', metavar='PROPERTY_NAME', + help='Name of property to patch.' + ) + parser.add_argument( + '--private', + action='store_true', + default=False, + help='Set property to private.' + ) + parser.add_argument( + '--public', + action='store_true', + default=False, + help='Set property to public.' + ) + return parser + + def args2body(self, parsed_args): + return dict( + property_name=parsed_args.property_name, + private=(parsed_args.private is True)) diff --git a/blazarclient/exception.py b/blazarclient/exception.py index 3e8c75d..a381aee 100644 --- a/blazarclient/exception.py +++ b/blazarclient/exception.py @@ -90,3 +90,9 @@ class InsufficientAuthInformation(BlazarClientException): "for the authentication. The instance of " "keystoneauth1.session.Session class is required.") code = 400 + + +class ResourcePropertyNotFound(BlazarClientException): + """Occurs if the resource property specified does not exist""" + message = _("The resource property does not exist.") + code = 404 diff --git a/blazarclient/shell.py b/blazarclient/shell.py index 0536cc0..8d89942 100644 --- a/blazarclient/shell.py +++ b/blazarclient/shell.py @@ -44,6 +44,9 @@ COMMANDS_V1 = { 'host-create': hosts.CreateHost, 'host-update': hosts.UpdateHost, 'host-delete': hosts.DeleteHost, + 'host-property-list': hosts.ListHostProperties, + 'host-property-show': hosts.ShowHostProperty, + 'host-property-set': hosts.UpdateHostProperty, 'floatingip-list': floatingips.ListFloatingIPs, 'floatingip-show': floatingips.ShowFloatingIP, 'floatingip-create': floatingips.CreateFloatingIP, diff --git a/blazarclient/v1/hosts.py b/blazarclient/v1/hosts.py index d66864b..5954b37 100644 --- a/blazarclient/v1/hosts.py +++ b/blazarclient/v1/hosts.py @@ -14,6 +14,7 @@ # limitations under the License. from blazarclient import base +from blazarclient import exception from blazarclient.i18n import _ @@ -52,3 +53,43 @@ class ComputeHostClientManager(base.BaseClientManager): if sort_by: hosts = sorted(hosts, key=lambda l: l[sort_by]) return hosts + + def list_properties(self, detail=False, all=False, sort_by=None): + url = '/os-hosts/properties' + + query_parts = [] + if detail: + query_parts.append("detail=True") + if all: + query_parts.append("all=True") + if query_parts: + url += "?" + "&".join(query_parts) + + resp, body = self.request_manager.get(url) + resource_properties = body['resource_properties'] + + # Values is a reserved word in cliff so need to rename values column. + if detail: + for p in resource_properties: + p['property_values'] = p['values'] + del p['values'] + + if sort_by: + resource_properties = sorted(resource_properties, + key=lambda l: l[sort_by]) + return resource_properties + + def get_property(self, property_name): + resource_property = [ + x for x in self.list_properties(detail=True) + if x['property'] == property_name] + if not resource_property: + raise exception.ResourcePropertyNotFound() + return resource_property[0] + + def set_property(self, property_name, private): + data = {'private': private} + resp, body = self.request_manager.patch( + '/os-hosts/properties/%s' % property_name, body=data) + + return body['resource_property'] diff --git a/blazarclient/v1/shell_commands/hosts.py b/blazarclient/v1/shell_commands/hosts.py index b5d832f..250c376 100644 --- a/blazarclient/v1/shell_commands/hosts.py +++ b/blazarclient/v1/shell_commands/hosts.py @@ -16,6 +16,7 @@ import logging from blazarclient import command +from blazarclient import exception HOST_ID_PATTERN = '^[0-9]+$' @@ -120,3 +121,68 @@ class DeleteHost(command.DeleteCommand): log = logging.getLogger(__name__ + '.DeleteHost') name_key = 'hypervisor_hostname' id_pattern = HOST_ID_PATTERN + + +class ShowHostProperty(command.ShowPropertyCommand): + """Show host property.""" + resource = 'host' + json_indent = 4 + log = logging.getLogger(__name__ + '.ShowHostProperty') + + +class ListHostProperties(command.ListCommand): + """List host properties.""" + resource = 'host' + log = logging.getLogger(__name__ + '.ListHostProperties') + list_columns = ['property', 'private', 'property_values'] + + def args2body(self, parsed_args): + params = { + 'detail': parsed_args.detail, + 'all': parsed_args.all, + } + if parsed_args.sort_by: + if parsed_args.sort_by in self.list_columns: + params['sort_by'] = parsed_args.sort_by + else: + msg = 'Invalid sort option %s' % parsed_args.sort_by + raise exception.BlazarClientException(msg) + + return params + + def retrieve_list(self, parsed_args): + """Retrieve a list of resources from Blazar server.""" + blazar_client = self.get_client() + body = self.args2body(parsed_args) + resource_manager = getattr(blazar_client, self.resource) + data = resource_manager.list_properties(**body) + return data + + def get_parser(self, prog_name): + parser = super(ListHostProperties, self).get_parser(prog_name) + parser.add_argument( + '--detail', + action='store_true', + help='Return properties with values and attributes.', + default=False + ) + parser.add_argument( + '--sort-by', metavar="", + help='column name used to sort result', + default='property' + ) + parser.add_argument( + '--all', + action='store_true', + help='Return all properties, public and private.', + default=False + ) + return parser + + +class UpdateHostProperty(command.UpdatePropertyCommand): + """Update attributes of a host property.""" + resource = 'host' + json_indent = 4 + log = logging.getLogger(__name__ + '.UpdateHostProperty') + name_key = 'property_name' diff --git a/releasenotes/notes/host-resource-property-9ac5c21bd3ca6699.yaml b/releasenotes/notes/host-resource-property-9ac5c21bd3ca6699.yaml new file mode 100644 index 0000000..ff62e0b --- /dev/null +++ b/releasenotes/notes/host-resource-property-9ac5c21bd3ca6699.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Added support for managing host resource properties using the following new + commands: + + * ``host-property-list`` + * ``host-property-show`` + * ``host-property-set`` diff --git a/setup.cfg b/setup.cfg index c9e6364..34c9b21 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,9 @@ openstack.reservation.v1 = reservation_host_list = blazarclient.v1.shell_commands.hosts:ListHosts reservation_host_set = blazarclient.v1.shell_commands.hosts:UpdateHost reservation_host_show = blazarclient.v1.shell_commands.hosts:ShowHost + reservation_host_property_list = blazarclient.v1.shell_commands.hosts:ListHostProperties + reservation_host_property_show = blazarclient.v1.shell_commands.hosts:ShowHostProperty + reservation_host_property_set = blazarclient.v1.shell_commands.hosts:UpdateHostProperty reservation_lease_create = blazarclient.v1.shell_commands.leases:CreateLeaseBase reservation_lease_delete = blazarclient.v1.shell_commands.leases:DeleteLease reservation_lease_list = blazarclient.v1.shell_commands.leases:ListLeases