remove (some) dependencies on open fd's - give up using tee - make sure that runtest...
[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 StrictHostKeyChecking=no -o BatchMode=yes "
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 fetch (self, remote_file, local_file, recursive=False):
132         if self.is_local():
133             command="cp "
134             if recursive: command += "-r "
135             command += "%s %s"%(remote_file,local_file)
136         else:
137             command="scp "
138             command += TestSsh.std_options
139             if recursive: command += "-r "
140             command += self.key_part()
141             command += "%s:%s/%s %s"%(self.hostname_part(),self.buildname,remote_file,local_file)
142         utils.system(command)
143
144     # this is only to avoid harmless message when host cannot be identified
145     # convenience only
146     # the only place where this is needed is when tring to reach a slice in a node,
147     # which is done from the test master box
148     def clear_known_hosts (self):
149         known_hosts = "%s/.ssh/known_hosts"%os.getenv("HOME")
150         utils.header("Clearing entry for %s in %s"%(self.hostname,known_hosts))
151         utils.system("sed -i -e /^%s/d %s"%(self.hostname,known_hosts))
152