1""" 2Connection library for VMware 3 4.. versionadded:: 2015.8.2 5 6This is a base library used by a number of VMware services such as VMware 7ESX, ESXi, and vCenter servers. 8 9:codeauthor: Nitin Madhok <nmadhok@g.clemson.edu> 10:codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstanley.com> 11 12Dependencies 13~~~~~~~~~~~~ 14 15- pyVmomi Python Module 16- ESXCLI: This dependency is only needed to use the ``esxcli`` function. No other 17 functions in this module rely on ESXCLI. 18 19pyVmomi 20------- 21 22PyVmomi can be installed via pip: 23 24.. code-block:: bash 25 26 pip install pyVmomi 27 28.. note:: 29 30 Version 6.0 of pyVmomi has some problems with SSL error handling on certain 31 versions of Python. If using version 6.0 of pyVmomi, Python 2.6, 32 Python 2.7.9, or newer must be present. This is due to an upstream dependency 33 in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the 34 version of Python is not in the supported range, you will need to install an 35 earlier version of pyVmomi. See `Issue #29537`_ for more information. 36 37.. _Issue #29537: https://github.com/saltstack/salt/issues/29537 38 39Based on the note above, to install an earlier version of pyVmomi than the 40version currently listed in PyPi, run the following: 41 42.. code-block:: bash 43 44 pip install pyVmomi==5.5.0.2014.1.1 45 46The 5.5.0.2014.1.1 is a known stable version that this original VMware utils file 47was developed against. 48 49ESXCLI 50------ 51 52This dependency is only needed to use the ``esxcli`` function. At the time of this 53writing, no other functions in this module rely on ESXCLI. 54 55The ESXCLI package is also referred to as the VMware vSphere CLI, or vCLI. VMware 56provides vCLI package installation instructions for `vSphere 5.5`_ and 57`vSphere 6.0`_. 58 59.. _vSphere 5.5: http://pubs.vmware.com/vsphere-55/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html 60.. _vSphere 6.0: http://pubs.vmware.com/vsphere-60/index.jsp#com.vmware.vcli.getstart.doc/cli_install.4.2.html 61 62Once all of the required dependencies are in place and the vCLI package is 63installed, you can check to see if you can connect to your ESXi host or vCenter 64server by running the following command: 65 66.. code-block:: bash 67 68 esxcli -s <host-location> -u <username> -p <password> system syslog config get 69 70If the connection was successful, ESXCLI was successfully installed on your system. 71You should see output related to the ESXi host's syslog configuration. 72 73""" 74 75import atexit 76import errno 77import logging 78import ssl 79import time 80from http.client import BadStatusLine 81 82import salt.exceptions 83import salt.modules.cmdmod 84import salt.utils.path 85import salt.utils.platform 86import salt.utils.stringutils 87 88# pylint: disable=no-name-in-module 89try: 90 from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub, SoapStubAdapter 91 from pyVmomi import vim, vmodl, VmomiSupport 92 93 HAS_PYVMOMI = True 94except ImportError: 95 HAS_PYVMOMI = False 96 97try: 98 from com.vmware.vapi.std.errors_client import Unauthenticated 99 from vmware.vapi.vsphere.client import create_vsphere_client 100 101 HAS_VSPHERE_SDK = True 102 103except ImportError: 104 HAS_VSPHERE_SDK = False 105# pylint: enable=no-name-in-module 106try: 107 import gssapi 108 import base64 109 110 HAS_GSSAPI = True 111except ImportError: 112 HAS_GSSAPI = False 113 114 115log = logging.getLogger(__name__) 116 117 118def __virtual__(): 119 """ 120 Only load if PyVmomi is installed. 121 """ 122 if HAS_PYVMOMI: 123 return True 124 125 return False, "Missing dependency: The salt.utils.vmware module requires pyVmomi." 126 127 128def esxcli( 129 host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, credstore=None 130): 131 """ 132 Shell out and call the specified esxcli command, parse the result 133 and return something sane. 134 135 :param host: ESXi or vCenter host to connect to 136 :param user: User to connect as, usually root 137 :param pwd: Password to connect with 138 :param port: TCP port 139 :param cmd: esxcli command and arguments 140 :param esxi_host: If `host` is a vCenter host, then esxi_host is the 141 ESXi machine on which to execute this command 142 :param credstore: Optional path to the credential store file 143 144 :return: Dictionary 145 """ 146 147 esx_cmd = salt.utils.path.which("esxcli") 148 if not esx_cmd: 149 log.error( 150 "Missing dependency: The salt.utils.vmware.esxcli function requires ESXCLI." 151 ) 152 return False 153 154 # Set default port and protocol if none are provided. 155 if port is None: 156 port = 443 157 if protocol is None: 158 protocol = "https" 159 160 if credstore: 161 esx_cmd += " --credstore '{}'".format(credstore) 162 163 if not esxi_host: 164 # Then we are connecting directly to an ESXi server, 165 # 'host' points at that server, and esxi_host is a reference to the 166 # ESXi instance we are manipulating 167 esx_cmd += " -s {} -u {} -p '{}' --protocol={} --portnumber={} {}".format( 168 host, user, pwd, protocol, port, cmd 169 ) 170 else: 171 esx_cmd += " -s {} -h {} -u {} -p '{}' --protocol={} --portnumber={} {}".format( 172 host, esxi_host, user, pwd, protocol, port, cmd 173 ) 174 175 ret = salt.modules.cmdmod.run_all(esx_cmd, output_loglevel="quiet") 176 177 return ret 178 179 180def get_vsphere_client( 181 server, username, password, session=None, verify_ssl=True, ca_bundle=None 182): 183 """ 184 Internal helper method to create an instance of the vSphere API client. 185 Please provide username and password to authenticate. 186 187 :param basestring server: 188 vCenter host name or IP address 189 :param basestring username: 190 Name of the user 191 :param basestring password: 192 Password of the user 193 :param Session session: 194 Request HTTP session instance. If not specified, one 195 is automatically created and used 196 :param boolean verify_ssl: 197 Verify the SSL certificate. Default: True 198 :param basestring ca_bundle: 199 Path to the ca bundle to use when verifying SSL certificates. 200 201 :returns: 202 Vsphere Client instance 203 :rtype: 204 :class:`vmware.vapi.vmc.client.VsphereClient` 205 """ 206 if not session: 207 # Create an https session to be used for a vSphere client 208 session = salt.utils.http.session(verify_ssl=verify_ssl, ca_bundle=ca_bundle) 209 client = None 210 try: 211 client = create_vsphere_client( 212 server=server, username=username, password=password, session=session 213 ) 214 except Unauthenticated as err: 215 log.trace(err) 216 return client 217 218 219def _get_service_instance( 220 host, 221 username, 222 password, 223 protocol, 224 port, 225 mechanism, 226 principal, 227 domain, 228 verify_ssl=True, 229): 230 """ 231 Internal method to authenticate with a vCenter server or ESX/ESXi host 232 and return the service instance object. 233 """ 234 log.trace("Retrieving new service instance") 235 token = None 236 if mechanism == "userpass": 237 if username is None: 238 raise salt.exceptions.CommandExecutionError( 239 "Login mechanism userpass was specified but the mandatory " 240 "parameter 'username' is missing" 241 ) 242 if password is None: 243 raise salt.exceptions.CommandExecutionError( 244 "Login mechanism userpass was specified but the mandatory " 245 "parameter 'password' is missing" 246 ) 247 elif mechanism == "sspi": 248 if principal is not None and domain is not None: 249 try: 250 token = get_gssapi_token(principal, host, domain) 251 except Exception as exc: # pylint: disable=broad-except 252 raise salt.exceptions.VMwareConnectionError(str(exc)) 253 else: 254 err_msg = ( 255 "Login mechanism '{}' was specified but the" 256 " mandatory parameters are missing".format(mechanism) 257 ) 258 raise salt.exceptions.CommandExecutionError(err_msg) 259 else: 260 raise salt.exceptions.CommandExecutionError( 261 "Unsupported mechanism: '{}'".format(mechanism) 262 ) 263 264 log.trace( 265 "Connecting using the '%s' mechanism, with username '%s'", 266 mechanism, 267 username, 268 ) 269 default_msg = ( 270 "Could not connect to host '{}'. " 271 "Please check the debug log for more information.".format(host) 272 ) 273 274 try: 275 if verify_ssl: 276 service_instance = SmartConnect( 277 host=host, 278 user=username, 279 pwd=password, 280 protocol=protocol, 281 port=port, 282 b64token=token, 283 mechanism=mechanism, 284 ) 285 except TypeError as exc: 286 if "unexpected keyword argument" in exc.message: 287 log.error( 288 "Initial connect to the VMware endpoint failed with %s", exc.message 289 ) 290 log.error( 291 "This may mean that a version of PyVmomi EARLIER than 6.0.0.2016.6 is" 292 " installed." 293 ) 294 log.error("We recommend updating to that version or later.") 295 raise 296 except Exception as exc: # pylint: disable=broad-except 297 # pyVmomi's SmartConnect() actually raises Exception in some cases. 298 if ( 299 isinstance(exc, vim.fault.HostConnectFault) 300 and "[SSL: CERTIFICATE_VERIFY_FAILED]" in exc.msg 301 ) or "[SSL: CERTIFICATE_VERIFY_FAILED]" in str(exc): 302 err_msg = ( 303 "Could not verify the SSL certificate. You can use " 304 "verify_ssl: False if you do not want to verify the " 305 "SSL certificate. This is not recommended as it is " 306 "considered insecure." 307 ) 308 else: 309 log.exception(exc) 310 err_msg = exc.msg if hasattr(exc, "msg") else default_msg 311 raise salt.exceptions.VMwareConnectionError(err_msg) 312 313 if not verify_ssl: 314 try: 315 service_instance = SmartConnect( 316 host=host, 317 user=username, 318 pwd=password, 319 protocol=protocol, 320 port=port, 321 sslContext=ssl._create_unverified_context(), 322 b64token=token, 323 mechanism=mechanism, 324 ) 325 except Exception as exc: # pylint: disable=broad-except 326 # pyVmomi's SmartConnect() actually raises Exception in some cases. 327 if "certificate verify failed" in str(exc): 328 context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) 329 context.verify_mode = ssl.CERT_NONE 330 try: 331 service_instance = SmartConnect( 332 host=host, 333 user=username, 334 pwd=password, 335 protocol=protocol, 336 port=port, 337 sslContext=context, 338 b64token=token, 339 mechanism=mechanism, 340 ) 341 except Exception as exc: # pylint: disable=broad-except 342 log.exception(exc) 343 err_msg = exc.msg if hasattr(exc, "msg") else str(exc) 344 raise salt.exceptions.VMwareConnectionError( 345 "Could not connect to host '{}': {}".format(host, err_msg) 346 ) 347 else: 348 err_msg = exc.msg if hasattr(exc, "msg") else default_msg 349 log.trace(exc) 350 raise salt.exceptions.VMwareConnectionError(err_msg) 351 352 atexit.register(Disconnect, service_instance) 353 return service_instance 354 355 356def get_customizationspec_ref(si, customization_spec_name): 357 """ 358 Get a reference to a VMware customization spec for the purposes of customizing a clone 359 360 si 361 ServiceInstance for the vSphere or ESXi server (see get_service_instance) 362 363 customization_spec_name 364 Name of the customization spec 365 366 """ 367 customization_spec_name = si.content.customizationSpecManager.GetCustomizationSpec( 368 name=customization_spec_name 369 ) 370 return customization_spec_name 371 372 373def get_mor_using_container_view(si, obj_type, obj_name): 374 """ 375 Get reference to an object of specified object type and name 376 377 si 378 ServiceInstance for the vSphere or ESXi server (see get_service_instance) 379 380 obj_type 381 Type of the object (vim.StoragePod, vim.Datastore, etc) 382 383 obj_name 384 Name of the object 385 386 """ 387 inventory = get_inventory(si) 388 container = inventory.viewManager.CreateContainerView( 389 inventory.rootFolder, [obj_type], True 390 ) 391 for item in container.view: 392 if item.name == obj_name: 393 return item 394 return None 395 396 397def get_service_instance( 398 host, 399 username=None, 400 password=None, 401 protocol=None, 402 port=None, 403 mechanism="userpass", 404 principal=None, 405 domain=None, 406 verify_ssl=True, 407): 408 """ 409 Authenticate with a vCenter server or ESX/ESXi host and return the service instance object. 410 411 host 412 The location of the vCenter server or ESX/ESXi host. 413 414 username 415 The username used to login to the vCenter server or ESX/ESXi host. 416 Required if mechanism is ``userpass`` 417 418 password 419 The password used to login to the vCenter server or ESX/ESXi host. 420 Required if mechanism is ``userpass`` 421 422 protocol 423 Optionally set to alternate protocol if the vCenter server or ESX/ESXi host is not 424 using the default protocol. Default protocol is ``https``. 425 426 port 427 Optionally set to alternate port if the vCenter server or ESX/ESXi host is not 428 using the default port. Default port is ``443``. 429 430 mechanism 431 pyVmomi connection mechanism. Can either be ``userpass`` or ``sspi``. 432 Default mechanism is ``userpass``. 433 434 principal 435 Kerberos service principal. Required if mechanism is ``sspi`` 436 437 domain 438 Kerberos user domain. Required if mechanism is ``sspi`` 439 440 verify_ssl 441 Verify the SSL certificate. Default: True 442 """ 443 444 if protocol is None: 445 protocol = "https" 446 if port is None: 447 port = 443 448 449 service_instance = GetSi() 450 if service_instance: 451 stub = GetStub() 452 if salt.utils.platform.is_proxy() or ( 453 hasattr(stub, "host") and stub.host != ":".join([host, str(port)]) 454 ): 455 # Proxies will fork and mess up the cached service instance. 456 # If this is a proxy or we are connecting to a different host 457 # invalidate the service instance to avoid a potential memory leak 458 # and reconnect 459 Disconnect(service_instance) 460 service_instance = None 461 462 if not service_instance: 463 service_instance = _get_service_instance( 464 host, 465 username, 466 password, 467 protocol, 468 port, 469 mechanism, 470 principal, 471 domain, 472 verify_ssl=verify_ssl, 473 ) 474 475 # Test if data can actually be retrieved or connection has gone stale 476 log.trace("Checking connection is still authenticated") 477 try: 478 service_instance.CurrentTime() 479 except vim.fault.NotAuthenticated: 480 log.trace("Session no longer authenticating. Reconnecting") 481 Disconnect(service_instance) 482 service_instance = _get_service_instance( 483 host, 484 username, 485 password, 486 protocol, 487 port, 488 mechanism, 489 principal, 490 domain, 491 verify_ssl=verify_ssl, 492 ) 493 except vim.fault.NoPermission as exc: 494 log.exception(exc) 495 raise salt.exceptions.VMwareApiError( 496 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 497 ) 498 except vim.fault.VimFault as exc: 499 log.exception(exc) 500 raise salt.exceptions.VMwareApiError(exc.msg) 501 except vmodl.RuntimeFault as exc: 502 log.exception(exc) 503 raise salt.exceptions.VMwareRuntimeError(exc.msg) 504 505 return service_instance 506 507 508def get_new_service_instance_stub(service_instance, path, ns=None, version=None): 509 """ 510 Returns a stub that points to a different path, 511 created from an existing connection. 512 513 service_instance 514 The Service Instance. 515 516 path 517 Path of the new stub. 518 519 ns 520 Namespace of the new stub. 521 Default value is None 522 523 version 524 Version of the new stub. 525 Default value is None. 526 """ 527 # For python 2.7.9 and later, the default SSL context has more strict 528 # connection handshaking rule. We may need turn off the hostname checking 529 # and the client side cert verification. 530 context = ssl.create_default_context() 531 context.check_hostname = False 532 context.verify_mode = ssl.CERT_NONE 533 534 stub = service_instance._stub 535 hostname = stub.host.split(":")[0] 536 session_cookie = stub.cookie.split('"')[1] 537 VmomiSupport.GetRequestContext()["vcSessionCookie"] = session_cookie 538 new_stub = SoapStubAdapter( 539 host=hostname, ns=ns, path=path, version=version, poolSize=0, sslContext=context 540 ) 541 new_stub.cookie = stub.cookie 542 return new_stub 543 544 545def get_service_instance_from_managed_object(mo_ref, name="<unnamed>"): 546 """ 547 Retrieves the service instance from a managed object. 548 549 me_ref 550 Reference to a managed object (of type vim.ManagedEntity). 551 552 name 553 Name of managed object. This field is optional. 554 """ 555 if not name: 556 name = mo_ref.name 557 log.trace("[%s] Retrieving service instance from managed object", name) 558 si = vim.ServiceInstance("ServiceInstance") 559 si._stub = mo_ref._stub 560 return si 561 562 563def disconnect(service_instance): 564 """ 565 Function that disconnects from the vCenter server or ESXi host 566 567 service_instance 568 The Service Instance from which to obtain managed object references. 569 """ 570 log.trace("Disconnecting") 571 try: 572 Disconnect(service_instance) 573 except vim.fault.NoPermission as exc: 574 log.exception(exc) 575 raise salt.exceptions.VMwareApiError( 576 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 577 ) 578 except vim.fault.VimFault as exc: 579 log.exception(exc) 580 raise salt.exceptions.VMwareApiError(exc.msg) 581 except vmodl.RuntimeFault as exc: 582 log.exception(exc) 583 raise salt.exceptions.VMwareRuntimeError(exc.msg) 584 585 586def is_connection_to_a_vcenter(service_instance): 587 """ 588 Function that returns True if the connection is made to a vCenter Server and 589 False if the connection is made to an ESXi host 590 591 service_instance 592 The Service Instance from which to obtain managed object references. 593 """ 594 try: 595 api_type = service_instance.content.about.apiType 596 except vim.fault.NoPermission as exc: 597 log.exception(exc) 598 raise salt.exceptions.VMwareApiError( 599 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 600 ) 601 except vim.fault.VimFault as exc: 602 log.exception(exc) 603 raise salt.exceptions.VMwareApiError(exc.msg) 604 except vmodl.RuntimeFault as exc: 605 log.exception(exc) 606 raise salt.exceptions.VMwareRuntimeError(exc.msg) 607 log.trace("api_type = %s", api_type) 608 if api_type == "VirtualCenter": 609 return True 610 elif api_type == "HostAgent": 611 return False 612 else: 613 raise salt.exceptions.VMwareApiError( 614 "Unexpected api type '{}' . Supported types: " 615 "'VirtualCenter/HostAgent'".format(api_type) 616 ) 617 618 619def get_service_info(service_instance): 620 """ 621 Returns information of the vCenter or ESXi host 622 623 service_instance 624 The Service Instance from which to obtain managed object references. 625 """ 626 try: 627 return service_instance.content.about 628 except vim.fault.NoPermission as exc: 629 log.exception(exc) 630 raise salt.exceptions.VMwareApiError( 631 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 632 ) 633 except vim.fault.VimFault as exc: 634 log.exception(exc) 635 raise salt.exceptions.VMwareApiError(exc.msg) 636 except vmodl.RuntimeFault as exc: 637 log.exception(exc) 638 raise salt.exceptions.VMwareRuntimeError(exc.msg) 639 640 641def _get_dvs(service_instance, dvs_name): 642 """ 643 Return a reference to a Distributed Virtual Switch object. 644 645 :param service_instance: PyVmomi service instance 646 :param dvs_name: Name of DVS to return 647 :return: A PyVmomi DVS object 648 """ 649 switches = list_dvs(service_instance) 650 if dvs_name in switches: 651 inventory = get_inventory(service_instance) 652 container = inventory.viewManager.CreateContainerView( 653 inventory.rootFolder, [vim.DistributedVirtualSwitch], True 654 ) 655 for item in container.view: 656 if item.name == dvs_name: 657 return item 658 659 return None 660 661 662def _get_pnics(host_reference): 663 """ 664 Helper function that returns a list of PhysicalNics and their information. 665 """ 666 return host_reference.config.network.pnic 667 668 669def _get_vnics(host_reference): 670 """ 671 Helper function that returns a list of VirtualNics and their information. 672 """ 673 return host_reference.config.network.vnic 674 675 676def _get_vnic_manager(host_reference): 677 """ 678 Helper function that returns a list of Virtual NicManagers 679 and their information. 680 """ 681 return host_reference.configManager.virtualNicManager 682 683 684def _get_dvs_portgroup(dvs, portgroup_name): 685 """ 686 Return a portgroup object corresponding to the portgroup name on the dvs 687 688 :param dvs: DVS object 689 :param portgroup_name: Name of portgroup to return 690 :return: Portgroup object 691 """ 692 for portgroup in dvs.portgroup: 693 if portgroup.name == portgroup_name: 694 return portgroup 695 696 return None 697 698 699def _get_dvs_uplink_portgroup(dvs, portgroup_name): 700 """ 701 Return a portgroup object corresponding to the portgroup name on the dvs 702 703 :param dvs: DVS object 704 :param portgroup_name: Name of portgroup to return 705 :return: Portgroup object 706 """ 707 for portgroup in dvs.portgroup: 708 if portgroup.name == portgroup_name: 709 return portgroup 710 711 return None 712 713 714def get_gssapi_token(principal, host, domain): 715 """ 716 Get the gssapi token for Kerberos connection 717 718 principal 719 The service principal 720 host 721 Host url where we would like to authenticate 722 domain 723 Kerberos user domain 724 """ 725 726 if not HAS_GSSAPI: 727 raise ImportError("The gssapi library is not imported.") 728 729 service = "{}/{}@{}".format(principal, host, domain) 730 log.debug("Retrieving gsspi token for service %s", service) 731 service_name = gssapi.Name(service, gssapi.C_NT_USER_NAME) 732 ctx = gssapi.InitContext(service_name) 733 in_token = None 734 while not ctx.established: 735 out_token = ctx.step(in_token) 736 if out_token: 737 return base64.b64encode(salt.utils.stringutils.to_bytes(out_token)) 738 if ctx.established: 739 break 740 if not in_token: 741 raise salt.exceptions.CommandExecutionError( 742 "Can't receive token, no response from server" 743 ) 744 raise salt.exceptions.CommandExecutionError( 745 "Context established, but didn't receive token" 746 ) 747 748 749def get_hardware_grains(service_instance): 750 """ 751 Return hardware info for standard minion grains if the service_instance is a HostAgent type 752 753 service_instance 754 The service instance object to get hardware info for 755 756 .. versionadded:: 2016.11.0 757 """ 758 hw_grain_data = {} 759 if get_inventory(service_instance).about.apiType == "HostAgent": 760 view = service_instance.content.viewManager.CreateContainerView( 761 service_instance.RetrieveContent().rootFolder, [vim.HostSystem], True 762 ) 763 if view and view.view: 764 hw_grain_data["manufacturer"] = view.view[0].hardware.systemInfo.vendor 765 hw_grain_data["productname"] = view.view[0].hardware.systemInfo.model 766 767 for _data in view.view[0].hardware.systemInfo.otherIdentifyingInfo: 768 if _data.identifierType.key == "ServiceTag": 769 hw_grain_data["serialnumber"] = _data.identifierValue 770 771 hw_grain_data["osfullname"] = view.view[0].summary.config.product.fullName 772 hw_grain_data["osmanufacturer"] = view.view[0].summary.config.product.vendor 773 hw_grain_data["osrelease"] = view.view[0].summary.config.product.version 774 hw_grain_data["osbuild"] = view.view[0].summary.config.product.build 775 hw_grain_data["os_family"] = view.view[0].summary.config.product.name 776 hw_grain_data["os"] = view.view[0].summary.config.product.name 777 hw_grain_data["mem_total"] = view.view[0].hardware.memorySize / 1024 / 1024 778 hw_grain_data["biosversion"] = view.view[0].hardware.biosInfo.biosVersion 779 hw_grain_data["biosreleasedate"] = ( 780 view.view[0].hardware.biosInfo.releaseDate.date().strftime("%m/%d/%Y") 781 ) 782 hw_grain_data["cpu_model"] = view.view[0].hardware.cpuPkg[0].description 783 hw_grain_data["kernel"] = view.view[0].summary.config.product.productLineId 784 hw_grain_data["num_cpu_sockets"] = view.view[ 785 0 786 ].hardware.cpuInfo.numCpuPackages 787 hw_grain_data["num_cpu_cores"] = view.view[0].hardware.cpuInfo.numCpuCores 788 hw_grain_data["num_cpus"] = ( 789 hw_grain_data["num_cpu_sockets"] * hw_grain_data["num_cpu_cores"] 790 ) 791 hw_grain_data["ip_interfaces"] = {} 792 hw_grain_data["ip4_interfaces"] = {} 793 hw_grain_data["ip6_interfaces"] = {} 794 hw_grain_data["hwaddr_interfaces"] = {} 795 for _vnic in view.view[0].configManager.networkSystem.networkConfig.vnic: 796 hw_grain_data["ip_interfaces"][_vnic.device] = [] 797 hw_grain_data["ip4_interfaces"][_vnic.device] = [] 798 hw_grain_data["ip6_interfaces"][_vnic.device] = [] 799 800 hw_grain_data["ip_interfaces"][_vnic.device].append( 801 _vnic.spec.ip.ipAddress 802 ) 803 hw_grain_data["ip4_interfaces"][_vnic.device].append( 804 _vnic.spec.ip.ipAddress 805 ) 806 if _vnic.spec.ip.ipV6Config: 807 hw_grain_data["ip6_interfaces"][_vnic.device].append( 808 _vnic.spec.ip.ipV6Config.ipV6Address 809 ) 810 hw_grain_data["hwaddr_interfaces"][_vnic.device] = _vnic.spec.mac 811 hw_grain_data["host"] = view.view[ 812 0 813 ].configManager.networkSystem.dnsConfig.hostName 814 hw_grain_data["domain"] = view.view[ 815 0 816 ].configManager.networkSystem.dnsConfig.domainName 817 hw_grain_data["fqdn"] = "{}{}{}".format( 818 view.view[0].configManager.networkSystem.dnsConfig.hostName, 819 ( 820 "." 821 if view.view[0].configManager.networkSystem.dnsConfig.domainName 822 else "" 823 ), 824 view.view[0].configManager.networkSystem.dnsConfig.domainName, 825 ) 826 827 for _pnic in view.view[0].configManager.networkSystem.networkInfo.pnic: 828 hw_grain_data["hwaddr_interfaces"][_pnic.device] = _pnic.mac 829 830 hw_grain_data["timezone"] = view.view[ 831 0 832 ].configManager.dateTimeSystem.dateTimeInfo.timeZone.name 833 view = None 834 return hw_grain_data 835 836 837def get_inventory(service_instance): 838 """ 839 Return the inventory of a Service Instance Object. 840 841 service_instance 842 The Service Instance Object for which to obtain inventory. 843 """ 844 return service_instance.RetrieveContent() 845 846 847def get_root_folder(service_instance): 848 """ 849 Returns the root folder of a vCenter. 850 851 service_instance 852 The Service Instance Object for which to obtain the root folder. 853 """ 854 try: 855 log.trace("Retrieving root folder") 856 return service_instance.RetrieveContent().rootFolder 857 except vim.fault.NoPermission as exc: 858 log.exception(exc) 859 raise salt.exceptions.VMwareApiError( 860 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 861 ) 862 except vim.fault.VimFault as exc: 863 log.exception(exc) 864 raise salt.exceptions.VMwareApiError(exc.msg) 865 except vmodl.RuntimeFault as exc: 866 log.exception(exc) 867 raise salt.exceptions.VMwareRuntimeError(exc.msg) 868 869 870def get_content( 871 service_instance, 872 obj_type, 873 property_list=None, 874 container_ref=None, 875 traversal_spec=None, 876 local_properties=False, 877): 878 """ 879 Returns the content of the specified type of object for a Service Instance. 880 881 For more information, please see: 882 http://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.wssdk.pg.doc_50%2FPG_Ch5_PropertyCollector.7.6.html 883 884 service_instance 885 The Service Instance from which to obtain content. 886 887 obj_type 888 The type of content to obtain. 889 890 property_list 891 An optional list of object properties to used to return even more filtered content results. 892 893 container_ref 894 An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter, 895 ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory 896 rootFolder. 897 898 traversal_spec 899 An optional TraversalSpec to be used instead of the standard 900 ``Traverse All`` spec. 901 902 local_properties 903 Flag specifying whether the properties to be retrieved are local to the 904 container. If that is the case, the traversal spec needs to be None. 905 """ 906 # Start at the rootFolder if container starting point not specified 907 if not container_ref: 908 container_ref = get_root_folder(service_instance) 909 910 # By default, the object reference used as the starting poing for the filter 911 # is the container_ref passed in the function 912 obj_ref = container_ref 913 local_traversal_spec = False 914 if not traversal_spec and not local_properties: 915 local_traversal_spec = True 916 # We don't have a specific traversal spec override so we are going to 917 # get everything using a container view 918 try: 919 obj_ref = service_instance.content.viewManager.CreateContainerView( 920 container_ref, [obj_type], True 921 ) 922 except vim.fault.NoPermission as exc: 923 log.exception(exc) 924 raise salt.exceptions.VMwareApiError( 925 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 926 ) 927 except vim.fault.VimFault as exc: 928 log.exception(exc) 929 raise salt.exceptions.VMwareApiError(exc.msg) 930 except vmodl.RuntimeFault as exc: 931 log.exception(exc) 932 raise salt.exceptions.VMwareRuntimeError(exc.msg) 933 934 # Create 'Traverse All' traversal spec to determine the path for 935 # collection 936 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 937 name="traverseEntities", 938 path="view", 939 skip=False, 940 type=vim.view.ContainerView, 941 ) 942 943 # Create property spec to determine properties to be retrieved 944 property_spec = vmodl.query.PropertyCollector.PropertySpec( 945 type=obj_type, all=True if not property_list else False, pathSet=property_list 946 ) 947 948 # Create object spec to navigate content 949 obj_spec = vmodl.query.PropertyCollector.ObjectSpec( 950 obj=obj_ref, 951 skip=True if not local_properties else False, 952 selectSet=[traversal_spec] if not local_properties else None, 953 ) 954 955 # Create a filter spec and specify object, property spec in it 956 filter_spec = vmodl.query.PropertyCollector.FilterSpec( 957 objectSet=[obj_spec], 958 propSet=[property_spec], 959 reportMissingObjectsInResults=False, 960 ) 961 962 # Retrieve the contents 963 try: 964 content = service_instance.content.propertyCollector.RetrieveContents( 965 [filter_spec] 966 ) 967 except vim.fault.NoPermission as exc: 968 log.exception(exc) 969 raise salt.exceptions.VMwareApiError( 970 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 971 ) 972 except vim.fault.VimFault as exc: 973 log.exception(exc) 974 raise salt.exceptions.VMwareApiError(exc.msg) 975 except vmodl.RuntimeFault as exc: 976 log.exception(exc) 977 raise salt.exceptions.VMwareRuntimeError(exc.msg) 978 979 # Destroy the object view 980 if local_traversal_spec: 981 try: 982 obj_ref.Destroy() 983 except vim.fault.NoPermission as exc: 984 log.exception(exc) 985 raise salt.exceptions.VMwareApiError( 986 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 987 ) 988 except vim.fault.VimFault as exc: 989 log.exception(exc) 990 raise salt.exceptions.VMwareApiError(exc.msg) 991 except vmodl.RuntimeFault as exc: 992 log.exception(exc) 993 raise salt.exceptions.VMwareRuntimeError(exc.msg) 994 995 return content 996 997 998def get_mor_by_property( 999 service_instance, 1000 object_type, 1001 property_value, 1002 property_name="name", 1003 container_ref=None, 1004): 1005 """ 1006 Returns the first managed object reference having the specified property value. 1007 1008 service_instance 1009 The Service Instance from which to obtain managed object references. 1010 1011 object_type 1012 The type of content for which to obtain managed object references. 1013 1014 property_value 1015 The name of the property for which to obtain the managed object reference. 1016 1017 property_name 1018 An object property used to return the specified object reference results. Defaults to ``name``. 1019 1020 container_ref 1021 An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter, 1022 ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory 1023 rootFolder. 1024 """ 1025 # Get list of all managed object references with specified property 1026 object_list = get_mors_with_properties( 1027 service_instance, 1028 object_type, 1029 property_list=[property_name], 1030 container_ref=container_ref, 1031 ) 1032 1033 for obj in object_list: 1034 obj_id = str(obj.get("object", "")).strip("'\"") 1035 if obj[property_name] == property_value or property_value == obj_id: 1036 return obj["object"] 1037 1038 return None 1039 1040 1041def get_mors_with_properties( 1042 service_instance, 1043 object_type, 1044 property_list=None, 1045 container_ref=None, 1046 traversal_spec=None, 1047 local_properties=False, 1048): 1049 """ 1050 Returns a list containing properties and managed object references for the managed object. 1051 1052 service_instance 1053 The Service Instance from which to obtain managed object references. 1054 1055 object_type 1056 The type of content for which to obtain managed object references. 1057 1058 property_list 1059 An optional list of object properties used to return even more filtered managed object reference results. 1060 1061 container_ref 1062 An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter, 1063 ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory 1064 rootFolder. 1065 1066 traversal_spec 1067 An optional TraversalSpec to be used instead of the standard 1068 ``Traverse All`` spec 1069 1070 local_properties 1071 Flag specigying whether the properties to be retrieved are local to the 1072 container. If that is the case, the traversal spec needs to be None. 1073 """ 1074 # Get all the content 1075 content_args = [service_instance, object_type] 1076 content_kwargs = { 1077 "property_list": property_list, 1078 "container_ref": container_ref, 1079 "traversal_spec": traversal_spec, 1080 "local_properties": local_properties, 1081 } 1082 try: 1083 content = get_content(*content_args, **content_kwargs) 1084 except BadStatusLine: 1085 content = get_content(*content_args, **content_kwargs) 1086 except OSError as exc: 1087 if exc.errno != errno.EPIPE: 1088 raise 1089 content = get_content(*content_args, **content_kwargs) 1090 1091 object_list = [] 1092 for obj in content: 1093 properties = {} 1094 for prop in obj.propSet: 1095 properties[prop.name] = prop.val 1096 properties["object"] = obj.obj 1097 object_list.append(properties) 1098 log.trace("Retrieved %s objects", len(object_list)) 1099 return object_list 1100 1101 1102def get_properties_of_managed_object(mo_ref, properties): 1103 """ 1104 Returns specific properties of a managed object, retrieved in an 1105 optimally. 1106 1107 mo_ref 1108 The managed object reference. 1109 1110 properties 1111 List of properties of the managed object to retrieve. 1112 """ 1113 service_instance = get_service_instance_from_managed_object(mo_ref) 1114 log.trace("Retrieving name of %s", type(mo_ref).__name__) 1115 try: 1116 items = get_mors_with_properties( 1117 service_instance, 1118 type(mo_ref), 1119 container_ref=mo_ref, 1120 property_list=["name"], 1121 local_properties=True, 1122 ) 1123 mo_name = items[0]["name"] 1124 except vmodl.query.InvalidProperty: 1125 mo_name = "<unnamed>" 1126 log.trace( 1127 "Retrieving properties '%s' of %s '%s'", 1128 properties, 1129 type(mo_ref).__name__, 1130 mo_name, 1131 ) 1132 items = get_mors_with_properties( 1133 service_instance, 1134 type(mo_ref), 1135 container_ref=mo_ref, 1136 property_list=properties, 1137 local_properties=True, 1138 ) 1139 if not items: 1140 raise salt.exceptions.VMwareApiError( 1141 "Properties of managed object '{}' weren't retrieved".format(mo_name) 1142 ) 1143 return items[0] 1144 1145 1146def get_managed_object_name(mo_ref): 1147 """ 1148 Returns the name of a managed object. 1149 If the name wasn't found, it returns None. 1150 1151 mo_ref 1152 The managed object reference. 1153 """ 1154 props = get_properties_of_managed_object(mo_ref, ["name"]) 1155 return props.get("name") 1156 1157 1158def get_network_adapter_type(adapter_type): 1159 """ 1160 Return the network adapter type. 1161 1162 adpater_type 1163 The adapter type from which to obtain the network adapter type. 1164 """ 1165 if adapter_type == "vmxnet": 1166 return vim.vm.device.VirtualVmxnet() 1167 elif adapter_type == "vmxnet2": 1168 return vim.vm.device.VirtualVmxnet2() 1169 elif adapter_type == "vmxnet3": 1170 return vim.vm.device.VirtualVmxnet3() 1171 elif adapter_type == "e1000": 1172 return vim.vm.device.VirtualE1000() 1173 elif adapter_type == "e1000e": 1174 return vim.vm.device.VirtualE1000e() 1175 1176 raise ValueError("An unknown network adapter object type name.") 1177 1178 1179def get_network_adapter_object_type(adapter_object): 1180 """ 1181 Returns the network adapter type. 1182 1183 adapter_object 1184 The adapter object from which to obtain the network adapter type. 1185 """ 1186 if isinstance(adapter_object, vim.vm.device.VirtualVmxnet2): 1187 return "vmxnet2" 1188 if isinstance(adapter_object, vim.vm.device.VirtualVmxnet3): 1189 return "vmxnet3" 1190 if isinstance(adapter_object, vim.vm.device.VirtualVmxnet): 1191 return "vmxnet" 1192 if isinstance(adapter_object, vim.vm.device.VirtualE1000e): 1193 return "e1000e" 1194 if isinstance(adapter_object, vim.vm.device.VirtualE1000): 1195 return "e1000" 1196 1197 raise ValueError("An unknown network adapter object type.") 1198 1199 1200def get_dvss(dc_ref, dvs_names=None, get_all_dvss=False): 1201 """ 1202 Returns distributed virtual switches (DVSs) in a datacenter. 1203 1204 dc_ref 1205 The parent datacenter reference. 1206 1207 dvs_names 1208 The names of the DVSs to return. Default is None. 1209 1210 get_all_dvss 1211 Return all DVSs in the datacenter. Default is False. 1212 """ 1213 dc_name = get_managed_object_name(dc_ref) 1214 log.trace( 1215 "Retrieving DVSs in datacenter '%s', dvs_names='%s', get_all_dvss=%s", 1216 dc_name, 1217 ",".join(dvs_names) if dvs_names else None, 1218 get_all_dvss, 1219 ) 1220 properties = ["name"] 1221 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1222 path="networkFolder", 1223 skip=True, 1224 type=vim.Datacenter, 1225 selectSet=[ 1226 vmodl.query.PropertyCollector.TraversalSpec( 1227 path="childEntity", skip=False, type=vim.Folder 1228 ) 1229 ], 1230 ) 1231 service_instance = get_service_instance_from_managed_object(dc_ref) 1232 items = [ 1233 i["object"] 1234 for i in get_mors_with_properties( 1235 service_instance, 1236 vim.DistributedVirtualSwitch, 1237 container_ref=dc_ref, 1238 property_list=properties, 1239 traversal_spec=traversal_spec, 1240 ) 1241 if get_all_dvss or (dvs_names and i["name"] in dvs_names) 1242 ] 1243 return items 1244 1245 1246def get_network_folder(dc_ref): 1247 """ 1248 Retrieves the network folder of a datacenter 1249 """ 1250 dc_name = get_managed_object_name(dc_ref) 1251 log.trace("Retrieving network folder in datacenter '%s'", dc_name) 1252 service_instance = get_service_instance_from_managed_object(dc_ref) 1253 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1254 path="networkFolder", skip=False, type=vim.Datacenter 1255 ) 1256 entries = get_mors_with_properties( 1257 service_instance, 1258 vim.Folder, 1259 container_ref=dc_ref, 1260 property_list=["name"], 1261 traversal_spec=traversal_spec, 1262 ) 1263 if not entries: 1264 raise salt.exceptions.VMwareObjectRetrievalError( 1265 "Network folder in datacenter '{}' wasn't retrieved".format(dc_name) 1266 ) 1267 return entries[0]["object"] 1268 1269 1270def create_dvs(dc_ref, dvs_name, dvs_create_spec=None): 1271 """ 1272 Creates a distributed virtual switches (DVS) in a datacenter. 1273 Returns the reference to the newly created distributed virtual switch. 1274 1275 dc_ref 1276 The parent datacenter reference. 1277 1278 dvs_name 1279 The name of the DVS to create. 1280 1281 dvs_create_spec 1282 The DVS spec (vim.DVSCreateSpec) to use when creating the DVS. 1283 Default is None. 1284 """ 1285 dc_name = get_managed_object_name(dc_ref) 1286 log.trace("Creating DVS '%s' in datacenter '%s'", dvs_name, dc_name) 1287 if not dvs_create_spec: 1288 dvs_create_spec = vim.DVSCreateSpec() 1289 if not dvs_create_spec.configSpec: 1290 dvs_create_spec.configSpec = vim.VMwareDVSConfigSpec() 1291 dvs_create_spec.configSpec.name = dvs_name 1292 netw_folder_ref = get_network_folder(dc_ref) 1293 try: 1294 task = netw_folder_ref.CreateDVS_Task(dvs_create_spec) 1295 except vim.fault.NoPermission as exc: 1296 log.exception(exc) 1297 raise salt.exceptions.VMwareApiError( 1298 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1299 ) 1300 except vim.fault.VimFault as exc: 1301 log.exception(exc) 1302 raise salt.exceptions.VMwareApiError(exc.msg) 1303 except vmodl.RuntimeFault as exc: 1304 log.exception(exc) 1305 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1306 wait_for_task(task, dvs_name, str(task.__class__)) 1307 1308 1309def update_dvs(dvs_ref, dvs_config_spec): 1310 """ 1311 Updates a distributed virtual switch with the config_spec. 1312 1313 dvs_ref 1314 The DVS reference. 1315 1316 dvs_config_spec 1317 The updated config spec (vim.VMwareDVSConfigSpec) to be applied to 1318 the DVS. 1319 """ 1320 dvs_name = get_managed_object_name(dvs_ref) 1321 log.trace("Updating dvs '%s'", dvs_name) 1322 try: 1323 task = dvs_ref.ReconfigureDvs_Task(dvs_config_spec) 1324 except vim.fault.NoPermission as exc: 1325 log.exception(exc) 1326 raise salt.exceptions.VMwareApiError( 1327 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1328 ) 1329 except vim.fault.VimFault as exc: 1330 log.exception(exc) 1331 raise salt.exceptions.VMwareApiError(exc.msg) 1332 except vmodl.RuntimeFault as exc: 1333 log.exception(exc) 1334 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1335 wait_for_task(task, dvs_name, str(task.__class__)) 1336 1337 1338def set_dvs_network_resource_management_enabled(dvs_ref, enabled): 1339 """ 1340 Sets whether NIOC is enabled on a DVS. 1341 1342 dvs_ref 1343 The DVS reference. 1344 1345 enabled 1346 Flag specifying whether NIOC is enabled. 1347 """ 1348 dvs_name = get_managed_object_name(dvs_ref) 1349 log.trace( 1350 "Setting network resource management enable to %s on dvs '%s'", 1351 enabled, 1352 dvs_name, 1353 ) 1354 try: 1355 dvs_ref.EnableNetworkResourceManagement(enable=enabled) 1356 except vim.fault.NoPermission as exc: 1357 log.exception(exc) 1358 raise salt.exceptions.VMwareApiError( 1359 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1360 ) 1361 except vim.fault.VimFault as exc: 1362 log.exception(exc) 1363 raise salt.exceptions.VMwareApiError(exc.msg) 1364 except vmodl.RuntimeFault as exc: 1365 log.exception(exc) 1366 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1367 1368 1369def get_dvportgroups(parent_ref, portgroup_names=None, get_all_portgroups=False): 1370 """ 1371 Returns distributed virtual porgroups (dvportgroups). 1372 The parent object can be either a datacenter or a dvs. 1373 1374 parent_ref 1375 The parent object reference. Can be either a datacenter or a dvs. 1376 1377 portgroup_names 1378 The names of the dvss to return. Default is None. 1379 1380 get_all_portgroups 1381 Return all portgroups in the parent. Default is False. 1382 """ 1383 if not isinstance(parent_ref, (vim.Datacenter, vim.DistributedVirtualSwitch)): 1384 raise salt.exceptions.ArgumentValueError( 1385 "Parent has to be either a datacenter, or a distributed virtual switch" 1386 ) 1387 parent_name = get_managed_object_name(parent_ref) 1388 log.trace( 1389 "Retrieving portgroup in %s '%s', portgroups_names='%s', get_all_portgroups=%s", 1390 type(parent_ref).__name__, 1391 parent_name, 1392 ",".join(portgroup_names) if portgroup_names else None, 1393 get_all_portgroups, 1394 ) 1395 properties = ["name"] 1396 if isinstance(parent_ref, vim.Datacenter): 1397 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1398 path="networkFolder", 1399 skip=True, 1400 type=vim.Datacenter, 1401 selectSet=[ 1402 vmodl.query.PropertyCollector.TraversalSpec( 1403 path="childEntity", skip=False, type=vim.Folder 1404 ) 1405 ], 1406 ) 1407 else: # parent is distributed virtual switch 1408 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1409 path="portgroup", skip=False, type=vim.DistributedVirtualSwitch 1410 ) 1411 1412 service_instance = get_service_instance_from_managed_object(parent_ref) 1413 items = [ 1414 i["object"] 1415 for i in get_mors_with_properties( 1416 service_instance, 1417 vim.DistributedVirtualPortgroup, 1418 container_ref=parent_ref, 1419 property_list=properties, 1420 traversal_spec=traversal_spec, 1421 ) 1422 if get_all_portgroups or (portgroup_names and i["name"] in portgroup_names) 1423 ] 1424 return items 1425 1426 1427def get_uplink_dvportgroup(dvs_ref): 1428 """ 1429 Returns the uplink distributed virtual portgroup of a distributed virtual 1430 switch (dvs) 1431 1432 dvs_ref 1433 The dvs reference 1434 """ 1435 dvs_name = get_managed_object_name(dvs_ref) 1436 log.trace("Retrieving uplink portgroup of dvs '%s'", dvs_name) 1437 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1438 path="portgroup", skip=False, type=vim.DistributedVirtualSwitch 1439 ) 1440 service_instance = get_service_instance_from_managed_object(dvs_ref) 1441 items = [ 1442 entry["object"] 1443 for entry in get_mors_with_properties( 1444 service_instance, 1445 vim.DistributedVirtualPortgroup, 1446 container_ref=dvs_ref, 1447 property_list=["tag"], 1448 traversal_spec=traversal_spec, 1449 ) 1450 if entry["tag"] and [t for t in entry["tag"] if t.key == "SYSTEM/DVS.UPLINKPG"] 1451 ] 1452 if not items: 1453 raise salt.exceptions.VMwareObjectRetrievalError( 1454 "Uplink portgroup of DVS '{}' wasn't found".format(dvs_name) 1455 ) 1456 return items[0] 1457 1458 1459def create_dvportgroup(dvs_ref, spec): 1460 """ 1461 Creates a distributed virtual portgroup on a distributed virtual switch 1462 (dvs) 1463 1464 dvs_ref 1465 The dvs reference 1466 1467 spec 1468 Portgroup spec (vim.DVPortgroupConfigSpec) 1469 """ 1470 dvs_name = get_managed_object_name(dvs_ref) 1471 log.trace("Adding portgroup %s to dvs '%s'", spec.name, dvs_name) 1472 log.trace("spec = %s", spec) 1473 try: 1474 task = dvs_ref.CreateDVPortgroup_Task(spec) 1475 except vim.fault.NoPermission as exc: 1476 log.exception(exc) 1477 raise salt.exceptions.VMwareApiError( 1478 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1479 ) 1480 except vim.fault.VimFault as exc: 1481 log.exception(exc) 1482 raise salt.exceptions.VMwareApiError(exc.msg) 1483 except vmodl.RuntimeFault as exc: 1484 log.exception(exc) 1485 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1486 wait_for_task(task, dvs_name, str(task.__class__)) 1487 1488 1489def update_dvportgroup(portgroup_ref, spec): 1490 """ 1491 Updates a distributed virtual portgroup 1492 1493 portgroup_ref 1494 The portgroup reference 1495 1496 spec 1497 Portgroup spec (vim.DVPortgroupConfigSpec) 1498 """ 1499 pg_name = get_managed_object_name(portgroup_ref) 1500 log.trace("Updating portgrouo %s", pg_name) 1501 try: 1502 task = portgroup_ref.ReconfigureDVPortgroup_Task(spec) 1503 except vim.fault.NoPermission as exc: 1504 log.exception(exc) 1505 raise salt.exceptions.VMwareApiError( 1506 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1507 ) 1508 except vim.fault.VimFault as exc: 1509 log.exception(exc) 1510 raise salt.exceptions.VMwareApiError(exc.msg) 1511 except vmodl.RuntimeFault as exc: 1512 log.exception(exc) 1513 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1514 wait_for_task(task, pg_name, str(task.__class__)) 1515 1516 1517def remove_dvportgroup(portgroup_ref): 1518 """ 1519 Removes a distributed virtual portgroup 1520 1521 portgroup_ref 1522 The portgroup reference 1523 """ 1524 pg_name = get_managed_object_name(portgroup_ref) 1525 log.trace("Removing portgroup %s", pg_name) 1526 try: 1527 task = portgroup_ref.Destroy_Task() 1528 except vim.fault.NoPermission as exc: 1529 log.exception(exc) 1530 raise salt.exceptions.VMwareApiError( 1531 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1532 ) 1533 except vim.fault.VimFault as exc: 1534 log.exception(exc) 1535 raise salt.exceptions.VMwareApiError(exc.msg) 1536 except vmodl.RuntimeFault as exc: 1537 log.exception(exc) 1538 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1539 wait_for_task(task, pg_name, str(task.__class__)) 1540 1541 1542def get_networks(parent_ref, network_names=None, get_all_networks=False): 1543 """ 1544 Returns networks of standard switches. 1545 The parent object can be a datacenter. 1546 1547 parent_ref 1548 The parent object reference. A datacenter object. 1549 1550 network_names 1551 The name of the standard switch networks. Default is None. 1552 1553 get_all_networks 1554 Boolean indicates whether to return all networks in the parent. 1555 Default is False. 1556 """ 1557 1558 if not isinstance(parent_ref, vim.Datacenter): 1559 raise salt.exceptions.ArgumentValueError("Parent has to be a datacenter.") 1560 parent_name = get_managed_object_name(parent_ref) 1561 log.trace( 1562 "Retrieving network from %s '%s', network_names='%s', get_all_networks=%s", 1563 type(parent_ref).__name__, 1564 parent_name, 1565 ",".join(network_names) if network_names else None, 1566 get_all_networks, 1567 ) 1568 properties = ["name"] 1569 service_instance = get_service_instance_from_managed_object(parent_ref) 1570 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 1571 path="networkFolder", 1572 skip=True, 1573 type=vim.Datacenter, 1574 selectSet=[ 1575 vmodl.query.PropertyCollector.TraversalSpec( 1576 path="childEntity", skip=False, type=vim.Folder 1577 ) 1578 ], 1579 ) 1580 items = [ 1581 i["object"] 1582 for i in get_mors_with_properties( 1583 service_instance, 1584 vim.Network, 1585 container_ref=parent_ref, 1586 property_list=properties, 1587 traversal_spec=traversal_spec, 1588 ) 1589 if get_all_networks or (network_names and i["name"] in network_names) 1590 ] 1591 return items 1592 1593 1594def list_objects(service_instance, vim_object, properties=None): 1595 """ 1596 Returns a simple list of objects from a given service instance. 1597 1598 service_instance 1599 The Service Instance for which to obtain a list of objects. 1600 1601 object_type 1602 The type of content for which to obtain information. 1603 1604 properties 1605 An optional list of object properties used to return reference results. 1606 If not provided, defaults to ``name``. 1607 """ 1608 if properties is None: 1609 properties = ["name"] 1610 1611 items = [] 1612 item_list = get_mors_with_properties(service_instance, vim_object, properties) 1613 for item in item_list: 1614 items.append(item["name"]) 1615 return items 1616 1617 1618def get_license_manager(service_instance): 1619 """ 1620 Returns the license manager. 1621 1622 service_instance 1623 The Service Instance Object from which to obrain the license manager. 1624 """ 1625 1626 log.debug("Retrieving license manager") 1627 try: 1628 lic_manager = service_instance.content.licenseManager 1629 except vim.fault.NoPermission as exc: 1630 log.exception(exc) 1631 raise salt.exceptions.VMwareApiError( 1632 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1633 ) 1634 except vim.fault.VimFault as exc: 1635 log.exception(exc) 1636 raise salt.exceptions.VMwareApiError(exc.msg) 1637 except vmodl.RuntimeFault as exc: 1638 log.exception(exc) 1639 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1640 return lic_manager 1641 1642 1643def get_license_assignment_manager(service_instance): 1644 """ 1645 Returns the license assignment manager. 1646 1647 service_instance 1648 The Service Instance Object from which to obrain the license manager. 1649 """ 1650 1651 log.debug("Retrieving license assignment manager") 1652 try: 1653 lic_assignment_manager = ( 1654 service_instance.content.licenseManager.licenseAssignmentManager 1655 ) 1656 except vim.fault.NoPermission as exc: 1657 log.exception(exc) 1658 raise salt.exceptions.VMwareApiError( 1659 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1660 ) 1661 except vim.fault.VimFault as exc: 1662 log.exception(exc) 1663 raise salt.exceptions.VMwareApiError(exc.msg) 1664 except vmodl.RuntimeFault as exc: 1665 log.exception(exc) 1666 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1667 if not lic_assignment_manager: 1668 raise salt.exceptions.VMwareObjectRetrievalError( 1669 "License assignment manager was not retrieved" 1670 ) 1671 return lic_assignment_manager 1672 1673 1674def get_licenses(service_instance, license_manager=None): 1675 """ 1676 Returns the licenses on a specific instance. 1677 1678 service_instance 1679 The Service Instance Object from which to obrain the licenses. 1680 1681 license_manager 1682 The License Manager object of the service instance. If not provided it 1683 will be retrieved. 1684 """ 1685 1686 if not license_manager: 1687 license_manager = get_license_manager(service_instance) 1688 log.debug("Retrieving licenses") 1689 try: 1690 return license_manager.licenses 1691 except vim.fault.NoPermission as exc: 1692 log.exception(exc) 1693 raise salt.exceptions.VMwareApiError( 1694 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1695 ) 1696 except vim.fault.VimFault as exc: 1697 log.exception(exc) 1698 raise salt.exceptions.VMwareApiError(exc.msg) 1699 except vmodl.RuntimeFault as exc: 1700 log.exception(exc) 1701 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1702 1703 1704def add_license(service_instance, key, description, license_manager=None): 1705 """ 1706 Adds a license. 1707 1708 service_instance 1709 The Service Instance Object. 1710 1711 key 1712 The key of the license to add. 1713 1714 description 1715 The description of the license to add. 1716 1717 license_manager 1718 The License Manager object of the service instance. If not provided it 1719 will be retrieved. 1720 """ 1721 if not license_manager: 1722 license_manager = get_license_manager(service_instance) 1723 label = vim.KeyValue() 1724 label.key = "VpxClientLicenseLabel" 1725 label.value = description 1726 log.debug("Adding license '%s'", description) 1727 try: 1728 vmware_license = license_manager.AddLicense(key, [label]) 1729 except vim.fault.NoPermission as exc: 1730 log.exception(exc) 1731 raise salt.exceptions.VMwareApiError( 1732 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1733 ) 1734 except vim.fault.VimFault as exc: 1735 log.exception(exc) 1736 raise salt.exceptions.VMwareApiError(exc.msg) 1737 except vmodl.RuntimeFault as exc: 1738 log.exception(exc) 1739 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1740 return vmware_license 1741 1742 1743def get_assigned_licenses( 1744 service_instance, entity_ref=None, entity_name=None, license_assignment_manager=None 1745): 1746 """ 1747 Returns the licenses assigned to an entity. If entity ref is not provided, 1748 then entity_name is assumed to be the vcenter. This is later checked if 1749 the entity name is provided. 1750 1751 service_instance 1752 The Service Instance Object from which to obtain the licenses. 1753 1754 entity_ref 1755 VMware entity to get the assigned licenses for. 1756 If None, the entity is the vCenter itself. 1757 Default is None. 1758 1759 entity_name 1760 Entity name used in logging. 1761 Default is None. 1762 1763 license_assignment_manager 1764 The LicenseAssignmentManager object of the service instance. 1765 If not provided it will be retrieved. 1766 Default is None. 1767 """ 1768 if not license_assignment_manager: 1769 license_assignment_manager = get_license_assignment_manager(service_instance) 1770 if not entity_name: 1771 raise salt.exceptions.ArgumentValueError("No entity_name passed") 1772 # If entity_ref is not defined, then interested in the vcenter 1773 entity_id = None 1774 entity_type = "moid" 1775 check_name = False 1776 if not entity_ref: 1777 if entity_name: 1778 check_name = True 1779 entity_type = "uuid" 1780 try: 1781 entity_id = service_instance.content.about.instanceUuid 1782 except vim.fault.NoPermission as exc: 1783 log.exception(exc) 1784 raise salt.exceptions.VMwareApiError( 1785 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1786 ) 1787 except vim.fault.VimFault as exc: 1788 log.exception(exc) 1789 raise salt.exceptions.VMwareApiError(exc.msg) 1790 except vmodl.RuntimeFault as exc: 1791 log.exception(exc) 1792 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1793 else: 1794 entity_id = entity_ref._moId 1795 1796 log.trace("Retrieving licenses assigned to '%s'", entity_name) 1797 try: 1798 assignments = license_assignment_manager.QueryAssignedLicenses(entity_id) 1799 except vim.fault.NoPermission as exc: 1800 log.exception(exc) 1801 raise salt.exceptions.VMwareApiError( 1802 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1803 ) 1804 except vim.fault.VimFault as exc: 1805 log.exception(exc) 1806 raise salt.exceptions.VMwareApiError(exc.msg) 1807 except vmodl.RuntimeFault as exc: 1808 log.exception(exc) 1809 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1810 1811 if entity_type == "uuid" and len(assignments) > 1: 1812 log.trace("Unexpectectedly retrieved more than one VCenter license assignment.") 1813 raise salt.exceptions.VMwareObjectRetrievalError( 1814 "Unexpected return. Expect only a single assignment" 1815 ) 1816 1817 if check_name: 1818 if entity_name != assignments[0].entityDisplayName: 1819 log.trace( 1820 "Getting license info for wrong vcenter: %s != %s", 1821 entity_name, 1822 assignments[0].entityDisplayName, 1823 ) 1824 raise salt.exceptions.VMwareObjectRetrievalError( 1825 "Got license assignment info for a different vcenter" 1826 ) 1827 1828 return [a.assignedLicense for a in assignments] 1829 1830 1831def assign_license( 1832 service_instance, 1833 license_key, 1834 license_name, 1835 entity_ref=None, 1836 entity_name=None, 1837 license_assignment_manager=None, 1838): 1839 """ 1840 Assigns a license to an entity. 1841 1842 service_instance 1843 The Service Instance Object from which to obrain the licenses. 1844 1845 license_key 1846 The key of the license to add. 1847 1848 license_name 1849 The description of the license to add. 1850 1851 entity_ref 1852 VMware entity to assign the license to. 1853 If None, the entity is the vCenter itself. 1854 Default is None. 1855 1856 entity_name 1857 Entity name used in logging. 1858 Default is None. 1859 1860 license_assignment_manager 1861 The LicenseAssignmentManager object of the service instance. 1862 If not provided it will be retrieved 1863 Default is None. 1864 """ 1865 if not license_assignment_manager: 1866 license_assignment_manager = get_license_assignment_manager(service_instance) 1867 entity_id = None 1868 1869 if not entity_ref: 1870 # vcenter 1871 try: 1872 entity_id = service_instance.content.about.instanceUuid 1873 except vim.fault.NoPermission as exc: 1874 log.exception(exc) 1875 raise salt.exceptions.VMwareApiError( 1876 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1877 ) 1878 except vim.fault.VimFault as exc: 1879 raise salt.exceptions.VMwareApiError(exc.msg) 1880 except vmodl.RuntimeFault as exc: 1881 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1882 if not entity_name: 1883 entity_name = "vCenter" 1884 else: 1885 # e.g. vsan cluster or host 1886 entity_id = entity_ref._moId 1887 1888 log.trace("Assigning license to '%s'", entity_name) 1889 try: 1890 vmware_license = license_assignment_manager.UpdateAssignedLicense( 1891 entity_id, license_key, license_name 1892 ) 1893 except vim.fault.NoPermission as exc: 1894 log.exception(exc) 1895 raise salt.exceptions.VMwareApiError( 1896 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1897 ) 1898 except vim.fault.VimFault as exc: 1899 log.exception(exc) 1900 raise salt.exceptions.VMwareApiError(exc.msg) 1901 except vmodl.RuntimeFault as exc: 1902 log.exception(exc) 1903 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1904 return vmware_license 1905 1906 1907def list_datacenters(service_instance): 1908 """ 1909 Returns a list of datacenters associated with a given service instance. 1910 1911 service_instance 1912 The Service Instance Object from which to obtain datacenters. 1913 """ 1914 return list_objects(service_instance, vim.Datacenter) 1915 1916 1917def get_datacenters(service_instance, datacenter_names=None, get_all_datacenters=False): 1918 """ 1919 Returns all datacenters in a vCenter. 1920 1921 service_instance 1922 The Service Instance Object from which to obtain cluster. 1923 1924 datacenter_names 1925 List of datacenter names to filter by. Default value is None. 1926 1927 get_all_datacenters 1928 Flag specifying whether to retrieve all datacenters. 1929 Default value is None. 1930 """ 1931 items = [ 1932 i["object"] 1933 for i in get_mors_with_properties( 1934 service_instance, vim.Datacenter, property_list=["name"] 1935 ) 1936 if get_all_datacenters or (datacenter_names and i["name"] in datacenter_names) 1937 ] 1938 return items 1939 1940 1941def get_datacenter(service_instance, datacenter_name): 1942 """ 1943 Returns a vim.Datacenter managed object. 1944 1945 service_instance 1946 The Service Instance Object from which to obtain datacenter. 1947 1948 datacenter_name 1949 The datacenter name 1950 """ 1951 items = get_datacenters(service_instance, datacenter_names=[datacenter_name]) 1952 if not items: 1953 raise salt.exceptions.VMwareObjectRetrievalError( 1954 "Datacenter '{}' was not found".format(datacenter_name) 1955 ) 1956 return items[0] 1957 1958 1959def create_datacenter(service_instance, datacenter_name): 1960 """ 1961 Creates a datacenter. 1962 1963 .. versionadded:: 2017.7.0 1964 1965 service_instance 1966 The Service Instance Object 1967 1968 datacenter_name 1969 The datacenter name 1970 """ 1971 root_folder = get_root_folder(service_instance) 1972 log.trace("Creating datacenter '%s'", datacenter_name) 1973 try: 1974 dc_obj = root_folder.CreateDatacenter(datacenter_name) 1975 except vim.fault.NoPermission as exc: 1976 log.exception(exc) 1977 raise salt.exceptions.VMwareApiError( 1978 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 1979 ) 1980 except vim.fault.VimFault as exc: 1981 log.exception(exc) 1982 raise salt.exceptions.VMwareApiError(exc.msg) 1983 except vmodl.RuntimeFault as exc: 1984 log.exception(exc) 1985 raise salt.exceptions.VMwareRuntimeError(exc.msg) 1986 return dc_obj 1987 1988 1989def get_cluster(dc_ref, cluster): 1990 """ 1991 Returns a cluster in a datacenter. 1992 1993 dc_ref 1994 The datacenter reference 1995 1996 cluster 1997 The cluster to be retrieved 1998 """ 1999 dc_name = get_managed_object_name(dc_ref) 2000 log.trace("Retrieving cluster '%s' from datacenter '%s'", cluster, dc_name) 2001 si = get_service_instance_from_managed_object(dc_ref, name=dc_name) 2002 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2003 path="hostFolder", 2004 skip=True, 2005 type=vim.Datacenter, 2006 selectSet=[ 2007 vmodl.query.PropertyCollector.TraversalSpec( 2008 path="childEntity", skip=False, type=vim.Folder 2009 ) 2010 ], 2011 ) 2012 items = [ 2013 i["object"] 2014 for i in get_mors_with_properties( 2015 si, 2016 vim.ClusterComputeResource, 2017 container_ref=dc_ref, 2018 property_list=["name"], 2019 traversal_spec=traversal_spec, 2020 ) 2021 if i["name"] == cluster 2022 ] 2023 if not items: 2024 raise salt.exceptions.VMwareObjectRetrievalError( 2025 "Cluster '{}' was not found in datacenter '{}'".format(cluster, dc_name) 2026 ) 2027 return items[0] 2028 2029 2030def create_cluster(dc_ref, cluster_name, cluster_spec): 2031 """ 2032 Creates a cluster in a datacenter. 2033 2034 dc_ref 2035 The parent datacenter reference. 2036 2037 cluster_name 2038 The cluster name. 2039 2040 cluster_spec 2041 The cluster spec (vim.ClusterConfigSpecEx). 2042 Defaults to None. 2043 """ 2044 dc_name = get_managed_object_name(dc_ref) 2045 log.trace("Creating cluster '%s' in datacenter '%s'", cluster_name, dc_name) 2046 try: 2047 dc_ref.hostFolder.CreateClusterEx(cluster_name, cluster_spec) 2048 except vim.fault.NoPermission as exc: 2049 log.exception(exc) 2050 raise salt.exceptions.VMwareApiError( 2051 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2052 ) 2053 except vim.fault.VimFault as exc: 2054 log.exception(exc) 2055 raise salt.exceptions.VMwareApiError(exc.msg) 2056 except vmodl.RuntimeFault as exc: 2057 log.exception(exc) 2058 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2059 2060 2061def update_cluster(cluster_ref, cluster_spec): 2062 """ 2063 Updates a cluster in a datacenter. 2064 2065 cluster_ref 2066 The cluster reference. 2067 2068 cluster_spec 2069 The cluster spec (vim.ClusterConfigSpecEx). 2070 Defaults to None. 2071 """ 2072 cluster_name = get_managed_object_name(cluster_ref) 2073 log.trace("Updating cluster '%s'", cluster_name) 2074 try: 2075 task = cluster_ref.ReconfigureComputeResource_Task(cluster_spec, modify=True) 2076 except vim.fault.NoPermission as exc: 2077 log.exception(exc) 2078 raise salt.exceptions.VMwareApiError( 2079 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2080 ) 2081 except vim.fault.VimFault as exc: 2082 log.exception(exc) 2083 raise salt.exceptions.VMwareApiError(exc.msg) 2084 except vmodl.RuntimeFault as exc: 2085 log.exception(exc) 2086 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2087 wait_for_task(task, cluster_name, "ClusterUpdateTask") 2088 2089 2090def list_clusters(service_instance): 2091 """ 2092 Returns a list of clusters associated with a given service instance. 2093 2094 service_instance 2095 The Service Instance Object from which to obtain clusters. 2096 """ 2097 return list_objects(service_instance, vim.ClusterComputeResource) 2098 2099 2100def list_datastore_clusters(service_instance): 2101 """ 2102 Returns a list of datastore clusters associated with a given service instance. 2103 2104 service_instance 2105 The Service Instance Object from which to obtain datastore clusters. 2106 """ 2107 return list_objects(service_instance, vim.StoragePod) 2108 2109 2110def list_datastores(service_instance): 2111 """ 2112 Returns a list of datastores associated with a given service instance. 2113 2114 service_instance 2115 The Service Instance Object from which to obtain datastores. 2116 """ 2117 return list_objects(service_instance, vim.Datastore) 2118 2119 2120def get_datastore_files( 2121 service_instance, directory, datastores, container_object, browser_spec 2122): 2123 """ 2124 Get the files with a given browser specification from the datastore. 2125 2126 service_instance 2127 The Service Instance Object from which to obtain datastores. 2128 2129 directory 2130 The name of the directory where we would like to search 2131 2132 datastores 2133 Name of the datastores 2134 2135 container_object 2136 The base object for searches 2137 2138 browser_spec 2139 BrowserSpec object which defines the search criteria 2140 2141 return 2142 list of vim.host.DatastoreBrowser.SearchResults objects 2143 """ 2144 2145 files = [] 2146 datastore_objects = get_datastores( 2147 service_instance, container_object, datastore_names=datastores 2148 ) 2149 for datobj in datastore_objects: 2150 try: 2151 task = datobj.browser.SearchDatastore_Task( 2152 datastorePath="[{}] {}".format(datobj.name, directory), 2153 searchSpec=browser_spec, 2154 ) 2155 except vim.fault.NoPermission as exc: 2156 log.exception(exc) 2157 raise salt.exceptions.VMwareApiError( 2158 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2159 ) 2160 except vim.fault.VimFault as exc: 2161 log.exception(exc) 2162 raise salt.exceptions.VMwareApiError(exc.msg) 2163 except vmodl.RuntimeFault as exc: 2164 log.exception(exc) 2165 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2166 try: 2167 files.append( 2168 salt.utils.vmware.wait_for_task( 2169 task, directory, "query virtual machine files" 2170 ) 2171 ) 2172 except salt.exceptions.VMwareFileNotFoundError: 2173 pass 2174 return files 2175 2176 2177def get_datastores( 2178 service_instance, 2179 reference, 2180 datastore_names=None, 2181 backing_disk_ids=None, 2182 get_all_datastores=False, 2183): 2184 """ 2185 Returns a list of vim.Datastore objects representing the datastores visible 2186 from a VMware object, filtered by their names, or the backing disk 2187 cannonical name or scsi_addresses 2188 2189 service_instance 2190 The Service Instance Object from which to obtain datastores. 2191 2192 reference 2193 The VMware object from which the datastores are visible. 2194 2195 datastore_names 2196 The list of datastore names to be retrieved. Default value is None. 2197 2198 backing_disk_ids 2199 The list of canonical names of the disks backing the datastores 2200 to be retrieved. Only supported if reference is a vim.HostSystem. 2201 Default value is None 2202 2203 get_all_datastores 2204 Specifies whether to retrieve all disks in the host. 2205 Default value is False. 2206 """ 2207 obj_name = get_managed_object_name(reference) 2208 if get_all_datastores: 2209 log.trace("Retrieving all datastores visible to '%s'", obj_name) 2210 else: 2211 log.trace( 2212 "Retrieving datastores visible to '%s': names = (%s); " 2213 "backing disk ids = (%s)", 2214 obj_name, 2215 datastore_names, 2216 backing_disk_ids, 2217 ) 2218 if backing_disk_ids and not isinstance(reference, vim.HostSystem): 2219 2220 raise salt.exceptions.ArgumentValueError( 2221 "Unsupported reference type '{}' when backing disk filter " 2222 "is set".format(reference.__class__.__name__) 2223 ) 2224 if (not get_all_datastores) and backing_disk_ids: 2225 # At this point we know the reference is a vim.HostSystem 2226 log.trace("Filtering datastores with backing disk ids: %s", backing_disk_ids) 2227 storage_system = get_storage_system(service_instance, reference, obj_name) 2228 props = salt.utils.vmware.get_properties_of_managed_object( 2229 storage_system, ["fileSystemVolumeInfo.mountInfo"] 2230 ) 2231 mount_infos = props.get("fileSystemVolumeInfo.mountInfo", []) 2232 disk_datastores = [] 2233 # Non vmfs volumes aren't backed by a disk 2234 for vol in [ 2235 i.volume for i in mount_infos if isinstance(i.volume, vim.HostVmfsVolume) 2236 ]: 2237 2238 if not [e for e in vol.extent if e.diskName in backing_disk_ids]: 2239 # Skip volume if it doesn't contain an extent with a 2240 # canonical name of interest 2241 continue 2242 log.trace( 2243 "Found datastore '%s' for disk id(s) '%s'", 2244 vol.name, 2245 [e.diskName for e in vol.extent], 2246 ) 2247 disk_datastores.append(vol.name) 2248 log.trace("Datastore found for disk filter: %s", disk_datastores) 2249 if datastore_names: 2250 datastore_names.extend(disk_datastores) 2251 else: 2252 datastore_names = disk_datastores 2253 2254 if (not get_all_datastores) and (not datastore_names): 2255 log.trace( 2256 "No datastore to be filtered after retrieving the datastores " 2257 "backed by the disk id(s) '%s'", 2258 backing_disk_ids, 2259 ) 2260 return [] 2261 2262 log.trace("datastore_names = %s", datastore_names) 2263 2264 # Use the default traversal spec 2265 if isinstance(reference, vim.HostSystem): 2266 # Create a different traversal spec for hosts because it looks like the 2267 # default doesn't retrieve the datastores 2268 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2269 name="host_datastore_traversal", 2270 path="datastore", 2271 skip=False, 2272 type=vim.HostSystem, 2273 ) 2274 elif isinstance(reference, vim.ClusterComputeResource): 2275 # Traversal spec for clusters 2276 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2277 name="cluster_datastore_traversal", 2278 path="datastore", 2279 skip=False, 2280 type=vim.ClusterComputeResource, 2281 ) 2282 elif isinstance(reference, vim.Datacenter): 2283 # Traversal spec for datacenter 2284 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2285 name="datacenter_datastore_traversal", 2286 path="datastore", 2287 skip=False, 2288 type=vim.Datacenter, 2289 ) 2290 elif isinstance(reference, vim.StoragePod): 2291 # Traversal spec for datastore clusters 2292 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2293 name="datastore_cluster_traversal", 2294 path="childEntity", 2295 skip=False, 2296 type=vim.StoragePod, 2297 ) 2298 elif ( 2299 isinstance(reference, vim.Folder) 2300 and get_managed_object_name(reference) == "Datacenters" 2301 ): 2302 # Traversal of root folder (doesn't support multiple levels of Folders) 2303 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2304 path="childEntity", 2305 selectSet=[ 2306 vmodl.query.PropertyCollector.TraversalSpec( 2307 path="datastore", skip=False, type=vim.Datacenter 2308 ) 2309 ], 2310 skip=False, 2311 type=vim.Folder, 2312 ) 2313 else: 2314 raise salt.exceptions.ArgumentValueError( 2315 "Unsupported reference type '{}'".format(reference.__class__.__name__) 2316 ) 2317 2318 items = get_mors_with_properties( 2319 service_instance, 2320 object_type=vim.Datastore, 2321 property_list=["name"], 2322 container_ref=reference, 2323 traversal_spec=traversal_spec, 2324 ) 2325 log.trace("Retrieved %s datastores", len(items)) 2326 items = [i for i in items if get_all_datastores or i["name"] in datastore_names] 2327 log.trace("Filtered datastores: %s", [i["name"] for i in items]) 2328 return [i["object"] for i in items] 2329 2330 2331def rename_datastore(datastore_ref, new_datastore_name): 2332 """ 2333 Renames a datastore 2334 2335 datastore_ref 2336 vim.Datastore reference to the datastore object to be changed 2337 2338 new_datastore_name 2339 New datastore name 2340 """ 2341 ds_name = get_managed_object_name(datastore_ref) 2342 log.trace("Renaming datastore '%s' to '%s'", ds_name, new_datastore_name) 2343 try: 2344 datastore_ref.RenameDatastore(new_datastore_name) 2345 except vim.fault.NoPermission as exc: 2346 log.exception(exc) 2347 raise salt.exceptions.VMwareApiError( 2348 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2349 ) 2350 except vim.fault.VimFault as exc: 2351 log.exception(exc) 2352 raise salt.exceptions.VMwareApiError(exc.msg) 2353 except vmodl.RuntimeFault as exc: 2354 log.exception(exc) 2355 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2356 2357 2358def get_storage_system(service_instance, host_ref, hostname=None): 2359 """ 2360 Returns a host's storage system 2361 """ 2362 2363 if not hostname: 2364 hostname = get_managed_object_name(host_ref) 2365 2366 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2367 path="configManager.storageSystem", type=vim.HostSystem, skip=False 2368 ) 2369 objs = get_mors_with_properties( 2370 service_instance, 2371 vim.HostStorageSystem, 2372 property_list=["systemFile"], 2373 container_ref=host_ref, 2374 traversal_spec=traversal_spec, 2375 ) 2376 if not objs: 2377 raise salt.exceptions.VMwareObjectRetrievalError( 2378 "Host's '{}' storage system was not retrieved".format(hostname) 2379 ) 2380 log.trace("[%s] Retrieved storage system", hostname) 2381 return objs[0]["object"] 2382 2383 2384def _get_partition_info(storage_system, device_path): 2385 """ 2386 Returns partition information for a device path, of type 2387 vim.HostDiskPartitionInfo 2388 """ 2389 try: 2390 partition_infos = storage_system.RetrieveDiskPartitionInfo( 2391 devicePath=[device_path] 2392 ) 2393 except vim.fault.NoPermission as exc: 2394 log.exception(exc) 2395 raise salt.exceptions.VMwareApiError( 2396 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2397 ) 2398 except vim.fault.VimFault as exc: 2399 log.exception(exc) 2400 raise salt.exceptions.VMwareApiError(exc.msg) 2401 except vmodl.RuntimeFault as exc: 2402 log.exception(exc) 2403 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2404 log.trace("partition_info = %s", partition_infos[0]) 2405 return partition_infos[0] 2406 2407 2408def _get_new_computed_partition_spec(storage_system, device_path, partition_info): 2409 """ 2410 Computes the new disk partition info when adding a new vmfs partition that 2411 uses up the remainder of the disk; returns a tuple 2412 (new_partition_number, vim.HostDiskPartitionSpec 2413 """ 2414 log.trace( 2415 "Adding a partition at the end of the disk and getting the new " 2416 "computed partition spec" 2417 ) 2418 # TODO implement support for multiple partitions 2419 # We support adding a partition add the end of the disk with partitions 2420 free_partitions = [p for p in partition_info.layout.partition if p.type == "none"] 2421 if not free_partitions: 2422 raise salt.exceptions.VMwareObjectNotFoundError( 2423 "Free partition was not found on device '{}'".format( 2424 partition_info.deviceName 2425 ) 2426 ) 2427 free_partition = free_partitions[0] 2428 2429 # Create a layout object that copies the existing one 2430 layout = vim.HostDiskPartitionLayout( 2431 total=partition_info.layout.total, partition=partition_info.layout.partition 2432 ) 2433 # Create a partition with the free space on the disk 2434 # Change the free partition type to vmfs 2435 free_partition.type = "vmfs" 2436 try: 2437 computed_partition_info = storage_system.ComputeDiskPartitionInfo( 2438 devicePath=device_path, 2439 partitionFormat=vim.HostDiskPartitionInfoPartitionFormat.gpt, 2440 layout=layout, 2441 ) 2442 except vim.fault.NoPermission as exc: 2443 log.exception(exc) 2444 raise salt.exceptions.VMwareApiError( 2445 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2446 ) 2447 except vim.fault.VimFault as exc: 2448 log.exception(exc) 2449 raise salt.exceptions.VMwareApiError(exc.msg) 2450 except vmodl.RuntimeFault as exc: 2451 log.exception(exc) 2452 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2453 log.trace("computed partition info = {0}", computed_partition_info) 2454 log.trace("Retrieving new partition number") 2455 partition_numbers = [ 2456 p.partition 2457 for p in computed_partition_info.layout.partition 2458 if ( 2459 p.start.block == free_partition.start.block 2460 or 2461 # XXX If the entire disk is free (i.e. the free 2462 # disk partition starts at block 0) the newily 2463 # created partition is created from block 1 2464 (free_partition.start.block == 0 and p.start.block == 1) 2465 ) 2466 and p.end.block == free_partition.end.block 2467 and p.type == "vmfs" 2468 ] 2469 if not partition_numbers: 2470 raise salt.exceptions.VMwareNotFoundError( 2471 "New partition was not found in computed partitions of device '{}'".format( 2472 partition_info.deviceName 2473 ) 2474 ) 2475 log.trace("new partition number = %s", partition_numbers[0]) 2476 return (partition_numbers[0], computed_partition_info.spec) 2477 2478 2479def create_vmfs_datastore( 2480 host_ref, datastore_name, disk_ref, vmfs_major_version, storage_system=None 2481): 2482 """ 2483 Creates a VMFS datastore from a disk_id 2484 2485 host_ref 2486 vim.HostSystem object referencing a host to create the datastore on 2487 2488 datastore_name 2489 Name of the datastore 2490 2491 disk_ref 2492 vim.HostScsiDislk on which the datastore is created 2493 2494 vmfs_major_version 2495 VMFS major version to use 2496 """ 2497 # TODO Support variable sized partitions 2498 hostname = get_managed_object_name(host_ref) 2499 disk_id = disk_ref.canonicalName 2500 log.debug( 2501 "Creating datastore '%s' on host '%s', scsi disk '%s', vmfs v%s", 2502 datastore_name, 2503 hostname, 2504 disk_id, 2505 vmfs_major_version, 2506 ) 2507 if not storage_system: 2508 si = get_service_instance_from_managed_object(host_ref, name=hostname) 2509 storage_system = get_storage_system(si, host_ref, hostname) 2510 2511 target_disk = disk_ref 2512 partition_info = _get_partition_info(storage_system, target_disk.devicePath) 2513 log.trace("partition_info = %s", partition_info) 2514 new_partition_number, partition_spec = _get_new_computed_partition_spec( 2515 storage_system, target_disk.devicePath, partition_info 2516 ) 2517 spec = vim.VmfsDatastoreCreateSpec( 2518 vmfs=vim.HostVmfsSpec( 2519 majorVersion=vmfs_major_version, 2520 volumeName=datastore_name, 2521 extent=vim.HostScsiDiskPartition( 2522 diskName=disk_id, partition=new_partition_number 2523 ), 2524 ), 2525 diskUuid=target_disk.uuid, 2526 partition=partition_spec, 2527 ) 2528 try: 2529 ds_ref = host_ref.configManager.datastoreSystem.CreateVmfsDatastore(spec) 2530 except vim.fault.NoPermission as exc: 2531 log.exception(exc) 2532 raise salt.exceptions.VMwareApiError( 2533 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2534 ) 2535 except vim.fault.VimFault as exc: 2536 log.exception(exc) 2537 raise salt.exceptions.VMwareApiError(exc.msg) 2538 except vmodl.RuntimeFault as exc: 2539 log.exception(exc) 2540 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2541 log.debug("Created datastore '%s' on host '%s'", datastore_name, hostname) 2542 return ds_ref 2543 2544 2545def get_host_datastore_system(host_ref, hostname=None): 2546 """ 2547 Returns a host's datastore system 2548 2549 host_ref 2550 Reference to the ESXi host 2551 2552 hostname 2553 Name of the host. This argument is optional. 2554 """ 2555 2556 if not hostname: 2557 hostname = get_managed_object_name(host_ref) 2558 service_instance = get_service_instance_from_managed_object(host_ref) 2559 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2560 path="configManager.datastoreSystem", type=vim.HostSystem, skip=False 2561 ) 2562 objs = get_mors_with_properties( 2563 service_instance, 2564 vim.HostDatastoreSystem, 2565 property_list=["datastore"], 2566 container_ref=host_ref, 2567 traversal_spec=traversal_spec, 2568 ) 2569 if not objs: 2570 raise salt.exceptions.VMwareObjectRetrievalError( 2571 "Host's '{}' datastore system was not retrieved".format(hostname) 2572 ) 2573 log.trace("[%s] Retrieved datastore system", hostname) 2574 return objs[0]["object"] 2575 2576 2577def remove_datastore(service_instance, datastore_ref): 2578 """ 2579 Creates a VMFS datastore from a disk_id 2580 2581 service_instance 2582 The Service Instance Object containing the datastore 2583 2584 datastore_ref 2585 The reference to the datastore to remove 2586 """ 2587 ds_props = get_properties_of_managed_object(datastore_ref, ["host", "info", "name"]) 2588 ds_name = ds_props["name"] 2589 log.debug("Removing datastore '%s'", ds_name) 2590 ds_hosts = ds_props.get("host") 2591 if not ds_hosts: 2592 raise salt.exceptions.VMwareApiError( 2593 "Datastore '{}' can't be removed. No attached hosts found".format(ds_name) 2594 ) 2595 hostname = get_managed_object_name(ds_hosts[0].key) 2596 host_ds_system = get_host_datastore_system(ds_hosts[0].key, hostname=hostname) 2597 try: 2598 host_ds_system.RemoveDatastore(datastore_ref) 2599 except vim.fault.NoPermission as exc: 2600 log.exception(exc) 2601 raise salt.exceptions.VMwareApiError( 2602 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2603 ) 2604 except vim.fault.VimFault as exc: 2605 log.exception(exc) 2606 raise salt.exceptions.VMwareApiError(exc.msg) 2607 except vmodl.RuntimeFault as exc: 2608 log.exception(exc) 2609 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2610 log.trace("[%s] Removed datastore '%s'", hostname, ds_name) 2611 2612 2613def get_hosts( 2614 service_instance, 2615 datacenter_name=None, 2616 host_names=None, 2617 cluster_name=None, 2618 get_all_hosts=False, 2619): 2620 """ 2621 Returns a list of vim.HostSystem objects representing ESXi hosts 2622 in a vcenter filtered by their names and/or datacenter, cluster membership. 2623 2624 service_instance 2625 The Service Instance Object from which to obtain the hosts. 2626 2627 datacenter_name 2628 The datacenter name. Default is None. 2629 2630 host_names 2631 The host_names to be retrieved. Default is None. 2632 2633 cluster_name 2634 The cluster name - used to restrict the hosts retrieved. Only used if 2635 the datacenter is set. This argument is optional. 2636 2637 get_all_hosts 2638 Specifies whether to retrieve all hosts in the container. 2639 Default value is False. 2640 """ 2641 properties = ["name"] 2642 if cluster_name and not datacenter_name: 2643 raise salt.exceptions.ArgumentValueError( 2644 "Must specify the datacenter when specifying the cluster" 2645 ) 2646 if not host_names: 2647 host_names = [] 2648 if not datacenter_name: 2649 # Assume the root folder is the starting point 2650 start_point = get_root_folder(service_instance) 2651 else: 2652 start_point = get_datacenter(service_instance, datacenter_name) 2653 if cluster_name: 2654 # Retrieval to test if cluster exists. Cluster existence only makes 2655 # sense if the datacenter has been specified 2656 properties.append("parent") 2657 2658 # Search for the objects 2659 hosts = get_mors_with_properties( 2660 service_instance, 2661 vim.HostSystem, 2662 container_ref=start_point, 2663 property_list=properties, 2664 ) 2665 log.trace("Retrieved hosts: %s", [h["name"] for h in hosts]) 2666 filtered_hosts = [] 2667 for h in hosts: 2668 # Complex conditions checking if a host should be added to the 2669 # filtered list (either due to its name and/or cluster membership) 2670 2671 if cluster_name: 2672 if not isinstance(h["parent"], vim.ClusterComputeResource): 2673 continue 2674 parent_name = get_managed_object_name(h["parent"]) 2675 if parent_name != cluster_name: 2676 continue 2677 2678 if get_all_hosts: 2679 filtered_hosts.append(h["object"]) 2680 continue 2681 2682 if h["name"] in host_names: 2683 filtered_hosts.append(h["object"]) 2684 return filtered_hosts 2685 2686 2687def _get_scsi_address_to_lun_key_map( 2688 service_instance, host_ref, storage_system=None, hostname=None 2689): 2690 """ 2691 Returns a map between the scsi addresses and the keys of all luns on an ESXi 2692 host. 2693 map[<scsi_address>] = <lun key> 2694 2695 service_instance 2696 The Service Instance Object from which to obtain the hosts 2697 2698 host_ref 2699 The vim.HostSystem object representing the host that contains the 2700 requested disks. 2701 2702 storage_system 2703 The host's storage system. Default is None. 2704 2705 hostname 2706 Name of the host. Default is None. 2707 """ 2708 if not hostname: 2709 hostname = get_managed_object_name(host_ref) 2710 if not storage_system: 2711 storage_system = get_storage_system(service_instance, host_ref, hostname) 2712 try: 2713 device_info = storage_system.storageDeviceInfo 2714 except vim.fault.NoPermission as exc: 2715 log.exception(exc) 2716 raise salt.exceptions.VMwareApiError( 2717 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2718 ) 2719 except vim.fault.VimFault as exc: 2720 log.exception(exc) 2721 raise salt.exceptions.VMwareApiError(exc.msg) 2722 except vmodl.RuntimeFault as exc: 2723 log.exception(exc) 2724 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2725 if not device_info: 2726 raise salt.exceptions.VMwareObjectRetrievalError( 2727 "Host's '{}' storage device info was not retrieved".format(hostname) 2728 ) 2729 multipath_info = device_info.multipathInfo 2730 if not multipath_info: 2731 raise salt.exceptions.VMwareObjectRetrievalError( 2732 "Host's '{}' multipath info was not retrieved".format(hostname) 2733 ) 2734 if multipath_info.lun is None: 2735 raise salt.exceptions.VMwareObjectRetrievalError( 2736 "No luns were retrieved from host '{}'".format(hostname) 2737 ) 2738 lun_key_by_scsi_addr = {} 2739 for l in multipath_info.lun: 2740 # The vmware scsi_address may have multiple comma separated values 2741 # The first one is the actual scsi address 2742 lun_key_by_scsi_addr.update({p.name.split(",")[0]: l.lun for p in l.path}) 2743 log.trace( 2744 "Scsi address to lun id map on host '%s': %s", hostname, lun_key_by_scsi_addr 2745 ) 2746 return lun_key_by_scsi_addr 2747 2748 2749def get_all_luns(host_ref, storage_system=None, hostname=None): 2750 """ 2751 Returns a list of all vim.HostScsiDisk objects in a disk 2752 2753 host_ref 2754 The vim.HostSystem object representing the host that contains the 2755 requested disks. 2756 2757 storage_system 2758 The host's storage system. Default is None. 2759 2760 hostname 2761 Name of the host. This argument is optional. 2762 """ 2763 if not hostname: 2764 hostname = get_managed_object_name(host_ref) 2765 if not storage_system: 2766 si = get_service_instance_from_managed_object(host_ref, name=hostname) 2767 storage_system = get_storage_system(si, host_ref, hostname) 2768 if not storage_system: 2769 raise salt.exceptions.VMwareObjectRetrievalError( 2770 "Host's '{}' storage system was not retrieved".format(hostname) 2771 ) 2772 try: 2773 device_info = storage_system.storageDeviceInfo 2774 except vim.fault.NoPermission as exc: 2775 log.exception(exc) 2776 raise salt.exceptions.VMwareApiError( 2777 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 2778 ) 2779 except vim.fault.VimFault as exc: 2780 log.exception(exc) 2781 raise salt.exceptions.VMwareApiError(exc.msg) 2782 except vmodl.RuntimeFault as exc: 2783 log.exception(exc) 2784 raise salt.exceptions.VMwareRuntimeError(exc.msg) 2785 if not device_info: 2786 raise salt.exceptions.VMwareObjectRetrievalError( 2787 "Host's '{}' storage device info was not retrieved".format(hostname) 2788 ) 2789 2790 scsi_luns = device_info.scsiLun 2791 if scsi_luns: 2792 log.trace( 2793 "Retrieved scsi luns in host '%s': %s", 2794 hostname, 2795 [l.canonicalName for l in scsi_luns], 2796 ) 2797 return scsi_luns 2798 log.trace("Retrieved no scsi_luns in host '%s'", hostname) 2799 return [] 2800 2801 2802def get_scsi_address_to_lun_map(host_ref, storage_system=None, hostname=None): 2803 """ 2804 Returns a map of all vim.ScsiLun objects on a ESXi host keyed by their 2805 scsi address 2806 2807 host_ref 2808 The vim.HostSystem object representing the host that contains the 2809 requested disks. 2810 2811 storage_system 2812 The host's storage system. Default is None. 2813 2814 hostname 2815 Name of the host. This argument is optional. 2816 """ 2817 if not hostname: 2818 hostname = get_managed_object_name(host_ref) 2819 si = get_service_instance_from_managed_object(host_ref, name=hostname) 2820 if not storage_system: 2821 storage_system = get_storage_system(si, host_ref, hostname) 2822 lun_ids_to_scsi_addr_map = _get_scsi_address_to_lun_key_map( 2823 si, host_ref, storage_system, hostname 2824 ) 2825 luns_to_key_map = { 2826 d.key: d for d in get_all_luns(host_ref, storage_system, hostname) 2827 } 2828 return { 2829 scsi_addr: luns_to_key_map[lun_key] 2830 for scsi_addr, lun_key in lun_ids_to_scsi_addr_map.items() 2831 } 2832 2833 2834def get_disks(host_ref, disk_ids=None, scsi_addresses=None, get_all_disks=False): 2835 """ 2836 Returns a list of vim.HostScsiDisk objects representing disks 2837 in a ESXi host, filtered by their cannonical names and scsi_addresses 2838 2839 host_ref 2840 The vim.HostSystem object representing the host that contains the 2841 requested disks. 2842 2843 disk_ids 2844 The list of canonical names of the disks to be retrieved. Default value 2845 is None 2846 2847 scsi_addresses 2848 The list of scsi addresses of the disks to be retrieved. Default value 2849 is None 2850 2851 get_all_disks 2852 Specifies whether to retrieve all disks in the host. 2853 Default value is False. 2854 """ 2855 hostname = get_managed_object_name(host_ref) 2856 if get_all_disks: 2857 log.trace("Retrieving all disks in host '%s'", hostname) 2858 else: 2859 log.trace( 2860 "Retrieving disks in host '%s': ids = (%s); scsi addresses = (%s)", 2861 hostname, 2862 disk_ids, 2863 scsi_addresses, 2864 ) 2865 if not (disk_ids or scsi_addresses): 2866 return [] 2867 si = get_service_instance_from_managed_object(host_ref, name=hostname) 2868 storage_system = get_storage_system(si, host_ref, hostname) 2869 disk_keys = [] 2870 if scsi_addresses: 2871 # convert the scsi addresses to disk keys 2872 lun_key_by_scsi_addr = _get_scsi_address_to_lun_key_map( 2873 si, host_ref, storage_system, hostname 2874 ) 2875 disk_keys = [ 2876 key 2877 for scsi_addr, key in lun_key_by_scsi_addr.items() 2878 if scsi_addr in scsi_addresses 2879 ] 2880 log.trace("disk_keys based on scsi_addresses = %s", disk_keys) 2881 2882 scsi_luns = get_all_luns(host_ref, storage_system) 2883 scsi_disks = [ 2884 disk 2885 for disk in scsi_luns 2886 if isinstance(disk, vim.HostScsiDisk) 2887 and ( 2888 get_all_disks 2889 or 2890 # Filter by canonical name 2891 (disk_ids and (disk.canonicalName in disk_ids)) 2892 or 2893 # Filter by disk keys from scsi addresses 2894 (disk.key in disk_keys) 2895 ) 2896 ] 2897 log.trace( 2898 "Retrieved disks in host '%s': %s", 2899 hostname, 2900 [d.canonicalName for d in scsi_disks], 2901 ) 2902 return scsi_disks 2903 2904 2905def get_disk_partition_info(host_ref, disk_id, storage_system=None): 2906 """ 2907 Returns all partitions on a disk 2908 2909 host_ref 2910 The reference of the ESXi host containing the disk 2911 2912 disk_id 2913 The canonical name of the disk whose partitions are to be removed 2914 2915 storage_system 2916 The ESXi host's storage system. Default is None. 2917 """ 2918 hostname = get_managed_object_name(host_ref) 2919 service_instance = get_service_instance_from_managed_object(host_ref) 2920 if not storage_system: 2921 storage_system = get_storage_system(service_instance, host_ref, hostname) 2922 2923 props = get_properties_of_managed_object( 2924 storage_system, ["storageDeviceInfo.scsiLun"] 2925 ) 2926 if not props.get("storageDeviceInfo.scsiLun"): 2927 raise salt.exceptions.VMwareObjectRetrievalError( 2928 "No devices were retrieved in host '{}'".format(hostname) 2929 ) 2930 log.trace( 2931 "[%s] Retrieved %s devices: %s", 2932 hostname, 2933 len(props["storageDeviceInfo.scsiLun"]), 2934 ", ".join([l.canonicalName for l in props["storageDeviceInfo.scsiLun"]]), 2935 ) 2936 disks = [ 2937 l 2938 for l in props["storageDeviceInfo.scsiLun"] 2939 if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id 2940 ] 2941 if not disks: 2942 raise salt.exceptions.VMwareObjectRetrievalError( 2943 "Disk '{}' was not found in host '{}'".format(disk_id, hostname) 2944 ) 2945 log.trace("[%s] device_path = %s", hostname, disks[0].devicePath) 2946 partition_info = _get_partition_info(storage_system, disks[0].devicePath) 2947 log.trace( 2948 "[%s] Retrieved %s partition(s) on disk '%s'", 2949 hostname, 2950 len(partition_info.spec.partition), 2951 disk_id, 2952 ) 2953 return partition_info 2954 2955 2956def erase_disk_partitions( 2957 service_instance, host_ref, disk_id, hostname=None, storage_system=None 2958): 2959 """ 2960 Erases all partitions on a disk 2961 2962 in a vcenter filtered by their names and/or datacenter, cluster membership 2963 2964 service_instance 2965 The Service Instance Object from which to obtain all information 2966 2967 host_ref 2968 The reference of the ESXi host containing the disk 2969 2970 disk_id 2971 The canonical name of the disk whose partitions are to be removed 2972 2973 hostname 2974 The ESXi hostname. Default is None. 2975 2976 storage_system 2977 The ESXi host's storage system. Default is None. 2978 """ 2979 2980 if not hostname: 2981 hostname = get_managed_object_name(host_ref) 2982 if not storage_system: 2983 storage_system = get_storage_system(service_instance, host_ref, hostname) 2984 2985 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 2986 path="configManager.storageSystem", type=vim.HostSystem, skip=False 2987 ) 2988 results = get_mors_with_properties( 2989 service_instance, 2990 vim.HostStorageSystem, 2991 ["storageDeviceInfo.scsiLun"], 2992 container_ref=host_ref, 2993 traversal_spec=traversal_spec, 2994 ) 2995 if not results: 2996 raise salt.exceptions.VMwareObjectRetrievalError( 2997 "Host's '{}' devices were not retrieved".format(hostname) 2998 ) 2999 log.trace( 3000 "[%s] Retrieved %s devices: %s", 3001 hostname, 3002 len(results[0].get("storageDeviceInfo.scsiLun", [])), 3003 ", ".join( 3004 [l.canonicalName for l in results[0].get("storageDeviceInfo.scsiLun", [])] 3005 ), 3006 ) 3007 disks = [ 3008 l 3009 for l in results[0].get("storageDeviceInfo.scsiLun", []) 3010 if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id 3011 ] 3012 if not disks: 3013 raise salt.exceptions.VMwareObjectRetrievalError( 3014 "Disk '{}' was not found in host '{}'".format(disk_id, hostname) 3015 ) 3016 log.trace("[%s] device_path = %s", hostname, disks[0].devicePath) 3017 # Erase the partitions by setting an empty partition spec 3018 try: 3019 storage_system.UpdateDiskPartitions( 3020 disks[0].devicePath, vim.HostDiskPartitionSpec() 3021 ) 3022 except vim.fault.NoPermission as exc: 3023 log.exception(exc) 3024 raise salt.exceptions.VMwareApiError( 3025 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3026 ) 3027 except vim.fault.VimFault as exc: 3028 log.exception(exc) 3029 raise salt.exceptions.VMwareApiError(exc.msg) 3030 except vmodl.RuntimeFault as exc: 3031 log.exception(exc) 3032 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3033 log.trace("[%s] Erased partitions on disk '%s'", hostname, disk_id) 3034 3035 3036def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False): 3037 """ 3038 Returns a list of vim.VsanHostDiskMapping objects representing disks 3039 in a ESXi host, filtered by their cannonical names. 3040 3041 host_ref 3042 The vim.HostSystem object representing the host that contains the 3043 requested disks. 3044 3045 cache_disk_ids 3046 The list of cannonical names of the cache disks to be retrieved. The 3047 canonical name of the cache disk is enough to identify the disk group 3048 because it is guaranteed to have one and only one cache disk. 3049 Default is None. 3050 3051 get_all_disk_groups 3052 Specifies whether to retrieve all disks groups in the host. 3053 Default value is False. 3054 """ 3055 hostname = get_managed_object_name(host_ref) 3056 if get_all_disk_groups: 3057 log.trace("Retrieving all disk groups on host '%s'", hostname) 3058 else: 3059 log.trace( 3060 "Retrieving disk groups from host '%s', with cache disk ids : (%s)", 3061 hostname, 3062 cache_disk_ids, 3063 ) 3064 if not cache_disk_ids: 3065 return [] 3066 try: 3067 vsan_host_config = host_ref.config.vsanHostConfig 3068 except vim.fault.NoPermission as exc: 3069 log.exception(exc) 3070 raise salt.exceptions.VMwareApiError( 3071 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3072 ) 3073 except vim.fault.VimFault as exc: 3074 log.exception(exc) 3075 raise salt.exceptions.VMwareApiError(exc.msg) 3076 except vmodl.RuntimeFault as exc: 3077 log.exception(exc) 3078 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3079 if not vsan_host_config: 3080 raise salt.exceptions.VMwareObjectRetrievalError( 3081 "No host config found on host '{}'".format(hostname) 3082 ) 3083 vsan_storage_info = vsan_host_config.storageInfo 3084 if not vsan_storage_info: 3085 raise salt.exceptions.VMwareObjectRetrievalError( 3086 "No vsan storage info found on host '{}'".format(hostname) 3087 ) 3088 vsan_disk_mappings = vsan_storage_info.diskMapping 3089 if not vsan_disk_mappings: 3090 return [] 3091 disk_groups = [ 3092 dm 3093 for dm in vsan_disk_mappings 3094 if (get_all_disk_groups or (dm.ssd.canonicalName in cache_disk_ids)) 3095 ] 3096 log.trace( 3097 "Retrieved disk groups on host '%s', with cache disk ids : %s", 3098 hostname, 3099 [d.ssd.canonicalName for d in disk_groups], 3100 ) 3101 return disk_groups 3102 3103 3104def _check_disks_in_diskgroup(disk_group, cache_disk_id, capacity_disk_ids): 3105 """ 3106 Checks that the disks in a disk group are as expected and raises 3107 CheckError exceptions if the check fails 3108 """ 3109 if not disk_group.ssd.canonicalName == cache_disk_id: 3110 raise salt.exceptions.ArgumentValueError( 3111 "Incorrect diskgroup cache disk; got id: '{}'; expected id: '{}'".format( 3112 disk_group.ssd.canonicalName, cache_disk_id 3113 ) 3114 ) 3115 non_ssd_disks = [d.canonicalName for d in disk_group.nonSsd] 3116 if sorted(non_ssd_disks) != sorted(capacity_disk_ids): 3117 raise salt.exceptions.ArgumentValueError( 3118 "Incorrect capacity disks; got ids: '{}'; expected ids: '{}'".format( 3119 sorted(non_ssd_disks), sorted(capacity_disk_ids) 3120 ) 3121 ) 3122 log.trace("Checked disks in diskgroup with cache disk id '%s'", cache_disk_id) 3123 return True 3124 3125 3126# TODO Support host caches on multiple datastores 3127def get_host_cache(host_ref, host_cache_manager=None): 3128 """ 3129 Returns a vim.HostScsiDisk if the host cache is configured on the specified 3130 host, other wise returns None 3131 3132 host_ref 3133 The vim.HostSystem object representing the host that contains the 3134 requested disks. 3135 3136 host_cache_manager 3137 The vim.HostCacheConfigurationManager object representing the cache 3138 configuration manager on the specified host. Default is None. If None, 3139 it will be retrieved in the method 3140 """ 3141 hostname = get_managed_object_name(host_ref) 3142 service_instance = get_service_instance_from_managed_object(host_ref) 3143 log.trace("Retrieving the host cache on host '%s'", hostname) 3144 if not host_cache_manager: 3145 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 3146 path="configManager.cacheConfigurationManager", 3147 type=vim.HostSystem, 3148 skip=False, 3149 ) 3150 results = get_mors_with_properties( 3151 service_instance, 3152 vim.HostCacheConfigurationManager, 3153 ["cacheConfigurationInfo"], 3154 container_ref=host_ref, 3155 traversal_spec=traversal_spec, 3156 ) 3157 if not results or not results[0].get("cacheConfigurationInfo"): 3158 log.trace("Host '%s' has no host cache", hostname) 3159 return None 3160 return results[0]["cacheConfigurationInfo"][0] 3161 else: 3162 results = get_properties_of_managed_object( 3163 host_cache_manager, ["cacheConfigurationInfo"] 3164 ) 3165 if not results: 3166 log.trace("Host '%s' has no host cache", hostname) 3167 return None 3168 return results["cacheConfigurationInfo"][0] 3169 3170 3171# TODO Support host caches on multiple datastores 3172def configure_host_cache( 3173 host_ref, datastore_ref, swap_size_MiB, host_cache_manager=None 3174): 3175 """ 3176 Configures the host cahe of the specified host 3177 3178 host_ref 3179 The vim.HostSystem object representing the host that contains the 3180 requested disks. 3181 3182 datastore_ref 3183 The vim.Datastore opject representing the datastore the host cache will 3184 be configured on. 3185 3186 swap_size_MiB 3187 The size in Mibibytes of the swap. 3188 3189 host_cache_manager 3190 The vim.HostCacheConfigurationManager object representing the cache 3191 configuration manager on the specified host. Default is None. If None, 3192 it will be retrieved in the method 3193 """ 3194 hostname = get_managed_object_name(host_ref) 3195 if not host_cache_manager: 3196 props = get_properties_of_managed_object( 3197 host_ref, ["configManager.cacheConfigurationManager"] 3198 ) 3199 if not props.get("configManager.cacheConfigurationManager"): 3200 raise salt.exceptions.VMwareObjectRetrievalError( 3201 "Host '{}' has no host cache".format(hostname) 3202 ) 3203 host_cache_manager = props["configManager.cacheConfigurationManager"] 3204 log.trace( 3205 "Configuring the host cache on host '%s', datastore '%s', swap size=%s MiB", 3206 hostname, 3207 datastore_ref.name, 3208 swap_size_MiB, 3209 ) 3210 3211 spec = vim.HostCacheConfigurationSpec( 3212 datastore=datastore_ref, swapSize=swap_size_MiB 3213 ) 3214 log.trace("host_cache_spec=%s", spec) 3215 try: 3216 task = host_cache_manager.ConfigureHostCache_Task(spec) 3217 except vim.fault.NoPermission as exc: 3218 log.exception(exc) 3219 raise salt.exceptions.VMwareApiError( 3220 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3221 ) 3222 except vim.fault.VimFault as exc: 3223 log.exception(exc) 3224 raise salt.exceptions.VMwareApiError(exc.msg) 3225 except vmodl.RuntimeFault as exc: 3226 log.exception(exc) 3227 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3228 wait_for_task(task, hostname, "HostCacheConfigurationTask") 3229 log.trace("Configured host cache on host '%s'", hostname) 3230 return True 3231 3232 3233def list_hosts(service_instance): 3234 """ 3235 Returns a list of hosts associated with a given service instance. 3236 3237 service_instance 3238 The Service Instance Object from which to obtain hosts. 3239 """ 3240 return list_objects(service_instance, vim.HostSystem) 3241 3242 3243def get_resource_pools( 3244 service_instance, 3245 resource_pool_names, 3246 datacenter_name=None, 3247 get_all_resource_pools=False, 3248): 3249 """ 3250 Retrieves resource pool objects 3251 3252 service_instance 3253 The service instance object to query the vCenter 3254 3255 resource_pool_names 3256 Resource pool names 3257 3258 datacenter_name 3259 Name of the datacenter where the resource pool is available 3260 3261 get_all_resource_pools 3262 Boolean 3263 3264 return 3265 Resourcepool managed object reference 3266 """ 3267 3268 properties = ["name"] 3269 if not resource_pool_names: 3270 resource_pool_names = [] 3271 if datacenter_name: 3272 container_ref = get_datacenter(service_instance, datacenter_name) 3273 else: 3274 container_ref = get_root_folder(service_instance) 3275 3276 resource_pools = get_mors_with_properties( 3277 service_instance, 3278 vim.ResourcePool, 3279 container_ref=container_ref, 3280 property_list=properties, 3281 ) 3282 3283 selected_pools = [] 3284 for pool in resource_pools: 3285 if get_all_resource_pools or (pool["name"] in resource_pool_names): 3286 selected_pools.append(pool["object"]) 3287 if not selected_pools: 3288 raise salt.exceptions.VMwareObjectRetrievalError( 3289 "The resource pools with properties " 3290 "names={} get_all={} could not be found".format( 3291 selected_pools, get_all_resource_pools 3292 ) 3293 ) 3294 3295 return selected_pools 3296 3297 3298def list_resourcepools(service_instance): 3299 """ 3300 Returns a list of resource pools associated with a given service instance. 3301 3302 service_instance 3303 The Service Instance Object from which to obtain resource pools. 3304 """ 3305 return list_objects(service_instance, vim.ResourcePool) 3306 3307 3308def list_networks(service_instance): 3309 """ 3310 Returns a list of networks associated with a given service instance. 3311 3312 service_instance 3313 The Service Instance Object from which to obtain networks. 3314 """ 3315 return list_objects(service_instance, vim.Network) 3316 3317 3318def list_vms(service_instance): 3319 """ 3320 Returns a list of VMs associated with a given service instance. 3321 3322 service_instance 3323 The Service Instance Object from which to obtain VMs. 3324 """ 3325 return list_objects(service_instance, vim.VirtualMachine) 3326 3327 3328def list_folders(service_instance): 3329 """ 3330 Returns a list of folders associated with a given service instance. 3331 3332 service_instance 3333 The Service Instance Object from which to obtain folders. 3334 """ 3335 return list_objects(service_instance, vim.Folder) 3336 3337 3338def list_dvs(service_instance): 3339 """ 3340 Returns a list of distributed virtual switches associated with a given service instance. 3341 3342 service_instance 3343 The Service Instance Object from which to obtain distributed virtual switches. 3344 """ 3345 return list_objects(service_instance, vim.DistributedVirtualSwitch) 3346 3347 3348def list_vapps(service_instance): 3349 """ 3350 Returns a list of vApps associated with a given service instance. 3351 3352 service_instance 3353 The Service Instance Object from which to obtain vApps. 3354 """ 3355 return list_objects(service_instance, vim.VirtualApp) 3356 3357 3358def list_portgroups(service_instance): 3359 """ 3360 Returns a list of distributed virtual portgroups associated with a given service instance. 3361 3362 service_instance 3363 The Service Instance Object from which to obtain distributed virtual switches. 3364 """ 3365 return list_objects(service_instance, vim.dvs.DistributedVirtualPortgroup) 3366 3367 3368def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level="debug"): 3369 """ 3370 Waits for a task to be completed. 3371 3372 task 3373 The task to wait for. 3374 3375 instance_name 3376 The name of the ESXi host, vCenter Server, or Virtual Machine that 3377 the task is being run on. 3378 3379 task_type 3380 The type of task being performed. Useful information for debugging purposes. 3381 3382 sleep_seconds 3383 The number of seconds to wait before querying the task again. 3384 Defaults to ``1`` second. 3385 3386 log_level 3387 The level at which to log task information. Default is ``debug``, 3388 but ``info`` is also supported. 3389 """ 3390 time_counter = 0 3391 start_time = time.time() 3392 log.trace("task = %s, task_type = %s", task, task.__class__.__name__) 3393 try: 3394 task_info = task.info 3395 except vim.fault.NoPermission as exc: 3396 log.exception(exc) 3397 raise salt.exceptions.VMwareApiError( 3398 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3399 ) 3400 except vim.fault.FileNotFound as exc: 3401 log.exception(exc) 3402 raise salt.exceptions.VMwareFileNotFoundError(exc.msg) 3403 except vim.fault.VimFault as exc: 3404 log.exception(exc) 3405 raise salt.exceptions.VMwareApiError(exc.msg) 3406 except vmodl.RuntimeFault as exc: 3407 log.exception(exc) 3408 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3409 while task_info.state == "running" or task_info.state == "queued": 3410 if time_counter % sleep_seconds == 0: 3411 msg = "[ {} ] Waiting for {} task to finish [{} s]".format( 3412 instance_name, task_type, time_counter 3413 ) 3414 if log_level == "info": 3415 log.info(msg) 3416 else: 3417 log.debug(msg) 3418 time.sleep(1.0 - ((time.time() - start_time) % 1.0)) 3419 time_counter += 1 3420 try: 3421 task_info = task.info 3422 except vim.fault.NoPermission as exc: 3423 log.exception(exc) 3424 raise salt.exceptions.VMwareApiError( 3425 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3426 ) 3427 except vim.fault.FileNotFound as exc: 3428 log.exception(exc) 3429 raise salt.exceptions.VMwareFileNotFoundError(exc.msg) 3430 except vim.fault.VimFault as exc: 3431 log.exception(exc) 3432 raise salt.exceptions.VMwareApiError(exc.msg) 3433 except vmodl.RuntimeFault as exc: 3434 log.exception(exc) 3435 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3436 if task_info.state == "success": 3437 msg = "[ {} ] Successfully completed {} task in {} seconds".format( 3438 instance_name, task_type, time_counter 3439 ) 3440 if log_level == "info": 3441 log.info(msg) 3442 else: 3443 log.debug(msg) 3444 # task is in a successful state 3445 return task_info.result 3446 else: 3447 # task is in an error state 3448 try: 3449 raise task_info.error 3450 except vim.fault.NoPermission as exc: 3451 log.exception(exc) 3452 raise salt.exceptions.VMwareApiError( 3453 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3454 ) 3455 except vim.fault.FileNotFound as exc: 3456 log.exception(exc) 3457 raise salt.exceptions.VMwareFileNotFoundError(exc.msg) 3458 except vim.fault.VimFault as exc: 3459 log.exception(exc) 3460 raise salt.exceptions.VMwareApiError(exc.msg) 3461 except vmodl.fault.SystemError as exc: 3462 log.exception(exc) 3463 raise salt.exceptions.VMwareSystemError(exc.msg) 3464 except vmodl.fault.InvalidArgument as exc: 3465 log.exception(exc) 3466 exc_message = exc.msg 3467 if exc.faultMessage: 3468 exc_message = "{} ({})".format(exc_message, exc.faultMessage[0].message) 3469 raise salt.exceptions.VMwareApiError(exc_message) 3470 3471 3472def get_vm_by_property( 3473 service_instance, 3474 name, 3475 datacenter=None, 3476 vm_properties=None, 3477 traversal_spec=None, 3478 parent_ref=None, 3479): 3480 """ 3481 Get virtual machine properties based on the traversal specs and properties list, 3482 returns Virtual Machine object with properties. 3483 3484 service_instance 3485 Service instance object to access vCenter 3486 3487 name 3488 Name of the virtual machine. 3489 3490 datacenter 3491 Datacenter name 3492 3493 vm_properties 3494 List of vm properties. 3495 3496 traversal_spec 3497 Traversal Spec object(s) for searching. 3498 3499 parent_ref 3500 Container Reference object for searching under a given object. 3501 """ 3502 if datacenter and not parent_ref: 3503 parent_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) 3504 if not vm_properties: 3505 vm_properties = [ 3506 "name", 3507 "config.hardware.device", 3508 "summary.storage.committed", 3509 "summary.storage.uncommitted", 3510 "summary.storage.unshared", 3511 "layoutEx.file", 3512 "config.guestFullName", 3513 "config.guestId", 3514 "guest.net", 3515 "config.hardware.memoryMB", 3516 "config.hardware.numCPU", 3517 "config.files.vmPathName", 3518 "summary.runtime.powerState", 3519 "guest.toolsStatus", 3520 ] 3521 vm_list = salt.utils.vmware.get_mors_with_properties( 3522 service_instance, 3523 vim.VirtualMachine, 3524 vm_properties, 3525 container_ref=parent_ref, 3526 traversal_spec=traversal_spec, 3527 ) 3528 vm_formatted = [vm for vm in vm_list if vm["name"] == name] 3529 if not vm_formatted: 3530 raise salt.exceptions.VMwareObjectRetrievalError( 3531 "The virtual machine was not found." 3532 ) 3533 elif len(vm_formatted) > 1: 3534 raise salt.exceptions.VMwareMultipleObjectsError( 3535 " ".join( 3536 [ 3537 "Multiple virtual machines were found with the" 3538 "same name, please specify a container." 3539 ] 3540 ) 3541 ) 3542 return vm_formatted[0] 3543 3544 3545def get_folder(service_instance, datacenter, placement, base_vm_name=None): 3546 """ 3547 Returns a Folder Object 3548 3549 service_instance 3550 Service instance object 3551 3552 datacenter 3553 Name of the datacenter 3554 3555 placement 3556 Placement dictionary 3557 3558 base_vm_name 3559 Existing virtual machine name (for cloning) 3560 """ 3561 log.trace("Retrieving folder information") 3562 if base_vm_name: 3563 vm_object = get_vm_by_property( 3564 service_instance, base_vm_name, vm_properties=["name"] 3565 ) 3566 vm_props = salt.utils.vmware.get_properties_of_managed_object( 3567 vm_object, properties=["parent"] 3568 ) 3569 if "parent" in vm_props: 3570 folder_object = vm_props["parent"] 3571 else: 3572 raise salt.exceptions.VMwareObjectRetrievalError( 3573 " ".join(["The virtual machine parent", "object is not defined"]) 3574 ) 3575 elif "folder" in placement: 3576 folder_objects = salt.utils.vmware.get_folders( 3577 service_instance, [placement["folder"]], datacenter 3578 ) 3579 if len(folder_objects) > 1: 3580 raise salt.exceptions.VMwareMultipleObjectsError( 3581 " ".join( 3582 [ 3583 "Multiple instances are available of the", 3584 "specified folder {}".format(placement["folder"]), 3585 ] 3586 ) 3587 ) 3588 folder_object = folder_objects[0] 3589 elif datacenter: 3590 datacenter_object = salt.utils.vmware.get_datacenter( 3591 service_instance, datacenter 3592 ) 3593 dc_props = salt.utils.vmware.get_properties_of_managed_object( 3594 datacenter_object, properties=["vmFolder"] 3595 ) 3596 if "vmFolder" in dc_props: 3597 folder_object = dc_props["vmFolder"] 3598 else: 3599 raise salt.exceptions.VMwareObjectRetrievalError( 3600 "The datacenter vm folder object is not defined" 3601 ) 3602 return folder_object 3603 3604 3605def get_placement(service_instance, datacenter, placement=None): 3606 """ 3607 To create a virtual machine a resource pool needs to be supplied, we would like to use the strictest as possible. 3608 3609 datacenter 3610 Name of the datacenter 3611 3612 placement 3613 Dictionary with the placement info, cluster, host resource pool name 3614 3615 return 3616 Resource pool, cluster and host object if any applies 3617 """ 3618 log.trace("Retrieving placement information") 3619 resourcepool_object, placement_object = None, None 3620 if "host" in placement: 3621 host_objects = get_hosts( 3622 service_instance, datacenter_name=datacenter, host_names=[placement["host"]] 3623 ) 3624 if not host_objects: 3625 raise salt.exceptions.VMwareObjectRetrievalError( 3626 " ".join( 3627 [ 3628 "The specified host", 3629 "{} cannot be found.".format(placement["host"]), 3630 ] 3631 ) 3632 ) 3633 try: 3634 host_props = get_properties_of_managed_object( 3635 host_objects[0], properties=["resourcePool"] 3636 ) 3637 resourcepool_object = host_props["resourcePool"] 3638 except vmodl.query.InvalidProperty: 3639 traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( 3640 path="parent", 3641 skip=True, 3642 type=vim.HostSystem, 3643 selectSet=[ 3644 vmodl.query.PropertyCollector.TraversalSpec( 3645 path="resourcePool", skip=False, type=vim.ClusterComputeResource 3646 ) 3647 ], 3648 ) 3649 resourcepools = get_mors_with_properties( 3650 service_instance, 3651 vim.ResourcePool, 3652 container_ref=host_objects[0], 3653 property_list=["name"], 3654 traversal_spec=traversal_spec, 3655 ) 3656 if resourcepools: 3657 resourcepool_object = resourcepools[0]["object"] 3658 else: 3659 raise salt.exceptions.VMwareObjectRetrievalError( 3660 "The resource pool of host {} cannot be found.".format( 3661 placement["host"] 3662 ) 3663 ) 3664 placement_object = host_objects[0] 3665 elif "resourcepool" in placement: 3666 resourcepool_objects = get_resource_pools( 3667 service_instance, [placement["resourcepool"]], datacenter_name=datacenter 3668 ) 3669 if len(resourcepool_objects) > 1: 3670 raise salt.exceptions.VMwareMultipleObjectsError( 3671 " ".join( 3672 [ 3673 "Multiple instances are available of the", 3674 "specified host {}.".format(placement["host"]), 3675 ] 3676 ) 3677 ) 3678 resourcepool_object = resourcepool_objects[0] 3679 res_props = get_properties_of_managed_object( 3680 resourcepool_object, properties=["parent"] 3681 ) 3682 if "parent" in res_props: 3683 placement_object = res_props["parent"] 3684 else: 3685 raise salt.exceptions.VMwareObjectRetrievalError( 3686 " ".join(["The resource pool's parent", "object is not defined"]) 3687 ) 3688 elif "cluster" in placement: 3689 datacenter_object = get_datacenter(service_instance, datacenter) 3690 cluster_object = get_cluster(datacenter_object, placement["cluster"]) 3691 clus_props = get_properties_of_managed_object( 3692 cluster_object, properties=["resourcePool"] 3693 ) 3694 if "resourcePool" in clus_props: 3695 resourcepool_object = clus_props["resourcePool"] 3696 else: 3697 raise salt.exceptions.VMwareObjectRetrievalError( 3698 " ".join(["The cluster's resource pool", "object is not defined"]) 3699 ) 3700 placement_object = cluster_object 3701 else: 3702 # We are checking the schema for this object, this exception should never be raised 3703 raise salt.exceptions.VMwareObjectRetrievalError( 3704 " ".join(["Placement is not defined."]) 3705 ) 3706 return (resourcepool_object, placement_object) 3707 3708 3709def convert_to_kb(unit, size): 3710 """ 3711 Converts the given size to KB based on the unit, returns a long integer. 3712 3713 unit 3714 Unit of the size eg. GB; Note: to VMware a GB is the same as GiB = 1024MiB 3715 size 3716 Number which represents the size 3717 """ 3718 if unit.lower() == "gb": 3719 # vCenter needs long value 3720 target_size = int(size * 1024 * 1024) 3721 elif unit.lower() == "mb": 3722 target_size = int(size * 1024) 3723 elif unit.lower() == "kb": 3724 target_size = int(size) 3725 else: 3726 raise salt.exceptions.ArgumentValueError("The unit is not specified") 3727 return {"size": target_size, "unit": "KB"} 3728 3729 3730def power_cycle_vm(virtual_machine, action="on"): 3731 """ 3732 Powers on/off a virtual machine specified by its name. 3733 3734 virtual_machine 3735 vim.VirtualMachine object to power on/off virtual machine 3736 3737 action 3738 Operation option to power on/off the machine 3739 """ 3740 if action == "on": 3741 try: 3742 task = virtual_machine.PowerOn() 3743 task_name = "power on" 3744 except vim.fault.NoPermission as exc: 3745 log.exception(exc) 3746 raise salt.exceptions.VMwareApiError( 3747 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3748 ) 3749 except vim.fault.VimFault as exc: 3750 log.exception(exc) 3751 raise salt.exceptions.VMwareApiError(exc.msg) 3752 except vmodl.RuntimeFault as exc: 3753 log.exception(exc) 3754 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3755 elif action == "off": 3756 try: 3757 task = virtual_machine.PowerOff() 3758 task_name = "power off" 3759 except vim.fault.NoPermission as exc: 3760 log.exception(exc) 3761 raise salt.exceptions.VMwareApiError( 3762 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3763 ) 3764 except vim.fault.VimFault as exc: 3765 log.exception(exc) 3766 raise salt.exceptions.VMwareApiError(exc.msg) 3767 except vmodl.RuntimeFault as exc: 3768 log.exception(exc) 3769 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3770 else: 3771 raise salt.exceptions.ArgumentValueError("The given action is not supported") 3772 try: 3773 wait_for_task(task, get_managed_object_name(virtual_machine), task_name) 3774 except salt.exceptions.VMwareFileNotFoundError as exc: 3775 raise salt.exceptions.VMwarePowerOnError( 3776 " ".join( 3777 [ 3778 "An error occurred during power", 3779 "operation, a file was not found: {}".format(exc), 3780 ] 3781 ) 3782 ) 3783 return virtual_machine 3784 3785 3786def create_vm( 3787 vm_name, vm_config_spec, folder_object, resourcepool_object, host_object=None 3788): 3789 """ 3790 Creates virtual machine from config spec 3791 3792 vm_name 3793 Virtual machine name to be created 3794 3795 vm_config_spec 3796 Virtual Machine Config Spec object 3797 3798 folder_object 3799 vm Folder managed object reference 3800 3801 resourcepool_object 3802 Resource pool object where the machine will be created 3803 3804 host_object 3805 Host object where the machine will ne placed (optional) 3806 3807 return 3808 Virtual Machine managed object reference 3809 """ 3810 try: 3811 if host_object and isinstance(host_object, vim.HostSystem): 3812 task = folder_object.CreateVM_Task( 3813 vm_config_spec, pool=resourcepool_object, host=host_object 3814 ) 3815 else: 3816 task = folder_object.CreateVM_Task(vm_config_spec, pool=resourcepool_object) 3817 except vim.fault.NoPermission as exc: 3818 log.exception(exc) 3819 raise salt.exceptions.VMwareApiError( 3820 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3821 ) 3822 except vim.fault.VimFault as exc: 3823 log.exception(exc) 3824 raise salt.exceptions.VMwareApiError(exc.msg) 3825 except vmodl.RuntimeFault as exc: 3826 log.exception(exc) 3827 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3828 vm_object = wait_for_task(task, vm_name, "CreateVM Task", 10, "info") 3829 return vm_object 3830 3831 3832def register_vm(datacenter, name, vmx_path, resourcepool_object, host_object=None): 3833 """ 3834 Registers a virtual machine to the inventory with the given vmx file, on success 3835 it returns the vim.VirtualMachine managed object reference 3836 3837 datacenter 3838 Datacenter object of the virtual machine, vim.Datacenter object 3839 3840 name 3841 Name of the virtual machine 3842 3843 vmx_path: 3844 Full path to the vmx file, datastore name should be included 3845 3846 resourcepool 3847 Placement resource pool of the virtual machine, vim.ResourcePool object 3848 3849 host 3850 Placement host of the virtual machine, vim.HostSystem object 3851 """ 3852 try: 3853 if host_object: 3854 task = datacenter.vmFolder.RegisterVM_Task( 3855 path=vmx_path, 3856 name=name, 3857 asTemplate=False, 3858 host=host_object, 3859 pool=resourcepool_object, 3860 ) 3861 else: 3862 task = datacenter.vmFolder.RegisterVM_Task( 3863 path=vmx_path, name=name, asTemplate=False, pool=resourcepool_object 3864 ) 3865 except vim.fault.NoPermission as exc: 3866 log.exception(exc) 3867 raise salt.exceptions.VMwareApiError( 3868 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3869 ) 3870 except vim.fault.VimFault as exc: 3871 log.exception(exc) 3872 raise salt.exceptions.VMwareApiError(exc.msg) 3873 except vmodl.RuntimeFault as exc: 3874 log.exception(exc) 3875 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3876 try: 3877 vm_ref = wait_for_task(task, name, "RegisterVM Task") 3878 except salt.exceptions.VMwareFileNotFoundError as exc: 3879 raise salt.exceptions.VMwareVmRegisterError( 3880 "An error occurred during registration operation, the " 3881 "configuration file was not found: {}".format(exc) 3882 ) 3883 return vm_ref 3884 3885 3886def update_vm(vm_ref, vm_config_spec): 3887 """ 3888 Updates the virtual machine configuration with the given object 3889 3890 vm_ref 3891 Virtual machine managed object reference 3892 3893 vm_config_spec 3894 Virtual machine config spec object to update 3895 """ 3896 vm_name = get_managed_object_name(vm_ref) 3897 log.trace("Updating vm '%s'", vm_name) 3898 try: 3899 task = vm_ref.ReconfigVM_Task(vm_config_spec) 3900 except vim.fault.NoPermission as exc: 3901 log.exception(exc) 3902 raise salt.exceptions.VMwareApiError( 3903 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3904 ) 3905 except vim.fault.VimFault as exc: 3906 log.exception(exc) 3907 raise salt.exceptions.VMwareApiError(exc.msg) 3908 except vmodl.RuntimeFault as exc: 3909 log.exception(exc) 3910 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3911 vm_ref = wait_for_task(task, vm_name, "ReconfigureVM Task") 3912 return vm_ref 3913 3914 3915def delete_vm(vm_ref): 3916 """ 3917 Destroys the virtual machine 3918 3919 vm_ref 3920 Managed object reference of a virtual machine object 3921 """ 3922 vm_name = get_managed_object_name(vm_ref) 3923 log.trace("Destroying vm '%s'", vm_name) 3924 try: 3925 task = vm_ref.Destroy_Task() 3926 except vim.fault.NoPermission as exc: 3927 log.exception(exc) 3928 raise salt.exceptions.VMwareApiError( 3929 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3930 ) 3931 except vim.fault.VimFault as exc: 3932 log.exception(exc) 3933 raise salt.exceptions.VMwareApiError(exc.msg) 3934 except vmodl.RuntimeFault as exc: 3935 log.exception(exc) 3936 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3937 wait_for_task(task, vm_name, "Destroy Task") 3938 3939 3940def unregister_vm(vm_ref): 3941 """ 3942 Destroys the virtual machine 3943 3944 vm_ref 3945 Managed object reference of a virtual machine object 3946 """ 3947 vm_name = get_managed_object_name(vm_ref) 3948 log.trace("Destroying vm '%s'", vm_name) 3949 try: 3950 vm_ref.UnregisterVM() 3951 except vim.fault.NoPermission as exc: 3952 log.exception(exc) 3953 raise salt.exceptions.VMwareApiError( 3954 "Not enough permissions. Required privilege: {}".format(exc.privilegeId) 3955 ) 3956 except vim.fault.VimFault as exc: 3957 raise salt.exceptions.VMwareApiError(exc.msg) 3958 except vmodl.RuntimeFault as exc: 3959 raise salt.exceptions.VMwareRuntimeError(exc.msg) 3960