fb-vmware 1.7.1__py3-none-any.whl → 1.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. fb_vmware/__init__.py +1 -1
  2. fb_vmware/app/__init__.py +285 -6
  3. fb_vmware/app/get_host_list.py +115 -100
  4. fb_vmware/app/get_network_list.py +176 -218
  5. fb_vmware/app/get_rpool_list.py +386 -0
  6. fb_vmware/app/get_storage_cluster_info.py +303 -0
  7. fb_vmware/app/get_storage_cluster_list.py +100 -107
  8. fb_vmware/app/get_storage_list.py +145 -112
  9. fb_vmware/app/get_vm_info.py +79 -17
  10. fb_vmware/app/get_vm_list.py +169 -95
  11. fb_vmware/app/search_storage.py +470 -0
  12. fb_vmware/argparse_actions.py +78 -0
  13. fb_vmware/base.py +28 -1
  14. fb_vmware/cluster.py +99 -7
  15. fb_vmware/connect.py +450 -20
  16. fb_vmware/datastore.py +195 -6
  17. fb_vmware/dc.py +19 -1
  18. fb_vmware/ds_cluster.py +215 -2
  19. fb_vmware/dvs.py +37 -1
  20. fb_vmware/errors.py +31 -10
  21. fb_vmware/host.py +40 -2
  22. fb_vmware/host_port_group.py +1 -2
  23. fb_vmware/network.py +17 -1
  24. fb_vmware/obj.py +30 -1
  25. fb_vmware/vm.py +19 -1
  26. fb_vmware/xlate.py +8 -13
  27. fb_vmware-1.8.1.data/data/share/locale/de/LC_MESSAGES/fb_vmware.mo +0 -0
  28. fb_vmware-1.8.1.data/data/share/locale/en/LC_MESSAGES/fb_vmware.mo +0 -0
  29. {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/METADATA +2 -1
  30. fb_vmware-1.8.1.dist-info/RECORD +40 -0
  31. {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/entry_points.txt +3 -0
  32. fb_vmware-1.7.1.data/data/share/locale/de_DE/LC_MESSAGES/fb_vmware.mo +0 -0
  33. fb_vmware-1.7.1.data/data/share/locale/en_US/LC_MESSAGES/fb_vmware.mo +0 -0
  34. fb_vmware-1.7.1.dist-info/RECORD +0 -36
  35. {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/WHEEL +0 -0
  36. {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -16,6 +16,9 @@ import pathlib
16
16
  import sys
17
17
  from operator import attrgetter
18
18
 
19
+ # Third party modules
20
+ from fb_tools.spinner import Spinner
21
+
19
22
  # Own modules
20
23
  from . import BaseVmwareApplication, VmwareAppError
21
24
  from .. import __version__ as GLOBAL_VERSION
@@ -24,7 +27,7 @@ from ..errors import VSphereExpectedError
24
27
  from ..ether import VsphereEthernetcard
25
28
  from ..xlate import XLATOR
26
29
 
27
- __version__ = "1.8.0"
30
+ __version__ = "1.10.0"
28
31
  LOG = logging.getLogger(__name__)
29
32
 
30
33
  _ = XLATOR.gettext
@@ -132,11 +135,23 @@ class GetVmApplication(BaseVmwareApplication):
132
135
  # -------------------------------------------------------------------------
133
136
  def show_vm(self, vm_name):
134
137
  """Show a particular VM on STDOUT."""
135
- print("\n{}: ".format(vm_name), end="")
138
+ print()
139
+ msg_tpl = _("Getting data of VM {} ... ")
136
140
  if self.verbose:
137
- print()
138
-
139
- vm = self._get_vm_data(vm_name)
141
+ msg = msg_tpl.format(self.colored(vm_name, "CYAN"))
142
+ print(msg)
143
+ vm = self._get_vm_data(vm_name)
144
+ else:
145
+ msg_len = len(msg_tpl.format(vm_name))
146
+ spin_prompt = msg_tpl.format(self.colored(vm_name, "CYAN"))
147
+ spinner_name = self.get_random_spinner_name()
148
+ with Spinner(spin_prompt, spinner_name):
149
+ vm = self._get_vm_data(vm_name)
150
+ sys.stdout.write(" " * msg_len)
151
+ sys.stdout.write("\r")
152
+ sys.stdout.flush()
153
+
154
+ print("{}: ".format(vm_name), end="")
140
155
  if not vm:
141
156
  print(self.colored(_("NOT FOUND"), "RED"))
142
157
  return False
@@ -190,6 +205,24 @@ class GetVmApplication(BaseVmwareApplication):
190
205
  # -------------------------------------------------------------------------
191
206
  def _print_ctrlrs(self, vm):
192
207
 
208
+ ctrl_lbl = _("Controller") + ":"
209
+ len_ctrl_lbl = len(ctrl_lbl)
210
+
211
+ len_no_disk = 1
212
+ len_ctrl_no = 1
213
+ for ctrlr in filter(lambda x: x.scsi_ctrl_nr is not None, vm.controllers):
214
+ nr_disks = len(ctrlr.devices)
215
+ no_disk = ngettext("{nr:>2} disk ", "{nr:>2} disks", nr_disks).format(nr=nr_disks)
216
+ if len(no_disk) > len_no_disk:
217
+ len_no_disk = len(no_disk)
218
+ if len(str(ctrlr.bus_nr)) > len_ctrl_no:
219
+ len_ctrl_no = len(str(ctrlr.bus_nr))
220
+
221
+ line_template = (
222
+ f" {{la:<{len_ctrl_lbl}}} {{nr:>{len_ctrl_no}}} - {{di:<{len_no_disk}}} - {{ty}}"
223
+ )
224
+ # LOG.debug(f"Line template: {line_template!r}")
225
+
193
226
  first = True
194
227
  for ctrlr in sorted(
195
228
  filter(lambda x: x.scsi_ctrl_nr is not None, vm.controllers), key=attrgetter("bus_nr")
@@ -198,28 +231,46 @@ class GetVmApplication(BaseVmwareApplication):
198
231
  continue
199
232
  label = ""
200
233
  if first:
201
- label = "Controller:"
234
+ label = ctrl_lbl
202
235
  first = False
203
236
  ctype = _("Unknown")
204
237
  nr_disks = len(ctrlr.devices)
205
238
  if ctrlr.ctrl_type in VsphereDiskController.type_names.keys():
206
239
  ctype = VsphereDiskController.type_names[ctrlr.ctrl_type]
207
240
  no_disk = ngettext("{nr:>2} disk ", "{nr:>2} disks", nr_disks).format(nr=nr_disks)
208
- # no_disk = _("{nr:>2} disks").format(nr=len(ctrlr.devices))
209
- # if len(ctrlr.devices) == 1:
210
- # no_disk = _(" 1 disk ")
211
- msg = " {la:<15} {nr:>2} - {di} - {ty}".format(
212
- la=label, nr=ctrlr.bus_nr, di=no_disk, ty=ctype
213
- )
241
+ msg = line_template.format(la=label, nr=ctrlr.bus_nr, di=no_disk, ty=ctype)
214
242
  print(msg)
215
243
 
216
244
  # -------------------------------------------------------------------------
217
245
  def _print_disks(self, vm):
218
246
 
219
247
  if not vm.disks:
220
- print(" Disks: {}".format(_("None")))
248
+ print(" " + _("Disks") + ": " + _("None"))
221
249
  return
222
250
 
251
+ ctrl_lbl = _("Controller")
252
+ file_lbl = _("File")
253
+ len_disk_lbl = 1
254
+ len_ctrl_lbl = len(ctrl_lbl)
255
+ len_ctrlr_nr = 1
256
+
257
+ for disk in vm.disks:
258
+ if len(disk.label) > len_disk_lbl:
259
+ len_disk_lbl = len(disk.label)
260
+ ctrlr_nr = -1
261
+ for ctrlr in vm.controllers:
262
+ if disk.key in ctrlr.devices:
263
+ ctrlr_nr = ctrlr.bus_nr
264
+ break
265
+ if len(str(ctrlr_nr)) > len_ctrlr_nr:
266
+ len_ctrlr_nr = len(str(ctrlr_nr))
267
+
268
+ line_template = f" {{la}} {{n:<{len_disk_lbl}}}"
269
+ line_template += f" - {{s:7.1f}} GiB - {{ctrl_lbl:<{len_ctrl_lbl}}}"
270
+ line_template += f" {{c:>{len_ctrlr_nr}}}"
271
+ line_template += " - {file_lbl} {f}"
272
+ # LOG.debug(f"Line template: {line_template!r}")
273
+
223
274
  total_gb = 0.0
224
275
  first = True
225
276
  for disk in vm.disks:
@@ -233,8 +284,14 @@ class GetVmApplication(BaseVmwareApplication):
233
284
  if disk.key in ctrlr.devices:
234
285
  ctrlr_nr = ctrlr.bus_nr
235
286
  break
236
- msg = " {la} {n:<15} - {s:7.1f} GiB - Controller {c:>2} - File {f}".format(
237
- la=label, n=disk.label, s=disk.size_gb, c=ctrlr_nr, f=disk.file_name
287
+ msg = line_template.format(
288
+ la=label,
289
+ n=disk.label,
290
+ s=disk.size_gb,
291
+ ctrl_lbl=ctrl_lbl,
292
+ c=ctrlr_nr,
293
+ file_lbl=file_lbl,
294
+ f=disk.file_name,
238
295
  )
239
296
  print(msg)
240
297
  if len(vm.disks) > 1:
@@ -303,7 +360,8 @@ class GetVmApplication(BaseVmwareApplication):
303
360
  vm=self.colored(vm_name, "CYAN"), vs=self.colored(vsphere_name, "CYAN")
304
361
  )
305
362
  )
306
- vm = vsphere.get_vm(vm_name, vsphere_name=vsphere_name, no_error=True, as_obj=True)
363
+ vm = vsphere.get_vm_direct(vm_name, vsphere_name=vsphere_name, no_error=True)
364
+ # vm = vsphere.get_vm(vm_name, vsphere_name=vsphere_name, no_error=True)
307
365
  if not vm:
308
366
  continue
309
367
 
@@ -337,7 +395,11 @@ def main():
337
395
  if app.verbose > 2:
338
396
  print(_("{c}-Object:\n{a}").format(c=app.__class__.__name__, a=app), file=sys.stderr)
339
397
 
340
- app()
398
+ try:
399
+ app()
400
+ except KeyboardInterrupt:
401
+ print("\n" + app.colored(_("User interrupt."), "YELLOW"))
402
+ sys.exit(5)
341
403
 
342
404
  sys.exit(0)
343
405
 
@@ -10,6 +10,7 @@
10
10
  from __future__ import absolute_import, print_function
11
11
 
12
12
  # Standard modules
13
+ import argparse
13
14
  import locale
14
15
  import logging
15
16
  import pathlib
@@ -19,10 +20,16 @@ from operator import attrgetter, itemgetter
19
20
 
20
21
  # Third party modules
21
22
  from fb_tools.argparse_actions import RegexOptionAction
22
- from fb_tools.common import pp, to_bool
23
+ from fb_tools.common import is_sequence
24
+ from fb_tools.common import pp
25
+ from fb_tools.common import to_bool
23
26
  from fb_tools.spinner import Spinner
24
27
  from fb_tools.xlate import format_list
25
28
 
29
+ from rich import box
30
+ from rich.table import Table
31
+ from rich.text import Text
32
+
26
33
  # Own modules
27
34
  from . import BaseVmwareApplication, VmwareAppError
28
35
  from .. import __version__ as GLOBAL_VERSION
@@ -30,7 +37,7 @@ from ..errors import VSphereExpectedError
30
37
  from ..vm import VsphereVm
31
38
  from ..xlate import XLATOR
32
39
 
33
- __version__ = "1.9.0"
40
+ __version__ = "1.12.1"
34
41
  LOG = logging.getLogger(__name__)
35
42
 
36
43
  _ = XLATOR.gettext
@@ -44,6 +51,51 @@ class GetVmListAppError(VmwareAppError):
44
51
  pass
45
52
 
46
53
 
54
+ # =============================================================================
55
+ class RegexListOptionAction(argparse.Action):
56
+ """An argparse action for regular expressions."""
57
+
58
+ # -------------------------------------------------------------------------
59
+ def __init__(self, option_strings, topic, re_options=None, *args, **kwargs):
60
+ """Initialise a RegexListOptionAction object."""
61
+ self._topic = topic
62
+ self._re_options = None
63
+ if re_options is not None:
64
+ self._re_options = re_options
65
+
66
+ super(RegexListOptionAction, self).__init__(*args, option_strings=option_strings, **kwargs)
67
+
68
+ # -------------------------------------------------------------------------
69
+ def __call__(self, parser, namespace, pattern, option_string=None):
70
+ """Parse the regular expression option."""
71
+ try:
72
+ if is_sequence(pattern):
73
+ if len(pattern) > 1:
74
+ used_pattern = []
75
+ for pat in pattern:
76
+ used_pattern.append("(" + pat + ")")
77
+ pattern_all = "|".join(used_pattern)
78
+ elif len(pattern) == 1:
79
+ pattern_all = pattern[0]
80
+ else:
81
+ pattern_all = None
82
+ else:
83
+ pattern_all = pattern
84
+
85
+ if pattern_all is not None:
86
+ if self._re_options is None:
87
+ re_test = re.compile(pattern_all) # noqa
88
+ else:
89
+ re_test = re.compile(pattern_all, self._re_options) # noqa
90
+ except Exception as e:
91
+ msg = _("Got a {c} for pattern {p!r}: {e}").format(
92
+ c=e.__class__.__name__, p=pattern_all, e=e
93
+ )
94
+ raise argparse.ArgumentError(self, msg)
95
+
96
+ setattr(namespace, self.dest, pattern_all)
97
+
98
+
47
99
  # =============================================================================
48
100
  class GetVmListApplication(BaseVmwareApplication):
49
101
  """Class for the application objects."""
@@ -83,6 +135,7 @@ class GetVmListApplication(BaseVmwareApplication):
83
135
 
84
136
  self._vm_pattern = self.default_vm_pattern
85
137
  self._details = False
138
+ self.count_templates = None
86
139
 
87
140
  self.sort_keys = self.default_sort_keys
88
141
 
@@ -156,12 +209,10 @@ class GetVmListApplication(BaseVmwareApplication):
156
209
  filter_group = self.arg_parser.add_argument_group(_("Filter options"))
157
210
 
158
211
  filter_group.add_argument(
159
- "-p",
160
- "--pattern",
161
- "--search-pattern",
162
212
  dest="vm_pattern",
213
+ nargs="*",
163
214
  metavar="REGEX",
164
- action=RegexOptionAction,
215
+ action=RegexListOptionAction,
165
216
  topic=_("for names of VMs"),
166
217
  re_options=re.IGNORECASE,
167
218
  help=_(
@@ -295,7 +346,7 @@ class GetVmListApplication(BaseVmwareApplication):
295
346
  else:
296
347
  LOG.warn(
297
348
  _(
298
- "Sorting key {!r} not usable, if not detailed output " "was given."
349
+ "Sorting key {!r} not usable, if not detailed output was given."
299
350
  ).format(key)
300
351
  )
301
352
  if not self.sort_keys:
@@ -335,10 +386,10 @@ class GetVmListApplication(BaseVmwareApplication):
335
386
 
336
387
  re_name = re.compile(self.vm_pattern, re.IGNORECASE)
337
388
 
338
- if self.verbose:
389
+ if self.verbose or self.quiet:
339
390
  for vsphere_name in self.vsphere:
340
391
  all_vms += self.get_vms(vsphere_name, re_name)
341
- elif not self.quiet:
392
+ else:
342
393
  spin_prompt = _("Getting all vSphere VMs ...") + " "
343
394
  spinner_name = self.get_random_spinner_name()
344
395
  with Spinner(spin_prompt, spinner_name):
@@ -351,99 +402,101 @@ class GetVmListApplication(BaseVmwareApplication):
351
402
  if self.verbose > 1:
352
403
  LOG.debug(_("Using sorting keys:") + " " + format_list(self.sort_keys, do_repr=True))
353
404
 
354
- if self.details:
355
- self.print_vms_detailed(all_vms)
356
- else:
357
- self.print_vms(all_vms)
405
+ self.print_virtual_machines(all_vms)
358
406
 
359
407
  return ret
360
408
 
361
409
  # -------------------------------------------------------------------------
362
- def print_vms(self, all_vms):
410
+ def print_virtual_machines(self, all_vms):
363
411
  """Print out on STDOUT the list of found VMs."""
364
- label_list = ("name", "vsphere", "dc", "path")
365
- labels = {
366
- "name": _("Host"),
367
- "vsphere": "vSphere",
368
- "dc": _("Data Center"),
369
- "path": _("Path"),
370
- }
371
-
372
- self._print_vms(all_vms, label_list, labels)
373
-
374
- # -------------------------------------------------------------------------
375
- def print_vms_detailed(self, all_vms):
376
- """Print out on STDOUT the list of found VMs in a detailled way."""
377
- label_list = self.avail_sort_keys
378
- labels = {
379
- "name": "VM/Template",
380
- "vsphere": "vSphere",
381
- "dc": _("Data Center"),
382
- "cluster": _("Cluster"),
383
- "path": _("Path"),
384
- "type": _("Type"),
385
- "onl_str": _("Online Status"),
386
- "cfg_ver": _("Config Version"),
387
- "os": _("Operating System"),
388
- }
389
-
390
- self._print_vms(all_vms, label_list, labels)
391
-
392
- # -------------------------------------------------------------------------
393
- def _print_vms(self, all_vms, label_list, labels):
394
-
395
- str_lengths = {}
396
- for label in labels.keys():
397
- str_lengths[label] = len(labels[label])
398
-
399
- max_len = 0
400
- count = 0
401
- for cdata in all_vms:
402
- for field in ("cluster", "path", "type", "cfg_ver", "os"):
403
- if field in labels and cdata[field] is None:
404
- cdata[field] = "~"
405
- for label in labels.keys():
406
- val = cdata[label]
407
- if len(val) > str_lengths[label]:
408
- str_lengths[label] = len(val)
409
-
410
- for label in labels.keys():
411
- if max_len:
412
- max_len += 2
413
- max_len += str_lengths[label]
414
-
415
412
  if self.verbose > 1:
416
- LOG.debug("Label length:\n" + pp(str_lengths))
417
- LOG.debug("Max line length: {} chars".format(max_len))
418
-
419
- tpl = ""
420
- for label in label_list:
421
- if tpl != "":
422
- tpl += " "
423
- tpl += "{{{la}:<{le}}}".format(la=label, le=str_lengths[label])
424
- if self.verbose > 1:
425
- LOG.debug(_("Line template: {}").format(tpl))
426
-
427
- if not self.quiet:
428
- print()
429
- print(tpl.format(**labels))
430
- print("-" * max_len)
413
+ LOG.debug("Print out VM list: " + pp(all_vms))
431
414
 
432
415
  all_vms.sort(key=itemgetter(*self.sort_keys))
433
416
 
434
- for cdata in all_vms:
435
- count += 1
417
+ show_header = True
418
+ title = _("Virtual Machines")
419
+ title += "\n" + ("=" * len(title))
420
+ box_style = box.ROUNDED
421
+ if self.quiet:
422
+ show_header = False
423
+ title = None
424
+ box_style = None
425
+
426
+ if self.quiet:
427
+ caption = None
428
+ else:
429
+ count = len(all_vms)
430
+ if count:
431
+ caption = "\n" + ngettext(
432
+ "Found one virtual machine.",
433
+ "Found {} virtual machines.",
434
+ count,
435
+ ).format(count)
436
+ else:
437
+ caption = "\n" + _("No virtual machines found.")
438
+
439
+ if self.count_templates is not None:
440
+ if self.count_templates:
441
+ caption += "\n" + ngettext(
442
+ "One VM of them is a template.",
443
+ "{} VMs of them are templates.",
444
+ self.count_templates,
445
+ ).format(self.count_templates)
446
+ caption += "\n"
447
+
448
+ table = Table(
449
+ title=title,
450
+ title_style="bold cyan",
451
+ caption=caption,
452
+ caption_style="default on default",
453
+ caption_justify="left",
454
+ box=box_style,
455
+ show_header=show_header,
456
+ show_footer=False,
457
+ )
458
+
459
+ table.add_column(header="VM/Template")
460
+ table.add_column(header="vSphere")
461
+ table.add_column(header=_("Data Center"))
462
+ if self.details:
463
+ table.add_column(header=_("Cluster"))
464
+ table.add_column(header=_("Path"))
465
+ if self.details:
466
+ table.add_column(header=_("Type"))
467
+ table.add_column(header=_("Online Status"))
468
+ table.add_column(header=_("Config Version"))
469
+ table.add_column(header=_("Operating System"))
436
470
 
437
- print(tpl.format(**cdata))
471
+ for vm in all_vms:
438
472
 
439
- if not self.quiet:
440
- print()
441
- if count == 0:
442
- msg = _("Found no VMware VMs.")
473
+ if self.details:
474
+ online_status = Text(vm["onl_str"], style="bold green")
475
+ if vm["is_template"]:
476
+ online_status = Text(vm["onl_str"], style="bold cyan")
477
+ elif not vm["is_online"]:
478
+ online_status = Text(vm["onl_str"], style="bold red")
479
+
480
+ table.add_row(
481
+ vm["name"],
482
+ vm["vsphere"],
483
+ vm["dc"],
484
+ vm["cluster"],
485
+ vm["path"],
486
+ vm["type"],
487
+ online_status,
488
+ vm["cfg_ver"],
489
+ vm["os"],
490
+ )
443
491
  else:
444
- msg = ngettext("Found one VMware VM.", "Found {} VMware VMs.", count).format(count)
445
- print(msg)
446
- print()
492
+ table.add_row(
493
+ vm["name"],
494
+ vm["vsphere"],
495
+ vm["dc"],
496
+ vm["path"],
497
+ )
498
+
499
+ self.rich_console.print(table)
447
500
 
448
501
  # -------------------------------------------------------------------------
449
502
  def get_vms(self, vsphere_name, re_name=None):
@@ -455,10 +508,16 @@ class GetVmListApplication(BaseVmwareApplication):
455
508
  re_name = re.compile(self.vm_pattern, re.IGNORECASE)
456
509
 
457
510
  if self.details:
458
- vm_list = vsphere.get_vms(re_name, vsphere_name=vsphere_name, as_obj=True)
511
+ # vm_list = vsphere.get_vms(re_name, vsphere_name=vsphere_name, as_obj=True)
512
+ vm_list = vsphere.get_vm_list(
513
+ re_name, vsphere_name=vsphere_name, name_only=False, disconnect=True
514
+ )
459
515
  vms = self.mangle_vmlist_details(vm_list, vsphere_name)
460
516
  else:
461
- vm_list = vsphere.get_vms(re_name, vsphere_name=vsphere_name, name_only=True)
517
+ # vm_list = vsphere.get_vms(re_name, vsphere_name=vsphere_name, name_only=True)
518
+ vm_list = vsphere.get_vm_list(
519
+ re_name, vsphere_name=vsphere_name, name_only=True, disconnect=True
520
+ )
462
521
  vms = self.mangle_vmlist_no_details(vm_list, vsphere_name)
463
522
 
464
523
  return vms
@@ -466,6 +525,8 @@ class GetVmListApplication(BaseVmwareApplication):
466
525
  # -------------------------------------------------------------------------
467
526
  def mangle_vmlist_no_details(self, vm_list, vsphere_name):
468
527
  """Prepare the non-detailled data about found VMs for output."""
528
+ if self.verbose > 1:
529
+ LOG.debug(_("Performing VM list ..."))
469
530
  if self.verbose > 3:
470
531
  LOG.debug("Mangling VM list:\n" + pp(vm_list))
471
532
 
@@ -485,7 +546,7 @@ class GetVmListApplication(BaseVmwareApplication):
485
546
  }
486
547
 
487
548
  if cdata["path"]:
488
- cdata["path"] = "/" + cdata["path"]
549
+ cdata["path"] = cdata["path"]
489
550
  else:
490
551
  cdata["path"] = "/"
491
552
 
@@ -501,8 +562,12 @@ class GetVmListApplication(BaseVmwareApplication):
501
562
  # -------------------------------------------------------------------------
502
563
  def mangle_vmlist_details(self, vm_list, vsphere_name):
503
564
  """Prepare the detailled data about found VMs for output."""
565
+ if self.verbose > 1:
566
+ LOG.debug(_("Performing detailled VM list ..."))
504
567
  vms = []
505
568
 
569
+ self.count_templates = 0
570
+
506
571
  first = True
507
572
  for vm in sorted(vm_list, key=attrgetter("name", "path")):
508
573
 
@@ -571,10 +636,12 @@ class GetVmListApplication(BaseVmwareApplication):
571
636
  "onl_str": "Online",
572
637
  "cfg_ver": vm.config_version,
573
638
  "os": vm.guest_id,
639
+ "is_online": True,
640
+ "is_template": False,
574
641
  }
575
642
 
576
643
  if cdata["path"]:
577
- cdata["path"] = "/" + cdata["path"]
644
+ cdata["path"] = cdata["path"]
578
645
  else:
579
646
  cdata["path"] = "/"
580
647
 
@@ -586,9 +653,12 @@ class GetVmListApplication(BaseVmwareApplication):
586
653
 
587
654
  if not vm.online:
588
655
  cdata["onl_str"] = "Offline"
656
+ cdata["is_online"] = False
589
657
 
590
658
  if vm.template:
591
659
  cdata["type"] = "VMware Template"
660
+ cdata["is_template"] = True
661
+ self.count_templates += 1
592
662
 
593
663
  return cdata
594
664
 
@@ -607,7 +677,11 @@ def main():
607
677
  if app.verbose > 2:
608
678
  print(_("{c}-Object:\n{a}").format(c=app.__class__.__name__, a=app), file=sys.stderr)
609
679
 
610
- app()
680
+ try:
681
+ app()
682
+ except KeyboardInterrupt:
683
+ print("\n" + app.colored(_("User interrupt."), "YELLOW"))
684
+ sys.exit(5)
611
685
 
612
686
  sys.exit(0)
613
687