Skip to content

Commit 903d954

Browse files
authored
VRAE-3223: get datastore info (#192)
* allow pytest to run from other directories by looking for vcenter.conf relative to conftest.py * rename get_mors_type to get_parent_of_type * added get_cluster and get_clusters * VRAE-3223: get info about datastores * use existing cluster.get_cluster * move get_clusters to utils/cluster.py * fix bug in _find_filtered object to raise if datacenter_name isn't present when cluster_name is the most specific name * restructure datastore utils code and tests * comment: we are so bad (vcenter.conf)
1 parent d629079 commit 903d954

14 files changed

Lines changed: 638 additions & 281 deletions

File tree

src/saltext/vmware/modules/datastore.py

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
# SPDX-License: Apache-2.0
33
import logging
44

5-
import salt.exceptions
6-
import saltext.vmware.utils.common as utils_common
5+
import saltext.vmware.utils.datastore as utils_datastore
76
from saltext.vmware.utils.connect import get_service_instance
87

98
log = logging.getLogger(__name__)
@@ -40,11 +39,12 @@ def maintenance_mode(datastore_name, datacenter_name=None, service_instance=None
4039
"""
4140
if service_instance is None:
4241
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
43-
dc_ref = None
44-
if datacenter_name:
45-
dc_ref = utils_common.get_datacenter(service_instance, datacenter_name)
46-
ds = utils_common.get_datastore(datastore_name, dc_ref, service_instance)
47-
ret = utils_common.datastore_enter_maintenance_mode(ds)
42+
assert isinstance(datastore_name, str)
43+
datastores = utils_datastore.get_datastores(
44+
service_instance, datastore_name=datastore_name, datacenter_name=datacenter_name
45+
)
46+
ds = datastores[0] if datastores else None
47+
ret = utils_datastore.enter_maintenance_mode(ds)
4848
if ret:
4949
return {"maintenanceMode": "inMaintenance"}
5050
return {"maintenanceMode": "failed to enter maintenance mode"}
@@ -65,11 +65,68 @@ def exit_maintenance_mode(datastore_name, datacenter_name=None, service_instance
6565
"""
6666
if service_instance is None:
6767
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
68-
dc_ref = None
69-
if datacenter_name:
70-
dc_ref = utils_common.get_datacenter(service_instance, datacenter_name)
71-
ds = utils_common.get_datastore(datastore_name, dc_ref, service_instance)
72-
ret = utils_common.datastore_exit_maintenance_mode(ds)
68+
assert isinstance(datastore_name, str)
69+
datastores = utils_datastore.get_datastores(
70+
service_instance, datastore_name=datastore_name, datacenter_name=datacenter_name
71+
)
72+
ds = datastores[0] if datastores else None
73+
ret = utils_datastore.exit_maintenance_mode(ds)
7374
if ret:
7475
return {"maintenanceMode": "normal"}
7576
return {"maintenanceMode": "failed to exit maintenance mode"}
77+
78+
79+
def get(
80+
datastore_name=None,
81+
datacenter_name=None,
82+
cluster_name=None,
83+
host_name=None,
84+
service_instance=None,
85+
):
86+
"""
87+
Return info about datastores.
88+
89+
datacenter_name
90+
Filter by this datacenter name (required when cluster is not specified)
91+
92+
datacenter_name
93+
Filter by this datacenter name (required when cluster is not specified)
94+
95+
cluster_name
96+
Filter by this cluster name (required when datacenter is not specified)
97+
98+
host_name
99+
Filter by this ESXi hostname (optional).
100+
101+
service_instance
102+
Use this vCenter service connection instance instead of creating a new one. (optional).
103+
104+
"""
105+
log.debug(f"Running {__virtualname__}.get")
106+
ret = []
107+
if not service_instance:
108+
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
109+
datastores = utils_datastore.get_datastores(
110+
service_instance,
111+
datastore_name=datastore_name,
112+
datacenter_name=datacenter_name,
113+
cluster_name=cluster_name,
114+
host_name=host_name,
115+
)
116+
117+
for datastore in datastores:
118+
summary = datastore.summary
119+
info = {
120+
"accessible": summary.accessible,
121+
"capacity": summary.capacity,
122+
"freeSpace": summary.freeSpace,
123+
"maintenanceMode": summary.maintenanceMode,
124+
"multipleHostAccess": summary.multipleHostAccess,
125+
"name": summary.name,
126+
"type": summary.type,
127+
"url": summary.url,
128+
"uncommitted": summary.uncommitted if summary.uncommitted else 0,
129+
}
130+
ret.append(info)
131+
132+
return ret

src/saltext/vmware/modules/vm.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import salt.utils.platform
66
import saltext.vmware.utils.common as utils_common
77
import saltext.vmware.utils.connect as connect
8+
import saltext.vmware.utils.datastore as utils_datastore
89
import saltext.vmware.utils.vm as utils_vm
910

1011
log = logging.getLogger(__name__)
@@ -509,9 +510,11 @@ def relocate(vm_name, new_host_name, datastore_name, service_instance=None):
509510
service_instance = connect.get_service_instance(opts=__opts__, pillar=__pillar__)
510511
vm_ref = utils_common.get_mor_by_property(service_instance, vim.VirtualMachine, vm_name)
511512
resources = utils_common.deployment_resources(new_host_name, service_instance)
512-
datastore_ref = utils_common.get_datastore(
513-
datastore_name, resources["datacenter"], service_instance
513+
assert isinstance(datastore_name, str)
514+
datastores = utils_datastore.get_datastores(
515+
service_instance, datastore_name=datastore_name, datacenter_name=datacenter_name
514516
)
517+
datastore_ref = datastores[0] if datastores else None
515518
ret = utils_vm.relocate(
516519
vm_ref, resources["destination_host"], datastore_ref, resources["resource_pool"]
517520
)

src/saltext/vmware/states/datastore.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# SPDX-License-Identifier: Apache-2.0
22
import logging
33

4-
import saltext.vmware.utils.common as utils_common
54
import saltext.vmware.utils.connect as connect
6-
import saltext.vmware.utils.vm as utils_vm
7-
from saltext.vmware.modules.tag import update
5+
import saltext.vmware.utils.datastore as utils_datastore
86

97
log = logging.getLogger(__name__)
108

@@ -45,10 +43,11 @@ def maintenance_mode(name, enter_maintenance_mode, datacenter_name=None, service
4543
if service_instance is None:
4644
service_instance = connect.get_service_instance(opts=__opts__, pillar=__pillar__)
4745
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
48-
dc_ref = None
49-
if datacenter_name:
50-
dc_ref = utils_common.get_datacenter(service_instance, datacenter_name)
51-
ds = utils_common.get_datastore(name, dc_ref, service_instance)
46+
assert isinstance(name, str)
47+
datastores = utils_datastore.get_datastores(
48+
service_instance, datastore_name=name, datacenter_name=datacenter_name
49+
)
50+
ds = datastores[0] if datastores else None
5251
status = ds.summary.maintenanceMode
5352
if enter_maintenance_mode:
5453
if status == "inMaintenance":
@@ -61,7 +60,7 @@ def maintenance_mode(name, enter_maintenance_mode, datacenter_name=None, service
6160
ret["comment"] = "These options are set to change."
6261
return ret
6362

64-
mode = utils_common.datastore_enter_maintenance_mode(ds)
63+
mode = utils_datastore.enter_maintenance_mode(ds)
6564
if mode:
6665
ret["changes"] = {"new": f"datastore {name} is in maintenance mode."}
6766
ret["comment"] = "These options changed."
@@ -80,7 +79,7 @@ def maintenance_mode(name, enter_maintenance_mode, datacenter_name=None, service
8079
ret["comment"] = "These options are set to change."
8180
return ret
8281

83-
mode = utils_common.datastore_exit_maintenance_mode(ds)
82+
mode = utils_datastore.exit_maintenance_mode(ds)
8483
if mode:
8584
ret["changes"] = {"new": f"datastore {name} exited maintenance mode."}
8685
ret["comment"] = "These options changed."

src/saltext/vmware/states/vm.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import saltext.vmware.utils.common as utils_common
55
import saltext.vmware.utils.connect as connect
6+
import saltext.vmware.utils.datastore as utils_datastore
67
import saltext.vmware.utils.vm as utils_vm
78

89
log = logging.getLogger(__name__)
@@ -244,9 +245,11 @@ def relocate(name, new_host_name, datastore_name, service_instance=None):
244245
ret["comment"] = f"{name} virtual machine is already on host {new_host_name}"
245246
return ret
246247
resources = utils_common.deployment_resources(new_host_name, service_instance)
247-
datastore_ref = utils_common.get_datastore(
248-
datastore_name, resources["datacenter"], service_instance
248+
assert isinstance(datastore_name, str)
249+
datastores = utils_datastore.get_datastores(
250+
service_instance, datastore_name=datastore_name, datacenter_name=resources["datacenter"]
249251
)
252+
datastore_ref = datastores[0] if datastores else None
250253
if __opts__["test"]:
251254
ret["changes"]["new"] = f"{name} virtual machine will be moved to host {new_host_name}"
252255
ret["comment"] = "These options are set to change."

src/saltext/vmware/utils/cluster.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,40 @@
1515
log = logging.getLogger(__name__)
1616

1717

18+
def get_clusters(service_instance, datacenter_name=None, cluster_name=None):
19+
"""
20+
Returns clusters in a vCenter.
21+
22+
service_instance
23+
The Service Instance Object from which to obtain cluster.
24+
25+
datacenter_name
26+
(Optional) Datacenter name to filter by.
27+
28+
cluster_name
29+
(Optional) Exact cluster name to filter by. Requires datacenter_name.
30+
"""
31+
if cluster_name and not datacenter_name:
32+
raise salt.exceptions.ArgumentValueError(
33+
"datacenter_name is required when looking up by cluster_name"
34+
)
35+
36+
clusters = []
37+
for cluster in utils_common.get_mors_with_properties(
38+
service_instance, vim.ClusterComputeResource, property_list=["name"]
39+
):
40+
if cluster_name and cluster_name != cluster["name"]:
41+
continue
42+
if (
43+
datacenter_name
44+
and datacenter_name
45+
!= utils_common.get_parent_of_type(cluster["object"], vim.Datacenter).name
46+
):
47+
continue
48+
clusters.append(cluster["object"])
49+
return clusters
50+
51+
1852
def get_cluster(dc_ref, cluster):
1953
"""
2054
Returns a cluster in a datacenter.

src/saltext/vmware/utils/common.py

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import salt.utils.platform
1616
import salt.utils.stringutils
1717

18-
1918
try:
2019
from pyVmomi import vim, vmodl
2120

@@ -435,6 +434,8 @@ def get_resource_pools(
435434
if not resource_pool_names:
436435
resource_pool_names = []
437436
if datacenter_name:
437+
import saltext.vmware.utils.datacenter as utils_datacenter
438+
438439
container_ref = utils_datacenter.get_datacenter(service_instance, datacenter_name)
439440
else:
440441
container_ref = get_root_folder(service_instance)
@@ -737,84 +738,67 @@ def delete_datacenter(service_instance, datacenter_name):
737738
wait_for_task(task, datacenter_name, "DeleteDatacenterTask")
738739

739740

740-
def get_vm_datacenter(*, vm):
741+
def get_parent_of_type(mors, type):
741742
"""
742-
Return a datacenter from vm
743-
"""
744-
datacenter = None
745-
while True:
746-
if isinstance(vm, vim.Datacenter):
747-
datacenter = vm
748-
break
749-
try:
750-
vm = vm.parent
751-
except AttributeError:
752-
break
753-
return datacenter
754-
743+
Finds the first parent of a managed object that matches the type specified.
755744
756-
def get_mors_type(obj, type):
745+
`None` is returned if no object is found.
757746
"""
758-
Return a vim type from managed object reference
759-
"""
760-
datacenter = None
761747
while True:
762-
if isinstance(obj, type):
763-
datacenter = obj
764-
break
748+
if isinstance(mors, type):
749+
return mors
765750
try:
766-
obj = obj.parent
751+
mors = mors.parent
767752
except AttributeError:
768-
break
769-
return datacenter
753+
return None
770754

771755

772-
def get_datastore(name, datacenter, service_instance):
756+
def find_filtered_object(service_instance, datacenter_name=None, cluster_name=None, host_name=None):
773757
"""
774-
Returns reference of datastore.
775-
776-
name
777-
Name of datastore.
778-
779-
datacenter
780-
Reference to datacenter.
758+
Finds zero or one matching objects: plug in almost any combination of datacenter, cluster, and/or host name.
781759
782-
service_instance
783-
The Service Instance from which to obtain managed object references.
784-
"""
785-
if datacenter is None:
786-
ds = get_mor_by_property(service_instance, vim.Datastore, name)
787-
else:
788-
ds = get_mor_by_property(service_instance, vim.Datastore, name, "name", datacenter)
789-
return ds
760+
If cluster_name is passed, datacenter_name must also be passed.
790761
762+
At least one of the optional parameters must be set.
791763
792-
def datastore_enter_maintenance_mode(datastore_ref):
793-
"""
794-
Put datastore in maintenance mode.
764+
The most specific object will be returned (if you pass host_name and datacenter_name, the host will be returned).
795765
796-
datastore_ref
797-
Reference to datastore.
798-
"""
799-
ret = datastore_ref.DatastoreEnterMaintenanceMode()
800-
if ret.task.info.state == "success":
801-
return True
802-
else:
803-
return False
766+
service_instance
767+
The Service Instance Object from which to obtain cluster.
804768
769+
datacenter_name
770+
(Optional) Datacenter name to filter by.
805771
806-
def datastore_exit_maintenance_mode(datastore_ref):
807-
"""
808-
Take datastore out of maintenance mode.
772+
cluster_name
773+
(Optional) Exact cluster name to filter by. If used, datacenter_name is required.
809774
810-
datastore_ref
811-
Reference to datastore.
775+
host_name
776+
(Optional) Exact host name name to filter by.
812777
"""
813-
task = datastore_ref.DatastoreExitMaintenanceMode_Task()
814-
wait_for_task(task, datastore_ref.name, "Take datastore out of maintenance mode")
815-
if datastore_ref.summary.maintenanceMode == "normal":
816-
return True
817-
return False
778+
try:
779+
if host_name:
780+
import saltext.vmware.utils.esxi as utils_esxi
781+
782+
hosts = utils_esxi.get_hosts(
783+
service_instance,
784+
datacenter_name=datacenter_name,
785+
cluster_name=cluster_name,
786+
host_names=[host_name],
787+
)
788+
return hosts[0] if hosts else None
789+
elif cluster_name and datacenter_name:
790+
import saltext.vmware.utils.cluster as utils_cluster
791+
792+
datacenter = get_datacenter(service_instance, datacenter_name)
793+
return utils_cluster.get_cluster(datacenter, cluster_name)
794+
elif datacenter_name:
795+
return get_datacenter(service_instance, datacenter_name=datacenter_name)
796+
else:
797+
raise salt.exceptions.ArgumentValueError(
798+
"find_filtered_object requires at least one of datacenter_name, host_name, or cluster_name with datacenter_name"
799+
)
800+
except salt.exceptions.VMwareObjectRetrievalError:
801+
return None
818802

819803

820804
def get_license_mgrs(service_instance, license_mgr_names=None, get_all_license_mgrs=False):

0 commit comments

Comments
 (0)