diff --git a/lib/ansible/plugins/connection/vmware_tools.py b/lib/ansible/plugins/connection/vmware_tools.py index 3ce79f9e38e..6ce4f8731fe 100644 --- a/lib/ansible/plugins/connection/vmware_tools.py +++ b/lib/ansible/plugins/connection/vmware_tools.py @@ -37,7 +37,7 @@ from ansible.module_utils.basic import missing_required_lib try: from pyVim.connect import Disconnect, SmartConnect, SmartConnectNoSSL - from pyVmomi import vim + from pyVmomi import vim, vmodl HAS_PYVMOMI = True except ImportError: @@ -279,6 +279,7 @@ class Connection(ConnectionBase): self.allow_executable = False self.has_pipelining = False self.allow_extras = True + self._si = None def _establish_connection(self): connection_kwargs = { @@ -304,7 +305,7 @@ class Connection(ConnectionBase): except vim.fault.InvalidLogin as e: raise AnsibleError("Connection Login Error: %s" % to_native(e.msg)) - def _establish_vm(self): + def _establish_vm(self, check_vm_credentials=True): searchIndex = self._si.content.searchIndex self.vm = searchIndex.FindByInventoryPath(self.get_option("vm_path")) @@ -316,7 +317,8 @@ class Connection(ConnectionBase): ) try: - self.authManager.ValidateCredentialsInGuest(vm=self.vm, auth=self.vm_auth) + if check_vm_credentials: + self.authManager.ValidateCredentialsInGuest(vm=self.vm, auth=self.vm_auth) except vim.fault.InvalidPowerState as e: raise AnsibleError("VM Power State Error: %s" % to_native(e.msg)) except vim.fault.RestrictedVersion as e: @@ -327,8 +329,19 @@ class Connection(ConnectionBase): raise AnsibleError("VM Login Error: %s" % to_native(e.msg)) except vim.fault.NoPermission as e: raise AnsibleConnectionFailure("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) - - def _connect(self): + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") + + def _connect(self, check_vm_credentials=True): if not HAS_REQUESTS: raise AnsibleError("%s : %s" % (missing_required_lib('requests'), REQUESTS_IMP_ERR)) @@ -337,13 +350,10 @@ class Connection(ConnectionBase): super(Connection, self)._connect() - if self.connected: - pass - - self._establish_connection() - self._establish_vm() - - self._connected = True + if not self.connected: + self._establish_connection() + self._establish_vm(check_vm_credentials=check_vm_credentials) + self._connected = True def close(self): """Close connection.""" @@ -353,11 +363,12 @@ class Connection(ConnectionBase): self._connected = False def reset(self): - """Reset the connection.""" - super(Connection, self).reset() - + """Reset the connection to vcenter.""" + # TODO: Fix persistent connection implementation currently ansible creates new connections to vcenter for each task + # therefore we're currently closing a non existing connection here and establish a connection just for being thrown away + # right afterwards. self.close() - self._connect() + self._connect(check_vm_credentials=False) def create_temporary_file_in_guest(self, prefix="", suffix=""): """Create a temporary file in the VM.""" @@ -365,6 +376,17 @@ class Connection(ConnectionBase): return self.fileManager.CreateTemporaryFileInGuest(vm=self.vm, auth=self.vm_auth, prefix=prefix, suffix=suffix) except vim.fault.NoPermission as e: raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") def _get_program_spec_program_path_and_arguments(self, cmd): if self.windowsGuest: @@ -398,6 +420,22 @@ class Connection(ConnectionBase): processes = self.processManager.ListProcessesInGuest(vm=self.vm, auth=self.vm_auth, pids=[pid]) except vim.fault.NoPermission as e: raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) + except vmodl.fault.SystemError as e: + # https://pubs.vmware.com/vsphere-6-5/index.jsp?topic=%2Fcom.vmware.wssdk.smssdk.doc%2Fvmodl.fault.SystemError.html + # https://github.com/ansible/ansible/issues/57607 + if e.reason == 'vix error codes = (1, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, Netlogon service stopped or dcpromo in progress. Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection plugin failed. Reason: %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") + except vim.fault.InvalidGuestLogin: + raise AnsibleConnectionFailure("Guest login failed. Native error: InvalidGuestLogin") + return processes[0] def _fix_url_for_hosts(self, url): @@ -417,6 +455,17 @@ class Connection(ConnectionBase): fileTransferInformation = self.fileManager.InitiateFileTransferFromGuest(vm=self.vm, auth=self.vm_auth, guestFilePath=guestFilePath) except vim.fault.NoPermission as e: raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") url = self._fix_url_for_hosts(fileTransferInformation.url) response = requests.get(url, verify=self.validate_certs, stream=True) @@ -432,6 +481,17 @@ class Connection(ConnectionBase): self.fileManager.DeleteFileInGuest(vm=self.vm, auth=self.vm_auth, filePath=filePath) except vim.fault.NoPermission as e: raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") def exec_command(self, cmd, in_data=None, sudoable=True): """Execute command.""" @@ -448,6 +508,17 @@ class Connection(ConnectionBase): raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) except vim.fault.FileNotFound as e: raise AnsibleError("StartProgramInGuest Error: %s" % to_native(e.msg)) + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") pid_info = self._get_pid_info(pid) @@ -486,6 +557,17 @@ class Connection(ConnectionBase): ) except vim.fault.NoPermission as e: raise AnsibleError("No Permission Error: %s %s" % (to_native(e.msg), to_native(e.privilegeId))) + except vmodl.fault.SystemError as e: + if e.reason == 'vix error codes = (3016, 0).\n': + raise AnsibleConnectionFailure( + "Connection failed, is the vm currently rebooting? Reason: %s" % ( + to_native(e.reason) + ) + ) + else: + raise AnsibleConnectionFailure("Connection failed. Reason %s" % (to_native(e.reason))) + except vim.fault.GuestOperationsUnavailable: + raise AnsibleConnectionFailure("Cannot connect to guest. Native error: GuestOperationsUnavailable") url = self._fix_url_for_hosts(put_url)