1 # Thierry Parmentelat <thierry.parmentelat@inria.fr>
2 # Copyright (C) 2010 INRIA
4 # class for issuing commands on a box, either local or remote
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
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
13 # also, the copy operations
14 # (*) either do nothing if ran locally
15 # (*) or copy a local file into the remote 'buildname'
25 # inserts a backslash before each occurence of the following chars
26 # \ " ' < > & | ; ( ) $ * ~
28 def backslash_shell_specials (command):
31 if char in "\\\"'<>&|;()$*~":
37 # check main IP address against the provided hostname
39 def is_local_hostname (hostname):
40 if hostname == "localhost":
44 local_ip = socket.gethostbyname(socket.gethostname())
45 remote_ip = socket.gethostbyname(hostname)
46 return local_ip==remote_ip
48 utils.header("WARNING : something wrong in is_local_hostname with hostname=%s"%hostname)
51 # some boxes have their working space in user's homedir (/root),
52 # some others in a dedicated area with max. space (/vservers)
53 # when root is not specified we use the homedir
54 def __init__(self,hostname,buildname=None,key=None, username=None,unknown_host=True, root=None):
55 self.hostname=hostname
56 self.buildname=buildname
58 self.username=username
59 self.unknown_host=unknown_host
63 return TestSsh.is_local_hostname(self.hostname)
65 std_options="-o BatchMode=yes -o StrictHostKeyChecking=no -o CheckHostIP=no -o ConnectTimeout=5 "
66 unknown_option="-o UserKnownHostsFile=/dev/null "
71 return "-i %s "%self.key
73 def hostname_part (self):
77 return "%s@%s"%(self.username,self.hostname)
79 # command gets run on the right box
80 def actual_command (self, command, keep_stdin=False, dry_run=False,backslash=True):
87 ssh_command += TestSsh.std_options
88 if self.unknown_host: ssh_command += TestSsh.unknown_option
89 ssh_command += self.key_part()
90 ssh_command += self.hostname_part() + " "
92 ssh_command += TestSsh.backslash_shell_specials(command)
94 ssh_command += command
98 def actual_argv (self, argv,keep_stdin=False, dry_run=False):
102 ssh_argv.append('ssh')
104 if not keep_stdin: ssh_argv.append('-n')
105 ssh_argv += TestSsh.std_options.split()
106 if self.unknown_host: ssh_argv += TestSsh.unknown_option.split()
107 ssh_argv += self.key_part().split()
108 ssh_argv.append(self.hostname_part())
112 def header (self,message):
113 if not message: return
114 print "===============",message
117 def run(self, command,message=None,background=False,dry_run=False):
118 local_command = self.actual_command(command, dry_run=dry_run)
120 utils.header("DRY RUN " + local_command)
124 return utils.system(local_command,background)
126 def run_in_buildname (self,command, background=False, dry_run=False):
128 return utils.system(command,background)
129 self.create_buildname_once(dry_run)
130 return self.run("cd %s ; %s"%(self.fullname(self.buildname),command),
131 background=background, dry_run=dry_run)
133 def fullname (self,dirname):
134 if self.root==None: return dirname
135 else: return os.path.join(self.root,dirname)
137 def mkdir (self,dirname=None,abs=False,dry_run=False):
140 return os.path.mkdir(dirname)
142 # ab. paths remain as-is
145 dirname="%s/%s"%(self.buildname,dirname)
147 dirname=self.buildname
148 dirname=self.fullname(dirname)
149 if dirname=='.': return
150 return self.run("mkdir -p %s"%dirname,dry_run=dry_run)
152 def rmdir (self,dirname=None, dry_run=False):
155 return shutil.rmtree(dirname)
158 dirname="%s/%s"%(self.buildname,dirname)
160 dirname=self.buildname
161 dirname=self.fullname(dirname)
162 return self.run("rm -rf %s"%dirname, dry_run=dry_run)
164 def create_buildname_once (self, dry_run):
167 # create remote buildname on demand
169 self.buildname_created
171 self.mkdir(dry_run=dry_run)
172 self.buildname_created=True
174 def copy (self,local_file,recursive=False,dry_run=False):
177 self.create_buildname_once(dry_run)
180 scp_command += TestSsh.std_options
181 if recursive: scp_command += "-r "
182 scp_command += self.key_part()
183 scp_command += "%s %s:%s/%s"%(local_file,self.hostname_part(),
184 self.fullname(self.buildname),os.path.basename(local_file) or ".")
186 utils.header ("DRY RUN TestSsh.copy %s"%scp_command)
187 # need to be consistent with the non-dry-run mode
189 return utils.system(scp_command)
191 def copy_abs (self,local_file,remote_file,recursive=False):
195 dest= "%s:"%self.hostname_part()
197 scp_command += TestSsh.std_options
198 if recursive: scp_command += "-r "
199 scp_command += self.key_part()
200 scp_command += "%s %s%s"%(local_file,dest,remote_file)
201 return utils.system(scp_command)
203 def copy_home (self, local_file, recursive=False):
204 return self.copy_abs(local_file,os.path.basename(local_file),recursive)
206 def fetch (self, remote_file, local_file, recursive=False, dry_run=False):
209 if recursive: command += "-r "
210 command += "%s %s"%(remote_file,local_file)
214 command += TestSsh.std_options
215 if recursive: command += "-r "
216 command += self.key_part()
217 # absolute path - do not preprend buildname
218 if remote_file.find("/")==0:
219 remote_path=remote_file
221 remote_path="%s/%s"%(self.buildname,remote_file)
222 remote_path=self.fullname(remote_path)
223 command += "%s:%s %s"%(self.hostname_part(),remote_path,local_file)
224 return utils.system(command)
226 # this is only to avoid harmless message when host cannot be identified
228 # the only place where this is needed is when tring to reach a slice in a node,
229 # which is done from the test master box
230 def clear_known_hosts (self):
231 known_hosts = "%s/.ssh/known_hosts"%os.getenv("HOME")
232 utils.header("Clearing entry for %s in %s"%(self.hostname,known_hosts))
233 return utils.system("sed -i -e /^%s/d %s"%(self.hostname,known_hosts))