"Fossies" - the Fresh Open Source Software Archive 
Member "salt-3002.2/salt/modules/vsphere.py" (18 Nov 2020, 377785 Bytes) of package /linux/misc/salt-3002.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "vsphere.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3002.1_vs_3002.2.
1 """
2 Manage VMware vCenter servers and ESXi hosts.
3
4 .. versionadded:: 2015.8.4
5
6 :codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstaley.com>
7
8 Dependencies
9 ============
10
11 - pyVmomi Python Module
12 - ESXCLI
13
14 pyVmomi
15 -------
16
17 PyVmomi can be installed via pip:
18
19 .. code-block:: bash
20
21 pip install pyVmomi
22
23 .. note::
24
25 Version 6.0 of pyVmomi has some problems with SSL error handling on certain
26 versions of Python. If using version 6.0 of pyVmomi, Python 2.7.9,
27 or newer must be present. This is due to an upstream dependency
28 in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the
29 version of Python is not in the supported range, you will need to install an
30 earlier version of pyVmomi. See `Issue #29537`_ for more information.
31
32 .. _Issue #29537: https://github.com/saltstack/salt/issues/29537
33
34 Based on the note above, to install an earlier version of pyVmomi than the
35 version currently listed in PyPi, run the following:
36
37 .. code-block:: bash
38
39 pip install pyVmomi==5.5.0.2014.1.1
40
41 The 5.5.0.2014.1.1 is a known stable version that this original vSphere Execution
42 Module was developed against.
43
44 vSphere Automation SDK
45 ----------------------
46
47 vSphere Automation SDK can be installed via pip:
48
49 .. code-block:: bash
50
51 pip install --upgrade pip setuptools
52 pip install --upgrade git+https://github.com/vmware/vsphere-automation-sdk-python.git
53
54 .. note::
55
56 The SDK also requires OpenSSL 1.0.1+ if you want to connect to vSphere 6.5+ in order to support
57 TLS1.1 & 1.2.
58
59 In order to use the tagging functions in this module, vSphere Automation SDK is necessary to
60 install.
61
62 The module is currently in version 1.0.3
63 (as of 8/26/2019)
64
65 ESXCLI
66 ------
67
68 Currently, about a third of the functions used in the vSphere Execution Module require
69 the ESXCLI package be installed on the machine running the Proxy Minion process.
70
71 The ESXCLI package is also referred to as the VMware vSphere CLI, or vCLI. VMware
72 provides vCLI package installation instructions for `vSphere 5.5`_ and
73 `vSphere 6.0`_.
74
75 .. _vSphere 5.5: http://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html
76 .. _vSphere 6.0: http://pubs.vmware.com/vsphere-60/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html
77
78 Once all of the required dependencies are in place and the vCLI package is
79 installed, you can check to see if you can connect to your ESXi host or vCenter
80 server by running the following command:
81
82 .. code-block:: bash
83
84 esxcli -s <host-location> -u <username> -p <password> system syslog config get
85
86 If the connection was successful, ESXCLI was successfully installed on your system.
87 You should see output related to the ESXi host's syslog configuration.
88
89 .. note::
90
91 Be aware that some functionality in this execution module may depend on the
92 type of license attached to a vCenter Server or ESXi host(s).
93
94 For example, certain services are only available to manipulate service state
95 or policies with a VMware vSphere Enterprise or Enterprise Plus license, while
96 others are available with a Standard license. The ``ntpd`` service is restricted
97 to an Enterprise Plus license, while ``ssh`` is available via the Standard
98 license.
99
100 Please see the `vSphere Comparison`_ page for more information.
101
102 .. _vSphere Comparison: https://www.vmware.com/products/vsphere/compare
103
104
105 About
106 =====
107
108 This execution module was designed to be able to handle connections both to a
109 vCenter Server, as well as to an ESXi host. It utilizes the pyVmomi Python
110 library and the ESXCLI package to run remote execution functions against either
111 the defined vCenter server or the ESXi host.
112
113 Whether or not the function runs against a vCenter Server or an ESXi host depends
114 entirely upon the arguments passed into the function. Each function requires a
115 ``host`` location, ``username``, and ``password``. If the credentials provided
116 apply to a vCenter Server, then the function will be run against the vCenter
117 Server. For example, when listing hosts using vCenter credentials, you'll get a
118 list of hosts associated with that vCenter Server:
119
120 .. code-block:: bash
121
122 # salt my-minion vsphere.list_hosts <vcenter-ip> <vcenter-user> <vcenter-password>
123 my-minion:
124 - esxi-1.example.com
125 - esxi-2.example.com
126
127 However, some functions should be used against ESXi hosts, not vCenter Servers.
128 Functionality such as getting a host's coredump network configuration should be
129 performed against a host and not a vCenter server. If the authentication
130 information you're using is against a vCenter server and not an ESXi host, you
131 can provide the host name that is associated with the vCenter server in the
132 command, as a list, using the ``host_names`` or ``esxi_host`` kwarg. For
133 example:
134
135 .. code-block:: bash
136
137 # salt my-minion vsphere.get_coredump_network_config <vcenter-ip> <vcenter-user> \
138 <vcenter-password> esxi_hosts='[esxi-1.example.com, esxi-2.example.com]'
139 my-minion:
140 ----------
141 esxi-1.example.com:
142 ----------
143 Coredump Config:
144 ----------
145 enabled:
146 False
147 esxi-2.example.com:
148 ----------
149 Coredump Config:
150 ----------
151 enabled:
152 True
153 host_vnic:
154 vmk0
155 ip:
156 coredump-location.example.com
157 port:
158 6500
159
160 You can also use these functions against an ESXi host directly by establishing a
161 connection to an ESXi host using the host's location, username, and password. If ESXi
162 connection credentials are used instead of vCenter credentials, the ``host_names`` and
163 ``esxi_hosts`` arguments are not needed.
164
165 .. code-block:: bash
166
167 # salt my-minion vsphere.get_coredump_network_config esxi-1.example.com root <host-password>
168 local:
169 ----------
170 10.4.28.150:
171 ----------
172 Coredump Config:
173 ----------
174 enabled:
175 True
176 host_vnic:
177 vmk0
178 ip:
179 coredump-location.example.com
180 port:
181 6500
182 """
183
184
185 import datetime
186 import logging
187 import sys
188 from functools import wraps
189
190 import salt.utils.args
191 import salt.utils.dictupdate as dictupdate
192 import salt.utils.http
193 import salt.utils.path
194 import salt.utils.pbm
195 import salt.utils.vmware
196 import salt.utils.vsan
197 from salt.config.schemas.esxcluster import (
198 ESXClusterConfigSchema,
199 ESXClusterEntitySchema,
200 )
201 from salt.config.schemas.esxi import (
202 DiskGroupsDiskIdSchema,
203 SimpleHostCacheSchema,
204 VmfsDatastoreSchema,
205 )
206 from salt.config.schemas.esxvm import (
207 ESXVirtualMachineDeleteSchema,
208 ESXVirtualMachineUnregisterSchema,
209 )
210 from salt.config.schemas.vcenter import VCenterEntitySchema
211 from salt.exceptions import (
212 ArgumentValueError,
213 CommandExecutionError,
214 InvalidConfigError,
215 InvalidEntityError,
216 VMwareApiError,
217 VMwareObjectExistsError,
218 VMwareObjectRetrievalError,
219 VMwareSaltError,
220 )
221 from salt.utils.decorators import depends, ignores_kwargs
222 from salt.utils.dictdiffer import recursive_diff
223 from salt.utils.listdiffer import list_diff
224
225 log = logging.getLogger(__name__)
226
227 try:
228 import jsonschema
229
230 HAS_JSONSCHEMA = True
231 except ImportError:
232 HAS_JSONSCHEMA = False
233
234 try:
235 # pylint: disable=no-name-in-module
236 from pyVmomi import (
237 vim,
238 vmodl,
239 pbm,
240 VmomiSupport,
241 )
242
243 # pylint: enable=no-name-in-module
244
245 # We check the supported vim versions to infer the pyVmomi version
246 if (
247 "vim25/6.0" in VmomiSupport.versionMap
248 and sys.version_info > (2, 7)
249 and sys.version_info < (2, 7, 9)
250 ):
251
252 log.debug(
253 "pyVmomi not loaded: Incompatible versions " "of Python. See Issue #29537."
254 )
255 raise ImportError()
256 HAS_PYVMOMI = True
257 except ImportError:
258 HAS_PYVMOMI = False
259
260 # vSphere SDK Automation
261 # pylint: disable=unused-import
262 try:
263 from com.vmware.cis.tagging_client import Category, CategoryModel
264 from com.vmware.cis.tagging_client import Tag, TagModel, TagAssociation
265 from com.vmware.vcenter_client import Cluster
266 from com.vmware.vapi.std_client import DynamicID
267
268 # Error Handling
269 from com.vmware.vapi.std.errors_client import (
270 AlreadyExists,
271 InvalidArgument,
272 NotFound,
273 Unauthenticated,
274 Unauthorized,
275 )
276
277 vsphere_errors = (
278 AlreadyExists,
279 InvalidArgument,
280 NotFound,
281 Unauthenticated,
282 Unauthorized,
283 )
284 HAS_VSPHERE_SDK = True
285 except ImportError:
286 HAS_VSPHERE_SDK = False
287 # pylint: enable=unused-import
288
289 # ESXI
290 esx_cli = salt.utils.path.which("esxcli")
291 if esx_cli:
292 HAS_ESX_CLI = True
293 else:
294 HAS_ESX_CLI = False
295
296 __virtualname__ = "vsphere"
297 __proxyenabled__ = ["esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm"]
298
299
300 def __virtual__():
301 return __virtualname__
302
303
304 def get_proxy_type():
305 """
306 Returns the proxy type retrieved either from the pillar of from the proxy
307 minion's config. Returns ``<undefined>`` otherwise.
308
309 CLI Example:
310
311 .. code-block:: bash
312
313 salt '*' vsphere.get_proxy_type
314 """
315 if __pillar__.get("proxy", {}).get("proxytype"):
316 return __pillar__["proxy"]["proxytype"]
317 if __opts__.get("proxy", {}).get("proxytype"):
318 return __opts__["proxy"]["proxytype"]
319 return "<undefined>"
320
321
322 def _get_proxy_connection_details():
323 """
324 Returns the connection details of the following proxies: esxi
325 """
326 proxytype = get_proxy_type()
327 if proxytype == "esxi":
328 details = __salt__["esxi.get_details"]()
329 elif proxytype == "esxcluster":
330 details = __salt__["esxcluster.get_details"]()
331 elif proxytype == "esxdatacenter":
332 details = __salt__["esxdatacenter.get_details"]()
333 elif proxytype == "vcenter":
334 details = __salt__["vcenter.get_details"]()
335 elif proxytype == "esxvm":
336 details = __salt__["esxvm.get_details"]()
337 else:
338 raise CommandExecutionError("'{}' proxy is not supported" "".format(proxytype))
339 return (
340 details.get("vcenter") if "vcenter" in details else details.get("host"),
341 details.get("username"),
342 details.get("password"),
343 details.get("protocol"),
344 details.get("port"),
345 details.get("mechanism"),
346 details.get("principal"),
347 details.get("domain"),
348 )
349
350
351 def supports_proxies(*proxy_types):
352 """
353 Decorator to specify which proxy types are supported by a function
354
355 proxy_types:
356 Arbitrary list of strings with the supported types of proxies
357 """
358
359 def _supports_proxies(fn):
360 @wraps(fn)
361 def __supports_proxies(*args, **kwargs):
362 proxy_type = get_proxy_type()
363 if proxy_type not in proxy_types:
364 raise CommandExecutionError(
365 "'{}' proxy is not supported by function {}"
366 "".format(proxy_type, fn.__name__)
367 )
368 return fn(*args, **salt.utils.args.clean_kwargs(**kwargs))
369
370 return __supports_proxies
371
372 return _supports_proxies
373
374
375 def gets_service_instance_via_proxy(fn):
376 """
377 Decorator that connects to a target system (vCenter or ESXi host) using the
378 proxy details and passes the connection (vim.ServiceInstance) to
379 the decorated function.
380
381 Supported proxies: esxi, esxcluster, esxdatacenter.
382
383 Notes:
384 1. The decorated function must have a ``service_instance`` parameter
385 or a ``**kwarg`` type argument (name of argument is not important);
386 2. If the ``service_instance`` parameter is already defined, the value
387 is passed through to the decorated function;
388 3. If the ``service_instance`` parameter in not defined, the
389 connection is created using the proxy details and the service instance
390 is returned.
391
392 CLI Example:
393 None, this is a decorator
394 """
395 fn_name = fn.__name__
396 (
397 arg_names,
398 args_name,
399 kwargs_name,
400 default_values,
401 ) = salt.utils.args.get_function_argspec(fn)
402 default_values = default_values if default_values is not None else []
403
404 @wraps(fn)
405 def _gets_service_instance_via_proxy(*args, **kwargs):
406 if "service_instance" not in arg_names and not kwargs_name:
407 raise CommandExecutionError(
408 "Function {} must have either a 'service_instance', or a "
409 "'**kwargs' type parameter".format(fn_name)
410 )
411 connection_details = _get_proxy_connection_details()
412 # Figure out how to pass in the connection value
413 local_service_instance = None
414 if "service_instance" in arg_names:
415 idx = arg_names.index("service_instance")
416 if idx >= len(arg_names) - len(default_values):
417 # 'service_instance' has a default value:
418 # we check if we need to instantiate it or
419 # pass it through
420 #
421 # NOTE: if 'service_instance' doesn't have a default value
422 # it must be explicitly set in the function call so we pass it
423 # through
424
425 # There are two cases:
426 # 1. service_instance was passed in as a positional parameter
427 # 2. service_instance was passed in as a named paramter
428 if len(args) > idx:
429 # case 1: The call was made with enough positional
430 # parameters to include 'service_instance'
431 if not args[idx]:
432 local_service_instance = salt.utils.vmware.get_service_instance(
433 *connection_details
434 )
435 # Tuples are immutable, so if we want to change what
436 # was passed in, we need to first convert to a list.
437 args = list(args)
438 args[idx] = local_service_instance
439 else:
440 # case 2: Not enough positional parameters so
441 # 'service_instance' must be a named parameter
442 if not kwargs.get("service_instance"):
443 local_service_instance = salt.utils.vmware.get_service_instance(
444 *connection_details
445 )
446 kwargs["service_instance"] = local_service_instance
447 else:
448 # 'service_instance' is not a paremter in the function definition
449 # but it will be caught by the **kwargs parameter
450 if not kwargs.get("service_instance"):
451 local_service_instance = salt.utils.vmware.get_service_instance(
452 *connection_details
453 )
454 kwargs["service_instance"] = local_service_instance
455 try:
456 ret = fn(*args, **salt.utils.args.clean_kwargs(**kwargs))
457 # Disconnect if connected in the decorator
458 if local_service_instance:
459 salt.utils.vmware.disconnect(local_service_instance)
460 return ret
461 except Exception as e: # pylint: disable=broad-except
462 # Disconnect if connected in the decorator
463 if local_service_instance:
464 salt.utils.vmware.disconnect(local_service_instance)
465 # raise original exception and traceback
466 raise
467
468 return _gets_service_instance_via_proxy
469
470
471 @depends(HAS_PYVMOMI)
472 @supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm")
473 def get_service_instance_via_proxy(service_instance=None):
474 """
475 Returns a service instance to the proxied endpoint (vCenter/ESXi host).
476
477 service_instance
478 Service instance (vim.ServiceInstance) of the vCenter.
479 Default is None.
480
481 Note:
482 Should be used by state functions not invoked directly.
483
484 CLI Example:
485 See note above
486 """
487 connection_details = _get_proxy_connection_details()
488 return salt.utils.vmware.get_service_instance(*connection_details)
489
490
491 @depends(HAS_PYVMOMI)
492 @supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm")
493 def disconnect(service_instance):
494 """
495 Disconnects from a vCenter or ESXi host
496
497 Note:
498 Should be used by state functions, not invoked directly.
499
500 service_instance
501 Service instance (vim.ServiceInstance)
502
503 CLI Example:
504
505 See note above.
506 """
507 salt.utils.vmware.disconnect(service_instance)
508 return True
509
510
511 @depends(HAS_ESX_CLI)
512 def esxcli_cmd(
513 cmd_str,
514 host=None,
515 username=None,
516 password=None,
517 protocol=None,
518 port=None,
519 esxi_hosts=None,
520 credstore=None,
521 ):
522 """
523 Run an ESXCLI command directly on the host or list of hosts.
524
525 host
526 The location of the host.
527
528 username
529 The username used to login to the host, such as ``root``.
530
531 password
532 The password used to login to the host.
533
534 cmd_str
535 The ESXCLI command to run. Note: This should not include the ``-s``, ``-u``,
536 ``-p``, ``-h``, ``--protocol``, or ``--portnumber`` arguments that are
537 frequently passed when using a bare ESXCLI command from the command line.
538 Those arguments are handled by this function via the other args and kwargs.
539
540 protocol
541 Optionally set to alternate protocol if the host is not using the default
542 protocol. Default protocol is ``https``.
543
544 port
545 Optionally set to alternate port if the host is not using the default
546 port. Default port is ``443``.
547
548 esxi_hosts
549 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
550 on a list of one or more ESXi machines.
551
552 credstore
553 Optionally set to path to the credential store file.
554
555 CLI Example:
556
557 .. code-block:: bash
558
559 # Used for ESXi host connection information
560 salt '*' vsphere.esxcli_cmd my.esxi.host root bad-password \
561 'system coredump network get'
562
563 # Used for connecting to a vCenter Server
564 salt '*' vsphere.esxcli_cmd my.vcenter.location root bad-password \
565 'system coredump network get' esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
566 """
567 ret = {}
568 if esxi_hosts:
569 if not isinstance(esxi_hosts, list):
570 raise CommandExecutionError("'esxi_hosts' must be a list.")
571
572 for esxi_host in esxi_hosts:
573 response = salt.utils.vmware.esxcli(
574 host,
575 username,
576 password,
577 cmd_str,
578 protocol=protocol,
579 port=port,
580 esxi_host=esxi_host,
581 credstore=credstore,
582 )
583 if response["retcode"] != 0:
584 ret.update({esxi_host: {"Error": response.get("stdout")}})
585 else:
586 ret.update({esxi_host: response})
587 else:
588 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
589 response = salt.utils.vmware.esxcli(
590 host,
591 username,
592 password,
593 cmd_str,
594 protocol=protocol,
595 port=port,
596 credstore=credstore,
597 )
598 if response["retcode"] != 0:
599 ret.update({host: {"Error": response.get("stdout")}})
600 else:
601 ret.update({host: response})
602
603 return ret
604
605
606 @depends(HAS_ESX_CLI)
607 def get_coredump_network_config(
608 host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None
609 ):
610 """
611 Retrieve information on ESXi or vCenter network dump collection and
612 format it into a dictionary.
613
614 host
615 The location of the host.
616
617 username
618 The username used to login to the host, such as ``root``.
619
620 password
621 The password used to login to the host.
622
623 protocol
624 Optionally set to alternate protocol if the host is not using the default
625 protocol. Default protocol is ``https``.
626
627 port
628 Optionally set to alternate port if the host is not using the default
629 port. Default port is ``443``.
630
631 esxi_hosts
632 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
633 on a list of one or more ESXi machines.
634
635 credstore
636 Optionally set to path to the credential store file.
637
638 :return: A dictionary with the network configuration, or, if getting
639 the network config failed, a an error message retrieved from the
640 standard cmd.run_all dictionary, per host.
641
642 CLI Example:
643
644 .. code-block:: bash
645
646 # Used for ESXi host connection information
647 salt '*' vsphere.get_coredump_network_config my.esxi.host root bad-password
648
649 # Used for connecting to a vCenter Server
650 salt '*' vsphere.get_coredump_network_config my.vcenter.location root bad-password \
651 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
652
653 """
654 cmd = "system coredump network get"
655 ret = {}
656 if esxi_hosts:
657 if not isinstance(esxi_hosts, list):
658 raise CommandExecutionError("'esxi_hosts' must be a list.")
659
660 for esxi_host in esxi_hosts:
661 response = salt.utils.vmware.esxcli(
662 host,
663 username,
664 password,
665 cmd,
666 protocol=protocol,
667 port=port,
668 esxi_host=esxi_host,
669 credstore=credstore,
670 )
671 if response["retcode"] != 0:
672 ret.update({esxi_host: {"Error": response.get("stdout")}})
673 else:
674 # format the response stdout into something useful
675 ret.update(
676 {esxi_host: {"Coredump Config": _format_coredump_stdout(response)}}
677 )
678 else:
679 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
680 response = salt.utils.vmware.esxcli(
681 host,
682 username,
683 password,
684 cmd,
685 protocol=protocol,
686 port=port,
687 credstore=credstore,
688 )
689 if response["retcode"] != 0:
690 ret.update({host: {"Error": response.get("stdout")}})
691 else:
692 # format the response stdout into something useful
693 stdout = _format_coredump_stdout(response)
694 ret.update({host: {"Coredump Config": stdout}})
695
696 return ret
697
698
699 @depends(HAS_ESX_CLI)
700 def coredump_network_enable(
701 host,
702 username,
703 password,
704 enabled,
705 protocol=None,
706 port=None,
707 esxi_hosts=None,
708 credstore=None,
709 ):
710 """
711 Enable or disable ESXi core dump collection. Returns ``True`` if coredump is enabled
712 and returns ``False`` if core dump is not enabled. If there was an error, the error
713 will be the value printed in the ``Error`` key dictionary for the given host.
714
715 host
716 The location of the host.
717
718 username
719 The username used to login to the host, such as ``root``.
720
721 password
722 The password used to login to the host.
723
724 enabled
725 Python True or False to enable or disable coredumps.
726
727 protocol
728 Optionally set to alternate protocol if the host is not using the default
729 protocol. Default protocol is ``https``.
730
731 port
732 Optionally set to alternate port if the host is not using the default
733 port. Default port is ``443``.
734
735 esxi_hosts
736 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
737 on a list of one or more ESXi machines.
738
739 credstore
740 Optionally set to path to the credential store file.
741
742 CLI Example:
743
744 .. code-block:: bash
745
746 # Used for ESXi host connection information
747 salt '*' vsphere.coredump_network_enable my.esxi.host root bad-password True
748
749 # Used for connecting to a vCenter Server
750 salt '*' vsphere.coredump_network_enable my.vcenter.location root bad-password True \
751 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
752 """
753 if enabled:
754 enable_it = 1
755 else:
756 enable_it = 0
757 cmd = "system coredump network set -e {}".format(enable_it)
758
759 ret = {}
760 if esxi_hosts:
761 if not isinstance(esxi_hosts, list):
762 raise CommandExecutionError("'esxi_hosts' must be a list.")
763
764 for esxi_host in esxi_hosts:
765 response = salt.utils.vmware.esxcli(
766 host,
767 username,
768 password,
769 cmd,
770 protocol=protocol,
771 port=port,
772 esxi_host=esxi_host,
773 credstore=credstore,
774 )
775 if response["retcode"] != 0:
776 ret.update({esxi_host: {"Error": response.get("stdout")}})
777 else:
778 ret.update({esxi_host: {"Coredump Enabled": enabled}})
779
780 else:
781 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
782 response = salt.utils.vmware.esxcli(
783 host,
784 username,
785 password,
786 cmd,
787 protocol=protocol,
788 port=port,
789 credstore=credstore,
790 )
791 if response["retcode"] != 0:
792 ret.update({host: {"Error": response.get("stdout")}})
793 else:
794 ret.update({host: {"Coredump Enabled": enabled}})
795
796 return ret
797
798
799 @depends(HAS_ESX_CLI)
800 def set_coredump_network_config(
801 host,
802 username,
803 password,
804 dump_ip,
805 protocol=None,
806 port=None,
807 host_vnic="vmk0",
808 dump_port=6500,
809 esxi_hosts=None,
810 credstore=None,
811 ):
812 """
813
814 Set the network parameters for a network coredump collection.
815 Note that ESXi requires that the dumps first be enabled (see
816 `coredump_network_enable`) before these parameters may be set.
817
818 host
819 The location of the host.
820
821 username
822 The username used to login to the host, such as ``root``.
823
824 password
825 The password used to login to the host.
826
827 dump_ip
828 IP address of host that will accept the dump.
829
830 protocol
831 Optionally set to alternate protocol if the host is not using the default
832 protocol. Default protocol is ``https``.
833
834 port
835 Optionally set to alternate port if the host is not using the default
836 port. Default port is ``443``.
837
838 esxi_hosts
839 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
840 on a list of one or more ESXi machines.
841
842 host_vnic
843 Host VNic port through which to communicate. Defaults to ``vmk0``.
844
845 dump_port
846 TCP port to use for the dump, defaults to ``6500``.
847
848 credstore
849 Optionally set to path to the credential store file.
850
851 :return: A standard cmd.run_all dictionary with a `success` key added, per host.
852 `success` will be True if the set succeeded, False otherwise.
853
854 CLI Example:
855
856 .. code-block:: bash
857
858 # Used for ESXi host connection information
859 salt '*' vsphere.set_coredump_network_config my.esxi.host root bad-password 'dump_ip.host.com'
860
861 # Used for connecting to a vCenter Server
862 salt '*' vsphere.set_coredump_network_config my.vcenter.location root bad-password 'dump_ip.host.com' \
863 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
864 """
865 cmd = "system coredump network set -v {} -i {} -o {}".format(
866 host_vnic, dump_ip, dump_port
867 )
868 ret = {}
869 if esxi_hosts:
870 if not isinstance(esxi_hosts, list):
871 raise CommandExecutionError("'esxi_hosts' must be a list.")
872
873 for esxi_host in esxi_hosts:
874 response = salt.utils.vmware.esxcli(
875 host,
876 username,
877 password,
878 cmd,
879 protocol=protocol,
880 port=port,
881 esxi_host=esxi_host,
882 credstore=credstore,
883 )
884 if response["retcode"] != 0:
885 response["success"] = False
886 else:
887 response["success"] = True
888
889 # Update the cmd.run_all dictionary for each particular host.
890 ret.update({esxi_host: response})
891 else:
892 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
893 response = salt.utils.vmware.esxcli(
894 host,
895 username,
896 password,
897 cmd,
898 protocol=protocol,
899 port=port,
900 credstore=credstore,
901 )
902 if response["retcode"] != 0:
903 response["success"] = False
904 else:
905 response["success"] = True
906 ret.update({host: response})
907
908 return ret
909
910
911 @depends(HAS_ESX_CLI)
912 def get_firewall_status(
913 host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None
914 ):
915 """
916 Show status of all firewall rule sets.
917
918 host
919 The location of the host.
920
921 username
922 The username used to login to the host, such as ``root``.
923
924 password
925 The password used to login to the host.
926
927 protocol
928 Optionally set to alternate protocol if the host is not using the default
929 protocol. Default protocol is ``https``.
930
931 port
932 Optionally set to alternate port if the host is not using the default
933 port. Default port is ``443``.
934
935 esxi_hosts
936 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
937 on a list of one or more ESXi machines.
938
939 credstore
940 Optionally set to path to the credential store file.
941
942 :return: Nested dictionary with two toplevel keys ``rulesets`` and ``success``
943 ``success`` will be True or False depending on query success
944 ``rulesets`` will list the rulesets and their statuses if ``success``
945 was true, per host.
946
947 CLI Example:
948
949 .. code-block:: bash
950
951 # Used for ESXi host connection information
952 salt '*' vsphere.get_firewall_status my.esxi.host root bad-password
953
954 # Used for connecting to a vCenter Server
955 salt '*' vsphere.get_firewall_status my.vcenter.location root bad-password \
956 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
957 """
958 cmd = "network firewall ruleset list"
959
960 ret = {}
961 if esxi_hosts:
962 if not isinstance(esxi_hosts, list):
963 raise CommandExecutionError("'esxi_hosts' must be a list.")
964
965 for esxi_host in esxi_hosts:
966 response = salt.utils.vmware.esxcli(
967 host,
968 username,
969 password,
970 cmd,
971 protocol=protocol,
972 port=port,
973 esxi_host=esxi_host,
974 credstore=credstore,
975 )
976 if response["retcode"] != 0:
977 ret.update(
978 {
979 esxi_host: {
980 "Error": response["stdout"],
981 "success": False,
982 "rulesets": None,
983 }
984 }
985 )
986 else:
987 # format the response stdout into something useful
988 ret.update({esxi_host: _format_firewall_stdout(response)})
989 else:
990 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
991 response = salt.utils.vmware.esxcli(
992 host,
993 username,
994 password,
995 cmd,
996 protocol=protocol,
997 port=port,
998 credstore=credstore,
999 )
1000 if response["retcode"] != 0:
1001 ret.update(
1002 {
1003 host: {
1004 "Error": response["stdout"],
1005 "success": False,
1006 "rulesets": None,
1007 }
1008 }
1009 )
1010 else:
1011 # format the response stdout into something useful
1012 ret.update({host: _format_firewall_stdout(response)})
1013
1014 return ret
1015
1016
1017 @depends(HAS_ESX_CLI)
1018 def enable_firewall_ruleset(
1019 host,
1020 username,
1021 password,
1022 ruleset_enable,
1023 ruleset_name,
1024 protocol=None,
1025 port=None,
1026 esxi_hosts=None,
1027 credstore=None,
1028 ):
1029 """
1030 Enable or disable an ESXi firewall rule set.
1031
1032 host
1033 The location of the host.
1034
1035 username
1036 The username used to login to the host, such as ``root``.
1037
1038 password
1039 The password used to login to the host.
1040
1041 ruleset_enable
1042 True to enable the ruleset, false to disable.
1043
1044 ruleset_name
1045 Name of ruleset to target.
1046
1047 protocol
1048 Optionally set to alternate protocol if the host is not using the default
1049 protocol. Default protocol is ``https``.
1050
1051 port
1052 Optionally set to alternate port if the host is not using the default
1053 port. Default port is ``443``.
1054
1055 esxi_hosts
1056 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
1057 on a list of one or more ESXi machines.
1058
1059 credstore
1060 Optionally set to path to the credential store file.
1061
1062 :return: A standard cmd.run_all dictionary, per host.
1063
1064 CLI Example:
1065
1066 .. code-block:: bash
1067
1068 # Used for ESXi host connection information
1069 salt '*' vsphere.enable_firewall_ruleset my.esxi.host root bad-password True 'syslog'
1070
1071 # Used for connecting to a vCenter Server
1072 salt '*' vsphere.enable_firewall_ruleset my.vcenter.location root bad-password True 'syslog' \
1073 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
1074 """
1075 cmd = "network firewall ruleset set --enabled {} --ruleset-id={}".format(
1076 ruleset_enable, ruleset_name
1077 )
1078
1079 ret = {}
1080 if esxi_hosts:
1081 if not isinstance(esxi_hosts, list):
1082 raise CommandExecutionError("'esxi_hosts' must be a list.")
1083
1084 for esxi_host in esxi_hosts:
1085 response = salt.utils.vmware.esxcli(
1086 host,
1087 username,
1088 password,
1089 cmd,
1090 protocol=protocol,
1091 port=port,
1092 esxi_host=esxi_host,
1093 credstore=credstore,
1094 )
1095 ret.update({esxi_host: response})
1096 else:
1097 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1098 response = salt.utils.vmware.esxcli(
1099 host,
1100 username,
1101 password,
1102 cmd,
1103 protocol=protocol,
1104 port=port,
1105 credstore=credstore,
1106 )
1107 ret.update({host: response})
1108
1109 return ret
1110
1111
1112 @depends(HAS_ESX_CLI)
1113 def syslog_service_reload(
1114 host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None
1115 ):
1116 """
1117 Reload the syslog service so it will pick up any changes.
1118
1119 host
1120 The location of the host.
1121
1122 username
1123 The username used to login to the host, such as ``root``.
1124
1125 password
1126 The password used to login to the host.
1127
1128 protocol
1129 Optionally set to alternate protocol if the host is not using the default
1130 protocol. Default protocol is ``https``.
1131
1132 port
1133 Optionally set to alternate port if the host is not using the default
1134 port. Default port is ``443``.
1135
1136 esxi_hosts
1137 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
1138 on a list of one or more ESXi machines.
1139
1140 credstore
1141 Optionally set to path to the credential store file.
1142
1143 :return: A standard cmd.run_all dictionary. This dictionary will at least
1144 have a `retcode` key. If `retcode` is 0 the command was successful.
1145
1146 CLI Example:
1147
1148 .. code-block:: bash
1149
1150 # Used for ESXi host connection information
1151 salt '*' vsphere.syslog_service_reload my.esxi.host root bad-password
1152
1153 # Used for connecting to a vCenter Server
1154 salt '*' vsphere.syslog_service_reload my.vcenter.location root bad-password \
1155 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
1156 """
1157 cmd = "system syslog reload"
1158
1159 ret = {}
1160 if esxi_hosts:
1161 if not isinstance(esxi_hosts, list):
1162 raise CommandExecutionError("'esxi_hosts' must be a list.")
1163
1164 for esxi_host in esxi_hosts:
1165 response = salt.utils.vmware.esxcli(
1166 host,
1167 username,
1168 password,
1169 cmd,
1170 protocol=protocol,
1171 port=port,
1172 esxi_host=esxi_host,
1173 credstore=credstore,
1174 )
1175 ret.update({esxi_host: response})
1176 else:
1177 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1178 response = salt.utils.vmware.esxcli(
1179 host,
1180 username,
1181 password,
1182 cmd,
1183 protocol=protocol,
1184 port=port,
1185 credstore=credstore,
1186 )
1187 ret.update({host: response})
1188
1189 return ret
1190
1191
1192 @depends(HAS_ESX_CLI)
1193 def set_syslog_config(
1194 host,
1195 username,
1196 password,
1197 syslog_config,
1198 config_value,
1199 protocol=None,
1200 port=None,
1201 firewall=True,
1202 reset_service=True,
1203 esxi_hosts=None,
1204 credstore=None,
1205 ):
1206 """
1207 Set the specified syslog configuration parameter. By default, this function will
1208 reset the syslog service after the configuration is set.
1209
1210 host
1211 ESXi or vCenter host to connect to.
1212
1213 username
1214 User to connect as, usually root.
1215
1216 password
1217 Password to connect with.
1218
1219 syslog_config
1220 Name of parameter to set (corresponds to the command line switch for
1221 esxcli without the double dashes (--))
1222
1223 Valid syslog_config values are ``logdir``, ``loghost``, ``default-rotate`,
1224 ``default-size``, ``default-timeout``, and ``logdir-unique``.
1225
1226 config_value
1227 Value for the above parameter. For ``loghost``, URLs or IP addresses to
1228 use for logging. Multiple log servers can be specified by listing them,
1229 comma-separated, but without spaces before or after commas.
1230
1231 (reference: https://blogs.vmware.com/vsphere/2012/04/configuring-multiple-syslog-servers-for-esxi-5.html)
1232
1233 protocol
1234 Optionally set to alternate protocol if the host is not using the default
1235 protocol. Default protocol is ``https``.
1236
1237 port
1238 Optionally set to alternate port if the host is not using the default
1239 port. Default port is ``443``.
1240
1241 firewall
1242 Enable the firewall rule set for syslog. Defaults to ``True``.
1243
1244 reset_service
1245 After a successful parameter set, reset the service. Defaults to ``True``.
1246
1247 esxi_hosts
1248 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
1249 on a list of one or more ESXi machines.
1250
1251 credstore
1252 Optionally set to path to the credential store file.
1253
1254 :return: Dictionary with a top-level key of 'success' which indicates
1255 if all the parameters were reset, and individual keys
1256 for each parameter indicating which succeeded or failed, per host.
1257
1258 CLI Example:
1259
1260 .. code-block:: bash
1261
1262 # Used for ESXi host connection information
1263 salt '*' vsphere.set_syslog_config my.esxi.host root bad-password \
1264 loghost ssl://localhost:5432,tcp://10.1.0.1:1514
1265
1266 # Used for connecting to a vCenter Server
1267 salt '*' vsphere.set_syslog_config my.vcenter.location root bad-password \
1268 loghost ssl://localhost:5432,tcp://10.1.0.1:1514 \
1269 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
1270
1271 """
1272 ret = {}
1273
1274 # First, enable the syslog firewall ruleset, for each host, if needed.
1275 if firewall and syslog_config == "loghost":
1276 if esxi_hosts:
1277 if not isinstance(esxi_hosts, list):
1278 raise CommandExecutionError("'esxi_hosts' must be a list.")
1279
1280 for esxi_host in esxi_hosts:
1281 response = enable_firewall_ruleset(
1282 host,
1283 username,
1284 password,
1285 ruleset_enable=True,
1286 ruleset_name="syslog",
1287 protocol=protocol,
1288 port=port,
1289 esxi_hosts=[esxi_host],
1290 credstore=credstore,
1291 ).get(esxi_host)
1292 if response["retcode"] != 0:
1293 ret.update(
1294 {
1295 esxi_host: {
1296 "enable_firewall": {
1297 "message": response["stdout"],
1298 "success": False,
1299 }
1300 }
1301 }
1302 )
1303 else:
1304 ret.update({esxi_host: {"enable_firewall": {"success": True}}})
1305 else:
1306 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1307 response = enable_firewall_ruleset(
1308 host,
1309 username,
1310 password,
1311 ruleset_enable=True,
1312 ruleset_name="syslog",
1313 protocol=protocol,
1314 port=port,
1315 credstore=credstore,
1316 ).get(host)
1317 if response["retcode"] != 0:
1318 ret.update(
1319 {
1320 host: {
1321 "enable_firewall": {
1322 "message": response["stdout"],
1323 "success": False,
1324 }
1325 }
1326 }
1327 )
1328 else:
1329 ret.update({host: {"enable_firewall": {"success": True}}})
1330
1331 # Set the config value on each esxi_host, if provided.
1332 if esxi_hosts:
1333 if not isinstance(esxi_hosts, list):
1334 raise CommandExecutionError("'esxi_hosts' must be a list.")
1335
1336 for esxi_host in esxi_hosts:
1337 response = _set_syslog_config_helper(
1338 host,
1339 username,
1340 password,
1341 syslog_config,
1342 config_value,
1343 protocol=protocol,
1344 port=port,
1345 reset_service=reset_service,
1346 esxi_host=esxi_host,
1347 credstore=credstore,
1348 )
1349 # Ensure we don't overwrite any dictionary data already set
1350 # By updating the esxi_host directly.
1351 if ret.get(esxi_host) is None:
1352 ret.update({esxi_host: {}})
1353 ret[esxi_host].update(response)
1354
1355 else:
1356 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1357 response = _set_syslog_config_helper(
1358 host,
1359 username,
1360 password,
1361 syslog_config,
1362 config_value,
1363 protocol=protocol,
1364 port=port,
1365 reset_service=reset_service,
1366 credstore=credstore,
1367 )
1368 # Ensure we don't overwrite any dictionary data already set
1369 # By updating the host directly.
1370 if ret.get(host) is None:
1371 ret.update({host: {}})
1372 ret[host].update(response)
1373
1374 return ret
1375
1376
1377 @depends(HAS_ESX_CLI)
1378 def get_syslog_config(
1379 host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None
1380 ):
1381 """
1382 Retrieve the syslog configuration.
1383
1384 host
1385 The location of the host.
1386
1387 username
1388 The username used to login to the host, such as ``root``.
1389
1390 password
1391 The password used to login to the host.
1392
1393 protocol
1394 Optionally set to alternate protocol if the host is not using the default
1395 protocol. Default protocol is ``https``.
1396
1397 port
1398 Optionally set to alternate port if the host is not using the default
1399 port. Default port is ``443``.
1400
1401 esxi_hosts
1402 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
1403 on a list of one or more ESXi machines.
1404
1405 credstore
1406 Optionally set to path to the credential store file.
1407
1408 :return: Dictionary with keys and values corresponding to the
1409 syslog configuration, per host.
1410
1411 CLI Example:
1412
1413 .. code-block:: bash
1414
1415 # Used for ESXi host connection information
1416 salt '*' vsphere.get_syslog_config my.esxi.host root bad-password
1417
1418 # Used for connecting to a vCenter Server
1419 salt '*' vsphere.get_syslog_config my.vcenter.location root bad-password \
1420 esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
1421 """
1422 cmd = "system syslog config get"
1423
1424 ret = {}
1425 if esxi_hosts:
1426 if not isinstance(esxi_hosts, list):
1427 raise CommandExecutionError("'esxi_hosts' must be a list.")
1428
1429 for esxi_host in esxi_hosts:
1430 response = salt.utils.vmware.esxcli(
1431 host,
1432 username,
1433 password,
1434 cmd,
1435 protocol=protocol,
1436 port=port,
1437 esxi_host=esxi_host,
1438 credstore=credstore,
1439 )
1440 # format the response stdout into something useful
1441 ret.update({esxi_host: _format_syslog_config(response)})
1442 else:
1443 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1444 response = salt.utils.vmware.esxcli(
1445 host,
1446 username,
1447 password,
1448 cmd,
1449 protocol=protocol,
1450 port=port,
1451 credstore=credstore,
1452 )
1453 # format the response stdout into something useful
1454 ret.update({host: _format_syslog_config(response)})
1455
1456 return ret
1457
1458
1459 @depends(HAS_ESX_CLI)
1460 def reset_syslog_config(
1461 host,
1462 username,
1463 password,
1464 protocol=None,
1465 port=None,
1466 syslog_config=None,
1467 esxi_hosts=None,
1468 credstore=None,
1469 ):
1470 """
1471 Reset the syslog service to its default settings.
1472
1473 Valid syslog_config values are ``logdir``, ``loghost``, ``logdir-unique``,
1474 ``default-rotate``, ``default-size``, ``default-timeout``,
1475 or ``all`` for all of these.
1476
1477 host
1478 The location of the host.
1479
1480 username
1481 The username used to login to the host, such as ``root``.
1482
1483 password
1484 The password used to login to the host.
1485
1486 protocol
1487 Optionally set to alternate protocol if the host is not using the default
1488 protocol. Default protocol is ``https``.
1489
1490 port
1491 Optionally set to alternate port if the host is not using the default
1492 port. Default port is ``443``.
1493
1494 syslog_config
1495 List of parameters to reset, provided as a comma-delimited string, or 'all' to
1496 reset all syslog configuration parameters. Required.
1497
1498 esxi_hosts
1499 If ``host`` is a vCenter host, then use esxi_hosts to execute this function
1500 on a list of one or more ESXi machines.
1501
1502 credstore
1503 Optionally set to path to the credential store file.
1504
1505 :return: Dictionary with a top-level key of 'success' which indicates
1506 if all the parameters were reset, and individual keys
1507 for each parameter indicating which succeeded or failed, per host.
1508
1509 CLI Example:
1510
1511 ``syslog_config`` can be passed as a quoted, comma-separated string, e.g.
1512
1513 .. code-block:: bash
1514
1515 # Used for ESXi host connection information
1516 salt '*' vsphere.reset_syslog_config my.esxi.host root bad-password \
1517 syslog_config='logdir,loghost'
1518
1519 # Used for connecting to a vCenter Server
1520 salt '*' vsphere.reset_syslog_config my.vcenter.location root bad-password \
1521 syslog_config='logdir,loghost' esxi_hosts='[esxi-1.host.com, esxi-2.host.com]'
1522 """
1523 if not syslog_config:
1524 raise CommandExecutionError(
1525 "The 'reset_syslog_config' function requires a " "'syslog_config' setting."
1526 )
1527
1528 valid_resets = [
1529 "logdir",
1530 "loghost",
1531 "default-rotate",
1532 "default-size",
1533 "default-timeout",
1534 "logdir-unique",
1535 ]
1536 cmd = "system syslog config set --reset="
1537 if "," in syslog_config:
1538 resets = [ind_reset.strip() for ind_reset in syslog_config.split(",")]
1539 elif syslog_config == "all":
1540 resets = valid_resets
1541 else:
1542 resets = [syslog_config]
1543
1544 ret = {}
1545 if esxi_hosts:
1546 if not isinstance(esxi_hosts, list):
1547 raise CommandExecutionError("'esxi_hosts' must be a list.")
1548
1549 for esxi_host in esxi_hosts:
1550 response_dict = _reset_syslog_config_params(
1551 host,
1552 username,
1553 password,
1554 cmd,
1555 resets,
1556 valid_resets,
1557 protocol=protocol,
1558 port=port,
1559 esxi_host=esxi_host,
1560 credstore=credstore,
1561 )
1562 ret.update({esxi_host: response_dict})
1563 else:
1564 # Handles a single host or a vCenter connection when no esxi_hosts are provided.
1565 response_dict = _reset_syslog_config_params(
1566 host,
1567 username,
1568 password,
1569 cmd,
1570 resets,
1571 valid_resets,
1572 protocol=protocol,
1573 port=port,
1574 credstore=credstore,
1575 )
1576 ret.update({host: response_dict})
1577
1578 return ret
1579
1580
1581 @ignores_kwargs("credstore")
1582 def upload_ssh_key(
1583 host,
1584 username,
1585 password,
1586 ssh_key=None,
1587 ssh_key_file=None,
1588 protocol=None,
1589 port=None,
1590 certificate_verify=False,
1591 ):
1592 """
1593 Upload an ssh key for root to an ESXi host via http PUT.
1594 This function only works for ESXi, not vCenter.
1595 Only one ssh key can be uploaded for root. Uploading a second key will
1596 replace any existing key.
1597
1598 :param host: The location of the ESXi Host
1599 :param username: Username to connect as
1600 :param password: Password for the ESXi web endpoint
1601 :param ssh_key: Public SSH key, will be added to authorized_keys on ESXi
1602 :param ssh_key_file: File containing the SSH key. Use 'ssh_key' or
1603 ssh_key_file, but not both.
1604 :param protocol: defaults to https, can be http if ssl is disabled on ESXi
1605 :param port: defaults to 443 for https
1606 :param certificate_verify: If true require that the SSL connection present
1607 a valid certificate
1608 :return: Dictionary with a 'status' key, True if upload is successful.
1609 If upload is unsuccessful, 'status' key will be False and
1610 an 'Error' key will have an informative message.
1611
1612 CLI Example:
1613
1614 .. code-block:: bash
1615
1616 salt '*' vsphere.upload_ssh_key my.esxi.host root bad-password ssh_key_file='/etc/salt/my_keys/my_key.pub'
1617
1618 """
1619 if protocol is None:
1620 protocol = "https"
1621 if port is None:
1622 port = 443
1623
1624 url = "{}://{}:{}/host/ssh_root_authorized_keys".format(protocol, host, port)
1625 ret = {}
1626 result = None
1627 try:
1628 if ssh_key:
1629 result = salt.utils.http.query(
1630 url,
1631 status=True,
1632 text=True,
1633 method="PUT",
1634 username=username,
1635 password=password,
1636 data=ssh_key,
1637 verify_ssl=certificate_verify,
1638 )
1639 elif ssh_key_file:
1640 result = salt.utils.http.query(
1641 url,
1642 status=True,
1643 text=True,
1644 method="PUT",
1645 username=username,
1646 password=password,
1647 data_file=ssh_key_file,
1648 data_render=False,
1649 verify_ssl=certificate_verify,
1650 )
1651 if result.get("status") == 200:
1652 ret["status"] = True
1653 else:
1654 ret["status"] = False
1655 ret["Error"] = result["error"]
1656 except Exception as msg: # pylint: disable=broad-except
1657 ret["status"] = False
1658 ret["Error"] = msg
1659
1660 return ret
1661
1662
1663 @ignores_kwargs("credstore")
1664 def get_ssh_key(
1665 host, username, password, protocol=None, port=None, certificate_verify=False
1666 ):
1667 """
1668 Retrieve the authorized_keys entry for root.
1669 This function only works for ESXi, not vCenter.
1670
1671 :param host: The location of the ESXi Host
1672 :param username: Username to connect as
1673 :param password: Password for the ESXi web endpoint
1674 :param protocol: defaults to https, can be http if ssl is disabled on ESXi
1675 :param port: defaults to 443 for https
1676 :param certificate_verify: If true require that the SSL connection present
1677 a valid certificate
1678 :return: True if upload is successful
1679
1680 CLI Example:
1681
1682 .. code-block:: bash
1683
1684 salt '*' vsphere.get_ssh_key my.esxi.host root bad-password certificate_verify=True
1685
1686 """
1687 if protocol is None:
1688 protocol = "https"
1689 if port is None:
1690 port = 443
1691
1692 url = "{}://{}:{}/host/ssh_root_authorized_keys".format(protocol, host, port)
1693 ret = {}
1694 try:
1695 result = salt.utils.http.query(
1696 url,
1697 status=True,
1698 text=True,
1699 method="GET",
1700 username=username,
1701 password=password,
1702 verify_ssl=certificate_verify,
1703 )
1704 if result.get("status") == 200:
1705 ret["status"] = True
1706 ret["key"] = result["text"]
1707 else:
1708 ret["status"] = False
1709 ret["Error"] = result["error"]
1710 except Exception as msg: # pylint: disable=broad-except
1711 ret["status"] = False
1712 ret["Error"] = msg
1713
1714 return ret
1715
1716
1717 @depends(HAS_PYVMOMI)
1718 @ignores_kwargs("credstore")
1719 def get_host_datetime(
1720 host, username, password, protocol=None, port=None, host_names=None
1721 ):
1722 """
1723 Get the date/time information for a given host or list of host_names.
1724
1725 host
1726 The location of the host.
1727
1728 username
1729 The username used to login to the host, such as ``root``.
1730
1731 password
1732 The password used to login to the host.
1733
1734 protocol
1735 Optionally set to alternate protocol if the host is not using the default
1736 protocol. Default protocol is ``https``.
1737
1738 port
1739 Optionally set to alternate port if the host is not using the default
1740 port. Default port is ``443``.
1741
1742 host_names
1743 List of ESXi host names. When the host, username, and password credentials
1744 are provided for a vCenter Server, the host_names argument is required to tell
1745 vCenter the hosts for which to get date/time information.
1746
1747 If host_names is not provided, the date/time information will be retrieved for the
1748 ``host`` location instead. This is useful for when service instance connection
1749 information is used for a single ESXi host.
1750
1751 CLI Example:
1752
1753 .. code-block:: bash
1754
1755 # Used for single ESXi host connection information
1756 salt '*' vsphere.get_host_datetime my.esxi.host root bad-password
1757
1758 # Used for connecting to a vCenter Server
1759 salt '*' vsphere.get_host_datetime my.vcenter.location root bad-password \
1760 host_names='[esxi-1.host.com, esxi-2.host.com]'
1761 """
1762 service_instance = salt.utils.vmware.get_service_instance(
1763 host=host, username=username, password=password, protocol=protocol, port=port
1764 )
1765 host_names = _check_hosts(service_instance, host, host_names)
1766 ret = {}
1767 for host_name in host_names:
1768 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
1769 date_time_manager = _get_date_time_mgr(host_ref)
1770 date_time = date_time_manager.QueryDateTime()
1771 ret.update({host_name: date_time})
1772
1773 return ret
1774
1775
1776 @depends(HAS_PYVMOMI)
1777 @ignores_kwargs("credstore")
1778 def get_ntp_config(host, username, password, protocol=None, port=None, host_names=None):
1779 """
1780 Get the NTP configuration information for a given host or list of host_names.
1781
1782 host
1783 The location of the host.
1784
1785 username
1786 The username used to login to the host, such as ``root``.
1787
1788 password
1789 The password used to login to the host.
1790
1791 protocol
1792 Optionally set to alternate protocol if the host is not using the default
1793 protocol. Default protocol is ``https``.
1794
1795 port
1796 Optionally set to alternate port if the host is not using the default
1797 port. Default port is ``443``.
1798
1799 host_names
1800 List of ESXi host names. When the host, username, and password credentials
1801 are provided for a vCenter Server, the host_names argument is required to tell
1802 vCenter the hosts for which to get ntp configuration information.
1803
1804 If host_names is not provided, the NTP configuration will be retrieved for the
1805 ``host`` location instead. This is useful for when service instance connection
1806 information is used for a single ESXi host.
1807
1808 CLI Example:
1809
1810 .. code-block:: bash
1811
1812 # Used for single ESXi host connection information
1813 salt '*' vsphere.get_ntp_config my.esxi.host root bad-password
1814
1815 # Used for connecting to a vCenter Server
1816 salt '*' vsphere.get_ntp_config my.vcenter.location root bad-password \
1817 host_names='[esxi-1.host.com, esxi-2.host.com]'
1818 """
1819 service_instance = salt.utils.vmware.get_service_instance(
1820 host=host, username=username, password=password, protocol=protocol, port=port
1821 )
1822 host_names = _check_hosts(service_instance, host, host_names)
1823 ret = {}
1824 for host_name in host_names:
1825 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
1826 ntp_config = host_ref.configManager.dateTimeSystem.dateTimeInfo.ntpConfig.server
1827 ret.update({host_name: ntp_config})
1828
1829 return ret
1830
1831
1832 @depends(HAS_PYVMOMI)
1833 @ignores_kwargs("credstore")
1834 def get_service_policy(
1835 host, username, password, service_name, protocol=None, port=None, host_names=None
1836 ):
1837 """
1838 Get the service name's policy for a given host or list of hosts.
1839
1840 host
1841 The location of the host.
1842
1843 username
1844 The username used to login to the host, such as ``root``.
1845
1846 password
1847 The password used to login to the host.
1848
1849 service_name
1850 The name of the service for which to retrieve the policy. Supported service names are:
1851 - DCUI
1852 - TSM
1853 - SSH
1854 - lbtd
1855 - lsassd
1856 - lwiod
1857 - netlogond
1858 - ntpd
1859 - sfcbd-watchdog
1860 - snmpd
1861 - vprobed
1862 - vpxa
1863 - xorg
1864
1865 protocol
1866 Optionally set to alternate protocol if the host is not using the default
1867 protocol. Default protocol is ``https``.
1868
1869 port
1870 Optionally set to alternate port if the host is not using the default
1871 port. Default port is ``443``.
1872
1873 host_names
1874 List of ESXi host names. When the host, username, and password credentials
1875 are provided for a vCenter Server, the host_names argument is required to tell
1876 vCenter the hosts for which to get service policy information.
1877
1878 If host_names is not provided, the service policy information will be retrieved
1879 for the ``host`` location instead. This is useful for when service instance
1880 connection information is used for a single ESXi host.
1881
1882 CLI Example:
1883
1884 .. code-block:: bash
1885
1886 # Used for single ESXi host connection information
1887 salt '*' vsphere.get_service_policy my.esxi.host root bad-password 'ssh'
1888
1889 # Used for connecting to a vCenter Server
1890 salt '*' vsphere.get_service_policy my.vcenter.location root bad-password 'ntpd' \
1891 host_names='[esxi-1.host.com, esxi-2.host.com]'
1892 """
1893 service_instance = salt.utils.vmware.get_service_instance(
1894 host=host, username=username, password=password, protocol=protocol, port=port
1895 )
1896 valid_services = [
1897 "DCUI",
1898 "TSM",
1899 "SSH",
1900 "ssh",
1901 "lbtd",
1902 "lsassd",
1903 "lwiod",
1904 "netlogond",
1905 "ntpd",
1906 "sfcbd-watchdog",
1907 "snmpd",
1908 "vprobed",
1909 "vpxa",
1910 "xorg",
1911 ]
1912 host_names = _check_hosts(service_instance, host, host_names)
1913
1914 ret = {}
1915 for host_name in host_names:
1916 # Check if the service_name provided is a valid one.
1917 # If we don't have a valid service, return. The service will be invalid for all hosts.
1918 if service_name not in valid_services:
1919 ret.update(
1920 {
1921 host_name: {
1922 "Error": "{} is not a valid service name.".format(service_name)
1923 }
1924 }
1925 )
1926 return ret
1927
1928 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
1929 services = host_ref.configManager.serviceSystem.serviceInfo.service
1930
1931 # Don't require users to know that VMware lists the ssh service as TSM-SSH
1932 if service_name == "SSH" or service_name == "ssh":
1933 temp_service_name = "TSM-SSH"
1934 else:
1935 temp_service_name = service_name
1936
1937 # Loop through services until we find a matching name
1938 for service in services:
1939 if service.key == temp_service_name:
1940 ret.update({host_name: {service_name: service.policy}})
1941 # We've found a match - break out of the loop so we don't overwrite the
1942 # Updated host_name value with an error message.
1943 break
1944 else:
1945 msg = "Could not find service '{}' for host '{}'.".format(
1946 service_name, host_name
1947 )
1948 ret.update({host_name: {"Error": msg}})
1949
1950 # If we made it this far, something else has gone wrong.
1951 if ret.get(host_name) is None:
1952 msg = "'vsphere.get_service_policy' failed for host {}.".format(host_name)
1953 log.debug(msg)
1954 ret.update({host_name: {"Error": msg}})
1955
1956 return ret
1957
1958
1959 @depends(HAS_PYVMOMI)
1960 @ignores_kwargs("credstore")
1961 def get_service_running(
1962 host, username, password, service_name, protocol=None, port=None, host_names=None
1963 ):
1964 """
1965 Get the service name's running state for a given host or list of hosts.
1966
1967 host
1968 The location of the host.
1969
1970 username
1971 The username used to login to the host, such as ``root``.
1972
1973 password
1974 The password used to login to the host.
1975
1976 service_name
1977 The name of the service for which to retrieve the policy. Supported service names are:
1978 - DCUI
1979 - TSM
1980 - SSH
1981 - lbtd
1982 - lsassd
1983 - lwiod
1984 - netlogond
1985 - ntpd
1986 - sfcbd-watchdog
1987 - snmpd
1988 - vprobed
1989 - vpxa
1990 - xorg
1991
1992 protocol
1993 Optionally set to alternate protocol if the host is not using the default
1994 protocol. Default protocol is ``https``.
1995
1996 port
1997 Optionally set to alternate port if the host is not using the default
1998 port. Default port is ``443``.
1999
2000 host_names
2001 List of ESXi host names. When the host, username, and password credentials
2002 are provided for a vCenter Server, the host_names argument is required to tell
2003 vCenter the hosts for which to get the service's running state.
2004
2005 If host_names is not provided, the service's running state will be retrieved
2006 for the ``host`` location instead. This is useful for when service instance
2007 connection information is used for a single ESXi host.
2008
2009 CLI Example:
2010
2011 .. code-block:: bash
2012
2013 # Used for single ESXi host connection information
2014 salt '*' vsphere.get_service_running my.esxi.host root bad-password 'ssh'
2015
2016 # Used for connecting to a vCenter Server
2017 salt '*' vsphere.get_service_running my.vcenter.location root bad-password 'ntpd' \
2018 host_names='[esxi-1.host.com, esxi-2.host.com]'
2019 """
2020 service_instance = salt.utils.vmware.get_service_instance(
2021 host=host, username=username, password=password, protocol=protocol, port=port
2022 )
2023 valid_services = [
2024 "DCUI",
2025 "TSM",
2026 "SSH",
2027 "ssh",
2028 "lbtd",
2029 "lsassd",
2030 "lwiod",
2031 "netlogond",
2032 "ntpd",
2033 "sfcbd-watchdog",
2034 "snmpd",
2035 "vprobed",
2036 "vpxa",
2037 "xorg",
2038 ]
2039 host_names = _check_hosts(service_instance, host, host_names)
2040
2041 ret = {}
2042 for host_name in host_names:
2043 # Check if the service_name provided is a valid one.
2044 # If we don't have a valid service, return. The service will be invalid for all hosts.
2045 if service_name not in valid_services:
2046 ret.update(
2047 {
2048 host_name: {
2049 "Error": "{} is not a valid service name.".format(service_name)
2050 }
2051 }
2052 )
2053 return ret
2054
2055 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2056 services = host_ref.configManager.serviceSystem.serviceInfo.service
2057
2058 # Don't require users to know that VMware lists the ssh service as TSM-SSH
2059 if service_name == "SSH" or service_name == "ssh":
2060 temp_service_name = "TSM-SSH"
2061 else:
2062 temp_service_name = service_name
2063
2064 # Loop through services until we find a matching name
2065 for service in services:
2066 if service.key == temp_service_name:
2067 ret.update({host_name: {service_name: service.running}})
2068 # We've found a match - break out of the loop so we don't overwrite the
2069 # Updated host_name value with an error message.
2070 break
2071 else:
2072 msg = "Could not find service '{}' for host '{}'.".format(
2073 service_name, host_name
2074 )
2075 ret.update({host_name: {"Error": msg}})
2076
2077 # If we made it this far, something else has gone wrong.
2078 if ret.get(host_name) is None:
2079 msg = "'vsphere.get_service_running' failed for host {}.".format(host_name)
2080 log.debug(msg)
2081 ret.update({host_name: {"Error": msg}})
2082
2083 return ret
2084
2085
2086 @depends(HAS_PYVMOMI)
2087 @ignores_kwargs("credstore")
2088 def get_vmotion_enabled(
2089 host, username, password, protocol=None, port=None, host_names=None
2090 ):
2091 """
2092 Get the VMotion enabled status for a given host or a list of host_names. Returns ``True``
2093 if VMotion is enabled, ``False`` if it is not enabled.
2094
2095 host
2096 The location of the host.
2097
2098 username
2099 The username used to login to the host, such as ``root``.
2100
2101 password
2102 The password used to login to the host.
2103
2104 protocol
2105 Optionally set to alternate protocol if the host is not using the default
2106 protocol. Default protocol is ``https``.
2107
2108 port
2109 Optionally set to alternate port if the host is not using the default
2110 port. Default port is ``443``.
2111
2112 host_names
2113 List of ESXi host names. When the host, username, and password credentials
2114 are provided for a vCenter Server, the host_names argument is required to
2115 tell vCenter which hosts to check if VMotion is enabled.
2116
2117 If host_names is not provided, the VMotion status will be retrieved for the
2118 ``host`` location instead. This is useful for when service instance
2119 connection information is used for a single ESXi host.
2120
2121 CLI Example:
2122
2123 .. code-block:: bash
2124
2125 # Used for single ESXi host connection information
2126 salt '*' vsphere.get_vmotion_enabled my.esxi.host root bad-password
2127
2128 # Used for connecting to a vCenter Server
2129 salt '*' vsphere.get_vmotion_enabled my.vcenter.location root bad-password \
2130 host_names='[esxi-1.host.com, esxi-2.host.com]'
2131 """
2132 service_instance = salt.utils.vmware.get_service_instance(
2133 host=host, username=username, password=password, protocol=protocol, port=port
2134 )
2135 host_names = _check_hosts(service_instance, host, host_names)
2136 ret = {}
2137 for host_name in host_names:
2138 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2139 vmotion_vnic = host_ref.configManager.vmotionSystem.netConfig.selectedVnic
2140 if vmotion_vnic:
2141 ret.update({host_name: {"VMotion Enabled": True}})
2142 else:
2143 ret.update({host_name: {"VMotion Enabled": False}})
2144
2145 return ret
2146
2147
2148 @depends(HAS_PYVMOMI)
2149 @ignores_kwargs("credstore")
2150 def get_vsan_enabled(
2151 host, username, password, protocol=None, port=None, host_names=None
2152 ):
2153 """
2154 Get the VSAN enabled status for a given host or a list of host_names. Returns ``True``
2155 if VSAN is enabled, ``False`` if it is not enabled, and ``None`` if a VSAN Host Config
2156 is unset, per host.
2157
2158 host
2159 The location of the host.
2160
2161 username
2162 The username used to login to the host, such as ``root``.
2163
2164 password
2165 The password used to login to the host.
2166
2167 protocol
2168 Optionally set to alternate protocol if the host is not using the default
2169 protocol. Default protocol is ``https``.
2170
2171 port
2172 Optionally set to alternate port if the host is not using the default
2173 port. Default port is ``443``.
2174
2175 host_names
2176 List of ESXi host names. When the host, username, and password credentials
2177 are provided for a vCenter Server, the host_names argument is required to
2178 tell vCenter which hosts to check if VSAN enabled.
2179
2180 If host_names is not provided, the VSAN status will be retrieved for the
2181 ``host`` location instead. This is useful for when service instance
2182 connection information is used for a single ESXi host.
2183
2184 CLI Example:
2185
2186 .. code-block:: bash
2187
2188 # Used for single ESXi host connection information
2189 salt '*' vsphere.get_vsan_enabled my.esxi.host root bad-password
2190
2191 # Used for connecting to a vCenter Server
2192 salt '*' vsphere.get_vsan_enabled my.vcenter.location root bad-password \
2193 host_names='[esxi-1.host.com, esxi-2.host.com]'
2194 """
2195 service_instance = salt.utils.vmware.get_service_instance(
2196 host=host, username=username, password=password, protocol=protocol, port=port
2197 )
2198 host_names = _check_hosts(service_instance, host, host_names)
2199 ret = {}
2200 for host_name in host_names:
2201 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2202 vsan_config = host_ref.config.vsanHostConfig
2203
2204 # We must have a VSAN Config in place get information about VSAN state.
2205 if vsan_config is None:
2206 msg = "VSAN System Config Manager is unset for host '{}'.".format(host_name)
2207 log.debug(msg)
2208 ret.update({host_name: {"Error": msg}})
2209 else:
2210 ret.update({host_name: {"VSAN Enabled": vsan_config.enabled}})
2211
2212 return ret
2213
2214
2215 @depends(HAS_PYVMOMI)
2216 @ignores_kwargs("credstore")
2217 def get_vsan_eligible_disks(
2218 host, username, password, protocol=None, port=None, host_names=None
2219 ):
2220 """
2221 Returns a list of VSAN-eligible disks for a given host or list of host_names.
2222
2223 host
2224 The location of the host.
2225
2226 username
2227 The username used to login to the host, such as ``root``.
2228
2229 password
2230 The password used to login to the host.
2231
2232 protocol
2233 Optionally set to alternate protocol if the host is not using the default
2234 protocol. Default protocol is ``https``.
2235
2236 port
2237 Optionally set to alternate port if the host is not using the default
2238 port. Default port is ``443``.
2239
2240 host_names
2241 List of ESXi host names. When the host, username, and password credentials
2242 are provided for a vCenter Server, the host_names argument is required to
2243 tell vCenter which hosts to check if any VSAN-eligible disks are available.
2244
2245 If host_names is not provided, the VSAN-eligible disks will be retrieved
2246 for the ``host`` location instead. This is useful for when service instance
2247 connection information is used for a single ESXi host.
2248
2249 CLI Example:
2250
2251 .. code-block:: bash
2252
2253 # Used for single ESXi host connection information
2254 salt '*' vsphere.get_vsan_eligible_disks my.esxi.host root bad-password
2255
2256 # Used for connecting to a vCenter Server
2257 salt '*' vsphere.get_vsan_eligible_disks my.vcenter.location root bad-password \
2258 host_names='[esxi-1.host.com, esxi-2.host.com]'
2259 """
2260 service_instance = salt.utils.vmware.get_service_instance(
2261 host=host, username=username, password=password, protocol=protocol, port=port
2262 )
2263 host_names = _check_hosts(service_instance, host, host_names)
2264 response = _get_vsan_eligible_disks(service_instance, host, host_names)
2265
2266 ret = {}
2267 for host_name, value in response.items():
2268 error = value.get("Error")
2269 if error:
2270 ret.update({host_name: {"Error": error}})
2271 continue
2272
2273 disks = value.get("Eligible")
2274 # If we have eligible disks, it will be a list of disk objects
2275 if disks and isinstance(disks, list):
2276 disk_names = []
2277 # We need to return ONLY the disk names, otherwise
2278 # MessagePack can't deserialize the disk objects.
2279 for disk in disks:
2280 disk_names.append(disk.canonicalName)
2281 ret.update({host_name: {"Eligible": disk_names}})
2282 else:
2283 # If we have disks, but it's not a list, it's actually a
2284 # string message that we're passing along.
2285 ret.update({host_name: {"Eligible": disks}})
2286
2287 return ret
2288
2289
2290 @depends(HAS_PYVMOMI)
2291 @supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm")
2292 @gets_service_instance_via_proxy
2293 def test_vcenter_connection(service_instance=None):
2294 """
2295 Checks if a connection is to a vCenter
2296
2297 CLI Example:
2298
2299 .. code-block:: bash
2300
2301 salt '*' vsphere.test_vcenter_connection
2302 """
2303 try:
2304 if salt.utils.vmware.is_connection_to_a_vcenter(service_instance):
2305 return True
2306 except VMwareSaltError:
2307 return False
2308 return False
2309
2310
2311 @depends(HAS_PYVMOMI)
2312 @ignores_kwargs("credstore")
2313 def system_info(host, username, password, protocol=None, port=None):
2314 """
2315 Return system information about a VMware environment.
2316
2317 host
2318 The location of the host.
2319
2320 username
2321 The username used to login to the host, such as ``root``.
2322
2323 password
2324 The password used to login to the host.
2325
2326 protocol
2327 Optionally set to alternate protocol if the host is not using the default
2328 protocol. Default protocol is ``https``.
2329
2330 port
2331 Optionally set to alternate port if the host is not using the default
2332 port. Default port is ``443``.
2333
2334 CLI Example:
2335
2336 .. code-block:: bash
2337
2338 salt '*' vsphere.system_info 1.2.3.4 root bad-password
2339 """
2340 service_instance = salt.utils.vmware.get_service_instance(
2341 host=host, username=username, password=password, protocol=protocol, port=port
2342 )
2343 ret = salt.utils.vmware.get_inventory(service_instance).about.__dict__
2344 if "apiType" in ret:
2345 if ret["apiType"] == "HostAgent":
2346 ret = dictupdate.update(
2347 ret, salt.utils.vmware.get_hardware_grains(service_instance)
2348 )
2349 return ret
2350
2351
2352 @depends(HAS_PYVMOMI)
2353 @ignores_kwargs("credstore")
2354 def list_datacenters(host, username, password, protocol=None, port=None):
2355 """
2356 Returns a list of datacenters for the specified host.
2357
2358 host
2359 The location of the host.
2360
2361 username
2362 The username used to login to the host, such as ``root``.
2363
2364 password
2365 The password used to login to the host.
2366
2367 protocol
2368 Optionally set to alternate protocol if the host is not using the default
2369 protocol. Default protocol is ``https``.
2370
2371 port
2372 Optionally set to alternate port if the host is not using the default
2373 port. Default port is ``443``.
2374
2375 CLI Example:
2376
2377 .. code-block:: bash
2378
2379 salt '*' vsphere.list_datacenters 1.2.3.4 root bad-password
2380
2381 """
2382 service_instance = salt.utils.vmware.get_service_instance(
2383 host=host, username=username, password=password, protocol=protocol, port=port
2384 )
2385 return salt.utils.vmware.list_datacenters(service_instance)
2386
2387
2388 @depends(HAS_PYVMOMI)
2389 @ignores_kwargs("credstore")
2390 def list_clusters(host, username, password, protocol=None, port=None):
2391 """
2392 Returns a list of clusters for the specified host.
2393
2394 host
2395 The location of the host.
2396
2397 username
2398 The username used to login to the host, such as ``root``.
2399
2400 password
2401 The password used to login to the host.
2402
2403 protocol
2404 Optionally set to alternate protocol if the host is not using the default
2405 protocol. Default protocol is ``https``.
2406
2407 port
2408 Optionally set to alternate port if the host is not using the default
2409 port. Default port is ``443``.
2410
2411 CLI Example:
2412
2413 .. code-block:: bash
2414
2415 salt '*' vsphere.list_clusters 1.2.3.4 root bad-password
2416
2417 """
2418 service_instance = salt.utils.vmware.get_service_instance(
2419 host=host, username=username, password=password, protocol=protocol, port=port
2420 )
2421 return salt.utils.vmware.list_clusters(service_instance)
2422
2423
2424 @depends(HAS_PYVMOMI)
2425 @ignores_kwargs("credstore")
2426 def list_datastore_clusters(host, username, password, protocol=None, port=None):
2427 """
2428 Returns a list of datastore clusters for the specified host.
2429
2430 host
2431 The location of the host.
2432
2433 username
2434 The username used to login to the host, such as ``root``.
2435
2436 password
2437 The password used to login to the host.
2438
2439 protocol
2440 Optionally set to alternate protocol if the host is not using the default
2441 protocol. Default protocol is ``https``.
2442
2443 port
2444 Optionally set to alternate port if the host is not using the default
2445 port. Default port is ``443``.
2446
2447 CLI Example:
2448
2449 .. code-block:: bash
2450
2451 salt '*' vsphere.list_datastore_clusters 1.2.3.4 root bad-password
2452 """
2453 service_instance = salt.utils.vmware.get_service_instance(
2454 host=host, username=username, password=password, protocol=protocol, port=port
2455 )
2456 return salt.utils.vmware.list_datastore_clusters(service_instance)
2457
2458
2459 @depends(HAS_PYVMOMI)
2460 @ignores_kwargs("credstore")
2461 def list_datastores(host, username, password, protocol=None, port=None):
2462 """
2463 Returns a list of datastores for the specified host.
2464
2465 host
2466 The location of the host.
2467
2468 username
2469 The username used to login to the host, such as ``root``.
2470
2471 password
2472 The password used to login to the host.
2473
2474 protocol
2475 Optionally set to alternate protocol if the host is not using the default
2476 protocol. Default protocol is ``https``.
2477
2478 port
2479 Optionally set to alternate port if the host is not using the default
2480 port. Default port is ``443``.
2481
2482 CLI Example:
2483
2484 .. code-block:: bash
2485
2486 salt '*' vsphere.list_datastores 1.2.3.4 root bad-password
2487 """
2488 service_instance = salt.utils.vmware.get_service_instance(
2489 host=host, username=username, password=password, protocol=protocol, port=port
2490 )
2491 return salt.utils.vmware.list_datastores(service_instance)
2492
2493
2494 @depends(HAS_PYVMOMI)
2495 @ignores_kwargs("credstore")
2496 def list_hosts(host, username, password, protocol=None, port=None):
2497 """
2498 Returns a list of hosts for the specified VMware environment.
2499
2500 host
2501 The location of the host.
2502
2503 username
2504 The username used to login to the host, such as ``root``.
2505
2506 password
2507 The password used to login to the host.
2508
2509 protocol
2510 Optionally set to alternate protocol if the host is not using the default
2511 protocol. Default protocol is ``https``.
2512
2513 port
2514 Optionally set to alternate port if the host is not using the default
2515 port. Default port is ``443``.
2516
2517 CLI Example:
2518
2519 .. code-block:: bash
2520
2521 salt '*' vsphere.list_hosts 1.2.3.4 root bad-password
2522 """
2523 service_instance = salt.utils.vmware.get_service_instance(
2524 host=host, username=username, password=password, protocol=protocol, port=port
2525 )
2526 return salt.utils.vmware.list_hosts(service_instance)
2527
2528
2529 @depends(HAS_PYVMOMI)
2530 @ignores_kwargs("credstore")
2531 def list_resourcepools(host, username, password, protocol=None, port=None):
2532 """
2533 Returns a list of resource pools for the specified host.
2534
2535 host
2536 The location of the host.
2537
2538 username
2539 The username used to login to the host, such as ``root``.
2540
2541 password
2542 The password used to login to the host.
2543
2544 protocol
2545 Optionally set to alternate protocol if the host is not using the default
2546 protocol. Default protocol is ``https``.
2547
2548 port
2549 Optionally set to alternate port if the host is not using the default
2550 port. Default port is ``443``.
2551
2552 CLI Example:
2553
2554 .. code-block:: bash
2555
2556 salt '*' vsphere.list_resourcepools 1.2.3.4 root bad-password
2557 """
2558 service_instance = salt.utils.vmware.get_service_instance(
2559 host=host, username=username, password=password, protocol=protocol, port=port
2560 )
2561 return salt.utils.vmware.list_resourcepools(service_instance)
2562
2563
2564 @depends(HAS_PYVMOMI)
2565 @ignores_kwargs("credstore")
2566 def list_networks(host, username, password, protocol=None, port=None):
2567 """
2568 Returns a list of networks for the specified host.
2569
2570 host
2571 The location of the host.
2572
2573 username
2574 The username used to login to the host, such as ``root``.
2575
2576 password
2577 The password used to login to the host.
2578
2579 protocol
2580 Optionally set to alternate protocol if the host is not using the default
2581 protocol. Default protocol is ``https``.
2582
2583 port
2584 Optionally set to alternate port if the host is not using the default
2585 port. Default port is ``443``.
2586
2587 CLI Example:
2588
2589 .. code-block:: bash
2590
2591 salt '*' vsphere.list_networks 1.2.3.4 root bad-password
2592 """
2593 service_instance = salt.utils.vmware.get_service_instance(
2594 host=host, username=username, password=password, protocol=protocol, port=port
2595 )
2596 return salt.utils.vmware.list_networks(service_instance)
2597
2598
2599 @depends(HAS_PYVMOMI)
2600 @ignores_kwargs("credstore")
2601 def list_vms(host, username, password, protocol=None, port=None):
2602 """
2603 Returns a list of VMs for the specified host.
2604
2605 host
2606 The location of the host.
2607
2608 username
2609 The username used to login to the host, such as ``root``.
2610
2611 password
2612 The password used to login to the host.
2613
2614 protocol
2615 Optionally set to alternate protocol if the host is not using the default
2616 protocol. Default protocol is ``https``.
2617
2618 port
2619 Optionally set to alternate port if the host is not using the default
2620 port. Default port is ``443``.
2621
2622 CLI Example:
2623
2624 .. code-block:: bash
2625
2626 salt '*' vsphere.list_vms 1.2.3.4 root bad-password
2627 """
2628 service_instance = salt.utils.vmware.get_service_instance(
2629 host=host, username=username, password=password, protocol=protocol, port=port
2630 )
2631 return salt.utils.vmware.list_vms(service_instance)
2632
2633
2634 @depends(HAS_PYVMOMI)
2635 @ignores_kwargs("credstore")
2636 def list_folders(host, username, password, protocol=None, port=None):
2637 """
2638 Returns a list of folders for the specified host.
2639
2640 host
2641 The location of the host.
2642
2643 username
2644 The username used to login to the host, such as ``root``.
2645
2646 password
2647 The password used to login to the host.
2648
2649 protocol
2650 Optionally set to alternate protocol if the host is not using the default
2651 protocol. Default protocol is ``https``.
2652
2653 port
2654 Optionally set to alternate port if the host is not using the default
2655 port. Default port is ``443``.
2656
2657 CLI Example:
2658
2659 .. code-block:: bash
2660
2661 salt '*' vsphere.list_folders 1.2.3.4 root bad-password
2662 """
2663 service_instance = salt.utils.vmware.get_service_instance(
2664 host=host, username=username, password=password, protocol=protocol, port=port
2665 )
2666 return salt.utils.vmware.list_folders(service_instance)
2667
2668
2669 @depends(HAS_PYVMOMI)
2670 @ignores_kwargs("credstore")
2671 def list_dvs(host, username, password, protocol=None, port=None):
2672 """
2673 Returns a list of distributed virtual switches for the specified host.
2674
2675 host
2676 The location of the host.
2677
2678 username
2679 The username used to login to the host, such as ``root``.
2680
2681 password
2682 The password used to login to the host.
2683
2684 protocol
2685 Optionally set to alternate protocol if the host is not using the default
2686 protocol. Default protocol is ``https``.
2687
2688 port
2689 Optionally set to alternate port if the host is not using the default
2690 port. Default port is ``443``.
2691
2692 CLI Example:
2693
2694 .. code-block:: bash
2695
2696 salt '*' vsphere.list_dvs 1.2.3.4 root bad-password
2697 """
2698 service_instance = salt.utils.vmware.get_service_instance(
2699 host=host, username=username, password=password, protocol=protocol, port=port
2700 )
2701 return salt.utils.vmware.list_dvs(service_instance)
2702
2703
2704 @depends(HAS_PYVMOMI)
2705 @ignores_kwargs("credstore")
2706 def list_vapps(host, username, password, protocol=None, port=None):
2707 """
2708 Returns a list of vApps for the specified host.
2709
2710 host
2711 The location of the host.
2712
2713 username
2714 The username used to login to the host, such as ``root``.
2715
2716 password
2717 The password used to login to the host.
2718
2719 protocol
2720 Optionally set to alternate protocol if the host is not using the default
2721 protocol. Default protocol is ``https``.
2722
2723 port
2724 Optionally set to alternate port if the host is not using the default
2725 port. Default port is ``443``.
2726
2727 CLI Example:
2728
2729 .. code-block:: bash
2730
2731 # List vapps from all minions
2732 salt '*' vsphere.list_vapps 1.2.3.4 root bad-password
2733 """
2734 service_instance = salt.utils.vmware.get_service_instance(
2735 host=host, username=username, password=password, protocol=protocol, port=port
2736 )
2737 return salt.utils.vmware.list_vapps(service_instance)
2738
2739
2740 @depends(HAS_PYVMOMI)
2741 @ignores_kwargs("credstore")
2742 def list_ssds(host, username, password, protocol=None, port=None, host_names=None):
2743 """
2744 Returns a list of SSDs for the given host or list of host_names.
2745
2746 host
2747 The location of the host.
2748
2749 username
2750 The username used to login to the host, such as ``root``.
2751
2752 password
2753 The password used to login to the host.
2754
2755 protocol
2756 Optionally set to alternate protocol if the host is not using the default
2757 protocol. Default protocol is ``https``.
2758
2759 port
2760 Optionally set to alternate port if the host is not using the default
2761 port. Default port is ``443``.
2762
2763 host_names
2764 List of ESXi host names. When the host, username, and password credentials
2765 are provided for a vCenter Server, the host_names argument is required to
2766 tell vCenter the hosts for which to retrieve SSDs.
2767
2768 If host_names is not provided, SSDs will be retrieved for the
2769 ``host`` location instead. This is useful for when service instance
2770 connection information is used for a single ESXi host.
2771
2772 CLI Example:
2773
2774 .. code-block:: bash
2775
2776 # Used for single ESXi host connection information
2777 salt '*' vsphere.list_ssds my.esxi.host root bad-password
2778
2779 # Used for connecting to a vCenter Server
2780 salt '*' vsphere.list_ssds my.vcenter.location root bad-password \
2781 host_names='[esxi-1.host.com, esxi-2.host.com]'
2782 """
2783 service_instance = salt.utils.vmware.get_service_instance(
2784 host=host, username=username, password=password, protocol=protocol, port=port
2785 )
2786 host_names = _check_hosts(service_instance, host, host_names)
2787 ret = {}
2788 names = []
2789 for host_name in host_names:
2790 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2791 disks = _get_host_ssds(host_ref)
2792 for disk in disks:
2793 names.append(disk.canonicalName)
2794 ret.update({host_name: names})
2795
2796 return ret
2797
2798
2799 @depends(HAS_PYVMOMI)
2800 @ignores_kwargs("credstore")
2801 def list_non_ssds(host, username, password, protocol=None, port=None, host_names=None):
2802 """
2803 Returns a list of Non-SSD disks for the given host or list of host_names.
2804
2805 .. note::
2806
2807 In the pyVmomi StorageSystem, ScsiDisks may, or may not have an ``ssd`` attribute.
2808 This attribute indicates if the ScsiDisk is SSD backed. As this option is optional,
2809 if a relevant disk in the StorageSystem does not have ``ssd = true``, it will end
2810 up in the ``non_ssds`` list here.
2811
2812 host
2813 The location of the host.
2814
2815 username
2816 The username used to login to the host, such as ``root``.
2817
2818 password
2819 The password used to login to the host.
2820
2821 protocol
2822 Optionally set to alternate protocol if the host is not using the default
2823 protocol. Default protocol is ``https``.
2824
2825 port
2826 Optionally set to alternate port if the host is not using the default
2827 port. Default port is ``443``.
2828
2829 host_names
2830 List of ESXi host names. When the host, username, and password credentials
2831 are provided for a vCenter Server, the host_names argument is required to
2832 tell vCenter the hosts for which to retrieve Non-SSD disks.
2833
2834 If host_names is not provided, Non-SSD disks will be retrieved for the
2835 ``host`` location instead. This is useful for when service instance
2836 connection information is used for a single ESXi host.
2837
2838 CLI Example:
2839
2840 .. code-block:: bash
2841
2842 # Used for single ESXi host connection information
2843 salt '*' vsphere.list_non_ssds my.esxi.host root bad-password
2844
2845 # Used for connecting to a vCenter Server
2846 salt '*' vsphere.list_non_ssds my.vcenter.location root bad-password \
2847 host_names='[esxi-1.host.com, esxi-2.host.com]'
2848 """
2849 service_instance = salt.utils.vmware.get_service_instance(
2850 host=host, username=username, password=password, protocol=protocol, port=port
2851 )
2852 host_names = _check_hosts(service_instance, host, host_names)
2853 ret = {}
2854 names = []
2855 for host_name in host_names:
2856 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2857 disks = _get_host_non_ssds(host_ref)
2858 for disk in disks:
2859 names.append(disk.canonicalName)
2860 ret.update({host_name: names})
2861
2862 return ret
2863
2864
2865 @depends(HAS_PYVMOMI)
2866 @ignores_kwargs("credstore")
2867 def set_ntp_config(
2868 host, username, password, ntp_servers, protocol=None, port=None, host_names=None
2869 ):
2870 """
2871 Set NTP configuration for a given host of list of host_names.
2872
2873 host
2874 The location of the host.
2875
2876 username
2877 The username used to login to the host, such as ``root``.
2878
2879 password
2880 The password used to login to the host.
2881
2882 ntp_servers
2883 A list of servers that should be added to and configured for the specified
2884 host's NTP configuration.
2885
2886 protocol
2887 Optionally set to alternate protocol if the host is not using the default
2888 protocol. Default protocol is ``https``.
2889
2890 port
2891 Optionally set to alternate port if the host is not using the default
2892 port. Default port is ``443``.
2893
2894 host_names
2895 List of ESXi host names. When the host, username, and password credentials
2896 are provided for a vCenter Server, the host_names argument is required to tell
2897 vCenter which hosts to configure ntp servers.
2898
2899 If host_names is not provided, the NTP servers will be configured for the
2900 ``host`` location instead. This is useful for when service instance connection
2901 information is used for a single ESXi host.
2902
2903 CLI Example:
2904
2905 .. code-block:: bash
2906
2907 # Used for single ESXi host connection information
2908 salt '*' vsphere.ntp_configure my.esxi.host root bad-password '[192.174.1.100, 192.174.1.200]'
2909
2910 # Used for connecting to a vCenter Server
2911 salt '*' vsphere.ntp_configure my.vcenter.location root bad-password '[192.174.1.100, 192.174.1.200]' \
2912 host_names='[esxi-1.host.com, esxi-2.host.com]'
2913 """
2914 service_instance = salt.utils.vmware.get_service_instance(
2915 host=host, username=username, password=password, protocol=protocol, port=port
2916 )
2917 if not isinstance(ntp_servers, list):
2918 raise CommandExecutionError("'ntp_servers' must be a list.")
2919
2920 # Get NTP Config Object from ntp_servers
2921 ntp_config = vim.HostNtpConfig(server=ntp_servers)
2922
2923 # Get DateTimeConfig object from ntp_config
2924 date_config = vim.HostDateTimeConfig(ntpConfig=ntp_config)
2925
2926 host_names = _check_hosts(service_instance, host, host_names)
2927 ret = {}
2928 for host_name in host_names:
2929 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
2930 date_time_manager = _get_date_time_mgr(host_ref)
2931 log.debug(
2932 "Configuring NTP Servers '{}' for host '{}'.".format(ntp_servers, host_name)
2933 )
2934
2935 try:
2936 date_time_manager.UpdateDateTimeConfig(config=date_config)
2937 except vim.fault.HostConfigFault as err:
2938 msg = "vsphere.ntp_configure_servers failed: {}".format(err)
2939 log.debug(msg)
2940 ret.update({host_name: {"Error": msg}})
2941 continue
2942
2943 ret.update({host_name: {"NTP Servers": ntp_config}})
2944 return ret
2945
2946
2947 @depends(HAS_PYVMOMI)
2948 @ignores_kwargs("credstore")
2949 def service_start(
2950 host, username, password, service_name, protocol=None, port=None, host_names=None
2951 ):
2952 """
2953 Start the named service for the given host or list of hosts.
2954
2955 host
2956 The location of the host.
2957
2958 username
2959 The username used to login to the host, such as ``root``.
2960
2961 password
2962 The password used to login to the host.
2963
2964 service_name
2965 The name of the service for which to set the policy. Supported service names are:
2966 - DCUI
2967 - TSM
2968 - SSH
2969 - lbtd
2970 - lsassd
2971 - lwiod
2972 - netlogond
2973 - ntpd
2974 - sfcbd-watchdog
2975 - snmpd
2976 - vprobed
2977 - vpxa
2978 - xorg
2979
2980 protocol
2981 Optionally set to alternate protocol if the host is not using the default
2982 protocol. Default protocol is ``https``.
2983
2984 port
2985 Optionally set to alternate port if the host is not using the default
2986 port. Default port is ``443``.
2987
2988 host_names
2989 List of ESXi host names. When the host, username, and password credentials
2990 are provided for a vCenter Server, the host_names argument is required to tell
2991 vCenter the hosts for which to start the service.
2992
2993 If host_names is not provided, the service will be started for the ``host``
2994 location instead. This is useful for when service instance connection information
2995 is used for a single ESXi host.
2996
2997 CLI Example:
2998
2999 .. code-block:: bash
3000
3001 # Used for single ESXi host connection information
3002 salt '*' vsphere.service_start my.esxi.host root bad-password 'ntpd'
3003
3004 # Used for connecting to a vCenter Server
3005 salt '*' vsphere.service_start my.vcenter.location root bad-password 'ntpd' \
3006 host_names='[esxi-1.host.com, esxi-2.host.com]'
3007 """
3008 service_instance = salt.utils.vmware.get_service_instance(
3009 host=host, username=username, password=password, protocol=protocol, port=port
3010 )
3011 host_names = _check_hosts(service_instance, host, host_names)
3012 valid_services = [
3013 "DCUI",
3014 "TSM",
3015 "SSH",
3016 "ssh",
3017 "lbtd",
3018 "lsassd",
3019 "lwiod",
3020 "netlogond",
3021 "ntpd",
3022 "sfcbd-watchdog",
3023 "snmpd",
3024 "vprobed",
3025 "vpxa",
3026 "xorg",
3027 ]
3028 ret = {}
3029
3030 # Don't require users to know that VMware lists the ssh service as TSM-SSH
3031 if service_name == "SSH" or service_name == "ssh":
3032 temp_service_name = "TSM-SSH"
3033 else:
3034 temp_service_name = service_name
3035
3036 for host_name in host_names:
3037 # Check if the service_name provided is a valid one.
3038 # If we don't have a valid service, return. The service will be invalid for all hosts.
3039 if service_name not in valid_services:
3040 ret.update(
3041 {
3042 host_name: {
3043 "Error": "{} is not a valid service name.".format(service_name)
3044 }
3045 }
3046 )
3047 return ret
3048
3049 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3050 service_manager = _get_service_manager(host_ref)
3051 log.debug("Starting the '{}' service on {}.".format(service_name, host_name))
3052
3053 # Start the service
3054 try:
3055 service_manager.StartService(id=temp_service_name)
3056 except vim.fault.HostConfigFault as err:
3057 msg = "'vsphere.service_start' failed for host {}: {}".format(
3058 host_name, err
3059 )
3060 log.debug(msg)
3061 ret.update({host_name: {"Error": msg}})
3062 continue
3063 # Some services are restricted by the vSphere License Level.
3064 except vim.fault.RestrictedVersion as err:
3065 log.debug(err)
3066 ret.update({host_name: {"Error": err}})
3067 continue
3068
3069 ret.update({host_name: {"Service Started": True}})
3070
3071 return ret
3072
3073
3074 @depends(HAS_PYVMOMI)
3075 @ignores_kwargs("credstore")
3076 def service_stop(
3077 host, username, password, service_name, protocol=None, port=None, host_names=None
3078 ):
3079 """
3080 Stop the named service for the given host or list of hosts.
3081
3082 host
3083 The location of the host.
3084
3085 username
3086 The username used to login to the host, such as ``root``.
3087
3088 password
3089 The password used to login to the host.
3090
3091 service_name
3092 The name of the service for which to set the policy. Supported service names are:
3093 - DCUI
3094 - TSM
3095 - SSH
3096 - lbtd
3097 - lsassd
3098 - lwiod
3099 - netlogond
3100 - ntpd
3101 - sfcbd-watchdog
3102 - snmpd
3103 - vprobed
3104 - vpxa
3105 - xorg
3106
3107 protocol
3108 Optionally set to alternate protocol if the host is not using the default
3109 protocol. Default protocol is ``https``.
3110
3111 port
3112 Optionally set to alternate port if the host is not using the default
3113 port. Default port is ``443``.
3114
3115 host_names
3116 List of ESXi host names. When the host, username, and password credentials
3117 are provided for a vCenter Server, the host_names argument is required to tell
3118 vCenter the hosts for which to stop the service.
3119
3120 If host_names is not provided, the service will be stopped for the ``host``
3121 location instead. This is useful for when service instance connection information
3122 is used for a single ESXi host.
3123
3124 CLI Example:
3125
3126 .. code-block:: bash
3127
3128 # Used for single ESXi host connection information
3129 salt '*' vsphere.service_stop my.esxi.host root bad-password 'ssh'
3130
3131 # Used for connecting to a vCenter Server
3132 salt '*' vsphere.service_stop my.vcenter.location root bad-password 'ssh' \
3133 host_names='[esxi-1.host.com, esxi-2.host.com]'
3134 """
3135 service_instance = salt.utils.vmware.get_service_instance(
3136 host=host, username=username, password=password, protocol=protocol, port=port
3137 )
3138 host_names = _check_hosts(service_instance, host, host_names)
3139 valid_services = [
3140 "DCUI",
3141 "TSM",
3142 "SSH",
3143 "ssh",
3144 "lbtd",
3145 "lsassd",
3146 "lwiod",
3147 "netlogond",
3148 "ntpd",
3149 "sfcbd-watchdog",
3150 "snmpd",
3151 "vprobed",
3152 "vpxa",
3153 "xorg",
3154 ]
3155 ret = {}
3156
3157 # Don't require users to know that VMware lists the ssh service as TSM-SSH
3158 if service_name == "SSH" or service_name == "ssh":
3159 temp_service_name = "TSM-SSH"
3160 else:
3161 temp_service_name = service_name
3162
3163 for host_name in host_names:
3164 # Check if the service_name provided is a valid one.
3165 # If we don't have a valid service, return. The service will be invalid for all hosts.
3166 if service_name not in valid_services:
3167 ret.update(
3168 {
3169 host_name: {
3170 "Error": "{} is not a valid service name.".format(service_name)
3171 }
3172 }
3173 )
3174 return ret
3175
3176 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3177 service_manager = _get_service_manager(host_ref)
3178 log.debug("Stopping the '{}' service on {}.".format(service_name, host_name))
3179
3180 # Stop the service.
3181 try:
3182 service_manager.StopService(id=temp_service_name)
3183 except vim.fault.HostConfigFault as err:
3184 msg = "'vsphere.service_stop' failed for host {}: {}".format(host_name, err)
3185 log.debug(msg)
3186 ret.update({host_name: {"Error": msg}})
3187 continue
3188 # Some services are restricted by the vSphere License Level.
3189 except vim.fault.RestrictedVersion as err:
3190 log.debug(err)
3191 ret.update({host_name: {"Error": err}})
3192 continue
3193
3194 ret.update({host_name: {"Service Stopped": True}})
3195
3196 return ret
3197
3198
3199 @depends(HAS_PYVMOMI)
3200 @ignores_kwargs("credstore")
3201 def service_restart(
3202 host, username, password, service_name, protocol=None, port=None, host_names=None
3203 ):
3204 """
3205 Restart the named service for the given host or list of hosts.
3206
3207 host
3208 The location of the host.
3209
3210 username
3211 The username used to login to the host, such as ``root``.
3212
3213 password
3214 The password used to login to the host.
3215
3216 service_name
3217 The name of the service for which to set the policy. Supported service names are:
3218 - DCUI
3219 - TSM
3220 - SSH
3221 - lbtd
3222 - lsassd
3223 - lwiod
3224 - netlogond
3225 - ntpd
3226 - sfcbd-watchdog
3227 - snmpd
3228 - vprobed
3229 - vpxa
3230 - xorg
3231
3232 protocol
3233 Optionally set to alternate protocol if the host is not using the default
3234 protocol. Default protocol is ``https``.
3235
3236 port
3237 Optionally set to alternate port if the host is not using the default
3238 port. Default port is ``443``.
3239
3240 host_names
3241 List of ESXi host names. When the host, username, and password credentials
3242 are provided for a vCenter Server, the host_names argument is required to tell
3243 vCenter the hosts for which to restart the service.
3244
3245 If host_names is not provided, the service will be restarted for the ``host``
3246 location instead. This is useful for when service instance connection information
3247 is used for a single ESXi host.
3248
3249 CLI Example:
3250
3251 .. code-block:: bash
3252
3253 # Used for single ESXi host connection information
3254 salt '*' vsphere.service_restart my.esxi.host root bad-password 'ntpd'
3255
3256 # Used for connecting to a vCenter Server
3257 salt '*' vsphere.service_restart my.vcenter.location root bad-password 'ntpd' \
3258 host_names='[esxi-1.host.com, esxi-2.host.com]'
3259 """
3260 service_instance = salt.utils.vmware.get_service_instance(
3261 host=host, username=username, password=password, protocol=protocol, port=port
3262 )
3263 host_names = _check_hosts(service_instance, host, host_names)
3264 valid_services = [
3265 "DCUI",
3266 "TSM",
3267 "SSH",
3268 "ssh",
3269 "lbtd",
3270 "lsassd",
3271 "lwiod",
3272 "netlogond",
3273 "ntpd",
3274 "sfcbd-watchdog",
3275 "snmpd",
3276 "vprobed",
3277 "vpxa",
3278 "xorg",
3279 ]
3280 ret = {}
3281
3282 # Don't require users to know that VMware lists the ssh service as TSM-SSH
3283 if service_name == "SSH" or service_name == "ssh":
3284 temp_service_name = "TSM-SSH"
3285 else:
3286 temp_service_name = service_name
3287
3288 for host_name in host_names:
3289 # Check if the service_name provided is a valid one.
3290 # If we don't have a valid service, return. The service will be invalid for all hosts.
3291 if service_name not in valid_services:
3292 ret.update(
3293 {
3294 host_name: {
3295 "Error": "{} is not a valid service name.".format(service_name)
3296 }
3297 }
3298 )
3299 return ret
3300
3301 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3302 service_manager = _get_service_manager(host_ref)
3303 log.debug("Restarting the '{}' service on {}.".format(service_name, host_name))
3304
3305 # Restart the service.
3306 try:
3307 service_manager.RestartService(id=temp_service_name)
3308 except vim.fault.HostConfigFault as err:
3309 msg = "'vsphere.service_restart' failed for host {}: {}".format(
3310 host_name, err
3311 )
3312 log.debug(msg)
3313 ret.update({host_name: {"Error": msg}})
3314 continue
3315 # Some services are restricted by the vSphere License Level.
3316 except vim.fault.RestrictedVersion as err:
3317 log.debug(err)
3318 ret.update({host_name: {"Error": err}})
3319 continue
3320
3321 ret.update({host_name: {"Service Restarted": True}})
3322
3323 return ret
3324
3325
3326 @depends(HAS_PYVMOMI)
3327 @ignores_kwargs("credstore")
3328 def set_service_policy(
3329 host,
3330 username,
3331 password,
3332 service_name,
3333 service_policy,
3334 protocol=None,
3335 port=None,
3336 host_names=None,
3337 ):
3338 """
3339 Set the service name's policy for a given host or list of hosts.
3340
3341 host
3342 The location of the host.
3343
3344 username
3345 The username used to login to the host, such as ``root``.
3346
3347 password
3348 The password used to login to the host.
3349
3350 service_name
3351 The name of the service for which to set the policy. Supported service names are:
3352 - DCUI
3353 - TSM
3354 - SSH
3355 - lbtd
3356 - lsassd
3357 - lwiod
3358 - netlogond
3359 - ntpd
3360 - sfcbd-watchdog
3361 - snmpd
3362 - vprobed
3363 - vpxa
3364 - xorg
3365
3366 service_policy
3367 The policy to set for the service. For example, 'automatic'.
3368
3369 protocol
3370 Optionally set to alternate protocol if the host is not using the default
3371 protocol. Default protocol is ``https``.
3372
3373 port
3374 Optionally set to alternate port if the host is not using the default
3375 port. Default port is ``443``.
3376
3377 host_names
3378 List of ESXi host names. When the host, username, and password credentials
3379 are provided for a vCenter Server, the host_names argument is required to tell
3380 vCenter the hosts for which to set the service policy.
3381
3382 If host_names is not provided, the service policy information will be retrieved
3383 for the ``host`` location instead. This is useful for when service instance
3384 connection information is used for a single ESXi host.
3385
3386 CLI Example:
3387
3388 .. code-block:: bash
3389
3390 # Used for single ESXi host connection information
3391 salt '*' vsphere.set_service_policy my.esxi.host root bad-password 'ntpd' 'automatic'
3392
3393 # Used for connecting to a vCenter Server
3394 salt '*' vsphere.set_service_policy my.vcenter.location root bad-password 'ntpd' 'automatic' \
3395 host_names='[esxi-1.host.com, esxi-2.host.com]'
3396 """
3397 service_instance = salt.utils.vmware.get_service_instance(
3398 host=host, username=username, password=password, protocol=protocol, port=port
3399 )
3400 host_names = _check_hosts(service_instance, host, host_names)
3401 valid_services = [
3402 "DCUI",
3403 "TSM",
3404 "SSH",
3405 "ssh",
3406 "lbtd",
3407 "lsassd",
3408 "lwiod",
3409 "netlogond",
3410 "ntpd",
3411 "sfcbd-watchdog",
3412 "snmpd",
3413 "vprobed",
3414 "vpxa",
3415 "xorg",
3416 ]
3417 ret = {}
3418
3419 for host_name in host_names:
3420 # Check if the service_name provided is a valid one.
3421 # If we don't have a valid service, return. The service will be invalid for all hosts.
3422 if service_name not in valid_services:
3423 ret.update(
3424 {
3425 host_name: {
3426 "Error": "{} is not a valid service name.".format(service_name)
3427 }
3428 }
3429 )
3430 return ret
3431
3432 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3433 service_manager = _get_service_manager(host_ref)
3434 services = host_ref.configManager.serviceSystem.serviceInfo.service
3435
3436 # Services are stored in a general list - we need loop through the list and find
3437 # service key that matches our service name.
3438 for service in services:
3439 service_key = None
3440
3441 # Find the service key based on the given service_name
3442 if service.key == service_name:
3443 service_key = service.key
3444 elif service_name == "ssh" or service_name == "SSH":
3445 if service.key == "TSM-SSH":
3446 service_key = "TSM-SSH"
3447
3448 # If we have a service_key, we've found a match. Update the policy.
3449 if service_key:
3450 try:
3451 service_manager.UpdateServicePolicy(
3452 id=service_key, policy=service_policy
3453 )
3454 except vim.fault.NotFound:
3455 msg = "The service name '{}' was not found.".format(service_name)
3456 log.debug(msg)
3457 ret.update({host_name: {"Error": msg}})
3458 continue
3459 # Some services are restricted by the vSphere License Level.
3460 except vim.fault.HostConfigFault as err:
3461 msg = "'vsphere.set_service_policy' failed for host {}: {}".format(
3462 host_name, err
3463 )
3464 log.debug(msg)
3465 ret.update({host_name: {"Error": msg}})
3466 continue
3467
3468 ret.update({host_name: True})
3469
3470 # If we made it this far, something else has gone wrong.
3471 if ret.get(host_name) is None:
3472 msg = "Could not find service '{}' for host '{}'.".format(
3473 service_name, host_name
3474 )
3475 log.debug(msg)
3476 ret.update({host_name: {"Error": msg}})
3477
3478 return ret
3479
3480
3481 @depends(HAS_PYVMOMI)
3482 @ignores_kwargs("credstore")
3483 def update_host_datetime(
3484 host, username, password, protocol=None, port=None, host_names=None
3485 ):
3486 """
3487 Update the date/time on the given host or list of host_names. This function should be
3488 used with caution since network delays and execution delays can result in time skews.
3489
3490 host
3491 The location of the host.
3492
3493 username
3494 The username used to login to the host, such as ``root``.
3495
3496 password
3497 The password used to login to the host.
3498
3499 protocol
3500 Optionally set to alternate protocol if the host is not using the default
3501 protocol. Default protocol is ``https``.
3502
3503 port
3504 Optionally set to alternate port if the host is not using the default
3505 port. Default port is ``443``.
3506
3507 host_names
3508 List of ESXi host names. When the host, username, and password credentials
3509 are provided for a vCenter Server, the host_names argument is required to
3510 tell vCenter which hosts should update their date/time.
3511
3512 If host_names is not provided, the date/time will be updated for the ``host``
3513 location instead. This is useful for when service instance connection
3514 information is used for a single ESXi host.
3515
3516 CLI Example:
3517
3518 .. code-block:: bash
3519
3520 # Used for single ESXi host connection information
3521 salt '*' vsphere.update_date_time my.esxi.host root bad-password
3522
3523 # Used for connecting to a vCenter Server
3524 salt '*' vsphere.update_date_time my.vcenter.location root bad-password \
3525 host_names='[esxi-1.host.com, esxi-2.host.com]'
3526 """
3527 service_instance = salt.utils.vmware.get_service_instance(
3528 host=host, username=username, password=password, protocol=protocol, port=port
3529 )
3530 host_names = _check_hosts(service_instance, host, host_names)
3531 ret = {}
3532 for host_name in host_names:
3533 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3534 date_time_manager = _get_date_time_mgr(host_ref)
3535 try:
3536 date_time_manager.UpdateDateTime(datetime.datetime.utcnow())
3537 except vim.fault.HostConfigFault as err:
3538 msg = "'vsphere.update_date_time' failed for host {}: {}".format(
3539 host_name, err
3540 )
3541 log.debug(msg)
3542 ret.update({host_name: {"Error": msg}})
3543 continue
3544
3545 ret.update({host_name: {"Datetime Updated": True}})
3546
3547 return ret
3548
3549
3550 @depends(HAS_PYVMOMI)
3551 @ignores_kwargs("credstore")
3552 def update_host_password(
3553 host, username, password, new_password, protocol=None, port=None
3554 ):
3555 """
3556 Update the password for a given host.
3557
3558 .. note:: Currently only works with connections to ESXi hosts. Does not work with vCenter servers.
3559
3560 host
3561 The location of the ESXi host.
3562
3563 username
3564 The username used to login to the ESXi host, such as ``root``.
3565
3566 password
3567 The password used to login to the ESXi host.
3568
3569 new_password
3570 The new password that will be updated for the provided username on the ESXi host.
3571
3572 protocol
3573 Optionally set to alternate protocol if the host is not using the default
3574 protocol. Default protocol is ``https``.
3575
3576 port
3577 Optionally set to alternate port if the host is not using the default
3578 port. Default port is ``443``.
3579
3580 CLI Example:
3581
3582 .. code-block:: bash
3583
3584 salt '*' vsphere.update_host_password my.esxi.host root original-bad-password new-bad-password
3585
3586 """
3587 service_instance = salt.utils.vmware.get_service_instance(
3588 host=host, username=username, password=password, protocol=protocol, port=port
3589 )
3590 # Get LocalAccountManager object
3591 account_manager = salt.utils.vmware.get_inventory(service_instance).accountManager
3592
3593 # Create user account specification object and assign id and password attributes
3594 user_account = vim.host.LocalAccountManager.AccountSpecification()
3595 user_account.id = username
3596 user_account.password = new_password
3597
3598 # Update the password
3599 try:
3600 account_manager.UpdateUser(user_account)
3601 except vmodl.fault.SystemError as err:
3602 raise CommandExecutionError(err.msg)
3603 except vim.fault.UserNotFound:
3604 raise CommandExecutionError(
3605 "'vsphere.update_host_password' failed for host {}: "
3606 "User was not found.".format(host)
3607 )
3608 # If the username and password already exist, we don't need to do anything.
3609 except vim.fault.AlreadyExists:
3610 pass
3611
3612 return True
3613
3614
3615 @depends(HAS_PYVMOMI)
3616 @ignores_kwargs("credstore")
3617 def vmotion_disable(
3618 host, username, password, protocol=None, port=None, host_names=None
3619 ):
3620 """
3621 Disable vMotion for a given host or list of host_names.
3622
3623 host
3624 The location of the host.
3625
3626 username
3627 The username used to login to the host, such as ``root``.
3628
3629 password
3630 The password used to login to the host.
3631
3632 protocol
3633 Optionally set to alternate protocol if the host is not using the default
3634 protocol. Default protocol is ``https``.
3635
3636 port
3637 Optionally set to alternate port if the host is not using the default
3638 port. Default port is ``443``.
3639
3640 host_names
3641 List of ESXi host names. When the host, username, and password credentials
3642 are provided for a vCenter Server, the host_names argument is required to
3643 tell vCenter which hosts should disable VMotion.
3644
3645 If host_names is not provided, VMotion will be disabled for the ``host``
3646 location instead. This is useful for when service instance connection
3647 information is used for a single ESXi host.
3648
3649 CLI Example:
3650
3651 .. code-block:: bash
3652
3653 # Used for single ESXi host connection information
3654 salt '*' vsphere.vmotion_disable my.esxi.host root bad-password
3655
3656 # Used for connecting to a vCenter Server
3657 salt '*' vsphere.vmotion_disable my.vcenter.location root bad-password \
3658 host_names='[esxi-1.host.com, esxi-2.host.com]'
3659 """
3660 service_instance = salt.utils.vmware.get_service_instance(
3661 host=host, username=username, password=password, protocol=protocol, port=port
3662 )
3663 host_names = _check_hosts(service_instance, host, host_names)
3664 ret = {}
3665 for host_name in host_names:
3666 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3667 vmotion_system = host_ref.configManager.vmotionSystem
3668
3669 # Disable VMotion for the host by removing the VNic selected to use for VMotion.
3670 try:
3671 vmotion_system.DeselectVnic()
3672 except vim.fault.HostConfigFault as err:
3673 msg = "vsphere.vmotion_disable failed: {}".format(err)
3674 log.debug(msg)
3675 ret.update({host_name: {"Error": msg, "VMotion Disabled": False}})
3676 continue
3677
3678 ret.update({host_name: {"VMotion Disabled": True}})
3679
3680 return ret
3681
3682
3683 @depends(HAS_PYVMOMI)
3684 @ignores_kwargs("credstore")
3685 def vmotion_enable(
3686 host, username, password, protocol=None, port=None, host_names=None, device="vmk0"
3687 ):
3688 """
3689 Enable vMotion for a given host or list of host_names.
3690
3691 host
3692 The location of the host.
3693
3694 username
3695 The username used to login to the host, such as ``root``.
3696
3697 password
3698 The password used to login to the host.
3699
3700 protocol
3701 Optionally set to alternate protocol if the host is not using the default
3702 protocol. Default protocol is ``https``.
3703
3704 port
3705 Optionally set to alternate port if the host is not using the default
3706 port. Default port is ``443``.
3707
3708 host_names
3709 List of ESXi host names. When the host, username, and password credentials
3710 are provided for a vCenter Server, the host_names argument is required to
3711 tell vCenter which hosts should enable VMotion.
3712
3713 If host_names is not provided, VMotion will be enabled for the ``host``
3714 location instead. This is useful for when service instance connection
3715 information is used for a single ESXi host.
3716
3717 device
3718 The device that uniquely identifies the VirtualNic that will be used for
3719 VMotion for each host. Defaults to ``vmk0``.
3720
3721 CLI Example:
3722
3723 .. code-block:: bash
3724
3725 # Used for single ESXi host connection information
3726 salt '*' vsphere.vmotion_enable my.esxi.host root bad-password
3727
3728 # Used for connecting to a vCenter Server
3729 salt '*' vsphere.vmotion_enable my.vcenter.location root bad-password \
3730 host_names='[esxi-1.host.com, esxi-2.host.com]'
3731 """
3732 service_instance = salt.utils.vmware.get_service_instance(
3733 host=host, username=username, password=password, protocol=protocol, port=port
3734 )
3735 host_names = _check_hosts(service_instance, host, host_names)
3736 ret = {}
3737 for host_name in host_names:
3738 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3739 vmotion_system = host_ref.configManager.vmotionSystem
3740
3741 # Enable VMotion for the host by setting the given device to provide the VNic to use for VMotion.
3742 try:
3743 vmotion_system.SelectVnic(device)
3744 except vim.fault.HostConfigFault as err:
3745 msg = "vsphere.vmotion_disable failed: {}".format(err)
3746 log.debug(msg)
3747 ret.update({host_name: {"Error": msg, "VMotion Enabled": False}})
3748 continue
3749
3750 ret.update({host_name: {"VMotion Enabled": True}})
3751
3752 return ret
3753
3754
3755 @depends(HAS_PYVMOMI)
3756 @ignores_kwargs("credstore")
3757 def vsan_add_disks(host, username, password, protocol=None, port=None, host_names=None):
3758 """
3759 Add any VSAN-eligible disks to the VSAN System for the given host or list of host_names.
3760
3761 host
3762 The location of the host.
3763
3764 username
3765 The username used to login to the host, such as ``root``.
3766
3767 password
3768 The password used to login to the host.
3769
3770 protocol
3771 Optionally set to alternate protocol if the host is not using the default
3772 protocol. Default protocol is ``https``.
3773
3774 port
3775 Optionally set to alternate port if the host is not using the default
3776 port. Default port is ``443``.
3777
3778 host_names
3779 List of ESXi host names. When the host, username, and password credentials
3780 are provided for a vCenter Server, the host_names argument is required to
3781 tell vCenter which hosts need to add any VSAN-eligible disks to the host's
3782 VSAN system.
3783
3784 If host_names is not provided, VSAN-eligible disks will be added to the hosts's
3785 VSAN system for the ``host`` location instead. This is useful for when service
3786 instance connection information is used for a single ESXi host.
3787
3788 CLI Example:
3789
3790 .. code-block:: bash
3791
3792 # Used for single ESXi host connection information
3793 salt '*' vsphere.vsan_add_disks my.esxi.host root bad-password
3794
3795 # Used for connecting to a vCenter Server
3796 salt '*' vsphere.vsan_add_disks my.vcenter.location root bad-password \
3797 host_names='[esxi-1.host.com, esxi-2.host.com]'
3798 """
3799 service_instance = salt.utils.vmware.get_service_instance(
3800 host=host, username=username, password=password, protocol=protocol, port=port
3801 )
3802 host_names = _check_hosts(service_instance, host, host_names)
3803 response = _get_vsan_eligible_disks(service_instance, host, host_names)
3804
3805 ret = {}
3806 for host_name, value in response.items():
3807 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3808 vsan_system = host_ref.configManager.vsanSystem
3809
3810 # We must have a VSAN Config in place before we can manipulate it.
3811 if vsan_system is None:
3812 msg = (
3813 "VSAN System Config Manager is unset for host '{}'. "
3814 "VSAN configuration cannot be changed without a configured "
3815 "VSAN System.".format(host_name)
3816 )
3817 log.debug(msg)
3818 ret.update({host_name: {"Error": msg}})
3819 else:
3820 eligible = value.get("Eligible")
3821 error = value.get("Error")
3822
3823 if eligible and isinstance(eligible, list):
3824 # If we have eligible, matching disks, add them to VSAN.
3825 try:
3826 task = vsan_system.AddDisks(eligible)
3827 salt.utils.vmware.wait_for_task(
3828 task, host_name, "Adding disks to VSAN", sleep_seconds=3
3829 )
3830 except vim.fault.InsufficientDisks as err:
3831 log.debug(err.msg)
3832 ret.update({host_name: {"Error": err.msg}})
3833 continue
3834 except Exception as err: # pylint: disable=broad-except
3835 msg = "'vsphere.vsan_add_disks' failed for host {}: {}".format(
3836 host_name, err
3837 )
3838 log.debug(msg)
3839 ret.update({host_name: {"Error": msg}})
3840 continue
3841
3842 log.debug(
3843 "Successfully added disks to the VSAN system for host '{}'.".format(
3844 host_name
3845 )
3846 )
3847 # We need to return ONLY the disk names, otherwise Message Pack can't deserialize the disk objects.
3848 disk_names = []
3849 for disk in eligible:
3850 disk_names.append(disk.canonicalName)
3851 ret.update({host_name: {"Disks Added": disk_names}})
3852 elif eligible and isinstance(eligible, str):
3853 # If we have a string type in the eligible value, we don't
3854 # have any VSAN-eligible disks. Pull the message through.
3855 ret.update({host_name: {"Disks Added": eligible}})
3856 elif error:
3857 # If we hit an error, populate the Error return dict for state functions.
3858 ret.update({host_name: {"Error": error}})
3859 else:
3860 # If we made it this far, we somehow have eligible disks, but they didn't
3861 # match the disk list and just got an empty list of matching disks.
3862 ret.update(
3863 {
3864 host_name: {
3865 "Disks Added": "No new VSAN-eligible disks were found to add."
3866 }
3867 }
3868 )
3869
3870 return ret
3871
3872
3873 @depends(HAS_PYVMOMI)
3874 @ignores_kwargs("credstore")
3875 def vsan_disable(host, username, password, protocol=None, port=None, host_names=None):
3876 """
3877 Disable VSAN for a given host or list of host_names.
3878
3879 host
3880 The location of the host.
3881
3882 username
3883 The username used to login to the host, such as ``root``.
3884
3885 password
3886 The password used to login to the host.
3887
3888 protocol
3889 Optionally set to alternate protocol if the host is not using the default
3890 protocol. Default protocol is ``https``.
3891
3892 port
3893 Optionally set to alternate port if the host is not using the default
3894 port. Default port is ``443``.
3895
3896 host_names
3897 List of ESXi host names. When the host, username, and password credentials
3898 are provided for a vCenter Server, the host_names argument is required to
3899 tell vCenter which hosts should disable VSAN.
3900
3901 If host_names is not provided, VSAN will be disabled for the ``host``
3902 location instead. This is useful for when service instance connection
3903 information is used for a single ESXi host.
3904
3905 CLI Example:
3906
3907 .. code-block:: bash
3908
3909 # Used for single ESXi host connection information
3910 salt '*' vsphere.vsan_disable my.esxi.host root bad-password
3911
3912 # Used for connecting to a vCenter Server
3913 salt '*' vsphere.vsan_disable my.vcenter.location root bad-password \
3914 host_names='[esxi-1.host.com, esxi-2.host.com]'
3915 """
3916 service_instance = salt.utils.vmware.get_service_instance(
3917 host=host, username=username, password=password, protocol=protocol, port=port
3918 )
3919 # Create a VSAN Configuration Object and set the enabled attribute to True
3920 vsan_config = vim.vsan.host.ConfigInfo()
3921 vsan_config.enabled = False
3922
3923 host_names = _check_hosts(service_instance, host, host_names)
3924 ret = {}
3925 for host_name in host_names:
3926 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
3927 vsan_system = host_ref.configManager.vsanSystem
3928
3929 # We must have a VSAN Config in place before we can manipulate it.
3930 if vsan_system is None:
3931 msg = (
3932 "VSAN System Config Manager is unset for host '{}'. "
3933 "VSAN configuration cannot be changed without a configured "
3934 "VSAN System.".format(host_name)
3935 )
3936 log.debug(msg)
3937 ret.update({host_name: {"Error": msg}})
3938 else:
3939 try:
3940 # Disable vsan on the host
3941 task = vsan_system.UpdateVsan_Task(vsan_config)
3942 salt.utils.vmware.wait_for_task(
3943 task, host_name, "Disabling VSAN", sleep_seconds=3
3944 )
3945 except vmodl.fault.SystemError as err:
3946 log.debug(err.msg)
3947 ret.update({host_name: {"Error": err.msg}})
3948 continue
3949 except Exception as err: # pylint: disable=broad-except
3950 msg = "'vsphere.vsan_disable' failed for host {}: {}".format(
3951 host_name, err
3952 )
3953 log.debug(msg)
3954 ret.update({host_name: {"Error": msg}})
3955 continue
3956
3957 ret.update({host_name: {"VSAN Disabled": True}})
3958
3959 return ret
3960
3961
3962 @depends(HAS_PYVMOMI)
3963 @ignores_kwargs("credstore")
3964 def vsan_enable(host, username, password, protocol=None, port=None, host_names=None):
3965 """
3966 Enable VSAN for a given host or list of host_names.
3967
3968 host
3969 The location of the host.
3970
3971 username
3972 The username used to login to the host, such as ``root``.
3973
3974 password
3975 The password used to login to the host.
3976
3977 protocol
3978 Optionally set to alternate protocol if the host is not using the default
3979 protocol. Default protocol is ``https``.
3980
3981 port
3982 Optionally set to alternate port if the host is not using the default
3983 port. Default port is ``443``.
3984
3985 host_names
3986 List of ESXi host names. When the host, username, and password credentials
3987 are provided for a vCenter Server, the host_names argument is required to
3988 tell vCenter which hosts should enable VSAN.
3989
3990 If host_names is not provided, VSAN will be enabled for the ``host``
3991 location instead. This is useful for when service instance connection
3992 information is used for a single ESXi host.
3993
3994 CLI Example:
3995
3996 .. code-block:: bash
3997
3998 # Used for single ESXi host connection information
3999 salt '*' vsphere.vsan_enable my.esxi.host root bad-password
4000
4001 # Used for connecting to a vCenter Server
4002 salt '*' vsphere.vsan_enable my.vcenter.location root bad-password \
4003 host_names='[esxi-1.host.com, esxi-2.host.com]'
4004 """
4005 service_instance = salt.utils.vmware.get_service_instance(
4006 host=host, username=username, password=password, protocol=protocol, port=port
4007 )
4008 # Create a VSAN Configuration Object and set the enabled attribute to True
4009 vsan_config = vim.vsan.host.ConfigInfo()
4010 vsan_config.enabled = True
4011
4012 host_names = _check_hosts(service_instance, host, host_names)
4013 ret = {}
4014 for host_name in host_names:
4015 host_ref = _get_host_ref(service_instance, host, host_name=host_name)
4016 vsan_system = host_ref.configManager.vsanSystem
4017
4018 # We must have a VSAN Config in place before we can manipulate it.
4019 if vsan_system is None:
4020 msg = (
4021 "VSAN System Config Manager is unset for host '{}'. "
4022 "VSAN configuration cannot be changed without a configured "
4023 "VSAN System.".format(host_name)
4024 )
4025 log.debug(msg)
4026 ret.update({host_name: {"Error": msg}})
4027 else:
4028 try:
4029 # Enable vsan on the host
4030 task = vsan_system.UpdateVsan_Task(vsan_config)
4031 salt.utils.vmware.wait_for_task(
4032 task, host_name, "Enabling VSAN", sleep_seconds=3
4033 )
4034 except vmodl.fault.SystemError as err:
4035 log.debug(err.msg)
4036 ret.update({host_name: {"Error": err.msg}})
4037 continue
4038 except vim.fault.VsanFault as err:
4039 msg = "'vsphere.vsan_enable' failed for host {}: {}".format(
4040 host_name, err
4041 )
4042 log.debug(msg)
4043 ret.update({host_name: {"Error": msg}})
4044 continue
4045
4046 ret.update({host_name: {"VSAN Enabled": True}})
4047
4048 return ret
4049
4050
4051 def _get_dvs_config_dict(dvs_name, dvs_config):
4052 """
4053 Returns the dict representation of the DVS config
4054
4055 dvs_name
4056 The name of the DVS
4057
4058 dvs_config
4059 The DVS config
4060 """
4061 log.trace("Building the dict of the DVS '{}' config".format(dvs_name))
4062 conf_dict = {
4063 "name": dvs_name,
4064 "contact_email": dvs_config.contact.contact,
4065 "contact_name": dvs_config.contact.name,
4066 "description": dvs_config.description,
4067 "lacp_api_version": dvs_config.lacpApiVersion,
4068 "network_resource_control_version": dvs_config.networkResourceControlVersion,
4069 "network_resource_management_enabled": dvs_config.networkResourceManagementEnabled,
4070 "max_mtu": dvs_config.maxMtu,
4071 }
4072 if isinstance(dvs_config.uplinkPortPolicy, vim.DVSNameArrayUplinkPortPolicy):
4073 conf_dict.update({"uplink_names": dvs_config.uplinkPortPolicy.uplinkPortName})
4074 return conf_dict
4075
4076
4077 def _get_dvs_link_discovery_protocol(dvs_name, dvs_link_disc_protocol):
4078 """
4079 Returns the dict representation of the DVS link discovery protocol
4080
4081 dvs_name
4082 The name of the DVS
4083
4084 dvs_link_disc_protocl
4085 The DVS link discovery protocol
4086 """
4087 log.trace(
4088 "Building the dict of the DVS '{}' link discovery " "protocol".format(dvs_name)
4089 )
4090 return {
4091 "operation": dvs_link_disc_protocol.operation,
4092 "protocol": dvs_link_disc_protocol.protocol,
4093 }
4094
4095
4096 def _get_dvs_product_info(dvs_name, dvs_product_info):
4097 """
4098 Returns the dict representation of the DVS product_info
4099
4100 dvs_name
4101 The name of the DVS
4102
4103 dvs_product_info
4104 The DVS product info
4105 """
4106 log.trace("Building the dict of the DVS '{}' product " "info".format(dvs_name))
4107 return {
4108 "name": dvs_product_info.name,
4109 "vendor": dvs_product_info.vendor,
4110 "version": dvs_product_info.version,
4111 }
4112
4113
4114 def _get_dvs_capability(dvs_name, dvs_capability):
4115 """
4116 Returns the dict representation of the DVS product_info
4117
4118 dvs_name
4119 The name of the DVS
4120
4121 dvs_capability
4122 The DVS capability
4123 """
4124 log.trace("Building the dict of the DVS '{}' capability" "".format(dvs_name))
4125 return {
4126 "operation_supported": dvs_capability.dvsOperationSupported,
4127 "portgroup_operation_supported": dvs_capability.dvPortGroupOperationSupported,
4128 "port_operation_supported": dvs_capability.dvPortOperationSupported,
4129 }
4130
4131
4132 def _get_dvs_infrastructure_traffic_resources(dvs_name, dvs_infra_traffic_ress):
4133 """
4134 Returns a list of dict representations of the DVS infrastructure traffic
4135 resource
4136
4137 dvs_name
4138 The name of the DVS
4139
4140 dvs_infra_traffic_ress
4141 The DVS infrastructure traffic resources
4142 """
4143 log.trace(
4144 "Building the dicts of the DVS '{}' infrastructure traffic "
4145 "resources".format(dvs_name)
4146 )
4147 res_dicts = []
4148 for res in dvs_infra_traffic_ress:
4149 res_dict = {
4150 "key": res.key,
4151 "limit": res.allocationInfo.limit,
4152 "reservation": res.allocationInfo.reservation,
4153 }
4154 if res.allocationInfo.shares:
4155 res_dict.update(
4156 {
4157 "num_shares": res.allocationInfo.shares.shares,
4158 "share_level": res.allocationInfo.shares.level,
4159 }
4160 )
4161 res_dicts.append(res_dict)
4162 return res_dicts
4163
4164
4165 @depends(HAS_PYVMOMI)
4166 @supports_proxies("esxdatacenter", "esxcluster")
4167 @gets_service_instance_via_proxy
4168 def list_dvss(datacenter=None, dvs_names=None, service_instance=None):
4169 """
4170 Returns a list of distributed virtual switches (DVSs).
4171 The list can be filtered by the datacenter or DVS names.
4172
4173 datacenter
4174 The datacenter to look for DVSs in.
4175 Default value is None.
4176
4177 dvs_names
4178 List of DVS names to look for. If None, all DVSs are returned.
4179 Default value is None.
4180
4181 .. code-block:: bash
4182
4183 salt '*' vsphere.list_dvss
4184
4185 salt '*' vsphere.list_dvss dvs_names=[dvs1,dvs2]
4186 """
4187 ret_list = []
4188 proxy_type = get_proxy_type()
4189 if proxy_type == "esxdatacenter":
4190 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4191 dc_ref = _get_proxy_target(service_instance)
4192 elif proxy_type == "esxcluster":
4193 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4194 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4195
4196 for dvs in salt.utils.vmware.get_dvss(dc_ref, dvs_names, (not dvs_names)):
4197 dvs_dict = {}
4198 # XXX: Because of how VMware did DVS object inheritance we can\'t
4199 # be more restrictive when retrieving the dvs config, we have to
4200 # retrieve the entire object
4201 props = salt.utils.vmware.get_properties_of_managed_object(
4202 dvs, ["name", "config", "capability", "networkResourcePool"]
4203 )
4204 dvs_dict = _get_dvs_config_dict(props["name"], props["config"])
4205 # Product info
4206 dvs_dict.update(
4207 {
4208 "product_info": _get_dvs_product_info(
4209 props["name"], props["config"].productInfo
4210 )
4211 }
4212 )
4213 # Link Discovery Protocol
4214 if props["config"].linkDiscoveryProtocolConfig:
4215 dvs_dict.update(
4216 {
4217 "link_discovery_protocol": _get_dvs_link_discovery_protocol(
4218 props["name"], props["config"].linkDiscoveryProtocolConfig
4219 )
4220 }
4221 )
4222 # Capability
4223 dvs_dict.update(
4224 {"capability": _get_dvs_capability(props["name"], props["capability"])}
4225 )
4226 # InfrastructureTrafficResourceConfig - available with vSphere 6.0
4227 if hasattr(props["config"], "infrastructureTrafficResourceConfig"):
4228 dvs_dict.update(
4229 {
4230 "infrastructure_traffic_resource_pools": _get_dvs_infrastructure_traffic_resources(
4231 props["name"],
4232 props["config"].infrastructureTrafficResourceConfig,
4233 )
4234 }
4235 )
4236 ret_list.append(dvs_dict)
4237 return ret_list
4238
4239
4240 def _apply_dvs_config(config_spec, config_dict):
4241 """
4242 Applies the values of the config dict dictionary to a config spec
4243 (vim.VMwareDVSConfigSpec)
4244 """
4245 if config_dict.get("name"):
4246 config_spec.name = config_dict["name"]
4247 if config_dict.get("contact_email") or config_dict.get("contact_name"):
4248 if not config_spec.contact:
4249 config_spec.contact = vim.DVSContactInfo()
4250 config_spec.contact.contact = config_dict.get("contact_email")
4251 config_spec.contact.name = config_dict.get("contact_name")
4252 if config_dict.get("description"):
4253 config_spec.description = config_dict.get("description")
4254 if config_dict.get("max_mtu"):
4255 config_spec.maxMtu = config_dict.get("max_mtu")
4256 if config_dict.get("lacp_api_version"):
4257 config_spec.lacpApiVersion = config_dict.get("lacp_api_version")
4258 if config_dict.get("network_resource_control_version"):
4259 config_spec.networkResourceControlVersion = config_dict.get(
4260 "network_resource_control_version"
4261 )
4262 if config_dict.get("uplink_names"):
4263 if not config_spec.uplinkPortPolicy or not isinstance(
4264 config_spec.uplinkPortPolicy, vim.DVSNameArrayUplinkPortPolicy
4265 ):
4266
4267 config_spec.uplinkPortPolicy = vim.DVSNameArrayUplinkPortPolicy()
4268 config_spec.uplinkPortPolicy.uplinkPortName = config_dict["uplink_names"]
4269
4270
4271 def _apply_dvs_link_discovery_protocol(disc_prot_config, disc_prot_dict):
4272 """
4273 Applies the values of the disc_prot_dict dictionary to a link discovery
4274 protocol config object (vim.LinkDiscoveryProtocolConfig)
4275 """
4276 disc_prot_config.operation = disc_prot_dict["operation"]
4277 disc_prot_config.protocol = disc_prot_dict["protocol"]
4278
4279
4280 def _apply_dvs_product_info(product_info_spec, product_info_dict):
4281 """
4282 Applies the values of the product_info_dict dictionary to a product info
4283 spec (vim.DistributedVirtualSwitchProductSpec)
4284 """
4285 if product_info_dict.get("name"):
4286 product_info_spec.name = product_info_dict["name"]
4287 if product_info_dict.get("vendor"):
4288 product_info_spec.vendor = product_info_dict["vendor"]
4289 if product_info_dict.get("version"):
4290 product_info_spec.version = product_info_dict["version"]
4291
4292
4293 def _apply_dvs_capability(capability_spec, capability_dict):
4294 """
4295 Applies the values of the capability_dict dictionary to a DVS capability
4296 object (vim.vim.DVSCapability)
4297 """
4298 if "operation_supported" in capability_dict:
4299 capability_spec.dvsOperationSupported = capability_dict["operation_supported"]
4300 if "port_operation_supported" in capability_dict:
4301 capability_spec.dvPortOperationSupported = capability_dict[
4302 "port_operation_supported"
4303 ]
4304 if "portgroup_operation_supported" in capability_dict:
4305 capability_spec.dvPortGroupOperationSupported = capability_dict[
4306 "portgroup_operation_supported"
4307 ]
4308
4309
4310 def _apply_dvs_infrastructure_traffic_resources(
4311 infra_traffic_resources, resource_dicts
4312 ):
4313 """
4314 Applies the values of the resource dictionaries to infra traffic resources,
4315 creating the infra traffic resource if required
4316 (vim.DistributedVirtualSwitchProductSpec)
4317 """
4318 for res_dict in resource_dicts:
4319 filtered_traffic_resources = [
4320 r for r in infra_traffic_resources if r.key == res_dict["key"]
4321 ]
4322 if filtered_traffic_resources:
4323 traffic_res = filtered_traffic_resources[0]
4324 else:
4325 traffic_res = vim.DvsHostInfrastructureTrafficResource()
4326 traffic_res.key = res_dict["key"]
4327 traffic_res.allocationInfo = (
4328 vim.DvsHostInfrastructureTrafficResourceAllocation()
4329 )
4330 infra_traffic_resources.append(traffic_res)
4331 if res_dict.get("limit"):
4332 traffic_res.allocationInfo.limit = res_dict["limit"]
4333 if res_dict.get("reservation"):
4334 traffic_res.allocationInfo.reservation = res_dict["reservation"]
4335 if res_dict.get("num_shares") or res_dict.get("share_level"):
4336 if not traffic_res.allocationInfo.shares:
4337 traffic_res.allocationInfo.shares = vim.SharesInfo()
4338 if res_dict.get("share_level"):
4339 traffic_res.allocationInfo.shares.level = vim.SharesLevel(
4340 res_dict["share_level"]
4341 )
4342 if res_dict.get("num_shares"):
4343 # XXX Even though we always set the number of shares if provided,
4344 # the vCenter will ignore it unless the share level is 'custom'.
4345 traffic_res.allocationInfo.shares.shares = res_dict["num_shares"]
4346
4347
4348 def _apply_dvs_network_resource_pools(network_resource_pools, resource_dicts):
4349 """
4350 Applies the values of the resource dictionaries to network resource pools,
4351 creating the resource pools if required
4352 (vim.DVSNetworkResourcePoolConfigSpec)
4353 """
4354 for res_dict in resource_dicts:
4355 ress = [r for r in network_resource_pools if r.key == res_dict["key"]]
4356 if ress:
4357 res = ress[0]
4358 else:
4359 res = vim.DVSNetworkResourcePoolConfigSpec()
4360 res.key = res_dict["key"]
4361 res.allocationInfo = vim.DVSNetworkResourcePoolAllocationInfo()
4362 network_resource_pools.append(res)
4363 if res_dict.get("limit"):
4364 res.allocationInfo.limit = res_dict["limit"]
4365 if res_dict.get("num_shares") and res_dict.get("share_level"):
4366 if not res.allocationInfo.shares:
4367 res.allocationInfo.shares = vim.SharesInfo()
4368 res.allocationInfo.shares.shares = res_dict["num_shares"]
4369 res.allocationInfo.shares.level = vim.SharesLevel(res_dict["share_level"])
4370
4371
4372 @depends(HAS_PYVMOMI)
4373 @supports_proxies("esxdatacenter", "esxcluster")
4374 @gets_service_instance_via_proxy
4375 def create_dvs(dvs_dict, dvs_name, service_instance=None):
4376 """
4377 Creates a distributed virtual switch (DVS).
4378
4379 Note: The ``dvs_name`` param will override any name set in ``dvs_dict``.
4380
4381 dvs_dict
4382 Dict representation of the new DVS (example in salt.states.dvs)
4383
4384 dvs_name
4385 Name of the DVS to be created.
4386
4387 service_instance
4388 Service instance (vim.ServiceInstance) of the vCenter.
4389 Default is None.
4390
4391 .. code-block:: bash
4392
4393 salt '*' vsphere.create_dvs dvs dict=$dvs_dict dvs_name=dvs_name
4394 """
4395 log.trace("Creating dvs '{}' with dict = {}".format(dvs_name, dvs_dict))
4396 proxy_type = get_proxy_type()
4397 if proxy_type == "esxdatacenter":
4398 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4399 dc_ref = _get_proxy_target(service_instance)
4400 elif proxy_type == "esxcluster":
4401 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4402 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4403 # Make the name of the DVS consistent with the call
4404 dvs_dict["name"] = dvs_name
4405 # Build the config spec from the input
4406 dvs_create_spec = vim.DVSCreateSpec()
4407 dvs_create_spec.configSpec = vim.VMwareDVSConfigSpec()
4408 _apply_dvs_config(dvs_create_spec.configSpec, dvs_dict)
4409 if dvs_dict.get("product_info"):
4410 dvs_create_spec.productInfo = vim.DistributedVirtualSwitchProductSpec()
4411 _apply_dvs_product_info(dvs_create_spec.productInfo, dvs_dict["product_info"])
4412 if dvs_dict.get("capability"):
4413 dvs_create_spec.capability = vim.DVSCapability()
4414 _apply_dvs_capability(dvs_create_spec.capability, dvs_dict["capability"])
4415 if dvs_dict.get("link_discovery_protocol"):
4416 dvs_create_spec.configSpec.linkDiscoveryProtocolConfig = (
4417 vim.LinkDiscoveryProtocolConfig()
4418 )
4419 _apply_dvs_link_discovery_protocol(
4420 dvs_create_spec.configSpec.linkDiscoveryProtocolConfig,
4421 dvs_dict["link_discovery_protocol"],
4422 )
4423 if dvs_dict.get("infrastructure_traffic_resource_pools"):
4424 dvs_create_spec.configSpec.infrastructureTrafficResourceConfig = []
4425 _apply_dvs_infrastructure_traffic_resources(
4426 dvs_create_spec.configSpec.infrastructureTrafficResourceConfig,
4427 dvs_dict["infrastructure_traffic_resource_pools"],
4428 )
4429 log.trace("dvs_create_spec = {}".format(dvs_create_spec))
4430 salt.utils.vmware.create_dvs(dc_ref, dvs_name, dvs_create_spec)
4431 if "network_resource_management_enabled" in dvs_dict:
4432 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs_name])
4433 if not dvs_refs:
4434 raise VMwareObjectRetrievalError(
4435 "DVS '{}' wasn't found in datacenter '{}'"
4436 "".format(dvs_name, datacenter)
4437 )
4438 dvs_ref = dvs_refs[0]
4439 salt.utils.vmware.set_dvs_network_resource_management_enabled(
4440 dvs_ref, dvs_dict["network_resource_management_enabled"]
4441 )
4442 return True
4443
4444
4445 @depends(HAS_PYVMOMI)
4446 @supports_proxies("esxdatacenter", "esxcluster")
4447 @gets_service_instance_via_proxy
4448 def update_dvs(dvs_dict, dvs, service_instance=None):
4449 """
4450 Updates a distributed virtual switch (DVS).
4451
4452 Note: Updating the product info, capability, uplinks of a DVS is not
4453 supported so the corresponding entries in ``dvs_dict`` will be
4454 ignored.
4455
4456 dvs_dict
4457 Dictionary with the values the DVS should be update with
4458 (example in salt.states.dvs)
4459
4460 dvs
4461 Name of the DVS to be updated.
4462
4463 service_instance
4464 Service instance (vim.ServiceInstance) of the vCenter.
4465 Default is None.
4466
4467 .. code-block:: bash
4468
4469 salt '*' vsphere.update_dvs dvs_dict=$dvs_dict dvs=dvs1
4470 """
4471 # Remove ignored properties
4472 log.trace("Updating dvs '{}' with dict = {}".format(dvs, dvs_dict))
4473 for prop in ["product_info", "capability", "uplink_names", "name"]:
4474 if prop in dvs_dict:
4475 del dvs_dict[prop]
4476 proxy_type = get_proxy_type()
4477 if proxy_type == "esxdatacenter":
4478 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4479 dc_ref = _get_proxy_target(service_instance)
4480 elif proxy_type == "esxcluster":
4481 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4482 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4483 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
4484 if not dvs_refs:
4485 raise VMwareObjectRetrievalError(
4486 "DVS '{}' wasn't found in " "datacenter '{}'" "".format(dvs, datacenter)
4487 )
4488 dvs_ref = dvs_refs[0]
4489 # Build the config spec from the input
4490 dvs_props = salt.utils.vmware.get_properties_of_managed_object(
4491 dvs_ref, ["config", "capability"]
4492 )
4493 dvs_config = vim.VMwareDVSConfigSpec()
4494 # Copy all of the properties in the config of the of the DVS to a
4495 # DvsConfigSpec
4496 skipped_properties = ["host"]
4497 for prop in dvs_config.__dict__.keys():
4498 if prop in skipped_properties:
4499 continue
4500 if hasattr(dvs_props["config"], prop):
4501 setattr(dvs_config, prop, getattr(dvs_props["config"], prop))
4502 _apply_dvs_config(dvs_config, dvs_dict)
4503 if dvs_dict.get("link_discovery_protocol"):
4504 if not dvs_config.linkDiscoveryProtocolConfig:
4505 dvs_config.linkDiscoveryProtocolConfig = vim.LinkDiscoveryProtocolConfig()
4506 _apply_dvs_link_discovery_protocol(
4507 dvs_config.linkDiscoveryProtocolConfig, dvs_dict["link_discovery_protocol"]
4508 )
4509 if dvs_dict.get("infrastructure_traffic_resource_pools"):
4510 if not dvs_config.infrastructureTrafficResourceConfig:
4511 dvs_config.infrastructureTrafficResourceConfig = []
4512 _apply_dvs_infrastructure_traffic_resources(
4513 dvs_config.infrastructureTrafficResourceConfig,
4514 dvs_dict["infrastructure_traffic_resource_pools"],
4515 )
4516 log.trace("dvs_config= {}".format(dvs_config))
4517 salt.utils.vmware.update_dvs(dvs_ref, dvs_config_spec=dvs_config)
4518 if "network_resource_management_enabled" in dvs_dict:
4519 salt.utils.vmware.set_dvs_network_resource_management_enabled(
4520 dvs_ref, dvs_dict["network_resource_management_enabled"]
4521 )
4522 return True
4523
4524
4525 def _get_dvportgroup_out_shaping(pg_name, pg_default_port_config):
4526 """
4527 Returns the out shaping policy of a distributed virtual portgroup
4528
4529 pg_name
4530 The name of the portgroup
4531
4532 pg_default_port_config
4533 The dafault port config of the portgroup
4534 """
4535 log.trace("Retrieving portgroup's '{}' out shaping " "config".format(pg_name))
4536 out_shaping_policy = pg_default_port_config.outShapingPolicy
4537 if not out_shaping_policy:
4538 return {}
4539 return {
4540 "average_bandwidth": out_shaping_policy.averageBandwidth.value,
4541 "burst_size": out_shaping_policy.burstSize.value,
4542 "enabled": out_shaping_policy.enabled.value,
4543 "peak_bandwidth": out_shaping_policy.peakBandwidth.value,
4544 }
4545
4546
4547 def _get_dvportgroup_security_policy(pg_name, pg_default_port_config):
4548 """
4549 Returns the security policy of a distributed virtual portgroup
4550
4551 pg_name
4552 The name of the portgroup
4553
4554 pg_default_port_config
4555 The dafault port config of the portgroup
4556 """
4557 log.trace("Retrieving portgroup's '{}' security policy " "config".format(pg_name))
4558 sec_policy = pg_default_port_config.securityPolicy
4559 if not sec_policy:
4560 return {}
4561 return {
4562 "allow_promiscuous": sec_policy.allowPromiscuous.value,
4563 "forged_transmits": sec_policy.forgedTransmits.value,
4564 "mac_changes": sec_policy.macChanges.value,
4565 }
4566
4567
4568 def _get_dvportgroup_teaming(pg_name, pg_default_port_config):
4569 """
4570 Returns the teaming of a distributed virtual portgroup
4571
4572 pg_name
4573 The name of the portgroup
4574
4575 pg_default_port_config
4576 The dafault port config of the portgroup
4577 """
4578 log.trace("Retrieving portgroup's '{}' teaming" "config".format(pg_name))
4579 teaming_policy = pg_default_port_config.uplinkTeamingPolicy
4580 if not teaming_policy:
4581 return {}
4582 ret_dict = {
4583 "notify_switches": teaming_policy.notifySwitches.value,
4584 "policy": teaming_policy.policy.value,
4585 "reverse_policy": teaming_policy.reversePolicy.value,
4586 "rolling_order": teaming_policy.rollingOrder.value,
4587 }
4588 if teaming_policy.failureCriteria:
4589 failure_criteria = teaming_policy.failureCriteria
4590 ret_dict.update(
4591 {
4592 "failure_criteria": {
4593 "check_beacon": failure_criteria.checkBeacon.value,
4594 "check_duplex": failure_criteria.checkDuplex.value,
4595 "check_error_percent": failure_criteria.checkErrorPercent.value,
4596 "check_speed": failure_criteria.checkSpeed.value,
4597 "full_duplex": failure_criteria.fullDuplex.value,
4598 "percentage": failure_criteria.percentage.value,
4599 "speed": failure_criteria.speed.value,
4600 }
4601 }
4602 )
4603 if teaming_policy.uplinkPortOrder:
4604 uplink_order = teaming_policy.uplinkPortOrder
4605 ret_dict.update(
4606 {
4607 "port_order": {
4608 "active": uplink_order.activeUplinkPort,
4609 "standby": uplink_order.standbyUplinkPort,
4610 }
4611 }
4612 )
4613 return ret_dict
4614
4615
4616 def _get_dvportgroup_dict(pg_ref):
4617 """
4618 Returns a dictionary with a distributed virtual portgroup data
4619
4620
4621 pg_ref
4622 Portgroup reference
4623 """
4624 props = salt.utils.vmware.get_properties_of_managed_object(
4625 pg_ref,
4626 [
4627 "name",
4628 "config.description",
4629 "config.numPorts",
4630 "config.type",
4631 "config.defaultPortConfig",
4632 ],
4633 )
4634 pg_dict = {
4635 "name": props["name"],
4636 "description": props.get("config.description"),
4637 "num_ports": props["config.numPorts"],
4638 "type": props["config.type"],
4639 }
4640 if props["config.defaultPortConfig"]:
4641 dpg = props["config.defaultPortConfig"]
4642 if dpg.vlan and isinstance(
4643 dpg.vlan, vim.VmwareDistributedVirtualSwitchVlanIdSpec
4644 ):
4645
4646 pg_dict.update({"vlan_id": dpg.vlan.vlanId})
4647 pg_dict.update(
4648 {
4649 "out_shaping": _get_dvportgroup_out_shaping(
4650 props["name"], props["config.defaultPortConfig"]
4651 )
4652 }
4653 )
4654 pg_dict.update(
4655 {
4656 "security_policy": _get_dvportgroup_security_policy(
4657 props["name"], props["config.defaultPortConfig"]
4658 )
4659 }
4660 )
4661 pg_dict.update(
4662 {
4663 "teaming": _get_dvportgroup_teaming(
4664 props["name"], props["config.defaultPortConfig"]
4665 )
4666 }
4667 )
4668 return pg_dict
4669
4670
4671 @depends(HAS_PYVMOMI)
4672 @supports_proxies("esxdatacenter", "esxcluster")
4673 @gets_service_instance_via_proxy
4674 def list_dvportgroups(dvs=None, portgroup_names=None, service_instance=None):
4675 """
4676 Returns a list of distributed virtual switch portgroups.
4677 The list can be filtered by the portgroup names or by the DVS.
4678
4679 dvs
4680 Name of the DVS containing the portgroups.
4681 Default value is None.
4682
4683 portgroup_names
4684 List of portgroup names to look for. If None, all portgroups are
4685 returned.
4686 Default value is None
4687
4688 service_instance
4689 Service instance (vim.ServiceInstance) of the vCenter.
4690 Default is None.
4691
4692 .. code-block:: bash
4693
4694 salt '*' vsphere.list_dvporgroups
4695
4696 salt '*' vsphere.list_dvportgroups dvs=dvs1
4697
4698 salt '*' vsphere.list_dvportgroups portgroup_names=[pg1]
4699
4700 salt '*' vsphere.list_dvportgroups dvs=dvs1 portgroup_names=[pg1]
4701 """
4702 ret_dict = []
4703 proxy_type = get_proxy_type()
4704 if proxy_type == "esxdatacenter":
4705 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4706 dc_ref = _get_proxy_target(service_instance)
4707 elif proxy_type == "esxcluster":
4708 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4709 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4710 if dvs:
4711 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
4712 if not dvs_refs:
4713 raise VMwareObjectRetrievalError(
4714 "DVS '{}' was not " "retrieved".format(dvs)
4715 )
4716 dvs_ref = dvs_refs[0]
4717 get_all_portgroups = True if not portgroup_names else False
4718 for pg_ref in salt.utils.vmware.get_dvportgroups(
4719 parent_ref=dvs_ref if dvs else dc_ref,
4720 portgroup_names=portgroup_names,
4721 get_all_portgroups=get_all_portgroups,
4722 ):
4723
4724 ret_dict.append(_get_dvportgroup_dict(pg_ref))
4725 return ret_dict
4726
4727
4728 @depends(HAS_PYVMOMI)
4729 @supports_proxies("esxdatacenter", "esxcluster")
4730 @gets_service_instance_via_proxy
4731 def list_uplink_dvportgroup(dvs, service_instance=None):
4732 """
4733 Returns the uplink portgroup of a distributed virtual switch.
4734
4735 dvs
4736 Name of the DVS containing the portgroup.
4737
4738 service_instance
4739 Service instance (vim.ServiceInstance) of the vCenter.
4740 Default is None.
4741
4742 .. code-block:: bash
4743
4744 salt '*' vsphere.list_uplink_dvportgroup dvs=dvs_name
4745 """
4746 proxy_type = get_proxy_type()
4747 if proxy_type == "esxdatacenter":
4748 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4749 dc_ref = _get_proxy_target(service_instance)
4750 elif proxy_type == "esxcluster":
4751 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4752 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4753 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
4754 if not dvs_refs:
4755 raise VMwareObjectRetrievalError("DVS '{}' was not " "retrieved".format(dvs))
4756 uplink_pg_ref = salt.utils.vmware.get_uplink_dvportgroup(dvs_refs[0])
4757 return _get_dvportgroup_dict(uplink_pg_ref)
4758
4759
4760 def _apply_dvportgroup_out_shaping(pg_name, out_shaping, out_shaping_conf):
4761 """
4762 Applies the values in out_shaping_conf to an out_shaping object
4763
4764 pg_name
4765 The name of the portgroup
4766
4767 out_shaping
4768 The vim.DVSTrafficShapingPolicy to apply the config to
4769
4770 out_shaping_conf
4771 The out shaping config
4772 """
4773 log.trace("Building portgroup's '{}' out shaping " "policy".format(pg_name))
4774 if out_shaping_conf.get("average_bandwidth"):
4775 out_shaping.averageBandwidth = vim.LongPolicy()
4776 out_shaping.averageBandwidth.value = out_shaping_conf["average_bandwidth"]
4777 if out_shaping_conf.get("burst_size"):
4778 out_shaping.burstSize = vim.LongPolicy()
4779 out_shaping.burstSize.value = out_shaping_conf["burst_size"]
4780 if "enabled" in out_shaping_conf:
4781 out_shaping.enabled = vim.BoolPolicy()
4782 out_shaping.enabled.value = out_shaping_conf["enabled"]
4783 if out_shaping_conf.get("peak_bandwidth"):
4784 out_shaping.peakBandwidth = vim.LongPolicy()
4785 out_shaping.peakBandwidth.value = out_shaping_conf["peak_bandwidth"]
4786
4787
4788 def _apply_dvportgroup_security_policy(pg_name, sec_policy, sec_policy_conf):
4789 """
4790 Applies the values in sec_policy_conf to a security policy object
4791
4792 pg_name
4793 The name of the portgroup
4794
4795 sec_policy
4796 The vim.DVSTrafficShapingPolicy to apply the config to
4797
4798 sec_policy_conf
4799 The out shaping config
4800 """
4801 log.trace("Building portgroup's '{}' security policy ".format(pg_name))
4802 if "allow_promiscuous" in sec_policy_conf:
4803 sec_policy.allowPromiscuous = vim.BoolPolicy()
4804 sec_policy.allowPromiscuous.value = sec_policy_conf["allow_promiscuous"]
4805 if "forged_transmits" in sec_policy_conf:
4806 sec_policy.forgedTransmits = vim.BoolPolicy()
4807 sec_policy.forgedTransmits.value = sec_policy_conf["forged_transmits"]
4808 if "mac_changes" in sec_policy_conf:
4809 sec_policy.macChanges = vim.BoolPolicy()
4810 sec_policy.macChanges.value = sec_policy_conf["mac_changes"]
4811
4812
4813 def _apply_dvportgroup_teaming(pg_name, teaming, teaming_conf):
4814 """
4815 Applies the values in teaming_conf to a teaming policy object
4816
4817 pg_name
4818 The name of the portgroup
4819
4820 teaming
4821 The vim.VmwareUplinkPortTeamingPolicy to apply the config to
4822
4823 teaming_conf
4824 The teaming config
4825 """
4826 log.trace("Building portgroup's '{}' teaming".format(pg_name))
4827 if "notify_switches" in teaming_conf:
4828 teaming.notifySwitches = vim.BoolPolicy()
4829 teaming.notifySwitches.value = teaming_conf["notify_switches"]
4830 if "policy" in teaming_conf:
4831 teaming.policy = vim.StringPolicy()
4832 teaming.policy.value = teaming_conf["policy"]
4833 if "reverse_policy" in teaming_conf:
4834 teaming.reversePolicy = vim.BoolPolicy()
4835 teaming.reversePolicy.value = teaming_conf["reverse_policy"]
4836 if "rolling_order" in teaming_conf:
4837 teaming.rollingOrder = vim.BoolPolicy()
4838 teaming.rollingOrder.value = teaming_conf["rolling_order"]
4839 if "failure_criteria" in teaming_conf:
4840 if not teaming.failureCriteria:
4841 teaming.failureCriteria = vim.DVSFailureCriteria()
4842 failure_criteria_conf = teaming_conf["failure_criteria"]
4843 if "check_beacon" in failure_criteria_conf:
4844 teaming.failureCriteria.checkBeacon = vim.BoolPolicy()
4845 teaming.failureCriteria.checkBeacon.value = failure_criteria_conf[
4846 "check_beacon"
4847 ]
4848 if "check_duplex" in failure_criteria_conf:
4849 teaming.failureCriteria.checkDuplex = vim.BoolPolicy()
4850 teaming.failureCriteria.checkDuplex.value = failure_criteria_conf[
4851 "check_duplex"
4852 ]
4853 if "check_error_percent" in failure_criteria_conf:
4854 teaming.failureCriteria.checkErrorPercent = vim.BoolPolicy()
4855 teaming.failureCriteria.checkErrorPercent.value = failure_criteria_conf[
4856 "check_error_percent"
4857 ]
4858 if "check_speed" in failure_criteria_conf:
4859 teaming.failureCriteria.checkSpeed = vim.StringPolicy()
4860 teaming.failureCriteria.checkSpeed.value = failure_criteria_conf[
4861 "check_speed"
4862 ]
4863 if "full_duplex" in failure_criteria_conf:
4864 teaming.failureCriteria.fullDuplex = vim.BoolPolicy()
4865 teaming.failureCriteria.fullDuplex.value = failure_criteria_conf[
4866 "full_duplex"
4867 ]
4868 if "percentage" in failure_criteria_conf:
4869 teaming.failureCriteria.percentage = vim.IntPolicy()
4870 teaming.failureCriteria.percentage.value = failure_criteria_conf[
4871 "percentage"
4872 ]
4873 if "speed" in failure_criteria_conf:
4874 teaming.failureCriteria.speed = vim.IntPolicy()
4875 teaming.failureCriteria.speed.value = failure_criteria_conf["speed"]
4876 if "port_order" in teaming_conf:
4877 if not teaming.uplinkPortOrder:
4878 teaming.uplinkPortOrder = vim.VMwareUplinkPortOrderPolicy()
4879 if "active" in teaming_conf["port_order"]:
4880 teaming.uplinkPortOrder.activeUplinkPort = teaming_conf["port_order"][
4881 "active"
4882 ]
4883 if "standby" in teaming_conf["port_order"]:
4884 teaming.uplinkPortOrder.standbyUplinkPort = teaming_conf["port_order"][
4885 "standby"
4886 ]
4887
4888
4889 def _apply_dvportgroup_config(pg_name, pg_spec, pg_conf):
4890 """
4891 Applies the values in conf to a distributed portgroup spec
4892
4893 pg_name
4894 The name of the portgroup
4895
4896 pg_spec
4897 The vim.DVPortgroupConfigSpec to apply the config to
4898
4899 pg_conf
4900 The portgroup config
4901 """
4902 log.trace("Building portgroup's '{}' spec".format(pg_name))
4903 if "name" in pg_conf:
4904 pg_spec.name = pg_conf["name"]
4905 if "description" in pg_conf:
4906 pg_spec.description = pg_conf["description"]
4907 if "num_ports" in pg_conf:
4908 pg_spec.numPorts = pg_conf["num_ports"]
4909 if "type" in pg_conf:
4910 pg_spec.type = pg_conf["type"]
4911
4912 if not pg_spec.defaultPortConfig:
4913 for prop in ["vlan_id", "out_shaping", "security_policy", "teaming"]:
4914 if prop in pg_conf:
4915 pg_spec.defaultPortConfig = vim.VMwareDVSPortSetting()
4916 if "vlan_id" in pg_conf:
4917 pg_spec.defaultPortConfig.vlan = vim.VmwareDistributedVirtualSwitchVlanIdSpec()
4918 pg_spec.defaultPortConfig.vlan.vlanId = pg_conf["vlan_id"]
4919 if "out_shaping" in pg_conf:
4920 if not pg_spec.defaultPortConfig.outShapingPolicy:
4921 pg_spec.defaultPortConfig.outShapingPolicy = vim.DVSTrafficShapingPolicy()
4922 _apply_dvportgroup_out_shaping(
4923 pg_name, pg_spec.defaultPortConfig.outShapingPolicy, pg_conf["out_shaping"]
4924 )
4925 if "security_policy" in pg_conf:
4926 if not pg_spec.defaultPortConfig.securityPolicy:
4927 pg_spec.defaultPortConfig.securityPolicy = vim.DVSSecurityPolicy()
4928 _apply_dvportgroup_security_policy(
4929 pg_name,
4930 pg_spec.defaultPortConfig.securityPolicy,
4931 pg_conf["security_policy"],
4932 )
4933 if "teaming" in pg_conf:
4934 if not pg_spec.defaultPortConfig.uplinkTeamingPolicy:
4935 pg_spec.defaultPortConfig.uplinkTeamingPolicy = (
4936 vim.VmwareUplinkPortTeamingPolicy()
4937 )
4938 _apply_dvportgroup_teaming(
4939 pg_name, pg_spec.defaultPortConfig.uplinkTeamingPolicy, pg_conf["teaming"]
4940 )
4941
4942
4943 @depends(HAS_PYVMOMI)
4944 @supports_proxies("esxdatacenter", "esxcluster")
4945 @gets_service_instance_via_proxy
4946 def create_dvportgroup(portgroup_dict, portgroup_name, dvs, service_instance=None):
4947 """
4948 Creates a distributed virtual portgroup.
4949
4950 Note: The ``portgroup_name`` param will override any name already set
4951 in ``portgroup_dict``.
4952
4953 portgroup_dict
4954 Dictionary with the config values the portgroup should be created with
4955 (example in salt.states.dvs).
4956
4957 portgroup_name
4958 Name of the portgroup to be created.
4959
4960 dvs
4961 Name of the DVS that will contain the portgroup.
4962
4963 service_instance
4964 Service instance (vim.ServiceInstance) of the vCenter.
4965 Default is None.
4966
4967 .. code-block:: bash
4968
4969 salt '*' vsphere.create_dvportgroup portgroup_dict=<dict>
4970 portgroup_name=pg1 dvs=dvs1
4971 """
4972 log.trace(
4973 "Creating portgroup'{}' in dvs '{}' "
4974 "with dict = {}".format(portgroup_name, dvs, portgroup_dict)
4975 )
4976 proxy_type = get_proxy_type()
4977 if proxy_type == "esxdatacenter":
4978 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
4979 dc_ref = _get_proxy_target(service_instance)
4980 elif proxy_type == "esxcluster":
4981 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
4982 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
4983 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
4984 if not dvs_refs:
4985 raise VMwareObjectRetrievalError("DVS '{}' was not " "retrieved".format(dvs))
4986 # Make the name of the dvportgroup consistent with the parameter
4987 portgroup_dict["name"] = portgroup_name
4988 spec = vim.DVPortgroupConfigSpec()
4989 _apply_dvportgroup_config(portgroup_name, spec, portgroup_dict)
4990 salt.utils.vmware.create_dvportgroup(dvs_refs[0], spec)
4991 return True
4992
4993
4994 @depends(HAS_PYVMOMI)
4995 @supports_proxies("esxdatacenter", "esxcluster")
4996 @gets_service_instance_via_proxy
4997 def update_dvportgroup(portgroup_dict, portgroup, dvs, service_instance=True):
4998 """
4999 Updates a distributed virtual portgroup.
5000
5001 portgroup_dict
5002 Dictionary with the values the portgroup should be update with
5003 (example in salt.states.dvs).
5004
5005 portgroup
5006 Name of the portgroup to be updated.
5007
5008 dvs
5009 Name of the DVS containing the portgroups.
5010
5011 service_instance
5012 Service instance (vim.ServiceInstance) of the vCenter.
5013 Default is None.
5014
5015 .. code-block:: bash
5016
5017 salt '*' vsphere.update_dvportgroup portgroup_dict=<dict>
5018 portgroup=pg1
5019
5020 salt '*' vsphere.update_dvportgroup portgroup_dict=<dict>
5021 portgroup=pg1 dvs=dvs1
5022 """
5023 log.trace(
5024 "Updating portgroup'{}' in dvs '{}' "
5025 "with dict = {}".format(portgroup, dvs, portgroup_dict)
5026 )
5027 proxy_type = get_proxy_type()
5028 if proxy_type == "esxdatacenter":
5029 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
5030 dc_ref = _get_proxy_target(service_instance)
5031 elif proxy_type == "esxcluster":
5032 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
5033 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
5034 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
5035 if not dvs_refs:
5036 raise VMwareObjectRetrievalError("DVS '{}' was not " "retrieved".format(dvs))
5037 pg_refs = salt.utils.vmware.get_dvportgroups(
5038 dvs_refs[0], portgroup_names=[portgroup]
5039 )
5040 if not pg_refs:
5041 raise VMwareObjectRetrievalError(
5042 "Portgroup '{}' was not " "retrieved".format(portgroup)
5043 )
5044 pg_props = salt.utils.vmware.get_properties_of_managed_object(
5045 pg_refs[0], ["config"]
5046 )
5047 spec = vim.DVPortgroupConfigSpec()
5048 # Copy existing properties in spec
5049 for prop in [
5050 "autoExpand",
5051 "configVersion",
5052 "defaultPortConfig",
5053 "description",
5054 "name",
5055 "numPorts",
5056 "policy",
5057 "portNameFormat",
5058 "scope",
5059 "type",
5060 "vendorSpecificConfig",
5061 ]:
5062 setattr(spec, prop, getattr(pg_props["config"], prop))
5063 _apply_dvportgroup_config(portgroup, spec, portgroup_dict)
5064 salt.utils.vmware.update_dvportgroup(pg_refs[0], spec)
5065 return True
5066
5067
5068 @depends(HAS_PYVMOMI)
5069 @supports_proxies("esxdatacenter", "esxcluster")
5070 @gets_service_instance_via_proxy
5071 def remove_dvportgroup(portgroup, dvs, service_instance=None):
5072 """
5073 Removes a distributed virtual portgroup.
5074
5075 portgroup
5076 Name of the portgroup to be removed.
5077
5078 dvs
5079 Name of the DVS containing the portgroups.
5080
5081 service_instance
5082 Service instance (vim.ServiceInstance) of the vCenter.
5083 Default is None.
5084
5085 .. code-block:: bash
5086
5087 salt '*' vsphere.remove_dvportgroup portgroup=pg1 dvs=dvs1
5088 """
5089 log.trace("Removing portgroup'{}' in dvs '{}' " "".format(portgroup, dvs))
5090 proxy_type = get_proxy_type()
5091 if proxy_type == "esxdatacenter":
5092 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
5093 dc_ref = _get_proxy_target(service_instance)
5094 elif proxy_type == "esxcluster":
5095 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
5096 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
5097 dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs])
5098 if not dvs_refs:
5099 raise VMwareObjectRetrievalError("DVS '{}' was not " "retrieved".format(dvs))
5100 pg_refs = salt.utils.vmware.get_dvportgroups(
5101 dvs_refs[0], portgroup_names=[portgroup]
5102 )
5103 if not pg_refs:
5104 raise VMwareObjectRetrievalError(
5105 "Portgroup '{}' was not " "retrieved".format(portgroup)
5106 )
5107 salt.utils.vmware.remove_dvportgroup(pg_refs[0])
5108 return True
5109
5110
5111 def _get_policy_dict(policy):
5112 """Returns a dictionary representation of a policy"""
5113 profile_dict = {
5114 "name": policy.name,
5115 "description": policy.description,
5116 "resource_type": policy.resourceType.resourceType,
5117 }
5118 subprofile_dicts = []
5119 if isinstance(policy, pbm.profile.CapabilityBasedProfile) and isinstance(
5120 policy.constraints, pbm.profile.SubProfileCapabilityConstraints
5121 ):
5122
5123 for subprofile in policy.constraints.subProfiles:
5124 subprofile_dict = {
5125 "name": subprofile.name,
5126 "force_provision": subprofile.forceProvision,
5127 }
5128 cap_dicts = []
5129 for cap in subprofile.capability:
5130 cap_dict = {"namespace": cap.id.namespace, "id": cap.id.id}
5131 # We assume there is one constraint with one value set
5132 val = cap.constraint[0].propertyInstance[0].value
5133 if isinstance(val, pbm.capability.types.Range):
5134 val_dict = {"type": "range", "min": val.min, "max": val.max}
5135 elif isinstance(val, pbm.capability.types.DiscreteSet):
5136 val_dict = {"type": "set", "values": val.values}
5137 else:
5138 val_dict = {"type": "scalar", "value": val}
5139 cap_dict["setting"] = val_dict
5140 cap_dicts.append(cap_dict)
5141 subprofile_dict["capabilities"] = cap_dicts
5142 subprofile_dicts.append(subprofile_dict)
5143 profile_dict["subprofiles"] = subprofile_dicts
5144 return profile_dict
5145
5146
5147 @depends(HAS_PYVMOMI)
5148 @supports_proxies("esxdatacenter", "vcenter")
5149 @gets_service_instance_via_proxy
5150 def list_storage_policies(policy_names=None, service_instance=None):
5151 """
5152 Returns a list of storage policies.
5153
5154 policy_names
5155 Names of policies to list. If None, all policies are listed.
5156 Default is None.
5157
5158 service_instance
5159 Service instance (vim.ServiceInstance) of the vCenter.
5160 Default is None.
5161
5162 .. code-block:: bash
5163
5164 salt '*' vsphere.list_storage_policies
5165
5166 salt '*' vsphere.list_storage_policy policy_names=[policy_name]
5167 """
5168 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5169 if not policy_names:
5170 policies = salt.utils.pbm.get_storage_policies(
5171 profile_manager, get_all_policies=True
5172 )
5173 else:
5174 policies = salt.utils.pbm.get_storage_policies(profile_manager, policy_names)
5175 return [_get_policy_dict(p) for p in policies]
5176
5177
5178 @depends(HAS_PYVMOMI)
5179 @supports_proxies("esxdatacenter", "vcenter")
5180 @gets_service_instance_via_proxy
5181 def list_default_vsan_policy(service_instance=None):
5182 """
5183 Returns the default vsan storage policy.
5184
5185 service_instance
5186 Service instance (vim.ServiceInstance) of the vCenter.
5187 Default is None.
5188
5189 .. code-block:: bash
5190
5191 salt '*' vsphere.list_storage_policies
5192
5193 salt '*' vsphere.list_storage_policy policy_names=[policy_name]
5194 """
5195 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5196 policies = salt.utils.pbm.get_storage_policies(
5197 profile_manager, get_all_policies=True
5198 )
5199 def_policies = [
5200 p for p in policies if p.systemCreatedProfileType == "VsanDefaultProfile"
5201 ]
5202 if not def_policies:
5203 raise VMwareObjectRetrievalError("Default VSAN policy was not " "retrieved")
5204 return _get_policy_dict(def_policies[0])
5205
5206
5207 def _get_capability_definition_dict(cap_metadata):
5208 # We assume each capability definition has one property with the same id
5209 # as the capability so we display its type as belonging to the capability
5210 # The object model permits multiple properties
5211 return {
5212 "namespace": cap_metadata.id.namespace,
5213 "id": cap_metadata.id.id,
5214 "mandatory": cap_metadata.mandatory,
5215 "description": cap_metadata.summary.summary,
5216 "type": cap_metadata.propertyMetadata[0].type.typeName,
5217 }
5218
5219
5220 @depends(HAS_PYVMOMI)
5221 @supports_proxies("esxdatacenter", "vcenter")
5222 @gets_service_instance_via_proxy
5223 def list_capability_definitions(service_instance=None):
5224 """
5225 Returns a list of the metadata of all capabilities in the vCenter.
5226
5227 service_instance
5228 Service instance (vim.ServiceInstance) of the vCenter.
5229 Default is None.
5230
5231 .. code-block:: bash
5232
5233 salt '*' vsphere.list_capabilities
5234 """
5235 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5236 ret_list = [
5237 _get_capability_definition_dict(c)
5238 for c in salt.utils.pbm.get_capability_definitions(profile_manager)
5239 ]
5240 return ret_list
5241
5242
5243 def _apply_policy_config(policy_spec, policy_dict):
5244 """Applies a policy dictionary to a policy spec"""
5245 log.trace("policy_dict = {}".format(policy_dict))
5246 if policy_dict.get("name"):
5247 policy_spec.name = policy_dict["name"]
5248 if policy_dict.get("description"):
5249 policy_spec.description = policy_dict["description"]
5250 if policy_dict.get("subprofiles"):
5251 # Incremental changes to subprofiles and capabilities are not
5252 # supported because they would complicate updates too much
5253 # The whole configuration of all sub-profiles is expected and applied
5254 policy_spec.constraints = pbm.profile.SubProfileCapabilityConstraints()
5255 subprofiles = []
5256 for subprofile_dict in policy_dict["subprofiles"]:
5257 subprofile_spec = pbm.profile.SubProfileCapabilityConstraints.SubProfile(
5258 name=subprofile_dict["name"]
5259 )
5260 cap_specs = []
5261 if subprofile_dict.get("force_provision"):
5262 subprofile_spec.forceProvision = subprofile_dict["force_provision"]
5263 for cap_dict in subprofile_dict["capabilities"]:
5264 prop_inst_spec = pbm.capability.PropertyInstance(id=cap_dict["id"])
5265 setting_type = cap_dict["setting"]["type"]
5266 if setting_type == "set":
5267 prop_inst_spec.value = pbm.capability.types.DiscreteSet()
5268 prop_inst_spec.value.values = cap_dict["setting"]["values"]
5269 elif setting_type == "range":
5270 prop_inst_spec.value = pbm.capability.types.Range()
5271 prop_inst_spec.value.max = cap_dict["setting"]["max"]
5272 prop_inst_spec.value.min = cap_dict["setting"]["min"]
5273 elif setting_type == "scalar":
5274 prop_inst_spec.value = cap_dict["setting"]["value"]
5275 cap_spec = pbm.capability.CapabilityInstance(
5276 id=pbm.capability.CapabilityMetadata.UniqueId(
5277 id=cap_dict["id"], namespace=cap_dict["namespace"]
5278 ),
5279 constraint=[
5280 pbm.capability.ConstraintInstance(
5281 propertyInstance=[prop_inst_spec]
5282 )
5283 ],
5284 )
5285 cap_specs.append(cap_spec)
5286 subprofile_spec.capability = cap_specs
5287 subprofiles.append(subprofile_spec)
5288 policy_spec.constraints.subProfiles = subprofiles
5289 log.trace("updated policy_spec = {}".format(policy_spec))
5290 return policy_spec
5291
5292
5293 @depends(HAS_PYVMOMI)
5294 @supports_proxies("esxdatacenter", "vcenter")
5295 @gets_service_instance_via_proxy
5296 def create_storage_policy(policy_name, policy_dict, service_instance=None):
5297 """
5298 Creates a storage policy.
5299
5300 Supported capability types: scalar, set, range.
5301
5302 policy_name
5303 Name of the policy to create.
5304 The value of the argument will override any existing name in
5305 ``policy_dict``.
5306
5307 policy_dict
5308 Dictionary containing the changes to apply to the policy.
5309 (example in salt.states.pbm)
5310
5311 service_instance
5312 Service instance (vim.ServiceInstance) of the vCenter.
5313 Default is None.
5314
5315 .. code-block:: bash
5316
5317 salt '*' vsphere.create_storage_policy policy_name='policy name'
5318 policy_dict="$policy_dict"
5319 """
5320 log.trace(
5321 "create storage policy '{}', dict = {}" "".format(policy_name, policy_dict)
5322 )
5323 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5324 policy_create_spec = pbm.profile.CapabilityBasedProfileCreateSpec()
5325 # Hardcode the storage profile resource type
5326 policy_create_spec.resourceType = pbm.profile.ResourceType(
5327 resourceType=pbm.profile.ResourceTypeEnum.STORAGE
5328 )
5329 # Set name argument
5330 policy_dict["name"] = policy_name
5331 log.trace("Setting policy values in policy_update_spec")
5332 _apply_policy_config(policy_create_spec, policy_dict)
5333 salt.utils.pbm.create_storage_policy(profile_manager, policy_create_spec)
5334 return {"create_storage_policy": True}
5335
5336
5337 @depends(HAS_PYVMOMI)
5338 @supports_proxies("esxdatacenter", "vcenter")
5339 @gets_service_instance_via_proxy
5340 def update_storage_policy(policy, policy_dict, service_instance=None):
5341 """
5342 Updates a storage policy.
5343
5344 Supported capability types: scalar, set, range.
5345
5346 policy
5347 Name of the policy to update.
5348
5349 policy_dict
5350 Dictionary containing the changes to apply to the policy.
5351 (example in salt.states.pbm)
5352
5353 service_instance
5354 Service instance (vim.ServiceInstance) of the vCenter.
5355 Default is None.
5356
5357 .. code-block:: bash
5358
5359 salt '*' vsphere.update_storage_policy policy='policy name'
5360 policy_dict="$policy_dict"
5361 """
5362 log.trace("updating storage policy, dict = {}".format(policy_dict))
5363 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5364 policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy])
5365 if not policies:
5366 raise VMwareObjectRetrievalError("Policy '{}' was not found" "".format(policy))
5367 policy_ref = policies[0]
5368 policy_update_spec = pbm.profile.CapabilityBasedProfileUpdateSpec()
5369 log.trace("Setting policy values in policy_update_spec")
5370 for prop in ["description", "constraints"]:
5371 setattr(policy_update_spec, prop, getattr(policy_ref, prop))
5372 _apply_policy_config(policy_update_spec, policy_dict)
5373 salt.utils.pbm.update_storage_policy(
5374 profile_manager, policy_ref, policy_update_spec
5375 )
5376 return {"update_storage_policy": True}
5377
5378
5379 @depends(HAS_PYVMOMI)
5380 @supports_proxies("esxcluster", "esxdatacenter", "vcenter")
5381 @gets_service_instance_via_proxy
5382 def list_default_storage_policy_of_datastore(datastore, service_instance=None):
5383 """
5384 Returns a list of datastores assign the storage policies.
5385
5386 datastore
5387 Name of the datastore to assign.
5388 The datastore needs to be visible to the VMware entity the proxy
5389 points to.
5390
5391 service_instance
5392 Service instance (vim.ServiceInstance) of the vCenter.
5393 Default is None.
5394
5395 .. code-block:: bash
5396
5397 salt '*' vsphere.list_default_storage_policy_of_datastore datastore=ds1
5398 """
5399 log.trace(
5400 "Listing the default storage policy of datastore '{}'" "".format(datastore)
5401 )
5402 # Find datastore
5403 target_ref = _get_proxy_target(service_instance)
5404 ds_refs = salt.utils.vmware.get_datastores(
5405 service_instance, target_ref, datastore_names=[datastore]
5406 )
5407 if not ds_refs:
5408 raise VMwareObjectRetrievalError(
5409 "Datastore '{}' was not " "found".format(datastore)
5410 )
5411 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5412 policy = salt.utils.pbm.get_default_storage_policy_of_datastore(
5413 profile_manager, ds_refs[0]
5414 )
5415 return _get_policy_dict(policy)
5416
5417
5418 @depends(HAS_PYVMOMI)
5419 @supports_proxies("esxcluster", "esxdatacenter", "vcenter")
5420 @gets_service_instance_via_proxy
5421 def assign_default_storage_policy_to_datastore(
5422 policy, datastore, service_instance=None
5423 ):
5424 """
5425 Assigns a storage policy as the default policy to a datastore.
5426
5427 policy
5428 Name of the policy to assign.
5429
5430 datastore
5431 Name of the datastore to assign.
5432 The datastore needs to be visible to the VMware entity the proxy
5433 points to.
5434
5435 service_instance
5436 Service instance (vim.ServiceInstance) of the vCenter.
5437 Default is None.
5438
5439 .. code-block:: bash
5440
5441 salt '*' vsphere.assign_storage_policy_to_datastore
5442 policy='policy name' datastore=ds1
5443 """
5444 log.trace("Assigning policy {} to datastore {}" "".format(policy, datastore))
5445 profile_manager = salt.utils.pbm.get_profile_manager(service_instance)
5446 # Find policy
5447 policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy])
5448 if not policies:
5449 raise VMwareObjectRetrievalError("Policy '{}' was not found" "".format(policy))
5450 policy_ref = policies[0]
5451 # Find datastore
5452 target_ref = _get_proxy_target(service_instance)
5453 ds_refs = salt.utils.vmware.get_datastores(
5454 service_instance, target_ref, datastore_names=[datastore]
5455 )
5456 if not ds_refs:
5457 raise VMwareObjectRetrievalError(
5458 "Datastore '{}' was not " "found".format(datastore)
5459 )
5460 ds_ref = ds_refs[0]
5461 salt.utils.pbm.assign_default_storage_policy_to_datastore(
5462 profile_manager, policy_ref, ds_ref
5463 )
5464 return True
5465
5466
5467 @depends(HAS_PYVMOMI)
5468 @supports_proxies("esxdatacenter", "esxcluster", "vcenter", "esxvm")
5469 @gets_service_instance_via_proxy
5470 def list_datacenters_via_proxy(datacenter_names=None, service_instance=None):
5471 """
5472 Returns a list of dict representations of VMware datacenters.
5473 Connection is done via the proxy details.
5474
5475 Supported proxies: esxdatacenter
5476
5477 datacenter_names
5478 List of datacenter names.
5479 Default is None.
5480
5481 service_instance
5482 Service instance (vim.ServiceInstance) of the vCenter.
5483 Default is None.
5484
5485 .. code-block:: bash
5486
5487 salt '*' vsphere.list_datacenters_via_proxy
5488
5489 salt '*' vsphere.list_datacenters_via_proxy dc1
5490
5491 salt '*' vsphere.list_datacenters_via_proxy dc1,dc2
5492
5493 salt '*' vsphere.list_datacenters_via_proxy datacenter_names=[dc1, dc2]
5494 """
5495 if not datacenter_names:
5496 dc_refs = salt.utils.vmware.get_datacenters(
5497 service_instance, get_all_datacenters=True
5498 )
5499 else:
5500 dc_refs = salt.utils.vmware.get_datacenters(service_instance, datacenter_names)
5501
5502 return [
5503 {"name": salt.utils.vmware.get_managed_object_name(dc_ref)}
5504 for dc_ref in dc_refs
5505 ]
5506
5507
5508 @depends(HAS_PYVMOMI)
5509 @supports_proxies("esxdatacenter", "vcenter")
5510 @gets_service_instance_via_proxy
5511 def create_datacenter(datacenter_name, service_instance=None):
5512 """
5513 Creates a datacenter.
5514
5515 Supported proxies: esxdatacenter
5516
5517 datacenter_name
5518 The datacenter name
5519
5520 service_instance
5521 Service instance (vim.ServiceInstance) of the vCenter.
5522 Default is None.
5523
5524 .. code-block:: bash
5525
5526 salt '*' vsphere.create_datacenter dc1
5527 """
5528 salt.utils.vmware.create_datacenter(service_instance, datacenter_name)
5529 return {"create_datacenter": True}
5530
5531
5532 def _get_cluster_dict(cluster_name, cluster_ref):
5533 """
5534 Returns a cluster dict representation from
5535 a vim.ClusterComputeResource object.
5536
5537 cluster_name
5538 Name of the cluster
5539
5540 cluster_ref
5541 Reference to the cluster
5542 """
5543
5544 log.trace(
5545 "Building a dictionary representation of cluster " "'{}'".format(cluster_name)
5546 )
5547 props = salt.utils.vmware.get_properties_of_managed_object(
5548 cluster_ref, properties=["configurationEx"]
5549 )
5550 res = {
5551 "ha": {"enabled": props["configurationEx"].dasConfig.enabled},
5552 "drs": {"enabled": props["configurationEx"].drsConfig.enabled},
5553 }
5554 # Convert HA properties of interest
5555 ha_conf = props["configurationEx"].dasConfig
5556 log.trace("ha_conf = {}".format(ha_conf))
5557 res["ha"]["admission_control_enabled"] = ha_conf.admissionControlEnabled
5558 if ha_conf.admissionControlPolicy and isinstance(
5559 ha_conf.admissionControlPolicy,
5560 vim.ClusterFailoverResourcesAdmissionControlPolicy,
5561 ):
5562 pol = ha_conf.admissionControlPolicy
5563 res["ha"]["admission_control_policy"] = {
5564 "cpu_failover_percent": pol.cpuFailoverResourcesPercent,
5565 "memory_failover_percent": pol.memoryFailoverResourcesPercent,
5566 }
5567 if ha_conf.defaultVmSettings:
5568 def_vm_set = ha_conf.defaultVmSettings
5569 res["ha"]["default_vm_settings"] = {
5570 "isolation_response": def_vm_set.isolationResponse,
5571 "restart_priority": def_vm_set.restartPriority,
5572 }
5573 res["ha"]["hb_ds_candidate_policy"] = ha_conf.hBDatastoreCandidatePolicy
5574 if ha_conf.hostMonitoring:
5575 res["ha"]["host_monitoring"] = ha_conf.hostMonitoring
5576 if ha_conf.option:
5577 res["ha"]["options"] = [
5578 {"key": o.key, "value": o.value} for o in ha_conf.option
5579 ]
5580 res["ha"]["vm_monitoring"] = ha_conf.vmMonitoring
5581 # Convert DRS properties
5582 drs_conf = props["configurationEx"].drsConfig
5583 log.trace("drs_conf = {}".format(drs_conf))
5584 res["drs"]["vmotion_rate"] = 6 - drs_conf.vmotionRate
5585 res["drs"]["default_vm_behavior"] = drs_conf.defaultVmBehavior
5586 # vm_swap_placement
5587 res["vm_swap_placement"] = props["configurationEx"].vmSwapPlacement
5588 # Convert VSAN properties
5589 si = salt.utils.vmware.get_service_instance_from_managed_object(cluster_ref)
5590
5591 if salt.utils.vsan.vsan_supported(si):
5592 # XXX The correct way of retrieving the VSAN data (on the if branch)
5593 # is not supported before 60u2 vcenter
5594 vcenter_info = salt.utils.vmware.get_service_info(si)
5595 if int(vcenter_info.build) >= 3634794: # 60u2
5596 # VSAN API is fully supported by the VC starting with 60u2
5597 vsan_conf = salt.utils.vsan.get_cluster_vsan_info(cluster_ref)
5598 log.trace("vsan_conf = {}".format(vsan_conf))
5599 res["vsan"] = {
5600 "enabled": vsan_conf.enabled,
5601 "auto_claim_storage": vsan_conf.defaultConfig.autoClaimStorage,
5602 }
5603 if vsan_conf.dataEfficiencyConfig:
5604 data_eff = vsan_conf.dataEfficiencyConfig
5605 res["vsan"].update(
5606 {
5607 # We force compression_enabled to be True/False
5608 "compression_enabled": data_eff.compressionEnabled or False,
5609 "dedup_enabled": data_eff.dedupEnabled,
5610 }
5611 )
5612 else: # before 60u2 (no advanced vsan info)
5613 if props["configurationEx"].vsanConfigInfo:
5614 default_config = props["configurationEx"].vsanConfigInfo.defaultConfig
5615 res["vsan"] = {
5616 "enabled": props["configurationEx"].vsanConfigInfo.enabled,
5617 "auto_claim_storage": default_config.autoClaimStorage,
5618 }
5619 return res
5620
5621
5622 @depends(HAS_PYVMOMI)
5623 @supports_proxies("esxcluster", "esxdatacenter")
5624 @gets_service_instance_via_proxy
5625 def list_cluster(datacenter=None, cluster=None, service_instance=None):
5626 """
5627 Returns a dict representation of an ESX cluster.
5628
5629 datacenter
5630 Name of datacenter containing the cluster.
5631 Ignored if already contained by proxy details.
5632 Default value is None.
5633
5634 cluster
5635 Name of cluster.
5636 Ignored if already contained by proxy details.
5637 Default value is None.
5638
5639 service_instance
5640 Service instance (vim.ServiceInstance) of the vCenter.
5641 Default is None.
5642
5643 .. code-block:: bash
5644
5645 # vcenter proxy
5646 salt '*' vsphere.list_cluster datacenter=dc1 cluster=cl1
5647
5648 # esxdatacenter proxy
5649 salt '*' vsphere.list_cluster cluster=cl1
5650
5651 # esxcluster proxy
5652 salt '*' vsphere.list_cluster
5653 """
5654 proxy_type = get_proxy_type()
5655 if proxy_type == "esxdatacenter":
5656 dc_ref = _get_proxy_target(service_instance)
5657 if not cluster:
5658 raise ArgumentValueError("'cluster' needs to be specified")
5659 cluster_ref = salt.utils.vmware.get_cluster(dc_ref, cluster)
5660 elif proxy_type == "esxcluster":
5661 cluster_ref = _get_proxy_target(service_instance)
5662 cluster = __salt__["esxcluster.get_details"]()["cluster"]
5663 log.trace(
5664 "Retrieving representation of cluster '{}' in a "
5665 "{} proxy".format(cluster, proxy_type)
5666 )
5667 return _get_cluster_dict(cluster, cluster_ref)
5668
5669
5670 def _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec=None, vsan_61=True):
5671 """
5672 Applies the values of cluster_dict dictionary to a cluster spec
5673 (vim.ClusterConfigSpecEx).
5674
5675 All vsan values (cluster_dict['vsan']) will be applied to
5676 vsan_spec (vim.vsan.cluster.ConfigInfoEx). Can be not omitted
5677 if not required.
5678
5679 VSAN 6.1 config needs to be applied differently than the post VSAN 6.1 way.
5680 The type of configuration desired is dictated by the flag vsan_61.
5681 """
5682 log.trace("Applying cluster dict {}".format(cluster_dict))
5683 if cluster_dict.get("ha"):
5684 ha_dict = cluster_dict["ha"]
5685 if not cluster_spec.dasConfig:
5686 cluster_spec.dasConfig = vim.ClusterDasConfigInfo()
5687 das_config = cluster_spec.dasConfig
5688 if "enabled" in ha_dict:
5689 das_config.enabled = ha_dict["enabled"]
5690 if ha_dict["enabled"]:
5691 # Default values when ha is enabled
5692 das_config.failoverLevel = 1
5693 if "admission_control_enabled" in ha_dict:
5694 das_config.admissionControlEnabled = ha_dict["admission_control_enabled"]
5695 if "admission_control_policy" in ha_dict:
5696 adm_pol_dict = ha_dict["admission_control_policy"]
5697 if not das_config.admissionControlPolicy or not isinstance(
5698 das_config.admissionControlPolicy,
5699 vim.ClusterFailoverResourcesAdmissionControlPolicy,
5700 ):
5701
5702 das_config.admissionControlPolicy = vim.ClusterFailoverResourcesAdmissionControlPolicy(
5703 cpuFailoverResourcesPercent=adm_pol_dict["cpu_failover_percent"],
5704 memoryFailoverResourcesPercent=adm_pol_dict[
5705 "memory_failover_percent"
5706 ],
5707 )
5708 if "default_vm_settings" in ha_dict:
5709 vm_set_dict = ha_dict["default_vm_settings"]
5710 if not das_config.defaultVmSettings:
5711 das_config.defaultVmSettings = vim.ClusterDasVmSettings()
5712 if "isolation_response" in vm_set_dict:
5713 das_config.defaultVmSettings.isolationResponse = vm_set_dict[
5714 "isolation_response"
5715 ]
5716 if "restart_priority" in vm_set_dict:
5717 das_config.defaultVmSettings.restartPriority = vm_set_dict[
5718 "restart_priority"
5719 ]
5720 if "hb_ds_candidate_policy" in ha_dict:
5721 das_config.hBDatastoreCandidatePolicy = ha_dict["hb_ds_candidate_policy"]
5722 if "host_monitoring" in ha_dict:
5723 das_config.hostMonitoring = ha_dict["host_monitoring"]
5724 if "options" in ha_dict:
5725 das_config.option = []
5726 for opt_dict in ha_dict["options"]:
5727 das_config.option.append(vim.OptionValue(key=opt_dict["key"]))
5728 if "value" in opt_dict:
5729 das_config.option[-1].value = opt_dict["value"]
5730 if "vm_monitoring" in ha_dict:
5731 das_config.vmMonitoring = ha_dict["vm_monitoring"]
5732 cluster_spec.dasConfig = das_config
5733 if cluster_dict.get("drs"):
5734 drs_dict = cluster_dict["drs"]
5735 drs_config = vim.ClusterDrsConfigInfo()
5736 if "enabled" in drs_dict:
5737 drs_config.enabled = drs_dict["enabled"]
5738 if "vmotion_rate" in drs_dict:
5739 drs_config.vmotionRate = 6 - drs_dict["vmotion_rate"]
5740 if "default_vm_behavior" in drs_dict:
5741 drs_config.defaultVmBehavior = vim.DrsBehavior(
5742 drs_dict["default_vm_behavior"]
5743 )
5744 cluster_spec.drsConfig = drs_config
5745 if cluster_dict.get("vm_swap_placement"):
5746 cluster_spec.vmSwapPlacement = cluster_dict["vm_swap_placement"]
5747 if cluster_dict.get("vsan"):
5748 vsan_dict = cluster_dict["vsan"]
5749 if not vsan_61: # VSAN is 6.2 and above
5750 if "enabled" in vsan_dict:
5751 if not vsan_spec.vsanClusterConfig:
5752 vsan_spec.vsanClusterConfig = vim.vsan.cluster.ConfigInfo()
5753 vsan_spec.vsanClusterConfig.enabled = vsan_dict["enabled"]
5754 if "auto_claim_storage" in vsan_dict:
5755 if not vsan_spec.vsanClusterConfig:
5756 vsan_spec.vsanClusterConfig = vim.vsan.cluster.ConfigInfo()
5757 if not vsan_spec.vsanClusterConfig.defaultConfig:
5758 vsan_spec.vsanClusterConfig.defaultConfig = (
5759 vim.VsanClusterConfigInfoHostDefaultInfo()
5760 )
5761 elif vsan_spec.vsanClusterConfig.defaultConfig.uuid:
5762 # If this remains set it caused an error
5763 vsan_spec.vsanClusterConfig.defaultConfig.uuid = None
5764 vsan_spec.vsanClusterConfig.defaultConfig.autoClaimStorage = vsan_dict[
5765 "auto_claim_storage"
5766 ]
5767 if "compression_enabled" in vsan_dict:
5768 if not vsan_spec.dataEfficiencyConfig:
5769 vsan_spec.dataEfficiencyConfig = vim.vsan.DataEfficiencyConfig()
5770 vsan_spec.dataEfficiencyConfig.compressionEnabled = vsan_dict[
5771 "compression_enabled"
5772 ]
5773 if "dedup_enabled" in vsan_dict:
5774 if not vsan_spec.dataEfficiencyConfig:
5775 vsan_spec.dataEfficiencyConfig = vim.vsan.DataEfficiencyConfig()
5776 vsan_spec.dataEfficiencyConfig.dedupEnabled = vsan_dict["dedup_enabled"]
5777 # In all cases we need to configure the vsan on the cluster
5778 # directly so not to have a mismatch between vsan_spec and
5779 # cluster_spec
5780 if not cluster_spec.vsanConfig:
5781 cluster_spec.vsanConfig = vim.VsanClusterConfigInfo()
5782 vsan_config = cluster_spec.vsanConfig
5783 if "enabled" in vsan_dict:
5784 vsan_config.enabled = vsan_dict["enabled"]
5785 if "auto_claim_storage" in vsan_dict:
5786 if not vsan_config.defaultConfig:
5787 vsan_config.defaultConfig = vim.VsanClusterConfigInfoHostDefaultInfo()
5788 elif vsan_config.defaultConfig.uuid:
5789 # If this remains set it caused an error
5790 vsan_config.defaultConfig.uuid = None
5791 vsan_config.defaultConfig.autoClaimStorage = vsan_dict["auto_claim_storage"]
5792 log.trace("cluster_spec = {}".format(cluster_spec))
5793
5794
5795 @depends(HAS_PYVMOMI)
5796 @depends(HAS_JSONSCHEMA)
5797 @supports_proxies("esxcluster", "esxdatacenter")
5798 @gets_service_instance_via_proxy
5799 def create_cluster(cluster_dict, datacenter=None, cluster=None, service_instance=None):
5800 """
5801 Creates a cluster.
5802
5803 Note: cluster_dict['name'] will be overridden by the cluster param value
5804
5805 config_dict
5806 Dictionary with the config values of the new cluster.
5807
5808 datacenter
5809 Name of datacenter containing the cluster.
5810 Ignored if already contained by proxy details.
5811 Default value is None.
5812
5813 cluster
5814 Name of cluster.
5815 Ignored if already contained by proxy details.
5816 Default value is None.
5817
5818 service_instance
5819 Service instance (vim.ServiceInstance) of the vCenter.
5820 Default is None.
5821
5822 .. code-block:: bash
5823
5824 # esxdatacenter proxy
5825 salt '*' vsphere.create_cluster cluster_dict=$cluster_dict cluster=cl1
5826
5827 # esxcluster proxy
5828 salt '*' vsphere.create_cluster cluster_dict=$cluster_dict
5829 """
5830 # Validate cluster dictionary
5831 schema = ESXClusterConfigSchema.serialize()
5832 try:
5833 jsonschema.validate(cluster_dict, schema)
5834 except jsonschema.exceptions.ValidationError as exc:
5835 raise InvalidConfigError(exc)
5836 # Get required details from the proxy
5837 proxy_type = get_proxy_type()
5838 if proxy_type == "esxdatacenter":
5839 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
5840 dc_ref = _get_proxy_target(service_instance)
5841 if not cluster:
5842 raise ArgumentValueError("'cluster' needs to be specified")
5843 elif proxy_type == "esxcluster":
5844 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
5845 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
5846 cluster = __salt__["esxcluster.get_details"]()["cluster"]
5847
5848 if cluster_dict.get("vsan") and not salt.utils.vsan.vsan_supported(
5849 service_instance
5850 ):
5851
5852 raise VMwareApiError("VSAN operations are not supported")
5853 si = service_instance
5854 cluster_spec = vim.ClusterConfigSpecEx()
5855 vsan_spec = None
5856 ha_config = None
5857 vsan_61 = None
5858 if cluster_dict.get("vsan"):
5859 # XXX The correct way of retrieving the VSAN data (on the if branch)
5860 # is not supported before 60u2 vcenter
5861 vcenter_info = salt.utils.vmware.get_service_info(si)
5862 if (
5863 float(vcenter_info.apiVersion) >= 6.0 and int(vcenter_info.build) >= 3634794
5864 ): # 60u2
5865 vsan_spec = vim.vsan.ReconfigSpec(modify=True)
5866 vsan_61 = False
5867 # We need to keep HA disabled and enable it afterwards
5868 if cluster_dict.get("ha", {}).get("enabled"):
5869 enable_ha = True
5870 ha_config = cluster_dict["ha"]
5871 del cluster_dict["ha"]
5872 else:
5873 vsan_61 = True
5874 # If VSAN is 6.1 the configuration of VSAN happens when configuring the
5875 # cluster via the regular endpoint
5876 _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec, vsan_61)
5877 salt.utils.vmware.create_cluster(dc_ref, cluster, cluster_spec)
5878 if not vsan_61:
5879 # Only available after VSAN 61
5880 if vsan_spec:
5881 cluster_ref = salt.utils.vmware.get_cluster(dc_ref, cluster)
5882 salt.utils.vsan.reconfigure_cluster_vsan(cluster_ref, vsan_spec)
5883 if enable_ha:
5884 # Set HA after VSAN has been configured
5885 _apply_cluster_dict(cluster_spec, {"ha": ha_config})
5886 salt.utils.vmware.update_cluster(cluster_ref, cluster_spec)
5887 # Set HA back on the object
5888 cluster_dict["ha"] = ha_config
5889 return {"create_cluster": True}
5890
5891
5892 @depends(HAS_PYVMOMI)
5893 @depends(HAS_JSONSCHEMA)
5894 @supports_proxies("esxcluster", "esxdatacenter")
5895 @gets_service_instance_via_proxy
5896 def update_cluster(cluster_dict, datacenter=None, cluster=None, service_instance=None):
5897 """
5898 Updates a cluster.
5899
5900 config_dict
5901 Dictionary with the config values of the new cluster.
5902
5903 datacenter
5904 Name of datacenter containing the cluster.
5905 Ignored if already contained by proxy details.
5906 Default value is None.
5907
5908 cluster
5909 Name of cluster.
5910 Ignored if already contained by proxy details.
5911 Default value is None.
5912
5913 service_instance
5914 Service instance (vim.ServiceInstance) of the vCenter.
5915 Default is None.
5916
5917 .. code-block:: bash
5918
5919 # esxdatacenter proxy
5920 salt '*' vsphere.update_cluster cluster_dict=$cluster_dict cluster=cl1
5921
5922 # esxcluster proxy
5923 salt '*' vsphere.update_cluster cluster_dict=$cluster_dict
5924
5925 """
5926 # Validate cluster dictionary
5927 schema = ESXClusterConfigSchema.serialize()
5928 try:
5929 jsonschema.validate(cluster_dict, schema)
5930 except jsonschema.exceptions.ValidationError as exc:
5931 raise InvalidConfigError(exc)
5932 # Get required details from the proxy
5933 proxy_type = get_proxy_type()
5934 if proxy_type == "esxdatacenter":
5935 datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"]
5936 dc_ref = _get_proxy_target(service_instance)
5937 if not cluster:
5938 raise ArgumentValueError("'cluster' needs to be specified")
5939 elif proxy_type == "esxcluster":
5940 datacenter = __salt__["esxcluster.get_details"]()["datacenter"]
5941 dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter)
5942 cluster = __salt__["esxcluster.get_details"]()["cluster"]
5943
5944 if cluster_dict.get("vsan") and not salt.utils.vsan.vsan_supported(
5945 service_instance
5946 ):
5947
5948 raise VMwareApiError("VSAN operations are not supported")
5949
5950 cluster_ref = salt.utils.vmware.get_cluster(dc_ref, cluster)
5951 cluster_spec = vim.ClusterConfigSpecEx()
5952 props = salt.utils.vmware.get_properties_of_managed_object(
5953 cluster_ref, properties=["configurationEx"]
5954 )
5955 # Copy elements we want to update to spec
5956 for p in ["dasConfig", "drsConfig"]:
5957 setattr(cluster_spec, p, getattr(props["configurationEx"], p))
5958 if props["configurationEx"].vsanConfigInfo:
5959 cluster_spec.vsanConfig = props["configurationEx"].vsanConfigInfo
5960 vsan_spec = None
5961 vsan_61 = None
5962 if cluster_dict.get("vsan"):
5963 # XXX The correct way of retrieving the VSAN data (on the if branch)
5964 # is not supported before 60u2 vcenter
5965 vcenter_info = salt.utils.vmware.get_service_info(service_instance)
5966 if (
5967 float(vcenter_info.apiVersion) >= 6.0 and int(vcenter_info.build) >= 3634794
5968 ): # 60u2
5969 vsan_61 = False
5970 vsan_info = salt.utils.vsan.get_cluster_vsan_info(cluster_ref)
5971 vsan_spec = vim.vsan.ReconfigSpec(modify=True)
5972 # Only interested in the vsanClusterConfig and the
5973 # dataEfficiencyConfig
5974 # vsan_spec.vsanClusterConfig = vsan_info
5975 vsan_spec.dataEfficiencyConfig = vsan_info.dataEfficiencyConfig
5976 vsan_info.dataEfficiencyConfig = None
5977 else:
5978 vsan_61 = True
5979
5980 _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec, vsan_61)
5981 # We try to reconfigure vsan first as it fails if HA is enabled so the
5982 # command will abort not having any side-effects
5983 # also if HA was previously disabled it can be enabled automatically if
5984 # desired
5985 if vsan_spec:
5986 log.trace("vsan_spec = {}".format(vsan_spec))
5987 salt.utils.vsan.reconfigure_cluster_vsan(cluster_ref, vsan_spec)
5988
5989 # We need to retrieve again the properties and reapply them
5990 # As the VSAN configuration has changed
5991 cluster_spec = vim.ClusterConfigSpecEx()
5992 props = salt.utils.vmware.get_properties_of_managed_object(
5993 cluster_ref, properties=["configurationEx"]
5994 )
5995 # Copy elements we want to update to spec
5996 for p in ["dasConfig", "drsConfig"]:
5997 setattr(cluster_spec, p, getattr(props["configurationEx"], p))
5998 if props["configurationEx"].vsanConfigInfo:
5999 cluster_spec.vsanConfig = props["configurationEx"].vsanConfigInfo
6000 # We only need to configure the cluster_spec, as if it were a vsan_61
6001 # cluster
6002 _apply_cluster_dict(cluster_spec, cluster_dict)
6003 salt.utils.vmware.update_cluster(cluster_ref, cluster_spec)
6004 return {"update_cluster": True}
6005
6006
6007 @depends(HAS_PYVMOMI)
6008 @supports_proxies("esxi", "esxcluster", "esxdatacenter")