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
fb_vmware/datastore.py
CHANGED
|
@@ -27,12 +27,14 @@ from fb_tools.xlate import format_list
|
|
|
27
27
|
from pyVmomi import vim
|
|
28
28
|
|
|
29
29
|
# Own modules
|
|
30
|
+
from .errors import FbVMWareRuntimeError
|
|
30
31
|
from .errors import VSphereHandlerError
|
|
31
32
|
from .errors import VSphereNameError
|
|
33
|
+
from .errors import VSphereNoDatastoreFoundError
|
|
32
34
|
from .obj import VsphereObject
|
|
33
35
|
from .xlate import XLATOR
|
|
34
36
|
|
|
35
|
-
__version__ = "1.
|
|
37
|
+
__version__ = "1.8.2"
|
|
36
38
|
LOG = logging.getLogger(__name__)
|
|
37
39
|
|
|
38
40
|
_ = XLATOR.gettext
|
|
@@ -63,6 +65,15 @@ class VsphereDatastore(VsphereObject):
|
|
|
63
65
|
"version",
|
|
64
66
|
)
|
|
65
67
|
|
|
68
|
+
valid_storage_types = (
|
|
69
|
+
"NFS",
|
|
70
|
+
"SSD",
|
|
71
|
+
"HDD",
|
|
72
|
+
"LOCAL",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
default_storage_type = "HDD"
|
|
76
|
+
|
|
66
77
|
# -------------------------------------------------------------------------
|
|
67
78
|
def __init__(
|
|
68
79
|
self,
|
|
@@ -109,10 +120,13 @@ class VsphereDatastore(VsphereObject):
|
|
|
109
120
|
self._url = str(url)
|
|
110
121
|
self._for_k8s = False
|
|
111
122
|
|
|
112
|
-
self._storage_type =
|
|
123
|
+
self._storage_type = self.default_storage_type
|
|
113
124
|
|
|
114
125
|
self._calculated_usage = 0.0
|
|
115
126
|
|
|
127
|
+
self.hosts = None
|
|
128
|
+
self.compute_clusters = None
|
|
129
|
+
|
|
116
130
|
super(VsphereDatastore, self).__init__(
|
|
117
131
|
name=name,
|
|
118
132
|
obj_type="vsphere_datastore",
|
|
@@ -285,7 +299,7 @@ class VsphereDatastore(VsphereObject):
|
|
|
285
299
|
# -----------------------------------------------------------
|
|
286
300
|
@property
|
|
287
301
|
def storage_type(self):
|
|
288
|
-
"""Return the type of storage volume, such as
|
|
302
|
+
"""Return the type of storage volume, such as HDD or SSD."""
|
|
289
303
|
return self._storage_type
|
|
290
304
|
|
|
291
305
|
# -----------------------------------------------------------
|
|
@@ -321,6 +335,8 @@ class VsphereDatastore(VsphereObject):
|
|
|
321
335
|
verbose=0,
|
|
322
336
|
base_dir=None,
|
|
323
337
|
test_mode=False,
|
|
338
|
+
detailled=False,
|
|
339
|
+
hostlist=None,
|
|
324
340
|
):
|
|
325
341
|
"""Create a new VsphereDatastore object based on the data given from pyvmomi module."""
|
|
326
342
|
if test_mode:
|
|
@@ -388,8 +404,50 @@ class VsphereDatastore(VsphereObject):
|
|
|
388
404
|
LOG.debug(_("Creating {} object from:").format(cls.__name__) + "\n" + pp(params))
|
|
389
405
|
|
|
390
406
|
ds = cls(**params)
|
|
407
|
+
|
|
408
|
+
if detailled:
|
|
409
|
+
ds.get_hosts(data, hostlist=hostlist)
|
|
410
|
+
|
|
391
411
|
return ds
|
|
392
412
|
|
|
413
|
+
# -------------------------------------------------------------------------
|
|
414
|
+
def get_hosts(self, data, hostlist=None):
|
|
415
|
+
"""Get a list of all connected ESX hosts."""
|
|
416
|
+
if not hasattr(data, "host"):
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
if hostlist is None:
|
|
420
|
+
hostlist = {}
|
|
421
|
+
|
|
422
|
+
self.hosts = set()
|
|
423
|
+
self.compute_clusters = set()
|
|
424
|
+
|
|
425
|
+
for host_data in data.host:
|
|
426
|
+
host_name = host_data.key.name
|
|
427
|
+
self.hosts.add(host_name)
|
|
428
|
+
if host_name not in hostlist:
|
|
429
|
+
parents = self.get_parents(host_data.key)
|
|
430
|
+
if self.verbose > 2:
|
|
431
|
+
LOG.debug(f"Parents of host {host_name!r}:\n" + pp(parents))
|
|
432
|
+
dc = None
|
|
433
|
+
cr = None
|
|
434
|
+
for i in range(len(parents), 0, -1):
|
|
435
|
+
(parent_type, parent_name) = parents[i - 1]
|
|
436
|
+
if parent_type == "vim.Datacenter":
|
|
437
|
+
dc = parent_name
|
|
438
|
+
if parent_type in ("vim.ComputeResource", "vim.ClusterComputeResource"):
|
|
439
|
+
cr = parent_name
|
|
440
|
+
|
|
441
|
+
# hostlist[host_name] = (parents[1][1], parents[3][1])
|
|
442
|
+
hostlist[host_name] = {
|
|
443
|
+
"dc": dc,
|
|
444
|
+
"cr": cr,
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
for host in hostlist:
|
|
448
|
+
compute_cluster = hostlist[host]["cr"]
|
|
449
|
+
self.compute_clusters.add(compute_cluster)
|
|
450
|
+
|
|
393
451
|
# -------------------------------------------------------------------------
|
|
394
452
|
@classmethod
|
|
395
453
|
def storage_type_by_name(cls, name):
|
|
@@ -398,16 +456,19 @@ class VsphereDatastore(VsphereObject):
|
|
|
398
456
|
return "NFS"
|
|
399
457
|
|
|
400
458
|
if "-sas-" in name.lower():
|
|
401
|
-
return "
|
|
459
|
+
return "HDD"
|
|
402
460
|
|
|
403
461
|
if "-ssd-" in name.lower():
|
|
404
462
|
return "SSD"
|
|
405
463
|
|
|
406
464
|
if "-sata-" in name.lower():
|
|
407
|
-
return "
|
|
465
|
+
return "HDD"
|
|
466
|
+
|
|
467
|
+
if "-hdd-" in name.lower():
|
|
468
|
+
return "HDD"
|
|
408
469
|
|
|
409
470
|
if cls.re_vmcb_fs.search(name):
|
|
410
|
-
return "
|
|
471
|
+
return "HDD"
|
|
411
472
|
|
|
412
473
|
if cls.re_local_ds.search(name):
|
|
413
474
|
return "LOCAL"
|
|
@@ -426,6 +487,24 @@ class VsphereDatastore(VsphereObject):
|
|
|
426
487
|
return True
|
|
427
488
|
return False
|
|
428
489
|
|
|
490
|
+
# -----------------------------------------------------------
|
|
491
|
+
def get_pyvmomi_obj(self, service_instance):
|
|
492
|
+
"""Return the appropriate PyVMomi object for the current object."""
|
|
493
|
+
obj = None
|
|
494
|
+
if not self.name:
|
|
495
|
+
return None
|
|
496
|
+
|
|
497
|
+
content = service_instance.RetrieveContent()
|
|
498
|
+
container = content.viewManager.CreateContainerView(
|
|
499
|
+
content.rootFolder, vim.Datastore, True
|
|
500
|
+
)
|
|
501
|
+
for c in container.view:
|
|
502
|
+
if c.name == self.name:
|
|
503
|
+
obj = c
|
|
504
|
+
break
|
|
505
|
+
|
|
506
|
+
return obj
|
|
507
|
+
|
|
429
508
|
# -------------------------------------------------------------------------
|
|
430
509
|
def as_dict(self, short=True):
|
|
431
510
|
"""
|
|
@@ -803,6 +882,116 @@ class VsphereDatastoreDict(MutableMapping, FbGenericBaseObject):
|
|
|
803
882
|
|
|
804
883
|
return ds_name
|
|
805
884
|
|
|
885
|
+
# -------------------------------------------------------------------------
|
|
886
|
+
def search_space(
|
|
887
|
+
self,
|
|
888
|
+
needed_gb,
|
|
889
|
+
storage_type="any",
|
|
890
|
+
reserve_space=True,
|
|
891
|
+
compute_cluster=None,
|
|
892
|
+
use_local=False,
|
|
893
|
+
use_random_select=False,
|
|
894
|
+
):
|
|
895
|
+
"""Find a datastore in dict with the given minimum free space and the given type."""
|
|
896
|
+
st_type = storage_type.lower()
|
|
897
|
+
search_chains = {
|
|
898
|
+
"any": ("hdd", "ssd"),
|
|
899
|
+
"hdd": ("hdd",),
|
|
900
|
+
"ssd": ("ssd",),
|
|
901
|
+
}
|
|
902
|
+
if use_local:
|
|
903
|
+
search_chains["any"] = ("hdd", "ssd", "local")
|
|
904
|
+
search_chains["local"] = ("local",)
|
|
905
|
+
|
|
906
|
+
if st_type not in search_chains:
|
|
907
|
+
raise ValueError(
|
|
908
|
+
_("Could not handle storage type {}.").format(self.colored(storage_type, "RED"))
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
for st_tp in search_chains[st_type]:
|
|
912
|
+
ds_name = self._search_space(
|
|
913
|
+
needed_gb,
|
|
914
|
+
storage_type=st_tp,
|
|
915
|
+
reserve_space=reserve_space,
|
|
916
|
+
compute_cluster=compute_cluster,
|
|
917
|
+
use_random_select=use_random_select,
|
|
918
|
+
)
|
|
919
|
+
if ds_name:
|
|
920
|
+
return ds_name
|
|
921
|
+
|
|
922
|
+
raise VSphereNoDatastoreFoundError(needed_gb)
|
|
923
|
+
|
|
924
|
+
# -------------------------------------------------------------------------
|
|
925
|
+
def _search_space(
|
|
926
|
+
self,
|
|
927
|
+
needed_gb,
|
|
928
|
+
storage_type,
|
|
929
|
+
reserve_space=True,
|
|
930
|
+
compute_cluster=None,
|
|
931
|
+
use_random_select=False,
|
|
932
|
+
):
|
|
933
|
+
|
|
934
|
+
LOG.debug(
|
|
935
|
+
_("Searching datastore for {c:d} GiB of type {t!r}.").format(
|
|
936
|
+
c=needed_gb, t=storage_type
|
|
937
|
+
)
|
|
938
|
+
)
|
|
939
|
+
LOG.debug(_("Given compute cluster: {!r}.").format(compute_cluster))
|
|
940
|
+
|
|
941
|
+
avail_ds_names = []
|
|
942
|
+
spaces = {}
|
|
943
|
+
for ds_name, ds in self.items():
|
|
944
|
+
usable = True
|
|
945
|
+
if ds.storage_type.lower() != storage_type.lower():
|
|
946
|
+
# LOG.debug(f"Datastore {ds_name} has wrong storage type {ds.storage_type}.")
|
|
947
|
+
continue
|
|
948
|
+
if ds.avail_space_gb < needed_gb:
|
|
949
|
+
# LOG.debug(f"Datastore {ds_name} is too small with {ds.avail_space_gb:0f} GB.")
|
|
950
|
+
usable = False
|
|
951
|
+
|
|
952
|
+
if usable and compute_cluster:
|
|
953
|
+
if ds.compute_clusters is None:
|
|
954
|
+
msg = _(
|
|
955
|
+
"Cannot detect connection with compute cluster {cl!r}, datastore "
|
|
956
|
+
"was not detailled discovered."
|
|
957
|
+
).format(ds_name)
|
|
958
|
+
raise FbVMWareRuntimeError(msg)
|
|
959
|
+
found = False
|
|
960
|
+
for cc_name in ds.compute_clusters:
|
|
961
|
+
# LOG.debug(f"Checking for CC {cc_name!r} == {compute_cluster!r}.")
|
|
962
|
+
if cc_name == compute_cluster:
|
|
963
|
+
found = True
|
|
964
|
+
break
|
|
965
|
+
if not found:
|
|
966
|
+
# LOG.debug(
|
|
967
|
+
# f"Datastore {ds_name} is connected with wrong computing clusters: "
|
|
968
|
+
# + pp(ds_cluster.compute_clusters)
|
|
969
|
+
# )
|
|
970
|
+
usable = False
|
|
971
|
+
|
|
972
|
+
if usable:
|
|
973
|
+
avail_ds_names.append(ds_name)
|
|
974
|
+
spaces[ds_name] = ds.avail_space_gb
|
|
975
|
+
|
|
976
|
+
if not avail_ds_names:
|
|
977
|
+
return None
|
|
978
|
+
|
|
979
|
+
if use_random_select:
|
|
980
|
+
ds_name = random.choice(avail_ds_names)
|
|
981
|
+
else:
|
|
982
|
+
ds_name = None
|
|
983
|
+
last_val = 0.0
|
|
984
|
+
for n in spaces.keys():
|
|
985
|
+
if spaces[n] > last_val:
|
|
986
|
+
ds_name = n
|
|
987
|
+
last_val = spaces[n]
|
|
988
|
+
|
|
989
|
+
if reserve_space:
|
|
990
|
+
ds = self[ds_name]
|
|
991
|
+
ds.calculated_usage += needed_gb
|
|
992
|
+
|
|
993
|
+
return ds_name
|
|
994
|
+
|
|
806
995
|
|
|
807
996
|
# =============================================================================
|
|
808
997
|
if __name__ == "__main__":
|
fb_vmware/dc.py
CHANGED
|
@@ -23,7 +23,7 @@ from .obj import DEFAULT_OBJ_STATUS
|
|
|
23
23
|
from .obj import VsphereObject
|
|
24
24
|
from .xlate import XLATOR
|
|
25
25
|
|
|
26
|
-
__version__ = "1.
|
|
26
|
+
__version__ = "1.1.1"
|
|
27
27
|
LOG = logging.getLogger(__name__)
|
|
28
28
|
|
|
29
29
|
DEFAULT_HOST_FOLDER = "host"
|
|
@@ -133,6 +133,24 @@ class VsphereDatacenter(VsphereObject):
|
|
|
133
133
|
"""Key for Maximum Hardware Version used on this datacenter."""
|
|
134
134
|
return self._max_hw_version_key
|
|
135
135
|
|
|
136
|
+
# -----------------------------------------------------------
|
|
137
|
+
def get_pyvmomi_obj(self, service_instance):
|
|
138
|
+
"""Return the appropriate PyVMomi object for the current object."""
|
|
139
|
+
obj = None
|
|
140
|
+
if not self.name:
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
content = service_instance.RetrieveContent()
|
|
144
|
+
container = content.viewManager.CreateContainerView(
|
|
145
|
+
content.rootFolder, vim.Datacenter, True
|
|
146
|
+
)
|
|
147
|
+
for c in container.view:
|
|
148
|
+
if c.name == self.name:
|
|
149
|
+
obj = c
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
return obj
|
|
153
|
+
|
|
136
154
|
# -------------------------------------------------------------------------
|
|
137
155
|
def as_dict(self, short=True):
|
|
138
156
|
"""
|
fb_vmware/ds_cluster.py
CHANGED
|
@@ -11,6 +11,7 @@ from __future__ import absolute_import
|
|
|
11
11
|
|
|
12
12
|
# Standard modules
|
|
13
13
|
import logging
|
|
14
|
+
import random
|
|
14
15
|
|
|
15
16
|
try:
|
|
16
17
|
from collections.abc import MutableMapping
|
|
@@ -25,12 +26,16 @@ from fb_tools.xlate import format_list
|
|
|
25
26
|
from pyVmomi import vim
|
|
26
27
|
|
|
27
28
|
# Own modules
|
|
29
|
+
from .datastore import VsphereDatastore
|
|
30
|
+
from .datastore import VsphereDatastoreDict
|
|
31
|
+
from .errors import FbVMWareRuntimeError
|
|
28
32
|
from .errors import VSphereHandlerError
|
|
29
33
|
from .errors import VSphereNameError
|
|
34
|
+
from .errors import VSphereNoDsClusterFoundError
|
|
30
35
|
from .obj import VsphereObject
|
|
31
36
|
from .xlate import XLATOR
|
|
32
37
|
|
|
33
|
-
__version__ = "1.
|
|
38
|
+
__version__ = "1.8.2"
|
|
34
39
|
LOG = logging.getLogger(__name__)
|
|
35
40
|
|
|
36
41
|
_ = XLATOR.gettext
|
|
@@ -53,6 +58,13 @@ class VsphereDsCluster(VsphereObject):
|
|
|
53
58
|
"version",
|
|
54
59
|
)
|
|
55
60
|
|
|
61
|
+
valid_storage_types = (
|
|
62
|
+
"SSD",
|
|
63
|
+
"HDD",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
default_storage_type = "HDD"
|
|
67
|
+
|
|
56
68
|
# -------------------------------------------------------------------------
|
|
57
69
|
def __init__(
|
|
58
70
|
self,
|
|
@@ -74,8 +86,12 @@ class VsphereDsCluster(VsphereObject):
|
|
|
74
86
|
self._dc_name = None
|
|
75
87
|
self._capacity = int(capacity)
|
|
76
88
|
self._free_space = int(free_space)
|
|
89
|
+
self.datastores = None
|
|
90
|
+
self.hosts = None
|
|
91
|
+
self.compute_clusters = None
|
|
77
92
|
|
|
78
93
|
self._calculated_usage = 0.0
|
|
94
|
+
self._storage_type = self.default_storage_type
|
|
79
95
|
|
|
80
96
|
super(VsphereDsCluster, self).__init__(
|
|
81
97
|
name=name,
|
|
@@ -92,6 +108,10 @@ class VsphereDsCluster(VsphereObject):
|
|
|
92
108
|
self.vsphere = vsphere
|
|
93
109
|
self.dc_name = dc_name
|
|
94
110
|
|
|
111
|
+
st_type = self.storage_type_by_name(self.name)
|
|
112
|
+
if st_type:
|
|
113
|
+
self._storage_type = st_type
|
|
114
|
+
|
|
95
115
|
if initialized is not None:
|
|
96
116
|
self.initialized = initialized
|
|
97
117
|
|
|
@@ -178,6 +198,30 @@ class VsphereDsCluster(VsphereObject):
|
|
|
178
198
|
|
|
179
199
|
self._vsphere = val
|
|
180
200
|
|
|
201
|
+
# -----------------------------------------------------------
|
|
202
|
+
@property
|
|
203
|
+
def storage_type(self):
|
|
204
|
+
"""Return the type of storage volume, such as HDD or SSD."""
|
|
205
|
+
return self._storage_type
|
|
206
|
+
|
|
207
|
+
# -------------------------------------------------------------------------
|
|
208
|
+
@classmethod
|
|
209
|
+
def storage_type_by_name(cls, name):
|
|
210
|
+
"""Guess the storage type by its name. May be overridden in descentant classes."""
|
|
211
|
+
if "-sas-" in name.lower():
|
|
212
|
+
return "HDD"
|
|
213
|
+
|
|
214
|
+
if "-ssd-" in name.lower():
|
|
215
|
+
return "SSD"
|
|
216
|
+
|
|
217
|
+
if "-sata-" in name.lower():
|
|
218
|
+
return "HDD"
|
|
219
|
+
|
|
220
|
+
if "-hdd-" in name.lower():
|
|
221
|
+
return "HDD"
|
|
222
|
+
|
|
223
|
+
return None
|
|
224
|
+
|
|
181
225
|
# -------------------------------------------------------------------------
|
|
182
226
|
@classmethod
|
|
183
227
|
def from_summary(
|
|
@@ -189,6 +233,7 @@ class VsphereDsCluster(VsphereObject):
|
|
|
189
233
|
verbose=0,
|
|
190
234
|
base_dir=None,
|
|
191
235
|
test_mode=False,
|
|
236
|
+
detailled=False,
|
|
192
237
|
):
|
|
193
238
|
"""Create a new VsphereDsCluster object based on the data given from pyvmomi."""
|
|
194
239
|
if test_mode:
|
|
@@ -239,10 +284,71 @@ class VsphereDsCluster(VsphereObject):
|
|
|
239
284
|
|
|
240
285
|
if verbose > 2:
|
|
241
286
|
LOG.debug(_("Creating {} object from:").format(cls.__name__) + "\n" + pp(params))
|
|
242
|
-
|
|
243
287
|
cluster = cls(**params)
|
|
288
|
+
|
|
289
|
+
if detailled:
|
|
290
|
+
cluster.get_detailled_info(data)
|
|
291
|
+
|
|
244
292
|
return cluster
|
|
245
293
|
|
|
294
|
+
# -----------------------------------------------------------
|
|
295
|
+
def get_detailled_info(self, data):
|
|
296
|
+
"""Get detailled infos about owning datastores and connected hosts."""
|
|
297
|
+
if not hasattr(data, "childEntity"):
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
self.datastores = VsphereDatastoreDict()
|
|
301
|
+
self.hosts = set()
|
|
302
|
+
self.compute_clusters = set()
|
|
303
|
+
|
|
304
|
+
hostlist = {}
|
|
305
|
+
|
|
306
|
+
for child in data.childEntity:
|
|
307
|
+
if isinstance(child, vim.Datastore):
|
|
308
|
+
if self.verbose > 1:
|
|
309
|
+
LOG.debug(
|
|
310
|
+
_("Datastore {ds!r} is assigned to datastore_cluster {dsc!r}.").format(
|
|
311
|
+
ds=child.name, dsc=self.name
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
ds = VsphereDatastore.from_summary(
|
|
315
|
+
child,
|
|
316
|
+
vsphere=self.vsphere,
|
|
317
|
+
dc_name=self.dc_name,
|
|
318
|
+
cluster=self.name,
|
|
319
|
+
appname=self.appname,
|
|
320
|
+
verbose=self.verbose,
|
|
321
|
+
base_dir=self.base_dir,
|
|
322
|
+
detailled=True,
|
|
323
|
+
hostlist=hostlist,
|
|
324
|
+
)
|
|
325
|
+
self.datastores.append(ds)
|
|
326
|
+
|
|
327
|
+
for host in ds.hosts:
|
|
328
|
+
self.hosts.add(host)
|
|
329
|
+
|
|
330
|
+
if ds.compute_clusters:
|
|
331
|
+
for compute_cluster in ds.compute_clusters:
|
|
332
|
+
self.compute_clusters.add(compute_cluster)
|
|
333
|
+
|
|
334
|
+
# -----------------------------------------------------------
|
|
335
|
+
def get_pyvmomi_obj(self, service_instance):
|
|
336
|
+
"""Return the appropriate PyVMomi object for the current object."""
|
|
337
|
+
obj = None
|
|
338
|
+
if not self.name:
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
content = service_instance.RetrieveContent()
|
|
342
|
+
container = content.viewManager.CreateContainerView(
|
|
343
|
+
content.rootFolder, vim.StoragePod, True
|
|
344
|
+
)
|
|
345
|
+
for c in container.view:
|
|
346
|
+
if c.name == self.name:
|
|
347
|
+
obj = c
|
|
348
|
+
break
|
|
349
|
+
|
|
350
|
+
return obj
|
|
351
|
+
|
|
246
352
|
# -------------------------------------------------------------------------
|
|
247
353
|
def as_dict(self, short=True):
|
|
248
354
|
"""
|
|
@@ -262,6 +368,7 @@ class VsphereDsCluster(VsphereObject):
|
|
|
262
368
|
res["dc_name"] = self.dc_name
|
|
263
369
|
res["free_space"] = self.free_space
|
|
264
370
|
res["free_space_gb"] = self.free_space_gb
|
|
371
|
+
res["storage_type"] = self.storage_type
|
|
265
372
|
res["vsphere"] = self.vsphere
|
|
266
373
|
|
|
267
374
|
return res
|
|
@@ -550,6 +657,112 @@ class VsphereDsClusterDict(MutableMapping, FbGenericBaseObject):
|
|
|
550
657
|
res.append(self._map[cluster_name].as_dict(short))
|
|
551
658
|
return res
|
|
552
659
|
|
|
660
|
+
# -------------------------------------------------------------------------
|
|
661
|
+
def search_space(
|
|
662
|
+
self,
|
|
663
|
+
needed_gb,
|
|
664
|
+
storage_type="any",
|
|
665
|
+
reserve_space=True,
|
|
666
|
+
compute_cluster=None,
|
|
667
|
+
use_random_select=False,
|
|
668
|
+
):
|
|
669
|
+
"""Find a datastore cluster with the given minimum free space and the given type."""
|
|
670
|
+
st_type = storage_type.lower()
|
|
671
|
+
search_chains = {
|
|
672
|
+
"any": ("hdd", "ssd"),
|
|
673
|
+
"hdd": ("hdd",),
|
|
674
|
+
"ssd": ("ssd",),
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if st_type not in search_chains:
|
|
678
|
+
raise ValueError(
|
|
679
|
+
_("Could not handle storage type {}.").format(self.colored(storage_type, "RED"))
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
for st_tp in search_chains[st_type]:
|
|
683
|
+
ds_cluster_name = self._search_space(
|
|
684
|
+
needed_gb,
|
|
685
|
+
storage_type=st_tp,
|
|
686
|
+
reserve_space=reserve_space,
|
|
687
|
+
compute_cluster=compute_cluster,
|
|
688
|
+
use_random_select=use_random_select,
|
|
689
|
+
)
|
|
690
|
+
if ds_cluster_name:
|
|
691
|
+
return ds_cluster_name
|
|
692
|
+
|
|
693
|
+
raise VSphereNoDsClusterFoundError(needed_gb)
|
|
694
|
+
|
|
695
|
+
# -------------------------------------------------------------------------
|
|
696
|
+
def _search_space(
|
|
697
|
+
self,
|
|
698
|
+
needed_gb,
|
|
699
|
+
storage_type,
|
|
700
|
+
reserve_space=True,
|
|
701
|
+
compute_cluster=None,
|
|
702
|
+
use_random_select=False,
|
|
703
|
+
):
|
|
704
|
+
|
|
705
|
+
LOG.debug(
|
|
706
|
+
_("Searching datastore cluster for {c:d} GiB of type {t!r}.").format(
|
|
707
|
+
c=needed_gb, t=storage_type
|
|
708
|
+
)
|
|
709
|
+
)
|
|
710
|
+
LOG.debug(_("Given compute cluster: {!r}.").format(compute_cluster))
|
|
711
|
+
|
|
712
|
+
avail_dsc_names = []
|
|
713
|
+
spaces = {}
|
|
714
|
+
for dsc_name, dsc in self.items():
|
|
715
|
+
usable = True
|
|
716
|
+
if dsc.storage_type.lower() != storage_type.lower():
|
|
717
|
+
# LOG.debug(f"DS cluster {dsc_name} has wrong storage type {ds.storage_type}.")
|
|
718
|
+
continue
|
|
719
|
+
if dsc.avail_space_gb < needed_gb:
|
|
720
|
+
# LOG.debug(f"DS cluster {dsc_name} is too small with {ds.avail_space_gb:0f} GB.")
|
|
721
|
+
usable = False
|
|
722
|
+
|
|
723
|
+
if usable and compute_cluster:
|
|
724
|
+
if dsc.compute_clusters is None:
|
|
725
|
+
msg = _(
|
|
726
|
+
"Cannot detect connection with compute cluster {cl!r}, datastore cluster "
|
|
727
|
+
"was not detailled discovered."
|
|
728
|
+
).format(dsc_name)
|
|
729
|
+
raise FbVMWareRuntimeError(msg)
|
|
730
|
+
found = False
|
|
731
|
+
for cc_name in dsc.compute_clusters:
|
|
732
|
+
# LOG.debug(f"Checking for CC {cc_name!r} == {compute_cluster!r}.")
|
|
733
|
+
if cc_name == compute_cluster:
|
|
734
|
+
found = True
|
|
735
|
+
break
|
|
736
|
+
if not found:
|
|
737
|
+
# LOG.debug(
|
|
738
|
+
# f"DS cluster {dsc_name} is connected with wrong computing clusters: "
|
|
739
|
+
# + pp(dsc.compute_clusters)
|
|
740
|
+
# )
|
|
741
|
+
usable = False
|
|
742
|
+
|
|
743
|
+
if usable:
|
|
744
|
+
avail_dsc_names.append(dsc_name)
|
|
745
|
+
spaces[dsc_name] = dsc.avail_space_gb
|
|
746
|
+
|
|
747
|
+
if not avail_dsc_names:
|
|
748
|
+
return None
|
|
749
|
+
|
|
750
|
+
if use_random_select:
|
|
751
|
+
dsc_name = random.choice(avail_dsc_names)
|
|
752
|
+
else:
|
|
753
|
+
dsc_name = None
|
|
754
|
+
last_val = 0.0
|
|
755
|
+
for n in spaces.keys():
|
|
756
|
+
if spaces[n] > last_val:
|
|
757
|
+
dsc_name = n
|
|
758
|
+
last_val = spaces[n]
|
|
759
|
+
|
|
760
|
+
if reserve_space:
|
|
761
|
+
dsc = self[dsc_name]
|
|
762
|
+
dsc.calculated_usage += needed_gb
|
|
763
|
+
|
|
764
|
+
return dsc_name
|
|
765
|
+
|
|
553
766
|
|
|
554
767
|
# =============================================================================
|
|
555
768
|
|
fb_vmware/dvs.py
CHANGED
|
@@ -29,7 +29,7 @@ from .obj import DEFAULT_OBJ_STATUS
|
|
|
29
29
|
from .obj import VsphereObject
|
|
30
30
|
from .xlate import XLATOR
|
|
31
31
|
|
|
32
|
-
__version__ = "
|
|
32
|
+
__version__ = "1.1.0"
|
|
33
33
|
LOG = logging.getLogger(__name__)
|
|
34
34
|
|
|
35
35
|
_ = XLATOR.gettext
|
|
@@ -299,6 +299,24 @@ class VsphereDVS(VsphereObject):
|
|
|
299
299
|
|
|
300
300
|
self._vsphere = val
|
|
301
301
|
|
|
302
|
+
# -----------------------------------------------------------
|
|
303
|
+
def get_pyvmomi_obj(self, service_instance):
|
|
304
|
+
"""Return the appropriate PyVMomi object for the current object."""
|
|
305
|
+
obj = None
|
|
306
|
+
if not self.name:
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
content = service_instance.RetrieveContent()
|
|
310
|
+
container = content.viewManager.CreateContainerView(
|
|
311
|
+
content.rootFolder, vim.DistributedVirtualSwitch, True
|
|
312
|
+
)
|
|
313
|
+
for c in container.view:
|
|
314
|
+
if c.name == self.name:
|
|
315
|
+
obj = c
|
|
316
|
+
break
|
|
317
|
+
|
|
318
|
+
return obj
|
|
319
|
+
|
|
302
320
|
# -------------------------------------------------------------------------
|
|
303
321
|
def as_dict(self, short=True):
|
|
304
322
|
"""
|
|
@@ -707,6 +725,24 @@ class VsphereDvPortGroup(VsphereNetwork):
|
|
|
707
725
|
return
|
|
708
726
|
self._uplink = to_bool(value)
|
|
709
727
|
|
|
728
|
+
# -----------------------------------------------------------
|
|
729
|
+
def get_pyvmomi_obj(self, service_instance):
|
|
730
|
+
"""Return the appropriate PyVMomi object for the current object."""
|
|
731
|
+
obj = None
|
|
732
|
+
if not self.name:
|
|
733
|
+
return None
|
|
734
|
+
|
|
735
|
+
content = service_instance.RetrieveContent()
|
|
736
|
+
container = content.viewManager.CreateContainerView(
|
|
737
|
+
content.rootFolder, vim.dvs.DistributedVirtualPortgroup, True
|
|
738
|
+
)
|
|
739
|
+
for c in container.view:
|
|
740
|
+
if c.name == self.name:
|
|
741
|
+
obj = c
|
|
742
|
+
break
|
|
743
|
+
|
|
744
|
+
return obj
|
|
745
|
+
|
|
710
746
|
# -------------------------------------------------------------------------
|
|
711
747
|
def as_dict(self, short=True):
|
|
712
748
|
"""
|