summaryrefslogtreecommitdiff
path: root/novaclient/v2/shell.py
diff options
context:
space:
mode:
Diffstat (limited to 'novaclient/v2/shell.py')
-rw-r--r--novaclient/v2/shell.py4447
1 files changed, 4447 insertions, 0 deletions
diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
new file mode 100644
index 0000000..190d07f
--- /dev/null
+++ b/novaclient/v2/shell.py
@@ -0,0 +1,4447 @@
1# Copyright 2010 Jacob Kaplan-Moss
2
3# Copyright 2011 OpenStack Foundation
4# Copyright 2013 IBM Corp.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19from __future__ import print_function
20
21import argparse
22import copy
23import datetime
24import getpass
25import locale
26import logging
27import os
28import sys
29import time
30
31from oslo.utils import encodeutils
32from oslo.utils import strutils
33from oslo.utils import timeutils
34import six
35
36from novaclient import client
37from novaclient import exceptions
38from novaclient.i18n import _
39from novaclient.openstack.common import cliutils
40from novaclient.openstack.common import uuidutils
41from novaclient import utils
42from novaclient.v2 import availability_zones
43from novaclient.v2 import quotas
44from novaclient.v2 import servers
45
46
47logger = logging.getLogger(__name__)
48
49
50CLIENT_BDM2_KEYS = {
51 'id': 'uuid',
52 'source': 'source_type',
53 'dest': 'destination_type',
54 'bus': 'disk_bus',
55 'device': 'device_name',
56 'size': 'volume_size',
57 'format': 'guest_format',
58 'bootindex': 'boot_index',
59 'type': 'device_type',
60 'shutdown': 'delete_on_termination',
61}
62
63
64def _key_value_pairing(text):
65 try:
66 (k, v) = text.split('=', 1)
67 return (k, v)
68 except ValueError:
69 msg = "%r is not in the format of key=value" % text
70 raise argparse.ArgumentTypeError(msg)
71
72
73def _match_image(cs, wanted_properties):
74 image_list = cs.images.list()
75 images_matched = []
76 match = set(wanted_properties)
77 for img in image_list:
78 try:
79 if match == match.intersection(set(img.metadata.items())):
80 images_matched.append(img)
81 except AttributeError:
82 pass
83 return images_matched
84
85
86def _parse_block_device_mapping_v2(args, image):
87 bdm = []
88
89 if args.boot_volume:
90 bdm_dict = {'uuid': args.boot_volume, 'source_type': 'volume',
91 'destination_type': 'volume', 'boot_index': 0,
92 'delete_on_termination': False}
93 bdm.append(bdm_dict)
94
95 if args.snapshot:
96 bdm_dict = {'uuid': args.snapshot, 'source_type': 'snapshot',
97 'destination_type': 'volume', 'boot_index': 0,
98 'delete_on_termination': False}
99 bdm.append(bdm_dict)
100
101 for device_spec in args.block_device:
102 spec_dict = dict(v.split('=') for v in device_spec.split(','))
103 bdm_dict = {}
104
105 for key, value in six.iteritems(spec_dict):
106 bdm_dict[CLIENT_BDM2_KEYS[key]] = value
107
108 # Convert the delete_on_termination to a boolean or set it to true by
109 # default for local block devices when not specified.
110 if 'delete_on_termination' in bdm_dict:
111 action = bdm_dict['delete_on_termination']
112 bdm_dict['delete_on_termination'] = (action == 'remove')
113 elif bdm_dict.get('destination_type') == 'local':
114 bdm_dict['delete_on_termination'] = True
115
116 bdm.append(bdm_dict)
117
118 for ephemeral_spec in args.ephemeral:
119 bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
120 'boot_index': -1, 'delete_on_termination': True}
121
122 eph_dict = dict(v.split('=') for v in ephemeral_spec.split(','))
123 if 'size' in eph_dict:
124 bdm_dict['volume_size'] = eph_dict['size']
125 if 'format' in eph_dict:
126 bdm_dict['guest_format'] = eph_dict['format']
127
128 bdm.append(bdm_dict)
129
130 if args.swap:
131 bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
132 'boot_index': -1, 'delete_on_termination': True,
133 'guest_format': 'swap', 'volume_size': args.swap}
134 bdm.append(bdm_dict)
135
136 return bdm
137
138
139def _boot(cs, args):
140 """Boot a new server."""
141 if args.image:
142 image = _find_image(cs, args.image)
143 else:
144 image = None
145
146 if not image and args.image_with:
147 images = _match_image(cs, args.image_with)
148 if images:
149 # TODO(harlowja): log a warning that we
150 # are selecting the first of many?
151 image = images[0]
152
153 if not args.flavor:
154 raise exceptions.CommandError(_("you need to specify a Flavor ID "))
155
156 min_count = 1
157 max_count = 1
158 # Don't let user mix num_instances and max_count/min_count.
159 if (args.num_instances is not None and
160 args.min_count is None and
161 args.max_count is None):
162 if args.num_instances < 1:
163 raise exceptions.CommandError(_("num_instances should be >= 1"))
164 max_count = args.num_instances
165 elif (args.num_instances is not None and
166 (args.min_count is not None or args.max_count is not None)):
167 raise exceptions.CommandError(_("Don't mix num-instances and "
168 "max/min-count"))
169 if args.min_count is not None:
170 if args.min_count < 1:
171 raise exceptions.CommandError(_("min_count should be >= 1"))
172 min_count = args.min_count
173 max_count = min_count
174 if args.max_count is not None:
175 if args.max_count < 1:
176 raise exceptions.CommandError(_("max_count should be >= 1"))
177 max_count = args.max_count
178 if (args.min_count is not None and
179 args.max_count is not None and
180 args.min_count > args.max_count):
181 raise exceptions.CommandError(_("min_count should be <= max_count"))
182
183 flavor = _find_flavor(cs, args.flavor)
184
185 meta = dict(v.split('=', 1) for v in args.meta)
186
187 files = {}
188 for f in args.files:
189 try:
190 dst, src = f.split('=', 1)
191 files[dst] = open(src)
192 except IOError as e:
193 raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") %
194 {'src': src, 'exc': e})
195 except ValueError as e:
196 raise exceptions.CommandError(_("Invalid file argument '%s'. "
197 "File arguments must be of the "
198 "form '--file "
199 "<dst-path=src-path>'") % f)
200
201 # use the os-keypair extension
202 key_name = None
203 if args.key_name is not None:
204 key_name = args.key_name
205
206 if args.user_data:
207 try:
208 userdata = open(args.user_data)
209 except IOError as e:
210 raise exceptions.CommandError(_("Can't open '%(user_data)s': "
211 "%(exc)s") %
212 {'user_data': args.user_data,
213 'exc': e})
214 else:
215 userdata = None
216
217 if args.availability_zone:
218 availability_zone = args.availability_zone
219 else:
220 availability_zone = None
221
222 if args.security_groups:
223 security_groups = args.security_groups.split(',')
224 else:
225 security_groups = None
226
227 block_device_mapping = {}
228 for bdm in args.block_device_mapping:
229 device_name, mapping = bdm.split('=', 1)
230 block_device_mapping[device_name] = mapping
231
232 block_device_mapping_v2 = _parse_block_device_mapping_v2(args, image)
233
234 n_boot_args = len(list(filter(
235 bool, (image, args.boot_volume, args.snapshot))))
236 have_bdm = block_device_mapping_v2 or block_device_mapping
237
238 # Fail if more than one boot devices are present
239 # or if there is no device to boot from.
240 if n_boot_args > 1 or n_boot_args == 0 and not have_bdm:
241 raise exceptions.CommandError(
242 _("you need to specify at least one source ID (Image, Snapshot, "
243 "or Volume), a block device mapping or provide a set of "
244 "properties to match against an image"))
245
246 if block_device_mapping and block_device_mapping_v2:
247 raise exceptions.CommandError(
248 _("you can't mix old block devices (--block-device-mapping) "
249 "with the new ones (--block-device, --boot-volume, --snapshot, "
250 "--ephemeral, --swap)"))
251
252 nics = []
253 for nic_str in args.nics:
254 err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
255 "the form --nic <net-id=net-uuid,v4-fixed-ip=ip-addr,"
256 "v6-fixed-ip=ip-addr,port-id=port-uuid>, with at minimum "
257 "net-id or port-id (but not both) specified.") % nic_str)
258 nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
259 "port-id": ""}
260
261 for kv_str in nic_str.split(","):
262 try:
263 k, v = kv_str.split("=", 1)
264 except ValueError as e:
265 raise exceptions.CommandError(err_msg)
266
267 if k in nic_info:
268 nic_info[k] = v
269 else:
270 raise exceptions.CommandError(err_msg)
271
272 if bool(nic_info['net-id']) == bool(nic_info['port-id']):
273 raise exceptions.CommandError(err_msg)
274
275 nics.append(nic_info)
276
277 hints = {}
278 if args.scheduler_hints:
279 for hint in args.scheduler_hints:
280 key, _sep, value = hint.partition('=')
281 # NOTE(vish): multiple copies of the same hint will
282 # result in a list of values
283 if key in hints:
284 if isinstance(hints[key], six.string_types):
285 hints[key] = [hints[key]]
286 hints[key] += [value]
287 else:
288 hints[key] = value
289 boot_args = [args.name, image, flavor]
290
291 if str(args.config_drive).lower() in ("true", "1"):
292 config_drive = True
293 elif str(args.config_drive).lower() in ("false", "0", "", "none"):
294 config_drive = None
295 else:
296 config_drive = args.config_drive
297
298 boot_kwargs = dict(
299 meta=meta,
300 files=files,
301 key_name=key_name,
302 min_count=min_count,
303 max_count=max_count,
304 userdata=userdata,
305 availability_zone=availability_zone,
306 security_groups=security_groups,
307 block_device_mapping=block_device_mapping,
308 block_device_mapping_v2=block_device_mapping_v2,
309 nics=nics,
310 scheduler_hints=hints,
311 config_drive=config_drive)
312
313 return boot_args, boot_kwargs
314
315
316@cliutils.arg(
317 '--flavor',
318 default=None,
319 metavar='<flavor>',
320 help=_("Name or ID of flavor (see 'nova flavor-list')."))
321@cliutils.arg(
322 '--image',
323 default=None,
324 metavar='<image>',
325 help=_("Name or ID of image (see 'nova image-list'). "))
326@cliutils.arg(
327 '--image-with',
328 default=[],
329 type=_key_value_pairing,
330 action='append',
331 metavar='<key=value>',
332 help=_("Image metadata property (see 'nova image-show'). "))
333@cliutils.arg(
334 '--boot-volume',
335 default=None,
336 metavar="<volume_id>",
337 help=_("Volume ID to boot from."))
338@cliutils.arg(
339 '--snapshot',
340 default=None,
341 metavar="<snapshot_id>",
342 help=_("Snapshot ID to boot from (will create a volume)."))
343@cliutils.arg(
344 '--num-instances',
345 default=None,
346 type=int,
347 metavar='<number>',
348 help=argparse.SUPPRESS)
349@cliutils.arg(
350 '--min-count',
351 default=None,
352 type=int,
353 metavar='<number>',
354 help=_("Boot at least <number> servers (limited by quota)."))
355@cliutils.arg(
356 '--max-count',
357 default=None,
358 type=int,
359 metavar='<number>',
360 help=_("Boot up to <number> servers (limited by quota)."))
361@cliutils.arg(
362 '--meta',
363 metavar="<key=value>",
364 action='append',
365 default=[],
366 help=_("Record arbitrary key/value metadata to /meta_data.json "
367 "on the metadata server. Can be specified multiple times."))
368@cliutils.arg(
369 '--file',
370 metavar="<dst-path=src-path>",
371 action='append',
372 dest='files',
373 default=[],
374 help=_("Store arbitrary files from <src-path> locally to <dst-path> "
375 "on the new server. You may store up to 5 files."))
376@cliutils.arg(
377 '--key-name',
378 default=os.environ.get('NOVACLIENT_DEFAULT_KEY_NAME'),
379 metavar='<key-name>',
380 help=_("Key name of keypair that should be created earlier with \
381 the command keypair-add"))
382@cliutils.arg(
383 '--key_name',
384 help=argparse.SUPPRESS)
385@cliutils.arg('name', metavar='<name>', help=_('Name for the new server'))
386@cliutils.arg(
387 '--user-data',
388 default=None,
389 metavar='<user-data>',
390 help=_("user data file to pass to be exposed by the metadata server."))
391@cliutils.arg(
392 '--user_data',
393 help=argparse.SUPPRESS)
394@cliutils.arg(
395 '--availability-zone',
396 default=None,
397 metavar='<availability-zone>',
398 help=_("The availability zone for server placement."))
399@cliutils.arg(
400 '--availability_zone',
401 help=argparse.SUPPRESS)
402@cliutils.arg(
403 '--security-groups',
404 default=None,
405 metavar='<security-groups>',
406 help=_("Comma separated list of security group names."))
407@cliutils.arg(
408 '--security_groups',
409 help=argparse.SUPPRESS)
410@cliutils.arg(
411 '--block-device-mapping',
412 metavar="<dev-name=mapping>",
413 action='append',
414 default=[],
415 help=_("Block device mapping in the format "
416 "<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>."))
417@cliutils.arg(
418 '--block_device_mapping',
419 action='append',
420 help=argparse.SUPPRESS)
421@cliutils.arg(
422 '--block-device',
423 metavar="key1=value1[,key2=value2...]",
424 action='append',
425 default=[],
426 help=_("Block device mapping with the keys: "
427 "id=UUID (image_id, snapshot_id or volume_id only if using source "
428 "image, snapshot or volume) "
429 "source=source type (image, snapshot, volume or blank), "
430 "dest=destination type of the block device (volume or local), "
431 "bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, "
432 "hypervisor driver chooses a suitable default, "
433 "honoured only if device type is supplied) "
434 "type=device type (e.g. disk, cdrom, ...; defaults to 'disk') "
435 "device=name of the device (e.g. vda, xda, ...; "
436 "if omitted, hypervisor driver chooses suitable device "
437 "depending on selected bus), "
438 "size=size of the block device in GB (if omitted, "
439 "hypervisor driver calculates size), "
440 "format=device will be formatted (e.g. swap, ntfs, ...; optional), "
441 "bootindex=integer used for ordering the boot disks "
442 "(for image backed instances it is equal to 0, "
443 "for others need to be specified) and "
444 "shutdown=shutdown behaviour (either preserve or remove, "
445 "for local destination set to remove)."))
446@cliutils.arg(
447 '--swap',
448 metavar="<swap_size>",
449 default=None,
450 help=_("Create and attach a local swap block device of <swap_size> MB."))
451@cliutils.arg(
452 '--ephemeral',
453 metavar="size=<size>[,format=<format>]",
454 action='append',
455 default=[],
456 help=_("Create and attach a local ephemeral block device of <size> GB "
457 "and format it to <format>."))
458@cliutils.arg(
459 '--hint',
460 action='append',
461 dest='scheduler_hints',
462 default=[],
463 metavar='<key=value>',
464 help=_("Send arbitrary key/value pairs to the scheduler for custom "
465 "use."))
466@cliutils.arg(
467 '--nic',
468 metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
469 "port-id=port-uuid>",
470 action='append',
471 dest='nics',
472 default=[],
473 help=_("Create a NIC on the server. "
474 "Specify option multiple times to create multiple NICs. "
475 "net-id: attach NIC to network with this UUID "
476 "(either port-id or net-id must be provided), "
477 "v4-fixed-ip: IPv4 fixed address for NIC (optional), "
478 "v6-fixed-ip: IPv6 fixed address for NIC (optional), "
479 "port-id: attach NIC to port with this UUID "
480 "(either port-id or net-id must be provided)."))
481@cliutils.arg(
482 '--config-drive',
483 metavar="<value>",
484 dest='config_drive',
485 default=False,
486 help=_("Enable config drive"))
487@cliutils.arg(
488 '--poll',
489 dest='poll',
490 action="store_true",
491 default=False,
492 help=_('Report the new server boot progress until it completes.'))
493def do_boot(cs, args):
494 """Boot a new server."""
495 boot_args, boot_kwargs = _boot(cs, args)
496
497 extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args)
498 boot_kwargs.update(extra_boot_kwargs)
499
500 server = cs.servers.create(*boot_args, **boot_kwargs)
501 _print_server(cs, args, server)
502
503 if args.poll:
504 _poll_for_status(cs.servers.get, server.id, 'building', ['active'])
505
506
507def do_cloudpipe_list(cs, _args):
508 """Print a list of all cloudpipe instances."""
509 cloudpipes = cs.cloudpipe.list()
510 columns = ['Project Id', "Public IP", "Public Port", "Internal IP"]
511 utils.print_list(cloudpipes, columns)
512
513
514@cliutils.arg(
515 'project',
516 metavar='<project_id>',
517 help=_('UUID of the project to create the cloudpipe for.'))
518def do_cloudpipe_create(cs, args):
519 """Create a cloudpipe instance for the given project."""
520 cs.cloudpipe.create(args.project)
521
522
523@cliutils.arg('address', metavar='<ip address>', help=_('New IP Address.'))
524@cliutils.arg('port', metavar='<port>', help='New Port.')
525def do_cloudpipe_configure(cs, args):
526 """Update the VPN IP/port of a cloudpipe instance."""
527 cs.cloudpipe.update(args.address, args.port)
528
529
530def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
531 poll_period=5, show_progress=True,
532 status_field="status", silent=False):
533 """Block while an action is being performed, periodically printing
534 progress.
535 """
536 def print_progress(progress):
537 if show_progress:
538 msg = (_('\rServer %(action)s... %(progress)s%% complete')
539 % dict(action=action, progress=progress))
540 else:
541 msg = _('\rServer %(action)s...') % dict(action=action)
542
543 sys.stdout.write(msg)
544 sys.stdout.flush()
545
546 if not silent:
547 print()
548
549 while True:
550 obj = poll_fn(obj_id)
551
552 status = getattr(obj, status_field)
553
554 if status:
555 status = status.lower()
556
557 progress = getattr(obj, 'progress', None) or 0
558 if status in final_ok_states:
559 if not silent:
560 print_progress(100)
561 print(_("\nFinished"))
562 break
563 elif status == "error":
564 if not silent:
565 print(_("\nError %s server") % action)
566 raise exceptions.InstanceInErrorState(obj.fault['message'])
567
568 if not silent:
569 print_progress(progress)
570
571 time.sleep(poll_period)
572
573
574def _translate_keys(collection, convert):
575 for item in collection:
576 keys = item.__dict__.keys()
577 for from_key, to_key in convert:
578 if from_key in keys and to_key not in keys:
579 setattr(item, to_key, item._info[from_key])
580
581
582def _translate_extended_states(collection):
583 power_states = [
584 'NOSTATE', # 0x00
585 'Running', # 0x01
586 '', # 0x02
587 'Paused', # 0x03
588 'Shutdown', # 0x04
589 '', # 0x05
590 'Crashed', # 0x06
591 'Suspended' # 0x07
592 ]
593
594 for item in collection:
595 try:
596 setattr(item, 'power_state',
597 power_states[getattr(item, 'power_state')])
598 except AttributeError:
599 setattr(item, 'power_state', "N/A")
600 try:
601 getattr(item, 'task_state')
602 except AttributeError:
603 setattr(item, 'task_state', "N/A")
604
605
606def _translate_flavor_keys(collection):
607 _translate_keys(collection, [('ram', 'memory_mb')])
608
609
610def _print_flavor_extra_specs(flavor):
611 try:
612 return flavor.get_keys()
613 except exceptions.NotFound:
614 return "N/A"
615
616
617def _print_flavor_list(flavors, show_extra_specs=False):
618 _translate_flavor_keys(flavors)
619
620 headers = [
621 'ID',
622 'Name',
623 'Memory_MB',
624 'Disk',
625 'Ephemeral',
626 'Swap',
627 'VCPUs',
628 'RXTX_Factor',
629 'Is_Public',
630 ]
631
632 if show_extra_specs:
633 formatters = {'extra_specs': _print_flavor_extra_specs}
634 headers.append('extra_specs')
635 else:
636 formatters = {}
637
638 utils.print_list(flavors, headers, formatters)
639
640
641@cliutils.arg(
642 '--extra-specs',
643 dest='extra_specs',
644 action='store_true',
645 default=False,
646 help=_('Get extra-specs of each flavor.'))
647@cliutils.arg(
648 '--all',
649 dest='all',
650 action='store_true',
651 default=False,
652 help=_('Display all flavors (Admin only).'))
653def do_flavor_list(cs, args):
654 """Print a list of available 'flavors' (sizes of servers)."""
655 if args.all:
656 flavors = cs.flavors.list(is_public=None)
657 else:
658 flavors = cs.flavors.list()
659 _print_flavor_list(flavors, args.extra_specs)
660
661
662@cliutils.arg(
663 'flavor',
664 metavar='<flavor>',
665 help=_("Name or ID of the flavor to delete"))
666def do_flavor_delete(cs, args):
667 """Delete a specific flavor"""
668 flavorid = _find_flavor(cs, args.flavor)
669 cs.flavors.delete(flavorid)
670 _print_flavor_list([flavorid])
671
672
673@cliutils.arg(
674 'flavor',
675 metavar='<flavor>',
676 help=_("Name or ID of flavor"))
677def do_flavor_show(cs, args):
678 """Show details about the given flavor."""
679 flavor = _find_flavor(cs, args.flavor)
680 _print_flavor(flavor)
681
682
683@cliutils.arg(
684 'name',
685 metavar='<name>',
686 help=_("Name of the new flavor"))
687@cliutils.arg(
688 'id',
689 metavar='<id>',
690 help=_("Unique ID (integer or UUID) for the new flavor."
691 " If specifying 'auto', a UUID will be generated as id"))
692@cliutils.arg(
693 'ram',
694 metavar='<ram>',
695 help=_("Memory size in MB"))
696@cliutils.arg(
697 'disk',
698 metavar='<disk>',
699 help=_("Disk size in GB"))
700@cliutils.arg(
701 '--ephemeral',
702 metavar='<ephemeral>',
703 help=_("Ephemeral space size in GB (default 0)"),
704 default=0)
705@cliutils.arg(
706 'vcpus',
707 metavar='<vcpus>',
708 help=_("Number of vcpus"))
709@cliutils.arg(
710 '--swap',
711 metavar='<swap>',
712 help=_("Swap space size in MB (default 0)"),
713 default=0)
714@cliutils.arg(
715 '--rxtx-factor',
716 metavar='<factor>',
717 help=_("RX/TX factor (default 1)"),
718 default=1.0)
719@cliutils.arg(
720 '--is-public',
721 metavar='<is-public>',
722 help=_("Make flavor accessible to the public (default true)"),
723 type=lambda v: strutils.bool_from_string(v, True),
724 default=True)
725def do_flavor_create(cs, args):
726 """Create a new flavor"""
727 f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id,
728 args.ephemeral, args.swap, args.rxtx_factor,
729 args.is_public)
730 _print_flavor_list([f])
731
732
733@cliutils.arg(
734 'flavor',
735 metavar='<flavor>',
736 help=_("Name or ID of flavor"))
737@cliutils.arg(
738 'action',
739 metavar='<action>',
740 choices=['set', 'unset'],
741 help=_("Actions: 'set' or 'unset'"))
742@cliutils.arg(
743 'metadata',
744 metavar='<key=value>',
745 nargs='+',
746 action='append',
747 default=[],
748 help=_('Extra_specs to set/unset (only key is necessary on unset)'))
749def do_flavor_key(cs, args):
750 """Set or unset extra_spec for a flavor."""
751 flavor = _find_flavor(cs, args.flavor)
752 keypair = _extract_metadata(args)
753
754 if args.action == 'set':
755 flavor.set_keys(keypair)
756 elif args.action == 'unset':
757 flavor.unset_keys(keypair.keys())
758
759
760@cliutils.arg(
761 '--flavor',
762 metavar='<flavor>',
763 help=_("Filter results by flavor name or ID."))
764@cliutils.arg(
765 '--tenant', metavar='<tenant_id>',
766 help=_('Filter results by tenant ID.'))
767def do_flavor_access_list(cs, args):
768 """Print access information about the given flavor."""
769 if args.flavor and args.tenant:
770 raise exceptions.CommandError(_("Unable to filter results by "
771 "both --flavor and --tenant."))
772 elif args.flavor:
773 flavor = _find_flavor(cs, args.flavor)
774 if flavor.is_public:
775 raise exceptions.CommandError(_("Failed to get access list "
776 "for public flavor type."))
777 kwargs = {'flavor': flavor}
778 elif args.tenant:
779 kwargs = {'tenant': args.tenant}
780 else:
781 raise exceptions.CommandError(_("Unable to get all access lists. "
782 "Specify --flavor or --tenant"))
783
784 try:
785 access_list = cs.flavor_access.list(**kwargs)
786 except NotImplementedError as e:
787 raise exceptions.CommandError("%s" % str(e))
788
789 columns = ['Flavor_ID', 'Tenant_ID']
790 utils.print_list(access_list, columns)
791
792
793@cliutils.arg(
794 'flavor',
795 metavar='<flavor>',
796 help=_("Flavor name or ID to add access for the given tenant."))
797@cliutils.arg(
798 'tenant', metavar='<tenant_id>',
799 help=_('Tenant ID to add flavor access for.'))
800def do_flavor_access_add(cs, args):
801 """Add flavor access for the given tenant."""
802 flavor = _find_flavor(cs, args.flavor)
803 access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant)
804 columns = ['Flavor_ID', 'Tenant_ID']
805 utils.print_list(access_list, columns)
806
807
808@cliutils.arg(
809 'flavor',
810 metavar='<flavor>',
811 help=_("Flavor name or ID to remove access for the given tenant."))
812@cliutils.arg(
813 'tenant', metavar='<tenant_id>',
814 help=_('Tenant ID to remove flavor access for.'))
815def do_flavor_access_remove(cs, args):
816 """Remove flavor access for the given tenant."""
817 flavor = _find_flavor(cs, args.flavor)
818 access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant)
819 columns = ['Flavor_ID', 'Tenant_ID']
820 utils.print_list(access_list, columns)
821
822
823@cliutils.arg(
824 'project_id', metavar='<project_id>',
825 help=_('The ID of the project.'))
826def do_scrub(cs, args):
827 """Delete networks and security groups associated with a project."""
828 networks_list = cs.networks.list()
829 networks_list = [network for network in networks_list
830 if getattr(network, 'project_id', '') == args.project_id]
831 search_opts = {'all_tenants': 1}
832 groups = cs.security_groups.list(search_opts)
833 groups = [group for group in groups
834 if group.tenant_id == args.project_id]
835 for network in networks_list:
836 cs.networks.disassociate(network)
837 for group in groups:
838 cs.security_groups.delete(group)
839
840
841@cliutils.arg(
842 '--fields',
843 default=None,
844 metavar='<fields>',
845 help='Comma-separated list of fields to display. '
846 'Use the show command to see which fields are available.')
847def do_network_list(cs, args):
848 """Print a list of available networks."""
849 network_list = cs.networks.list()
850 columns = ['ID', 'Label', 'Cidr']
851
852 formatters = {}
853 field_titles = []
854 if args.fields:
855 for field in args.fields.split(','):
856 field_title, formatter = utils._make_field_formatter(field, {})
857 field_titles.append(field_title)
858 formatters[field_title] = formatter
859
860 columns = columns + field_titles
861 utils.print_list(network_list, columns)
862
863
864@cliutils.arg(
865 'network',
866 metavar='<network>',
867 help=_("uuid or label of network"))
868def do_network_show(cs, args):
869 """Show details about the given network."""
870 network = utils.find_resource(cs.networks, args.network)
871 utils.print_dict(network._info)
872
873
874@cliutils.arg(
875 'network',
876 metavar='<network>',
877 help=_("uuid or label of network"))
878def do_network_delete(cs, args):
879 """Delete network by label or id."""
880 network = utils.find_resource(cs.networks, args.network)
881 network.delete()
882
883
884@cliutils.arg(
885 '--host-only',
886 dest='host_only',
887 metavar='<0|1>',
888 nargs='?',
889 type=int,
890 const=1,
891 default=0)
892@cliutils.arg(
893 '--project-only',
894 dest='project_only',
895 metavar='<0|1>',
896 nargs='?',
897 type=int,
898 const=1,
899 default=0)
900@cliutils.arg(
901 'network',
902 metavar='<network>',
903 help="uuid of network")
904def do_network_disassociate(cs, args):
905 """Disassociate host and/or project from the given network."""
906 if args.host_only:
907 cs.networks.disassociate(args.network, True, False)
908 elif args.project_only:
909 cs.networks.disassociate(args.network, False, True)
910 else:
911 cs.networks.disassociate(args.network, True, True)
912
913
914@cliutils.arg(
915 'network',
916 metavar='<network>',
917 help="uuid of network")
918@cliutils.arg(
919 'host',
920 metavar='<host>',
921 help="Name of host")
922def do_network_associate_host(cs, args):
923 """Associate host with network."""
924 cs.networks.associate_host(args.network, args.host)
925
926
927@cliutils.arg(
928 'network',
929 metavar='<network>',
930 help="uuid of network")
931def do_network_associate_project(cs, args):
932 """Associate project with network."""
933 cs.networks.associate_project(args.network)
934
935
936def _filter_network_create_options(args):
937 valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6',
938 'gateway', 'gateway_v6', 'bridge', 'bridge_interface',
939 'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr',
940 'project_id', 'priority', 'vlan', 'mtu', 'dhcp_server',
941 'allowed_start', 'allowed_end']
942 kwargs = {}
943 for k, v in args.__dict__.items():
944 if k in valid_args and v is not None:
945 kwargs[k] = v
946
947 return kwargs
948
949
950@cliutils.arg(
951 'label',
952 metavar='<network_label>',
953 help=_("Label for network"))
954@cliutils.arg(
955 '--fixed-range-v4',
956 dest='cidr',
957 metavar='<x.x.x.x/yy>',
958 help=_("IPv4 subnet (ex: 10.0.0.0/8)"))
959@cliutils.arg(
960 '--fixed-range-v6',
961 dest="cidr_v6",
962 help=_('IPv6 subnet (ex: fe80::/64'))
963@cliutils.arg(
964 '--vlan',
965 dest='vlan',
966 type=int,
967 metavar='<vlan id>',
968 help=_("The vlan ID to be assigned to the project."))
969@cliutils.arg(
970 '--vlan-start',
971 dest='vlan_start',
972 type=int,
973 metavar='<vlan start>',
974 help=_('First vlan ID to be assigned to the project. Subsequent vlan '
975 'IDs will be assigned incrementally.'))
976@cliutils.arg(
977 '--vpn',
978 dest='vpn_start',
979 type=int,
980 metavar='<vpn start>',
981 help=_("vpn start"))
982@cliutils.arg(
983 '--gateway',
984 dest="gateway",
985 help=_('gateway'))
986@cliutils.arg(
987 '--gateway-v6',
988 dest="gateway_v6",
989 help=_('IPv6 gateway'))
990@cliutils.arg(
991 '--bridge',
992 dest="bridge",
993 metavar='<bridge>',
994 help=_('VIFs on this network are connected to this bridge.'))
995@cliutils.arg(
996 '--bridge-interface',
997 dest="bridge_interface",
998 metavar='<bridge interface>',
999 help=_('The bridge is connected to this interface.'))
1000@cliutils.arg(
1001 '--multi-host',
1002 dest="multi_host",
1003 metavar="<'T'|'F'>",
1004 help=_('Multi host'))
1005@cliutils.arg(
1006 '--dns1',
1007 dest="dns1",
1008 metavar="<DNS Address>", help='First DNS')
1009@cliutils.arg(
1010 '--dns2',
1011 dest="dns2",
1012 metavar="<DNS Address>",
1013 help=_('Second DNS'))
1014@cliutils.arg(
1015 '--uuid',
1016 dest="uuid",
1017 metavar="<network uuid>",
1018 help=_('Network UUID'))
1019@cliutils.arg(
1020 '--fixed-cidr',
1021 dest="fixed_cidr",
1022 metavar='<x.x.x.x/yy>',
1023 help=_('IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)'))
1024@cliutils.arg(
1025 '--project-id',
1026 dest="project_id",
1027 metavar="<project id>",
1028 help=_('Project ID'))
1029@cliutils.arg(
1030 '--priority',
1031 dest="priority",
1032 metavar="<number>",
1033 help=_('Network interface priority'))
1034@cliutils.arg(
1035 '--mtu',
1036 dest="mtu",
1037 type=int,
1038 help=_('MTU for network'))
1039@cliutils.arg(
1040 '--enable-dhcp',
1041 dest="enable_dhcp",
1042 metavar="<'T'|'F'>",
1043 help=_('Enable dhcp'))
1044@cliutils.arg(
1045 '--dhcp-server',
1046 dest="dhcp_server",
1047 help=_('Dhcp-server (defaults to gateway address)'))
1048@cliutils.arg(
1049 '--share-address',
1050 dest="share_address",
1051 metavar="<'T'|'F'>",
1052 help=_('Share address'))
1053@cliutils.arg(
1054 '--allowed-start',
1055 dest="allowed_start",
1056 help=_('Start of allowed addresses for instances'))
1057@cliutils.arg(
1058 '--allowed-end',
1059 dest="allowed_end",
1060 help=_('End of allowed addresses for instances'))
1061def do_network_create(cs, args):
1062 """Create a network."""
1063
1064 if not (args.cidr or args.cidr_v6):
1065 raise exceptions.CommandError(
1066 _("Must specify either fixed_range_v4 or fixed_range_v6"))
1067 kwargs = _filter_network_create_options(args)
1068 if args.multi_host is not None:
1069 kwargs['multi_host'] = bool(args.multi_host == 'T' or
1070 strutils.bool_from_string(args.multi_host))
1071 if args.enable_dhcp is not None:
1072 kwargs['enable_dhcp'] = bool(
1073 args.enable_dhcp == 'T' or
1074 strutils.bool_from_string(args.enable_dhcp))
1075 if args.share_address is not None:
1076 kwargs['share_address'] = bool(
1077 args.share_address == 'T' or
1078 strutils.bool_from_string(args.share_address))
1079
1080 cs.networks.create(**kwargs)
1081
1082
1083@cliutils.arg(
1084 '--limit',
1085 dest="limit",
1086 metavar="<limit>",
1087 help=_('Number of images to return per request.'))
1088def do_image_list(cs, _args):
1089 """Print a list of available images to boot from."""
1090 limit = _args.limit
1091 image_list = cs.images.list(limit=limit)
1092
1093 def parse_server_name(image):
1094 try:
1095 return image.server['id']
1096 except (AttributeError, KeyError):
1097 return ''
1098
1099 fmts = {'Server': parse_server_name}
1100 utils.print_list(image_list, ['ID', 'Name', 'Status', 'Server'],
1101 fmts, sortby_index=1)
1102
1103
1104@cliutils.arg(
1105 'image',
1106 metavar='<image>',
1107 help=_("Name or ID of image"))
1108@cliutils.arg(
1109 'action',
1110 metavar='<action>',
1111 choices=['set', 'delete'],
1112 help=_("Actions: 'set' or 'delete'"))
1113@cliutils.arg(
1114 'metadata',
1115 metavar='<key=value>',
1116 nargs='+',
1117 action='append',
1118 default=[],
1119 help=_('Metadata to add/update or delete (only key is necessary on '
1120 'delete)'))
1121def do_image_meta(cs, args):
1122 """Set or Delete metadata on an image."""
1123 image = _find_image(cs, args.image)
1124 metadata = _extract_metadata(args)
1125
1126 if args.action == 'set':
1127 cs.images.set_meta(image, metadata)
1128 elif args.action == 'delete':
1129 cs.images.delete_meta(image, metadata.keys())
1130
1131
1132def _extract_metadata(args):
1133 metadata = {}
1134 for metadatum in args.metadata[0]:
1135 # Can only pass the key in on 'delete'
1136 # So this doesn't have to have '='
1137 if metadatum.find('=') > -1:
1138 (key, value) = metadatum.split('=', 1)
1139 else:
1140 key = metadatum
1141 value = None
1142
1143 metadata[key] = value
1144 return metadata
1145
1146
1147def _print_image(image):
1148 info = image._info.copy()
1149
1150 # ignore links, we don't need to present those
1151 info.pop('links')
1152
1153 # try to replace a server entity to just an id
1154 server = info.pop('server', None)
1155 try:
1156 info['server'] = server['id']
1157 except (KeyError, TypeError):
1158 pass
1159
1160 # break up metadata and display each on its own row
1161 metadata = info.pop('metadata', {})
1162 try:
1163 for key, value in metadata.items():
1164 _key = 'metadata %s' % key
1165 info[_key] = value
1166 except AttributeError:
1167 pass
1168
1169 utils.print_dict(info)
1170
1171
1172def _print_flavor(flavor):
1173 info = flavor._info.copy()
1174 # ignore links, we don't need to present those
1175 info.pop('links')
1176 info.update({"extra_specs": _print_flavor_extra_specs(flavor)})
1177 utils.print_dict(info)
1178
1179
1180@cliutils.arg(
1181 'image',
1182 metavar='<image>',
1183 help=_("Name or ID of image"))
1184def do_image_show(cs, args):
1185 """Show details about the given image."""
1186 image = _find_image(cs, args.image)
1187 _print_image(image)
1188
1189
1190@cliutils.arg(
1191 'image', metavar='<image>', nargs='+',
1192 help=_('Name or ID of image(s).'))
1193def do_image_delete(cs, args):
1194 """Delete specified image(s)."""
1195 for image in args.image:
1196 try:
1197 _find_image(cs, image).delete()
1198 except Exception as e:
1199 print(_("Delete for image %(image)s failed: %(e)s") %
1200 {'image': image, 'e': e})
1201
1202
1203@cliutils.arg(
1204 '--reservation-id',
1205 dest='reservation_id',
1206 metavar='<reservation-id>',
1207 default=None,
1208 help=_('Only return servers that match reservation-id.'))
1209@cliutils.arg(
1210 '--reservation_id',
1211 help=argparse.SUPPRESS)
1212@cliutils.arg(
1213 '--ip',
1214 dest='ip',
1215 metavar='<ip-regexp>',
1216 default=None,
1217 help=_('Search with regular expression match by IP address.'))
1218@cliutils.arg(
1219 '--ip6',
1220 dest='ip6',
1221 metavar='<ip6-regexp>',
1222 default=None,
1223 help=_('Search with regular expression match by IPv6 address.'))
1224@cliutils.arg(
1225 '--name',
1226 dest='name',
1227 metavar='<name-regexp>',
1228 default=None,
1229 help=_('Search with regular expression match by name'))
1230@cliutils.arg(
1231 '--instance-name',
1232 dest='instance_name',
1233 metavar='<name-regexp>',
1234 default=None,
1235 help=_('Search with regular expression match by server name.'))
1236@cliutils.arg(
1237 '--instance_name',
1238 help=argparse.SUPPRESS)
1239@cliutils.arg(
1240 '--status',
1241 dest='status',
1242 metavar='<status>',
1243 default=None,
1244 help=_('Search by server status'))
1245@cliutils.arg(
1246 '--flavor',
1247 dest='flavor',
1248 metavar='<flavor>',
1249 default=None,
1250 help=_('Search by flavor name or ID'))
1251@cliutils.arg(
1252 '--image',
1253 dest='image',
1254 metavar='<image>',
1255 default=None,
1256 help=_('Search by image name or ID'))
1257@cliutils.arg(
1258 '--host',
1259 dest='host',
1260 metavar='<hostname>',
1261 default=None,
1262 help=_('Search servers by hostname to which they are assigned (Admin '
1263 'only).'))
1264@cliutils.arg(
1265 '--all-tenants',
1266 dest='all_tenants',
1267 metavar='<0|1>',
1268 nargs='?',
1269 type=int,
1270 const=1,
1271 default=int(strutils.bool_from_string(
1272 os.environ.get("ALL_TENANTS", 'false'), True)),
1273 help=_('Display information from all tenants (Admin only).'))
1274@cliutils.arg(
1275 '--all_tenants',
1276 nargs='?',
1277 type=int,
1278 const=1,
1279 help=argparse.SUPPRESS)
1280@cliutils.arg(
1281 '--tenant',
1282 # nova db searches by project_id
1283 dest='tenant',
1284 metavar='<tenant>',
1285 nargs='?',
1286 help=_('Display information from single tenant (Admin only). '
1287 'The --all-tenants option must also be provided.'))
1288@cliutils.arg(
1289 '--user',
1290 dest='user',
1291 metavar='<user>',
1292 nargs='?',
1293 help=_('Display information from single user (Admin only).'))
1294@cliutils.arg(
1295 '--deleted',
1296 dest='deleted',
1297 action="store_true",
1298 default=False,
1299 help='Only display deleted servers (Admin only).')
1300@cliutils.arg(
1301 '--fields',
1302 default=None,
1303 metavar='<fields>',
1304 help=_('Comma-separated list of fields to display. '
1305 'Use the show command to see which fields are available.'))
1306@cliutils.arg(
1307 '--minimal',
1308 dest='minimal',
1309 action="store_true",
1310 default=False,
1311 help=_('Get only uuid and name.'))
1312@cliutils.arg(
1313 '--sort',
1314 dest='sort',
1315 metavar='<key>[:<direction>]',
1316 help=('Comma-separated list of sort keys and directions in the form'
1317 ' of <key>[:<asc|desc>]. The direction defaults to descending if'
1318 ' not specified.'))
1319def do_list(cs, args):
1320 """List active servers."""
1321 imageid = None
1322 flavorid = None
1323 if args.image:
1324 imageid = _find_image(cs, args.image).id
1325 if args.flavor:
1326 flavorid = _find_flavor(cs, args.flavor).id
1327 # search by tenant or user only works with all_tenants
1328 if args.tenant or args.user:
1329 args.all_tenants = 1
1330 search_opts = {
1331 'all_tenants': args.all_tenants,
1332 'reservation_id': args.reservation_id,
1333 'ip': args.ip,
1334 'ip6': args.ip6,
1335 'name': args.name,
1336 'image': imageid,
1337 'flavor': flavorid,
1338 'status': args.status,
1339 'tenant_id': args.tenant,
1340 'user_id': args.user,
1341 'host': args.host,
1342 'deleted': args.deleted,
1343 'instance_name': args.instance_name}
1344
1345 filters = {'flavor': lambda f: f['id'],
1346 'security_groups': utils._format_security_groups}
1347
1348 formatters = {}
1349 field_titles = []
1350 if args.fields:
1351 for field in args.fields.split(','):
1352 field_title, formatter = utils._make_field_formatter(field,
1353 filters)
1354 field_titles.append(field_title)
1355 formatters[field_title] = formatter
1356
1357 id_col = 'ID'
1358
1359 detailed = not args.minimal
1360
1361 sort_keys = []
1362 sort_dirs = []
1363 if args.sort:
1364 for sort in args.sort.split(','):
1365 sort_key, _sep, sort_dir = sort.partition(':')
1366 if not sort_dir:
1367 sort_dir = 'desc'
1368 elif sort_dir not in ('asc', 'desc'):
1369 raise exceptions.CommandError(_(
1370 'Unknown sort direction: %s') % sort_dir)
1371 sort_keys.append(sort_key)
1372 sort_dirs.append(sort_dir)
1373
1374 servers = cs.servers.list(detailed=detailed,
1375 search_opts=search_opts,
1376 sort_keys=sort_keys,
1377 sort_dirs=sort_dirs)
1378 convert = [('OS-EXT-SRV-ATTR:host', 'host'),
1379 ('OS-EXT-STS:task_state', 'task_state'),
1380 ('OS-EXT-SRV-ATTR:instance_name', 'instance_name'),
1381 ('OS-EXT-STS:power_state', 'power_state'),
1382 ('hostId', 'host_id')]
1383 _translate_keys(servers, convert)
1384 _translate_extended_states(servers)
1385 if args.minimal:
1386 columns = [
1387 id_col,
1388 'Name']
1389 elif field_titles:
1390 columns = [id_col] + field_titles
1391 else:
1392 columns = [
1393 id_col,
1394 'Name',
1395 'Status',
1396 'Task State',
1397 'Power State',
1398 'Networks'
1399 ]
1400 # If getting the data for all tenants, print
1401 # Tenant ID as well
1402 if search_opts['all_tenants']:
1403 columns.insert(2, 'Tenant ID')
1404 formatters['Networks'] = utils._format_servers_list_networks
1405 sortby_index = 1
1406 if args.sort:
1407 sortby_index = None
1408 utils.print_list(servers, columns,
1409 formatters, sortby_index=sortby_index)
1410
1411
1412@cliutils.arg(
1413 '--hard',
1414 dest='reboot_type',
1415 action='store_const',
1416 const=servers.REBOOT_HARD,
1417 default=servers.REBOOT_SOFT,
1418 help=_('Perform a hard reboot (instead of a soft one).'))
1419@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1420@cliutils.arg(
1421 '--poll',
1422 dest='poll',
1423 action="store_true",
1424 default=False,
1425 help=_('Poll until reboot is complete.'))
1426def do_reboot(cs, args):
1427 """Reboot a server."""
1428 server = _find_server(cs, args.server)
1429 server.reboot(args.reboot_type)
1430
1431 if args.poll:
1432 _poll_for_status(cs.servers.get, server.id, 'rebooting', ['active'],
1433 show_progress=False)
1434
1435
1436@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1437@cliutils.arg('image', metavar='<image>', help=_("Name or ID of new image."))
1438@cliutils.arg(
1439 '--rebuild-password',
1440 dest='rebuild_password',
1441 metavar='<rebuild-password>',
1442 default=False,
1443 help=_("Set the provided admin password on the rebuilt server."))
1444@cliutils.arg(
1445 '--rebuild_password',
1446 help=argparse.SUPPRESS)
1447@cliutils.arg(
1448 '--poll',
1449 dest='poll',
1450 action="store_true",
1451 default=False,
1452 help=_('Report the server rebuild progress until it completes.'))
1453@cliutils.arg(
1454 '--minimal',
1455 dest='minimal',
1456 action="store_true",
1457 default=False,
1458 help=_('Skips flavor/image lookups when showing servers'))
1459@cliutils.arg(
1460 '--preserve-ephemeral',
1461 action="store_true",
1462 default=False,
1463 help='Preserve the default ephemeral storage partition on rebuild.')
1464@cliutils.arg(
1465 '--name',
1466 metavar='<name>',
1467 default=None,
1468 help=_('Name for the new server'))
1469@cliutils.arg(
1470 '--meta',
1471 metavar="<key=value>",
1472 action='append',
1473 default=[],
1474 help=_("Record arbitrary key/value metadata to /meta_data.json "
1475 "on the metadata server. Can be specified multiple times."))
1476@cliutils.arg(
1477 '--file',
1478 metavar="<dst-path=src-path>",
1479 action='append',
1480 dest='files',
1481 default=[],
1482 help=_("Store arbitrary files from <src-path> locally to <dst-path> "
1483 "on the new server. You may store up to 5 files."))
1484def do_rebuild(cs, args):
1485 """Shutdown, re-image, and re-boot a server."""
1486 server = _find_server(cs, args.server)
1487 image = _find_image(cs, args.image)
1488
1489 if args.rebuild_password is not False:
1490 _password = args.rebuild_password
1491 else:
1492 _password = None
1493
1494 kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args)
1495 kwargs['preserve_ephemeral'] = args.preserve_ephemeral
1496 kwargs['name'] = args.name
1497 meta = dict(v.split('=', 1) for v in args.meta)
1498 kwargs['meta'] = meta
1499
1500 files = {}
1501 for f in args.files:
1502 try:
1503 dst, src = f.split('=', 1)
1504 with open(src, 'r') as s:
1505 files[dst] = s.read()
1506 except IOError as e:
1507 raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") %
1508 {'src': src, 'exc': e})
1509 except ValueError as e:
1510 raise exceptions.CommandError(_("Invalid file argument '%s'. "
1511 "File arguments must be of the "
1512 "form '--file "
1513 "<dst-path=src-path>'") % f)
1514 kwargs['files'] = files
1515 server = server.rebuild(image, _password, **kwargs)
1516 _print_server(cs, args, server)
1517
1518 if args.poll:
1519 _poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active'])
1520
1521
1522@cliutils.arg(
1523 'server', metavar='<server>',
1524 help=_('Name (old name) or ID of server.'))
1525@cliutils.arg('name', metavar='<name>', help=_('New name for the server.'))
1526def do_rename(cs, args):
1527 """Rename a server."""
1528 _find_server(cs, args.server).update(name=args.name)
1529
1530
1531@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1532@cliutils.arg(
1533 'flavor',
1534 metavar='<flavor>',
1535 help=_("Name or ID of new flavor."))
1536@cliutils.arg(
1537 '--poll',
1538 dest='poll',
1539 action="store_true",
1540 default=False,
1541 help=_('Report the server resize progress until it completes.'))
1542def do_resize(cs, args):
1543 """Resize a server."""
1544 server = _find_server(cs, args.server)
1545 flavor = _find_flavor(cs, args.flavor)
1546 kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args)
1547 server.resize(flavor, **kwargs)
1548 if args.poll:
1549 _poll_for_status(cs.servers.get, server.id, 'resizing',
1550 ['active', 'verify_resize'])
1551
1552
1553@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1554def do_resize_confirm(cs, args):
1555 """Confirm a previous resize."""
1556 _find_server(cs, args.server).confirm_resize()
1557
1558
1559@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1560def do_resize_revert(cs, args):
1561 """Revert a previous resize (and return to the previous VM)."""
1562 _find_server(cs, args.server).revert_resize()
1563
1564
1565@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1566@cliutils.arg(
1567 '--poll',
1568 dest='poll',
1569 action="store_true",
1570 default=False,
1571 help=_('Report the server migration progress until it completes.'))
1572def do_migrate(cs, args):
1573 """Migrate a server. The new host will be selected by the scheduler."""
1574 server = _find_server(cs, args.server)
1575 server.migrate()
1576
1577 if args.poll:
1578 _poll_for_status(cs.servers.get, server.id, 'migrating',
1579 ['active', 'verify_resize'])
1580
1581
1582@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1583def do_pause(cs, args):
1584 """Pause a server."""
1585 _find_server(cs, args.server).pause()
1586
1587
1588@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1589def do_unpause(cs, args):
1590 """Unpause a server."""
1591 _find_server(cs, args.server).unpause()
1592
1593
1594@cliutils.arg(
1595 'server',
1596 metavar='<server>', nargs='+',
1597 help=_('Name or ID of server(s).'))
1598def do_stop(cs, args):
1599 """Stop the server(s)."""
1600 utils.do_action_on_many(
1601 lambda s: _find_server(cs, s).stop(),
1602 args.server,
1603 _("Request to stop server %s has been accepted."),
1604 _("Unable to stop the specified server(s)."))
1605
1606
1607@cliutils.arg(
1608 'server',
1609 metavar='<server>', nargs='+',
1610 help=_('Name or ID of server(s).'))
1611def do_start(cs, args):
1612 """Start the server(s)."""
1613 utils.do_action_on_many(
1614 lambda s: _find_server(cs, s).start(),
1615 args.server,
1616 _("Request to start server %s has been accepted."),
1617 _("Unable to start the specified server(s)."))
1618
1619
1620@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1621def do_lock(cs, args):
1622 """Lock a server. A normal (non-admin) user will not be able to execute
1623 actions on a locked server.
1624 """
1625 _find_server(cs, args.server).lock()
1626
1627
1628@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1629def do_unlock(cs, args):
1630 """Unlock a server."""
1631 _find_server(cs, args.server).unlock()
1632
1633
1634@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1635def do_suspend(cs, args):
1636 """Suspend a server."""
1637 _find_server(cs, args.server).suspend()
1638
1639
1640@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1641def do_resume(cs, args):
1642 """Resume a server."""
1643 _find_server(cs, args.server).resume()
1644
1645
1646@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1647@cliutils.arg(
1648 '--password',
1649 metavar='<password>',
1650 dest='password',
1651 help=_('The admin password to be set in the rescue environment.'))
1652@cliutils.arg(
1653 '--image',
1654 metavar='<image>',
1655 dest='image',
1656 help=_('The image to rescue with.'))
1657def do_rescue(cs, args):
1658 """Reboots a server into rescue mode, which starts the machine
1659 from either the initial image or a specified image, attaching the current
1660 boot disk as secondary.
1661 """
1662 kwargs = {}
1663 if args.image:
1664 kwargs['image'] = _find_image(cs, args.image)
1665 if args.password:
1666 kwargs['password'] = args.password
1667 utils.print_dict(_find_server(cs, args.server).rescue(**kwargs)[1])
1668
1669
1670@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1671def do_unrescue(cs, args):
1672 """Restart the server from normal boot disk again."""
1673 _find_server(cs, args.server).unrescue()
1674
1675
1676@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1677def do_shelve(cs, args):
1678 """Shelve a server."""
1679 _find_server(cs, args.server).shelve()
1680
1681
1682@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1683def do_shelve_offload(cs, args):
1684 """Remove a shelved server from the compute node."""
1685 _find_server(cs, args.server).shelve_offload()
1686
1687
1688@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1689def do_unshelve(cs, args):
1690 """Unshelve a server."""
1691 _find_server(cs, args.server).unshelve()
1692
1693
1694@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1695def do_diagnostics(cs, args):
1696 """Retrieve server diagnostics."""
1697 server = _find_server(cs, args.server)
1698 utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80)
1699
1700
1701@cliutils.arg(
1702 'server', metavar='<server>',
1703 help=_('Name or ID of a server for which the network cache should '
1704 'be refreshed from neutron (Admin only).'))
1705def do_refresh_network(cs, args):
1706 """Refresh server network information."""
1707 server = _find_server(cs, args.server)
1708 cs.server_external_events.create([{'server_uuid': server.id,
1709 'name': 'network-changed'}])
1710
1711
1712@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1713def do_root_password(cs, args):
1714 """
1715 Change the admin password for a server.
1716 """
1717 server = _find_server(cs, args.server)
1718 p1 = getpass.getpass('New password: ')
1719 p2 = getpass.getpass('Again: ')
1720 if p1 != p2:
1721 raise exceptions.CommandError(_("Passwords do not match."))
1722 server.change_password(p1)
1723
1724
1725@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1726@cliutils.arg('name', metavar='<name>', help=_('Name of snapshot.'))
1727@cliutils.arg(
1728 '--show',
1729 dest='show',
1730 action="store_true",
1731 default=False,
1732 help=_('Print image info.'))
1733@cliutils.arg(
1734 '--poll',
1735 dest='poll',
1736 action="store_true",
1737 default=False,
1738 help=_('Report the snapshot progress and poll until image creation is '
1739 'complete.'))
1740def do_image_create(cs, args):
1741 """Create a new image by taking a snapshot of a running server."""
1742 server = _find_server(cs, args.server)
1743 image_uuid = cs.servers.create_image(server, args.name)
1744
1745 if args.poll:
1746 _poll_for_status(cs.images.get, image_uuid, 'snapshotting',
1747 ['active'])
1748
1749 # NOTE(sirp): A race-condition exists between when the image finishes
1750 # uploading and when the servers's `task_state` is cleared. To account
1751 # for this, we need to poll a second time to ensure the `task_state` is
1752 # cleared before returning, ensuring that a snapshot taken immediately
1753 # after this function returns will succeed.
1754 #
1755 # A better long-term solution will be to separate 'snapshotting' and
1756 # 'image-uploading' in Nova and clear the task-state once the VM
1757 # snapshot is complete but before the upload begins.
1758 task_state_field = "OS-EXT-STS:task_state"
1759 if hasattr(server, task_state_field):
1760 _poll_for_status(cs.servers.get, server.id, 'image_snapshot',
1761 [None], status_field=task_state_field,
1762 show_progress=False, silent=True)
1763
1764 if args.show:
1765 _print_image(cs.images.get(image_uuid))
1766
1767
1768@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1769@cliutils.arg('name', metavar='<name>', help=_('Name of the backup image.'))
1770@cliutils.arg(
1771 'backup_type', metavar='<backup-type>',
1772 help=_('The backup type, like "daily" or "weekly".'))
1773@cliutils.arg(
1774 'rotation', metavar='<rotation>',
1775 help=_('Int parameter representing how many backups to keep '
1776 'around.'))
1777def do_backup(cs, args):
1778 """Backup a server by creating a 'backup' type snapshot."""
1779 _find_server(cs, args.server).backup(args.name,
1780 args.backup_type,
1781 args.rotation)
1782
1783
1784@cliutils.arg(
1785 'server',
1786 metavar='<server>',
1787 help=_("Name or ID of server"))
1788@cliutils.arg(
1789 'action',
1790 metavar='<action>',
1791 choices=['set', 'delete'],
1792 help=_("Actions: 'set' or 'delete'"))
1793@cliutils.arg(
1794 'metadata',
1795 metavar='<key=value>',
1796 nargs='+',
1797 action='append',
1798 default=[],
1799 help=_('Metadata to set or delete (only key is necessary on delete)'))
1800def do_meta(cs, args):
1801 """Set or Delete metadata on a server."""
1802 server = _find_server(cs, args.server)
1803 metadata = _extract_metadata(args)
1804
1805 if args.action == 'set':
1806 cs.servers.set_meta(server, metadata)
1807 elif args.action == 'delete':
1808 cs.servers.delete_meta(server, sorted(metadata.keys(), reverse=True))
1809
1810
1811def _print_server(cs, args, server=None):
1812 # By default when searching via name we will do a
1813 # findall(name=blah) and due a REST /details which is not the same
1814 # as a .get() and doesn't get the information about flavors and
1815 # images. This fix it as we redo the call with the id which does a
1816 # .get() to get all informations.
1817 if not server:
1818 server = _find_server(cs, args.server)
1819
1820 minimal = getattr(args, "minimal", False)
1821
1822 networks = server.networks
1823 info = server._info.copy()
1824 for network_label, address_list in networks.items():
1825 info['%s network' % network_label] = ', '.join(address_list)
1826
1827 flavor = info.get('flavor', {})
1828 flavor_id = flavor.get('id', '')
1829 if minimal:
1830 info['flavor'] = flavor_id
1831 else:
1832 info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name,
1833 flavor_id)
1834
1835 if 'security_groups' in info:
1836 # when we have multiple nics the info will include the
1837 # security groups N times where N == number of nics. Be nice
1838 # and only display it once.
1839 info['security_groups'] = ', '.join(
1840 sorted(set(group['name'] for group in info['security_groups'])))
1841
1842 image = info.get('image', {})
1843 if image:
1844 image_id = image.get('id', '')
1845 if minimal:
1846 info['image'] = image_id
1847 else:
1848 try:
1849 info['image'] = '%s (%s)' % (_find_image(cs, image_id).name,
1850 image_id)
1851 except Exception:
1852 info['image'] = '%s (%s)' % (_("Image not found"), image_id)
1853 else: # Booted from volume
1854 info['image'] = _("Attempt to boot from volume - no image supplied")
1855
1856 info.pop('links', None)
1857 info.pop('addresses', None)
1858
1859 utils.print_dict(info)
1860
1861
1862@cliutils.arg(
1863 '--minimal',
1864 dest='minimal',
1865 action="store_true",
1866 default=False,
1867 help=_('Skips flavor/image lookups when showing servers'))
1868@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1869def do_show(cs, args):
1870 """Show details about the given server."""
1871 _print_server(cs, args)
1872
1873
1874@cliutils.arg(
1875 'server', metavar='<server>', nargs='+',
1876 help=_('Name or ID of server(s).'))
1877def do_delete(cs, args):
1878 """Immediately shut down and delete specified server(s)."""
1879 utils.do_action_on_many(
1880 lambda s: _find_server(cs, s).delete(),
1881 args.server,
1882 _("Request to delete server %s has been accepted."),
1883 _("Unable to delete the specified server(s)."))
1884
1885
1886def _find_server(cs, server):
1887 """Get a server by name or ID."""
1888 return utils.find_resource(cs.servers, server)
1889
1890
1891def _find_image(cs, image):
1892 """Get an image by name or ID."""
1893 return utils.find_resource(cs.images, image)
1894
1895
1896def _find_flavor(cs, flavor):
1897 """Get a flavor by name, ID, or RAM size."""
1898 try:
1899 return utils.find_resource(cs.flavors, flavor, is_public=None)
1900 except exceptions.NotFound:
1901 return cs.flavors.find(ram=flavor)
1902
1903
1904@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1905@cliutils.arg(
1906 'network_id',
1907 metavar='<network-id>',
1908 help='Network ID.')
1909def do_add_fixed_ip(cs, args):
1910 """Add new IP address on a network to server."""
1911 server = _find_server(cs, args.server)
1912 server.add_fixed_ip(args.network_id)
1913
1914
1915@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
1916@cliutils.arg('address', metavar='<address>', help=_('IP Address.'))
1917def do_remove_fixed_ip(cs, args):
1918 """Remove an IP address from a server."""
1919 server = _find_server(cs, args.server)
1920 server.remove_fixed_ip(args.address)
1921
1922
1923def _find_volume(cs, volume):
1924 """Get a volume by name or ID."""
1925 return utils.find_resource(cs.volumes, volume)
1926
1927
1928def _find_volume_snapshot(cs, snapshot):
1929 """Get a volume snapshot by name or ID."""
1930 return utils.find_resource(cs.volume_snapshots, snapshot)
1931
1932
1933def _print_volume(volume):
1934 utils.print_dict(volume._info)
1935
1936
1937def _print_volume_snapshot(snapshot):
1938 utils.print_dict(snapshot._info)
1939
1940
1941def _translate_volume_keys(collection):
1942 _translate_keys(collection,
1943 [('displayName', 'display_name'),
1944 ('volumeType', 'volume_type')])
1945
1946
1947def _translate_volume_snapshot_keys(collection):
1948 _translate_keys(collection,
1949 [('displayName', 'display_name'),
1950 ('volumeId', 'volume_id')])
1951
1952
1953def _translate_availability_zone_keys(collection):
1954 _translate_keys(collection,
1955 [('zoneName', 'name'), ('zoneState', 'status')])
1956
1957
1958@cliutils.arg(
1959 '--all-tenants',
1960 dest='all_tenants',
1961 metavar='<0|1>',
1962 nargs='?',
1963 type=int,
1964 const=1,
1965 default=int(strutils.bool_from_string(
1966 os.environ.get("ALL_TENANTS", 'false'), True)),
1967 help=_('Display information from all tenants (Admin only).'))
1968@cliutils.arg(
1969 '--all_tenants',
1970 nargs='?',
1971 type=int,
1972 const=1,
1973 help=argparse.SUPPRESS)
1974@cliutils.service_type('volume')
1975def do_volume_list(cs, args):
1976 """List all the volumes."""
1977 search_opts = {'all_tenants': args.all_tenants}
1978 volumes = cs.volumes.list(search_opts=search_opts)
1979 _translate_volume_keys(volumes)
1980
1981 # Create a list of servers to which the volume is attached
1982 for vol in volumes:
1983 servers = [s.get('server_id') for s in vol.attachments]
1984 setattr(vol, 'attached_to', ','.join(map(str, servers)))
1985 utils.print_list(volumes, ['ID', 'Status', 'Display Name',
1986 'Size', 'Volume Type', 'Attached to'])
1987
1988
1989@cliutils.arg(
1990 'volume',
1991 metavar='<volume>',
1992 help=_('Name or ID of the volume.'))
1993@cliutils.service_type('volume')
1994def do_volume_show(cs, args):
1995 """Show details about a volume."""
1996 volume = _find_volume(cs, args.volume)
1997 _print_volume(volume)
1998
1999
2000@cliutils.arg(
2001 'size',
2002 metavar='<size>',
2003 type=int,
2004 help=_('Size of volume in GB'))
2005@cliutils.arg(
2006 '--snapshot-id',
2007 metavar='<snapshot-id>',
2008 default=None,
2009 help=_('Optional snapshot id to create the volume from. (Default=None)'))
2010@cliutils.arg(
2011 '--snapshot_id',
2012 help=argparse.SUPPRESS)
2013@cliutils.arg(
2014 '--image-id',
2015 metavar='<image-id>',
2016 help=_('Optional image id to create the volume from. (Default=None)'),
2017 default=None)
2018@cliutils.arg(
2019 '--display-name',
2020 metavar='<display-name>',
2021 default=None,
2022 help=_('Optional volume name. (Default=None)'))
2023@cliutils.arg(
2024 '--display_name',
2025 help=argparse.SUPPRESS)
2026@cliutils.arg(
2027 '--display-description',
2028 metavar='<display-description>',
2029 default=None,
2030 help=_('Optional volume description. (Default=None)'))
2031@cliutils.arg(
2032 '--display_description',
2033 help=argparse.SUPPRESS)
2034@cliutils.arg(
2035 '--volume-type',
2036 metavar='<volume-type>',
2037 default=None,
2038 help=_('Optional volume type. (Default=None)'))
2039@cliutils.arg(
2040 '--volume_type',
2041 help=argparse.SUPPRESS)
2042@cliutils.arg(
2043 '--availability-zone', metavar='<availability-zone>',
2044 help=_('Optional Availability Zone for volume. (Default=None)'),
2045 default=None)
2046@cliutils.service_type('volume')
2047def do_volume_create(cs, args):
2048 """Add a new volume."""
2049 volume = cs.volumes.create(args.size,
2050 args.snapshot_id,
2051 args.display_name,
2052 args.display_description,
2053 args.volume_type,
2054 args.availability_zone,
2055 imageRef=args.image_id)
2056 _print_volume(volume)
2057
2058
2059@cliutils.arg(
2060 'volume',
2061 metavar='<volume>', nargs='+',
2062 help=_('Name or ID of the volume(s) to delete.'))
2063@cliutils.service_type('volume')
2064def do_volume_delete(cs, args):
2065 """Remove volume(s)."""
2066 for volume in args.volume:
2067 try:
2068 _find_volume(cs, volume).delete()
2069 except Exception as e:
2070 print(_("Delete for volume %(volume)s failed: %(e)s") %
2071 {'volume': volume, 'e': e})
2072
2073
2074@cliutils.arg(
2075 'server',
2076 metavar='<server>',
2077 help=_('Name or ID of server.'))
2078@cliutils.arg(
2079 'volume',
2080 metavar='<volume>',
2081 help=_('ID of the volume to attach.'))
2082@cliutils.arg(
2083 'device', metavar='<device>', default=None, nargs='?',
2084 help=_('Name of the device e.g. /dev/vdb. '
2085 'Use "auto" for autoassign (if supported)'))
2086def do_volume_attach(cs, args):
2087 """Attach a volume to a server."""
2088 if args.device == 'auto':
2089 args.device = None
2090
2091 volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id,
2092 args.volume,
2093 args.device)
2094 _print_volume(volume)
2095
2096
2097@cliutils.arg(
2098 'server',
2099 metavar='<server>',
2100 help=_('Name or ID of server.'))
2101@cliutils.arg(
2102 'attachment_id',
2103 metavar='<attachment>',
2104 help=_('Attachment ID of the volume.'))
2105@cliutils.arg(
2106 'new_volume',
2107 metavar='<volume>',
2108 help=_('ID of the volume to attach.'))
2109def do_volume_update(cs, args):
2110 """Update volume attachment."""
2111 cs.volumes.update_server_volume(_find_server(cs, args.server).id,
2112 args.attachment_id,
2113 args.new_volume)
2114
2115
2116@cliutils.arg(
2117 'server',
2118 metavar='<server>',
2119 help=_('Name or ID of server.'))
2120@cliutils.arg(
2121 'attachment_id',
2122 metavar='<volume>',
2123 help=_('ID of the volume to detach.'))
2124def do_volume_detach(cs, args):
2125 """Detach a volume from a server."""
2126 cs.volumes.delete_server_volume(_find_server(cs, args.server).id,
2127 args.attachment_id)
2128
2129
2130@cliutils.service_type('volume')
2131def do_volume_snapshot_list(cs, _args):
2132 """List all the snapshots."""
2133 snapshots = cs.volume_snapshots.list()
2134 _translate_volume_snapshot_keys(snapshots)
2135 utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name',
2136 'Size'])
2137
2138
2139@cliutils.arg(
2140 'snapshot',
2141 metavar='<snapshot>',
2142 help=_('Name or ID of the snapshot.'))
2143@cliutils.service_type('volume')
2144def do_volume_snapshot_show(cs, args):
2145 """Show details about a snapshot."""
2146 snapshot = _find_volume_snapshot(cs, args.snapshot)
2147 _print_volume_snapshot(snapshot)
2148
2149
2150@cliutils.arg(
2151 'volume_id',
2152 metavar='<volume-id>',
2153 help=_('ID of the volume to snapshot'))
2154@cliutils.arg(
2155 '--force',
2156 metavar='<True|False>',
2157 help=_('Optional flag to indicate whether to snapshot a volume even if '
2158 'its attached to a server. (Default=False)'),
2159 default=False)
2160@cliutils.arg(
2161 '--display-name',
2162 metavar='<display-name>',
2163 default=None,
2164 help=_('Optional snapshot name. (Default=None)'))
2165@cliutils.arg(
2166 '--display_name',
2167 help=argparse.SUPPRESS)
2168@cliutils.arg(
2169 '--display-description',
2170 metavar='<display-description>',
2171 default=None,
2172 help=_('Optional snapshot description. (Default=None)'))
2173@cliutils.arg(
2174 '--display_description',
2175 help=argparse.SUPPRESS)
2176@cliutils.service_type('volume')
2177def do_volume_snapshot_create(cs, args):
2178 """Add a new snapshot."""
2179 snapshot = cs.volume_snapshots.create(args.volume_id,
2180 args.force,
2181 args.display_name,
2182 args.display_description)
2183 _print_volume_snapshot(snapshot)
2184
2185
2186@cliutils.arg(
2187 'snapshot',
2188 metavar='<snapshot>',
2189 help=_('Name or ID of the snapshot to delete.'))
2190@cliutils.service_type('volume')
2191def do_volume_snapshot_delete(cs, args):
2192 """Remove a snapshot."""
2193 snapshot = _find_volume_snapshot(cs, args.snapshot)
2194 snapshot.delete()
2195
2196
2197def _print_volume_type_list(vtypes):
2198 utils.print_list(vtypes, ['ID', 'Name'])
2199
2200
2201@cliutils.service_type('volume')
2202def do_volume_type_list(cs, args):
2203 """Print a list of available 'volume types'."""
2204 vtypes = cs.volume_types.list()
2205 _print_volume_type_list(vtypes)
2206
2207
2208@cliutils.arg(
2209 'name',
2210 metavar='<name>',
2211 help=_("Name of the new volume type"))
2212@cliutils.service_type('volume')
2213def do_volume_type_create(cs, args):
2214 """Create a new volume type."""
2215 vtype = cs.volume_types.create(args.name)
2216 _print_volume_type_list([vtype])
2217
2218
2219@cliutils.arg(
2220 'id',
2221 metavar='<id>',
2222 help=_("Unique ID of the volume type to delete"))
2223@cliutils.service_type('volume')
2224def do_volume_type_delete(cs, args):
2225 """Delete a specific volume type."""
2226 cs.volume_types.delete(args.id)
2227
2228
2229@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2230@cliutils.arg(
2231 'console_type',
2232 metavar='<console-type>',
2233 help=_('Type of vnc console ("novnc" or "xvpvnc").'))
2234def do_get_vnc_console(cs, args):
2235 """Get a vnc console to a server."""
2236 server = _find_server(cs, args.server)
2237 data = server.get_vnc_console(args.console_type)
2238
2239 class VNCConsole(object):
2240 def __init__(self, console_dict):
2241 self.type = console_dict['type']
2242 self.url = console_dict['url']
2243
2244 utils.print_list([VNCConsole(data['console'])], ['Type', 'Url'])
2245
2246
2247@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2248@cliutils.arg(
2249 'console_type',
2250 metavar='<console-type>',
2251 help=_('Type of spice console ("spice-html5").'))
2252def do_get_spice_console(cs, args):
2253 """Get a spice console to a server."""
2254 server = _find_server(cs, args.server)
2255 data = server.get_spice_console(args.console_type)
2256
2257 class SPICEConsole(object):
2258 def __init__(self, console_dict):
2259 self.type = console_dict['type']
2260 self.url = console_dict['url']
2261
2262 utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url'])
2263
2264
2265@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2266@cliutils.arg(
2267 'console_type',
2268 metavar='<console-type>',
2269 help='Type of rdp console ("rdp-html5").')
2270def do_get_rdp_console(cs, args):
2271 """Get a rdp console to a server."""
2272 server = _find_server(cs, args.server)
2273 data = server.get_rdp_console(args.console_type)
2274
2275 class RDPConsole(object):
2276 def __init__(self, console_dict):
2277 self.type = console_dict['type']
2278 self.url = console_dict['url']
2279
2280 utils.print_list([RDPConsole(data['console'])], ['Type', 'Url'])
2281
2282
2283@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2284@cliutils.arg(
2285 '--console_type', default='serial',
2286 help=_('Type of serial console, default="serial".'))
2287def do_get_serial_console(cs, args):
2288 """Get a serial console to a server."""
2289 if args.console_type not in ('serial',):
2290 raise exceptions.CommandError(
2291 _("Invalid parameter value for 'console_type', "
2292 "currently supported 'serial'."))
2293
2294 server = _find_server(cs, args.server)
2295 data = server.get_serial_console(args.console_type)
2296
2297 class SerialConsole(object):
2298 def __init__(self, console_dict):
2299 self.type = console_dict['type']
2300 self.url = console_dict['url']
2301
2302 utils.print_list([SerialConsole(data['console'])], ['Type', 'Url'])
2303
2304
2305@cliutils.arg('server', metavar='<server>', help='Name or ID of server.')
2306@cliutils.arg(
2307 'private_key',
2308 metavar='<private-key>',
2309 help=_('Private key (used locally to decrypt password) (Optional). '
2310 'When specified, the command displays the clear (decrypted) VM '
2311 'password. When not specified, the ciphered VM password is '
2312 'displayed.'),
2313 nargs='?',
2314 default=None)
2315def do_get_password(cs, args):
2316 """Get the admin password for a server."""
2317 server = _find_server(cs, args.server)
2318 data = server.get_password(args.private_key)
2319 print(data)
2320
2321
2322@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2323def do_clear_password(cs, args):
2324 """Clear the admin password for a server."""
2325 server = _find_server(cs, args.server)
2326 server.clear_password()
2327
2328
2329def _print_floating_ip_list(floating_ips):
2330 convert = [('instance_id', 'server_id')]
2331 _translate_keys(floating_ips, convert)
2332
2333 utils.print_list(floating_ips,
2334 ['Id', 'IP', 'Server Id', 'Fixed IP', 'Pool'])
2335
2336
2337@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2338@cliutils.arg(
2339 '--length',
2340 metavar='<length>',
2341 default=None,
2342 help=_('Length in lines to tail.'))
2343def do_console_log(cs, args):
2344 """Get console log output of a server."""
2345 server = _find_server(cs, args.server)
2346 data = server.get_console_output(length=args.length)
2347 print(data)
2348
2349
2350@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2351@cliutils.arg('address', metavar='<address>', help=_('IP Address.'))
2352@cliutils.arg(
2353 '--fixed-address',
2354 metavar='<fixed_address>',
2355 default=None,
2356 help=_('Fixed IP Address to associate with.'))
2357def do_add_floating_ip(cs, args):
2358 """DEPRECATED, use floating-ip-associate instead."""
2359 _associate_floating_ip(cs, args)
2360
2361
2362@cliutils.arg('server', metavar='<server>', help='Name or ID of server.')
2363@cliutils.arg('address', metavar='<address>', help='IP Address.')
2364@cliutils.arg(
2365 '--fixed-address',
2366 metavar='<fixed_address>',
2367 default=None,
2368 help='Fixed IP Address to associate with.')
2369def do_floating_ip_associate(cs, args):
2370 """Associate a floating IP address to a server."""
2371 _associate_floating_ip(cs, args)
2372
2373
2374def _associate_floating_ip(cs, args):
2375 server = _find_server(cs, args.server)
2376 server.add_floating_ip(args.address, args.fixed_address)
2377
2378
2379@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2380@cliutils.arg('address', metavar='<address>', help=_('IP Address.'))
2381def do_remove_floating_ip(cs, args):
2382 """DEPRECATED, use floating-ip-disassociate instead."""
2383 _disassociate_floating_ip(cs, args)
2384
2385
2386@cliutils.arg('server', metavar='<server>', help='Name or ID of server.')
2387@cliutils.arg('address', metavar='<address>', help='IP Address.')
2388def do_floating_ip_disassociate(cs, args):
2389 """Disassociate a floating IP address from a server."""
2390 _disassociate_floating_ip(cs, args)
2391
2392
2393def _disassociate_floating_ip(cs, args):
2394 server = _find_server(cs, args.server)
2395 server.remove_floating_ip(args.address)
2396
2397
2398@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2399@cliutils.arg(
2400 'secgroup',
2401 metavar='<secgroup>',
2402 help=_('Name of Security Group.'))
2403def do_add_secgroup(cs, args):
2404 """Add a Security Group to a server."""
2405 server = _find_server(cs, args.server)
2406 server.add_security_group(args.secgroup)
2407
2408
2409@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2410@cliutils.arg(
2411 'secgroup',
2412 metavar='<secgroup>',
2413 help=_('Name of Security Group.'))
2414def do_remove_secgroup(cs, args):
2415 """Remove a Security Group from a server."""
2416 server = _find_server(cs, args.server)
2417 server.remove_security_group(args.secgroup)
2418
2419
2420@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
2421def do_list_secgroup(cs, args):
2422 """List Security Group(s) of a server."""
2423 server = _find_server(cs, args.server)
2424 groups = server.list_security_group()
2425 _print_secgroups(groups)
2426
2427
2428@cliutils.arg(
2429 'pool',
2430 metavar='<floating-ip-pool>',
2431 help=_('Name of Floating IP Pool. (Optional)'),
2432 nargs='?',
2433 default=None)
2434def do_floating_ip_create(cs, args):
2435 """Allocate a floating IP for the current tenant."""
2436 _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)])
2437
2438
2439@cliutils.arg('address', metavar='<address>', help=_('IP of Floating IP.'))
2440def do_floating_ip_delete(cs, args):
2441 """De-allocate a floating IP."""
2442 floating_ips = cs.floating_ips.list()
2443 for floating_ip in floating_ips:
2444 if floating_ip.ip == args.address:
2445 return cs.floating_ips.delete(floating_ip.id)
2446 raise exceptions.CommandError(_("Floating IP %s not found.") %
2447 args.address)
2448
2449
2450@cliutils.arg(
2451 '--all-tenants',
2452 action='store_true',
2453 default=False,
2454 help=_('Display floatingips from all tenants (Admin only).'))
2455def do_floating_ip_list(cs, args):
2456 """List floating IPs."""
2457 _print_floating_ip_list(cs.floating_ips.list(args.all_tenants))
2458
2459
2460def do_floating_ip_pool_list(cs, _args):
2461 """List all floating IP pools."""
2462 utils.print_list(cs.floating_ip_pools.list(), ['name'])
2463
2464
2465@cliutils.arg(
2466 '--host', dest='host', metavar='<host>', default=None,
2467 help=_('Filter by host'))
2468def do_floating_ip_bulk_list(cs, args):
2469 """List all floating IPs."""
2470 utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id',
2471 'address',
2472 'instance_uuid',
2473 'pool',
2474 'interface'])
2475
2476
2477@cliutils.arg('ip_range', metavar='<range>', help=_('Address range to create'))
2478@cliutils.arg(
2479 '--pool', dest='pool', metavar='<pool>', default=None,
2480 help=_('Pool for new Floating IPs'))
2481@cliutils.arg(
2482 '--interface', metavar='<interface>', default=None,
2483 help=_('Interface for new Floating IPs'))
2484def do_floating_ip_bulk_create(cs, args):
2485 """Bulk create floating IPs by range."""
2486 cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface)
2487
2488
2489@cliutils.arg('ip_range', metavar='<range>', help=_('Address range to delete'))
2490def do_floating_ip_bulk_delete(cs, args):
2491 """Bulk delete floating IPs by range."""
2492 cs.floating_ips_bulk.delete(args.ip_range)
2493
2494
2495def _print_dns_list(dns_entries):
2496 utils.print_list(dns_entries, ['ip', 'name', 'domain'])
2497
2498
2499def _print_domain_list(domain_entries):
2500 utils.print_list(domain_entries, ['domain', 'scope',
2501 'project', 'availability_zone'])
2502
2503
2504def do_dns_domains(cs, args):
2505 """Print a list of available dns domains."""
2506 domains = cs.dns_domains.domains()
2507 _print_domain_list(domains)
2508
2509
2510@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2511@cliutils.arg('--ip', metavar='<ip>', help=_('IP address'), default=None)
2512@cliutils.arg('--name', metavar='<name>', help=_('DNS name'), default=None)
2513def do_dns_list(cs, args):
2514 """List current DNS entries for domain and IP or domain and name."""
2515 if not (args.ip or args.name):
2516 raise exceptions.CommandError(
2517 _("You must specify either --ip or --name"))
2518 if args.name:
2519 entry = cs.dns_entries.get(args.domain, args.name)
2520 _print_dns_list([entry])
2521 else:
2522 entries = cs.dns_entries.get_for_ip(args.domain,
2523 ip=args.ip)
2524 _print_dns_list(entries)
2525
2526
2527@cliutils.arg('ip', metavar='<ip>', help=_('IP address'))
2528@cliutils.arg('name', metavar='<name>', help=_('DNS name'))
2529@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2530@cliutils.arg(
2531 '--type',
2532 metavar='<type>',
2533 help=_('dns type (e.g. "A")'),
2534 default='A')
2535def do_dns_create(cs, args):
2536 """Create a DNS entry for domain, name and IP."""
2537 cs.dns_entries.create(args.domain, args.name, args.ip, args.type)
2538
2539
2540@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2541@cliutils.arg('name', metavar='<name>', help=_('DNS name'))
2542def do_dns_delete(cs, args):
2543 """Delete the specified DNS entry."""
2544 cs.dns_entries.delete(args.domain, args.name)
2545
2546
2547@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2548def do_dns_delete_domain(cs, args):
2549 """Delete the specified DNS domain."""
2550 cs.dns_domains.delete(args.domain)
2551
2552
2553@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2554@cliutils.arg(
2555 '--availability-zone',
2556 metavar='<availability-zone>',
2557 default=None,
2558 help=_('Limit access to this domain to servers '
2559 'in the specified availability zone.'))
2560@cliutils.arg(
2561 '--availability_zone',
2562 help=argparse.SUPPRESS)
2563def do_dns_create_private_domain(cs, args):
2564 """Create the specified DNS domain."""
2565 cs.dns_domains.create_private(args.domain,
2566 args.availability_zone)
2567
2568
2569@cliutils.arg('domain', metavar='<domain>', help=_('DNS domain'))
2570@cliutils.arg(
2571 '--project', metavar='<project>',
2572 help=_('Limit access to this domain to users '
2573 'of the specified project.'),
2574 default=None)
2575def do_dns_create_public_domain(cs, args):
2576 """Create the specified DNS domain."""
2577 cs.dns_domains.create_public(args.domain,
2578 args.project)
2579
2580
2581def _print_secgroup_rules(rules, show_source_group=True):
2582 class FormattedRule(object):
2583 def __init__(self, obj):
2584 items = (obj if isinstance(obj, dict) else obj._info).items()
2585 for k, v in items:
2586 if k == 'ip_range':
2587 v = v.get('cidr')
2588 elif k == 'group':
2589 k = 'source_group'
2590 v = v.get('name')
2591 if v is None:
2592 v = ''
2593
2594 setattr(self, k, v)
2595
2596 rules = [FormattedRule(rule) for rule in rules]
2597 headers = ['IP Protocol', 'From Port', 'To Port', 'IP Range']
2598 if show_source_group:
2599 headers.append('Source Group')
2600 utils.print_list(rules, headers)
2601
2602
2603def _print_secgroups(secgroups):
2604 utils.print_list(secgroups, ['Id', 'Name', 'Description'])
2605
2606
2607def _get_secgroup(cs, secgroup):
2608 # Check secgroup is an ID (nova-network) or UUID (neutron)
2609 if (utils.is_integer_like(encodeutils.safe_encode(secgroup)) or
2610 uuidutils.is_uuid_like(secgroup)):
2611 try:
2612 return cs.security_groups.get(secgroup)
2613 except exceptions.NotFound:
2614 pass
2615
2616 # Check secgroup as a name
2617 match_found = False
2618 for s in cs.security_groups.list():
2619 encoding = (
2620 locale.getpreferredencoding() or sys.stdin.encoding or 'UTF-8')
2621 if not six.PY3:
2622 s.name = s.name.encode(encoding)
2623 if secgroup == s.name:
2624 if match_found is not False:
2625 msg = (_("Multiple security group matches found for name '%s'"
2626 ", use an ID to be more specific.") % secgroup)
2627 raise exceptions.NoUniqueMatch(msg)
2628 match_found = s
2629 if match_found is False:
2630 raise exceptions.CommandError(_("Secgroup ID or name '%s' not found.")
2631 % secgroup)
2632 return match_found
2633
2634
2635@cliutils.arg(
2636 'secgroup',
2637 metavar='<secgroup>',
2638 help=_('ID or name of security group.'))
2639@cliutils.arg(
2640 'ip_proto',
2641 metavar='<ip-proto>',
2642 help=_('IP protocol (icmp, tcp, udp).'))
2643@cliutils.arg(
2644 'from_port',
2645 metavar='<from-port>',
2646 help=_('Port at start of range.'))
2647@cliutils.arg(
2648 'to_port',
2649 metavar='<to-port>',
2650 help=_('Port at end of range.'))
2651@cliutils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
2652def do_secgroup_add_rule(cs, args):
2653 """Add a rule to a security group."""
2654 secgroup = _get_secgroup(cs, args.secgroup)
2655 rule = cs.security_group_rules.create(secgroup.id,
2656 args.ip_proto,
2657 args.from_port,
2658 args.to_port,
2659 args.cidr)
2660 _print_secgroup_rules([rule])
2661
2662
2663@cliutils.arg(
2664 'secgroup',
2665 metavar='<secgroup>',
2666 help=_('ID or name of security group.'))
2667@cliutils.arg(
2668 'ip_proto',
2669 metavar='<ip-proto>',
2670 help=_('IP protocol (icmp, tcp, udp).'))
2671@cliutils.arg(
2672 'from_port',
2673 metavar='<from-port>',
2674 help=_('Port at start of range.'))
2675@cliutils.arg(
2676 'to_port',
2677 metavar='<to-port>',
2678 help=_('Port at end of range.'))
2679@cliutils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
2680def do_secgroup_delete_rule(cs, args):
2681 """Delete a rule from a security group."""
2682 secgroup = _get_secgroup(cs, args.secgroup)
2683 for rule in secgroup.rules:
2684 if (rule['ip_protocol'] and
2685 rule['ip_protocol'].upper() == args.ip_proto.upper() and
2686 rule['from_port'] == int(args.from_port) and
2687 rule['to_port'] == int(args.to_port) and
2688 rule['ip_range']['cidr'] == args.cidr):
2689 _print_secgroup_rules([rule])
2690 return cs.security_group_rules.delete(rule['id'])
2691
2692 raise exceptions.CommandError(_("Rule not found"))
2693
2694
2695@cliutils.arg('name', metavar='<name>', help=_('Name of security group.'))
2696@cliutils.arg(
2697 'description', metavar='<description>',
2698 help=_('Description of security group.'))
2699def do_secgroup_create(cs, args):
2700 """Create a security group."""
2701 secgroup = cs.security_groups.create(args.name, args.description)
2702 _print_secgroups([secgroup])
2703
2704
2705@cliutils.arg(
2706 'secgroup',
2707 metavar='<secgroup>',
2708 help=_('ID or name of security group.'))
2709@cliutils.arg('name', metavar='<name>', help=_('Name of security group.'))
2710@cliutils.arg(
2711 'description', metavar='<description>',
2712 help=_('Description of security group.'))
2713def do_secgroup_update(cs, args):
2714 """Update a security group."""
2715 sg = _get_secgroup(cs, args.secgroup)
2716 secgroup = cs.security_groups.update(sg, args.name, args.description)
2717 _print_secgroups([secgroup])
2718
2719
2720@cliutils.arg(
2721 'secgroup',
2722 metavar='<secgroup>',
2723 help=_('ID or name of security group.'))
2724def do_secgroup_delete(cs, args):
2725 """Delete a security group."""
2726 secgroup = _get_secgroup(cs, args.secgroup)
2727 cs.security_groups.delete(secgroup)
2728 _print_secgroups([secgroup])
2729
2730
2731@cliutils.arg(
2732 '--all-tenants',
2733 dest='all_tenants',
2734 metavar='<0|1>',
2735 nargs='?',
2736 type=int,
2737 const=1,
2738 default=int(strutils.bool_from_string(
2739 os.environ.get("ALL_TENANTS", 'false'), True)),
2740 help=_('Display information from all tenants (Admin only).'))
2741@cliutils.arg(
2742 '--all_tenants',
2743 nargs='?',
2744 type=int,
2745 const=1,
2746 help=argparse.SUPPRESS)
2747def do_secgroup_list(cs, args):
2748 """List security groups for the current tenant."""
2749 search_opts = {'all_tenants': args.all_tenants}
2750 columns = ['Id', 'Name', 'Description']
2751 if args.all_tenants:
2752 columns.append('Tenant_ID')
2753 groups = cs.security_groups.list(search_opts=search_opts)
2754 utils.print_list(groups, columns)
2755
2756
2757@cliutils.arg(
2758 'secgroup',
2759 metavar='<secgroup>',
2760 help=_('ID or name of security group.'))
2761def do_secgroup_list_rules(cs, args):
2762 """List rules for a security group."""
2763 secgroup = _get_secgroup(cs, args.secgroup)
2764 _print_secgroup_rules(secgroup.rules)
2765
2766
2767@cliutils.arg(
2768 'secgroup',
2769 metavar='<secgroup>',
2770 help=_('ID or name of security group.'))
2771@cliutils.arg(
2772 'source_group',
2773 metavar='<source-group>',
2774 help=_('ID or name of source group.'))
2775@cliutils.arg(
2776 'ip_proto',
2777 metavar='<ip-proto>',
2778 help=_('IP protocol (icmp, tcp, udp).'))
2779@cliutils.arg(
2780 'from_port',
2781 metavar='<from-port>',
2782 help=_('Port at start of range.'))
2783@cliutils.arg(
2784 'to_port',
2785 metavar='<to-port>',
2786 help=_('Port at end of range.'))
2787def do_secgroup_add_group_rule(cs, args):
2788 """Add a source group rule to a security group."""
2789 secgroup = _get_secgroup(cs, args.secgroup)
2790 source_group = _get_secgroup(cs, args.source_group)
2791 params = {}
2792 params['group_id'] = source_group.id
2793
2794 if args.ip_proto or args.from_port or args.to_port:
2795 if not (args.ip_proto and args.from_port and args.to_port):
2796 raise exceptions.CommandError(_("ip_proto, from_port, and to_port"
2797 " must be specified together"))
2798 params['ip_protocol'] = args.ip_proto.upper()
2799 params['from_port'] = args.from_port
2800 params['to_port'] = args.to_port
2801
2802 rule = cs.security_group_rules.create(secgroup.id, **params)
2803 _print_secgroup_rules([rule])
2804
2805
2806@cliutils.arg(
2807 'secgroup',
2808 metavar='<secgroup>',
2809 help=_('ID or name of security group.'))
2810@cliutils.arg(
2811 'source_group',
2812 metavar='<source-group>',
2813 help=_('ID or name of source group.'))
2814@cliutils.arg(
2815 'ip_proto',
2816 metavar='<ip-proto>',
2817 help=_('IP protocol (icmp, tcp, udp).'))
2818@cliutils.arg(
2819 'from_port',
2820 metavar='<from-port>',
2821 help=_('Port at start of range.'))
2822@cliutils.arg(
2823 'to_port',
2824 metavar='<to-port>',
2825 help=_('Port at end of range.'))
2826def do_secgroup_delete_group_rule(cs, args):
2827 """Delete a source group rule from a security group."""
2828 secgroup = _get_secgroup(cs, args.secgroup)
2829 source_group = _get_secgroup(cs, args.source_group)
2830 params = {}
2831 params['group_name'] = source_group.name
2832
2833 if args.ip_proto or args.from_port or args.to_port:
2834 if not (args.ip_proto and args.from_port and args.to_port):
2835 raise exceptions.CommandError(_("ip_proto, from_port, and to_port"
2836 " must be specified together"))
2837 params['ip_protocol'] = args.ip_proto.upper()
2838 params['from_port'] = int(args.from_port)
2839 params['to_port'] = int(args.to_port)
2840
2841 for rule in secgroup.rules:
2842 if (rule.get('ip_protocol') and
2843 rule['ip_protocol'].upper() == params.get(
2844 'ip_protocol').upper() and
2845 rule.get('from_port') == params.get('from_port') and
2846 rule.get('to_port') == params.get('to_port') and
2847 rule.get('group', {}).get('name') == params.get('group_name')):
2848 return cs.security_group_rules.delete(rule['id'])
2849
2850 raise exceptions.CommandError(_("Rule not found"))
2851
2852
2853@cliutils.arg('name', metavar='<name>', help=_('Name of key.'))
2854@cliutils.arg(
2855 '--pub-key',
2856 metavar='<pub-key>',
2857 default=None,
2858 help=_('Path to a public ssh key.'))
2859@cliutils.arg(
2860 '--pub_key',
2861 help=argparse.SUPPRESS)
2862def do_keypair_add(cs, args):
2863 """Create a new key pair for use with servers."""
2864 name = args.name
2865 pub_key = args.pub_key
2866
2867 if pub_key:
2868 try:
2869 with open(os.path.expanduser(pub_key)) as f:
2870 pub_key = f.read()
2871 except IOError as e:
2872 raise exceptions.CommandError(_("Can't open or read '%(key)s': "
2873 "%(exc)s") % {'key': pub_key,
2874 'exc': e})
2875
2876 keypair = cs.keypairs.create(name, pub_key)
2877
2878 if not pub_key:
2879 private_key = keypair.private_key
2880 print(private_key)
2881
2882
2883@cliutils.arg('name', metavar='<name>', help=_('Keypair name to delete.'))
2884def do_keypair_delete(cs, args):
2885 """Delete keypair given by its name."""
2886 name = _find_keypair(cs, args.name)
2887 cs.keypairs.delete(name)
2888
2889
2890def do_keypair_list(cs, args):
2891 """Print a list of keypairs for a user"""
2892 keypairs = cs.keypairs.list()
2893 columns = ['Name', 'Fingerprint']
2894 utils.print_list(keypairs, columns)
2895
2896
2897def _print_keypair(keypair):
2898 kp = keypair._info.copy()
2899 pk = kp.pop('public_key')
2900 utils.print_dict(kp)
2901 print(_("Public key: %s") % pk)
2902
2903
2904@cliutils.arg(
2905 'keypair',
2906 metavar='<keypair>',
2907 help=_("Name or ID of keypair"))
2908def do_keypair_show(cs, args):
2909 """Show details about the given keypair."""
2910 keypair = _find_keypair(cs, args.keypair)
2911 _print_keypair(keypair)
2912
2913
2914def _find_keypair(cs, keypair):
2915 """Get a keypair by name or ID."""
2916 return utils.find_resource(cs.keypairs, keypair)
2917
2918
2919@cliutils.arg(
2920 '--tenant',
2921 # nova db searches by project_id
2922 dest='tenant',
2923 metavar='<tenant>',
2924 nargs='?',
2925 help=_('Display information from single tenant (Admin only).'))
2926@cliutils.arg(
2927 '--reserved',
2928 dest='reserved',
2929 action='store_true',
2930 default=False,
2931 help=_('Include reservations count.'))
2932def do_absolute_limits(cs, args):
2933 """Print a list of absolute limits for a user"""
2934 limits = cs.limits.get(args.reserved, args.tenant).absolute
2935
2936 class Limit(object):
2937 def __init__(self, name, used, max, other):
2938 self.name = name
2939 self.used = used
2940 self.max = max
2941 self.other = other
2942
2943 limit_map = {
2944 'maxServerMeta': {'name': 'Server Meta', 'type': 'max'},
2945 'maxPersonality': {'name': 'Personality', 'type': 'max'},
2946 'maxPersonalitySize': {'name': 'Personality Size', 'type': 'max'},
2947 'maxImageMeta': {'name': 'ImageMeta', 'type': 'max'},
2948 'maxTotalKeypairs': {'name': 'Keypairs', 'type': 'max'},
2949 'totalCoresUsed': {'name': 'Cores', 'type': 'used'},
2950 'maxTotalCores': {'name': 'Cores', 'type': 'max'},
2951 'totalRAMUsed': {'name': 'RAM', 'type': 'used'},
2952 'maxTotalRAMSize': {'name': 'RAM', 'type': 'max'},
2953 'totalInstancesUsed': {'name': 'Instances', 'type': 'used'},
2954 'maxTotalInstances': {'name': 'Instances', 'type': 'max'},
2955 'totalFloatingIpsUsed': {'name': 'FloatingIps', 'type': 'used'},
2956 'maxTotalFloatingIps': {'name': 'FloatingIps', 'type': 'max'},
2957 'totalSecurityGroupsUsed': {'name': 'SecurityGroups', 'type': 'used'},
2958 'maxSecurityGroups': {'name': 'SecurityGroups', 'type': 'max'},
2959 'maxSecurityGroupRules': {'name': 'SecurityGroupRules', 'type': 'max'},
2960 'maxServerGroups': {'name': 'ServerGroups', 'type': 'max'},
2961 'totalServerGroupsUsed': {'name': 'ServerGroups', 'type': 'used'},
2962 'maxServerGroupMembers': {'name': 'ServerGroupMembers', 'type': 'max'},
2963 }
2964
2965 max = {}
2966 used = {}
2967 other = {}
2968 limit_names = []
2969 columns = ['Name', 'Used', 'Max']
2970 for l in limits:
2971 map = limit_map.get(l.name, {'name': l.name, 'type': 'other'})
2972 name = map['name']
2973 if map['type'] == 'max':
2974 max[name] = l.value
2975 elif map['type'] == 'used':
2976 used[name] = l.value
2977 else:
2978 other[name] = l.value
2979 columns.append('Other')
2980 if name not in limit_names:
2981 limit_names.append(name)
2982
2983 limit_names.sort()
2984
2985 limit_list = []
2986 for name in limit_names:
2987 l = Limit(name,
2988 used.get(name, "-"),
2989 max.get(name, "-"),
2990 other.get(name, "-"))
2991 limit_list.append(l)
2992
2993 utils.print_list(limit_list, columns)
2994
2995
2996def do_rate_limits(cs, args):
2997 """Print a list of rate limits for a user"""
2998 limits = cs.limits.get().rate
2999 columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
3000 utils.print_list(limits, columns)
3001
3002
3003@cliutils.arg(
3004 '--start',
3005 metavar='<start>',
3006 help=_('Usage range start date ex 2012-01-20 (default: 4 weeks ago)'),
3007 default=None)
3008@cliutils.arg(
3009 '--end',
3010 metavar='<end>',
3011 help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'),
3012 default=None)
3013def do_usage_list(cs, args):
3014 """List usage data for all tenants."""
3015 dateformat = "%Y-%m-%d"
3016 rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours",
3017 "Disk GB-Hours"]
3018
3019 now = timeutils.utcnow()
3020
3021 if args.start:
3022 start = datetime.datetime.strptime(args.start, dateformat)
3023 else:
3024 start = now - datetime.timedelta(weeks=4)
3025
3026 if args.end:
3027 end = datetime.datetime.strptime(args.end, dateformat)
3028 else:
3029 end = now + datetime.timedelta(days=1)
3030
3031 def simplify_usage(u):
3032 simplerows = [x.lower().replace(" ", "_") for x in rows]
3033
3034 setattr(u, simplerows[0], u.tenant_id)
3035 setattr(u, simplerows[1], "%d" % len(u.server_usages))
3036 setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage)
3037 setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage)
3038 setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage)
3039
3040 usage_list = cs.usage.list(start, end, detailed=True)
3041
3042 print(_("Usage from %(start)s to %(end)s:") %
3043 {'start': start.strftime(dateformat),
3044 'end': end.strftime(dateformat)})
3045
3046 for usage in usage_list:
3047 simplify_usage(usage)
3048
3049 utils.print_list(usage_list, rows)
3050
3051
3052@cliutils.arg(
3053 '--start',
3054 metavar='<start>',
3055 help=_('Usage range start date ex 2012-01-20 (default: 4 weeks ago)'),
3056 default=None)
3057@cliutils.arg(
3058 '--end', metavar='<end>',
3059 help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'),
3060 default=None)
3061@cliutils.arg(
3062 '--tenant',
3063 metavar='<tenant-id>',
3064 default=None,
3065 help=_('UUID or name of tenant to get usage for.'))
3066def do_usage(cs, args):
3067 """Show usage data for a single tenant."""
3068 dateformat = "%Y-%m-%d"
3069 rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"]
3070
3071 now = timeutils.utcnow()
3072
3073 if args.start:
3074 start = datetime.datetime.strptime(args.start, dateformat)
3075 else:
3076 start = now - datetime.timedelta(weeks=4)
3077
3078 if args.end:
3079 end = datetime.datetime.strptime(args.end, dateformat)
3080 else:
3081 end = now + datetime.timedelta(days=1)
3082
3083 def simplify_usage(u):
3084 simplerows = [x.lower().replace(" ", "_") for x in rows]
3085
3086 setattr(u, simplerows[0], "%d" % len(u.server_usages))
3087 setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage)
3088 setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage)
3089 setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage)
3090
3091 if args.tenant:
3092 usage = cs.usage.get(args.tenant, start, end)
3093 else:
3094 if isinstance(cs.client, client.SessionClient):
3095 auth = cs.client.auth
3096 project_id = auth.get_auth_ref(cs.client.session).project_id
3097 usage = cs.usage.get(project_id, start, end)
3098 else:
3099 usage = cs.usage.get(cs.client.tenant_id, start, end)
3100
3101 print(_("Usage from %(start)s to %(end)s:") %
3102 {'start': start.strftime(dateformat),
3103 'end': end.strftime(dateformat)})
3104
3105 if getattr(usage, 'total_vcpus_usage', None):
3106 simplify_usage(usage)
3107 utils.print_list([usage], rows)
3108 else:
3109 print(_('None'))
3110
3111
3112@cliutils.arg(
3113 'pk_filename',
3114 metavar='<private-key-filename>',
3115 nargs='?',
3116 default='pk.pem',
3117 help=_('Filename for the private key [Default: pk.pem]'))
3118@cliutils.arg(
3119 'cert_filename',
3120 metavar='<x509-cert-filename>',
3121 nargs='?',
3122 default='cert.pem',
3123 help=_('Filename for the X.509 certificate [Default: cert.pem]'))
3124def do_x509_create_cert(cs, args):
3125 """Create x509 cert for a user in tenant."""
3126
3127 if os.path.exists(args.pk_filename):
3128 raise exceptions.CommandError(_("Unable to write privatekey - %s "
3129 "exists.") % args.pk_filename)
3130 if os.path.exists(args.cert_filename):
3131 raise exceptions.CommandError(_("Unable to write x509 cert - %s "
3132 "exists.") % args.cert_filename)
3133
3134 certs = cs.certs.create()
3135
3136 try:
3137 old_umask = os.umask(0o377)
3138 with open(args.pk_filename, 'w') as private_key:
3139 private_key.write(certs.private_key)
3140 print(_("Wrote private key to %s") % args.pk_filename)
3141 finally:
3142 os.umask(old_umask)
3143
3144 with open(args.cert_filename, 'w') as cert:
3145 cert.write(certs.data)
3146 print(_("Wrote x509 certificate to %s") % args.cert_filename)
3147
3148
3149@cliutils.arg(
3150 'filename',
3151 metavar='<filename>',
3152 nargs='?',
3153 default='cacert.pem',
3154 help=_('Filename to write the x509 root cert.'))
3155def do_x509_get_root_cert(cs, args):
3156 """Fetch the x509 root cert."""
3157 if os.path.exists(args.filename):
3158 raise exceptions.CommandError(_("Unable to write x509 root cert - \
3159 %s exists.") % args.filename)
3160
3161 with open(args.filename, 'w') as cert:
3162 cacert = cs.certs.get()
3163 cert.write(cacert.data)
3164 print(_("Wrote x509 root cert to %s") % args.filename)
3165
3166
3167@cliutils.arg(
3168 '--hypervisor',
3169 metavar='<hypervisor>',
3170 default=None,
3171 help=_('type of hypervisor.'))
3172def do_agent_list(cs, args):
3173 """List all builds."""
3174 result = cs.agents.list(args.hypervisor)
3175 columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version",
3176 'Md5hash', 'Url']
3177 utils.print_list(result, columns)
3178
3179
3180@cliutils.arg('os', metavar='<os>', help=_('type of os.'))
3181@cliutils.arg(
3182 'architecture',
3183 metavar='<architecture>',
3184 help=_('type of architecture'))
3185@cliutils.arg('version', metavar='<version>', help=_('version'))
3186@cliutils.arg('url', metavar='<url>', help=_('url'))
3187@cliutils.arg('md5hash', metavar='<md5hash>', help=_('md5 hash'))
3188@cliutils.arg(
3189 'hypervisor',
3190 metavar='<hypervisor>',
3191 default='xen',
3192 help=_('type of hypervisor.'))
3193def do_agent_create(cs, args):
3194 """Create new agent build."""
3195 result = cs.agents.create(args.os, args.architecture,
3196 args.version, args.url,
3197 args.md5hash, args.hypervisor)
3198 utils.print_dict(result._info.copy())
3199
3200
3201@cliutils.arg('id', metavar='<id>', help=_('id of the agent-build'))
3202def do_agent_delete(cs, args):
3203 """Delete existing agent build."""
3204 cs.agents.delete(args.id)
3205
3206
3207@cliutils.arg('id', metavar='<id>', help=_('id of the agent-build'))
3208@cliutils.arg('version', metavar='<version>', help=_('version'))
3209@cliutils.arg('url', metavar='<url>', help=_('url'))
3210@cliutils.arg('md5hash', metavar='<md5hash>', help=_('md5hash'))
3211def do_agent_modify(cs, args):
3212 """Modify existing agent build."""
3213 result = cs.agents.update(args.id, args.version,
3214 args.url, args.md5hash)
3215 utils.print_dict(result._info)
3216
3217
3218def _find_aggregate(cs, aggregate):
3219 """Get a aggregate by name or ID."""
3220 return utils.find_resource(cs.aggregates, aggregate)
3221
3222
3223def do_aggregate_list(cs, args):
3224 """Print a list of all aggregates."""
3225 aggregates = cs.aggregates.list()
3226 columns = ['Id', 'Name', 'Availability Zone']
3227 utils.print_list(aggregates, columns)
3228
3229
3230@cliutils.arg('name', metavar='<name>', help=_('Name of aggregate.'))
3231@cliutils.arg(
3232 'availability_zone',
3233 metavar='<availability-zone>',
3234 default=None,
3235 nargs='?',
3236 help=_('The availability zone of the aggregate (optional).'))
3237def do_aggregate_create(cs, args):
3238 """Create a new aggregate with the specified details."""
3239 aggregate = cs.aggregates.create(args.name, args.availability_zone)
3240 _print_aggregate_details(aggregate)
3241
3242
3243@cliutils.arg(
3244 'aggregate',
3245 metavar='<aggregate>',
3246 help=_('Name or ID of aggregate to delete.'))
3247def do_aggregate_delete(cs, args):
3248 """Delete the aggregate."""
3249 aggregate = _find_aggregate(cs, args.aggregate)
3250 cs.aggregates.delete(aggregate)
3251 print(_("Aggregate %s has been successfully deleted.") % aggregate.id)
3252
3253
3254@cliutils.arg(
3255 'aggregate',
3256 metavar='<aggregate>',
3257 help=_('Name or ID of aggregate to update.'))
3258@cliutils.arg('name', metavar='<name>', help=_('Name of aggregate.'))
3259@cliutils.arg(
3260 'availability_zone',
3261 metavar='<availability-zone>',
3262 nargs='?',
3263 default=None,
3264 help=_('The availability zone of the aggregate.'))
3265def do_aggregate_update(cs, args):
3266 """Update the aggregate's name and optionally availability zone."""
3267 aggregate = _find_aggregate(cs, args.aggregate)
3268 updates = {"name": args.name}
3269 if args.availability_zone:
3270 updates["availability_zone"] = args.availability_zone
3271
3272 aggregate = cs.aggregates.update(aggregate.id, updates)
3273 print(_("Aggregate %s has been successfully updated.") % aggregate.id)
3274 _print_aggregate_details(aggregate)
3275
3276
3277@cliutils.arg(
3278 'aggregate', metavar='<aggregate>',
3279 help=_('Name or ID of aggregate to update.'))
3280@cliutils.arg(
3281 'metadata',
3282 metavar='<key=value>',
3283 nargs='+',
3284 action='append',
3285 default=[],
3286 help=_('Metadata to add/update to aggregate. '
3287 'Specify only the key to delete a metadata item.'))
3288def do_aggregate_set_metadata(cs, args):
3289 """Update the metadata associated with the aggregate."""
3290 aggregate = _find_aggregate(cs, args.aggregate)
3291 metadata = _extract_metadata(args)
3292 currentmetadata = getattr(aggregate, 'metadata', {})
3293 if set(metadata.items()) & set(currentmetadata.items()):
3294 raise exceptions.CommandError(_("metadata already exists"))
3295 for key, value in metadata.items():
3296 if value is None and key not in currentmetadata:
3297 raise exceptions.CommandError(_("metadata key %s does not exist"
3298 " hence can not be deleted")
3299 % key)
3300 aggregate = cs.aggregates.set_metadata(aggregate.id, metadata)
3301 print(_("Metadata has been successfully updated for aggregate %s.") %
3302 aggregate.id)
3303 _print_aggregate_details(aggregate)
3304
3305
3306@cliutils.arg(
3307 'aggregate', metavar='<aggregate>',
3308 help=_('Name or ID of aggregate.'))
3309@cliutils.arg(
3310 'host', metavar='<host>',
3311 help=_('The host to add to the aggregate.'))
3312def do_aggregate_add_host(cs, args):
3313 """Add the host to the specified aggregate."""
3314 aggregate = _find_aggregate(cs, args.aggregate)
3315 aggregate = cs.aggregates.add_host(aggregate.id, args.host)
3316 print(_("Host %(host)s has been successfully added for aggregate "
3317 "%(aggregate_id)s ") % {'host': args.host,
3318 'aggregate_id': aggregate.id})
3319 _print_aggregate_details(aggregate)
3320
3321
3322@cliutils.arg(
3323 'aggregate', metavar='<aggregate>',
3324 help=_('Name or ID of aggregate.'))
3325@cliutils.arg(
3326 'host', metavar='<host>',
3327 help=_('The host to remove from the aggregate.'))
3328def do_aggregate_remove_host(cs, args):
3329 """Remove the specified host from the specified aggregate."""
3330 aggregate = _find_aggregate(cs, args.aggregate)
3331 aggregate = cs.aggregates.remove_host(aggregate.id, args.host)
3332 print(_("Host %(host)s has been successfully removed from aggregate "
3333 "%(aggregate_id)s ") % {'host': args.host,
3334 'aggregate_id': aggregate.id})
3335 _print_aggregate_details(aggregate)
3336
3337
3338@cliutils.arg(
3339 'aggregate', metavar='<aggregate>',
3340 help=_('Name or ID of aggregate.'))
3341def do_aggregate_details(cs, args):
3342 """Show details of the specified aggregate."""
3343 aggregate = _find_aggregate(cs, args.aggregate)
3344 _print_aggregate_details(aggregate)
3345
3346
3347def _print_aggregate_details(aggregate):
3348 columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata']
3349
3350 def parser_metadata(fields):
3351 return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {})
3352
3353 def parser_hosts(fields):
3354 return cliutils.pretty_choice_list(getattr(fields, 'hosts', []))
3355
3356 formatters = {
3357 'Metadata': parser_metadata,
3358 'Hosts': parser_hosts,
3359 }
3360 utils.print_list([aggregate], columns, formatters=formatters)
3361
3362
3363@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
3364@cliutils.arg(
3365 'host', metavar='<host>', default=None, nargs='?',
3366 help=_('destination host name.'))
3367@cliutils.arg(
3368 '--block-migrate',
3369 action='store_true',
3370 dest='block_migrate',
3371 default=False,
3372 help=_('True in case of block_migration. (Default=False:live_migration)'))
3373@cliutils.arg(
3374 '--block_migrate',
3375 action='store_true',
3376 help=argparse.SUPPRESS)
3377@cliutils.arg(
3378 '--disk-over-commit',
3379 action='store_true',
3380 dest='disk_over_commit',
3381 default=False,
3382 help=_('Allow overcommit.(Default=False)'))
3383@cliutils.arg(
3384 '--disk_over_commit',
3385 action='store_true',
3386 help=argparse.SUPPRESS)
3387def do_live_migration(cs, args):
3388 """Migrate running server to a new machine."""
3389 _find_server(cs, args.server).live_migrate(args.host,
3390 args.block_migrate,
3391 args.disk_over_commit)
3392
3393
3394@cliutils.arg(
3395 'server', metavar='<server>', nargs='+',
3396 help=_('Name or ID of server(s).'))
3397@cliutils.arg(
3398 '--active', action='store_const', dest='state',
3399 default='error', const='active',
3400 help=_('Request the server be reset to "active" state instead '
3401 'of "error" state (the default).'))
3402def do_reset_state(cs, args):
3403 """Reset the state of a server."""
3404 failure_flag = False
3405
3406 for server in args.server:
3407 try:
3408 _find_server(cs, server).reset_state(args.state)
3409 except Exception as e:
3410 failure_flag = True
3411 msg = "Reset state for server %s failed: %s" % (server, e)
3412 print(msg)
3413
3414 if failure_flag:
3415 msg = "Unable to reset the state for the specified server(s)."
3416 raise exceptions.CommandError(msg)
3417
3418
3419@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
3420def do_reset_network(cs, args):
3421 """Reset network of a server."""
3422 _find_server(cs, args.server).reset_network()
3423
3424
3425@cliutils.arg(
3426 '--host',
3427 metavar='<hostname>',
3428 default=None,
3429 help=_('Name of host.'))
3430@cliutils.arg(
3431 '--binary',
3432 metavar='<binary>',
3433 default=None,
3434 help=_('Service binary.'))
3435def do_service_list(cs, args):
3436 """Show a list of all running services. Filter by host & binary."""
3437 result = cs.services.list(host=args.host, binary=args.binary)
3438 columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"]
3439 # NOTE(sulo): we check if the response has disabled_reason
3440 # so as not to add the column when the extended ext is not enabled.
3441 if result and hasattr(result[0], 'disabled_reason'):
3442 columns.append("Disabled Reason")
3443
3444 # NOTE(gtt): After https://review.openstack.org/#/c/39998/ nova will
3445 # show id in response.
3446 if result and hasattr(result[0], 'id'):
3447 columns.insert(0, "Id")
3448
3449 utils.print_list(result, columns)
3450
3451
3452@cliutils.arg('host', metavar='<hostname>', help=_('Name of host.'))
3453@cliutils.arg('binary', metavar='<binary>', help=_('Service binary.'))
3454def do_service_enable(cs, args):
3455 """Enable the service."""
3456 result = cs.services.enable(args.host, args.binary)
3457 utils.print_list([result], ['Host', 'Binary', 'Status'])
3458
3459
3460@cliutils.arg('host', metavar='<hostname>', help=_('Name of host.'))
3461@cliutils.arg('binary', metavar='<binary>', help=_('Service binary.'))
3462@cliutils.arg(
3463 '--reason',
3464 metavar='<reason>',
3465 help=_('Reason for disabling service.'))
3466def do_service_disable(cs, args):
3467 """Disable the service."""
3468 if args.reason:
3469 result = cs.services.disable_log_reason(args.host, args.binary,
3470 args.reason)
3471 utils.print_list([result], ['Host', 'Binary', 'Status',
3472 'Disabled Reason'])
3473 else:
3474 result = cs.services.disable(args.host, args.binary)
3475 utils.print_list([result], ['Host', 'Binary', 'Status'])
3476
3477
3478@cliutils.arg('id', metavar='<id>', help=_('Id of service.'))
3479def do_service_delete(cs, args):
3480 """Delete the service."""
3481 cs.services.delete(args.id)
3482
3483
3484@cliutils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
3485def do_fixed_ip_get(cs, args):
3486 """Retrieve info on a fixed IP."""
3487 result = cs.fixed_ips.get(args.fixed_ip)
3488 utils.print_list([result], ['address', 'cidr', 'hostname', 'host'])
3489
3490
3491@cliutils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
3492def do_fixed_ip_reserve(cs, args):
3493 """Reserve a fixed IP."""
3494 cs.fixed_ips.reserve(args.fixed_ip)
3495
3496
3497@cliutils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
3498def do_fixed_ip_unreserve(cs, args):
3499 """Unreserve a fixed IP."""
3500 cs.fixed_ips.unreserve(args.fixed_ip)
3501
3502
3503@cliutils.arg('host', metavar='<hostname>', help=_('Name of host.'))
3504def do_host_describe(cs, args):
3505 """Describe a specific host."""
3506 result = cs.hosts.get(args.host)
3507 columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"]
3508 utils.print_list(result, columns)
3509
3510
3511@cliutils.arg(
3512 '--zone',
3513 metavar='<zone>',
3514 default=None,
3515 help=_('Filters the list, returning only those hosts in the availability '
3516 'zone <zone>.'))
3517def do_host_list(cs, args):
3518 """List all hosts by service."""
3519 columns = ["host_name", "service", "zone"]
3520 result = cs.hosts.list(args.zone)
3521 utils.print_list(result, columns)
3522
3523
3524@cliutils.arg('host', metavar='<hostname>', help='Name of host.')
3525@cliutils.arg(
3526 '--status', metavar='<enable|disable>', default=None, dest='status',
3527 help=_('Either enable or disable a host.'))
3528@cliutils.arg(
3529 '--maintenance',
3530 metavar='<enable|disable>',
3531 default=None,
3532 dest='maintenance',
3533 help=_('Either put or resume host to/from maintenance.'))
3534def do_host_update(cs, args):
3535 """Update host settings."""
3536 updates = {}
3537 columns = ["HOST"]
3538 if args.status:
3539 updates['status'] = args.status
3540 columns.append("status")
3541 if args.maintenance:
3542 updates['maintenance_mode'] = args.maintenance
3543 columns.append("maintenance_mode")
3544 result = cs.hosts.update(args.host, updates)
3545 utils.print_list([result], columns)
3546
3547
3548@cliutils.arg('host', metavar='<hostname>', help='Name of host.')
3549@cliutils.arg(
3550 '--action', metavar='<action>', dest='action',
3551 choices=['startup', 'shutdown', 'reboot'],
3552 help=_('A power action: startup, reboot, or shutdown.'))
3553def do_host_action(cs, args):
3554 """Perform a power action on a host."""
3555 result = cs.hosts.host_action(args.host, args.action)
3556 utils.print_list([result], ['HOST', 'power_action'])
3557
3558
3559def _find_hypervisor(cs, hypervisor):
3560 """Get a hypervisor by name or ID."""
3561 return utils.find_resource(cs.hypervisors, hypervisor)
3562
3563
3564@cliutils.arg(
3565 '--matching',
3566 metavar='<hostname>',
3567 default=None,
3568 help=_('List hypervisors matching the given <hostname>.'))
3569def do_hypervisor_list(cs, args):
3570 """List hypervisors."""
3571 columns = ['ID', 'Hypervisor hostname', 'State', 'Status']
3572 if args.matching:
3573 utils.print_list(cs.hypervisors.search(args.matching), columns)
3574 else:
3575 # Since we're not outputting detail data, choose
3576 # detailed=False for server-side efficiency
3577 utils.print_list(cs.hypervisors.list(False), columns)
3578
3579
3580@cliutils.arg(
3581 'hostname',
3582 metavar='<hostname>',
3583 help=_('The hypervisor hostname (or pattern) to search for.'))
3584def do_hypervisor_servers(cs, args):
3585 """List servers belonging to specific hypervisors."""
3586 hypers = cs.hypervisors.search(args.hostname, servers=True)
3587
3588 class InstanceOnHyper(object):
3589 def __init__(self, **kwargs):
3590 self.__dict__.update(kwargs)
3591
3592 # Massage the result into a list to be displayed
3593 instances = []
3594 for hyper in hypers:
3595 hyper_host = hyper.hypervisor_hostname
3596 hyper_id = hyper.id
3597 if hasattr(hyper, 'servers'):
3598 instances.extend([InstanceOnHyper(id=serv['uuid'],
3599 name=serv['name'],
3600 hypervisor_hostname=hyper_host,
3601 hypervisor_id=hyper_id)
3602 for serv in hyper.servers])
3603
3604 # Output the data
3605 utils.print_list(instances, ['ID', 'Name', 'Hypervisor ID',
3606 'Hypervisor Hostname'])
3607
3608
3609@cliutils.arg(
3610 'hypervisor',
3611 metavar='<hypervisor>',
3612 help=_('Name or ID of the hypervisor to show the details of.'))
3613def do_hypervisor_show(cs, args):
3614 """Display the details of the specified hypervisor."""
3615 hyper = _find_hypervisor(cs, args.hypervisor)
3616 utils.print_dict(utils.flatten_dict(hyper._info))
3617
3618
3619@cliutils.arg(
3620 'hypervisor',
3621 metavar='<hypervisor>',
3622 help=_('Name or ID of the hypervisor to show the uptime of.'))
3623def do_hypervisor_uptime(cs, args):
3624 """Display the uptime of the specified hypervisor."""
3625 hyper = _find_hypervisor(cs, args.hypervisor)
3626 hyper = cs.hypervisors.uptime(hyper)
3627
3628 # Output the uptime information
3629 utils.print_dict(hyper._info.copy())
3630
3631
3632def do_hypervisor_stats(cs, args):
3633 """Get hypervisor statistics over all compute nodes."""
3634 stats = cs.hypervisor_stats.statistics()
3635 utils.print_dict(stats._info.copy())
3636
3637
3638def ensure_service_catalog_present(cs):
3639 if not hasattr(cs.client, 'service_catalog'):
3640 # Turn off token caching and re-auth
3641 cs.client.unauthenticate()
3642 cs.client.use_token_cache(False)
3643 cs.client.authenticate()
3644
3645
3646def do_endpoints(cs, _args):
3647 """Discover endpoints that get returned from the authenticate services."""
3648 if isinstance(cs.client, client.SessionClient):
3649 auth = cs.client.auth
3650 sc = auth.get_access(cs.client.session).service_catalog
3651 for service in sc.get_data():
3652 _print_endpoints(service, cs.client.region_name)
3653 else:
3654 ensure_service_catalog_present(cs)
3655
3656 catalog = cs.client.service_catalog.catalog
3657 region = cs.client.region_name
3658 for service in catalog['access']['serviceCatalog']:
3659 _print_endpoints(service, region)
3660
3661
3662def _print_endpoints(service, region):
3663 name, endpoints = service["name"], service["endpoints"]
3664
3665 try:
3666 endpoint = _get_first_endpoint(endpoints, region)
3667 utils.print_dict(endpoint, name)
3668 except LookupError:
3669 print(_("WARNING: %(service)s has no endpoint in %(region)s! "
3670 "Available endpoints for this service:") %
3671 {'service': name, 'region': region})
3672 for other_endpoint in endpoints:
3673 utils.print_dict(other_endpoint, name)
3674
3675
3676def _get_first_endpoint(endpoints, region):
3677 """Find the first suitable endpoint in endpoints.
3678
3679 If there is only one endpoint, return it. If there is more than
3680 one endpoint, return the first one with the given region. If there
3681 are no endpoints, or there is more than one endpoint but none of
3682 them match the given region, raise KeyError.
3683
3684 """
3685 if len(endpoints) == 1:
3686 return endpoints[0]
3687 else:
3688 for candidate_endpoint in endpoints:
3689 if candidate_endpoint["region"] == region:
3690 return candidate_endpoint
3691
3692 raise LookupError("No suitable endpoint found")
3693
3694
3695@cliutils.arg(
3696 '--wrap', dest='wrap', metavar='<integer>', default=64,
3697 help=_('wrap PKI tokens to a specified length, or 0 to disable'))
3698def do_credentials(cs, _args):
3699 """Show user credentials returned from auth."""
3700 if isinstance(cs.client, client.SessionClient):
3701 auth = cs.client.auth
3702 sc = auth.get_access(cs.client.session).service_catalog
3703 utils.print_dict(sc.catalog['user'], 'User Credentials',
3704 wrap=int(_args.wrap))
3705 utils.print_dict(sc.get_token(), 'Token', wrap=int(_args.wrap))
3706 else:
3707 ensure_service_catalog_present(cs)
3708 catalog = cs.client.service_catalog.catalog
3709 utils.print_dict(catalog['access']['user'], "User Credentials",
3710 wrap=int(_args.wrap))
3711 utils.print_dict(catalog['access']['token'], "Token",
3712 wrap=int(_args.wrap))
3713
3714
3715@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
3716@cliutils.arg(
3717 '--port',
3718 dest='port',
3719 action='store',
3720 type=int,
3721 default=22,
3722 help=_('Optional flag to indicate which port to use for ssh. '
3723 '(Default=22)'))
3724@cliutils.arg(
3725 '--private',
3726 dest='private',
3727 action='store_true',
3728 default=False,
3729 help=argparse.SUPPRESS)
3730@cliutils.arg(
3731 '--address-type',
3732 dest='address_type',
3733 action='store',
3734 type=str,
3735 default='floating',
3736 help=_('Optional flag to indicate which IP type to use. Possible values '
3737 'includes fixed and floating (the Default).'))
3738@cliutils.arg(
3739 '--network', metavar='<network>',
3740 help=_('Network to use for the ssh.'), default=None)
3741@cliutils.arg(
3742 '--ipv6',
3743 dest='ipv6',
3744 action='store_true',
3745 default=False,
3746 help=_('Optional flag to indicate whether to use an IPv6 address '
3747 'attached to a server. (Defaults to IPv4 address)'))
3748@cliutils.arg(
3749 '--login', metavar='<login>', help=_('Login to use.'),
3750 default="root")
3751@cliutils.arg(
3752 '-i', '--identity',
3753 dest='identity',