python-novaclient/novaclient/v1_1/shell.py

3516 lines
116 KiB
Python

# Copyright 2010 Jacob Kaplan-Moss
# Copyright 2011 OpenStack Foundation
# Copyright 2013 IBM Corp.
# All Rights Reserved.
#
# 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.
from __future__ import print_function
import argparse
import copy
import datetime
import getpass
import locale
import os
import sys
import time
import six
from novaclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient.openstack.common import timeutils
from novaclient.openstack.common import uuidutils
from novaclient import utils
from novaclient.v1_1 import availability_zones
from novaclient.v1_1 import quotas
from novaclient.v1_1 import servers
CLIENT_BDM2_KEYS = {
'id': 'uuid',
'source': 'source_type',
'dest': 'destination_type',
'bus': 'disk_bus',
'device': 'device_name',
'size': 'volume_size',
'format': 'guest_format',
'bootindex': 'boot_index',
'type': 'device_type',
'shutdown': 'delete_on_termination',
}
def _key_value_pairing(text):
try:
(k, v) = text.split('=', 1)
return (k, v)
except ValueError:
msg = "%r is not in the format of key=value" % text
raise argparse.ArgumentTypeError(msg)
def _match_image(cs, wanted_properties):
image_list = cs.images.list()
images_matched = []
match = set(wanted_properties)
for img in image_list:
try:
if match == match.intersection(set(img.metadata.items())):
images_matched.append(img)
except AttributeError:
pass
return images_matched
def _parse_block_device_mapping_v2(args, image):
bdm = []
if args.boot_volume:
bdm_dict = {'uuid': args.boot_volume, 'source_type': 'volume',
'destination_type': 'volume', 'boot_index': 0,
'delete_on_termination': False}
bdm.append(bdm_dict)
if args.snapshot:
bdm_dict = {'uuid': args.snapshot, 'source_type': 'snapshot',
'destination_type': 'volume', 'boot_index': 0,
'delete_on_termination': False}
bdm.append(bdm_dict)
for device_spec in args.block_device:
spec_dict = dict(v.split('=') for v in device_spec.split(','))
bdm_dict = {}
for key, value in six.iteritems(spec_dict):
bdm_dict[CLIENT_BDM2_KEYS[key]] = value
# Convert the delete_on_termination to a boolean or set it to true by
# default for local block devices when not specified.
if 'delete_on_termination' in bdm_dict:
action = bdm_dict['delete_on_termination']
bdm_dict['delete_on_termination'] = (action == 'remove')
elif bdm_dict.get('destination_type') == 'local':
bdm_dict['delete_on_termination'] = True
bdm.append(bdm_dict)
for ephemeral_spec in args.ephemeral:
bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
'boot_index': -1, 'delete_on_termination': True}
eph_dict = dict(v.split('=') for v in ephemeral_spec.split(','))
if 'size' in eph_dict:
bdm_dict['volume_size'] = eph_dict['size']
if 'format' in eph_dict:
bdm_dict['guest_format'] = eph_dict['format']
bdm.append(bdm_dict)
if args.swap:
bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
'boot_index': -1, 'delete_on_termination': True,
'guest_format': 'swap', 'volume_size': args.swap}
bdm.append(bdm_dict)
return bdm
def _boot(cs, args):
"""Boot a new server."""
if args.image:
image = _find_image(cs, args.image)
else:
image = None
if not image and args.image_with:
images = _match_image(cs, args.image_with)
if images:
# TODO(harlowja): log a warning that we
# are selecting the first of many?
image = images[0]
if not args.flavor:
raise exceptions.CommandError(_("you need to specify a Flavor ID "))
min_count = 1
max_count = 1
# Don't let user mix num_instances and max_count/min_count.
if (args.num_instances is not None and
args.min_count is None and args.max_count is None):
if args.num_instances < 1:
raise exceptions.CommandError(_("num_instances should be >= 1"))
max_count = args.num_instances
elif (args.num_instances is not None and
(args.min_count is not None or args.max_count is not None)):
raise exceptions.CommandError(_("Don't mix num-instances and "
"max/min-count"))
if args.min_count is not None:
if args.min_count < 1:
raise exceptions.CommandError(_("min_count should be >= 1"))
min_count = args.min_count
max_count = min_count
if args.max_count is not None:
if args.max_count < 1:
raise exceptions.CommandError(_("max_count should be >= 1"))
max_count = args.max_count
if (args.min_count is not None and args.max_count is not None and
args.min_count > args.max_count):
raise exceptions.CommandError(_(
"min_count should be <= max_count"))
flavor = _find_flavor(cs, args.flavor)
meta = dict(v.split('=', 1) for v in args.meta)
files = {}
for f in args.files:
try:
dst, src = f.split('=', 1)
files[dst] = open(src)
except IOError as e:
raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") %
{'src': src, 'exc': e})
except ValueError as e:
raise exceptions.CommandError(_("Invalid file argument '%s'. "
"File arguments must be of the "
"form '--file "
"<dst-path=src-path>'") % f)
# use the os-keypair extension
key_name = None
if args.key_name is not None:
key_name = args.key_name
if args.user_data:
try:
userdata = open(args.user_data)
except IOError as e:
raise exceptions.CommandError(_("Can't open '%(user_data)s': "
"%(exc)s") %
{'user_data': args.user_data,
'exc': e})
else:
userdata = None
if args.availability_zone:
availability_zone = args.availability_zone
else:
availability_zone = None
if args.security_groups:
security_groups = args.security_groups.split(',')
else:
security_groups = None
block_device_mapping = {}
for bdm in args.block_device_mapping:
device_name, mapping = bdm.split('=', 1)
block_device_mapping[device_name] = mapping
block_device_mapping_v2 = _parse_block_device_mapping_v2(args, image)
n_boot_args = len(list(filter(
bool, (image, args.boot_volume, args.snapshot))))
have_bdm = block_device_mapping_v2 or block_device_mapping
# Fail if more than one boot devices are present
# or if there is no device to boot from.
if n_boot_args > 1 or n_boot_args == 0 and not have_bdm:
raise exceptions.CommandError(
_("you need to specify at least one source ID (Image, Snapshot, "
"or Volume), a block device mapping or provide a set of "
"properties to match against an image"))
if block_device_mapping and block_device_mapping_v2:
raise exceptions.CommandError(
_("you can't mix old block devices (--block-device-mapping) "
"with the new ones (--block-device, --boot-volume, --snapshot, "
"--ephemeral, --swap)"))
nics = []
for nic_str in args.nics:
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
"the form --nic <net-id=net-uuid,v4-fixed-ip=ip-addr,"
"v6-fixed-ip=ip-addr,port-id=port-uuid>, with at minimum "
"net-id or port-id specified.") % nic_str)
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
"port-id": ""}
for kv_str in nic_str.split(","):
try:
k, v = kv_str.split("=", 1)
except ValueError as e:
raise exceptions.CommandError(err_msg)
if k in nic_info:
nic_info[k] = v
else:
raise exceptions.CommandError(err_msg)
if not nic_info['net-id'] and not nic_info['port-id']:
raise exceptions.CommandError(err_msg)
nics.append(nic_info)
hints = {}
if args.scheduler_hints:
for hint in args.scheduler_hints:
key, _sep, value = hint.partition('=')
# NOTE(vish): multiple copies of the same hint will
# result in a list of values
if key in hints:
if isinstance(hints[key], six.string_types):
hints[key] = [hints[key]]
hints[key] += [value]
else:
hints[key] = value
boot_args = [args.name, image, flavor]
if str(args.config_drive).lower() in ("true", "1"):
config_drive = True
elif str(args.config_drive).lower() in ("false", "0", "", "none"):
config_drive = None
else:
config_drive = args.config_drive
boot_kwargs = dict(
meta=meta,
files=files,
key_name=key_name,
min_count=min_count,
max_count=max_count,
userdata=userdata,
availability_zone=availability_zone,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
block_device_mapping_v2=block_device_mapping_v2,
nics=nics,
scheduler_hints=hints,
config_drive=config_drive)
return boot_args, boot_kwargs
@utils.arg('--flavor',
default=None,
metavar='<flavor>',
help=_("Name or ID of flavor (see 'nova flavor-list')."))
@utils.arg('--image',
default=None,
metavar='<image>',
help=_("Name or ID of image (see 'nova image-list'). "))
@utils.arg('--image-with',
default=[],
type=_key_value_pairing,
action='append',
metavar='<key=value>',
help=_("Image metadata property (see 'nova image-show'). "))
@utils.arg('--boot-volume',
default=None,
metavar="<volume_id>",
help=_("Volume ID to boot from."))
@utils.arg('--snapshot',
default=None,
metavar="<snapshot_id>",
help=_("Snapshot ID to boot from (will create a volume)."))
@utils.arg('--num-instances',
default=None,
type=int,
metavar='<number>',
help=argparse.SUPPRESS)
@utils.arg('--min-count',
default=None,
type=int,
metavar='<number>',
help=_("Boot at least <number> servers (limited by quota)."))
@utils.arg('--max-count',
default=None,
type=int,
metavar='<number>',
help=_("Boot up to <number> servers (limited by quota)."))
@utils.arg('--meta',
metavar="<key=value>",
action='append',
default=[],
help=_("Record arbitrary key/value metadata to /meta.js "
"on the new server. Can be specified multiple times."))
@utils.arg('--file',
metavar="<dst-path=src-path>",
action='append',
dest='files',
default=[],
help=_("Store arbitrary files from <src-path> locally to <dst-path> "
"on the new server. You may store up to 5 files."))
@utils.arg('--key-name',
metavar='<key-name>',
help=_("Key name of keypair that should be created earlier with \
the command keypair-add"))
@utils.arg('--key_name',
help=argparse.SUPPRESS)
@utils.arg('name', metavar='<name>', help=_('Name for the new server'))
@utils.arg('--user-data',
default=None,
metavar='<user-data>',
help=_("user data file to pass to be exposed by the metadata server."))
@utils.arg('--user_data',
help=argparse.SUPPRESS)
@utils.arg('--availability-zone',
default=None,
metavar='<availability-zone>',
help=_("The availability zone for server placement."))
@utils.arg('--availability_zone',
help=argparse.SUPPRESS)
@utils.arg('--security-groups',
default=None,
metavar='<security-groups>',
help=_("Comma separated list of security group names."))
@utils.arg('--security_groups',
help=argparse.SUPPRESS)
@utils.arg('--block-device-mapping',
metavar="<dev-name=mapping>",
action='append',
default=[],
help=_("Block device mapping in the format "
"<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>."))
@utils.arg('--block_device_mapping',
action='append',
help=argparse.SUPPRESS)
@utils.arg('--block-device',
metavar="key1=value1[,key2=value2...]",
action='append',
default=[],
help=_("Block device mapping with the keys: "
"id=image_id, snapshot_id or volume_id, "
"source=source type (image, snapshot, volume or blank), "
"dest=destination type of the block device (volume or local), "
"bus=device's bus, "
"device=name of the device (e.g. vda, xda, ...), "
"size=size of the block device in GB, "
"format=device will be formatted (e.g. swap, ext3, ntfs, ...), "
"bootindex=integer used for ordering the boot disks, "
"type=device type (e.g. disk, cdrom, ...) and "
"shutdown=shutdown behaviour (either preserve or remove)."))
@utils.arg('--swap',
metavar="<swap_size>",
default=None,
help=_("Create and attach a local swap block device of <swap_size> MB."))
@utils.arg('--ephemeral',
metavar="size=<size>[,format=<format>]",
action='append',
default=[],
help=_("Create and attach a local ephemeral block device of <size> GB "
"and format it to <format>."))
@utils.arg('--hint',
action='append',
dest='scheduler_hints',
default=[],
metavar='<key=value>',
help=_("Send arbitrary key/value pairs to the scheduler for custom "
"use."))
@utils.arg('--nic',
metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
"port-id=port-uuid>",
action='append',
dest='nics',
default=[],
help=_("Create a NIC on the server. "
"Specify option multiple times to create multiple NICs. "
"net-id: attach NIC to network with this UUID "
"(required if no port-id), "
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
"v6-fixed-ip: IPv6 fixed address for NIC (optional), "
"port-id: attach NIC to port with this UUID "
"(required if no net-id)"))
@utils.arg('--config-drive',
metavar="<value>",
dest='config_drive',
default=False,
help=_("Enable config drive"))
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while server builds so progress can be reported.'))
def do_boot(cs, args):
"""Boot a new server."""
boot_args, boot_kwargs = _boot(cs, args)
extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args)
boot_kwargs.update(extra_boot_kwargs)
server = cs.servers.create(*boot_args, **boot_kwargs)
_print_server(cs, args, server)
if args.poll:
_poll_for_status(cs.servers.get, server.id, 'building', ['active'])
def do_cloudpipe_list(cs, _args):
"""Print a list of all cloudpipe instances."""
cloudpipes = cs.cloudpipe.list()
columns = ['Project Id', "Public IP", "Public Port", "Internal IP"]
utils.print_list(cloudpipes, columns)
@utils.arg('project', metavar='<project_id>',
help=_('UUID of the project to create the cloudpipe for.'))
def do_cloudpipe_create(cs, args):
"""Create a cloudpipe instance for the given project."""
cs.cloudpipe.create(args.project)
@utils.arg('address', metavar='<ip address>', help=_('New IP Address.'))
@utils.arg('port', metavar='<port>', help='New Port.')
def do_cloudpipe_configure(cs, args):
"""Update the VPN IP/port of a cloudpipe instance."""
cs.cloudpipe.update(args.address, args.port)
def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
poll_period=5, show_progress=True,
status_field="status", silent=False):
"""Block while an action is being performed, periodically printing
progress.
"""
def print_progress(progress):
if show_progress:
msg = (_('\rServer %(action)s... %(progress)s%% complete')
% dict(action=action, progress=progress))
else:
msg = _('\rServer %(action)s...') % dict(action=action)
sys.stdout.write(msg)
sys.stdout.flush()
if not silent:
print
while True:
obj = poll_fn(obj_id)
status = getattr(obj, status_field)
if status:
status = status.lower()
progress = getattr(obj, 'progress', None) or 0
if status in final_ok_states:
if not silent:
print_progress(100)
print(_("\nFinished"))
break
elif status == "error":
if not silent:
print(_("\nError %s server") % action)
raise exceptions.InstanceInErrorState(obj.fault['message'])
if not silent:
print_progress(progress)
time.sleep(poll_period)
def _translate_keys(collection, convert):
for item in collection:
keys = item.__dict__.keys()
for from_key, to_key in convert:
if from_key in keys and to_key not in keys:
setattr(item, to_key, item._info[from_key])
def _translate_extended_states(collection):
power_states = [
'NOSTATE', # 0x00
'Running', # 0x01
'', # 0x02
'Paused', # 0x03
'Shutdown', # 0x04
'', # 0x05
'Crashed', # 0x06
'Suspended' # 0x07
]
for item in collection:
try:
setattr(item, 'power_state',
power_states[getattr(item, 'power_state')]
)
except AttributeError:
setattr(item, 'power_state', "N/A")
try:
getattr(item, 'task_state')
except AttributeError:
setattr(item, 'task_state', "N/A")
def _translate_flavor_keys(collection):
_translate_keys(collection, [('ram', 'memory_mb')])
def _print_flavor_extra_specs(flavor):
try:
return flavor.get_keys()
except exceptions.NotFound:
return "N/A"
def _print_flavor_list(flavors, show_extra_specs=False):
_translate_flavor_keys(flavors)
headers = [
'ID',
'Name',
'Memory_MB',
'Disk',
'Ephemeral',
'Swap',
'VCPUs',
'RXTX_Factor',
'Is_Public',
]
if show_extra_specs:
formatters = {'extra_specs': _print_flavor_extra_specs}
headers.append('extra_specs')
else:
formatters = {}
utils.print_list(flavors, headers, formatters)
@utils.arg('--extra-specs',
dest='extra_specs',
action='store_true',
default=False,
help=_('Get extra-specs of each flavor.'))
@utils.arg('--all',
dest='all',
action='store_true',
default=False,
help=_('Display all flavors (Admin only).'))
def do_flavor_list(cs, args):
"""Print a list of available 'flavors' (sizes of servers)."""
if args.all:
flavors = cs.flavors.list(is_public=None)
else:
flavors = cs.flavors.list()
_print_flavor_list(flavors, args.extra_specs)
@utils.arg('flavor',
metavar='<flavor>',
help=_("Name or ID of the flavor to delete"))
def do_flavor_delete(cs, args):
"""Delete a specific flavor"""
flavorid = _find_flavor(cs, args.flavor)
cs.flavors.delete(flavorid)
_print_flavor_list([flavorid])
@utils.arg('flavor',
metavar='<flavor>',
help=_("Name or ID of flavor"))
def do_flavor_show(cs, args):
"""Show details about the given flavor."""
flavor = _find_flavor(cs, args.flavor)
_print_flavor(flavor)
@utils.arg('name',
metavar='<name>',
help=_("Name of the new flavor"))
@utils.arg('id',
metavar='<id>',
help=_("Unique ID (integer or UUID) for the new flavor."
" If specifying 'auto', a UUID will be generated as id"))
@utils.arg('ram',
metavar='<ram>',
help=_("Memory size in MB"))
@utils.arg('disk',
metavar='<disk>',
help=_("Disk size in GB"))
@utils.arg('--ephemeral',
metavar='<ephemeral>',
help=_("Ephemeral space size in GB (default 0)"),
default=0)
@utils.arg('vcpus',
metavar='<vcpus>',
help=_("Number of vcpus"))
@utils.arg('--swap',
metavar='<swap>',
help=_("Swap space size in MB (default 0)"),
default=0)
@utils.arg('--rxtx-factor',
metavar='<factor>',
help=_("RX/TX factor (default 1)"),
default=1.0)
@utils.arg('--is-public',
metavar='<is-public>',
help=_("Make flavor accessible to the public (default true)"),
type=lambda v: strutils.bool_from_string(v, True),
default=True)
def do_flavor_create(cs, args):
"""Create a new flavor"""
f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id,
args.ephemeral, args.swap, args.rxtx_factor,
args.is_public)
_print_flavor_list([f])
@utils.arg('flavor',
metavar='<flavor>',
help=_("Name or ID of flavor"))
@utils.arg('action',
metavar='<action>',
choices=['set', 'unset'],
help=_("Actions: 'set' or 'unset'"))
@utils.arg('metadata',
metavar='<key=value>',
nargs='+',
action='append',
default=[],
help=_('Extra_specs to set/unset (only key is necessary on unset)'))
def do_flavor_key(cs, args):
"""Set or unset extra_spec for a flavor."""
flavor = _find_flavor(cs, args.flavor)
keypair = _extract_metadata(args)
if args.action == 'set':
flavor.set_keys(keypair)
elif args.action == 'unset':
flavor.unset_keys(keypair.keys())
@utils.arg('--flavor',
metavar='<flavor>',
help=_("Filter results by flavor name or ID."))
@utils.arg('--tenant', metavar='<tenant_id>',
help=_('Filter results by tenant ID.'))
def do_flavor_access_list(cs, args):
"""Print access information about the given flavor."""
if args.flavor and args.tenant:
raise exceptions.CommandError(_("Unable to filter results by "
"both --flavor and --tenant."))
elif args.flavor:
flavor = _find_flavor(cs, args.flavor)
if flavor.is_public:
raise exceptions.CommandError(_("Failed to get access list "
"for public flavor type."))
kwargs = {'flavor': flavor}
elif args.tenant:
kwargs = {'tenant': args.tenant}
else:
raise exceptions.CommandError(_("Unable to get all access lists. "
"Specify --flavor or --tenant"))
try:
access_list = cs.flavor_access.list(**kwargs)
except NotImplementedError as e:
raise exceptions.CommandError("%s" % str(e))
columns = ['Flavor_ID', 'Tenant_ID']
utils.print_list(access_list, columns)
@utils.arg('flavor',
metavar='<flavor>',
help=_("Flavor name or ID to add access for the given tenant."))
@utils.arg('tenant', metavar='<tenant_id>',
help=_('Tenant ID to add flavor access for.'))
def do_flavor_access_add(cs, args):
"""Add flavor access for the given tenant."""
flavor = _find_flavor_for_admin(cs, args.flavor)
access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant)
columns = ['Flavor_ID', 'Tenant_ID']
utils.print_list(access_list, columns)
@utils.arg('flavor',
metavar='<flavor>',
help=_("Flavor name or ID to remove access for the given tenant."))
@utils.arg('tenant', metavar='<tenant_id>',
help=_('Tenant ID to remove flavor access for.'))
def do_flavor_access_remove(cs, args):
"""Remove flavor access for the given tenant."""
flavor = _find_flavor_for_admin(cs, args.flavor)
access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant)
columns = ['Flavor_ID', 'Tenant_ID']
utils.print_list(access_list, columns)
@utils.arg('project_id', metavar='<project_id>',
help=_('The ID of the project.'))
def do_scrub(cs, args):
"""Delete data associated with the project."""
networks_list = cs.networks.list()
networks_list = [network for network in networks_list
if getattr(network, 'project_id', '') == args.project_id]
search_opts = {'all_tenants': 1}
groups = cs.security_groups.list(search_opts)
groups = [group for group in groups
if group.tenant_id == args.project_id]
for network in networks_list:
cs.networks.disassociate(network)
for group in groups:
cs.security_groups.delete(group)
def do_network_list(cs, _args):
"""Print a list of available networks."""
network_list = cs.networks.list()
columns = ['ID', 'Label', 'Cidr']
utils.print_list(network_list, columns)
@utils.arg('network',
metavar='<network>',
help=_("uuid or label of network"))
def do_network_show(cs, args):
"""Show details about the given network."""
network = utils.find_resource(cs.networks, args.network)
utils.print_dict(network._info)
@utils.arg('--host-only',
dest='host_only',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=0)
@utils.arg('--project-only',
dest='project_only',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=0)
@utils.arg('network',
metavar='<network>',
help="uuid of network")
def do_network_disassociate(cs, args):
"""Disassociate host and/or project from the given network."""
if args.host_only:
cs.networks.disassociate(args.network, True, False)
elif args.project_only:
cs.networks.disassociate(args.network, False, True)
else:
cs.networks.disassociate(args.network, True, True)
@utils.arg('network',
metavar='<network>',
help="uuid of network")
@utils.arg('host',
metavar='<host>',
help="Name of host")
def do_network_associate_host(cs, args):
"""Associate host with network."""
cs.networks.associate_host(args.network, args.host)
@utils.arg('network',
metavar='<network>',
help="uuid of network")
def do_network_associate_project(cs, args):
"""Associate project with network."""
cs.networks.associate_project(args.network)
def _filter_network_create_options(args):
valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6',
'gateway', 'gateway_v6', 'bridge', 'bridge_interface',
'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr',
'project_id', 'priority']
kwargs = {}
for k, v in args.__dict__.items():
if k in valid_args and v is not None:
kwargs[k] = v
return kwargs
@utils.arg('label',
metavar='<network_label>',
help=_("Label for network"))
@utils.arg('--fixed-range-v4',
dest='cidr',
metavar='<x.x.x.x/yy>',
help=_("IPv4 subnet (ex: 10.0.0.0/8)"))
@utils.arg('--fixed-range-v6',
dest="cidr_v6",
help=_('IPv6 subnet (ex: fe80::/64'))
@utils.arg('--vlan',
dest='vlan_start',
metavar='<vlan id>',
help=_("vlan id"))
@utils.arg('--vpn',
dest='vpn_start',
metavar='<vpn start>',
help=_("vpn start"))
@utils.arg('--gateway',
dest="gateway",
help=_('gateway'))
@utils.arg('--gateway-v6',
dest="gateway_v6",
help=_('ipv6 gateway'))
@utils.arg('--bridge',
dest="bridge",
metavar='<bridge>',
help=_('VIFs on this network are connected to this bridge'))
@utils.arg('--bridge-interface',
dest="bridge_interface",
metavar='<bridge interface>',
help=_('the bridge is connected to this interface'))
@utils.arg('--multi-host',
dest="multi_host",
metavar="<'T'|'F'>",
help=_('Multi host'))
@utils.arg('--dns1',
dest="dns1",
metavar="<DNS Address>", help='First DNS')
@utils.arg('--dns2',
dest="dns2",
metavar="<DNS Address>",
help=_('Second DNS'))
@utils.arg('--uuid',
dest="uuid",
metavar="<network uuid>",
help=_('Network UUID'))
@utils.arg('--fixed-cidr',
dest="fixed_cidr",
metavar='<x.x.x.x/yy>',
help=_('IPv4 subnet for fixed IPS (ex: 10.20.0.0/16)'))
@utils.arg('--project-id',
dest="project_id",
metavar="<project id>",
help=_('Project id'))
@utils.arg('--priority',
dest="priority",
metavar="<number>",
help=_('Network interface priority'))
def do_network_create(cs, args):
"""Create a network."""
if not (args.cidr or args.cidr_v6):
raise exceptions.CommandError(
_("Must specify eith fixed_range_v4 or fixed_range_v6"))
kwargs = _filter_network_create_options(args)
if args.multi_host is not None:
kwargs['multi_host'] = bool(args.multi_host == 'T' or
strutils.bool_from_string(args.multi_host))
cs.networks.create(**kwargs)
@utils.arg('--limit',
dest="limit",
metavar="<limit>",
help=_('number of images to return per request'))
def do_image_list(cs, _args):
"""Print a list of available images to boot from."""
limit = _args.limit
image_list = cs.images.list(limit=limit)
def parse_server_name(image):
try:
return image.server['id']
except (AttributeError, KeyError):
return ''
fmts = {'Server': parse_server_name}
utils.print_list(image_list, ['ID', 'Name', 'Status', 'Server'],
fmts, sortby_index=1)
@utils.arg('image',
metavar='<image>',
help=_("Name or ID of image"))
@utils.arg('action',
metavar='<action>',
choices=['set', 'delete'],
help=_("Actions: 'set' or 'delete'"))
@utils.arg('metadata',
metavar='<key=value>',
nargs='+',
action='append',
default=[],
help=_('Metadata to add/update or delete (only key is necessary on '
'delete)'))
def do_image_meta(cs, args):
"""Set or Delete metadata on an image."""
image = _find_image(cs, args.image)
metadata = _extract_metadata(args)
if args.action == 'set':
cs.images.set_meta(image, metadata)
elif args.action == 'delete':
cs.images.delete_meta(image, metadata.keys())
def _extract_metadata(args):
metadata = {}
for metadatum in args.metadata[0]:
# Can only pass the key in on 'delete'
# So this doesn't have to have '='
if metadatum.find('=') > -1:
(key, value) = metadatum.split('=', 1)
else:
key = metadatum
value = None
metadata[key] = value
return metadata
def _print_image(image):
info = image._info.copy()
# ignore links, we don't need to present those
info.pop('links')
# try to replace a server entity to just an id
server = info.pop('server', None)
try:
info['server'] = server['id']
except (KeyError, TypeError):
pass
# break up metadata and display each on its own row
metadata = info.pop('metadata', {})
try:
for key, value in metadata.items():
_key = 'metadata %s' % key
info[_key] = value
except AttributeError:
pass
utils.print_dict(info)
def _print_flavor(flavor):
info = flavor._info.copy()
# ignore links, we don't need to present those
info.pop('links')
info.update({"extra_specs": _print_flavor_extra_specs(flavor)})
utils.print_dict(info)
@utils.arg('image',
metavar='<image>',
help=_("Name or ID of image"))
def do_image_show(cs, args):
"""Show details about the given image."""
image = _find_image(cs, args.image)
_print_image(image)
@utils.arg('image', metavar='<image>', nargs='+',
help=_('Name or ID of image(s).'))
def do_image_delete(cs, args):
"""Delete specified image(s)."""
for image in args.image:
try:
_find_image(cs, image).delete()
except Exception as e:
print(_("Delete for image %(image)s failed: %(e)s") %
{'image': image, 'e': e})
@utils.arg('--reservation-id',
dest='reservation_id',
metavar='<reservation-id>',
default=None,
help=_('Only return servers that match reservation-id.'))
@utils.arg('--reservation_id',
help=argparse.SUPPRESS)
@utils.arg('--ip',
dest='ip',
metavar='<ip-regexp>',
default=None,
help=_('Search with regular expression match by IP address (Admin only).'))
@utils.arg('--ip6',
dest='ip6',
metavar='<ip6-regexp>',
default=None,
help=_('Search with regular expression match by IPv6 address '
'(Admin only).'))
@utils.arg('--name',
dest='name',
metavar='<name-regexp>',
default=None,
help=_('Search with regular expression match by name'))
@utils.arg('--instance-name',
dest='instance_name',
metavar='<name-regexp>',
default=None,
help=_('Search with regular expression match by server name '
'(Admin only).'))
@utils.arg('--instance_name',
help=argparse.SUPPRESS)
@utils.arg('--status',
dest='status',
metavar='<status>',
default=None,
help=_('Search by server status'))
@utils.arg('--flavor',
dest='flavor',
metavar='<flavor>',
default=None,
help=_('Search by flavor name or ID'))
@utils.arg('--image',
dest='image',
metavar='<image>',
default=None,
help=_('Search by image name or ID'))
@utils.arg('--host',
dest='host',
metavar='<hostname>',
default=None,
help=_('Search servers by hostname to which they are assigned (Admin '
'only).'))
@utils.arg('--all-tenants',
dest='all_tenants',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=int(strutils.bool_from_string(
os.environ.get("ALL_TENANTS", 'false'), True)),
help=_('Display information from all tenants (Admin only).'))
@utils.arg('--all_tenants',
nargs='?',
type=int,
const=1,
help=argparse.SUPPRESS)
@utils.arg('--tenant',
#nova db searches by project_id
dest='tenant',
metavar='<tenant>',
nargs='?',
help=_('Display information from single tenant (Admin only).'))
@utils.arg('--deleted',
dest='deleted',
action="store_true",
default=False,
help='Only display deleted servers (Admin only).')
@utils.arg('--fields',
default=None,
metavar='<fields>',
help=_('Comma-separated list of fields to display. '
'Use the show command to see which fields are available.'))
@utils.arg('--minimal',
dest='minimal',
action="store_true",
default=False,
help=_('Get only uuid and name.'))
def do_list(cs, args):
"""List active servers."""
imageid = None
flavorid = None
if args.image:
imageid = _find_image(cs, args.image).id
if args.flavor:
flavorid = _find_flavor(cs, args.flavor).id
search_opts = {
'all_tenants': args.all_tenants,
'reservation_id': args.reservation_id,
'ip': args.ip,
'ip6': args.ip6,
'name': args.name,
'image': imageid,
'flavor': flavorid,
'status': args.status,
'tenant_id': args.tenant,
'host': args.host,
'deleted': args.deleted,
'instance_name': args.instance_name}
filters = {'flavor': lambda f: f['id'],
'security_groups': utils._format_security_groups}
formatters = {}
field_titles = []
if args.fields:
for field in args.fields.split(','):
field_title, formatter = utils._make_field_formatter(field,
filters)
field_titles.append(field_title)
formatters[field_title] = formatter
id_col = 'ID'
detailed = not args.minimal
servers = cs.servers.list(detailed=detailed,
search_opts=search_opts)
convert = [('OS-EXT-SRV-ATTR:host', 'host'),
('OS-EXT-STS:task_state', 'task_state'),
('OS-EXT-SRV-ATTR:instance_name', 'instance_name'),
('OS-EXT-STS:power_state', 'power_state'),
('hostId', 'host_id')]
_translate_keys(servers, convert)
_translate_extended_states(servers)
if args.minimal:
columns = [
id_col,
'Name']
elif field_titles:
columns = [id_col] + field_titles
else:
columns = [
id_col,
'Name',
'Status',
'Task State',
'Power State',
'Networks'
]
formatters['Networks'] = utils._format_servers_list_networks
utils.print_list(servers, columns,
formatters, sortby_index=1)
@utils.arg('--hard',
dest='reboot_type',
action='store_const',
const=servers.REBOOT_HARD,
default=servers.REBOOT_SOFT,
help=_('Perform a hard reboot (instead of a soft one).'))
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while server is rebooting.'))
def do_reboot(cs, args):
"""Reboot a server."""
server = _find_server(cs, args.server)
server.reboot(args.reboot_type)
if args.poll:
_poll_for_status(cs.servers.get, server.id, 'rebooting', ['active'],
show_progress=False)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('image', metavar='<image>', help=_("Name or ID of new image."))
@utils.arg('--rebuild-password',
dest='rebuild_password',
metavar='<rebuild-password>',
default=False,
help=_("Set the provided password on the rebuild server."))
@utils.arg('--rebuild_password',
help=argparse.SUPPRESS)
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while server rebuilds so progress can be reported.'))
@utils.arg('--minimal',
dest='minimal',
action="store_true",
default=False,
help=_('Skips flavor/image lookups when showing servers'))
@utils.arg('--preserve-ephemeral',
action="store_true",
default=False,
help='Preserve the default ephemeral storage partition on rebuild.')
def do_rebuild(cs, args):
"""Shutdown, re-image, and re-boot a server."""
server = _find_server(cs, args.server)
image = _find_image(cs, args.image)
if args.rebuild_password is not False:
_password = args.rebuild_password
else:
_password = None
kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args)
kwargs['preserve_ephemeral'] = args.preserve_ephemeral
server = server.rebuild(image, _password, **kwargs)
_print_server(cs, args, server)
if args.poll:
_poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active'])
@utils.arg('server', metavar='<server>',
help=_('Name (old name) or ID of server.'))
@utils.arg('name', metavar='<name>', help=_('New name for the server.'))
def do_rename(cs, args):
"""Rename a server."""
_find_server(cs, args.server).update(name=args.name)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('flavor', metavar='<flavor>', help=_("Name or ID of new flavor."))
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while servers resizes so progress can be reported.'))
def do_resize(cs, args):
"""Resize a server."""
server = _find_server(cs, args.server)
flavor = _find_flavor(cs, args.flavor)
kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args)
server.resize(flavor, **kwargs)
if args.poll:
_poll_for_status(cs.servers.get, server.id, 'resizing',
['active', 'verify_resize'])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_resize_confirm(cs, args):
"""Confirm a previous resize."""
_find_server(cs, args.server).confirm_resize()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_resize_revert(cs, args):
"""Revert a previous resize (and return to the previous VM)."""
_find_server(cs, args.server).revert_resize()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while server migrates so progress can be reported.'))
def do_migrate(cs, args):
"""Migrate a server. The new host will be selected by the scheduler."""
server = _find_server(cs, args.server)
server.migrate()
if args.poll:
_poll_for_status(cs.servers.get, server.id, 'migrating',
['active', 'verify_resize'])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_pause(cs, args):
"""Pause a server."""
_find_server(cs, args.server).pause()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_unpause(cs, args):
"""Unpause a server."""
_find_server(cs, args.server).unpause()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_stop(cs, args):
"""Stop a server."""
_find_server(cs, args.server).stop()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_start(cs, args):
"""Start a server."""
_find_server(cs, args.server).start()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_lock(cs, args):
"""Lock a server."""
_find_server(cs, args.server).lock()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_unlock(cs, args):
"""Unlock a server."""
_find_server(cs, args.server).unlock()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_suspend(cs, args):
"""Suspend a server."""
_find_server(cs, args.server).suspend()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_resume(cs, args):
"""Resume a server."""
_find_server(cs, args.server).resume()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_rescue(cs, args):
"""Rescue a server."""
utils.print_dict(_find_server(cs, args.server).rescue()[1])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_unrescue(cs, args):
"""Unrescue a server."""
_find_server(cs, args.server).unrescue()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_shelve(cs, args):
"""Shelve a server."""
_find_server(cs, args.server).shelve()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_shelve_offload(cs, args):
"""Remove a shelved server from the compute node."""
_find_server(cs, args.server).shelve_offload()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_unshelve(cs, args):
"""Unshelve a server."""
_find_server(cs, args.server).unshelve()
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_diagnostics(cs, args):
"""Retrieve server diagnostics."""
server = _find_server(cs, args.server)
utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80)
@utils.arg('server', metavar='<server>',
help=_('Name or ID of a server for which the network cache should '
'be refreshed from neutron (Admin only).'))
def do_refresh_network(cs, args):
"""Refresh server network information."""
server = _find_server(cs, args.server)
cs.server_external_events.create([{'server_uuid': server.id,
'name': 'network-changed'}])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_root_password(cs, args):
"""
Change the root password for a server.
"""
server = _find_server(cs, args.server)
p1 = getpass.getpass('New password: ')
p2 = getpass.getpass('Again: ')
if p1 != p2:
raise exceptions.CommandError(_("Passwords do not match."))
server.change_password(p1)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('name', metavar='<name>', help=_('Name of snapshot.'))
@utils.arg('--show',
dest='show',
action="store_true",
default=False,
help=_('Print image info.'))
@utils.arg('--poll',
dest='poll',
action="store_true",
default=False,
help=_('Blocks while server snapshots so progress can be reported.'))
def do_image_create(cs, args):
"""Create a new image by taking a snapshot of a running server."""
server = _find_server(cs, args.server)
image_uuid = cs.servers.create_image(server, args.name)
if args.poll:
_poll_for_status(cs.images.get, image_uuid, 'snapshotting',
['active'])
# NOTE(sirp): A race-condition exists between when the image finishes
# uploading and when the servers's `task_state` is cleared. To account
# for this, we need to poll a second time to ensure the `task_state` is
# cleared before returning, ensuring that a snapshot taken immediately
# after this function returns will succeed.
#
# A better long-term solution will be to separate 'snapshotting' and
# 'image-uploading' in Nova and clear the task-state once the VM
# snapshot is complete but before the upload begins.
task_state_field = "OS-EXT-STS:task_state"
if hasattr(server, task_state_field):
_poll_for_status(cs.servers.get, server.id, 'image_snapshot',
[None], status_field=task_state_field,
show_progress=False, silent=True)
if args.show:
_print_image(cs.images.get(image_uuid))
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('name', metavar='<name>', help=_('Name of the backup image.'))
@utils.arg('backup_type', metavar='<backup-type>',
help=_('The backup type, like "daily" or "weekly".'))
@utils.arg('rotation', metavar='<rotation>',
help=_('Int parameter representing how many backups to keep '
'around.'))
def do_backup(cs, args):
"""Backup a server by creating a 'backup' type snapshot."""
_find_server(cs, args.server).backup(args.name,
args.backup_type,
args.rotation)
@utils.arg('server',
metavar='<server>',
help=_("Name or ID of server"))
@utils.arg('action',
metavar='<action>',
choices=['set', 'delete'],
help=_("Actions: 'set' or 'delete'"))
@utils.arg('metadata',
metavar='<key=value>',
nargs='+',
action='append',
default=[],
help=_('Metadata to set or delete (only key is necessary on delete)'))
def do_meta(cs, args):
"""Set or Delete metadata on a server."""
server = _find_server(cs, args.server)
metadata = _extract_metadata(args)
if args.action == 'set':
cs.servers.set_meta(server, metadata)
elif args.action == 'delete':
cs.servers.delete_meta(server, sorted(metadata.keys(), reverse=True))
def _print_server(cs, args, server=None):
# By default when searching via name we will do a
# findall(name=blah) and due a REST /details which is not the same
# as a .get() and doesn't get the information about flavors and
# images. This fix it as we redo the call with the id which does a
# .get() to get all informations.
if not server:
server = _find_server(cs, args.server)
minimal = getattr(args, "minimal", False)
networks = server.networks
info = server._info.copy()
for network_label, address_list in networks.items():
info['%s network' % network_label] = ', '.join(address_list)
flavor = info.get('flavor', {})
flavor_id = flavor.get('id', '')
if minimal:
info['flavor'] = flavor_id
else:
info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name,
flavor_id)
if 'security_groups' in info:
info['security_groups'] = \
', '.join(group['name'] for group in info['security_groups'])
image = info.get('image', {})
if image:
image_id = image.get('id', '')
if minimal:
info['image'] = image_id
else:
try:
info['image'] = '%s (%s)' % (_find_image(cs, image_id).name,
image_id)
except Exception:
info['image'] = '%s (%s)' % (_("Image not found"), image_id)
else: # Booted from volume
info['image'] = _("Attempt to boot from volume - no image supplied")
info.pop('links', None)
info.pop('addresses', None)
utils.print_dict(info)
@utils.arg('--minimal',
dest='minimal',
action="store_true",
default=False,
help=_('Skips flavor/image lookups when showing servers'))
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_show(cs, args):
"""Show details about the given server."""
_print_server(cs, args)
@utils.arg('server', metavar='<server>', nargs='+',
help=_('Name or ID of server(s).'))
def do_delete(cs, args):
"""Immediately shut down and delete specified server(s)."""
failure_count = 0
for server in args.server:
try:
_find_server(cs, server).delete()
except Exception as e:
failure_count += 1
print(e)
if failure_count == len(args.server):
raise exceptions.CommandError(_("Unable to delete any of the "
"specified servers."))
def _find_server(cs, server):
"""Get a server by name or ID."""
return utils.find_resource(cs.servers, server)
def _find_image(cs, image):
"""Get an image by name or ID."""
return utils.find_resource(cs.images, image)
def _find_flavor_for_admin(cs, flavor):
"""Get a flavor for administrator by name, ID, or RAM size."""
try:
return utils.find_resource(cs.flavors, flavor, is_public=None)
except exceptions.NotFound:
return cs.flavors.find(ram=flavor)
def _find_flavor(cs, flavor):
"""Get a flavor by name, ID, or RAM size."""
try:
return utils.find_resource(cs.flavors, flavor)
except exceptions.NotFound:
return cs.flavors.find(ram=flavor)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('network_id',
metavar='<network-id>',
help='Network ID.')
def do_add_fixed_ip(cs, args):
"""Add new IP address on a network to server."""
server = _find_server(cs, args.server)
server.add_fixed_ip(args.network_id)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
def do_remove_fixed_ip(cs, args):
"""Remove an IP address from a server."""
server = _find_server(cs, args.server)
server.remove_fixed_ip(args.address)
def _find_volume(cs, volume):
"""Get a volume by name or ID."""
return utils.find_resource(cs.volumes, volume)
def _find_volume_snapshot(cs, snapshot):
"""Get a volume snapshot by name or ID."""
return utils.find_resource(cs.volume_snapshots, snapshot)
def _print_volume(volume):
utils.print_dict(volume._info)
def _print_volume_snapshot(snapshot):
utils.print_dict(snapshot._info)
def _translate_volume_keys(collection):
_translate_keys(collection,
[('displayName', 'display_name'),
('volumeType', 'volume_type')])
def _translate_volume_snapshot_keys(collection):
_translate_keys(collection,
[('displayName', 'display_name'),
('volumeId', 'volume_id')])
def _translate_availability_zone_keys(collection):
_translate_keys(collection,
[('zoneName', 'name'), ('zoneState', 'status')])
@utils.arg('--all-tenants',
dest='all_tenants',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=int(strutils.bool_from_string(
os.environ.get("ALL_TENANTS", 'false'), True)),
help=_('Display information from all tenants (Admin only).'))
@utils.arg('--all_tenants',
nargs='?',
type=int,
const=1,
help=argparse.SUPPRESS)
@utils.service_type('volume')
def do_volume_list(cs, args):
"""List all the volumes."""
search_opts = {'all_tenants': args.all_tenants}
volumes = cs.volumes.list(search_opts=search_opts)
_translate_volume_keys(volumes)
# Create a list of servers to which the volume is attached
for vol in volumes:
servers = [s.get('server_id') for s in vol.attachments]
setattr(vol, 'attached_to', ','.join(map(str, servers)))
utils.print_list(volumes, ['ID', 'Status', 'Display Name',
'Size', 'Volume Type', 'Attached to'])
@utils.arg('volume', metavar='<volume>', help=_('Name or ID of the volume.'))
@utils.service_type('volume')
def do_volume_show(cs, args):
"""Show details about a volume."""
volume = _find_volume(cs, args.volume)
_print_volume(volume)
@utils.arg('size',
metavar='<size>',
type=int,
help=_('Size of volume in GB'))
@utils.arg('--snapshot-id',
metavar='<snapshot-id>',
default=None,
help=_('Optional snapshot id to create the volume from. (Default=None)'))
@utils.arg('--snapshot_id',
help=argparse.SUPPRESS)
@utils.arg('--image-id',
metavar='<image-id>',
help=_('Optional image id to create the volume from. (Default=None)'),
default=None)
@utils.arg('--display-name',
metavar='<display-name>',
default=None,
help=_('Optional volume name. (Default=None)'))
@utils.arg('--display_name',
help=argparse.SUPPRESS)
@utils.arg('--display-description',
metavar='<display-description>',
default=None,
help=_('Optional volume description. (Default=None)'))
@utils.arg('--display_description',
help=argparse.SUPPRESS)
@utils.arg('--volume-type',
metavar='<volume-type>',
default=None,
help=_('Optional volume type. (Default=None)'))
@utils.arg('--volume_type',
help=argparse.SUPPRESS)
@utils.arg('--availability-zone', metavar='<availability-zone>',
help=_('Optional Availability Zone for volume. (Default=None)'),
default=None)
@utils.service_type('volume')
def do_volume_create(cs, args):
"""Add a new volume."""
volume = cs.volumes.create(args.size,
args.snapshot_id,
args.display_name,
args.display_description,
args.volume_type,
args.availability_zone,
imageRef=args.image_id)
_print_volume(volume)
@utils.arg('volume',
metavar='<volume>', nargs='+',
help=_('Name or ID of the volume(s) to delete.'))
@utils.service_type('volume')
def do_volume_delete(cs, args):
"""Remove volume(s)."""
for volume in args.volume:
try:
_find_volume(cs, volume).delete()
except Exception as e:
print(_("Delete for volume %(volume)s failed: %(e)s") %
{'volume': volume, 'e': e})
@utils.arg('server',
metavar='<server>',
help=_('Name or ID of server.'))
@utils.arg('volume',
metavar='<volume>',
help=_('ID of the volume to attach.'))
@utils.arg('device', metavar='<device>', default=None, nargs='?',
help=_('Name of the device e.g. /dev/vdb. '
'Use "auto" for autoassign (if supported)'))
def do_volume_attach(cs, args):
"""Attach a volume to a server."""
if args.device == 'auto':
args.device = None
volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id,
args.volume,
args.device)
_print_volume(volume)
@utils.arg('server',
metavar='<server>',
help=_('Name or ID of server.'))
@utils.arg('attachment_id',
metavar='<volume>',
help=_('Attachment ID of the volume.'))
@utils.arg('new_volume',
metavar='<volume>',
help=_('ID of the volume to attach.'))
def do_volume_update(cs, args):
"""Update volume attachment."""
volume = cs.volumes.update_server_volume(_find_server(cs, args.server).id,
args.attachment_id,
args.new_volume)
_print_volume(volume)
@utils.arg('server',
metavar='<server>',
help=_('Name or ID of server.'))
@utils.arg('attachment_id',
metavar='<volume>',
help=_('Attachment ID of the volume.'))
def do_volume_detach(cs, args):
"""Detach a volume from a server."""
cs.volumes.delete_server_volume(_find_server(cs, args.server).id,
args.attachment_id)
@utils.service_type('volume')
def do_volume_snapshot_list(cs, _args):
"""List all the snapshots."""
snapshots = cs.volume_snapshots.list()
_translate_volume_snapshot_keys(snapshots)
utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name',
'Size'])
@utils.arg('snapshot',
metavar='<snapshot>',
help=_('Name or ID of the snapshot.'))
@utils.service_type('volume')
def do_volume_snapshot_show(cs, args):
"""Show details about a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
_print_volume_snapshot(snapshot)
@utils.arg('volume_id',
metavar='<volume-id>',
help=_('ID of the volume to snapshot'))
@utils.arg('--force',
metavar='<True|False>',
help=_('Optional flag to indicate whether to snapshot a volume even if '
'its attached to a server. (Default=False)'),
default=False)
@utils.arg('--display-name',
metavar='<display-name>',
default=None,
help=_('Optional snapshot name. (Default=None)'))
@utils.arg('--display_name',
help=argparse.SUPPRESS)
@utils.arg('--display-description',
metavar='<display-description>',
default=None,
help=_('Optional snapshot description. (Default=None)'))
@utils.arg('--display_description',
help=argparse.SUPPRESS)
@utils.service_type('volume')
def do_volume_snapshot_create(cs, args):
"""Add a new snapshot."""
snapshot = cs.volume_snapshots.create(args.volume_id,
args.force,
args.display_name,
args.display_description)
_print_volume_snapshot(snapshot)
@utils.arg('snapshot',
metavar='<snapshot>',
help=_('Name or ID of the snapshot to delete.'))
@utils.service_type('volume')
def do_volume_snapshot_delete(cs, args):
"""Remove a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
snapshot.delete()
def _print_volume_type_list(vtypes):
utils.print_list(vtypes, ['ID', 'Name'])
@utils.service_type('volume')
def do_volume_type_list(cs, args):
"""Print a list of available 'volume types'."""
vtypes = cs.volume_types.list()
_print_volume_type_list(vtypes)
@utils.arg('name',
metavar='<name>',
help=_("Name of the new volume type"))
@utils.service_type('volume')
def do_volume_type_create(cs, args):
"""Create a new volume type."""
vtype = cs.volume_types.create(args.name)
_print_volume_type_list([vtype])
@utils.arg('id',
metavar='<id>',
help=_("Unique ID of the volume type to delete"))
@utils.service_type('volume')
def do_volume_type_delete(cs, args):
"""Delete a specific volume type."""
cs.volume_types.delete(args.id)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('console_type',
metavar='<console-type>',
help=_('Type of vnc console ("novnc" or "xvpvnc").'))
def do_get_vnc_console(cs, args):
"""Get a vnc console to a server."""
server = _find_server(cs, args.server)
data = server.get_vnc_console(args.console_type)
class VNCConsole:
def __init__(self, console_dict):
self.type = console_dict['type']
self.url = console_dict['url']
utils.print_list([VNCConsole(data['console'])], ['Type', 'Url'])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('console_type',
metavar='<console-type>',
help=_('Type of spice console ("spice-html5").'))
def do_get_spice_console(cs, args):
"""Get a spice console to a server."""
server = _find_server(cs, args.server)
data = server.get_spice_console(args.console_type)
class SPICEConsole:
def __init__(self, console_dict):
self.type = console_dict['type']
self.url = console_dict['url']
utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url'])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('console_type',
metavar='<console-type>',
help='Type of rdp console ("rdp-html5").')
def do_get_rdp_console(cs, args):
"""Get a rdp console to a server."""
server = _find_server(cs, args.server)
data = server.get_rdp_console(args.console_type)
class RDPConsole:
def __init__(self, console_dict):
self.type = console_dict['type']
self.url = console_dict['url']
utils.print_list([RDPConsole(data['console'])], ['Type', 'Url'])
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('private_key',
metavar='<private-key>',
help=_('Private key (used locally to decrypt password) (Optional). '
'When specified, the command displays the clear (decrypted) VM '
'password. When not specified, the ciphered VM password is '
'displayed.'),
nargs='?',
default=None)
def do_get_password(cs, args):
"""Get password for a server."""
server = _find_server(cs, args.server)
data = server.get_password(args.private_key)
print(data)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_clear_password(cs, args):
"""Clear password for a server."""
server = _find_server(cs, args.server)
server.clear_password()
def _print_floating_ip_list(floating_ips):
utils.print_list(floating_ips, ['Ip', 'Server Id', 'Fixed Ip', 'Pool'])
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--length',
metavar='<length>',
default=None,
help=_('Length in lines to tail.'))
def do_console_log(cs, args):
"""Get console log output of a server."""
server = _find_server(cs, args.server)
data = server.get_console_output(length=args.length)
print(data)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
@utils.arg('--fixed-address',
metavar='<fixed_address>',
default=None,
help=_('Fixed IP Address to associate with.'))
def do_add_floating_ip(cs, args):
"""DEPRECATED, use floating-ip-associate instead."""
_associate_floating_ip(cs, args)
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('address', metavar='<address>', help='IP Address.')
@utils.arg('--fixed-address',
metavar='<fixed_address>',
default=None,
help='Fixed IP Address to associate with.')
def do_floating_ip_associate(cs, args):
"""Associate a floating IP address to a server."""
_associate_floating_ip(cs, args)
def _associate_floating_ip(cs, args):
server = _find_server(cs, args.server)
server.add_floating_ip(args.address, args.fixed_address)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
def do_remove_floating_ip(cs, args):
"""DEPRECATED, use floating-ip-disassociate instead."""
_disassociate_floating_ip(cs, args)
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('address', metavar='<address>', help='IP Address.')
def do_floating_ip_disassociate(cs, args):
"""Disassociate a floating IP address from a server."""
_disassociate_floating_ip(cs, args)
def _disassociate_floating_ip(cs, args):
server = _find_server(cs, args.server)
server.remove_floating_ip(args.address)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('secgroup', metavar='<secgroup>', help=_('Name of Security Group.'))
def do_add_secgroup(cs, args):
"""Add a Security Group to a server."""
server = _find_server(cs, args.server)
server.add_security_group(args.secgroup)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('secgroup', metavar='<secgroup>', help=_('Name of Security Group.'))
def do_remove_secgroup(cs, args):
"""Remove a Security Group from a server."""
server = _find_server(cs, args.server)
server.remove_security_group(args.secgroup)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_list_secgroup(cs, args):
"""List Security Group(s) of a server."""
server = _find_server(cs, args.server)
groups = server.list_security_group()
_print_secgroups(groups)
@utils.arg('pool',
metavar='<floating-ip-pool>',
help=_('Name of Floating IP Pool. (Optional)'),
nargs='?',
default=None)
def do_floating_ip_create(cs, args):
"""Allocate a floating IP for the current tenant."""
_print_floating_ip_list([cs.floating_ips.create(pool=args.pool)])
@utils.arg('address', metavar='<address>', help=_('IP of Floating Ip.'))
def do_floating_ip_delete(cs, args):
"""De-allocate a floating IP."""
floating_ips = cs.floating_ips.list()
for floating_ip in floating_ips:
if floating_ip.ip == args.address:
return cs.floating_ips.delete(floating_ip.id)
raise exceptions.CommandError(_("Floating ip %s not found.") %
args.address)
@utils.arg('--all-tenants',
action='store_true',
default=False,
help=_('Display floatingips from all tenants (Admin only).'))
def do_floating_ip_list(cs, args):
"""List floating ips."""
_print_floating_ip_list(cs.floating_ips.list(args.all_tenants))
def do_floating_ip_pool_list(cs, _args):
"""List all floating ip pools."""
utils.print_list(cs.floating_ip_pools.list(), ['name'])
@utils.arg('--host', dest='host', metavar='<host>', default=None,
help=_('Filter by host'))
def do_floating_ip_bulk_list(cs, args):
"""List all floating ips."""
utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id',
'address',
'instance_uuid',
'pool',
'interface'])
@utils.arg('ip_range', metavar='<range>', help=_('Address range to create'))
@utils.arg('--pool', dest='pool', metavar='<pool>', default=None,
help=_('Pool for new Floating IPs'))
@utils.arg('--interface', metavar='<interface>', default=None,
help=_('Interface for new Floating IPs'))
def do_floating_ip_bulk_create(cs, args):
"""Bulk create floating ips by range."""
cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface)
@utils.arg('ip_range', metavar='<range>', help=_('Address range to delete'))
def do_floating_ip_bulk_delete(cs, args):
"""Bulk delete floating ips by range."""
cs.floating_ips_bulk.delete(args.ip_range)
def _print_dns_list(dns_entries):
utils.print_list(dns_entries, ['ip', 'name', 'domain'])
def _print_domain_list(domain_entries):
utils.print_list(domain_entries, ['domain', 'scope',
'project', 'availability_zone'])
def do_dns_domains(cs, args):
"""Print a list of available dns domains."""
domains = cs.dns_domains.domains()
_print_domain_list(domains)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
@utils.arg('--ip', metavar='<ip>', help=_('ip address'), default=None)
@utils.arg('--name', metavar='<name>', help=_('DNS name'), default=None)
def do_dns_list(cs, args):
"""List current DNS entries for domain and ip or domain and name."""
if not (args.ip or args.name):
raise exceptions.CommandError(
_("You must specify either --ip or --name"))
if args.name:
entry = cs.dns_entries.get(args.domain, args.name)
_print_dns_list([entry])
else:
entries = cs.dns_entries.get_for_ip(args.domain,
ip=args.ip)
_print_dns_list(entries)
@utils.arg('ip', metavar='<ip>', help=_('ip address'))
@utils.arg('name', metavar='<name>', help=_('DNS name'))
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
@utils.arg('--type', metavar='<type>', help=_('dns type (e.g. "A")'),
default='A')
def do_dns_create(cs, args):
"""Create a DNS entry for domain, name and ip."""
cs.dns_entries.create(args.domain, args.name, args.ip, args.type)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
@utils.arg('name', metavar='<name>', help=_('DNS name'))
def do_dns_delete(cs, args):
"""Delete the specified DNS entry."""
cs.dns_entries.delete(args.domain, args.name)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
def do_dns_delete_domain(cs, args):
"""Delete the specified DNS domain."""
cs.dns_domains.delete(args.domain)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
@utils.arg('--availability-zone',
metavar='<availability-zone>',
default=None,
help=_('Limit access to this domain to servers '
'in the specified availability zone.'))
@utils.arg('--availability_zone',
help=argparse.SUPPRESS)
def do_dns_create_private_domain(cs, args):
"""Create the specified DNS domain."""
cs.dns_domains.create_private(args.domain,
args.availability_zone)
@utils.arg('domain', metavar='<domain>', help=_('DNS domain'))
@utils.arg('--project', metavar='<project>',
help=_('Limit access to this domain to users '
'of the specified project.'),
default=None)
def do_dns_create_public_domain(cs, args):
"""Create the specified DNS domain."""
cs.dns_domains.create_public(args.domain,
args.project)
def _print_secgroup_rules(rules):
class FormattedRule:
def __init__(self, obj):
items = (obj if isinstance(obj, dict) else obj._info).items()
for k, v in items:
if k == 'ip_range':
v = v.get('cidr')
elif k == 'group':
k = 'source_group'
v = v.get('name')
if v is None:
v = ''
setattr(self, k, v)
rules = [FormattedRule(rule) for rule in rules]
utils.print_list(rules, ['IP Protocol', 'From Port', 'To Port',
'IP Range', 'Source Group'])
def _print_secgroups(secgroups):
utils.print_list(secgroups, ['Id', 'Name', 'Description'])
def _get_secgroup(cs, secgroup):
# Check secgroup is an ID (nova-network) or UUID (neutron)
if (utils.is_integer_like(strutils.safe_encode(secgroup))
or uuidutils.is_uuid_like(secgroup)):
try:
return cs.security_groups.get(secgroup)
except exceptions.NotFound:
pass
# Check secgroup as a name
match_found = False
for s in cs.security_groups.list():
encoding = (locale.getpreferredencoding() or
sys.stdin.encoding or
'UTF-8')
if not six.PY3:
s.name = s.name.encode(encoding)
if secgroup == s.name:
if match_found is not False:
msg = (_("Multiple security group matches found for name '%s'"
", use an ID to be more specific.") % secgroup)
raise exceptions.NoUniqueMatch(msg)
match_found = s
if match_found is False:
raise exceptions.CommandError(_("Secgroup ID or name '%s' not found.")
% secgroup)
return match_found
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_add_rule(cs, args):
"""Add a rule to a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
rule = cs.security_group_rules.create(secgroup.id,
args.ip_proto,
args.from_port,
args.to_port,
args.cidr)
_print_secgroup_rules([rule])
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_delete_rule(cs, args):
"""Delete a rule from a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
for rule in secgroup.rules:
if (rule['ip_protocol'] and
rule['ip_protocol'].upper() == args.ip_proto.upper() and
rule['from_port'] == int(args.from_port) and
rule['to_port'] == int(args.to_port) and
rule['ip_range']['cidr'] == args.cidr):
_print_secgroup_rules([rule])
return cs.security_group_rules.delete(rule['id'])
raise exceptions.CommandError(_("Rule not found"))
@utils.arg('name', metavar='<name>', help=_('Name of security group.'))
@utils.arg('description', metavar='<description>',
help=_('Description of security group.'))
def do_secgroup_create(cs, args):
"""Create a security group."""
secgroup = cs.security_groups.create(args.name, args.description)
_print_secgroups([secgroup])
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
@utils.arg('name', metavar='<name>', help=_('Name of security group.'))
@utils.arg('description', metavar='<description>',
help=_('Description of security group.'))
def do_secgroup_update(cs, args):
"""Update a security group."""
sg = _get_secgroup(cs, args.secgroup)
secgroup = cs.security_groups.update(sg, args.name, args.description)
_print_secgroups([secgroup])
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
def do_secgroup_delete(cs, args):
"""Delete a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
cs.security_groups.delete(secgroup)
_print_secgroups([secgroup])
@utils.arg('--all-tenants',
dest='all_tenants',
metavar='<0|1>',
nargs='?',
type=int,
const=1,
default=int(strutils.bool_from_string(
os.environ.get("ALL_TENANTS", 'false'), True)),
help=_('Display information from all tenants (Admin only).'))
@utils.arg('--all_tenants',
nargs='?',
type=int,
const=1,
help=argparse.SUPPRESS)
def do_secgroup_list(cs, args):
"""List security groups for the current tenant."""
search_opts = {'all_tenants': args.all_tenants}
columns = ['Id', 'Name', 'Description']
if args.all_tenants:
columns.append('Tenant_ID')
groups = cs.security_groups.list(search_opts=search_opts)
utils.print_list(groups, columns)
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
def do_secgroup_list_rules(cs, args):
"""List rules for a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
_print_secgroup_rules(secgroup.rules)
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
@utils.arg('source_group',
metavar='<source-group>',
help=_('ID or name of source group.'))
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
def do_secgroup_add_group_rule(cs, args):
"""Add a source group rule to a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
source_group = _get_secgroup(cs, args.source_group)
params = {}
params['group_id'] = source_group.id
if args.ip_proto or args.from_port or args.to_port:
if not (args.ip_proto and args.from_port and args.to_port):
raise exceptions.CommandError(_("ip_proto, from_port, and to_port"
" must be specified together"))
params['ip_protocol'] = args.ip_proto.upper()
params['from_port'] = args.from_port
params['to_port'] = args.to_port
rule = cs.security_group_rules.create(secgroup.id, **params)
_print_secgroup_rules([rule])
@utils.arg('secgroup',
metavar='<secgroup>',
help=_('ID or name of security group.'))
@utils.arg('source_group',
metavar='<source-group>',
help=_('ID or name of source group.'))
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
def do_secgroup_delete_group_rule(cs, args):
"""Delete a source group rule from a security group."""
secgroup = _get_secgroup(cs, args.secgroup)
source_group = _get_secgroup(cs, args.source_group)
params = {}
params['group_name'] = source_group.name
if args.ip_proto or args.from_port or args.to_port:
if not (args.ip_proto and args.from_port and args.to_port):
raise exceptions.CommandError(_("ip_proto, from_port, and to_port"
" must be specified together"))
params['ip_protocol'] = args.ip_proto.upper()
params['from_port'] = int(args.from_port)
params['to_port'] = int(args.to_port)
for rule in secgroup.rules:
if (rule.get('ip_protocol').upper() == params.get(
'ip_protocol').upper() and
rule.get('from_port') == params.get('from_port') and
rule.get('to_port') == params.get('to_port') and
rule.get('group', {}).get('name') ==
params.get('group_name')):
return cs.security_group_rules.delete(rule['id'])
raise exceptions.CommandError(_("Rule not found"))
@utils.arg('name', metavar='<name>', help=_('Name of key.'))
@utils.arg('--pub-key',
metavar='<pub-key>',
default=None,
help=_('Path to a public ssh key.'))
@utils.arg('--pub_key',
help=argparse.SUPPRESS)
def do_keypair_add(cs, args):
"""Create a new key pair for use with servers."""
name = args.name
pub_key = args.pub_key
if pub_key:
try:
with open(os.path.expanduser(pub_key)) as f:
pub_key = f.read()
except IOError as e:
raise exceptions.CommandError(_("Can't open or read '%(key)s': "
"%(exc)s") % {'key': pub_key,
'exc': e})
keypair = cs.keypairs.create(name, pub_key)
if not pub_key:
private_key = keypair.private_key
print(private_key)
@utils.arg('name', metavar='<name>', help=_('Keypair name to delete.'))
def do_keypair_delete(cs, args):
"""Delete keypair given by its name."""
name = _find_keypair(cs, args.name)
cs.keypairs.delete(name)
def do_keypair_list(cs, args):
"""Print a list of keypairs for a user"""
keypairs = cs.keypairs.list()
columns = ['Name', 'Fingerprint']
utils.print_list(keypairs, columns)
def _print_keypair(keypair):
kp = keypair._info.copy()
pk = kp.pop('public_key')
utils.print_dict(kp)
print(_("Public key: %s") % pk)
@utils.arg('keypair',
metavar='<keypair>',
help=_("Name or ID of keypair"))
def do_keypair_show(cs, args):
"""Show details about the given keypair."""
keypair = _find_keypair(cs, args.keypair)
_print_keypair(keypair)
def _find_keypair(cs, keypair):
"""Get a keypair by name or ID."""
return utils.find_resource(cs.keypairs, keypair)
@utils.arg('--tenant',
#nova db searches by project_id
dest='tenant',
metavar='<tenant>',
nargs='?',
help=_('Display information from single tenant (Admin only).'))
@utils.arg('--reserved',
dest='reserved',
action='store_true',
default=False,
help=_('Include reservations count.'))
def do_absolute_limits(cs, args):
"""Print a list of absolute limits for a user"""
limits = cs.limits.get(args.reserved, args.tenant).absolute
columns = ['Name', 'Value']
utils.print_list(limits, columns)
def do_rate_limits(cs, args):
"""Print a list of rate limits for a user"""
limits = cs.limits.get().rate
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
utils.print_list(limits, columns)
@utils.arg('--start', metavar='<start>',
help=_('Usage range start date ex 2012-01-20 (default: 4 weeks '
'ago)'),
default=None)
@utils.arg('--end', metavar='<end>',
help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'),
default=None)
def do_usage_list(cs, args):
"""List usage data for all tenants."""
dateformat = "%Y-%m-%d"
rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours",
"Disk GB-Hours"]
now = timeutils.utcnow()
if args.start:
start = datetime.datetime.strptime(args.start, dateformat)
else:
start = now - datetime.timedelta(weeks=4)
if args.end:
end = datetime.datetime.strptime(args.end, dateformat)
else:
end = now + datetime.timedelta(days=1)
def simplify_usage(u):
simplerows = [x.lower().replace(" ", "_") for x in rows]
setattr(u, simplerows[0], u.tenant_id)
setattr(u, simplerows[1], "%d" % len(u.server_usages))
setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage)
setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage)
setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage)
usage_list = cs.usage.list(start, end, detailed=True)
print(_("Usage from %(start)s to %(end)s:") %
{'start': start.strftime(dateformat),
'end': end.strftime(dateformat)})
for usage in usage_list:
simplify_usage(usage)
utils.print_list(usage_list, rows)
@utils.arg('--start', metavar='<start>',
help=_('Usage range start date ex 2012-01-20 (default: 4 weeks '
'ago)'),
default=None)
@utils.arg('--end', metavar='<end>',
help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'),
default=None)
@utils.arg('--tenant', metavar='<tenant-id>',
default=None,
help=_('UUID or name of tenant to get usage for.'))
def do_usage(cs, args):
"""Show usage data for a single tenant."""
dateformat = "%Y-%m-%d"
rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"]
now = timeutils.utcnow()
if args.start:
start = datetime.datetime.strptime(args.start, dateformat)
else:
start = now - datetime.timedelta(weeks=4)
if args.end:
end = datetime.datetime.strptime(args.end, dateformat)
else:
end = now + datetime.timedelta(days=1)
def simplify_usage(u):
simplerows = [x.lower().replace(" ", "_") for x in rows]
setattr(u, simplerows[0], "%d" % len(u.server_usages))
setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage)
setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage)
setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage)
if args.tenant:
usage = cs.usage.get(args.tenant, start, end)
else:
usage = cs.usage.get(cs.client.tenant_id, start, end)
print(_("Usage from %(start)s to %(end)s:") %
{'start': start.strftime(dateformat),
'end': end.strftime(dateformat)})
if getattr(usage, 'total_vcpus_usage', None):
simplify_usage(usage)
utils.print_list([usage], rows)
else:
print(_('None'))
@utils.arg('pk_filename',
metavar='<private-key-filename>',
nargs='?',
default='pk.pem',
help=_('Filename for the private key [Default: pk.pem]'))
@utils.arg('cert_filename',
metavar='<x509-cert-filename>',
nargs='?',
default='cert.pem',
help=_('Filename for the X.509 certificate [Default: cert.pem]'))
def do_x509_create_cert(cs, args):
"""Create x509 cert for a user in tenant."""
if os.path.exists(args.pk_filename):
raise exceptions.CommandError(_("Unable to write privatekey - %s "
"exists.") % args.pk_filename)
if os.path.exists(args.cert_filename):
raise exceptions.CommandError(_("Unable to write x509 cert - %s "
"exists.") % args.cert_filename)
certs = cs.certs.create()
try:
old_umask = os.umask(0o377)
with open(args.pk_filename, 'w') as private_key:
private_key.write(certs.private_key)
print(_("Wrote private key to %s") % args.pk_filename)
finally:
os.umask(old_umask)
with open(args.cert_filename, 'w') as cert:
cert.write(certs.data)
print(_("Wrote x509 certificate to %s") % args.cert_filename)
@utils.arg('filename',
metavar='<filename>',
nargs='?',
default='cacert.pem',
help=_('Filename to write the x509 root cert.'))
def do_x509_get_root_cert(cs, args):
"""Fetch the x509 root cert."""
if os.path.exists(args.filename):
raise exceptions.CommandError(_("Unable to write x509 root cert - \
%s exists.") % args.filename)
with open(args.filename, 'w') as cert:
cacert = cs.certs.get()
cert.write(cacert.data)
print(_("Wrote x509 root cert to %s") % args.filename)
@utils.arg('--hypervisor', metavar='<hypervisor>', default=None,
help=_('type of hypervisor.'))
def do_agent_list(cs, args):
"""List all builds."""
result = cs.agents.list(args.hypervisor)
columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version",
'Md5hash', 'Url']
utils.print_list(result, columns)
@utils.arg('os', metavar='<os>', help=_('type of os.'))
@utils.arg('architecture', metavar='<architecture>',
help=_('type of architecture'))
@utils.arg('version', metavar='<version>', help=_('version'))
@utils.arg('url', metavar='<url>', help=_('url'))
@utils.arg('md5hash', metavar='<md5hash>', help=_('md5 hash'))
@utils.arg('hypervisor', metavar='<hypervisor>', default='xen',
help=_('type of hypervisor.'))
def do_agent_create(cs, args):
"""Create new agent build."""
result = cs.agents.create(args.os, args.architecture,
args.version, args.url,
args.md5hash, args.hypervisor)
utils.print_dict(result._info.copy())
@utils.arg('id', metavar='<id>', help=_('id of the agent-build'))
def do_agent_delete(cs, args):
"""Delete existing agent build."""
cs.agents.delete(args.id)
@utils.arg('id', metavar='<id>', help=_('id of the agent-build'))
@utils.arg('version', metavar='<version>', help=_('version'))
@utils.arg('url', metavar='<url>', help=_('url'))
@utils.arg('md5hash', metavar='<md5hash>', help=_('md5hash'))
def do_agent_modify(cs, args):
"""Modify existing agent build."""
result = cs.agents.update(args.id, args.version,
args.url, args.md5hash)
utils.print_dict(result._info)
def _find_aggregate(cs, aggregate):
"""Get a aggregate by name or ID."""
return utils.find_resource(cs.aggregates, aggregate)
def do_aggregate_list(cs, args):
"""Print a list of all aggregates."""
aggregates = cs.aggregates.list()
columns = ['Id', 'Name', 'Availability Zone']
utils.print_list(aggregates, columns)
@utils.arg('name', metavar='<name>', help=_('Name of aggregate.'))
@utils.arg('availability_zone',
metavar='<availability-zone>',
default=None,
nargs='?',
help=_('The availability zone of the aggregate (optional).'))
def do_aggregate_create(cs, args):
"""Create a new aggregate with the specified details."""
aggregate = cs.aggregates.create(args.name, args.availability_zone)
_print_aggregate_details(aggregate)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate to delete.'))
def do_aggregate_delete(cs, args):
"""Delete the aggregate."""
aggregate = _find_aggregate(cs, args.aggregate)
cs.aggregates.delete(aggregate)
print(_("Aggregate %s has been successfully deleted.") % aggregate.id)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate to update.'))
@utils.arg('name', metavar='<name>', help=_('Name of aggregate.'))
@utils.arg('availability_zone',
metavar='<availability-zone>',
nargs='?',
default=None,
help=_('The availability zone of the aggregate.'))
def do_aggregate_update(cs, args):
"""Update the aggregate's name and optionally availability zone."""
aggregate = _find_aggregate(cs, args.aggregate)
updates = {"name": args.name}
if args.availability_zone:
updates["availability_zone"] = args.availability_zone
aggregate = cs.aggregates.update(aggregate.id, updates)
print(_("Aggregate %s has been successfully updated.") % aggregate.id)
_print_aggregate_details(aggregate)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate to update.'))
@utils.arg('metadata',
metavar='<key=value>',
nargs='+',
action='append',
default=[],
help=_('Metadata to add/update to aggregate. '
'Specify only the key to delete a metadata item.'))
def do_aggregate_set_metadata(cs, args):
"""Update the metadata associated with the aggregate."""
aggregate = _find_aggregate(cs, args.aggregate)
metadata = _extract_metadata(args)
aggregate = cs.aggregates.set_metadata(aggregate.id, metadata)
print(_("Metadata has been successfully updated for aggregate %s.") %
aggregate.id)
_print_aggregate_details(aggregate)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate.'))
@utils.arg('host', metavar='<host>',
help=_('The host to add to the aggregate.'))
def do_aggregate_add_host(cs, args):
"""Add the host to the specified aggregate."""
aggregate = _find_aggregate(cs, args.aggregate)
aggregate = cs.aggregates.add_host(aggregate.id, args.host)
print(_("Host %(host)s has been successfully added for aggregate "
"%(aggregate_id)s ") % {'host': args.host,
'aggregate_id': aggregate.id})
_print_aggregate_details(aggregate)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate.'))
@utils.arg('host', metavar='<host>',
help=_('The host to remove from the aggregate.'))
def do_aggregate_remove_host(cs, args):
"""Remove the specified host from the specified aggregate."""
aggregate = _find_aggregate(cs, args.aggregate)
aggregate = cs.aggregates.remove_host(aggregate.id, args.host)
print(_("Host %(host)s has been successfully removed from aggregate "
"%(aggregate_id)s ") % {'host': args.host,
'aggregate_id': aggregate.id})
_print_aggregate_details(aggregate)
@utils.arg('aggregate', metavar='<aggregate>',
help=_('Name or ID of aggregate.'))
def do_aggregate_details(cs, args):
"""Show details of the specified aggregate."""
aggregate = _find_aggregate(cs, args.aggregate)
_print_aggregate_details(aggregate)
def _print_aggregate_details(aggregate):
columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata']
def parser_metadata(fields):
return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {})
def parser_hosts(fields):
return utils.pretty_choice_list(getattr(fields, 'hosts', []))
formatters = {
'Metadata': parser_metadata,
'Hosts': parser_hosts,
}
utils.print_list([aggregate], columns, formatters=formatters)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('host', metavar='<host>', default=None, nargs='?',
help=_('destination host name.'))
@utils.arg('--block-migrate',
action='store_true',
dest='block_migrate',
default=False,
help=_('True in case of block_migration. (Default=False:live_migration)'))
@utils.arg('--block_migrate',
action='store_true',
help=argparse.SUPPRESS)
@utils.arg('--disk-over-commit',
action='store_true',
dest='disk_over_commit',
default=False,
help=_('Allow overcommit.(Default=False)'))
@utils.arg('--disk_over_commit',
action='store_true',
help=argparse.SUPPRESS)
def do_live_migration(cs, args):
"""Migrate running server to a new machine."""
_find_server(cs, args.server).live_migrate(args.host,
args.block_migrate,
args.disk_over_commit)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--active', action='store_const', dest='state',
default='error', const='active',
help=_('Request the server be reset to "active" state instead '
'of "error" state (the default).'))
def do_reset_state(cs, args):
"""Reset the state of a server."""
_find_server(cs, args.server).reset_state(args.state)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_reset_network(cs, args):
"""Reset network of a server."""
_find_server(cs, args.server).reset_network()
@utils.arg('--host', metavar='<hostname>', default=None,
help=_('Name of host.'))
@utils.arg('--binary', metavar='<binary>', default=None,
help=_('Service binary.'))
def do_service_list(cs, args):
"""Show a list of all running services. Filter by host & binary."""
result = cs.services.list(host=args.host, binary=args.binary)
columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"]
# NOTE(sulo): we check if the response has disabled_reason
# so as not to add the column when the extended ext is not enabled.
if result and hasattr(result[0], 'disabled_reason'):
columns.append("Disabled Reason")
# NOTE(gtt): After https://review.openstack.org/#/c/39998/ nova will
# show id in response.
if result and hasattr(result[0], 'id'):
columns.insert(0, "Id")
utils.print_list(result, columns)
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
@utils.arg('binary', metavar='<binary>', help=_('Service binary.'))
def do_service_enable(cs, args):
"""Enable the service."""
result = cs.services.enable(args.host, args.binary)
utils.print_list([result], ['Host', 'Binary', 'Status'])
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
@utils.arg('binary', metavar='<binary>', help=_('Service binary.'))
@utils.arg('--reason', metavar='<reason>',
help=_('Reason for disabling service.'))
def do_service_disable(cs, args):
"""Disable the service."""
if args.reason:
result = cs.services.disable_log_reason(args.host, args.binary,
args.reason)
utils.print_list([result], ['Host', 'Binary', 'Status',
'Disabled Reason'])
else:
result = cs.services.disable(args.host, args.binary)
utils.print_list([result], ['Host', 'Binary', 'Status'])
@utils.arg('id', metavar='<id>', help=_('Id of service.'))
def do_service_delete(cs, args):
"""Delete the service."""
cs.services.delete(args.id)
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
def do_fixed_ip_get(cs, args):
"""Retrieve info on a fixed ip."""
result = cs.fixed_ips.get(args.fixed_ip)
utils.print_list([result], ['address', 'cidr', 'hostname', 'host'])
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
def do_fixed_ip_reserve(cs, args):
"""Reserve a fixed IP."""
cs.fixed_ips.reserve(args.fixed_ip)
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
def do_fixed_ip_unreserve(cs, args):
"""Unreserve a fixed IP."""
cs.fixed_ips.unreserve(args.fixed_ip)
@utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
def do_host_describe(cs, args):
"""Describe a specific host."""
result = cs.hosts.get(args.host)
columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"]
utils.print_list(result, columns)
@utils.arg('--zone', metavar='<zone>', default=None,
help=_('Filters the list, returning only those '
'hosts in the availability zone <zone>.'))
def do_host_list(cs, args):
"""List all hosts by service."""
columns = ["host_name", "service", "zone"]
result = cs.hosts.list(args.zone)
utils.print_list(result, columns)
@utils.arg('host', metavar='<hostname>', help='Name of host.')
@utils.arg('--status', metavar='<enable|disable>', default=None, dest='status',
help=_('Either enable or disable a host.'))
@utils.arg('--maintenance',
metavar='<enable|disable>',
default=None,
dest='maintenance',
help=_('Either put or resume host to/from maintenance.'))
def do_host_update(cs, args):
"""Update host settings."""
updates = {}
columns = ["HOST"]
if args.status:
updates['status'] = args.status
columns.append("status")
if args.maintenance:
updates['maintenance_mode'] = args.maintenance
columns.append("maintenance_mode")
result = cs.hosts.update(args.host, updates)
utils.print_list([result], columns)
@utils.arg('host', metavar='<hostname>', help='Name of host.')
@utils.arg('--action', metavar='<action>', dest='action',
choices=['startup', 'shutdown', 'reboot'],
help=_('A power action: startup, reboot, or shutdown.'))
def do_host_action(cs, args):
"""Perform a power action on a host."""
result = cs.hosts.host_action(args.host, args.action)
utils.print_list([result], ['HOST', 'power_action'])
def _find_hypervisor(cs, hypervisor):
"""Get a hypervisor by name or ID."""
return utils.find_resource(cs.hypervisors, hypervisor)
@utils.arg('--matching', metavar='<hostname>', default=None,
help=_('List hypervisors matching the given <hostname>.'))
def do_hypervisor_list(cs, args):
"""List hypervisors."""
columns = ['ID', 'Hypervisor hostname']
if args.matching:
utils.print_list(cs.hypervisors.search(args.matching), columns)
else:
# Since we're not outputting detail data, choose
# detailed=False for server-side efficiency
utils.print_list(cs.hypervisors.list(False), columns)
@utils.arg('hostname', metavar='<hostname>',
help=_('The hypervisor hostname (or pattern) to search for.'))
def do_hypervisor_servers(cs, args):
"""List servers belonging to specific hypervisors."""
hypers = cs.hypervisors.search(args.hostname, servers=True)
class InstanceOnHyper(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# Massage the result into a list to be displayed
instances = []
for hyper in hypers:
hyper_host = hyper.hypervisor_hostname
hyper_id = hyper.id
if hasattr(hyper, 'servers'):
instances.extend([InstanceOnHyper(id=serv['uuid'],
name=serv['name'],
hypervisor_hostname=hyper_host,
hypervisor_id=hyper_id)
for serv in hyper.servers])
# Output the data
utils.print_list(instances, ['ID', 'Name', 'Hypervisor ID',
'Hypervisor Hostname'])
@utils.arg('hypervisor',
metavar='<hypervisor>',
help=_('Name or ID of the hypervisor to show the details of.'))
def do_hypervisor_show(cs, args):
"""Display the details of the specified hypervisor."""
hyper = _find_hypervisor(cs, args.hypervisor)
utils.print_dict(utils.flatten_dict(hyper._info))
@utils.arg('hypervisor',
metavar='<hypervisor>',
help=_('Name or ID of the hypervisor to show the uptime of.'))
def do_hypervisor_uptime(cs, args):
"""Display the uptime of the specified hypervisor."""
hyper = _find_hypervisor(cs, args.hypervisor)
hyper = cs.hypervisors.uptime(hyper)
# Output the uptime information
utils.print_dict(hyper._info.copy())
def do_hypervisor_stats(cs, args):
"""Get hypervisor statistics over all compute nodes."""
stats = cs.hypervisors.statistics()
utils.print_dict(stats._info.copy())
def ensure_service_catalog_present(cs):
if not hasattr(cs.client, 'service_catalog'):
# Turn off token caching and re-auth
cs.client.unauthenticate()
cs.client.use_token_cache(False)
cs.client.authenticate()
def do_endpoints(cs, _args):
"""Discover endpoints that get returned from the authenticate services."""
ensure_service_catalog_present(cs)
catalog = cs.client.service_catalog.catalog
for e in catalog['access']['serviceCatalog']:
utils.print_dict(e['endpoints'][0], e['name'])
@utils.arg('--wrap', dest='wrap', metavar='<integer>', default=64,
help=_('wrap PKI tokens to a specified length, or 0 to disable'))
def do_credentials(cs, _args):
"""Show user credentials returned from auth."""
ensure_service_catalog_present(cs)
catalog = cs.client.service_catalog.catalog
utils.print_dict(catalog['access']['user'], "User Credentials",
wrap=int(_args.wrap))
utils.print_dict(catalog['access']['token'], "Token", wrap=int(_args.wrap))
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--port',
dest='port',
action='store',
type=int,
default=22,
help=_('Optional flag to indicate which port to use for ssh. '
'(Default=22)'))
@utils.arg('--private',
dest='private',
action='store_true',
default=False,
help=_('Optional flag to indicate whether to only use private address '
'attached to an instance. (Default=False). If no public address is '
'found try private address'))
@utils.arg('--ipv6',
dest='ipv6',
action='store_true',
default=False,
help=_('Optional flag to indicate whether to use an IPv6 address '
'attached to a server. (Defaults to IPv4 address)'))
@utils.arg('--login', metavar='<login>', help=_('Login to use.'),
default="root")
@utils.arg('-i', '--identity',
dest='identity',
help=_('Private key file, same as the -i option to the ssh command.'),
default='')
@utils.arg('--extra-opts',
dest='extra',
help=_('Extra options to pass to ssh. see: man ssh'),
default='')
def do_ssh(cs, args):
"""SSH into a server."""
if '@' in args.server:
user, server = args.server.split('@', 1)
args.login = user
args.server = server
addresses = _find_server(cs, args.server).addresses
address_type = "private" if args.private else "public"
version = 6 if args.ipv6 else 4
if (address_type == "public" and address_type not in addresses and
"private" in addresses):
address_type = "private"
if address_type not in addresses:
print(_("ERROR: No %(addr_type)s addresses found for '%(server)s'.") %
{'addr_type': address_type, 'server': args.server})
return
ip_address = None
for address in addresses[address_type]:
if address['version'] == version:
ip_address = address['addr']
break
identity = '-i %s' % args.identity if len(args.identity) else ''
if ip_address:
os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address,
args.extra))
else:
pretty_version = "IPv%d" % version
print(_("ERROR: No %(addr_type)s %(pretty_version)s address found.") %
{'addr_type': address_type, 'pretty_version': pretty_version})
return
_quota_resources = ['instances', 'cores', 'ram',
'floating_ips', 'fixed_ips', 'metadata_items',
'injected_files', 'injected_file_content_bytes',
'injected_file_path_bytes', 'key_pairs',
'security_groups', 'security_group_rules']
def _quota_show(quotas):
class FormattedQuota(object):
def __init__(self, key, value):
setattr(self, 'quota', key)
setattr(self, 'limit', value)
quota_list = []
for resource in _quota_resources:
try:
quota = FormattedQuota(resource, getattr(quotas, resource))
quota_list.append(quota)
except AttributeError:
pass
columns = ['Quota', 'Limit']
utils.print_list(quota_list, columns)
def _quota_update(manager, identifier, args):
updates = {}
for resource in _quota_resources:
val = getattr(args, resource, None)
if val is not None:
updates[resource] = val
if updates:
# default value of force is None to make sure this client
# will be compatibile with old nova server
force_update = getattr(args, 'force', None)
user_id = getattr(args, 'user', None)
if isinstance(manager, quotas.QuotaSetManager):
manager.update(identifier, force=force_update, user_id=user_id,
**updates)
else:
manager.update(identifier, **updates)
@utils.arg('--tenant',
metavar='<tenant-id>',
default=None,
help=_('ID of tenant to list the quotas for.'))
@utils.arg('--user',
metavar='<user-id>',
default=None,
help=_('ID of user to list the quotas for.'))
def do_quota_show(cs, args):
"""List the quotas for a tenant/user."""
if not args.tenant:
_quota_show(cs.quotas.get(cs.client.tenant_id, user_id=args.user))
else:
_quota_show(cs.quotas.get(args.tenant, user_id=args.user))
@utils.arg('--tenant',
metavar='<tenant-id>',
default=None,
help=_('ID of tenant to list the default quotas for.'))
def do_quota_defaults(cs, args):
"""List the default quotas for a tenant."""
if not args.tenant:
_quota_show(cs.quotas.defaults(cs.client.tenant_id))
else:
_quota_show(cs.quotas.defaults(args.tenant))
@utils.arg('tenant',
metavar='<tenant-id>',
help=_('ID of tenant to set the quotas for.'))
@utils.arg('--user',
metavar='<user-id>',
default=None,
help=_('ID of user to set the quotas for.'))
@utils.arg('--instances',
metavar='<instances>',
type=int, default=None,
help=_('New value for the "instances" quota.'))
@utils.arg('--cores',
metavar='<cores>',
type=int, default=None,
help=_('New value for the "cores" quota.'))
@utils.arg('--ram',
metavar='<ram>',
type=int, default=None,
help=_('New value for the "ram" quota.'))
@utils.arg('--floating-ips',
metavar='<floating-ips>',
type=int,
default=None,
help=_('New value for the "floating-ips" quota.'))
@utils.arg('--floating_ips',
type=int,
help=argparse.SUPPRESS)
@utils.arg('--fixed-ips',
metavar='<fixed-ips>',
type=int,
default=None,
help=_('New value for the "fixed-ips" quota.'))
@utils.arg('--metadata-items',
metavar='<metadata-items>',
type=int,
default=None,
help=_('New value for the "metadata-items" quota.'))
@utils.arg('--metadata_items',
type=int,
help=argparse.SUPPRESS)
@utils.arg('--injected-files',
metavar='<injected-files>',
type=int,
default=None,
help=_('New value for the "injected-files" quota.'))
@utils.arg('--injected_files',
type=int,
help=argparse.SUPPRESS)
@utils.arg('--injected-file-content-bytes',
metavar='<injected-file-content-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-content-bytes" quota.'))
@utils.arg('--injected_file_content_bytes',
type=int,
help=argparse.SUPPRESS)
@utils.arg('--injected-file-path-bytes',
metavar='<injected-file-path-bytes>',
type=int,
default=None,
help=_('New value for the "injected-file-path-bytes" quota.'))
@utils.arg('--key-pairs',
metavar='<key-pairs>',
type=int,
default=None,
help=_('New value for the "key-pairs" quota.'))
@utils.arg('--security-groups',
metavar='<security-groups>',
type=int,
default=None,
help=_('New value for the "security-groups" quota.'))
@utils.arg('--security-group-rules',
metavar='<security-group-rules>',
type=int,
default=None,
help=_('New value for the "security-group-rules" quota.'))
@utils.arg('--force',
dest='force',
action="store_true",
default=None,
help=_('Whether force update the quota even if the already used'
' and reserved exceeds the new quota'))
def do_quota_update(cs, args):
"""Update the quotas for a tenant/user."""
_quota_update(cs.quotas, args.tenant, args)
@utils.arg('--tenant',
metavar='<tenant-id>',
help=_('ID of tenant to delete quota for.'))
@utils.arg('--user',
metavar='<user-id>',
help=_('ID of user to delete quota for.'))
def do_quota_delete(cs, args):
"""Delete quota for a tenant/user so their quota will Revert
back to default.
"""
cs.quotas.delete(args.tenant, user_id=args.user)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('host', metavar='<host>', help=_('Name or ID of target host.'))
@utils.arg('--password',
dest='password',
metavar='<password>',
default=None,
help=_("Set the provided password on the evacuated server. Not applicable "
"with on-shared-storage flag"))
@utils.arg('--on-shared-storage',
dest='on_shared_storage',
action="store_true",
default=False,
help=_('Specifies whether server files are located on shared storage'))
def do_evacuate(cs, args):
"""Evacuate server from failed host to specified one."""
server = _find_server(cs, args.server)
res = server.evacuate(args.host, args.on_shared_storage, args.password)[1]
if type(res) is dict:
utils.print_dict(res)
def _print_interfaces(interfaces):
columns = ['Port State', 'Port ID', 'Net ID', 'IP addresses',
'MAC Addr']
class FormattedInterface(object):
def __init__(self, interface):
for col in columns:
key = col.lower().replace(" ", "_")
if hasattr(interface, key):
setattr(self, key, getattr(interface, key))
self.ip_addresses = ",".join([fip['ip_address']
for fip in interface.fixed_ips])
utils.print_list([FormattedInterface(i) for i in interfaces], columns)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_interface_list(cs, args):
"""List interfaces attached to a server."""
server = _find_server(cs, args.server)
res = server.interface_list()
if type(res) is list:
_print_interfaces(res)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('--port-id', metavar='<port_id>', help=_('Port ID.'),
dest="port_id")
@utils.arg('--net-id', metavar='<net_id>', help=_('Network ID'),
default=None, dest="net_id")
@utils.arg('--fixed-ip', metavar='<fixed_ip>', help=_('Requested fixed IP.'),
default=None, dest="fixed_ip")
def do_interface_attach(cs, args):
"""Attach a network interface to a server."""
server = _find_server(cs, args.server)
res = server.interface_attach(args.port_id, args.net_id, args.fixed_ip)
if type(res) is dict:
utils.print_dict(res)
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('port_id', metavar='<port_id>', help=_('Port ID.'))
def do_interface_detach(cs, args):
"""Detach a network interface from a server."""
server = _find_server(cs, args.server)
res = server.interface_detach(args.port_id)
if type(res) is dict:
utils.print_dict(res)
def _treeizeAvailabilityZone(zone):
"""Build a tree view for availability zones."""
AvailabilityZone = availability_zones.AvailabilityZone
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
result = []
# Zone tree view item
az.zoneName = zone.zoneName
az.zoneState = ('available'
if zone.zoneState['available'] else 'not available')
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
if zone.hosts is not None:
zone_hosts = sorted(zone.hosts.items(), key=lambda x: x[0])
for (host, services) in zone_hosts:
# Host tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '|- %s' % host
az.zoneState = ''
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
for (svc, state) in services.items():
# Service tree view item
az = AvailabilityZone(zone.manager,
copy.deepcopy(zone._info), zone._loaded)
az.zoneName = '| |- %s' % svc
az.zoneState = '%s %s %s' % (
'enabled' if state['active'] else 'disabled',
':-)' if state['available'] else 'XXX',
state['updated_at'])
az._info['zoneName'] = az.zoneName
az._info['zoneState'] = az.zoneState
result.append(az)
return result
@utils.service_type('compute')
def do_availability_zone_list(cs, _args):
"""List all the availability zones."""
try:
availability_zones = cs.availability_zones.list()
except exceptions.Forbidden as e: # policy doesn't allow probably
try:
availability_zones = cs.availability_zones.list(detailed=False)
except Exception:
raise e
result = []
for zone in availability_zones:
result += _treeizeAvailabilityZone(zone)
_translate_availability_zone_keys(result)
utils.print_list(result, ['Name', 'Status'],
sortby_index=None)
def _print_server_group_details(server_group):
columns = ['Id', 'Name', 'Policies', 'Members', 'Metadata']
utils.print_list(server_group, columns)
def do_server_group_list(cs, args):
"""Print a list of all server groups."""
server_groups = cs.server_groups.list()
_print_server_group_details(server_groups)
@utils.arg('name', metavar='<name>', help='Server group name.')
@utils.arg('--policy', metavar='<policy>', action='append',
dest='policies', default=[], type=str,
help='Policies for the server groups')
def do_server_group_create(cs, args):
"""Create a new server group with the specified details."""
kwargs = {'name': args.name,
'policies': args.policies}
server_group = cs.server_groups.create(**kwargs)
_print_server_group_details([server_group])
@utils.arg('id', metavar='<id>',
help="Unique ID of the server group to delete")
def do_server_group_delete(cs, args):
"""Delete a specific server group."""
cs.server_groups.delete(args.id)
print("Instance group %s has been successfully deleted." % args.id)
@utils.arg('id', metavar='<id>',
help="Unique ID of the server group to get")
def do_server_group_get(cs, args):
"""Get a specific server group."""
server_group = cs.server_groups.get(args.id)
_print_server_group_details([server_group])