Skip to content

Commit 16bc3c2

Browse files
authored
esxi add maintenance mode (#196)
* esxi add maintenance mode * move get_host to utils * run pre * run pre * autodocs * pre * fix pre * fix timeout * to lower doc Host * write test * write state test * add state test test
1 parent 97c6e60 commit 16bc3c2

5 files changed

Lines changed: 278 additions & 0 deletions

File tree

src/saltext/vmware/modules/esxi.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,3 +2288,128 @@ def get(
22882288
return ret
22892289
except DEFAULT_EXCEPTIONS as exc:
22902290
raise salt.exceptions.SaltException(str(exc))
2291+
2292+
2293+
def in_maintenance_mode(host, service_instance=None):
2294+
"""
2295+
Check if host is in maintenance mode.
2296+
2297+
host
2298+
Host IP or HostSystem/ManagedObjectReference (required).
2299+
2300+
service_instance
2301+
Use this vCenter service connection instance instead of creating a new one (optional).
2302+
2303+
.. code-block:: bash
2304+
2305+
salt '*' vmware_esxi.in_maintenance_mode '10.288.6.117'
2306+
"""
2307+
if isinstance(host, vim.HostSystem):
2308+
host_ref = host
2309+
else:
2310+
if service_instance is None:
2311+
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
2312+
host_ref = utils_esxi.get_host(host, service_instance)
2313+
mode = "normal"
2314+
if host_ref.runtime.inMaintenanceMode:
2315+
mode = "inMaintenance"
2316+
return {"maintenanceMode": mode}
2317+
2318+
2319+
def maintenance_mode(
2320+
host,
2321+
timeout=0,
2322+
evacuate_powered_off_vms=False,
2323+
maintenance_spec=None,
2324+
catch_task_error=True,
2325+
service_instance=None,
2326+
):
2327+
"""
2328+
Put host into maintenance mode.
2329+
2330+
host
2331+
Host IP or HostSystem/ManagedObjectReference (required).
2332+
2333+
timeout
2334+
If value is greater than 0 then task will timeout if not completed with in window (optional).
2335+
2336+
evacuate_powered_off_vms
2337+
Only supported by VirtualCenter (optional).
2338+
If True, for DRS will fail unless all powered-off VMs have been manually registered.
2339+
If False, task will successed with powered-off VMs.
2340+
2341+
maintenance_spec
2342+
HostMaintenanceSpec (optional).
2343+
2344+
catch_task_error
2345+
If False and task failed then a salt exception will be thrown (optional).
2346+
2347+
service_instance
2348+
Use this vCenter service connection instance instead of creating a new one (optional).
2349+
2350+
.. code-block:: bash
2351+
2352+
salt '*' vmware_esxi.maintenance_mode '10.288.6.117'
2353+
"""
2354+
if isinstance(host, vim.HostSystem):
2355+
host_ref = host
2356+
else:
2357+
if service_instance is None:
2358+
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
2359+
host_ref = utils_esxi.get_host(host, service_instance)
2360+
mode = in_maintenance_mode(host_ref)
2361+
if mode["maintenanceMode"] == "inMaintenance":
2362+
mode["changes"] = False
2363+
return mode
2364+
try:
2365+
task = host_ref.EnterMaintenanceMode_Task(
2366+
timeout, evacuate_powered_off_vms, maintenance_spec
2367+
)
2368+
utils_common.wait_for_task(task, host_ref.name, "maintenanceMode")
2369+
except salt.exceptions.SaltException as exc:
2370+
if not catch_task_error:
2371+
raise exc
2372+
mode = in_maintenance_mode(host_ref, service_instance)
2373+
mode["changes"] = mode["maintenanceMode"] == "inMaintenance"
2374+
return mode
2375+
2376+
2377+
def exit_maintenance_mode(host, timeout=0, catch_task_error=True, service_instance=None):
2378+
"""
2379+
Put host out of maintenance mode.
2380+
2381+
host
2382+
Host IP or HostSystem/ManagedObjectReference (required).
2383+
2384+
timeout
2385+
If value is greater than 0 then task will timeout if not completed with in window (optional).
2386+
2387+
catch_task_error
2388+
If False and task failed then a salt exception will be thrown (optional).
2389+
2390+
service_instance
2391+
Use this vCenter service connection instance instead of creating a new one (optional).
2392+
2393+
.. code-block:: bash
2394+
2395+
salt '*' vmware_esxi.exit_maintenance_mode '10.288.6.117'
2396+
"""
2397+
if isinstance(host, vim.HostSystem):
2398+
host_ref = host
2399+
else:
2400+
if service_instance is None:
2401+
service_instance = get_service_instance(opts=__opts__, pillar=__pillar__)
2402+
host_ref = utils_esxi.get_host(host, service_instance)
2403+
mode = in_maintenance_mode(host_ref)
2404+
if mode["maintenanceMode"] == "normal":
2405+
mode["changes"] = False
2406+
return mode
2407+
try:
2408+
task = host_ref.ExitMaintenanceMode_Task(timeout)
2409+
utils_common.wait_for_task(task, host_ref.name, "maintenanceMode")
2410+
except salt.exceptions.SaltException as exc:
2411+
if not catch_task_error:
2412+
raise exc
2413+
mode = in_maintenance_mode(host_ref, service_instance)
2414+
mode["changes"] = mode["maintenanceMode"] == "normal"
2415+
return mode

src/saltext/vmware/states/esxi.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,91 @@ def user_absent(
651651
ret["comment"] = "User {} doesn't exist on {} host(s).".format(name, no_user)
652652
ret["result"] = None
653653
return ret
654+
655+
656+
def maintenance_mode(
657+
name,
658+
enter_maintenance_mode,
659+
timeout=0,
660+
evacuate_powered_off_vms=False,
661+
maintenance_spec=None,
662+
service_instance=None,
663+
):
664+
"""
665+
Put host into or out of maintenance mode.
666+
667+
name
668+
Host IP or HostSystem/ManagedObjectReference (required).
669+
670+
enter_maintenance_mode
671+
If True, put host into maintenance mode.
672+
If False, put host out of maintenance mode.
673+
674+
timeout
675+
If value is greater than 0 then task will timeout if not completed with in window (optional).
676+
677+
evacuate_powered_off_vms
678+
Only supported by VirtualCenter (optional).
679+
If True, for DRS will fail unless all powered-off VMs have been manually registered.
680+
If False, task will successed with powered-off VMs.
681+
Only relevant if enter_maintenance_mode must be True.
682+
683+
maintenance_spec
684+
HostMaintenanceSpec (optional).
685+
Only relevant if enter_maintenance_mode must be True.
686+
687+
service_instance
688+
Use this vCenter service connection instance instead of creating a new one (optional).
689+
690+
.. code-block:: bash
691+
692+
salt '*' vmware_esxi.maintenance_mode '10.288.6.117'
693+
.. code-block:: yaml
694+
695+
Maintenance Mode:
696+
vmware_esxi.maintenance_mode:
697+
- host: '10.288.6.117'
698+
- enter_maintenance_mode: true
699+
"""
700+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
701+
702+
# check that host is not all ready in maintenance state.
703+
host_state = __salt__["vmware_esxi.in_maintenance_mode"](
704+
host=name, service_instance=service_instance
705+
)
706+
if (host_state["maintenanceMode"] == "inMaintenance") == enter_maintenance_mode:
707+
ret["comment"] = f"Already in {'Maintenance' if enter_maintenance_mode else 'Normal'} mode."
708+
return ret
709+
710+
if __opts__["test"]:
711+
ret["result"] = None
712+
ret["changes"] = {
713+
"new": f"Host will enter {'Maintenance' if enter_maintenance_mode else 'Normal'} mode."
714+
}
715+
ret["comment"] = "These options are set to change."
716+
return ret
717+
718+
if enter_maintenance_mode:
719+
host_state = __salt__["vmware_esxi.maintenance_mode"](
720+
host=name,
721+
timeout=timeout,
722+
evacuate_powered_off_vms=evacuate_powered_off_vms,
723+
maintenance_spec=maintenance_spec,
724+
catch_task_error=True,
725+
service_instance=service_instance,
726+
)
727+
else:
728+
host_state = __salt__["vmware_esxi.exit_maintenance_mode"](
729+
host=name, timeout=timeout, catch_task_error=True, service_instance=service_instance
730+
)
731+
732+
ret["result"] = (host_state["maintenanceMode"] == "inMaintenance") == enter_maintenance_mode
733+
if ret["result"]:
734+
ret["changes"] = {
735+
"new": f"Host entered {'Maintenance' if enter_maintenance_mode else 'Normal'} mode."
736+
}
737+
else:
738+
ret[
739+
"comment"
740+
] = f"Failed to put host {str(name)} in {'Maintenance' if enter_maintenance_mode else 'Normal'} mode."
741+
return ret

src/saltext/vmware/utils/esxi.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,7 @@ def add_host(
330330
task = cluster_ref.AddHost_Task(connect_spec, connect)
331331
host_ref = utils_common.wait_for_task(task, host, "add host task")
332332
return host_ref.summary.runtime.connectionState
333+
334+
335+
def get_host(host, service_instance):
336+
return utils_common.get_mor_by_property(service_instance, vim.HostSystem, host)

tests/integration/modules/test_esxi.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,3 +658,29 @@ def test_add_update_remove_vmkernel_adapter(service_instance):
658658
assert ret
659659
for host in ret:
660660
assert not ret[host]
661+
662+
663+
def test_maintenance_mode(service_instance):
664+
hosts = list(esxi.get(service_instance=service_instance))
665+
assert hosts
666+
host = hosts[0]
667+
ret = esxi.in_maintenance_mode(host, service_instance)
668+
assert ret == dict(maintenanceMode="normal")
669+
670+
try:
671+
for i in range(3):
672+
ret = esxi.maintenance_mode(host, 120, service_instance=service_instance)
673+
assert ret == dict(maintenanceMode="inMaintenance", changes=not i)
674+
except Exception as e:
675+
esxi.exit_maintenance_mode(host, 120, service_instance=service_instance)
676+
raise e
677+
678+
ret = esxi.in_maintenance_mode(host, service_instance)
679+
assert ret == dict(maintenanceMode="inMaintenance")
680+
681+
for i in range(3):
682+
ret = esxi.exit_maintenance_mode(host, 120, service_instance=service_instance)
683+
assert ret == dict(maintenanceMode="normal", changes=not i)
684+
685+
ret = esxi.in_maintenance_mode(host, service_instance)
686+
assert ret == dict(maintenanceMode="normal")

tests/integration/states/test_esxi.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,38 @@ def test_vmkernel_adapter_present(vmware_datacenter, service_instance):
277277
)
278278
assert not delete_ret["result"]
279279
assert not delete_ret["changes"]
280+
281+
282+
def test_maintenance_mode(service_instance):
283+
hosts = list(esxi_mod.get(service_instance=service_instance))
284+
assert hosts
285+
host = hosts[0]
286+
try:
287+
for i in range(3):
288+
ret = esxi.maintenance_mode(host, True, 120, service_instance=service_instance)
289+
assert ret["result"]
290+
if not i:
291+
assert ret["changes"]
292+
else:
293+
assert not ret["changes"]
294+
except Exception as e:
295+
esxi.maintenance_mode(host, False, 120, service_instance=service_instance)
296+
raise e
297+
298+
for i in range(3):
299+
ret = esxi.maintenance_mode(host, False, 120, service_instance=service_instance)
300+
assert ret["result"]
301+
if not i:
302+
assert ret["changes"]
303+
else:
304+
assert not ret["changes"]
305+
306+
307+
def test_maintenance_mode_dry_run(service_instance, dry_run):
308+
hosts = list(esxi_mod.get(service_instance=service_instance))
309+
assert hosts
310+
host = hosts[0]
311+
ret = esxi.maintenance_mode(host, True, 120, service_instance=service_instance)
312+
assert ret["result"] is None
313+
assert ret["changes"]
314+
assert ret["comment"] == "These options are set to change."

0 commit comments

Comments
 (0)