@@ -62,6 +62,15 @@ public class HBaseClusterManager extends Configured implements ClusterManager {
62
62
"timeout 30 /usr/bin/ssh %1$s %2$s%3$s%4$s \" sudo -u %6$s %5$s\" " ;
63
63
private String tunnelCmd ;
64
64
65
+ /**
66
+ * The command format that is used to execute the remote command with sudo. Arguments:
67
+ * 1 SSH options, 2 user name , 3 "@" if username is set, 4 host,
68
+ * 5 original command, 6 timeout.
69
+ */
70
+ private static final String DEFAULT_TUNNEL_SUDO_CMD =
71
+ "timeout %6$s /usr/bin/ssh %1$s %2$s%3$s%4$s \" sudo %5$s\" " ;
72
+ private String tunnelSudoCmd ;
73
+
65
74
private static final String RETRY_ATTEMPTS_KEY = "hbase.it.clustermanager.retry.attempts" ;
66
75
private static final int DEFAULT_RETRY_ATTEMPTS = 5 ;
67
76
@@ -86,6 +95,7 @@ public void setConf(Configuration conf) {
86
95
sshOptions = (sshOptions == null ) ? "" : sshOptions ;
87
96
sshUserName = (sshUserName == null ) ? "" : sshUserName ;
88
97
tunnelCmd = conf .get ("hbase.it.clustermanager.ssh.cmd" , DEFAULT_TUNNEL_CMD );
98
+ tunnelSudoCmd = conf .get ("hbase.it.clustermanager.ssh.sudo.cmd" , DEFAULT_TUNNEL_SUDO_CMD );
89
99
// Print out ssh special config if any.
90
100
if ((sshUserName != null && sshUserName .length () > 0 ) ||
91
101
(sshOptions != null && sshOptions .length () > 0 )) {
@@ -152,10 +162,32 @@ public String[] getExecString() {
152
162
LOG .info ("Executing full command [" + cmd + "]" );
153
163
return new String [] { "/usr/bin/env" , "bash" , "-c" , cmd };
154
164
}
165
+ }
166
+
167
+ /**
168
+ * Executes commands over SSH
169
+ */
170
+ protected class RemoteSudoShell extends Shell .ShellCommandExecutor {
171
+ private String hostname ;
172
+
173
+ public RemoteSudoShell (String hostname , String [] execString , long timeout ) {
174
+ this (hostname , execString , null , null , timeout );
175
+ }
176
+
177
+ public RemoteSudoShell (String hostname , String [] execString , File dir , Map <String , String > env ,
178
+ long timeout ) {
179
+ super (execString , dir , env , timeout );
180
+ this .hostname = hostname ;
181
+ }
155
182
156
183
@ Override
157
- public void execute () throws IOException {
158
- super .execute ();
184
+ public String [] getExecString () {
185
+ String at = sshUserName .isEmpty () ? "" : "@" ;
186
+ String remoteCmd = StringUtils .join (super .getExecString (), " " );
187
+ String cmd = String .format (tunnelSudoCmd , sshOptions , sshUserName , at , hostname , remoteCmd ,
188
+ timeOutInterval /1000f );
189
+ LOG .info ("Executing full command [" + cmd + "]" );
190
+ return new String [] { "/usr/bin/env" , "bash" , "-c" , cmd };
159
191
}
160
192
}
161
193
@@ -299,7 +331,8 @@ protected CommandProvider getCommandProvider(ServiceType service) throws IOExcep
299
331
*/
300
332
private Pair <Integer , String > exec (String hostname , ServiceType service , String ... cmd )
301
333
throws IOException {
302
- LOG .info ("Executing remote command: " + StringUtils .join (cmd , " " ) + " , hostname:" + hostname );
334
+ LOG .info ("Executing remote command: {} , hostname:{}" , StringUtils .join (cmd , " " ),
335
+ hostname );
303
336
304
337
RemoteShell shell = new RemoteShell (hostname , getServiceUser (service ), cmd );
305
338
try {
@@ -312,8 +345,8 @@ private Pair<Integer, String> exec(String hostname, ServiceType service, String.
312
345
+ ", stdout: " + output );
313
346
}
314
347
315
- LOG .info ("Executed remote command, exit code:" + shell .getExitCode ()
316
- + " , output:" + shell .getOutput ());
348
+ LOG .info ("Executed remote command, exit code:{} , output:{}" , shell .getExitCode (),
349
+ shell .getOutput ());
317
350
318
351
return new Pair <>(shell .getExitCode (), shell .getOutput ());
319
352
}
@@ -331,7 +364,52 @@ private Pair<Integer, String> execWithRetries(String hostname, ServiceType servi
331
364
retryCounter .sleepUntilNextRetry ();
332
365
} catch (InterruptedException ex ) {
333
366
// ignore
334
- LOG .warn ("Sleep Interrupted:" + ex );
367
+ LOG .warn ("Sleep Interrupted:" , ex );
368
+ }
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Execute the given command on the host using SSH
374
+ * @return pair of exit code and command output
375
+ * @throws IOException if something goes wrong.
376
+ */
377
+ public Pair <Integer , String > execSudo (String hostname , long timeout , String ... cmd )
378
+ throws IOException {
379
+ LOG .info ("Executing remote command: {} , hostname:{}" , StringUtils .join (cmd , " " ),
380
+ hostname );
381
+
382
+ RemoteSudoShell shell = new RemoteSudoShell (hostname , cmd , timeout );
383
+ try {
384
+ shell .execute ();
385
+ } catch (Shell .ExitCodeException ex ) {
386
+ // capture the stdout of the process as well.
387
+ String output = shell .getOutput ();
388
+ // add output for the ExitCodeException.
389
+ throw new Shell .ExitCodeException (ex .getExitCode (), "stderr: " + ex .getMessage ()
390
+ + ", stdout: " + output );
391
+ }
392
+
393
+ LOG .info ("Executed remote command, exit code:{} , output:{}" , shell .getExitCode (),
394
+ shell .getOutput ());
395
+
396
+ return new Pair <>(shell .getExitCode (), shell .getOutput ());
397
+ }
398
+
399
+ public Pair <Integer , String > execSudoWithRetries (String hostname , long timeout , String ... cmd )
400
+ throws IOException {
401
+ RetryCounter retryCounter = retryCounterFactory .create ();
402
+ while (true ) {
403
+ try {
404
+ return execSudo (hostname , timeout , cmd );
405
+ } catch (IOException e ) {
406
+ retryOrThrow (retryCounter , e , hostname , cmd );
407
+ }
408
+ try {
409
+ retryCounter .sleepUntilNextRetry ();
410
+ } catch (InterruptedException ex ) {
411
+ // ignore
412
+ LOG .warn ("Sleep Interrupted:" , ex );
335
413
}
336
414
}
337
415
}
0 commit comments