def _execute_accelerate_module(self): args = "password=%s port=%s minutes=%d debug=%d ipv6=%s" % ( base64.b64encode(self.key.__str__()), str(self.accport), constants.ACCELERATE_DAEMON_TIMEOUT, int(utils.VERBOSITY), self.runner.accelerate_ipv6, ) if constants.ACCELERATE_MULTI_KEY: args += " multi_key=yes" inject = dict(password=self.key) if getattr(self.runner, 'accelerate_inventory_host', False): inject = utils.combine_vars( inject, self.runner.inventory.get_variables( self.runner.accelerate_inventory_host)) else: inject = utils.combine_vars( inject, self.runner.inventory.get_variables(self.host)) vvvv("attempting to start up the accelerate daemon...") self.ssh.connect() tmp_path = self.runner._make_tmp_path(self.ssh) return self.runner._execute_module(self.ssh, tmp_path, 'accelerate', args, inject=inject)
def connect(self, allow_ssh=True): ''' activates the connection object ''' try: if not self.is_connected: tries = 3 self.conn = socket.socket() self.conn.settimeout(constants.ACCELERATE_CONNECT_TIMEOUT) vvvv("attempting connection to %s via the accelerated port %d" % (self.host,self.accport)) while tries > 0: try: self.conn.connect((self.host,self.accport)) break except: vvvv("failed, retrying...") time.sleep(0.1) tries -= 1 if tries == 0: vvv("Could not connect via the accelerated connection, exceeded # of tries") raise errors.AnsibleError("Failed to connect") self.conn.settimeout(constants.ACCELERATE_TIMEOUT) except: if allow_ssh: vvv("Falling back to ssh to startup accelerated mode") res = self._execute_accelerate_module() if not res.is_successful(): raise errors.AnsibleError("Failed to launch the accelerated daemon on %s (reason: %s)" % (self.host,res.result.get('msg'))) return self.connect(allow_ssh=False) else: raise errors.AnsibleError("Failed to connect to %s:%s" % (self.host,self.accport)) self.is_connected = True return self
def _winrm_exec(self, command, args=(), from_exec=False): if from_exec: vvvv("WINRM EXEC %r %r" % (command, args), host=self.host) else: vvvvv("WINRM EXEC %r %r" % (command, args), host=self.host) if not self.protocol: self.protocol = self._winrm_connect() if not self.shell_id: self.shell_id = self.protocol.open_shell() command_id = None try: command_id = self.protocol.run_command(self.shell_id, command, args) response = Response( self.protocol.get_command_output(self.shell_id, command_id)) if from_exec: vvvv('WINRM RESULT %r' % response, host=self.host) else: vvvvv('WINRM RESULT %r' % response, host=self.host) vvvvv('WINRM STDOUT %s' % response.std_out, host=self.host) vvvvv('WINRM STDERR %s' % response.std_err, host=self.host) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: raise errors.AnsibleError( "Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.become and sudoable: cmd, prompt, success_key = utils.make_become_cmd( cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def processEnded(self, status): vvvv('processEnded %r' % (status,)) if isinstance(status.value, error.ProcessTerminated): self.rc = 1 self.done.errback(status.value) else: self.rc = 0 self.done.callback(status.value)
def _winrm_get_script_cmd(self, script): ''' Convert a PowerShell script to a single base64-encoded command. ''' vvvv('WINRM SCRIPT: %s' % script, host=self.host) encoded_script = base64.b64encode(script.encode('utf-16-le')) return ['PowerShell', '-NoProfile', '-NonInteractive', '-EncodedCommand', encoded_script]
def copyFile(self, path, producer): """ XXX """ vvvv('PUT %r %r' % (path, producer)) # XXX brutish version brute = _StdinConsumer(producer) return self._spawnProcess(brute, 'cat > %s' % (pipes.quote(path),))
def _spawnProcess(self, protocol, command): vvvv('ACTUALLY SPAWN: %r' % (command,)) factory = Factory() factory.protocol = lambda: _CommandProtocol(protocol) e = SSHCommandClientEndpoint.existingConnection( self.master_proto.transport.conn, command) d = e.connect(factory) vvvv('STARTING') return d.addCallbacks(self._commandStarted, self._commandFailedToStart)
def _winrm_get_script_cmd(self, script): ''' Convert a PowerShell script to a single base64-encoded command. ''' vvvv('WINRM SCRIPT: %s' % script, host=self.host) encoded_script = base64.b64encode(script.encode('utf-16-le')) return [ 'PowerShell', '-NoProfile', '-NonInteractive', '-EncodedCommand', encoded_script ]
def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise AnsibleFileNotFound("file or module does not exist: %s" % in_path) fd = file(in_path, 'rb') fstat = os.stat(in_path) try: vvv("PUT file is %d bytes" % fstat.st_size) last = False while fd.tell() <= fstat.st_size and not last: vvvv("file position currently %ld, file size is %ld" % (fd.tell(), fstat.st_size)) data = fd.read(CHUNK_SIZE) if fd.tell() >= fstat.st_size: last = True data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last) if self.runner.become: data['user'] = self.runner.become_user data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("failed to send the file to %s" % self.host) response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise AnsibleError( "failed to put the file in the requested location") finally: fd.close() vvvv("waiting for final response after PUT") response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed', False): raise AnsibleError( "failed to put the file in the requested location")
def connect(self, allow_ssh=True): ''' activates the connection object ''' try: if not self.is_connected: wrong_user = False tries = 3 self.conn = socket.socket() self.conn.settimeout(constants.ACCELERATE_CONNECT_TIMEOUT) vvvv( "attempting connection to %s via the accelerated port %d" % (self.host, self.accport)) while tries > 0: try: self.conn.connect((self.host, self.accport)) break except socket.error: vvvv("connection to %s failed, retrying..." % self.host) time.sleep(0.1) tries -= 1 if tries == 0: vvv("Could not connect via the accelerated connection, exceeded # of tries" ) raise AnsibleError("FAILED") elif wrong_user: vvv("Restarting daemon with a different remote_user") raise AnsibleError("WRONG_USER") self.conn.settimeout(constants.ACCELERATE_TIMEOUT) if not self.validate_user(): # the accelerated daemon was started with a # different remote_user. The above command # should have caused the accelerate daemon to # shutdown, so we'll reconnect. wrong_user = True except AnsibleError as e: if allow_ssh: if "WRONG_USER" in e: vvv("Switching users, waiting for the daemon on %s to shutdown completely..." % self.host) time.sleep(5) vvv("Falling back to ssh to startup accelerated mode") res = self._execute_accelerate_module() if not res.is_successful(): raise AnsibleError( "Failed to launch the accelerated daemon on %s (reason: %s)" % (self.host, res.result.get('msg'))) return self.connect(allow_ssh=False) else: raise AnsibleError("Failed to connect to %s:%s" % (self.host, self.accport)) self.is_connected = True return self
def _execute_accelerate_module(self): args = "password=%s port=%s debug=%d ipv6=%s" % (base64.b64encode(self.key.__str__()), str(self.accport), int(utils.VERBOSITY), self.runner.accelerate_ipv6) inject = dict(password=self.key) if getattr(self.runner, 'accelerate_inventory_host', False): inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.runner.accelerate_inventory_host)) else: inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.host)) vvvv("attempting to start up the accelerate daemon...") self.ssh.connect() tmp_path = self.runner._make_tmp_path(self.ssh) return self.runner._execute_module(self.ssh, tmp_path, 'accelerate', args, inject=inject)
def _execute_accelerate_module(self): args = "password=%s port=%s debug=%d" % (base64.b64encode(self.key.__str__()), str(self.accport), int(utils.VERBOSITY)) inject = dict(password=self.key) if self.runner.accelerate_inventory_host: inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.runner.accelerate_inventory_host)) else: inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.host)) vvvv("attempting to start up the accelerate daemon...") self.ssh.connect() tmp_path = self.runner._make_tmp_path(self.ssh) return self.runner._execute_module(self.ssh, tmp_path, 'accelerate', args, inject=inject)
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' # get winrm-specific connection vars host_vars = self.runner.inventory._hosts_cache[self.delegate].get_variables() port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) exc = None for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']: if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self.user): continue if transport == 'kerberos': realm = self.user.split('@', 1)[1].strip() or None else: realm = None endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) self._winrm_kwargs = dict(username=self.user, password=self.password, realm=realm) argspec = inspect.getargspec(Protocol.__init__) for arg in argspec.args: if arg in ('self', 'endpoint', 'transport', 'username', 'password', 'realm'): continue if 'ansible_winrm_%s' % arg in host_vars: self._winrm_kwargs[arg] = host_vars['ansible_winrm_%s' % arg] vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, **self._winrm_kwargs) try: protocol.send_message('') return protocol except WinRMTransportError, exc: err_msg = str(exc) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise errors.AnsibleError("the connection attempt timed out") m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 401: raise errors.AnsibleError("the username/password specified for this server was incorrect") elif code == 411: return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): cmd = cmd.encode('utf-8') vvv("EXEC %s" % cmd, host=self.host) cmd_parts = shlex.split(cmd, posix=False) vvvv("WINRM PARTS %r" % cmd_parts, host=self.host) if _winrm_hacks: cmd_parts = _winrm_hacks.filter_cmd_parts(self, cmd_parts) if not cmd_parts: vvv('WINRM NOOP') return (0, '', '', '') try: result = self._winrm_exec(cmd_parts[0], cmd_parts[1:]) except Exception, e: traceback.print_exc() raise errors.AnsibleError("failed to exec cmd %s" % cmd)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def connect(self, allow_ssh=True): """ activates the connection object """ try: if not self.is_connected: wrong_user = False tries = 3 self.conn = socket.socket() self.conn.settimeout(constants.ACCELERATE_CONNECT_TIMEOUT) vvvv("attempting connection to %s via the accelerated port %d" % (self.host, self.accport)) while tries > 0: try: self.conn.connect((self.host, self.accport)) break except socket.error: vvvv("connection to %s failed, retrying..." % self.host) time.sleep(0.1) tries -= 1 if tries == 0: vvv("Could not connect via the accelerated connection, exceeded # of tries") raise AnsibleError("FAILED") elif wrong_user: vvv("Restarting daemon with a different remote_user") raise AnsibleError("WRONG_USER") self.conn.settimeout(constants.ACCELERATE_TIMEOUT) if not self.validate_user(): # the accelerated daemon was started with a # different remote_user. The above command # should have caused the accelerate daemon to # shutdown, so we'll reconnect. wrong_user = True except AnsibleError, e: if allow_ssh: if "WRONG_USER" in e: vvv("Switching users, waiting for the daemon on %s to shutdown completely..." % self.host) time.sleep(5) vvv("Falling back to ssh to startup accelerated mode") res = self._execute_accelerate_module() if not res.is_successful(): raise AnsibleError( "Failed to launch the accelerated daemon on %s (reason: %s)" % (self.host, res.result.get("msg")) ) return self.connect(allow_ssh=False) else: raise AnsibleError("Failed to connect to %s:%s" % (self.host, self.accport))
def spawnProcess(self, protocol, command, sudo=False, sudo_user=None): vvvv('SPAWN %r' % (command,)) # copy the script over from hashlib import sha1 filename = '/tmp/script%s' % (sha1(command).hexdigest(),) new_command = '/bin/sh %s' % (pipes.quote(filename),) if sudo_user and sudo_user != 'root': new_command = 'su - %s %s' % (sudo_user, new_command) if sudo: new_command = 'sudo ' + new_command d = self.copyFile(filename, FileBodyProducer(StringIO(command))) # then run it d.addCallback(lambda _: self._spawnProcess(protocol, new_command)) return d
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) exc = None for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']: if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self.user): continue if transport == 'kerberos': realm = self.user.split('@', 1)[1].strip() or None else: realm = None endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, username=self.user, password=self.password, realm=realm) try: protocol.send_message('') return protocol except WinRMTransportError, exc: err_msg = str(exc) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise errors.AnsibleError( "the connection attempt timed out") m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 401: raise errors.AnsibleError( "the username/password specified for this server was incorrect" ) elif code == 411: return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def put_file(self, in_path, out_path): ''' transfer a file from local to remote ''' vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) fd = file(in_path, 'rb') fstat = os.stat(in_path) try: vvv("PUT file is %d bytes" % fstat.st_size) last = False while fd.tell() <= fstat.st_size and not last: vvvv("file position currently %ld, file size is %ld" % (fd.tell(), fstat.st_size)) data = fd.read(CHUNK_SIZE) if fd.tell() >= fstat.st_size: last = True data = dict(mode='put', data=base64.b64encode(data), out_path=out_path, last=last) if self.runner.sudo: data['user'] = self.runner.sudo_user data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("failed to send the file to %s" % self.host) response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed',False): raise errors.AnsibleError("failed to put the file in the requested location") finally: fd.close() vvvv("waiting for final response after PUT") response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if response.get('failed',False): raise errors.AnsibleError("failed to put the file in the requested location")
def exec_command(self, cmd, tmp_path, become_user=None, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if sudoable and self.runner.become and self.runner.become_method not in self.become_methods_supported: raise AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.become and sudoable: cmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def put_file(self, in_path, out_path): vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound( "file or module does not exist: %s" % in_path) with open(in_path) as in_file: in_size = os.path.getsize(in_path) script_template = ''' $s = [System.IO.File]::OpenWrite("%s"); [void]$s.Seek(%d, [System.IO.SeekOrigin]::Begin); $b = [System.Convert]::FromBase64String("%s"); [void]$s.Write($b, 0, $b.length); [void]$s.SetLength(%d); [void]$s.Close(); ''' # Determine max size of data we can pass per command. script = script_template % (powershell._escape(out_path), in_size, '', in_size) cmd = powershell._encode_script(script) # Encode script with no data, subtract its length from 8190 (max # windows command length), divide by 2.67 (UTF16LE base64 command # encoding), then by 1.35 again (data base64 encoding). buffer_size = int(((8190 - len(cmd)) / 2.67) / 1.35) for offset in xrange(0, in_size, buffer_size): try: out_data = in_file.read(buffer_size) if offset == 0: if out_data.lower().startswith( '#!powershell' ) and not out_path.lower().endswith('.ps1'): out_path = out_path + '.ps1' b64_data = base64.b64encode(out_data) script = script_template % (powershell._escape(out_path), offset, b64_data, in_size) vvvv("WINRM PUT %s to %s (offset=%d size=%d)" % (in_path, out_path, offset, len(out_data)), host=self.host) cmd_parts = powershell._encode_script(script, as_list=True) result = self._winrm_exec(cmd_parts[0], cmd_parts[1:]) if result.status_code != 0: raise IOError(result.std_err.encode('utf-8')) except Exception: traceback.print_exc() raise errors.AnsibleError("failed to transfer file to %s" % out_path)
def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs): vv("Kind of verbose") vvv("Verbose") vvvv("Lookout!") vvvvv("Super custom verbosity") return ReturnData(conn=conn, comm_ok=True, result={ "failed": False, "changed": False })
def _execute_accelerate_module(self): args = "password=%s port=%s minutes=%d debug=%d ipv6=%s" % ( base64.b64encode(self.key.__str__()), str(self.accport), constants.ACCELERATE_DAEMON_TIMEOUT, int(utils.VERBOSITY), self.runner.accelerate_ipv6, ) if constants.ACCELERATE_MULTI_KEY: args += " multi_key=yes" inject = dict(password=self.key) if getattr(self.runner, 'accelerate_inventory_host', False): inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.runner.accelerate_inventory_host)) else: inject = utils.combine_vars(inject, self.runner.inventory.get_variables(self.host)) vvvv("attempting to start up the accelerate daemon...") self.ssh.connect() tmp_path = self.runner._make_tmp_path(self.ssh) return self.runner._execute_module(self.ssh, tmp_path, 'accelerate', args, inject=inject)
def exec_command( self, cmd, tmp_path, sudo_user=None, sudoable=False, executable="/bin/sh", in_data=None, su=None, su_user=None ): """ run a command on the remote host """ if su or su_user: raise AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict(mode="command", cmd=cmd, tmp_path=tmp_path, executable=executable) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get("rc", None), "", response.get("stdout", ""), response.get("stderr", ""))
def validate_user(self): ''' Checks the remote uid of the accelerated daemon vs. the one specified for this play and will cause the accel daemon to exit if they don't match ''' data = dict( mode='validate_user', username=self.user, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break if response.get('failed'): raise errors.AnsibleError("Error while validating user: %s" % response.get("msg")) else: return response.get('rc') == 0
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) exc = None for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']: if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self.user): continue if transport == 'kerberos': realm = self.user.split('@', 1)[1].strip() or None else: realm = None endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, username=self.user, password=self.password, realm=realm) try: protocol.send_message('') return protocol except WinRMTransportError, exc: err_msg = str(exc) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise errors.AnsibleError("the connection attempt timed out") m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 401: raise errors.AnsibleError("the username/password specified for this server was incorrect") elif code == 411: return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def _winrm_exec(self, command, args=(), from_exec=False): if from_exec: vvvv("WINRM EXEC %r %r" % (command, args), host=self.host) else: vvvvv("WINRM EXEC %r %r" % (command, args), host=self.host) if not self.protocol: self.protocol = self._winrm_connect() if not self.shell_id: self.shell_id = self.protocol.open_shell() command_id = None try: command_id = self.protocol.run_command(self.shell_id, command, args) response = Response(self.protocol.get_command_output(self.shell_id, command_id)) if from_exec: vvvv('WINRM RESULT %r' % response, host=self.host) else: vvvvv('WINRM RESULT %r' % response, host=self.host) vvvvv('WINRM STDOUT %s' % response.std_out, host=self.host) vvvvv('WINRM STDERR %s' % response.std_err, host=self.host) return response finally: if command_id: self.protocol.cleanup_command(self.shell_id, command_id)
def put_file(self, in_path, out_path): vvv("PUT %s TO %s" % (in_path, out_path), host=self.host) if not os.path.exists(in_path): raise errors.AnsibleFileNotFound("file or module does not exist: %s" % in_path) with open(in_path) as in_file: in_size = os.path.getsize(in_path) script_template = ''' $s = [System.IO.File]::OpenWrite("%s"); [void]$s.Seek(%d, [System.IO.SeekOrigin]::Begin); $b = [System.Convert]::FromBase64String("%s"); [void]$s.Write($b, 0, $b.length); [void]$s.SetLength(%d); [void]$s.Close(); ''' # Determine max size of data we can pass per command. script = script_template % (powershell._escape(out_path), in_size, '', in_size) cmd = powershell._encode_script(script) # Encode script with no data, subtract its length from 8190 (max # windows command length), divide by 2.67 (UTF16LE base64 command # encoding), then by 1.35 again (data base64 encoding). buffer_size = int(((8190 - len(cmd)) / 2.67) / 1.35) for offset in xrange(0, in_size, buffer_size): try: out_data = in_file.read(buffer_size) if offset == 0: if out_data.lower().startswith('#!powershell') and not out_path.lower().endswith('.ps1'): out_path = out_path + '.ps1' b64_data = base64.b64encode(out_data) script = script_template % (powershell._escape(out_path), offset, b64_data, in_size) vvvv("WINRM PUT %s to %s (offset=%d size=%d)" % (in_path, out_path, offset, len(out_data)), host=self.host) cmd_parts = powershell._encode_script(script, as_list=True) result = self._winrm_exec(cmd_parts[0], cmd_parts[1:]) if result.status_code != 0: raise IOError(result.std_err.encode('utf-8')) except Exception: traceback.print_exc() raise errors.AnsibleError("failed to transfer file to %s" % out_path)
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) cache_key = '%s:%s@%s:%d' % (self.user, hashlib.md5( self.password).hexdigest(), self.host, port) if cache_key in _winrm_cache: vvvv('WINRM REUSE EXISTING CONNECTION: %s' % cache_key, host=self.host) return _winrm_cache[cache_key] transport_schemes = [('plaintext', 'https'), ('plaintext', 'http')] # FIXME: ssl/kerberos if port == 5985: transport_schemes = reversed(transport_schemes) exc = None for transport, scheme in transport_schemes: endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, username=self.user, password=self.password) try: protocol.send_message('') _winrm_cache[cache_key] = protocol return protocol except WinRMTransportError, exc: err_msg = str(exc.args[0]) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise errors.AnsibleError( "the connection attempt timed out") m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 401: raise errors.AnsibleError( "the username/password specified for this server was incorrect" ) elif code == 411: _winrm_cache[cache_key] = protocol return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' if _winrm_hacks: port = _winrm_hacks.get_port(self) else: port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) cache_key = '%s:%s@%s:%d' % (self.user, hashlib.md5(self.password).hexdigest(), self.host, port) if cache_key in _winrm_cache: vvvv('WINRM REUSE EXISTING CONNECTION: %s' % cache_key, host=self.host) return _winrm_cache[cache_key] transport_schemes = [('plaintext', 'https'), ('plaintext', 'http')] # FIXME: ssl/kerberos if port == 5985: transport_schemes = reversed(transport_schemes) exc = None for transport, scheme in transport_schemes: endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, username=self.user, password=self.password) try: protocol.send_message('') _winrm_cache[cache_key] = protocol return protocol except WinRMTransportError, exc: err_msg = str(exc.args[0]) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 411: _winrm_cache[cache_key] = protocol return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def fetch_file(self, in_path, out_path): out_path = out_path.replace('\\', '/') vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) buffer_size = 2**19 # 0.5MB chunks if not os.path.exists(os.path.dirname(out_path)): os.makedirs(os.path.dirname(out_path)) out_file = None try: offset = 0 while True: try: script = ''' If (Test-Path -PathType Leaf "%(path)s") { $stream = [System.IO.File]::OpenRead("%(path)s"); $stream.Seek(%(offset)d, [System.IO.SeekOrigin]::Begin) | Out-Null; $buffer = New-Object Byte[] %(buffer_size)d; $bytesRead = $stream.Read($buffer, 0, %(buffer_size)d); $bytes = $buffer[0..($bytesRead-1)]; [System.Convert]::ToBase64String($bytes); $stream.Close() | Out-Null; } ElseIf (Test-Path -PathType Container "%(path)s") { Write-Host "[DIR]"; } Else { Write-Error "%(path)s does not exist"; Exit 1; } ''' % dict(buffer_size=buffer_size, path=powershell._escape(in_path), offset=offset) vvvv("WINRM FETCH %s to %s (offset=%d)" % (in_path, out_path, offset), host=self.host) cmd_parts = powershell._encode_script(script, as_list=True) result = self._winrm_exec(cmd_parts[0], cmd_parts[1:]) if result.status_code != 0: raise IOError(result.std_err.encode('utf-8')) if result.std_out.strip() == '[DIR]': data = None else: data = base64.b64decode(result.std_out.strip()) if data is None: if not os.path.exists(out_path): os.makedirs(out_path) break else: if not out_file: # If out_path is a directory and we're expecting a file, bail out now. if os.path.isdir(out_path): break out_file = open(out_path, 'wb') out_file.write(data) if len(data) < buffer_size: break offset += len(data) except Exception: traceback.print_exc() raise errors.AnsibleError("failed to transfer file to %s" % out_path) finally: if out_file: out_file.close()
def _winrm_connect(self): ''' Establish a WinRM connection over HTTP/HTTPS. ''' # get winrm-specific connection vars host_vars = self.runner.inventory._hosts_cache[ self.delegate].get_variables() port = self.port or 5986 vvv("ESTABLISH WINRM CONNECTION FOR USER: %s on PORT %s TO %s" % \ (self.user, port, self.host), host=self.host) netloc = '%s:%d' % (self.host, port) exc = None for transport, scheme in self.transport_schemes['http' if port == 5985 else 'https']: if transport == 'kerberos' and (not HAVE_KERBEROS or not '@' in self.user): continue if transport == 'kerberos': realm = self.user.split('@', 1)[1].strip() or None else: realm = None endpoint = urlparse.urlunsplit((scheme, netloc, '/wsman', '', '')) self._winrm_kwargs = dict(username=self.user, password=self.password, realm=realm) argspec = inspect.getargspec(Protocol.__init__) for arg in argspec.args: if arg in ('self', 'endpoint', 'transport', 'username', 'password', 'realm'): continue if 'ansible_winrm_%s' % arg in host_vars: self._winrm_kwargs[arg] = host_vars['ansible_winrm_%s' % arg] vvvv('WINRM CONNECT: transport=%s endpoint=%s' % (transport, endpoint), host=self.host) protocol = Protocol(endpoint, transport=transport, **self._winrm_kwargs) try: protocol.send_message('') return protocol except WinRMTransportError, exc: err_msg = str(exc) if re.search(r'Operation\s+?timed\s+?out', err_msg, re.I): raise errors.AnsibleError( "the connection attempt timed out") m = re.search(r'Code\s+?(\d{3})', err_msg) if m: code = int(m.groups()[0]) if code == 401: raise errors.AnsibleError( "the username/password specified for this server was incorrect" ) elif code == 411: return protocol vvvv('WINRM CONNECTION ERROR: %s' % err_msg, host=self.host) continue
def _commandStarted(self, proto): vvvv('STARTED') return proto.done
def recv_data(self): header_len = 8 # size of a packed unsigned long long data = b"" try: vvvv("%s: in recv_data(), waiting for the header" % self.host) while len(data) < header_len: d = self.conn.recv(header_len - len(data)) if not d: vvvv("%s: received nothing, bailing out" % self.host) return None data += d vvvv("%s: got the header, unpacking" % self.host) data_len = struct.unpack('Q',data[:header_len])[0] data = data[header_len:] vvvv("%s: data received so far (expecting %d): %d" % (self.host,data_len,len(data))) while len(data) < data_len: d = self.conn.recv(data_len - len(data)) if not d: vvvv("%s: received nothing, bailing out" % self.host) return None data += d vvvv("%s: received all of the data, returning" % self.host) return data except socket.timeout: raise errors.AnsibleError("timed out while waiting to receive data")
def connectionMade(self): vvvv('connectionMade') if self.stdin: self.transport.write(self.stdin) self.transport.closeStdin()
def recv_data(self): header_len = 8 # size of a packed unsigned long long data = b"" try: vvvv("%s: in recv_data(), waiting for the header" % self.host) while len(data) < header_len: d = self.conn.recv(header_len - len(data)) if not d: vvvv("%s: received nothing, bailing out" % self.host) return None data += d vvvv("%s: got the header, unpacking" % self.host) data_len = struct.unpack('!Q', data[:header_len])[0] data = data[header_len:] vvvv("%s: data received so far (expecting %d): %d" % (self.host, data_len, len(data))) while len(data) < data_len: d = self.conn.recv(data_len - len(data)) if not d: vvvv("%s: received nothing, bailing out" % self.host) return None vvvv("%s: received %d bytes" % (self.host, len(d))) data += d vvvv("%s: received all of the data, returning" % self.host) return data except socket.timeout: raise AnsibleError("timed out while waiting to receive data")
def childDataReceived(self, childFD, data): vvvv('childDataReceived %r %s' % (childFD, len(data))) self.output[childFD] += data
def fetch_file(self, in_path, out_path): out_path = out_path.replace('\\', '/') vvv("FETCH %s TO %s" % (in_path, out_path), host=self.host) buffer_size = 2**20 # 1MB chunks if not os.path.exists(os.path.dirname(out_path)): os.makedirs(os.path.dirname(out_path)) out_file = None try: offset = 0 while True: try: script = ''' If (Test-Path -PathType Leaf "%(path)s") { $stream = [System.IO.File]::OpenRead("%(path)s"); $stream.Seek(%(offset)d, [System.IO.SeekOrigin]::Begin) | Out-Null; $buffer = New-Object Byte[] %(buffer_size)d; $bytesRead = $stream.Read($buffer, 0, %(buffer_size)d); $bytes = $buffer[0..($bytesRead-1)]; [System.Convert]::ToBase64String($bytes); $stream.Close() | Out-Null; } ElseIf (Test-Path -PathType Container "%(path)s") { Write-Host "[DIR]"; } Else { Write-Error "%(path)s does not exist"; Exit 1; } ''' % dict(buffer_size=buffer_size, path=powershell._escape(in_path), offset=offset) vvvv("WINRM FETCH %s to %s (offset=%d)" % (in_path, out_path, offset), host=self.host) cmd_parts = powershell._encode_script(script, as_list=True) result = self._winrm_exec(cmd_parts[0], cmd_parts[1:]) if result.status_code != 0: raise IOError(result.std_err.encode('utf-8')) if result.std_out.strip() == '[DIR]': data = None else: data = base64.b64decode(result.std_out.strip()) if data is None: if not os.path.exists(out_path): os.makedirs(out_path) break else: if not out_file: # If out_path is a directory and we're expecting a file, bail out now. if os.path.isdir(out_path): break out_file = open(out_path, 'wb') out_file.write(data) if len(data) < buffer_size: break offset += len(data) except Exception: traceback.print_exc() raise errors.AnsibleError("failed to transfer file to %s" % out_path) finally: if out_file: out_file.close()
def _commandFailedToStart(self, err): vvvv('FAILED TO START: %s' % (err,))