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.
- fb_vmware/__init__.py +1 -1
- fb_vmware/app/__init__.py +285 -6
- fb_vmware/app/get_host_list.py +115 -100
- fb_vmware/app/get_network_list.py +176 -218
- fb_vmware/app/get_rpool_list.py +386 -0
- fb_vmware/app/get_storage_cluster_info.py +303 -0
- fb_vmware/app/get_storage_cluster_list.py +100 -107
- fb_vmware/app/get_storage_list.py +145 -112
- fb_vmware/app/get_vm_info.py +79 -17
- fb_vmware/app/get_vm_list.py +169 -95
- fb_vmware/app/search_storage.py +470 -0
- fb_vmware/argparse_actions.py +78 -0
- fb_vmware/base.py +28 -1
- fb_vmware/cluster.py +99 -7
- fb_vmware/connect.py +450 -20
- fb_vmware/datastore.py +195 -6
- fb_vmware/dc.py +19 -1
- fb_vmware/ds_cluster.py +215 -2
- fb_vmware/dvs.py +37 -1
- fb_vmware/errors.py +31 -10
- fb_vmware/host.py +40 -2
- fb_vmware/host_port_group.py +1 -2
- fb_vmware/network.py +17 -1
- fb_vmware/obj.py +30 -1
- fb_vmware/vm.py +19 -1
- fb_vmware/xlate.py +8 -13
- fb_vmware-1.8.1.data/data/share/locale/de/LC_MESSAGES/fb_vmware.mo +0 -0
- fb_vmware-1.8.1.data/data/share/locale/en/LC_MESSAGES/fb_vmware.mo +0 -0
- {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/METADATA +2 -1
- fb_vmware-1.8.1.dist-info/RECORD +40 -0
- {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/entry_points.txt +3 -0
- fb_vmware-1.7.1.data/data/share/locale/de_DE/LC_MESSAGES/fb_vmware.mo +0 -0
- fb_vmware-1.7.1.data/data/share/locale/en_US/LC_MESSAGES/fb_vmware.mo +0 -0
- fb_vmware-1.7.1.dist-info/RECORD +0 -36
- {fb_vmware-1.7.1.dist-info → fb_vmware-1.8.1.dist-info}/WHEEL +0 -0
- {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
|