Robot Framework SSH Library
abstractclient.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from fnmatch import fnmatchcase
17 import os
18 import re
19 import stat
20 import time
21 import glob
22 import posixpath
23 import ntpath
24 import fnmatch
25 
26 from .config import (Configuration, IntegerEntry, NewlineEntry, StringEntry,
27  TimeEntry)
28 from .logger import logger
29 from .utils import is_bytes, is_string, unicode, is_list_like
30 
31 
33  pass
34 
35 
37 
38  def __init__(self, host, alias, port, timeout, newline, prompt, term_type,
39  width, height, path_separator, encoding, escape_ansi, encoding_errors):
40  super(_ClientConfiguration, self).__init__(
41  index=IntegerEntry(None),
42  host=StringEntry(host),
43  alias=StringEntry(alias),
44  port=IntegerEntry(port),
45  timeout=TimeEntry(timeout),
46  newline=NewlineEntry(newline),
47  prompt=StringEntry(prompt),
48  term_type=StringEntry(term_type),
49  width=IntegerEntry(width),
50  height=IntegerEntry(height),
51  path_separator=StringEntry(path_separator),
52  encoding=StringEntry(encoding),
53  escape_ansi=StringEntry(escape_ansi),
54  encoding_errors=StringEntry(encoding_errors)
55  )
56 
57 
58 
65  def __init__(self, host, alias=None, port=22, timeout=3, newline='LF',
66  prompt=None, term_type='vt100', width=80, height=24,
67  path_separator='/', encoding='utf8', escape_ansi=False, encoding_errors='strict'):
68  self.configconfig = _ClientConfiguration(host, alias, port, timeout, newline,
69  prompt, term_type, width, height,
70  path_separator, encoding, escape_ansi, encoding_errors)
71  self._sftp_client_sftp_client = None
72  self._scp_transfer_client_scp_transfer_client = None
73  self._scp_all_client_scp_all_client = None
74  self._shell_shell = None
75  self._started_commands_started_commands = []
76  self.clientclient = self._get_client_get_client()
77  self.widthwidth = width
78  self.heightheight = height
79 
80  def _get_client(self):
81  raise NotImplementedError('This should be implemented in the subclass.')
82 
83  @staticmethod
84 
90  def enable_logging(path):
91  raise NotImplementedError
92 
93  @property
94 
99  sftp_client = property
100 
101  def sftp_client(self):
102  if not self._sftp_client_sftp_client:
103  self._sftp_client_sftp_client = self._create_sftp_client_create_sftp_client()
104  return self._sftp_client_sftp_client
105 
106  @property
107 
112  scp_transfer_client = property
113 
115  if not self._scp_transfer_client_scp_transfer_client:
116  self._scp_transfer_client_scp_transfer_client = self._create_scp_transfer_client_create_scp_transfer_client()
117  return self._scp_transfer_client_scp_transfer_client
118 
119  @property
120 
125  scp_all_client = property
126 
127  def scp_all_client(self):
128  if not self._scp_all_client_scp_all_client:
129  self._scp_all_client_scp_all_client = self._create_scp_all_client_create_scp_all_client()
130  return self._scp_all_client_scp_all_client
131 
132  @property
133 
138  shell = property
139 
140  def shell(self):
141  if not self._shell_shell:
142  self._shell_shell = self._create_shell_create_shell()
143  if self.widthwidth != self.configconfig.width or self.heightheight != self.configconfig.height:
144  self._shell_shell.resize(self.configconfig.width, self.configconfig.height)
145  self.widthwidth, self.heightheight = self.configconfig.width, self.configconfig.height
146  return self._shell_shell
147 
149  raise NotImplementedError
150 
152  raise NotImplementedError
153 
155  raise NotImplementedError
156 
157  def _create_shell(self):
158  raise NotImplementedError
159 
160 
161  def close(self):
162  self._sftp_client_sftp_client = None
163  self._scp_transfer_client_scp_transfer_client = None
164  self._scp_all_client_scp_all_client = None
165  self._shell_shell = None
166  self.clientclient.close()
167  try:
168  logger.log_background_messages()
169  except AttributeError:
170  pass
171 
172 
209  def login(self, username=None, password=None, allow_agent=False, look_for_keys=False, delay=None, proxy_cmd=None,
210  read_config=False, jumphost_connection=None, keep_alive_interval='0 seconds'):
211  keep_alive_interval = int(TimeEntry(keep_alive_interval).value)
212  username = self._encode_encode(username)
213  if not password or password == '""':
214  password = None
215  if password and not allow_agent:
216  password = self._encode_encode(password)
217  try:
218  self._login_login(username, password, allow_agent, look_for_keys, proxy_cmd, read_config,
219  jumphost_connection, keep_alive_interval)
220  except SSHClientException:
221  self.clientclient.close()
222  raise SSHClientException("Authentication failed for user '%s'."
223  % self._decode_decode(username))
224  return self._read_login_output_read_login_output(delay)
225 
226  def _encode(self, text):
227  if is_bytes(text):
228  return text
229  if not is_string(text):
230  text = unicode(text)
231  return text.encode(self.configconfig.encoding, self.configconfig.encoding_errors)
232 
233  def _decode(self, bytes):
234  return bytes.decode(self.configconfig.encoding, self.configconfig.encoding_errors)
235 
236  def _login(self, username, password, allow_agent, look_for_keys, proxy_cmd, read_config,
237  jumphost_connection, keep_alive_interval):
238  raise NotImplementedError
239 
240  def _read_login_output(self, delay):
241  if not self.configconfig.prompt:
242  return self.readread(delay)
243  elif self.configconfig.prompt.startswith('REGEXP:'):
244  return self.read_until_regexpread_until_regexp(self.configconfig.prompt[7:])
245  return self.read_until_promptread_until_prompt()
246 
247 
285  def login_with_public_key(self, username, keyfile, password, allow_agent=False,
286  look_for_keys=False, delay=None, proxy_cmd=None,
287  jumphost_connection=None, read_config=False, keep_alive_interval='0 seconds'):
288  if username:
289  username = self._encode_encode(username)
290  if keyfile:
291  self._verify_key_file_verify_key_file(keyfile)
292  keep_alive_interval = int(TimeEntry(keep_alive_interval).value)
293  try:
294  self._login_with_public_key_login_with_public_key(username, keyfile, password,
295  allow_agent, look_for_keys,
296  proxy_cmd, jumphost_connection,
297  read_config, keep_alive_interval)
298  except SSHClientException:
299  self.clientclient.close()
300  raise SSHClientException("Login with public key failed for user "
301  "'%s'." % self._decode_decode(username))
302  return self._read_login_output_read_login_output(delay)
303 
304  def _verify_key_file(self, keyfile):
305  if not os.path.exists(keyfile):
306  raise SSHClientException("Given key file '%s' does not exist." %
307  keyfile)
308  try:
309  open(keyfile).close()
310  except IOError:
311  raise SSHClientException("Could not read key file '%s'." % keyfile)
312 
313  def _login_with_public_key(self, username, keyfile, password,
314  allow_agent, look_for_keys, proxy_cmd,
315  jumphost_index_or_alias, read_config, keep_alive_interval):
316  raise NotImplementedError
317 
318  @staticmethod
319  def get_banner_without_login(host, port=22):
320  raise NotImplementedError('Not supported on this Python interpreter.')
321 
322  def get_banner(self):
323  raise NotImplementedError('Not supported on this Python interpreter.')
324 
325 
344  def execute_command(self, command, sudo=False, sudo_password=None, timeout=None, output_during_execution=False,
345  output_if_timeout=False, invoke_subsystem=False, forward_agent=False):
346  self.start_commandstart_command(command, sudo, sudo_password, invoke_subsystem, forward_agent)
347  return self.read_command_outputread_command_output(timeout=timeout, output_during_execution=output_during_execution,
348  output_if_timeout=output_if_timeout)
349 
350 
369  def start_command(self, command, sudo=False, sudo_password=None, invoke_subsystem=False, forward_agent=False):
370  command = self._encode_encode(command)
371 
372  self._started_commands_started_commands.append(self._start_command_start_command(command, sudo, sudo_password, invoke_subsystem, forward_agent))
373 
374  def _start_command(self, command, sudo=False, sudo_password=None, invoke_subsystem=False, forward_agent=False):
375  raise NotImplementedError
376 
377 
389  def read_command_output(self, timeout=None, output_during_execution=False, output_if_timeout=False):
390  if timeout:
391  timeout = float(TimeEntry(timeout).value)
392  try:
393  return self._started_commands_started_commands.pop().read_outputs(timeout, output_during_execution, output_if_timeout)
394  except IndexError:
395  raise SSHClientException('No started commands to read output from.')
396 
397 
405  def write(self, text, add_newline=False):
406  text = self._encode_encode(text)
407  if add_newline:
408  text += self._encode_encode(self.configconfig.newline)
409  self.shellshellshell.write(text)
410 
411 
426  def read(self, delay=None):
427  output = self.shellshellshell.read()
428  if delay:
429  output += self._delayed_read_delayed_read(delay)
430  return self._decode_decode(output)
431 
432  def _delayed_read(self, delay):
433  delay = TimeEntry(delay).value
434  max_time = time.time() + self.configconfig.get('timeout').value
435  output = b''
436  while time.time() < max_time:
437  time.sleep(delay)
438  read = self.shellshellshell.read()
439  if not read:
440  break
441  output += read
442  return output
443 
444 
451  def read_char(self):
452  server_output = b''
453  while True:
454  try:
455  server_output += self.shellshellshell.read_byte()
456  return self._decode_decode(server_output)
457  except UnicodeDecodeError as e:
458  if e.reason == 'unexpected end of data':
459  pass
460  else:
461  raise
462 
463 
478  def read_until(self, expected):
479  return self._read_until_read_until(lambda s: expected in s, expected)
480 
481  def _read_until(self, matcher, expected, timeout=None):
482  output = ''
483  timeout = TimeEntry(timeout) if timeout else self.configconfig.get('timeout')
484  max_time = time.time() + timeout.value
485  while time.time() < max_time:
486  char = self.read_charread_char()
487  if not char:
488  time.sleep(.00001) # Release GIL so paramiko I/O thread can run
489  output += char
490  if matcher(output):
491  return output
492  raise SSHClientException("No match found for '%s' in %s\nOutput:\n%s."
493  % (expected, timeout, output))
494 
495 
510  return self.read_untilread_until(self.configconfig.newline)
511 
512 
528  def read_until_prompt(self, strip_prompt=False):
529  if not self.configconfig.prompt:
530  raise SSHClientException('Prompt is not set.')
531 
532  if self.configconfig.prompt.startswith('REGEXP:'):
533  output = self.read_until_regexpread_until_regexp(self.configconfig.prompt[7:])
534  else:
535  output = self.read_untilread_until(self.configconfig.prompt)
536  if strip_prompt:
537  output = self._strip_prompt_strip_prompt(output)
538  return output
539 
540  def _strip_prompt(self, output):
541  if self.configconfig.prompt.startswith('REGEXP:'):
542  pattern = re.compile(self.configconfig.prompt[7:])
543  match = pattern.search(output)
544  length = match.end() - match.start()
545  else:
546  length = len(self.configconfig.prompt)
547  return output[:-length]
548 
549 
565  def read_until_regexp(self, regexp):
566  if is_string(regexp):
567  regexp = re.compile(regexp)
568  return self._read_until_read_until(lambda s: regexp.search(s), regexp.pattern)
569 
570 
579  def read_until_regexp_with_prefix(self, regexp, prefix):
580  if is_string(regexp):
581  regexp = re.compile(regexp)
582  matcher = regexp.search
583  expected = regexp.pattern
584  ret = ""
585  timeout = self.configconfig.get('timeout')
586  start_time = time.time()
587  while time.time() < float(timeout.value) + start_time:
588  ret += self.read_charread_char()
589  if matcher(prefix + self._encode_encode(ret)):
590  return ret
591  raise SSHClientException(
592  "No match found for '%s' in %s\nOutput:\n%s"
593  % (expected, timeout, ret))
594 
595 
616  def write_until_expected(self, text, expected, timeout, interval):
617  expected = self._encode_encode(expected)
618  interval = TimeEntry(interval)
619  timeout = TimeEntry(timeout)
620  max_time = time.time() + timeout.value
621  while time.time() < max_time:
622  self.writewrite(text)
623  try:
624  return self._read_until_read_until(lambda s: expected in self._encode_encode(s), expected,
625  timeout=interval.value)
626  except SSHClientException:
627  pass
628  raise SSHClientException("No match found for '%s' in %s."
629  % (self._decode_decode(expected), timeout))
630 
631 
636  def put_file(self, source, destination='.', mode='0o744', newline='',
637  scp='OFF', scp_preserve_times=False):
638  client = self._create_client_create_client(scp)
639  return client.put_file(source, destination, scp_preserve_times, mode, newline,
640  self.configconfig.path_separator)
641 
642 
650  def put_directory(self, source, destination='.', mode='0o744', newline='',
651  recursive=False, scp='OFF', scp_preserve_times=False):
652  client = self._create_client_create_client(scp)
653  return client.put_directory(source, destination, scp_preserve_times, mode,
654  newline, self.configconfig.path_separator, recursive)
655 
656 
661  def get_file(self, source, destination='.', scp='OFF', scp_preserve_times=False):
662  client = self._create_client_create_client(scp)
663  if scp == 'ALL':
664  sources = self._get_files_for_scp_all_get_files_for_scp_all(source)
665  return client.get_file(sources, destination, scp_preserve_times, self.configconfig.path_separator)
666  return client.get_file(source, destination, scp_preserve_times, self.configconfig.path_separator)
667 
668  def _get_files_for_scp_all(self, source):
669  sources = self.execute_commandexecute_command('printf "%%s\\n" %s' % source)
670  result = sources[0].split('\n')
671  result[:] = [x for x in result if x] # remove empty entries
672  return result
673 
674 
682  def get_directory(self, source, destination='.', recursive=False,
683  scp='OFF', scp_preserve_times=False):
684  client = self._create_client_create_client(scp)
685  return client.get_directory(source, destination, scp_preserve_times,
686  self.configconfig.path_separator,
687  recursive)
688 
689 
697  def list_dir(self, path, pattern=None, absolute=False):
698  items = self.sftp_clientsftp_clientsftp_client.list_dir(path, pattern, absolute)
699  return sorted(items)
700 
701 
709  def list_files_in_dir(self, path, pattern=None, absolute=False):
710  files = self.sftp_clientsftp_clientsftp_client.list_files_in_dir(path, pattern, absolute)
711  return sorted(files)
712 
713 
721  def list_dirs_in_dir(self, path, pattern=None, absolute=False):
722  dirs = self.sftp_clientsftp_clientsftp_client.list_dirs_in_dir(path, pattern, absolute)
723  return sorted(dirs)
724 
725 
735  def is_dir(self, path):
736  has_glob = bool([ops for ops in '*?![' if(ops in path)])
737  if has_glob:
738  dir_dir = path[:(-len(path.split(self.configconfig.path_separator)[-1]))]
739  dirs = self.sftp_clientsftp_clientsftp_client.list_dirs_in_dir(dir_dir)
740  for dirname in dirs:
741  if fnmatch.fnmatch(dirname, path.split(self.configconfig.path_separator)[-1]):
742  return self.sftp_clientsftp_clientsftp_client.is_dir(dir_dir + dirname)
743  return self.sftp_clientsftp_clientsftp_client.is_dir(path)
744 
745 
755  def is_file(self, path):
756  if bool([ops for ops in '*?![' if(ops in path)]):
757  file_dir = path[:(-len(path.split(self.configconfig.path_separator)[-1]))]
758  if file_dir == '':
759  return self.sftp_clientsftp_clientsftp_client.is_file(path)
760  files = self.sftp_clientsftp_clientsftp_client.list_files_in_dir(file_dir)
761  for filename in files:
762  if fnmatch.fnmatch(filename, path.split(self.configconfig.path_separator)[-1]):
763  return self.sftp_clientsftp_clientsftp_client.is_file(file_dir + filename)
764  return self.sftp_clientsftp_clientsftp_client.is_file(path)
765 
766  def _create_client(self, scp):
767  if scp.upper() == 'ALL':
768  return self.scp_all_clientscp_all_clientscp_all_client
769  elif scp.upper() == 'TRANSFER':
770  return self.scp_transfer_clientscp_transfer_clientscp_transfer_client
771  else:
772  return self.sftp_clientsftp_clientsftp_client
773 
774 
775 
782 
783 
787  def read(self):
788  raise NotImplementedError
789 
790 
794  def read_byte(self):
795  raise NotImplementedError
796 
797 
802  def write(self, text):
803  raise NotImplementedError
804 
805 
806 
814 
815  def __init__(self, encoding):
816  self._encoding_encoding = encoding
817  self._homedir_homedir = self._absolute_path_absolute_path(b'.')
818 
819  def _absolute_path(self, path):
820  raise NotImplementedError
821 
822 
831  def is_file(self, path):
832  try:
833  item = self._stat_stat(path)
834  except IOError:
835  return False
836  return item.is_regular()
837 
838  def _stat(self, path):
839  raise NotImplementedError
840 
841 
850  def is_dir(self, path):
851  try:
852  item = self._stat_stat(path)
853  except IOError:
854  return False
855  return item.is_directory()
856 
857 
877  def list_dir(self, path, pattern=None, absolute=False):
878  return self._list_filtered_list_filtered(path, self._get_item_names_get_item_names, pattern,
879  absolute)
880 
881  def _list_filtered(self, path, filter_method, pattern=None, absolute=False):
882  self._verify_remote_dir_exists_verify_remote_dir_exists(path)
883  items = filter_method(path)
884  if pattern:
885  items = self._filter_by_pattern_filter_by_pattern(items, pattern)
886  if absolute:
887  items = self._include_absolute_path_include_absolute_path(items, path)
888  return items
889 
890  def _verify_remote_dir_exists(self, path):
891  if not self.is_diris_dir(path):
892  raise SSHClientException("There was no directory matching '%s'." %
893  path)
894 
895  def _get_item_names(self, path):
896  return [item.name for item in self._list_list(path)]
897 
898  def _list(self, path):
899  raise NotImplementedError
900 
901  def _filter_by_pattern(self, items, pattern):
902  return [name for name in items if fnmatchcase(name, pattern)]
903 
904  def _include_absolute_path(self, items, path):
905  absolute_path = self._absolute_path_absolute_path(path)
906  if absolute_path[1:3] == ':\\':
907  absolute_path += '\\'
908  else:
909  absolute_path += '/'
910  return [absolute_path + name for name in items]
911 
912 
929  def list_files_in_dir(self, path, pattern=None, absolute=False):
930  return self._list_filtered_list_filtered(path, self._get_file_names_get_file_names, pattern,
931  absolute)
932 
933  def _get_file_names(self, path):
934  return [item.name
935  for item in self._list_list(path)
936  if item.is_regular()
937  or (item.is_link()
938  and not self._is_dir_symlink_is_dir_symlink(path, item.name))]
939 
940  def _is_dir_symlink(self, path, item):
941  resolved_link = self._readlink_readlink('%s/%s' % (path, item))
942  return self.is_diris_dir('%s/%s' % (path, resolved_link))
943 
944 
961  def list_dirs_in_dir(self, path, pattern=None, absolute=False):
962  return self._list_filtered_list_filtered(path, self._get_directory_names_get_directory_names, pattern,
963  absolute)
964 
965  def _get_directory_names(self, path):
966  return [item.name for item in self._list_list(path) if item.is_directory()]
967 
968  def get_directory(self, source, destination, scp_preserve_time, path_separator='/',
969  recursive=False):
970  destination = self.build_destinationbuild_destination(source, destination, path_separator)
971  return self._get_directory_get_directory(source, destination, path_separator, recursive, scp_preserve_time)
972 
973 
997  def _get_directory(self, source, destination, path_separator='/',
998  recursive=False, scp_preserve_times=False):
999  source = self._remove_ending_path_separator_remove_ending_path_separator(path_separator, source)
1000  self._verify_remote_dir_exists_verify_remote_dir_exists(source)
1001  files = []
1002  items = self.list_dirlist_dir(source)
1003  if items:
1004  for item in items:
1005  remote = source + path_separator + item
1006  local = os.path.join(destination, item)
1007  if self.is_fileis_file(remote):
1008  files += self.get_fileget_file(remote, local, scp_preserve_times)
1009  elif recursive:
1010  files += self.get_directoryget_directory(remote, local, scp_preserve_times, path_separator,
1011  recursive)
1012  else:
1013  if not os.path.exists(destination):
1014  os.makedirs(destination)
1015  files.append((source, destination))
1016  return files
1017 
1018 
1024  def build_destination(self, source, destination, path_separator):
1025  if os.path.exists(destination) or destination == '.':
1026  fullpath_destination = os.path.join(destination, self.get_parent_folderget_parent_folder(source, path_separator))
1027  if not os.path.exists(fullpath_destination):
1028  os.makedirs(fullpath_destination)
1029  return fullpath_destination
1030  else:
1031  return destination
1032 
1033  def get_parent_folder(self, source, path_separator):
1034  if source.endswith(path_separator):
1035  return (source[:-len(path_separator)]).split(path_separator)[-1]
1036  else:
1037  return source.split(path_separator)[-1]
1038 
1039  def _remove_ending_path_separator(self, path_separator, source):
1040  if source.endswith(path_separator):
1041  source = source[:-len(path_separator)]
1042  return source
1043 
1044 
1067  def get_file(self, source, destination, scp_preserve_times, path_separator='/'):
1068  remote_files = self._get_get_file_sources_get_get_file_sources(source, path_separator)
1069  if not remote_files:
1070  msg = "There were no source files matching '%s'." % source
1071  raise SSHClientException(msg)
1072  local_files = self._get_get_file_destinations_get_get_file_destinations(remote_files, destination)
1073  files = list(zip(remote_files, local_files))
1074  for src, dst in files:
1075  self._get_file_get_file(src, dst, scp_preserve_times)
1076  return files
1077 
1078  def _get_get_file_sources(self, source, path_separator):
1079  if path_separator in source:
1080  path, pattern = source.rsplit(path_separator, 1)
1081  else:
1082  path, pattern = '', source
1083  if not path:
1084  path = '.'
1085  if not self.is_fileis_file(source):
1086  return [filename for filename in
1087  self.list_files_in_dirlist_files_in_dir(path, pattern, absolute=True)]
1088  else:
1089  return [source]
1090 
1091  def _get_get_file_destinations(self, source_files, destination):
1092  target_is_dir = destination.endswith(os.sep) or destination == '.'
1093  if not target_is_dir and len(source_files) > 1:
1094  raise SSHClientException('Cannot copy multiple source files to one '
1095  'destination file.')
1096  destination = os.path.abspath(destination.replace('/', os.sep))
1097  self._create_missing_local_dirs_create_missing_local_dirs(destination, target_is_dir)
1098  if target_is_dir:
1099  return [os.path.join(destination, os.path.basename(name))
1100  for name in source_files]
1101  return [destination]
1102 
1103  def _create_missing_local_dirs(self, destination, target_is_dir):
1104  if not target_is_dir:
1105  destination = os.path.dirname(destination)
1106  if not os.path.exists(destination):
1107  os.makedirs(destination)
1108 
1109  def _get_file(self, source, destination, scp_preserve_times):
1110  raise NotImplementedError
1111 
1112 
1141  def put_directory(self, source, destination, scp_preserve_times,mode, newline,
1142  path_separator='/', recursive=False):
1143  self._verify_local_dir_exists_verify_local_dir_exists(source)
1144  destination = self._remove_ending_path_separator_remove_ending_path_separator(path_separator,
1145  destination)
1146  if self.is_diris_dir(destination):
1147  destination = destination + path_separator +\
1148  source.rsplit(os.path.sep)[-1]
1149  return self._put_directory_put_directory(source, destination, mode, newline,
1150  path_separator, recursive, scp_preserve_times)
1151 
1152  def _put_directory(self, source, destination, mode, newline,
1153  path_separator, recursive, scp_preserve_times=False):
1154  files = []
1155  items = os.listdir(source)
1156  if items:
1157  for item in items:
1158  local_path = os.path.join(source, item)
1159  remote_path = destination + path_separator + item
1160  if os.path.isfile(local_path):
1161  files += self.put_fileput_file(local_path, remote_path, scp_preserve_times,
1162  mode, newline, path_separator)
1163  elif recursive and os.path.isdir(local_path):
1164  files += self._put_directory_put_directory(local_path, remote_path,
1165  mode, newline,
1166  path_separator, recursive, scp_preserve_times)
1167  else:
1168  self._create_missing_remote_path_create_missing_remote_path(destination, mode)
1169  files.append((source, destination))
1170  return files
1171 
1172  def _verify_local_dir_exists(self, path):
1173  if not os.path.isdir(path):
1174  raise SSHClientException("There was no source path matching '%s'."
1175  % path)
1176 
1177 
1208  def put_file(self, sources, destination, scp_preserve_times, mode, newline, path_separator='/'):
1209  if mode:
1210  mode = int(mode, 8)
1211  newline = {'CRLF': '\r\n', 'LF': '\n'}.get(newline.upper(), None)
1212  local_files = self._get_put_file_sources_get_put_file_sources(sources)
1213  remote_files, remote_dir = self._get_put_file_destinations_get_put_file_destinations(local_files,
1214  destination,
1215  path_separator)
1216  self._create_missing_remote_path_create_missing_remote_path(remote_dir, mode)
1217  files = list(zip(local_files, remote_files))
1218  for source, destination in files:
1219  self._put_file_put_file(source, destination, mode, newline, path_separator, scp_preserve_times)
1220  return files
1221 
1222  def _get_put_file_sources(self, source):
1223  source = source.replace('/', os.sep)
1224  if not os.path.exists(source):
1225  sources = [f for f in glob.glob(source)]
1226  else:
1227  sources = [f for f in [source]]
1228  if not sources:
1229  msg = "There are no source files matching '%s'." % source
1230  raise SSHClientException(msg)
1231  return sources
1232 
1233  def _get_put_file_destinations(self, sources, destination, path_separator):
1234  if destination[1:3] == ':' + path_separator:
1235  destination = path_separator + destination
1236  destination = self._format_destination_path_format_destination_path(destination)
1237  if destination == '.':
1238  destination = self._homedir_homedir + '/'
1239  if len(sources) > 1 and destination[-1] != '/' and not self.is_diris_dir(destination):
1240  raise ValueError('It is not possible to copy multiple source '
1241  'files to one destination file.')
1242  dir_path, filename = self._parse_path_elements_parse_path_elements(destination,
1243  path_separator)
1244  if filename:
1245  files = [path_separator.join([dir_path, filename])]
1246  else:
1247  files = [path_separator.join([dir_path, os.path.basename(path)])
1248  for path in sources]
1249  return files, dir_path
1250 
1251  def _format_destination_path(self, destination):
1252  destination = destination.replace('\\', '/')
1253  destination = ntpath.splitdrive(destination)[-1]
1254  return destination
1255 
1256  def _parse_path_elements(self, destination, path_separator):
1257  def _isabs(path):
1258  if destination.startswith(path_separator):
1259  return True
1260  if path_separator == '\\' and path[1:3] == ':\\':
1261  return True
1262  return False
1263  if not _isabs(destination):
1264  destination = path_separator.join([self._homedir_homedir, destination])
1265  if self.is_diris_dir(destination):
1266  return destination, ''
1267  return destination.rsplit(path_separator, 1)
1268 
1269  def _create_missing_remote_path(self, path, mode):
1270  if path.startswith(b'/'):
1271  current_dir = b'/'
1272  else:
1273  current_dir = self._absolute_path_absolute_path(b'.').encode(self._encoding_encoding)
1274  for dir_name in path.split(b'/'):
1275  if dir_name:
1276  current_dir = posixpath.join(current_dir, dir_name)
1277  try:
1278  self._client.stat(current_dir)
1279  except:
1280  if not isinstance(mode, int):
1281  mode = int(mode, 8)
1282  self._client.mkdir(current_dir, mode)
1283 
1284  def _put_file(self, source, destination, mode, newline, path_separator, scp_preserve_times=False):
1285  remote_file = self._create_remote_file_create_remote_file(destination, mode)
1286  with open(source, 'rb') as local_file:
1287  position = 0
1288  while True:
1289  data = local_file.read(4096)
1290  if not data:
1291  break
1292  if newline:
1293  data = re.sub(br'(\r\n|\r|\n)', newline.encode(self._encoding_encoding), data)
1294  self._write_to_remote_file_write_to_remote_file(remote_file, data, position)
1295  position += len(data)
1296  self._close_remote_file_close_remote_file(remote_file)
1297 
1298  def _create_remote_file(self, destination, mode):
1299  raise NotImplementedError
1300 
1301  def _write_to_remote_file(self, remote_file, data, position):
1302  raise NotImplementedError
1303 
1304  def _close_remote_file(self, remote_file):
1305  raise NotImplementedError
1306 
1307  def create_local_ssh_tunnel(self, local_port, remote_host, remote_port, client):
1308  raise NotImplementedError
1309 
1310  def _readlink(self, path):
1311  raise NotImplementedError
1312 
1313 
1314 
1322 
1323  def __init__(self, command, encoding):
1324  self._command_command = command
1325  self._encoding_encoding = encoding
1326  self._shell_shell = None
1327 
1328 
1338  def run_in(self, shell, sudo=False, sudo_password=None, invoke_subsystem=False):
1339  self._shell_shell = shell
1340  if invoke_subsystem:
1341  self._invoke_invoke()
1342  elif not sudo:
1343  self._execute_execute()
1344  else:
1345  self._execute_with_sudo_execute_with_sudo(sudo_password)
1346 
1347  def _execute(self):
1348  raise NotImplementedError
1349 
1350  def _invoke(self):
1351  raise NotImplementedError
1352 
1353  def _execute_with_sudo(self, sudo_password=None):
1354  raise NotImplementedError
1355 
1356 
1361  def read_outputs(self):
1362  raise NotImplementedError
1363 
1364 
1365 
1370 
1371  def __init__(self, name, mode):
1372  self.namename = name
1373  self.modemode = mode
1374 
1375 
1379  def is_regular(self):
1380  return stat.S_ISREG(self.modemode)
1381 
1382 
1386  def is_directory(self):
1387  return stat.S_ISDIR(self.modemode)
1388 
1389 
1393  def is_link(self):
1394  return stat.S_ISLNK(self.modemode)
Base class for the remote command.
def read_outputs(self)
Returns the outputs of this command.
def run_in(self, shell, sudo=False, sudo_password=None, invoke_subsystem=False)
Runs this command in the given shell.
def _execute_with_sudo(self, sudo_password=None)
def __init__(self, command, encoding)
Base class for the SFTP implementation.
def _create_remote_file(self, destination, mode)
def get_file(self, source, destination, scp_preserve_times, path_separator='/')
Downloads file(s) from the remote host to the local machine.
def get_parent_folder(self, source, path_separator)
def _create_missing_local_dirs(self, destination, target_is_dir)
def build_destination(self, source, destination, path_separator)
Add parent directory from source to destination path if destination is '.
def _put_file(self, source, destination, mode, newline, path_separator, scp_preserve_times=False)
def _get_get_file_sources(self, source, path_separator)
def _get_directory(self, source, destination, path_separator='/', recursive=False, scp_preserve_times=False)
Downloads directory(-ies) from the remote host to the local machine, optionally with subdirectories i...
def _filter_by_pattern(self, items, pattern)
def is_dir(self, path)
Checks if the path points to a directory on the remote host.
def _remove_ending_path_separator(self, path_separator, source)
def _get_put_file_destinations(self, sources, destination, path_separator)
def _list_filtered(self, path, filter_method, pattern=None, absolute=False)
def get_directory(self, source, destination, scp_preserve_time, path_separator='/', recursive=False)
def list_files_in_dir(self, path, pattern=None, absolute=False)
Gets the file names, or optionally the absolute paths, of the regular files on the given path on the ...
def put_file(self, sources, destination, scp_preserve_times, mode, newline, path_separator='/')
Uploads the file(s) from the local machine to the remote host.
def list_dirs_in_dir(self, path, pattern=None, absolute=False)
Gets the directory names, or optionally the absolute paths, on the given path on the remote host.
def _get_get_file_destinations(self, source_files, destination)
def list_dir(self, path, pattern=None, absolute=False)
Gets the item names, or optionally the absolute paths, on the given path on the remote host.
def _get_file(self, source, destination, scp_preserve_times)
def _write_to_remote_file(self, remote_file, data, position)
def create_local_ssh_tunnel(self, local_port, remote_host, remote_port, client)
def is_file(self, path)
Checks if the path points to a regular file on the remote host.
def put_directory(self, source, destination, scp_preserve_times, mode, newline, path_separator='/', recursive=False)
Uploads directory(-ies) from the local machine to the remote host, optionally with subdirectories inc...
def _parse_path_elements(self, destination, path_separator)
def _put_directory(self, source, destination, mode, newline, path_separator, recursive, scp_preserve_times=False)
Base class for the SSH client implementation.
def put_file(self, source, destination='.', mode='0o744', newline='', scp='OFF', scp_preserve_times=False)
Calls :py:meth:AbstractSFTPClient.put_file` with the given arguments.
def is_dir(self, path)
Calls :py:meth:AbstractSFTPClient.is_dir with the given path.
def get_file(self, source, destination='.', scp='OFF', scp_preserve_times=False)
Calls :py:meth:AbstractSFTPClient.get_file with the given arguments.
def put_directory(self, source, destination='.', mode='0o744', newline='', recursive=False, scp='OFF', scp_preserve_times=False)
Calls :py:meth:AbstractSFTPClient.put_directory with the given arguments and the connection specific ...
def start_command(self, command, sudo=False, sudo_password=None, invoke_subsystem=False, forward_agent=False)
Starts the execution of the command on the remote host.
def _start_command(self, command, sudo=False, sudo_password=None, invoke_subsystem=False, forward_agent=False)
def login_with_public_key(self, username, keyfile, password, allow_agent=False, look_for_keys=False, delay=None, proxy_cmd=None, jumphost_connection=None, read_config=False, keep_alive_interval='0 seconds')
Logs into the remote host using the public key authentication.
def get_directory(self, source, destination='.', recursive=False, scp='OFF', scp_preserve_times=False)
Calls :py:meth:AbstractSFTPClient.get_directory with the given arguments and the connection specific ...
def close(self)
Closes the connection.
def list_dir(self, path, pattern=None, absolute=False)
Calls :py:meth:.AbstractSFTPClient.list_dir with the given arguments.
def list_dirs_in_dir(self, path, pattern=None, absolute=False)
Calls :py:meth:AbstractSFTPClient.list_dirs_in_dir with the given arguments.
def read(self, delay=None)
Reads all output available in the current shell.
def read_until_prompt(self, strip_prompt=False)
Reads output from the current shell until the prompt is encountered or the timeout expires.
def read_command_output(self, timeout=None, output_during_execution=False, output_if_timeout=False)
Reads the output of the previous started command.
shell
Gets the shell for the connection.
def _read_until(self, matcher, expected, timeout=None)
def enable_logging(path)
Enables logging of SSH events to a file.
scp_transfer_client
Gets the SCP client for the file transfer.
def read_char(self)
Reads a single Unicode character from the current shell.
def login(self, username=None, password=None, allow_agent=False, look_for_keys=False, delay=None, proxy_cmd=None, read_config=False, jumphost_connection=None, keep_alive_interval='0 seconds')
Logs into the remote host using password authentication.
scp_all_client
Gets the SCP client for the file transfer.
sftp_client
Gets the SFTP client for the connection.
def write_until_expected(self, text, expected, timeout, interval)
Writes text repeatedly in the current shell until the expected appears in the output or the timeout e...
def _login_with_public_key(self, username, keyfile, password, allow_agent, look_for_keys, proxy_cmd, jumphost_index_or_alias, read_config, keep_alive_interval)
def __init__(self, host, alias=None, port=22, timeout=3, newline='LF', prompt=None, term_type='vt100', width=80, height=24, path_separator='/', encoding='utf8', escape_ansi=False, encoding_errors='strict')
def read_until_newline(self)
Reads output from the current shell until a newline character is encountered or the timeout expires.
def list_files_in_dir(self, path, pattern=None, absolute=False)
Calls :py:meth:AbstractSFTPClient.list_files_in_dir with the given arguments.
def write(self, text, add_newline=False)
Writes text in the current shell.
def read_until_regexp_with_prefix(self, regexp, prefix)
Read and return from output until regexp matches prefix + output.
def read_until(self, expected)
Reads output from the current shell until the expected text is encountered or the timeout expires.
def execute_command(self, command, sudo=False, sudo_password=None, timeout=None, output_during_execution=False, output_if_timeout=False, invoke_subsystem=False, forward_agent=False)
Executes the command on the remote host.
def is_file(self, path)
Calls :py:meth:AbstractSFTPClient.is_file with the given path.
def _login(self, username, password, allow_agent, look_for_keys, proxy_cmd, read_config, jumphost_connection, keep_alive_interval)
def read_until_regexp(self, regexp)
Reads output from the current shell until the regexp matches or the timeout expires.
Base class for the shell implementation.
def write(self, text)
Writes the text in the current shell.
def read(self)
Reads all the output from the shell.
def read_byte(self)
Reads a single byte from the shell.
Wrapper class for the language specific file information objects.
def is_directory(self)
Checks if this file is a directory.
def is_link(self)
Checks if this file is a symbolic link.
def is_regular(self)
Checks if this file is a regular file.
def __init__(self, host, alias, port, timeout, newline, prompt, term_type, width, height, path_separator, encoding, escape_ansi, encoding_errors)
A simple configuration class.
Definition: config.py:40
Integer value to be stored in stored in :py:class:Configuration.
Definition: config.py:113
New line sequence to be stored in :py:class:Configuration.
Definition: config.py:154
String value to be stored in :py:class:Configuration.
Definition: config.py:102
Time string to be stored in :py:class:Configuration.
Definition: config.py:124