diff options
Diffstat (limited to 'openstackclient/compute')
| -rw-r--r-- | openstackclient/compute/v2/console.py | 7 | ||||
| -rw-r--r-- | openstackclient/compute/v2/flavor.py | 28 | ||||
| -rw-r--r-- | openstackclient/compute/v2/host.py | 18 | ||||
| -rw-r--r-- | openstackclient/compute/v2/server.py | 225 | ||||
| -rw-r--r-- | openstackclient/compute/v2/server_backup.py | 16 | ||||
| -rw-r--r-- | openstackclient/compute/v2/server_image.py | 15 | ||||
| -rw-r--r-- | openstackclient/compute/v2/service.py | 26 | ||||
| -rw-r--r-- | openstackclient/compute/v2/usage.py | 8 |
8 files changed, 244 insertions, 99 deletions
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 25f92108..b2f7288f 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -15,8 +15,6 @@ """Compute v2 Console action implementations""" -import sys - from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import utils @@ -60,7 +58,10 @@ class ShowConsoleLog(command.Command): length += 1 data = server.get_console_output(length=length) - sys.stdout.write(data) + + if data and data[-1] != '\n': + data += '\n' + self.app.stdout.write(data) class ShowConsoleURL(command.ShowOne): diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 0f5dd742..2cc5f1e8 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -17,6 +17,7 @@ import logging +from novaclient import api_versions from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions @@ -134,6 +135,12 @@ class CreateFlavor(command.ShowOne): help=_("Allow <project> to access private flavor (name or ID) " "(Must be used with --private option)"), ) + parser.add_argument( + '--description', + metavar='<description>', + help=_("Description for the flavor.(Supported by API versions " + "'2.55' - '2.latest'") + ) identity_common.add_project_domain_option_to_parser(parser) return parser @@ -145,6 +152,11 @@ class CreateFlavor(command.ShowOne): msg = _("--project is only allowed with --private") raise exceptions.CommandError(msg) + if parsed_args.description: + if compute_client.api_version < api_versions.APIVersion("2.55"): + msg = _("--os-compute-api-version 2.55 or later is required") + raise exceptions.CommandError(msg) + args = ( parsed_args.name, parsed_args.ram, @@ -154,7 +166,8 @@ class CreateFlavor(command.ShowOne): parsed_args.ephemeral, parsed_args.swap, parsed_args.rxtx_factor, - parsed_args.public + parsed_args.public, + parsed_args.description ) flavor = compute_client.flavors.create(*args) @@ -332,6 +345,12 @@ class SetFlavor(command.Command): help=_('Set flavor access to project (name or ID) ' '(admin only)'), ) + parser.add_argument( + '--description', + metavar='<description>', + help=_("Set description for the flavor.(Supported by API " + "versions '2.55' - '2.latest'") + ) identity_common.add_project_domain_option_to_parser(parser) return parser @@ -380,6 +399,13 @@ class SetFlavor(command.Command): raise exceptions.CommandError(_("Command Failed: One or more of" " the operations failed")) + if parsed_args.description: + if compute_client.api_version < api_versions.APIVersion("2.55"): + msg = _("--os-compute-api-version 2.55 or later is required") + raise exceptions.CommandError(msg) + compute_client.flavors.update(flavor=parsed_args.flavor, + description=parsed_args.description) + class ShowFlavor(command.ShowOne): _description = _("Display flavor details") diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index a495b367..9fdfd927 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -40,9 +40,9 @@ class ListHost(command.Lister): "Service", "Zone" ) - data = compute_client.hosts.list_all(parsed_args.zone) + data = compute_client.api.host_list(parsed_args.zone) return (columns, - (utils.get_item_properties( + (utils.get_dict_properties( s, columns, ) for s in data)) @@ -95,13 +95,7 @@ class SetHost(command.Command): compute_client = self.app.client_manager.compute - # More than one hosts will be returned by using find_resource() - # so that the return value cannot be used in host update() method. - # find_resource() is just used for checking existence of host and - # keeping the exception message consistent with other commands. - utils.find_resource(compute_client.hosts, parsed_args.host) - - compute_client.hosts.update( + compute_client.api.host_set( parsed_args.host, kwargs ) @@ -128,8 +122,10 @@ class ShowHost(command.Lister): "Memory MB", "Disk GB" ) - data = compute_client.hosts.get(parsed_args.host) + + data = compute_client.api.host_show(parsed_args.host) + return (columns, - (utils.get_item_properties( + (utils.get_dict_properties( s, columns, ) for s in data)) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index f40fbdf6..b82f895c 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -20,7 +20,6 @@ import getpass import io import logging import os -import sys from novaclient.v2 import servers from osc_lib.cli import parseractions @@ -32,6 +31,7 @@ import six from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import common as network_common LOG = logging.getLogger(__name__) @@ -120,17 +120,21 @@ def _prefix_checked_value(prefix): return func -def _prep_server_detail(compute_client, image_client, server): +def _prep_server_detail(compute_client, image_client, server, refresh=True): """Prepare the detailed server dict for printing :param compute_client: a compute client instance + :param image_client: an image client instance :param server: a Server resource + :param refresh: Flag indicating if ``server`` is already the latest version + or if it needs to be refreshed, for example when showing + the latest details of a server after creating it. :rtype: a dict of server details """ - info = server._info.copy() - - server = utils.find_resource(compute_client.servers, info['id']) - info.update(server._info) + info = server.to_dict() + if refresh: + server = utils.find_resource(compute_client.servers, info['id']) + info.update(server.to_dict()) # Convert the image blob to a name image_info = info.get('image', {}) @@ -144,12 +148,18 @@ def _prep_server_detail(compute_client, image_client, server): # Convert the flavor blob to a name flavor_info = info.get('flavor', {}) - flavor_id = flavor_info.get('id', '') - try: - flavor = utils.find_resource(compute_client.flavors, flavor_id) - info['flavor'] = "%s (%s)" % (flavor.name, flavor_id) - except Exception: - info['flavor'] = flavor_id + # Microversion 2.47 puts the embedded flavor into the server response + # body but omits the id, so if not present we just expose the flavor + # dict in the server output. + if 'id' in flavor_info: + flavor_id = flavor_info.get('id', '') + try: + flavor = utils.find_resource(compute_client.flavors, flavor_id) + info['flavor'] = "%s (%s)" % (flavor.name, flavor_id) + except Exception: + info['flavor'] = flavor_id + else: + info['flavor'] = utils.format_dict(flavor_info) if 'os-extended-volumes:volumes_attached' in info: info.update( @@ -178,7 +188,7 @@ def _prep_server_detail(compute_client, image_client, server): if 'tenant_id' in info: info['project_id'] = info.pop('tenant_id') - # Map power state num to meanful string + # Map power state num to meaningful string if 'OS-EXT-STS:power_state' in info: info['OS-EXT-STS:power_state'] = _format_servers_list_power_state( info['OS-EXT-STS:power_state']) @@ -189,12 +199,6 @@ def _prep_server_detail(compute_client, image_client, server): return info -def _show_progress(progress): - if progress: - sys.stdout.write('\rProgress: %s' % progress) - sys.stdout.flush() - - class AddFixedIP(command.Command): _description = _("Add fixed IP address to server") @@ -234,11 +238,10 @@ class AddFixedIP(command.Command): ) -class AddFloatingIP(command.Command): +class AddFloatingIP(network_common.NetworkAndComputeCommand): _description = _("Add floating IP address to server") - def get_parser(self, prog_name): - parser = super(AddFloatingIP, self).get_parser(prog_name) + def update_parser_common(self, parser): parser.add_argument( "server", metavar="<server>", @@ -252,19 +255,37 @@ class AddFloatingIP(command.Command): parser.add_argument( "--fixed-ip-address", metavar="<ip-address>", - help=_("Fixed IP address to associate with this floating IP " - "address"), + help=_( + "Fixed IP address to associate with this floating IP address" + ), ) return parser - def take_action(self, parsed_args): + def take_action_network(self, client, parsed_args): compute_client = self.app.client_manager.compute + attrs = {} + obj = client.find_ip( + parsed_args.ip_address, + ignore_missing=False, + ) server = utils.find_resource( - compute_client.servers, parsed_args.server) + compute_client.servers, + parsed_args.server, + ) + port = list(client.ports(device_id=server.id))[0] + attrs['port_id'] = port.id + if parsed_args.fixed_ip_address: + attrs['fixed_ip_address'] = parsed_args.fixed_ip_address - server.add_floating_ip(parsed_args.ip_address, - parsed_args.fixed_ip_address) + client.update_ip(obj, **attrs) + + def take_action_compute(self, client, parsed_args): + client.api.floating_ip_add( + parsed_args.server, + parsed_args.ip_address, + fixed_address=parsed_args.fixed_ip_address, + ) class AddPort(command.Command): @@ -425,6 +446,12 @@ class CreateServer(command.ShowOne): help=_('Create server boot disk from this image (name or ID)'), ) disk_group.add_argument( + '--image-property', + metavar='<key=value>', + action=parseractions.KeyValueAction, + help=_("Image property to be matched"), + ) + disk_group.add_argument( '--volume', metavar='<volume>', help=_('Create server using this volume as the boot disk (name ' @@ -580,6 +607,12 @@ class CreateServer(command.ShowOne): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image @@ -592,6 +625,45 @@ class CreateServer(command.ShowOne): parsed_args.image, ) + if not image and parsed_args.image_property: + def emit_duplicated_warning(img, image_property): + img_uuid_list = [str(image.id) for image in img] + LOG.warning(_('Multiple matching images: %(img_uuid_list)s\n' + 'Using image: %(chosen_one)s') % + {'img_uuid_list': img_uuid_list, + 'chosen_one': img_uuid_list[0]}) + + def _match_image(image_api, wanted_properties): + image_list = image_api.image_list() + images_matched = [] + for img in image_list: + img_dict = {} + # exclude any unhashable entries + for key, value in img.items(): + try: + set([key, value]) + except TypeError: + pass + else: + img_dict[key] = value + if all(k in img_dict and img_dict[k] == v + for k, v in wanted_properties.items()): + images_matched.append(img) + else: + return [] + return images_matched + + images = _match_image(image_client.api, parsed_args.image_property) + if len(images) > 1: + emit_duplicated_warning(images, + parsed_args.image_property) + if images: + image = images[0] + else: + raise exceptions.CommandError(_("No images match the " + "property expected by " + "--image-property")) + # Lookup parsed_args.volume volume = None if parsed_args.volume: @@ -814,11 +886,11 @@ class CreateServer(command.ShowOne): server.id, callback=_show_progress, ): - sys.stdout.write('\n') + self.app.stdout.write('\n') else: LOG.error(_('Error creating server: %s'), parsed_args.server_name) - sys.stdout.write(_('Error creating server\n')) + self.app.stdout.write(_('Error creating server\n')) raise SystemExit details = _prep_server_detail(compute_client, image_client, server) @@ -872,6 +944,12 @@ class DeleteServer(command.Command): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_obj = utils.find_resource( @@ -883,11 +961,11 @@ class DeleteServer(command.Command): server_obj.id, callback=_show_progress, ): - sys.stdout.write('\n') + self.app.stdout.write('\n') else: LOG.error(_('Error deleting server: %s'), server_obj.id) - sys.stdout.write(_('Error deleting server\n')) + self.app.stdout.write(_('Error deleting server\n')) raise SystemExit @@ -1185,6 +1263,10 @@ class ListServer(command.Lister): s.flavor_name = flavor.name s.flavor_id = s.flavor['id'] else: + # TODO(mriedem): Fix this for microversion >= 2.47 where the + # flavor is embedded in the server response without the id. + # We likely need to drop the Flavor ID column in that case if + # --long is specified. s.flavor_name = '' s.flavor_id = '' @@ -1290,6 +1372,11 @@ class MigrateServer(command.Command): def take_action(self, parsed_args): + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute server = utils.find_resource( @@ -1313,13 +1400,14 @@ class MigrateServer(command.Command): if utils.wait_for_status( compute_client.servers.get, server.id, + success_status=['active', 'verify_resize'], callback=_show_progress, ): - sys.stdout.write(_('Complete\n')) + self.app.stdout.write(_('Complete\n')) else: LOG.error(_('Error migrating server: %s'), server.id) - sys.stdout.write(_('Error migrating server\n')) + self.app.stdout.write(_('Error migrating server\n')) raise SystemExit @@ -1380,6 +1468,12 @@ class RebootServer(command.Command): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute server = utils.find_resource( compute_client.servers, parsed_args.server) @@ -1391,11 +1485,11 @@ class RebootServer(command.Command): server.id, callback=_show_progress, ): - sys.stdout.write(_('Complete\n')) + self.app.stdout.write(_('Complete\n')) else: LOG.error(_('Error rebooting server: %s'), server.id) - sys.stdout.write(_('Error rebooting server\n')) + self.app.stdout.write(_('Error rebooting server\n')) raise SystemExit @@ -1428,6 +1522,12 @@ class RebuildServer(command.ShowOne): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image @@ -1435,7 +1535,8 @@ class RebuildServer(command.ShowOne): compute_client.servers, parsed_args.server) # If parsed_args.image is not set, default to the currently used one. - image_id = parsed_args.image or server._info.get('image', {}).get('id') + image_id = parsed_args.image or server.to_dict().get( + 'image', {}).get('id') image = utils.find_resource(image_client.images, image_id) server = server.rebuild(image, parsed_args.password) @@ -1445,14 +1546,15 @@ class RebuildServer(command.ShowOne): server.id, callback=_show_progress, ): - sys.stdout.write(_('Complete\n')) + self.app.stdout.write(_('Complete\n')) else: LOG.error(_('Error rebuilding server: %s'), server.id) - sys.stdout.write(_('Error rebuilding server\n')) + self.app.stdout.write(_('Error rebuilding server\n')) raise SystemExit - details = _prep_server_detail(compute_client, image_client, server) + details = _prep_server_detail(compute_client, image_client, server, + refresh=False) return zip(*sorted(six.iteritems(details))) @@ -1482,11 +1584,10 @@ class RemoveFixedIP(command.Command): server.remove_fixed_ip(parsed_args.ip_address) -class RemoveFloatingIP(command.Command): +class RemoveFloatingIP(network_common.NetworkAndComputeCommand): _description = _("Remove floating IP address from server") - def get_parser(self, prog_name): - parser = super(RemoveFloatingIP, self).get_parser(prog_name) + def update_parser_common(self, parser): parser.add_argument( "server", metavar="<server>", @@ -1501,13 +1602,21 @@ class RemoveFloatingIP(command.Command): ) return parser - def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + def take_action_network(self, client, parsed_args): + attrs = {} + obj = client.find_ip( + parsed_args.ip_address, + ignore_missing=False, + ) + attrs['port_id'] = None - server = utils.find_resource( - compute_client.servers, parsed_args.server) + client.update_ip(obj, **attrs) - server.remove_floating_ip(parsed_args.ip_address) + def take_action_compute(self, client, parsed_args): + client.api.floating_ip_remove( + parsed_args.server, + parsed_args.ip_address, + ) class RemovePort(command.Command): @@ -1727,6 +1836,11 @@ the new server and restart the old one.""") def take_action(self, parsed_args): + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute server = utils.find_resource( compute_client.servers, @@ -1745,11 +1859,11 @@ the new server and restart the old one.""") success_status=['active', 'verify_resize'], callback=_show_progress, ): - sys.stdout.write(_('Complete\n')) + self.app.stdout.write(_('Complete\n')) else: LOG.error(_('Error resizing server: %s'), server.id) - sys.stdout.write(_('Error resizing server\n')) + self.app.stdout.write(_('Error resizing server\n')) raise SystemExit elif parsed_args.confirm: compute_client.servers.confirm_resize(server) @@ -1890,7 +2004,9 @@ class ShelveServer(command.Command): class ShowServer(command.ShowOne): - _description = _("Show server details") + _description = _( + "Show server details. Specify ``--os-compute-api-version 2.47`` " + "or higher to see the embedded flavor information for the server.") def get_parser(self, prog_name): parser = super(ShowServer, self).get_parser(prog_name) @@ -1915,11 +2031,14 @@ class ShowServer(command.ShowOne): if parsed_args.diagnostics: (resp, data) = server.diagnostics() if not resp.status_code == 200: - sys.stderr.write(_("Error retrieving diagnostics data\n")) + self.app.stderr.write(_( + "Error retrieving diagnostics data\n" + )) return ({}, {}) else: data = _prep_server_detail(compute_client, - self.app.client_manager.image, server) + self.app.client_manager.image, server, + refresh=False) return zip(*sorted(six.iteritems(data))) diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index ddcf9101..a79f5f70 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -15,8 +15,6 @@ """Compute v2 Server action implementations""" -import sys - from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -26,12 +24,6 @@ import six from openstackclient.i18n import _ -def _show_progress(progress): - if progress: - sys.stderr.write('\rProgress: %s' % progress) - sys.stderr.flush() - - class CreateServerBackup(command.ShowOne): _description = _("Create a server backup image") @@ -74,6 +66,12 @@ class CreateServerBackup(command.ShowOne): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stderr.write('\rProgress: %s' % progress) + self.app.stderr.flush() + compute_client = self.app.client_manager.compute server = utils.find_resource( @@ -114,7 +112,7 @@ class CreateServerBackup(command.ShowOne): image.id, callback=_show_progress, ): - sys.stdout.write('\n') + self.app.stdout.write('\n') else: msg = _('Error creating server backup: %s') % parsed_args.name raise exceptions.CommandError(msg) diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index c66e0674..3bc5d94a 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -16,7 +16,6 @@ """Compute v2 Server action implementations""" import logging -import sys from osc_lib.command import command from osc_lib import exceptions @@ -30,12 +29,6 @@ from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -def _show_progress(progress): - if progress: - sys.stdout.write('\rProgress: %s' % progress) - sys.stdout.flush() - - class CreateServerImage(command.ShowOne): _description = _("Create a new server disk image from an existing server") @@ -64,6 +57,12 @@ class CreateServerImage(command.ShowOne): return parser def take_action(self, parsed_args): + + def _show_progress(progress): + if progress: + self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.flush() + compute_client = self.app.client_manager.compute server = utils.find_resource( @@ -92,7 +91,7 @@ class CreateServerImage(command.ShowOne): image_id, callback=_show_progress, ): - sys.stdout.write('\n') + self.app.stdout.write('\n') else: LOG.error(_('Error creating server image: %s'), parsed_args.server) diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 7331d29d..18e6d9d9 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -17,6 +17,7 @@ import logging +from novaclient import api_versions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -192,18 +193,23 @@ class SetService(command.Command): result += 1 force_down = None - try: - if parsed_args.down: - force_down = True - if parsed_args.up: - force_down = False - if force_down is not None: + if parsed_args.down: + force_down = True + if parsed_args.up: + force_down = False + if force_down is not None: + if compute_client.api_version < api_versions.APIVersion( + '2.11'): + msg = _('--os-compute-api-version 2.11 or later is ' + 'required') + raise exceptions.CommandError(msg) + try: cs.force_down(parsed_args.host, parsed_args.service, force_down=force_down) - except Exception: - state = "down" if force_down else "up" - LOG.error("Failed to set service state to %s", state) - result += 1 + except Exception: + state = "down" if force_down else "up" + LOG.error("Failed to set service state to %s", state) + result += 1 if result > 0: msg = _("Compute service %(service)s of host %(host)s failed to " diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 3edcffe4..4320bf90 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -16,7 +16,6 @@ """Usage action implementations""" import datetime -import sys from osc_lib.command import command from osc_lib import utils @@ -96,7 +95,7 @@ class ListUsage(command.Lister): pass if parsed_args.formatter == 'table' and len(usage_list) > 0: - sys.stdout.write(_("Usage from %(start)s to %(end)s: \n") % { + self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % { "start": start.strftime(dateformat), "end": end.strftime(dateformat), }) @@ -168,8 +167,9 @@ class ShowUsage(command.ShowOne): usage = compute_client.usage.get(project, start, end) if parsed_args.formatter == 'table': - sys.stdout.write(_("Usage from %(start)s to %(end)s on " - "project %(project)s: \n") % { + self.app.stdout.write(_( + "Usage from %(start)s to %(end)s on project %(project)s: \n" + ) % { "start": start.strftime(dateformat), "end": end.strftime(dateformat), "project": project, |
