summaryrefslogtreecommitdiff
path: root/openstackclient/compute
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/compute')
-rw-r--r--openstackclient/compute/v2/hypervisor.py137
-rw-r--r--openstackclient/compute/v2/hypervisor_stats.py40
-rw-r--r--openstackclient/compute/v2/server.py295
-rw-r--r--openstackclient/compute/v2/server_migration.py48
-rw-r--r--openstackclient/compute/v2/usage.py69
5 files changed, 387 insertions, 202 deletions
diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py
index 5f7497b5..d4b4003b 100644
--- a/openstackclient/compute/v2/hypervisor.py
+++ b/openstackclient/compute/v2/hypervisor.py
@@ -18,8 +18,8 @@
import json
import re
-from novaclient import api_versions
from novaclient import exceptions as nova_exceptions
+from openstack import utils as sdk_utils
from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
@@ -28,11 +28,44 @@ from osc_lib import utils
from openstackclient.i18n import _
+def _get_hypervisor_columns(item, client):
+ column_map = {'name': 'hypervisor_hostname'}
+ hidden_columns = ['location', 'servers']
+
+ if sdk_utils.supports_microversion(client, '2.88'):
+ hidden_columns.extend([
+ 'current_workload',
+ 'disk_available',
+ 'local_disk_free',
+ 'local_disk_size',
+ 'local_disk_used',
+ 'memory_free',
+ 'memory_size',
+ 'memory_used',
+ 'running_vms',
+ 'vcpus_used',
+ 'vcpus',
+ ])
+ else:
+ column_map.update({
+ 'disk_available': 'disk_available_least',
+ 'local_disk_free': 'free_disk_gb',
+ 'local_disk_size': 'local_gb',
+ 'local_disk_used': 'local_gb_used',
+ 'memory_free': 'free_ram_mb',
+ 'memory_used': 'memory_mb_used',
+ 'memory_size': 'memory_mb',
+ })
+
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
class ListHypervisor(command.Lister):
_description = _("List hypervisors")
def get_parser(self, prog_name):
- parser = super(ListHypervisor, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
'--matching',
metavar='<hostname>',
@@ -67,7 +100,7 @@ class ListHypervisor(command.Lister):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
list_opts = {}
@@ -78,7 +111,7 @@ class ListHypervisor(command.Lister):
raise exceptions.CommandError(msg)
if parsed_args.marker:
- if compute_client.api_version < api_versions.APIVersion('2.33'):
+ if not sdk_utils.supports_microversion(compute_client, '2.33'):
msg = _(
'--os-compute-api-version 2.33 or greater is required to '
'support the --marker option'
@@ -87,7 +120,7 @@ class ListHypervisor(command.Lister):
list_opts['marker'] = parsed_args.marker
if parsed_args.limit:
- if compute_client.api_version < api_versions.APIVersion('2.33'):
+ if not sdk_utils.supports_microversion(compute_client, '2.33'):
msg = _(
'--os-compute-api-version 2.33 or greater is required to '
'support the --limit option'
@@ -95,23 +128,43 @@ class ListHypervisor(command.Lister):
raise exceptions.CommandError(msg)
list_opts['limit'] = parsed_args.limit
- columns = (
+ column_headers = (
"ID",
"Hypervisor Hostname",
"Hypervisor Type",
"Host IP",
"State"
)
+ columns = (
+ 'id',
+ 'name',
+ 'hypervisor_type',
+ 'host_ip',
+ 'state'
+ )
if parsed_args.long:
- columns += ("vCPUs Used", "vCPUs", "Memory MB Used", "Memory MB")
+ if not sdk_utils.supports_microversion(compute_client, '2.88'):
+ column_headers += (
+ 'vCPUs Used',
+ 'vCPUs',
+ 'Memory MB Used',
+ 'Memory MB'
+ )
+ columns += (
+ 'vcpus_used',
+ 'vcpus',
+ 'memory_used',
+ 'memory_size'
+ )
if parsed_args.matching:
- data = compute_client.hypervisors.search(parsed_args.matching)
+ data = compute_client.find_hypervisor(
+ parsed_args.matching, ignore_missing=False)
else:
- data = compute_client.hypervisors.list(**list_opts)
+ data = compute_client.hypervisors(**list_opts, details=True)
return (
- columns,
+ column_headers,
(utils.get_item_properties(s, columns) for s in data),
)
@@ -120,7 +173,7 @@ class ShowHypervisor(command.ShowOne):
_description = _("Display hypervisor details")
def get_parser(self, prog_name):
- parser = super(ShowHypervisor, self).get_parser(prog_name)
+ parser = super().get_parser(prog_name)
parser.add_argument(
"hypervisor",
metavar="<hypervisor>",
@@ -129,20 +182,25 @@ class ShowHypervisor(command.ShowOne):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- hypervisor = utils.find_resource(compute_client.hypervisors,
- parsed_args.hypervisor)._info.copy()
+ compute_client = self.app.client_manager.sdk_connection.compute
+ hypervisor = compute_client.find_hypervisor(
+ parsed_args.hypervisor, ignore_missing=False).copy()
+
+ # Some of the properties in the hypervisor object need to be processed
+ # before they get reported to the user. We spend this section
+ # extracting the relevant details to be reported by modifying our
+ # copy of the hypervisor object.
+ aggregates = compute_client.aggregates()
+ hypervisor['aggregates'] = list()
+ service_details = hypervisor['service_details']
- aggregates = compute_client.aggregates.list()
- hypervisor["aggregates"] = list()
if aggregates:
# Hypervisors in nova cells are prefixed by "<cell>@"
- if "@" in hypervisor['service']['host']:
- cell, service_host = hypervisor['service']['host'].split(
- '@', 1)
+ if "@" in service_details['host']:
+ cell, service_host = service_details['host'].split('@', 1)
else:
cell = None
- service_host = hypervisor['service']['host']
+ service_host = service_details['host']
if cell:
# The host aggregates are also prefixed by "<cell>@"
@@ -154,42 +212,45 @@ class ShowHypervisor(command.ShowOne):
member_of = [aggregate.name
for aggregate in aggregates
if service_host in aggregate.hosts]
- hypervisor["aggregates"] = member_of
+ hypervisor['aggregates'] = member_of
try:
- uptime = compute_client.hypervisors.uptime(hypervisor['id'])._info
+ if sdk_utils.supports_microversion(compute_client, '2.88'):
+ uptime = hypervisor['uptime'] or ''
+ del hypervisor['uptime']
+ else:
+ del hypervisor['uptime']
+ uptime = compute_client.get_hypervisor_uptime(
+ hypervisor['id'])['uptime']
# Extract data from uptime value
# format: 0 up 0, 0 users, load average: 0, 0, 0
# example: 17:37:14 up 2:33, 3 users,
# load average: 0.33, 0.36, 0.34
m = re.match(
r"\s*(.+)\sup\s+(.+),\s+(.+)\susers?,\s+load average:\s(.+)",
- uptime['uptime'])
+ uptime)
if m:
- hypervisor["host_time"] = m.group(1)
- hypervisor["uptime"] = m.group(2)
- hypervisor["users"] = m.group(3)
- hypervisor["load_average"] = m.group(4)
+ hypervisor['host_time'] = m.group(1)
+ hypervisor['uptime'] = m.group(2)
+ hypervisor['users'] = m.group(3)
+ hypervisor['load_average'] = m.group(4)
except nova_exceptions.HTTPNotImplemented:
pass
- hypervisor["service_id"] = hypervisor["service"]["id"]
- hypervisor["service_host"] = hypervisor["service"]["host"]
- del hypervisor["service"]
+ hypervisor['service_id'] = service_details['id']
+ hypervisor['service_host'] = service_details['host']
+ del hypervisor['service_details']
- if compute_client.api_version < api_versions.APIVersion('2.28'):
+ if not sdk_utils.supports_microversion(compute_client, '2.28'):
# microversion 2.28 transformed this to a JSON blob rather than a
# string; on earlier fields, do this manually
- if hypervisor['cpu_info']:
- hypervisor['cpu_info'] = json.loads(hypervisor['cpu_info'])
- else:
- hypervisor['cpu_info'] = {}
-
- columns = tuple(sorted(hypervisor))
+ hypervisor['cpu_info'] = json.loads(hypervisor['cpu_info'] or '{}')
+ display_columns, columns = _get_hypervisor_columns(
+ hypervisor, compute_client)
data = utils.get_dict_properties(
hypervisor, columns,
formatters={
'cpu_info': format_columns.DictColumn,
})
- return (columns, data)
+ return display_columns, data
diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py
index 4493e080..cb63a800 100644
--- a/openstackclient/compute/v2/hypervisor_stats.py
+++ b/openstackclient/compute/v2/hypervisor_stats.py
@@ -11,19 +11,49 @@
# under the License.
#
-
"""Hypervisor Stats action implementations"""
from osc_lib.command import command
+from osc_lib import utils
from openstackclient.i18n import _
+def _get_hypervisor_stat_columns(item):
+ column_map = {
+ # NOTE(gtema): If we decide to use SDK names - empty this
+ 'disk_available': 'disk_available_least',
+ 'local_disk_free': 'free_disk_gb',
+ 'local_disk_size': 'local_gb',
+ 'local_disk_used': 'local_gb_used',
+ 'memory_free': 'free_ram_mb',
+ 'memory_size': 'memory_mb',
+ 'memory_used': 'memory_mb_used',
+
+ }
+ hidden_columns = ['id', 'links', 'location', 'name']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
class ShowHypervisorStats(command.ShowOne):
_description = _("Display hypervisor stats details")
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- hypervisor_stats = compute_client.hypervisors.statistics().to_dict()
-
- return zip(*sorted(hypervisor_stats.items()))
+ # The command is deprecated since it is being dropped in Nova.
+ self.log.warning(
+ _("This command is deprecated.")
+ )
+ compute_client = self.app.client_manager.sdk_connection.compute
+ # We do API request directly cause this deprecated method is not and
+ # will not be supported by OpenStackSDK.
+ response = compute_client.get(
+ '/os-hypervisors/statistics',
+ microversion='2.1')
+ hypervisor_stats = response.json().get('hypervisor_statistics')
+
+ display_columns, columns = _get_hypervisor_stat_columns(
+ hypervisor_stats)
+ data = utils.get_dict_properties(
+ hypervisor_stats, columns)
+ return (display_columns, data)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 39b2bdc8..85693e17 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -66,6 +66,32 @@ class PowerStateColumn(cliff_columns.FormattableColumn):
return 'N/A'
+class AddressesColumn(cliff_columns.FormattableColumn):
+ """Generate a formatted string of a server's addresses."""
+
+ def human_readable(self):
+ try:
+ return utils.format_dict_of_list({
+ k: [i['addr'] for i in v if 'addr' in i]
+ for k, v in self._value.items()})
+ except Exception:
+ return 'N/A'
+
+ def machine_readable(self):
+ return {k: [i['addr'] for i in v if 'addr' in i]
+ for k, v in self._value.items()}
+
+
+class HostColumn(cliff_columns.FormattableColumn):
+ """Generate a formatted string of a hostname."""
+
+ def human_readable(self):
+ if self._value is None:
+ return ''
+
+ return self._value
+
+
def _get_ip_address(addresses, address_type, ip_address_family):
# Old style addresses
if address_type in addresses:
@@ -111,14 +137,61 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
the latest details of a server after creating it.
:rtype: a dict of server details
"""
+ # Note: Some callers of this routine pass a novaclient server, and others
+ # pass an SDK server. Column names may be different across those cases.
info = server.to_dict()
if refresh:
server = utils.find_resource(compute_client.servers, info['id'])
info.update(server.to_dict())
+ # Some commands using this routine were originally implemented with the
+ # nova python wrappers, and were later migrated to use the SDK. Map the
+ # SDK's property names to the original property names to maintain backward
+ # compatibility for existing users. Data is duplicated under both the old
+ # and new name so users can consume the data by either name.
+ column_map = {
+ 'access_ipv4': 'accessIPv4',
+ 'access_ipv6': 'accessIPv6',
+ 'admin_password': 'adminPass',
+ 'admin_password': 'adminPass',
+ 'volumes': 'os-extended-volumes:volumes_attached',
+ 'availability_zone': 'OS-EXT-AZ:availability_zone',
+ 'block_device_mapping': 'block_device_mapping_v2',
+ 'compute_host': 'OS-EXT-SRV-ATTR:host',
+ 'created_at': 'created',
+ 'disk_config': 'OS-DCF:diskConfig',
+ 'flavor_id': 'flavorRef',
+ 'has_config_drive': 'config_drive',
+ 'host_id': 'hostId',
+ 'fault': 'fault',
+ 'hostname': 'OS-EXT-SRV-ATTR:hostname',
+ 'hypervisor_hostname': 'OS-EXT-SRV-ATTR:hypervisor_hostname',
+ 'image_id': 'imageRef',
+ 'instance_name': 'OS-EXT-SRV-ATTR:instance_name',
+ 'is_locked': 'locked',
+ 'kernel_id': 'OS-EXT-SRV-ATTR:kernel_id',
+ 'launch_index': 'OS-EXT-SRV-ATTR:launch_index',
+ 'launched_at': 'OS-SRV-USG:launched_at',
+ 'power_state': 'OS-EXT-STS:power_state',
+ 'project_id': 'tenant_id',
+ 'ramdisk_id': 'OS-EXT-SRV-ATTR:ramdisk_id',
+ 'reservation_id': 'OS-EXT-SRV-ATTR:reservation_id',
+ 'root_device_name': 'OS-EXT-SRV-ATTR:root_device_name',
+ 'scheduler_hints': 'OS-SCH-HNT:scheduler_hints',
+ 'task_state': 'OS-EXT-STS:task_state',
+ 'terminated_at': 'OS-SRV-USG:terminated_at',
+ 'updated_at': 'updated',
+ 'user_data': 'OS-EXT-SRV-ATTR:user_data',
+ 'vm_state': 'OS-EXT-STS:vm_state',
+ }
+
+ info.update({
+ column_map[column]: data for column, data in info.items()
+ if column in column_map})
+
# Convert the image blob to a name
image_info = info.get('image', {})
- if image_info:
+ if image_info and any(image_info.values()):
image_id = image_info.get('id', '')
try:
image = image_client.get_image(image_id)
@@ -166,7 +239,9 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
# NOTE(dtroyer): novaclient splits these into separate entries...
# Format addresses in a useful way
- info['addresses'] = format_columns.DictListColumn(server.networks)
+ info['addresses'] = (
+ AddressesColumn(info['addresses']) if 'addresses' in info
+ else format_columns.DictListColumn(info.get('networks')))
# Map 'metadata' field to 'properties'
info['properties'] = format_columns.DictColumn(info.pop('metadata'))
@@ -268,9 +343,11 @@ class AddFixedIP(command.ShowOne):
return ((), ())
kwargs = {
- 'net_id': net_id,
- 'fixed_ip': parsed_args.fixed_ip_address,
+ 'net_id': net_id
}
+ if parsed_args.fixed_ip_address:
+ kwargs['fixed_ips'] = [
+ {"ip_address": parsed_args.fixed_ip_address}]
if parsed_args.tag:
kwargs['tag'] = parsed_args.tag
@@ -429,8 +506,7 @@ class AddPort(command.Command):
port_id = parsed_args.port
kwargs = {
- 'port_id': port_id,
- 'fixed_ip': None,
+ 'port_id': port_id
}
if parsed_args.tag:
@@ -484,8 +560,7 @@ class AddNetwork(command.Command):
net_id = parsed_args.network
kwargs = {
- 'net_id': net_id,
- 'fixed_ip': None,
+ 'net_id': net_id
}
if parsed_args.tag:
@@ -721,11 +796,6 @@ class NICAction(argparse.Action):
if getattr(namespace, self.dest, None) is None:
setattr(namespace, self.dest, [])
- # Handle the special auto/none cases
- if values in ('auto', 'none'):
- getattr(namespace, self.dest).append(values)
- return
-
if self.key:
if ',' in values or '=' in values:
msg = _(
@@ -735,6 +805,12 @@ class NICAction(argparse.Action):
raise argparse.ArgumentTypeError(msg % values)
values = '='.join([self.key, values])
+ else:
+ # Handle the special auto/none cases but only when a key isn't set
+ # (otherwise those could be valid values for the key)
+ if values in ('auto', 'none'):
+ getattr(namespace, self.dest).append(values)
+ return
# We don't include 'tag' here by default since that requires a
# particular microversion
@@ -890,9 +966,7 @@ class CreateServer(command.ShowOne):
required=True,
help=_('Create server with this flavor (name or ID)'),
)
- disk_group = parser.add_mutually_exclusive_group(
- required=True,
- )
+ disk_group = parser.add_mutually_exclusive_group()
disk_group.add_argument(
'--image',
metavar='<image>',
@@ -1450,14 +1524,14 @@ class CreateServer(command.ShowOne):
if volume:
block_device_mapping_v2 = [{
'uuid': volume,
- 'boot_index': '0',
+ 'boot_index': 0,
'source_type': 'volume',
'destination_type': 'volume'
}]
elif snapshot:
block_device_mapping_v2 = [{
'uuid': snapshot,
- 'boot_index': '0',
+ 'boot_index': 0,
'source_type': 'snapshot',
'destination_type': 'volume',
'delete_on_termination': False
@@ -1466,7 +1540,7 @@ class CreateServer(command.ShowOne):
# Tell nova to create a root volume from the image provided.
block_device_mapping_v2 = [{
'uuid': image.id,
- 'boot_index': '0',
+ 'boot_index': 0,
'source_type': 'image',
'destination_type': 'volume',
'volume_size': parsed_args.boot_from_volume
@@ -1603,6 +1677,15 @@ class CreateServer(command.ShowOne):
block_device_mapping_v2.append(mapping)
+ if not image and not any(
+ [bdm.get('boot_index') == 0 for bdm in block_device_mapping_v2]
+ ):
+ msg = _(
+ 'An image (--image, --image-property) or bootable volume '
+ '(--volume, --snapshot, --block-device) is required'
+ )
+ raise exceptions.CommandError(msg)
+
nics = parsed_args.nics
if 'auto' in nics or 'none' in nics:
@@ -1614,6 +1697,14 @@ class CreateServer(command.ShowOne):
)
raise exceptions.CommandError(msg)
+ if compute_client.api_version < api_versions.APIVersion('2.37'):
+ msg = _(
+ '--os-compute-api-version 2.37 or greater is '
+ 'required to support explicit auto-allocation of a '
+ 'network or to disable network allocation'
+ )
+ raise exceptions.CommandError(msg)
+
nics = nics[0]
else:
for nic in nics:
@@ -1818,8 +1909,9 @@ class CreateServerDump(command.Command):
Trigger crash dump in server(s) with features like kdump in Linux.
It will create a dump file in the server(s) dumping the server(s)'
- memory, and also crash the server(s). OSC sees the dump file
- (server dump) as a kind of resource.
+ memory, and also crash the server(s). This is contingent on guest operating
+ system support, and the location of the dump file inside the guest will
+ depend on the exact guest operating system.
This command requires ``--os-compute-api-version`` 2.17 or greater.
"""
@@ -1835,12 +1927,10 @@ class CreateServerDump(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- for server in parsed_args.server:
- utils.find_resource(
- compute_client.servers,
- server,
- ).trigger_crash_dump()
+ compute_client = self.app.client_manager.sdk_connection.compute
+ for name_or_id in parsed_args.server:
+ server = compute_client.find_server(name_or_id)
+ server.trigger_crash_dump(compute_client)
class DeleteServer(command.Command):
@@ -2278,7 +2368,7 @@ class ListServer(command.Lister):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
identity_client = self.app.client_manager.identity
image_client = self.app.client_manager.image
@@ -2303,10 +2393,11 @@ class ListServer(command.Lister):
# flavor name is given, map it to ID.
flavor_id = None
if parsed_args.flavor:
- flavor_id = utils.find_resource(
- compute_client.flavors,
- parsed_args.flavor,
- ).id
+ flavor = compute_client.find_flavor(parsed_args.flavor)
+ if flavor is None:
+ msg = _('Unable to find flavor: %s') % parsed_args.flavor
+ raise exceptions.CommandError(msg)
+ flavor_id = flavor.id
# Nova only supports list servers searching by image ID. So if a
# image name is given, map it to ID.
@@ -2322,19 +2413,21 @@ class ListServer(command.Lister):
'ip': parsed_args.ip,
'ip6': parsed_args.ip6,
'name': parsed_args.name,
- 'instance_name': parsed_args.instance_name,
'status': parsed_args.status,
'flavor': flavor_id,
'image': image_id,
'host': parsed_args.host,
- 'tenant_id': project_id,
- 'all_tenants': parsed_args.all_projects,
+ 'project_id': project_id,
+ 'all_projects': parsed_args.all_projects,
'user_id': user_id,
'deleted': parsed_args.deleted,
'changes-before': parsed_args.changes_before,
'changes-since': parsed_args.changes_since,
}
+ if parsed_args.instance_name is not None:
+ search_opts['instance_name'] = parsed_args.instance_name
+
if parsed_args.availability_zone:
search_opts['availability_zone'] = parsed_args.availability_zone
@@ -2366,7 +2459,7 @@ class ListServer(command.Lister):
search_opts['power_state'] = power_state
if parsed_args.tags:
- if compute_client.api_version < api_versions.APIVersion('2.26'):
+ if not sdk_utils.supports_microversion(compute_client, '2.26'):
msg = _(
'--os-compute-api-version 2.26 or greater is required to '
'support the --tag option'
@@ -2376,7 +2469,7 @@ class ListServer(command.Lister):
search_opts['tags'] = ','.join(parsed_args.tags)
if parsed_args.not_tags:
- if compute_client.api_version < api_versions.APIVersion('2.26'):
+ if not sdk_utils.supports_microversion(compute_client, '2.26'):
msg = _(
'--os-compute-api-version 2.26 or greater is required to '
'support the --not-tag option'
@@ -2386,7 +2479,7 @@ class ListServer(command.Lister):
search_opts['not-tags'] = ','.join(parsed_args.not_tags)
if parsed_args.locked:
- if compute_client.api_version < api_versions.APIVersion('2.73'):
+ if not sdk_utils.supports_microversion(compute_client, '2.73'):
msg = _(
'--os-compute-api-version 2.73 or greater is required to '
'support the --locked option'
@@ -2395,7 +2488,7 @@ class ListServer(command.Lister):
search_opts['locked'] = True
elif parsed_args.unlocked:
- if compute_client.api_version < api_versions.APIVersion('2.73'):
+ if not sdk_utils.supports_microversion(compute_client, '2.73'):
msg = _(
'--os-compute-api-version 2.73 or greater is required to '
'support the --unlocked option'
@@ -2404,10 +2497,14 @@ class ListServer(command.Lister):
search_opts['locked'] = False
+ if parsed_args.limit is not None:
+ search_opts['limit'] = parsed_args.limit
+ search_opts['paginated'] = False
+
LOG.debug('search options: %s', search_opts)
if search_opts['changes-before']:
- if compute_client.api_version < api_versions.APIVersion('2.66'):
+ if not sdk_utils.supports_microversion(compute_client, '2.66'):
msg = _('--os-compute-api-version 2.66 or later is required')
raise exceptions.CommandError(msg)
@@ -2441,15 +2538,15 @@ class ListServer(command.Lister):
if parsed_args.long:
columns += (
- 'OS-EXT-STS:task_state',
- 'OS-EXT-STS:power_state',
+ 'task_state',
+ 'power_state',
)
column_headers += (
'Task State',
'Power State',
)
- columns += ('networks',)
+ columns += ('addresses',)
column_headers += ('Networks',)
if parsed_args.long:
@@ -2471,7 +2568,7 @@ class ListServer(command.Lister):
# microversion 2.47 puts the embedded flavor into the server response
# body but omits the id, so if not present we just expose the original
# flavor name in the output
- if compute_client.api_version >= api_versions.APIVersion('2.47'):
+ if sdk_utils.supports_microversion(compute_client, '2.47'):
columns += ('flavor_name',)
column_headers += ('Flavor',)
else:
@@ -2493,8 +2590,8 @@ class ListServer(command.Lister):
if parsed_args.long:
columns += (
- 'OS-EXT-AZ:availability_zone',
- 'OS-EXT-SRV-ATTR:host',
+ 'availability_zone',
+ 'hypervisor_hostname',
'metadata',
)
column_headers += (
@@ -2503,50 +2600,48 @@ class ListServer(command.Lister):
'Properties',
)
- marker_id = None
-
# support for additional columns
if parsed_args.columns:
for c in parsed_args.columns:
if c in ('Project ID', 'project_id'):
- columns += ('tenant_id',)
+ columns += ('project_id',)
column_headers += ('Project ID',)
if c in ('User ID', 'user_id'):
columns += ('user_id',)
column_headers += ('User ID',)
if c in ('Created At', 'created_at'):
- columns += ('created',)
+ columns += ('created_at',)
column_headers += ('Created At',)
if c in ('Security Groups', 'security_groups'):
columns += ('security_groups_name',)
column_headers += ('Security Groups',)
if c in ("Task State", "task_state"):
- columns += ('OS-EXT-STS:task_state',)
+ columns += ('task_state',)
column_headers += ('Task State',)
if c in ("Power State", "power_state"):
- columns += ('OS-EXT-STS:power_state',)
+ columns += ('power_state',)
column_headers += ('Power State',)
if c in ("Image ID", "image_id"):
columns += ('Image ID',)
column_headers += ('Image ID',)
if c in ("Flavor ID", "flavor_id"):
- columns += ('Flavor ID',)
+ columns += ('flavor_id',)
column_headers += ('Flavor ID',)
if c in ('Availability Zone', "availability_zone"):
- columns += ('OS-EXT-AZ:availability_zone',)
+ columns += ('availability_zone',)
column_headers += ('Availability Zone',)
if c in ('Host', "host"):
- columns += ('OS-EXT-SRV-ATTR:host',)
+ columns += ('hypervisor_hostname',)
column_headers += ('Host',)
if c in ('Properties', "properties"):
columns += ('Metadata',)
column_headers += ('Properties',)
- # convert back to tuple
- column_headers = tuple(column_headers)
- columns = tuple(columns)
+ # remove duplicates
+ column_headers = tuple(dict.fromkeys(column_headers))
+ columns = tuple(dict.fromkeys(columns))
- if parsed_args.marker:
+ if parsed_args.marker is not None:
# Check if both "--marker" and "--deleted" are used.
# In that scenario a lookup is not needed as the marker
# needs to be an ID, because find_resource does not
@@ -2554,16 +2649,10 @@ class ListServer(command.Lister):
if parsed_args.deleted:
marker_id = parsed_args.marker
else:
- marker_id = utils.find_resource(
- compute_client.servers,
- parsed_args.marker,
- ).id
+ marker_id = compute_client.find_server(parsed_args.marker).id
+ search_opts['marker'] = marker_id
- data = compute_client.servers.list(
- search_opts=search_opts,
- marker=marker_id,
- limit=parsed_args.limit,
- )
+ data = list(compute_client.servers(**search_opts))
images = {}
flavors = {}
@@ -2618,12 +2707,12 @@ class ListServer(command.Lister):
# "Flavor Name" is not crucial, so we swallow any
# exceptions
try:
- flavors[f_id] = compute_client.flavors.get(f_id)
+ flavors[f_id] = compute_client.find_flavor(f_id)
except Exception:
pass
else:
try:
- flavors_list = compute_client.flavors.list(is_public=None)
+ flavors_list = compute_client.flavors(is_public=None)
for i in flavors_list:
flavors[i.id] = i
except Exception:
@@ -2632,7 +2721,7 @@ class ListServer(command.Lister):
# Populate image_name, image_id, flavor_name and flavor_id attributes
# of server objects so that we can display those columns.
for s in data:
- if compute_client.api_version >= api_versions.APIVersion('2.69'):
+ if sdk_utils.supports_microversion(compute_client, '2.69'):
# NOTE(tssurya): From 2.69, we will have the keys 'flavor'
# and 'image' missing in the server response during
# infrastructure failure situations.
@@ -2641,7 +2730,7 @@ class ListServer(command.Lister):
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
continue
- if 'id' in s.image:
+ if 'id' in s.image and s.image.id is not None:
image = images.get(s.image['id'])
if image:
s.image_name = image.name
@@ -2654,7 +2743,7 @@ class ListServer(command.Lister):
s.image_name = IMAGE_STRING_FOR_BFV
s.image_id = IMAGE_STRING_FOR_BFV
- if compute_client.api_version < api_versions.APIVersion('2.47'):
+ if not sdk_utils.supports_microversion(compute_client, '2.47'):
flavor = flavors.get(s.flavor['id'])
if flavor:
s.flavor_name = flavor.name
@@ -2664,7 +2753,7 @@ class ListServer(command.Lister):
# Add a list with security group name as attribute
for s in data:
- if hasattr(s, 'security_groups'):
+ if hasattr(s, 'security_groups') and s.security_groups is not None:
s.security_groups_name = [x["name"] for x in s.security_groups]
else:
s.security_groups_name = []
@@ -2677,10 +2766,10 @@ class ListServer(command.Lister):
# it's on, providing useful information to a user in this
# situation.
if (
- compute_client.api_version >= api_versions.APIVersion('2.16') and
+ sdk_utils.supports_microversion(compute_client, '2.16') and
parsed_args.long
):
- if any([hasattr(s, 'host_status') for s in data]):
+ if any([s.host_status is not None for s in data]):
columns += ('Host Status',)
column_headers += ('Host Status',)
@@ -2690,16 +2779,17 @@ class ListServer(command.Lister):
utils.get_item_properties(
s, columns,
mixed_case_fields=(
- 'OS-EXT-STS:task_state',
- 'OS-EXT-STS:power_state',
- 'OS-EXT-AZ:availability_zone',
- 'OS-EXT-SRV-ATTR:host',
+ 'task_state',
+ 'power_state',
+ 'availability_zone',
+ 'host',
),
formatters={
- 'OS-EXT-STS:power_state': PowerStateColumn,
- 'networks': format_columns.DictListColumn,
+ 'power_state': PowerStateColumn,
+ 'addresses': AddressesColumn,
'metadata': format_columns.DictColumn,
'security_groups_name': format_columns.ListColumn,
+ 'hypervisor_hostname': HostColumn,
},
) for s in data
),
@@ -2709,8 +2799,9 @@ class ListServer(command.Lister):
class LockServer(command.Command):
- _description = _("Lock server(s). A non-admin user will not be able to "
- "execute actions")
+ _description = _("""Lock server(s)
+
+A non-admin user will not be able to execute actions.""")
def get_parser(self, prog_name):
parser = super(LockServer, self).get_parser(prog_name)
@@ -3707,7 +3798,11 @@ class RemoveServerVolume(command.Command):
class RescueServer(command.Command):
- _description = _("Put server in rescue mode")
+ _description = _(
+ "Put server in rescue mode. "
+ "Specify ``--os-compute-api-version 2.87`` or higher to rescue a "
+ "server booted from a volume."
+ )
def get_parser(self, prog_name):
parser = super(RescueServer, self).get_parser(prog_name)
@@ -4283,32 +4378,34 @@ class ShowServer(command.ShowOne):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
+ compute_client = self.app.client_manager.sdk_connection.compute
+
+ # Find by name or ID, then get the full details of the server
+ server = compute_client.find_server(
+ parsed_args.server, ignore_missing=False)
+ server = compute_client.get_server(server)
if parsed_args.diagnostics:
- (resp, data) = server.diagnostics()
- if not resp.status_code == 200:
- self.app.stderr.write(_(
- "Error retrieving diagnostics data\n"
- ))
- return ({}, {})
+ data = compute_client.get_server_diagnostics(server)
return zip(*sorted(data.items()))
topology = None
if parsed_args.topology:
- if compute_client.api_version < api_versions.APIVersion('2.78'):
+ if not sdk_utils.supports_microversion(compute_client, '2.78'):
msg = _(
'--os-compute-api-version 2.78 or greater is required to '
'support the --topology option'
)
raise exceptions.CommandError(msg)
- topology = server.topology()
+ topology = server.fetch_topology(compute_client)
data = _prep_server_detail(
- compute_client, self.app.client_manager.image, server,
+ # TODO(dannosliwcd): Replace these clients with SDK clients after
+ # all callers of _prep_server_detail() are using the SDK.
+ self.app.client_manager.compute,
+ self.app.client_manager.image,
+ server,
refresh=False)
if topology:
@@ -4473,7 +4570,7 @@ class SshServer(command.Command):
class StartServer(command.Command):
- _description = _("Start server(s).")
+ _description = _("Start server(s)")
def get_parser(self, prog_name):
parser = super(StartServer, self).get_parser(prog_name)
@@ -4505,7 +4602,7 @@ class StartServer(command.Command):
class StopServer(command.Command):
- _description = _("Stop server(s).")
+ _description = _("Stop server(s)")
def get_parser(self, prog_name):
parser = super(StopServer, self).get_parser(prog_name)
diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py
index 919b67bd..016d15d7 100644
--- a/openstackclient/compute/v2/server_migration.py
+++ b/openstackclient/compute/v2/server_migration.py
@@ -15,6 +15,7 @@
import uuid
from novaclient import api_versions
+from openstack import utils as sdk_utils
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -130,22 +131,22 @@ class ListMigration(command.Lister):
# the same as the column header names.
columns = [
'source_node', 'dest_node', 'source_compute', 'dest_compute',
- 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id',
- 'new_instance_type_id', 'created_at', 'updated_at',
+ 'dest_host', 'status', 'server_id', 'old_flavor_id',
+ 'new_flavor_id', 'created_at', 'updated_at',
]
# Insert migrations UUID after ID
- if compute_client.api_version >= api_versions.APIVersion("2.59"):
+ if sdk_utils.supports_microversion(compute_client, "2.59"):
column_headers.insert(0, "UUID")
columns.insert(0, "uuid")
- if compute_client.api_version >= api_versions.APIVersion("2.23"):
+ if sdk_utils.supports_microversion(compute_client, "2.23"):
column_headers.insert(0, "Id")
columns.insert(0, "id")
column_headers.insert(len(column_headers) - 2, "Type")
columns.insert(len(columns) - 2, "migration_type")
- if compute_client.api_version >= api_versions.APIVersion("2.80"):
+ if sdk_utils.supports_microversion(compute_client, "2.80"):
if parsed_args.project:
column_headers.insert(len(column_headers) - 2, "Project")
columns.insert(len(columns) - 2, "project_id")
@@ -159,19 +160,23 @@ class ListMigration(command.Lister):
)
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
identity_client = self.app.client_manager.identity
- search_opts = {
- 'host': parsed_args.host,
- 'status': parsed_args.status,
- }
+ search_opts = {}
+
+ if parsed_args.host is not None:
+ search_opts['host'] = parsed_args.host
+
+ if parsed_args.status is not None:
+ search_opts['status'] = parsed_args.status
if parsed_args.server:
- search_opts['instance_uuid'] = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).id
+ server = compute_client.find_server(parsed_args.server)
+ if server is None:
+ msg = _('Unable to find server: %s') % parsed_args.server
+ raise exceptions.CommandError(msg)
+ search_opts['instance_uuid'] = server.id
if parsed_args.type:
migration_type = parsed_args.type
@@ -181,7 +186,7 @@ class ListMigration(command.Lister):
search_opts['migration_type'] = migration_type
if parsed_args.marker:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
+ if not sdk_utils.supports_microversion(compute_client, "2.59"):
msg = _(
'--os-compute-api-version 2.59 or greater is required to '
'support the --marker option'
@@ -190,16 +195,17 @@ class ListMigration(command.Lister):
search_opts['marker'] = parsed_args.marker
if parsed_args.limit:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
+ if not sdk_utils.supports_microversion(compute_client, "2.59"):
msg = _(
'--os-compute-api-version 2.59 or greater is required to '
'support the --limit option'
)
raise exceptions.CommandError(msg)
search_opts['limit'] = parsed_args.limit
+ search_opts['paginated'] = False
if parsed_args.changes_since:
- if compute_client.api_version < api_versions.APIVersion('2.59'):
+ if not sdk_utils.supports_microversion(compute_client, "2.59"):
msg = _(
'--os-compute-api-version 2.59 or greater is required to '
'support the --changes-since option'
@@ -208,7 +214,7 @@ class ListMigration(command.Lister):
search_opts['changes_since'] = parsed_args.changes_since
if parsed_args.changes_before:
- if compute_client.api_version < api_versions.APIVersion('2.66'):
+ if not sdk_utils.supports_microversion(compute_client, "2.66"):
msg = _(
'--os-compute-api-version 2.66 or greater is required to '
'support the --changes-before option'
@@ -217,7 +223,7 @@ class ListMigration(command.Lister):
search_opts['changes_before'] = parsed_args.changes_before
if parsed_args.project:
- if compute_client.api_version < api_versions.APIVersion('2.80'):
+ if not sdk_utils.supports_microversion(compute_client, "2.80"):
msg = _(
'--os-compute-api-version 2.80 or greater is required to '
'support the --project option'
@@ -231,7 +237,7 @@ class ListMigration(command.Lister):
).id
if parsed_args.user:
- if compute_client.api_version < api_versions.APIVersion('2.80'):
+ if not sdk_utils.supports_microversion(compute_client, "2.80"):
msg = _(
'--os-compute-api-version 2.80 or greater is required to '
'support the --user option'
@@ -244,7 +250,7 @@ class ListMigration(command.Lister):
parsed_args.user_domain,
).id
- migrations = compute_client.migrations.list(**search_opts)
+ migrations = list(compute_client.migrations(**search_opts))
return self.print_migrations(parsed_args, compute_client, migrations)
diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py
index 69fa04e8..86f538a7 100644
--- a/openstackclient/compute/v2/usage.py
+++ b/openstackclient/compute/v2/usage.py
@@ -15,12 +15,10 @@
"""Usage action implementations"""
-import collections
import datetime
import functools
from cliff import columns as cliff_columns
-from novaclient import api_versions
from osc_lib.command import command
from osc_lib import utils
@@ -58,7 +56,7 @@ class ProjectColumn(cliff_columns.FormattableColumn):
class CountColumn(cliff_columns.FormattableColumn):
def human_readable(self):
- return len(self._value)
+ return len(self._value) if self._value is not None else None
class FloatColumn(cliff_columns.FormattableColumn):
@@ -69,7 +67,7 @@ class FloatColumn(cliff_columns.FormattableColumn):
def _formatters(project_cache):
return {
- 'tenant_id': functools.partial(
+ 'project_id': functools.partial(
ProjectColumn, project_cache=project_cache),
'server_usages': CountColumn,
'total_memory_mb_usage': FloatColumn,
@@ -102,10 +100,10 @@ def _merge_usage(usage, next_usage):
def _merge_usage_list(usages, next_usage_list):
for next_usage in next_usage_list:
- if next_usage.tenant_id in usages:
- _merge_usage(usages[next_usage.tenant_id], next_usage)
+ if next_usage.project_id in usages:
+ _merge_usage(usages[next_usage.project_id], next_usage)
else:
- usages[next_usage.tenant_id] = next_usage
+ usages[next_usage.project_id] = next_usage
class ListUsage(command.Lister):
@@ -138,9 +136,9 @@ class ListUsage(command.Lister):
else:
return project
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
columns = (
- "tenant_id",
+ "project_id",
"server_usages",
"total_memory_mb_usage",
"total_vcpus_usage",
@@ -154,36 +152,25 @@ class ListUsage(command.Lister):
"Disk GB-Hours"
)
- dateformat = "%Y-%m-%d"
+ date_cli_format = "%Y-%m-%d"
+ date_api_format = "%Y-%m-%dT%H:%M:%S"
now = datetime.datetime.utcnow()
if parsed_args.start:
- start = datetime.datetime.strptime(parsed_args.start, dateformat)
+ start = datetime.datetime.strptime(
+ parsed_args.start, date_cli_format)
else:
start = now - datetime.timedelta(weeks=4)
if parsed_args.end:
- end = datetime.datetime.strptime(parsed_args.end, dateformat)
+ end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
else:
end = now + datetime.timedelta(days=1)
- if compute_client.api_version < api_versions.APIVersion("2.40"):
- usage_list = compute_client.usage.list(start, end, detailed=True)
- else:
- # If the number of instances used to calculate the usage is greater
- # than CONF.api.max_limit, the usage will be split across multiple
- # requests and the responses will need to be merged back together.
- usages = collections.OrderedDict()
- usage_list = compute_client.usage.list(start, end, detailed=True)
- _merge_usage_list(usages, usage_list)
- marker = _get_usage_list_marker(usage_list)
- while marker:
- next_usage_list = compute_client.usage.list(
- start, end, detailed=True, marker=marker)
- marker = _get_usage_list_marker(next_usage_list)
- if marker:
- _merge_usage_list(usages, next_usage_list)
- usage_list = list(usages.values())
+ usage_list = list(compute_client.usages(
+ start=start.strftime(date_api_format),
+ end=end.strftime(date_api_format),
+ detailed=True))
# Cache the project list
project_cache = {}
@@ -196,8 +183,8 @@ class ListUsage(command.Lister):
if parsed_args.formatter == 'table' and len(usage_list) > 0:
self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
- "start": start.strftime(dateformat),
- "end": end.strftime(dateformat),
+ "start": start.strftime(date_cli_format),
+ "end": end.strftime(date_cli_format),
})
return (
@@ -239,17 +226,19 @@ class ShowUsage(command.ShowOne):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- compute_client = self.app.client_manager.compute
- dateformat = "%Y-%m-%d"
+ compute_client = self.app.client_manager.sdk_connection.compute
+ date_cli_format = "%Y-%m-%d"
+ date_api_format = "%Y-%m-%dT%H:%M:%S"
now = datetime.datetime.utcnow()
if parsed_args.start:
- start = datetime.datetime.strptime(parsed_args.start, dateformat)
+ start = datetime.datetime.strptime(
+ parsed_args.start, date_cli_format)
else:
start = now - datetime.timedelta(weeks=4)
if parsed_args.end:
- end = datetime.datetime.strptime(parsed_args.end, dateformat)
+ end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
else:
end = now + datetime.timedelta(days=1)
@@ -262,19 +251,21 @@ class ShowUsage(command.ShowOne):
# Get the project from the current auth
project = self.app.client_manager.auth_ref.project_id
- usage = compute_client.usage.get(project, start, end)
+ usage = compute_client.get_usage(
+ project=project, start=start.strftime(date_api_format),
+ end=end.strftime(date_api_format))
if parsed_args.formatter == 'table':
self.app.stdout.write(_(
"Usage from %(start)s to %(end)s on project %(project)s: \n"
) % {
- "start": start.strftime(dateformat),
- "end": end.strftime(dateformat),
+ "start": start.strftime(date_cli_format),
+ "end": end.strftime(date_cli_format),
"project": project,
})
columns = (
- "tenant_id",
+ "project_id",
"server_usages",
"total_memory_mb_usage",
"total_vcpus_usage",