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
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ @summary: Print a list of all resouce pools in a VMware vSphere.
5
+
6
+ @author: Frank Brehm
7
+ @contact: frank@brehm-online.com
8
+ @copyright: © 2026 by Frank Brehm, Berlin
9
+ """
10
+ from __future__ import absolute_import, print_function
11
+
12
+ # Standard modules
13
+ import locale
14
+ import logging
15
+ import pathlib
16
+ import sys
17
+ from operator import attrgetter
18
+
19
+ # Third party modules
20
+ from babel.numbers import format_decimal
21
+
22
+ from fb_tools.common import pp
23
+ from fb_tools.spinner import Spinner
24
+ from fb_tools.xlate import format_list
25
+
26
+ from rich import box
27
+ from rich.table import Table
28
+ from rich.text import Text
29
+
30
+ # Own modules
31
+ from . import BaseVmwareApplication, VmwareAppError
32
+ from .. import __version__ as GLOBAL_VERSION
33
+ from ..errors import VSphereExpectedError
34
+ from ..xlate import XLATOR
35
+
36
+ __version__ = "1.1.1"
37
+ LOG = logging.getLogger(__name__)
38
+
39
+ _ = XLATOR.gettext
40
+ ngettext = XLATOR.ngettext
41
+
42
+
43
+ # =============================================================================
44
+ class GetResPoolAppError(VmwareAppError):
45
+ """Base exception class for all exceptions in this application."""
46
+
47
+ pass
48
+
49
+
50
+ # =============================================================================
51
+ class GetResPoolListApplication(BaseVmwareApplication):
52
+ """Class for the application object."""
53
+
54
+ avail_sort_keys = ("name", "vsphere", "dc_name")
55
+ default_sort_keys = ["vsphere", "dc_name", "name"]
56
+
57
+ # -------------------------------------------------------------------------
58
+ def __init__(
59
+ self,
60
+ appname=None,
61
+ verbose=0,
62
+ version=GLOBAL_VERSION,
63
+ base_dir=None,
64
+ initialized=False,
65
+ usage=None,
66
+ description=None,
67
+ argparse_epilog=None,
68
+ argparse_prefix_chars="-",
69
+ env_prefix=None,
70
+ ):
71
+ """Initialize a GetResPoolListApplication object."""
72
+ desc = _(
73
+ "Tries to get a list of all resource pools (a.k.a. computing resource and cluster "
74
+ "computing resource) in VMware vSphere and print it out."
75
+ )
76
+
77
+ self.sort_keys = self.default_sort_keys
78
+
79
+ super(GetResPoolListApplication, self).__init__(
80
+ appname=appname,
81
+ verbose=verbose,
82
+ version=version,
83
+ base_dir=base_dir,
84
+ description=desc,
85
+ initialized=False,
86
+ )
87
+
88
+ self.initialized = True
89
+
90
+ # -------------------------------------------------------------------------
91
+ def init_arg_parser(self):
92
+ """Public available method to initiate the argument parser."""
93
+ output_options = self.arg_parser.add_argument_group(_("Output options"))
94
+
95
+ output_options.add_argument(
96
+ "-S",
97
+ "--sort",
98
+ metavar="KEY",
99
+ nargs="+",
100
+ dest="sort_keys",
101
+ choices=self.avail_sort_keys,
102
+ help=_(
103
+ "The keys for sorting the output. Available keys are: {avail}. "
104
+ "The default sorting keys are: {default}."
105
+ ).format(
106
+ avail=format_list(self.avail_sort_keys, do_repr=True),
107
+ default=format_list(self.default_sort_keys, do_repr=True),
108
+ ),
109
+ )
110
+
111
+ super(GetResPoolListApplication, self).init_arg_parser()
112
+
113
+ # -------------------------------------------------------------------------
114
+ def perform_arg_parser(self):
115
+ """Evaluate command line parameters."""
116
+ super(GetResPoolListApplication, self).perform_arg_parser()
117
+
118
+ if self.args.sort_keys:
119
+ self.sort_keys = self.args.sort_keys
120
+
121
+ # -------------------------------------------------------------------------
122
+ def _run(self):
123
+
124
+ LOG.debug(_("Starting {a!r}, version {v!r} ...").format(a=self.appname, v=self.version))
125
+
126
+ ret = 0
127
+ try:
128
+ ret = self.get_all_resource_pools()
129
+ except VSphereExpectedError as e:
130
+ LOG.error(str(e))
131
+ self.exit(6)
132
+ finally:
133
+ self.cleaning_up()
134
+
135
+ self.exit(ret)
136
+
137
+ # -------------------------------------------------------------------------
138
+ def get_all_resource_pools(self):
139
+ """Collect all resource pools, a.k.a. (cluster) computing resources."""
140
+ ret = 0
141
+ all_rpools = []
142
+
143
+ if self.verbose or self.quiet:
144
+ for vsphere_name in self.vsphere:
145
+ all_rpools += self.get_resource_pools(vsphere_name)
146
+ else:
147
+ spin_prompt = _("Getting all vSphere hosts ...") + " "
148
+ spinner_name = self.get_random_spinner_name()
149
+ with Spinner(spin_prompt, spinner_name):
150
+ for vsphere_name in self.vsphere:
151
+ all_rpools += self.get_resource_pools(vsphere_name)
152
+ sys.stdout.write(" " * len(spin_prompt))
153
+ sys.stdout.write("\r")
154
+ sys.stdout.flush()
155
+
156
+ all_rpools.sort(key=attrgetter(*self.sort_keys))
157
+
158
+ if len(all_rpools):
159
+ out_list = []
160
+ out = ""
161
+ if self.verbose == 2:
162
+ LOG.debug(_("First computing resource:") + "\n" + pp(all_rpools[0].as_dict()))
163
+ for rpool in all_rpools:
164
+ out_list.append(
165
+ f" * Vsphere {rpool.vsphere:<10} - DC {rpool.dc_name:<12} - {rpool.name}"
166
+ )
167
+ out = "\n".join(out_list)
168
+ elif self.verbose > 2:
169
+ for rpool in all_rpools:
170
+ out_list.append(rpool.as_dict())
171
+ out = pp(out_list)
172
+ if self.verbose >= 2:
173
+ LOG.debug("All computing resources:\n{}".format(out))
174
+
175
+ self.print_rpools(all_rpools)
176
+ else:
177
+ LOG.error(_("Did not found any resource pools or cluster resource pools."))
178
+ if not self.quiet:
179
+ print()
180
+ ret = 3
181
+
182
+ return ret
183
+
184
+ # -------------------------------------------------------------------------
185
+ def get_resource_pools(self, vsphere_name):
186
+ """Get all host of all (cluster) computing resources in a VMware vSphere."""
187
+ clusters = []
188
+
189
+ vsphere = self.vsphere[vsphere_name]
190
+
191
+ vsphere.get_clusters(vsphere_name=vsphere_name)
192
+
193
+ for cluster in sorted(vsphere.clusters):
194
+ clusters.append(cluster)
195
+
196
+ return clusters
197
+
198
+ # -------------------------------------------------------------------------
199
+ def print_rpools(self, rpools):
200
+ """Print on STDOUT all information about cluster) computing resources."""
201
+ show_header = True
202
+ show_footer = True
203
+ table_title = _("All compute resources and cluster compute resources") + "\n"
204
+ box_style = box.ROUNDED
205
+
206
+ if self.quiet:
207
+ show_header = False
208
+ show_footer = False
209
+ table_title = None
210
+ box_style = None
211
+
212
+ if self.quiet:
213
+ caption = None
214
+ else:
215
+ count = len(rpools)
216
+ if count:
217
+ caption = "\n" + ngettext(
218
+ "Found one compute resource.",
219
+ "Found {} compute resources.",
220
+ count,
221
+ ).format(count)
222
+ else:
223
+ caption = "\n" + _("Found no compute resources.")
224
+
225
+ totals = self._get_totals(rpools)
226
+
227
+ table = Table(
228
+ title=table_title,
229
+ title_style="bold cyan",
230
+ caption=caption,
231
+ caption_style="default on default",
232
+ caption_justify="left",
233
+ box=box_style,
234
+ show_header=show_header,
235
+ show_footer=show_footer,
236
+ )
237
+
238
+ table.add_column(header=_("vSphere"), footer=_("Total:"))
239
+ table.add_column(header=_("Data Center"), footer="")
240
+ table.add_column(header=_("Name"), footer="")
241
+ table.add_column(header=_("Cluster"), footer="")
242
+ table.add_column(header=_("Pool name"), footer="")
243
+ table.add_column(
244
+ header=_("Hosts total"),
245
+ justify="right",
246
+ footer=self._prepare_number(totals["hosts_total"], warn_on_value1=True),
247
+ )
248
+ table.add_column(
249
+ header=_("Hosts available"),
250
+ justify="right",
251
+ footer=self._prepare_number(
252
+ totals["hosts_avail"],
253
+ warn_on_value1=True,
254
+ compare_val=totals["hosts_total"],
255
+ ),
256
+ )
257
+ table.add_column(
258
+ header=_("CPU cores"),
259
+ justify="right",
260
+ footer=self._prepare_number(totals["cpu_cores"]),
261
+ )
262
+ table.add_column(
263
+ header=_("CPU threads"),
264
+ justify="right",
265
+ footer=self._prepare_number(totals["cpu_threads"]),
266
+ )
267
+ table.add_column(
268
+ header=_("Memory total"),
269
+ justify="right",
270
+ footer=self._prepare_number(totals["mem_total"]),
271
+ )
272
+ table.add_column(
273
+ header=_("Memory available"),
274
+ justify="right",
275
+ footer=self._prepare_number(totals["mem_avail"], compare_val=totals["mem_total"]),
276
+ )
277
+
278
+ for rpool in rpools:
279
+ row = []
280
+ row.append(rpool.vsphere)
281
+ row.append(rpool.dc_name)
282
+ row.append(rpool.name)
283
+
284
+ is_cluster = Text(_("Yes"), style="bold green")
285
+ if rpool.standalone:
286
+ is_cluster = Text(_("No"), style="green")
287
+
288
+ row.append(is_cluster)
289
+ row.append(rpool.base_resource_pool_name)
290
+ row.append(self._prepare_number(rpool.hosts_total, warn_on_value1=True))
291
+ row.append(
292
+ self._prepare_number(
293
+ rpool.hosts_effective,
294
+ warn_on_value1=True,
295
+ compare_val=rpool.hosts_total,
296
+ )
297
+ )
298
+ row.append(self._prepare_number(rpool.cpu_cores))
299
+ row.append(self._prepare_number(rpool.cpu_threads))
300
+ row.append(self._prepare_number(rpool.mem_mb_total))
301
+ row.append(
302
+ self._prepare_number(rpool.mem_mb_effective, compare_val=rpool.mem_mb_total)
303
+ )
304
+
305
+ table.add_row(*row)
306
+
307
+ self.rich_console.print(table)
308
+
309
+ if not self.quiet:
310
+ print()
311
+
312
+ # -------------------------------------------------------------------------
313
+ def _get_totals(self, rpools):
314
+
315
+ totals = {
316
+ "hosts_total": 0,
317
+ "hosts_avail": 0,
318
+ "cpu_cores": 0,
319
+ "cpu_threads": 0,
320
+ "mem_total": 0,
321
+ "mem_avail": 0,
322
+ }
323
+
324
+ for rpool in rpools:
325
+ totals["hosts_total"] += rpool.hosts_total
326
+ totals["hosts_avail"] += rpool.hosts_effective
327
+ totals["cpu_cores"] += rpool.cpu_cores
328
+ totals["cpu_threads"] += rpool.cpu_threads
329
+ totals["mem_total"] += rpool.mem_mb_total
330
+ totals["mem_avail"] += rpool.mem_mb_effective
331
+
332
+ return totals
333
+
334
+ # -------------------------------------------------------------------------
335
+ def _prepare_number(self, value, may_zero=False, warn_on_value1=False, compare_val=None):
336
+
337
+ if value is None:
338
+ return ""
339
+
340
+ try:
341
+ int_val = int(value)
342
+ except ValueError:
343
+ return value
344
+
345
+ val_str = format_decimal(int_val, format="#,##0")
346
+ if not may_zero and int_val == 0:
347
+ val_str = Text(val_str, style="bold red")
348
+ elif warn_on_value1 and int_val == 1:
349
+ val_str = Text(val_str, style="bold yellow")
350
+ elif compare_val is not None and int_val < compare_val:
351
+ val_str = Text(val_str, style="bold yellow")
352
+
353
+ return val_str
354
+
355
+
356
+ # =============================================================================
357
+ def main():
358
+ """Entrypoint for get-vsphere-cluster-list."""
359
+ my_path = pathlib.Path(__file__)
360
+ appname = my_path.name
361
+
362
+ locale.setlocale(locale.LC_ALL, "")
363
+
364
+ app = GetResPoolListApplication(appname=appname)
365
+ app.initialized = True
366
+
367
+ if app.verbose > 2:
368
+ print(_("{c}-Object:\n{a}").format(c=app.__class__.__name__, a=app), file=sys.stderr)
369
+
370
+ try:
371
+ app()
372
+ except KeyboardInterrupt:
373
+ print("\n" + app.colored(_("User interrupt."), "YELLOW"))
374
+ sys.exit(5)
375
+
376
+ sys.exit(0)
377
+
378
+
379
+ # =============================================================================
380
+ if __name__ == "__main__":
381
+
382
+ pass
383
+
384
+ # =============================================================================
385
+
386
+ # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ @summary: Print all available information about a given storaage cluster.
5
+
6
+ @author: Frank Brehm
7
+ @contact: frank@brehm-online.com
8
+ @copyright: © 2025 by Frank Brehm, Berlin
9
+ """
10
+ from __future__ import absolute_import, print_function
11
+
12
+ # Standard modules
13
+ import locale
14
+ import logging
15
+ import pathlib
16
+ import sys
17
+
18
+ # Third party modules
19
+ from babel.numbers import format_decimal
20
+
21
+ from fb_tools.common import pp
22
+ from fb_tools.spinner import Spinner
23
+ from fb_tools.xlate import format_list
24
+
25
+ from rich.table import Table
26
+
27
+ # Own modules
28
+ from . import BaseVmwareApplication
29
+ from . import VmwareAppError
30
+ from .. import __version__ as GLOBAL_VERSION
31
+ from ..errors import VSphereExpectedError
32
+ from ..xlate import XLATOR
33
+
34
+ __version__ = "1.0.2"
35
+ LOG = logging.getLogger(__name__)
36
+
37
+ _ = XLATOR.gettext
38
+ ngettext = XLATOR.ngettext
39
+
40
+
41
+ # =============================================================================
42
+ class GetStorageClusterInfoAppError(VmwareAppError):
43
+ """Base exception class for all exceptions in this application."""
44
+
45
+ pass
46
+
47
+
48
+ # =============================================================================
49
+ class GetStorageClusterInfoApp(BaseVmwareApplication):
50
+ """Class for the application objects."""
51
+
52
+ show_simulate_option = False
53
+
54
+ # -------------------------------------------------------------------------
55
+ def __init__(
56
+ self,
57
+ appname=None,
58
+ verbose=0,
59
+ version=GLOBAL_VERSION,
60
+ base_dir=None,
61
+ initialized=False,
62
+ usage=None,
63
+ description=None,
64
+ argparse_epilog=None,
65
+ argparse_prefix_chars="-",
66
+ env_prefix=None,
67
+ ):
68
+ """Initialize the GetStorageClusterInfoApp object."""
69
+ desc = _(
70
+ "Tries to get information about the given datastore cluster in "
71
+ "VMware vSphere and print it out."
72
+ )
73
+
74
+ self.ds_cluster_names = []
75
+
76
+ super(GetStorageClusterInfoApp, self).__init__(
77
+ appname=appname,
78
+ verbose=verbose,
79
+ version=version,
80
+ base_dir=base_dir,
81
+ description=desc,
82
+ initialized=False,
83
+ )
84
+
85
+ self.initialized = True
86
+
87
+ # -------------------------------------------------------------------------
88
+ def init_arg_parser(self):
89
+ """Initiate the argument parser."""
90
+ self.arg_parser.add_argument(
91
+ "clusters",
92
+ metavar="CLUSTER",
93
+ type=str,
94
+ nargs="+",
95
+ help=_("Names of the datastore clusters to get information."),
96
+ )
97
+
98
+ super(GetStorageClusterInfoApp, self).init_arg_parser()
99
+
100
+ # -------------------------------------------------------------------------
101
+ def perform_arg_parser(self):
102
+ """Evaluate the command line parameters. Maybe overridden."""
103
+ super(GetStorageClusterInfoApp, self).perform_arg_parser()
104
+
105
+ for cluster in self.args.clusters:
106
+ self.ds_cluster_names.append(cluster)
107
+
108
+ # -------------------------------------------------------------------------
109
+ def _run(self):
110
+
111
+ LOG.debug(_("Starting {a!r}, version {v!r} ...").format(a=self.appname, v=self.version))
112
+
113
+ ret = 99
114
+ try:
115
+ ret = self.show_ds_clusters()
116
+ finally:
117
+ self.cleaning_up()
118
+
119
+ self.exit(ret)
120
+
121
+ # -------------------------------------------------------------------------
122
+ def show_ds_clusters(self):
123
+ """Show all datastore clusters."""
124
+ ret = 0
125
+
126
+ try:
127
+ for vsphere_name in self.vsphere:
128
+ vsphere = self.vsphere[vsphere_name]
129
+ vsphere.get_datacenters()
130
+
131
+ except VSphereExpectedError as e:
132
+ LOG.error(str(e))
133
+ self.exit(8)
134
+
135
+ for cluster_name in sorted(self.ds_cluster_names, key=str.lower):
136
+ if not self.show_ds_cluster(cluster_name):
137
+ ret = 1
138
+
139
+ return ret
140
+
141
+ # -------------------------------------------------------------------------
142
+ def show_ds_cluster(self, cluster_name):
143
+ """Show a particular datastorecluster on STDOUT."""
144
+ print()
145
+ msg_tpl = _("Getting data of datastora cluster {} ... ")
146
+ if self.verbose:
147
+ msg = msg_tpl.format(self.colored(cluster_name, "CYAN"))
148
+ print(msg)
149
+ ds_cluster = self._get_ds_cluster_obj(cluster_name)
150
+ else:
151
+ msg_len = len(msg_tpl.format(cluster_name))
152
+ spin_prompt = msg_tpl.format(self.colored(cluster_name, "CYAN"))
153
+ spinner_name = self.get_random_spinner_name()
154
+ with Spinner(spin_prompt, spinner_name):
155
+ ds_cluster = self._get_ds_cluster_obj(cluster_name)
156
+ sys.stdout.write(" " * msg_len)
157
+ sys.stdout.write("\r")
158
+ sys.stdout.flush()
159
+
160
+ print("{}: ".format(cluster_name), end="")
161
+ if not ds_cluster:
162
+ print(self.colored(_("NOT FOUND"), "RED"))
163
+ return False
164
+
165
+ print("{ok}".format(ok=self.colored("OK", "GREEN")))
166
+ print()
167
+
168
+ dsc_table = Table(title=ds_cluster.name, title_style="bold cyan", box=None)
169
+ dsc_table.add_column(highlight=True, style="bold", no_wrap=True)
170
+ dsc_table.add_column()
171
+
172
+ info_table = Table(box=None, show_header=False, show_footer=False)
173
+ info_table.add_column(highlight=True, no_wrap=True)
174
+ info_table.add_column()
175
+ info_table.add_row("vSphere:", ds_cluster.vsphere)
176
+ info_table.add_row("Datacenter:", ds_cluster.dc_name)
177
+ info_table.add_row(
178
+ _("Connected computing clusters") + ":",
179
+ format_list(sorted(ds_cluster.compute_clusters)),
180
+ )
181
+
182
+ dsc_table.add_row(_("General"), info_table)
183
+ dsc_table.add_row("", "")
184
+
185
+ usage_pc_out = "- %"
186
+ used = ds_cluster.capacity_gb - ds_cluster.free_space_gb
187
+ if ds_cluster.capacity_gb:
188
+ used_pc = used / ds_cluster.capacity_gb
189
+ usage_pc_out = format_decimal(used_pc, format="0.0 %")
190
+
191
+ cap_table = Table(box=None, show_footer=True)
192
+ cap_table.add_column(header=_("Datastore"), footer=_("Datastore cluster total"))
193
+ cap_table.add_column(
194
+ header=_("Capacity in GB"),
195
+ footer=format_decimal(ds_cluster.capacity_gb, format="#,##0"),
196
+ justify="right",
197
+ )
198
+ cap_table.add_column(
199
+ header=_("Calculated usage in GB"),
200
+ footer=format_decimal(used, format="#,##0"),
201
+ justify="right",
202
+ )
203
+ cap_table.add_column(header=_("Usage in percent"), footer=usage_pc_out, justify="right")
204
+ cap_table.add_column(
205
+ header=_("Free space in GB"),
206
+ footer=format_decimal(ds_cluster.free_space_gb, format="#,##0"),
207
+ justify="right",
208
+ )
209
+ for ds_name in sorted(ds_cluster.datastores.keys(), key=str.lower):
210
+ ds = ds_cluster.datastores[ds_name]
211
+ ds_used = ds.capacity_gb - ds.free_space_gb
212
+ ds_used_pc = "- %"
213
+ if ds.capacity_gb:
214
+ ds_used_pc = format_decimal((ds_used / ds.capacity_gb), format="0.0 %")
215
+ cap_table.add_row(
216
+ ds_name,
217
+ format_decimal(ds.capacity_gb, format="#,##0"),
218
+ format_decimal(ds_used, format="#,##0"),
219
+ ds_used_pc,
220
+ format_decimal(ds.free_space_gb, format="#,##0"),
221
+ )
222
+
223
+ dsc_table.add_row(_("Capacity"), cap_table)
224
+ dsc_table.add_row("", "")
225
+
226
+ hosts_table = Table(box=None, show_header=False, show_footer=False)
227
+ hosts_table.add_column()
228
+ for host in sorted(ds_cluster.hosts, key=str.lower):
229
+ hosts_table.add_row("* " + host)
230
+
231
+ dsc_table.add_row(_("Connected hosts"), hosts_table)
232
+ dsc_table.add_row("", "")
233
+
234
+ self.rich_console.print(dsc_table)
235
+
236
+ # -------------------------------------------------------------------------
237
+ def _get_ds_cluster_obj(self, cluster_name):
238
+
239
+ if self.verbose > 1:
240
+ LOG.debug(
241
+ _("Pulling full data of datastore cluster {} ...").format(
242
+ self.colored(cluster_name, "CYAN")
243
+ )
244
+ )
245
+
246
+ ds_cluster = None
247
+
248
+ for vsphere_name in self.vsphere:
249
+ vsphere = self.vsphere[vsphere_name]
250
+ LOG.debug(
251
+ _("Searching for datastore cluster {dsc} in vSphere {vs} ...").format(
252
+ dsc=self.colored(cluster_name, "CYAN"), vs=self.colored(vsphere_name, "CYAN")
253
+ )
254
+ )
255
+
256
+ ds_cluster = vsphere.get_ds_cluster(
257
+ cluster_name, vsphere_name=vsphere_name, no_error=True, detailled=True
258
+ )
259
+ if not ds_cluster:
260
+ continue
261
+
262
+ break
263
+
264
+ if self.verbose > 2:
265
+ LOG.debug(
266
+ "Got data of datastore cluster {}:\n".format(cluster_name)
267
+ + pp(ds_cluster.as_dict())
268
+ )
269
+
270
+ return ds_cluster
271
+
272
+
273
+ # =============================================================================
274
+ def main():
275
+ """Entrypoint for get-vsphere-vm-info."""
276
+ my_path = pathlib.Path(__file__)
277
+ appname = my_path.name
278
+
279
+ locale.setlocale(locale.LC_ALL, "")
280
+
281
+ app = GetStorageClusterInfoApp(appname=appname)
282
+ app.initialized = True
283
+
284
+ if app.verbose > 2:
285
+ print(_("{c}-Object:\n{a}").format(c=app.__class__.__name__, a=app), file=sys.stderr)
286
+
287
+ try:
288
+ app()
289
+ except KeyboardInterrupt:
290
+ print("\n" + app.colored(_("User interrupt."), "YELLOW"))
291
+ sys.exit(5)
292
+
293
+ sys.exit(0)
294
+
295
+
296
+ # =============================================================================
297
+ if __name__ == "__main__":
298
+
299
+ pass
300
+
301
+ # =============================================================================
302
+
303
+ # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list