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,470 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
@summary: Search a datastore for vdisk of a given size 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
|
+
|
|
18
|
+
# from fb_tools.argparse_actions import RegexOptionAction
|
|
19
|
+
from fb_tools.common import pp
|
|
20
|
+
from fb_tools.spinner import Spinner
|
|
21
|
+
from fb_tools.xlate import format_list
|
|
22
|
+
|
|
23
|
+
# Own modules
|
|
24
|
+
from . import BaseVmwareApplication
|
|
25
|
+
from .. import __version__ as GLOBAL_VERSION
|
|
26
|
+
from ..argparse_actions import NonNegativeIntegerOptionAction
|
|
27
|
+
from ..datastore import VsphereDatastoreDict
|
|
28
|
+
from ..ds_cluster import VsphereDsCluster
|
|
29
|
+
from ..ds_cluster import VsphereDsClusterDict
|
|
30
|
+
from ..errors import VSphereExpectedError
|
|
31
|
+
from ..errors import VSphereNoDatastoreFoundError
|
|
32
|
+
from ..errors import VSphereNoDsClusterFoundError
|
|
33
|
+
from ..xlate import XLATOR
|
|
34
|
+
|
|
35
|
+
__version__ = "0.6.2"
|
|
36
|
+
LOG = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
_ = XLATOR.gettext
|
|
39
|
+
ngettext = XLATOR.ngettext
|
|
40
|
+
npgettext = XLATOR.npgettext
|
|
41
|
+
pgettext = XLATOR.pgettext
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# =============================================================================
|
|
45
|
+
class SearchStorageApp(BaseVmwareApplication):
|
|
46
|
+
"""Class for the application object."""
|
|
47
|
+
|
|
48
|
+
show_simulate_option = False
|
|
49
|
+
default_all_vspheres = False
|
|
50
|
+
|
|
51
|
+
valid_storage_types = []
|
|
52
|
+
for storage_type in VsphereDsCluster.valid_storage_types:
|
|
53
|
+
valid_storage_types.append(storage_type.lower())
|
|
54
|
+
valid_storage_types.append("any")
|
|
55
|
+
|
|
56
|
+
# -------------------------------------------------------------------------
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
appname=None,
|
|
60
|
+
verbose=0,
|
|
61
|
+
version=GLOBAL_VERSION,
|
|
62
|
+
base_dir=None,
|
|
63
|
+
initialized=False,
|
|
64
|
+
usage=None,
|
|
65
|
+
description=None,
|
|
66
|
+
argparse_epilog=None,
|
|
67
|
+
argparse_prefix_chars="-",
|
|
68
|
+
env_prefix=None,
|
|
69
|
+
):
|
|
70
|
+
"""Initialize a GetStorageListApp object."""
|
|
71
|
+
desc = _(
|
|
72
|
+
"Searches for a storage cluster or a datastore for a planned volume of a given size."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.datastores = VsphereDatastoreDict()
|
|
76
|
+
self.ds_clusters = VsphereDsClusterDict()
|
|
77
|
+
|
|
78
|
+
self.vsphere_name = None
|
|
79
|
+
self.cur_vsphere = None
|
|
80
|
+
self.dc = None
|
|
81
|
+
self.cluster = None
|
|
82
|
+
self.cluster_type = None
|
|
83
|
+
|
|
84
|
+
self.disk_size_gb = None
|
|
85
|
+
self.storage_type = None
|
|
86
|
+
|
|
87
|
+
super(SearchStorageApp, self).__init__(
|
|
88
|
+
appname=appname,
|
|
89
|
+
verbose=verbose,
|
|
90
|
+
version=version,
|
|
91
|
+
base_dir=base_dir,
|
|
92
|
+
description=desc,
|
|
93
|
+
initialized=False,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.initialized = True
|
|
97
|
+
|
|
98
|
+
# -------------------------------------------------------------------------
|
|
99
|
+
def init_arg_parser(self):
|
|
100
|
+
"""Public available method to initiate the argument parser."""
|
|
101
|
+
search_options = self.arg_parser.add_argument_group(_("Search options"))
|
|
102
|
+
|
|
103
|
+
search_options.add_argument(
|
|
104
|
+
"-S",
|
|
105
|
+
"--size",
|
|
106
|
+
dest="size",
|
|
107
|
+
type=int,
|
|
108
|
+
metavar=_("GBYTE"),
|
|
109
|
+
action=NonNegativeIntegerOptionAction,
|
|
110
|
+
may_zero=False,
|
|
111
|
+
help=_(
|
|
112
|
+
"The size of the virtual disk, for which a storage location should be searched."
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
typelist = format_list(self.valid_storage_types, do_repr=True)
|
|
117
|
+
help_msg = _(
|
|
118
|
+
"The required storage type of the resulting volume. Valid types are {types}."
|
|
119
|
+
).format(types=typelist, deflt="any")
|
|
120
|
+
search_options.add_argument(
|
|
121
|
+
"-T",
|
|
122
|
+
"--type",
|
|
123
|
+
"--storage-type",
|
|
124
|
+
metavar=_("TYPE"),
|
|
125
|
+
choices=self.valid_storage_types,
|
|
126
|
+
help=help_msg,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
search_options.add_argument(
|
|
130
|
+
"--vs",
|
|
131
|
+
"--vsphere",
|
|
132
|
+
dest="req_vsphere",
|
|
133
|
+
help=_(
|
|
134
|
+
"The vSphere name from configuration, in which the storage should be searched."
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
search_options.add_argument(
|
|
139
|
+
"-D",
|
|
140
|
+
"--dc",
|
|
141
|
+
"--datacenter",
|
|
142
|
+
metavar=_("DATACENTER"),
|
|
143
|
+
dest="dc",
|
|
144
|
+
help=_("The virtual datacenter in vSphere, in which the storage should be searched."),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
search_options.add_argument(
|
|
148
|
+
"--cluster",
|
|
149
|
+
metavar=_("CLUSTER"),
|
|
150
|
+
dest="cluster",
|
|
151
|
+
help=_(
|
|
152
|
+
"The computing cluster, which should be connected with the datastore cluster "
|
|
153
|
+
"or datastore in result."
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
super(SearchStorageApp, self).init_arg_parser()
|
|
158
|
+
|
|
159
|
+
# -------------------------------------------------------------------------
|
|
160
|
+
def add_vsphere_argument(self):
|
|
161
|
+
"""Add a commandline option for selecting the vSphere to use."""
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
# -------------------------------------------------------------------------
|
|
165
|
+
def perform_arg_parser(self):
|
|
166
|
+
"""Evaluate command line parameters."""
|
|
167
|
+
super(SearchStorageApp, self).perform_arg_parser()
|
|
168
|
+
|
|
169
|
+
LOG.debug("Given arguments:\n" + pp(self.args))
|
|
170
|
+
|
|
171
|
+
if getattr(self.args, "size", None) is not None:
|
|
172
|
+
self.disk_size_gb = self.args.size
|
|
173
|
+
|
|
174
|
+
if getattr(self.args, "type", None) is not None:
|
|
175
|
+
self.storage_type = getattr(self.args, "type", None)
|
|
176
|
+
|
|
177
|
+
if self.args.req_vsphere:
|
|
178
|
+
vsphere = self.args.req_vsphere
|
|
179
|
+
self.args.req_vsphere = [vsphere]
|
|
180
|
+
LOG.info(_("Selected vSphere: {}").format(self.colored(vsphere, "CYAN")))
|
|
181
|
+
|
|
182
|
+
if getattr(self.args, "dc", None) is not None and self.args.dc.strip() != "":
|
|
183
|
+
self.dc = self.args.dc.strip()
|
|
184
|
+
|
|
185
|
+
if getattr(self.args, "cluster", None) is not None and self.args.cluster.strip() != "":
|
|
186
|
+
self.cluster = self.args.cluster.strip()
|
|
187
|
+
|
|
188
|
+
# -------------------------------------------------------------------------
|
|
189
|
+
def pre_run(self):
|
|
190
|
+
"""Execute some actions before the main routine."""
|
|
191
|
+
if self.disk_size_gb is None:
|
|
192
|
+
self.disk_size_gb = self.prompt_for_disk_size()
|
|
193
|
+
|
|
194
|
+
storage_type = self.select_storage_type(self.storage_type)
|
|
195
|
+
if storage_type is None:
|
|
196
|
+
self.exit(1)
|
|
197
|
+
self.storage_type = storage_type
|
|
198
|
+
|
|
199
|
+
vs_name = self.select_vsphere()
|
|
200
|
+
self.do_vspheres = [vs_name]
|
|
201
|
+
|
|
202
|
+
super(SearchStorageApp, self).pre_run()
|
|
203
|
+
self.vsphere_name = vs_name
|
|
204
|
+
self.cur_vsphere = self.vsphere[vs_name]
|
|
205
|
+
|
|
206
|
+
dc_name = self.select_datacenter(vs_name, self.dc)
|
|
207
|
+
if dc_name is None:
|
|
208
|
+
self.exit(1)
|
|
209
|
+
self.dc = dc_name
|
|
210
|
+
|
|
211
|
+
(cluster_name, cluster_type) = self.select_computing_cluster(
|
|
212
|
+
vs_name=vs_name, dc_name=dc_name, cluster_name=self.cluster
|
|
213
|
+
)
|
|
214
|
+
if cluster_name[0] is None:
|
|
215
|
+
self.exit(1)
|
|
216
|
+
self.cluster = cluster_name
|
|
217
|
+
self.cluster_type = cluster_type
|
|
218
|
+
|
|
219
|
+
LOG.info(
|
|
220
|
+
_(
|
|
221
|
+
"Searching a storage location in vSphere {vs}, virtual datacenter {dc} "
|
|
222
|
+
"connected with the {cl_type} {cl} "
|
|
223
|
+
"for a disk of {sz} of type {st_type}."
|
|
224
|
+
).format(
|
|
225
|
+
vs=self.colored(vs_name, "CYAN"),
|
|
226
|
+
dc=self.colored(dc_name, "CYAN"),
|
|
227
|
+
cl_type=self.cluster_type,
|
|
228
|
+
cl=self.colored(self.cluster, "CYAN"),
|
|
229
|
+
sz=self.colored(str(self.disk_size_gb) + " GiByte", "CYAN"),
|
|
230
|
+
st_type=self.colored(storage_type, "CYAN"),
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# -------------------------------------------------------------------------
|
|
235
|
+
def _run(self):
|
|
236
|
+
|
|
237
|
+
LOG.debug(_("Starting {a!r}, version {v!r} ...").format(a=self.appname, v=self.version))
|
|
238
|
+
|
|
239
|
+
ret = 0
|
|
240
|
+
try:
|
|
241
|
+
self.get_storages()
|
|
242
|
+
self.search_for_space()
|
|
243
|
+
finally:
|
|
244
|
+
self.cleaning_up()
|
|
245
|
+
|
|
246
|
+
self.exit(ret)
|
|
247
|
+
|
|
248
|
+
# -------------------------------------------------------------------------
|
|
249
|
+
def get_storages(self):
|
|
250
|
+
"""Retrieve all datastore clusters and storages in current vSphere and datacenter."""
|
|
251
|
+
LOG.info(
|
|
252
|
+
_("Collect all datastore clusters and storages in current vSphere and datacenter.")
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
self.get_datastore_clusters()
|
|
256
|
+
len_ds_clusters = len(self.ds_clusters)
|
|
257
|
+
if len_ds_clusters:
|
|
258
|
+
one = pgettext("found_ds_cluster", "one")
|
|
259
|
+
msg = ngettext(
|
|
260
|
+
"Found total {one} datastore cluster in vSphere {vs}, datacenter {dc}.",
|
|
261
|
+
"Found total {nr} datastore clusters in vSphere {vs}, datacenter {dc}.",
|
|
262
|
+
len_ds_clusters,
|
|
263
|
+
)
|
|
264
|
+
msg = msg.format(
|
|
265
|
+
one=self.colored(one, "CYAN"),
|
|
266
|
+
nr=self.colored(str(len_ds_clusters), "CYAN"),
|
|
267
|
+
vs=self.colored(self.vsphere_name, "CYAN"),
|
|
268
|
+
dc=self.colored(self.dc, "CYAN"),
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
msg = _("Did not found a datastore cluster in vSphere {vs}, datacenter {dc}.").format(
|
|
272
|
+
vs=self.colored(self.vsphere_name, "CYAN"),
|
|
273
|
+
dc=self.colored(self.dc, "CYAN"),
|
|
274
|
+
)
|
|
275
|
+
LOG.info(msg)
|
|
276
|
+
|
|
277
|
+
self.get_datastores()
|
|
278
|
+
len_datastores = len(self.datastores)
|
|
279
|
+
if len_datastores:
|
|
280
|
+
one = pgettext("found_datastore", "one")
|
|
281
|
+
msg = ngettext(
|
|
282
|
+
"Found total {one} datastore in vSphere {vs}, datacenter {dc}.",
|
|
283
|
+
"Found total {nr} datastores in vSphere {vs}, datacenter {dc}.",
|
|
284
|
+
len_datastores,
|
|
285
|
+
)
|
|
286
|
+
msg = msg.format(
|
|
287
|
+
one=self.colored(one, "CYAN"),
|
|
288
|
+
nr=self.colored(str(len_datastores), "CYAN"),
|
|
289
|
+
vs=self.colored(self.vsphere_name, "CYAN"),
|
|
290
|
+
dc=self.colored(self.dc, "CYAN"),
|
|
291
|
+
)
|
|
292
|
+
else:
|
|
293
|
+
msg = _("Did not found a datastore in vSphere {vs}, datacenter {dc}.").format(
|
|
294
|
+
vs=self.colored(self.vsphere_name, "CYAN"),
|
|
295
|
+
dc=self.colored(self.dc, "CYAN"),
|
|
296
|
+
)
|
|
297
|
+
LOG.info(msg)
|
|
298
|
+
|
|
299
|
+
if not len_ds_clusters and not len_datastores:
|
|
300
|
+
msg = _(
|
|
301
|
+
"Found neither a datastore cluster nor a datastore in vSphere {vs}, "
|
|
302
|
+
"datacenter {dc}."
|
|
303
|
+
).format(
|
|
304
|
+
vs=self.colored(self.vsphere_name, "CYAN"),
|
|
305
|
+
dc=self.colored(self.dc, "CYAN"),
|
|
306
|
+
)
|
|
307
|
+
LOG.error(msg)
|
|
308
|
+
self.exit(7)
|
|
309
|
+
|
|
310
|
+
# -------------------------------------------------------------------------
|
|
311
|
+
def get_datastore_clusters(self):
|
|
312
|
+
"""Retrieve all datastore clusters in current vSphere and datacenter."""
|
|
313
|
+
self.ds_clusters = VsphereDsClusterDict()
|
|
314
|
+
|
|
315
|
+
# ----------
|
|
316
|
+
def _get_datastore_clusters():
|
|
317
|
+
try:
|
|
318
|
+
self.cur_vsphere.get_ds_clusters(
|
|
319
|
+
vsphere_name=self.vsphere_name,
|
|
320
|
+
search_in_dc=self.dc,
|
|
321
|
+
warn_if_empty=False,
|
|
322
|
+
detailled=True,
|
|
323
|
+
)
|
|
324
|
+
except VSphereExpectedError as e:
|
|
325
|
+
LOG.error(str(e))
|
|
326
|
+
self.exit(6)
|
|
327
|
+
|
|
328
|
+
for cluster_name in self.cur_vsphere.ds_clusters:
|
|
329
|
+
self.ds_clusters.append(self.cur_vsphere.ds_clusters[cluster_name])
|
|
330
|
+
|
|
331
|
+
if self.verbose or self.quiet:
|
|
332
|
+
_get_datastore_clusters()
|
|
333
|
+
else:
|
|
334
|
+
spin_prompt = _("Getting all vSphere storage clusters ...")
|
|
335
|
+
spinner_name = self.get_random_spinner_name()
|
|
336
|
+
with Spinner(spin_prompt, spinner_name):
|
|
337
|
+
_get_datastore_clusters()
|
|
338
|
+
sys.stdout.write(" " * len(spin_prompt))
|
|
339
|
+
sys.stdout.write("\r")
|
|
340
|
+
sys.stdout.flush()
|
|
341
|
+
|
|
342
|
+
if self.verbose > 2:
|
|
343
|
+
LOG.debug(_("Found datastore clusters:") + "\n" + pp(self.ds_clusters.as_list()))
|
|
344
|
+
|
|
345
|
+
# -------------------------------------------------------------------------
|
|
346
|
+
def get_datastores(self):
|
|
347
|
+
"""Retrieve datastores in current vSphere and datacenter."""
|
|
348
|
+
self.datastores = VsphereDatastoreDict()
|
|
349
|
+
|
|
350
|
+
# ----------
|
|
351
|
+
def _get_datastores():
|
|
352
|
+
try:
|
|
353
|
+
self.cur_vsphere.get_datastores(
|
|
354
|
+
vsphere_name=self.vsphere_name,
|
|
355
|
+
search_in_dc=self.dc,
|
|
356
|
+
warn_if_empty=False,
|
|
357
|
+
detailled=True,
|
|
358
|
+
no_local_ds=False,
|
|
359
|
+
)
|
|
360
|
+
except VSphereExpectedError as e:
|
|
361
|
+
LOG.error(str(e))
|
|
362
|
+
self.exit(6)
|
|
363
|
+
|
|
364
|
+
for ds_name in self.cur_vsphere.datastores:
|
|
365
|
+
self.datastores.append(self.cur_vsphere.datastores[ds_name])
|
|
366
|
+
|
|
367
|
+
if self.verbose or self.quiet:
|
|
368
|
+
_get_datastores()
|
|
369
|
+
else:
|
|
370
|
+
spin_prompt = _("Getting all vSphere datastores ...")
|
|
371
|
+
spinner_name = self.get_random_spinner_name()
|
|
372
|
+
with Spinner(spin_prompt, spinner_name):
|
|
373
|
+
_get_datastores()
|
|
374
|
+
sys.stdout.write(" " * len(spin_prompt))
|
|
375
|
+
sys.stdout.write("\r")
|
|
376
|
+
sys.stdout.flush()
|
|
377
|
+
|
|
378
|
+
if self.verbose > 0:
|
|
379
|
+
LOG.debug(_("Found datastores:") + "\n" + pp(self.datastores.as_list()))
|
|
380
|
+
|
|
381
|
+
# -------------------------------------------------------------------------
|
|
382
|
+
def search_for_space(self):
|
|
383
|
+
"""Search in evaluated datastore clusters and datastores for space for a virtual disk."""
|
|
384
|
+
if self.ds_clusters:
|
|
385
|
+
LOG.info(_("Searching for space in evaluated datastore clusters."))
|
|
386
|
+
# LOG.debug(
|
|
387
|
+
# f"Datastore cluster must be connected with computing cluster {self.cluster!r}")
|
|
388
|
+
try:
|
|
389
|
+
ds_cluster = self.ds_clusters.search_space(
|
|
390
|
+
needed_gb=self.disk_size_gb,
|
|
391
|
+
storage_type=self.storage_type,
|
|
392
|
+
reserve_space=False,
|
|
393
|
+
compute_cluster=self.cluster,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
msg = "\n " + self.colored("*", "GREEN") + " "
|
|
397
|
+
msg += _("Found usable datastore cluster:") + "\n\n"
|
|
398
|
+
msg += " " + self.colored(ds_cluster, "CYAN") + "\n"
|
|
399
|
+
|
|
400
|
+
print(msg)
|
|
401
|
+
self.exit(0)
|
|
402
|
+
except VSphereNoDsClusterFoundError as e:
|
|
403
|
+
print()
|
|
404
|
+
LOG.warn(str(e))
|
|
405
|
+
|
|
406
|
+
if self.datastores:
|
|
407
|
+
LOG.info(_("Searching for space in evaluated datastores."))
|
|
408
|
+
# LOG.debug(f"Datastore must be connected with computing cluster {self.cluster!r}")
|
|
409
|
+
try:
|
|
410
|
+
datastore = self.datastores.search_space(
|
|
411
|
+
needed_gb=self.disk_size_gb,
|
|
412
|
+
storage_type=self.storage_type,
|
|
413
|
+
reserve_space=False,
|
|
414
|
+
compute_cluster=self.cluster,
|
|
415
|
+
use_local=True,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
msg = "\n " + self.colored("*", "GREEN") + " "
|
|
419
|
+
msg += _("Found usable datastore:") + "\n\n"
|
|
420
|
+
msg += " " + self.colored(datastore, "CYAN") + "\n"
|
|
421
|
+
|
|
422
|
+
print(msg)
|
|
423
|
+
self.exit(0)
|
|
424
|
+
except VSphereNoDatastoreFoundError as e:
|
|
425
|
+
print()
|
|
426
|
+
LOG.warn(str(e))
|
|
427
|
+
|
|
428
|
+
print()
|
|
429
|
+
LOG.warn(_("No datastore cluster or datastore for the given volume."))
|
|
430
|
+
self.exit(3)
|
|
431
|
+
|
|
432
|
+
# -------------------------------------------------------------------------
|
|
433
|
+
def post_run(self):
|
|
434
|
+
"""Execute some actions after the main routine."""
|
|
435
|
+
super(SearchStorageApp, self).post_run()
|
|
436
|
+
|
|
437
|
+
self.cur_vsphere = None
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
# =============================================================================
|
|
441
|
+
def main():
|
|
442
|
+
"""Entrypoint for search-vsphere-storage."""
|
|
443
|
+
my_path = pathlib.Path(__file__)
|
|
444
|
+
appname = my_path.name
|
|
445
|
+
|
|
446
|
+
locale.setlocale(locale.LC_ALL, "")
|
|
447
|
+
|
|
448
|
+
app = SearchStorageApp(appname=appname)
|
|
449
|
+
app.initialized = True
|
|
450
|
+
|
|
451
|
+
if app.verbose > 2:
|
|
452
|
+
print(_("{c}-Object:\n{a}").format(c=app.__class__.__name__, a=app), file=sys.stderr)
|
|
453
|
+
|
|
454
|
+
try:
|
|
455
|
+
app()
|
|
456
|
+
except KeyboardInterrupt:
|
|
457
|
+
print("\n" + app.colored(_("User interrupt."), "YELLOW"))
|
|
458
|
+
sys.exit(5)
|
|
459
|
+
|
|
460
|
+
return 0
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
# =============================================================================
|
|
464
|
+
if __name__ == "__main__":
|
|
465
|
+
|
|
466
|
+
pass
|
|
467
|
+
|
|
468
|
+
# =============================================================================
|
|
469
|
+
|
|
470
|
+
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@summary: A module containing some useful argparse actions.
|
|
3
|
+
|
|
4
|
+
@author: Frank Brehm
|
|
5
|
+
@contact: frank@brehm-online.com
|
|
6
|
+
@copyright: © 2026 by Frank Brehm, Berlin
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import absolute_import
|
|
10
|
+
|
|
11
|
+
# Standard modules
|
|
12
|
+
import argparse
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
# import os
|
|
16
|
+
# from pathlib import Path
|
|
17
|
+
|
|
18
|
+
# Third party modules
|
|
19
|
+
# from fb_tools.common import is_sequence
|
|
20
|
+
|
|
21
|
+
# Own modules
|
|
22
|
+
from .xlate import XLATOR
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.2"
|
|
25
|
+
LOG = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
_ = XLATOR.gettext
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
class NonNegativeIntegerOptionAction(argparse.Action):
|
|
32
|
+
"""
|
|
33
|
+
It's an argparse action class to ensure a positive integer value.
|
|
34
|
+
|
|
35
|
+
It ensures, that the given value is an integer value, which ist greater or equal to 0.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# -------------------------------------------------------------------------
|
|
39
|
+
def __init__(self, option_strings, may_zero=True, *args, **kwargs):
|
|
40
|
+
"""Initialize the NonNegativeIntegerOptionAction object."""
|
|
41
|
+
self.may_zero = bool(may_zero)
|
|
42
|
+
|
|
43
|
+
super(NonNegativeIntegerOptionAction, self).__init__(
|
|
44
|
+
*args,
|
|
45
|
+
**kwargs,
|
|
46
|
+
option_strings=option_strings,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# -------------------------------------------------------------------------
|
|
50
|
+
def __call__(self, parser, namespace, value, option_string=None):
|
|
51
|
+
"""Check the given value from command line for type and the valid range."""
|
|
52
|
+
try:
|
|
53
|
+
val = int(value)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
msg = _("Got a {c} for converting {v!r} into an integer value: {e}").format(
|
|
56
|
+
c=e.__class__.__name__, v=value, e=e
|
|
57
|
+
)
|
|
58
|
+
raise argparse.ArgumentError(self, msg)
|
|
59
|
+
|
|
60
|
+
if val < 0:
|
|
61
|
+
msg = _("The option must not be negative (given: {}).").format(value)
|
|
62
|
+
raise argparse.ArgumentError(self, msg)
|
|
63
|
+
|
|
64
|
+
if not self.may_zero and val == 0:
|
|
65
|
+
msg = _("The option must not be zero.")
|
|
66
|
+
raise argparse.ArgumentError(self, msg)
|
|
67
|
+
|
|
68
|
+
setattr(namespace, self.dest, val)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# =============================================================================
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# =============================================================================
|
|
77
|
+
|
|
78
|
+
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list
|
fb_vmware/base.py
CHANGED
|
@@ -38,7 +38,7 @@ from .errors import VSphereUnsufficientCredentials
|
|
|
38
38
|
from .errors import VSphereVimFault
|
|
39
39
|
from .xlate import XLATOR
|
|
40
40
|
|
|
41
|
-
__version__ = "1.
|
|
41
|
+
__version__ = "1.2.0"
|
|
42
42
|
|
|
43
43
|
LOG = logging.getLogger(__name__)
|
|
44
44
|
|
|
@@ -308,6 +308,33 @@ class BaseVsphereHandler(HandlingObject):
|
|
|
308
308
|
|
|
309
309
|
return obj
|
|
310
310
|
|
|
311
|
+
# -------------------------------------------------------------------------
|
|
312
|
+
def get_all_objects(self, content, vimtype, name):
|
|
313
|
+
"""Get all appropriate pyvomomi objects with the given criteria."""
|
|
314
|
+
objects = []
|
|
315
|
+
|
|
316
|
+
container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
|
|
317
|
+
for c in container.view:
|
|
318
|
+
if c.name == name:
|
|
319
|
+
objects.append(c)
|
|
320
|
+
|
|
321
|
+
return objects
|
|
322
|
+
|
|
323
|
+
# -------------------------------------------------------------------------
|
|
324
|
+
def get_parents(self, managed_object):
|
|
325
|
+
"""Get the parents of a managed object as an array."""
|
|
326
|
+
parents = []
|
|
327
|
+
if hasattr(managed_object, "parent") and managed_object.parent is not None:
|
|
328
|
+
parent = managed_object.parent
|
|
329
|
+
grand_parents = self.get_parents(parent)
|
|
330
|
+
if grand_parents:
|
|
331
|
+
parents = grand_parents
|
|
332
|
+
parents += [(parent.__class__.__name__, parent.name)]
|
|
333
|
+
|
|
334
|
+
return parents
|
|
335
|
+
|
|
336
|
+
return None
|
|
337
|
+
|
|
311
338
|
# -------------------------------------------------------------------------
|
|
312
339
|
def __del__(self):
|
|
313
340
|
"""Destroy the current Python object in this magic method."""
|