use /dev/null as known_hosts, hope to lower noise from ssh warning messages
[tests.git] / system / TestSsh.py
1 #
2 # Thierry Parmentelat - INRIA
3 #
4 # class for issuing commands on a box, either local or remote
5 #
6 # the notion of 'buildname' is for providing each test run with a dir of its own
7 # buildname is generally the name of the build being tested, and can be considered unique
8 #
9 # thus 'run_in_buildname' mostly :
10 # (*) either runs locally in . - as on a local node we are already in a dedicated directory
11 # (*) or makes sure that there's a remote dir called 'buildname' and runs in it
12 #
13 # also, the copy operations
14 # (*) either do nothing if ran locally
15 # (*) or copy a local file into the remote 'buildname' 
16
17
18 import os.path
19 import utils
20
21 class TestSsh:
22     
23     # inserts a backslash before each occurence of the following chars
24     # \ " ' < > & | ; ( ) $ * ~ 
25     @staticmethod
26     def backslash_shell_specials (command):
27         result=''
28         for char in command:
29             if char in "\\\"'<>&|;()$*~":
30                 result +='\\'+char
31             else:
32                 result +=char
33         return result
34
35     # check main IP address against the provided hostname
36     @staticmethod
37     def is_local_hostname (hostname):
38         if hostname == "localhost":
39             return True
40         import socket
41         try:
42             local_ip = socket.gethostbyname(socket.gethostname())
43             remote_ip = socket.gethostbyname(hostname)
44             return local_ip==remote_ip
45         except:
46             utils.header("WARNING : something wrong in is_local_hostname with hostname=%s"%hostname)
47             return False
48
49     def __init__(self,hostname,buildname=None,key=None, username=None):
50         self.hostname=hostname
51         self.buildname=buildname
52         self.key=key
53         self.username=username
54
55     def is_local(self):
56         return TestSsh.is_local_hostname(self.hostname)
57      
58     std_options="-o BatchMode=yes -o StrictHostKeyChecking=no -o CheckHostIP=no -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null "
59     
60     def key_part (self):
61         if not self.key:
62             return ""
63         return "-i %s "%self.key
64
65     def hostname_part (self):
66         if not self.username:
67             return self.hostname
68         else:
69             return "%s@%s"%(self.username,self.hostname)
70     
71     # command gets run on the right box
72     def actual_command (self, command,keep_stdin=False):
73         if self.is_local():
74             return command
75         ssh_command = "ssh "
76         if not keep_stdin:
77             ssh_command += "-n "
78         ssh_command += TestSsh.std_options
79         ssh_command += self.key_part()
80         ssh_command += "%s %s" %(self.hostname_part(),TestSsh.backslash_shell_specials(command))
81         return ssh_command
82
83     def run(self, command,background=False):
84         local_command = self.actual_command(command)
85         return utils.system(local_command,background)
86
87     def clean_dir (self,dirname):
88         if self.is_local():
89             return 0
90         return self.run("rm -rf %s"%dirname)
91
92     def mkdir (self,dirname=None):
93         if self.is_local():
94             if dirname:
95                 return os.path.mkdir(dirname)
96             return 0
97         if dirname:
98             dirname="%s/%s"%(self.buildname,dirname)
99         else:
100             dirname=self.buildname
101         return self.run("mkdir -p %s"%dirname)
102
103     def create_buildname_once (self):
104         if self.is_local():
105             return
106         # create remote buildname on demand
107         try:
108             self.buildname_created
109         except:
110             self.mkdir()
111             self.buildname_created=True
112
113     def run_in_buildname (self,command, background=False):
114         if self.is_local():
115             return utils.system(command,background)
116         self.create_buildname_once()
117         return self.run("cd %s ; %s"%(self.buildname,command),background)
118
119     def copy (self,local_file,recursive=False):
120         if self.is_local():
121             return 0
122         self.create_buildname_once()
123         scp_command="scp "
124         scp_command += TestSsh.std_options
125         if recursive: scp_command += "-r "
126         scp_command += self.key_part()
127         scp_command += "%s %s:%s/%s"%(local_file,self.hostname_part(),
128                                       self.buildname,os.path.basename(local_file) or ".")
129         return utils.system(scp_command)
130
131     def copy_abs (self,local_file,remote_file,recursive=False):
132         if self.is_local():
133             dest=""
134         else:
135             dest= "%s:"%self.hostname_part()
136         scp_command="scp "
137         scp_command += TestSsh.std_options
138         if recursive: scp_command += "-r "
139         scp_command += self.key_part()
140         scp_command += "%s %s%s"%(local_file,dest,remote_file)
141         return utils.system(scp_command)
142
143     def copy_home (self, local_file, recursive=False):
144         return self.copy_abs(local_file,os.path.basename(local_file),recursive)
145
146     def fetch (self, remote_file, local_file, recursive=False):
147         if self.is_local():
148             command="cp "
149             if recursive: command += "-r "
150             command += "%s %s"%(remote_file,local_file)
151         else:
152             command="scp "
153             command += TestSsh.std_options
154             if recursive: command += "-r "
155             command += self.key_part()
156             # absolute path - do not preprend buildname
157             if remote_file.find("/")==0:
158                 remote_path=remote_file
159             else:
160                 remote_path="%s/%s"%(self.buildname,remote_file)
161             command += "%s:%s %s"%(self.hostname_part(),remote_path,local_file)
162         return utils.system(command)
163
164     # this is only to avoid harmless message when host cannot be identified
165     # convenience only
166     # the only place where this is needed is when tring to reach a slice in a node,
167     # which is done from the test master box
168     def clear_known_hosts (self):
169         known_hosts = "%s/.ssh/known_hosts"%os.getenv("HOME")
170         utils.header("Clearing entry for %s in %s"%(self.hostname,known_hosts))
171         return utils.system("sed -i -e /^%s/d %s"%(self.hostname,known_hosts))
172