@Override @SuppressWarnings("unchecked") public T build() { return (T) new SshjTool(this); } }
public int execShellDirect(Map<String,?> props, List<String> commands, Map<String,?> env) { OutputStream out = getOptionalVal(props, PROP_OUT_STREAM); OutputStream err = getOptionalVal(props, PROP_ERR_STREAM); Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT); List<String> cmdSequence = toCommandSequence(commands, env); List<String> allcmds = ImmutableList.<String>builder() .add(getOptionalVal(props, PROP_DIRECT_HEADER)) .addAll(cmdSequence) .add("exit $?") .build(); if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {}: {}", host, allcmds); Integer result = acquire(new ShellAction(allcmds, out, err, execTimeout)); if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} completed: return status {}", host, result); return asInt(result, -1); }
public SshjTool(Map<String,?> map) { this(builder().from(map)); }
@Override public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer) { return copyToServer(props, newInputStreamSupplier(localFile), (int)localFile.length(), pathAndFileOnRemoteServer); }
@Override public void connect() { try { if (LOG.isTraceEnabled()) LOG.trace("Connecting SshjTool {} ({})", this, System.identityHashCode(this)); acquire(sshClientConnection); } catch (Exception e) { if (LOG.isDebugEnabled()) LOG.debug(toString()+" failed to connect (rethrowing)", e); throw propagate(e, "failed to connect"); } }
try { action.clear(); if (LOG.isTraceEnabled()) LOG.trace(">> ({}) acquiring {}", toString(), action); Stopwatch perfStopwatch = Stopwatch.createStarted(); throw new IllegalStateException("Problem in "+toString()+" for "+action, e); if (LOG.isTraceEnabled()) LOG.trace("<< ({}) acquired {}", toString(), returnVal); if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} {} took {}", new Object[] { sshClientConnection.getHostAndPort(), String errorMessage = String.format("(%s) error acquiring %s", toString(), action); String fullMessage = String.format("%s (attempt %s/%s, in time %s/%s)", errorMessage, (i+1), sshTries, Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), (sshTriesTimeout.equals(Duration.PRACTICALLY_FOREVER) ? "unlimited" : Time.makeTimeStringRounded(sshTriesTimeout))); try { disconnect(); } catch (Exception e2) { LOG.debug("<< ("+toString()+") error closing connection: "+e+" / "+e2, e); throw propagate(e, fullMessage + "; out of retries"); } else if (sshTriesTimeout.isShorterThan(stopwatch)) { LOG.debug("<< {} (rethrowing, out of time - max {}): {}", new Object[] { fullMessage, Time.makeTimeStringRounded(sshTriesTimeout), e.getMessage() }); } else { if (LOG.isDebugEnabled()) LOG.debug("<< {}: {}", fullMessage, e.getMessage()); backoffForAttempt(i + 1, errorMessage + ": " + e.getMessage()); if (action != sshClientConnection) connect();
@Override public Integer create() throws Exception { try { session = acquire(newSessionAction()); if (!shell.isOpen()) { if (LOG.isDebugEnabled()) LOG.debug("Shell closed to {} when executing {}", SshjTool.this.toString(), commands); break; } else {
@Test(groups = "Integration") public void testExtractingConnectablePassphraselessKey() throws Exception { Maybe<LocationSpec<? extends Location>> lhps = mgmt.getLocationRegistry().getLocationSpec("named:localhost-passphrase"); Preconditions.checkArgument(lhps.isPresent(), "This test requires a localhost named location called 'localhost-passphrase' (which should have a passphrase set)"); LocalhostMachineProvisioningLocation lhp = (LocalhostMachineProvisioningLocation) mgmt.getLocationManager().createLocation(lhps.get()); SshMachineLocation sm = lhp.obtain(); SshjToolBuilder builder = SshjTool.builder().host(sm.getAddress().getHostName()).user(sm.getUser()); KeyPair data = sm.findKeyPair(); if (data!=null) builder.privateKeyData(SecureKeys.toPem(data)); String password = sm.findPassword(); if (password!=null) builder.password(password); SshjTool tool = builder.build(); tool.connect(); ByteArrayOutputStream out = new ByteArrayOutputStream(); int result = tool.execCommands(MutableMap.<String,Object>of("out", out), Arrays.asList("date")); Assert.assertTrue(out.toString().contains(" 20"), "out="+out); assertEquals(result, 0); }
int statusResult = asInt(acquire(new ShellAction(buildRetrieveStatusCommand(), statusOut, statusErr, execTimeout)), -1); if (Strings.isEmpty(statusOutStr)) { if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; command successful but no result available on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return null; } else { if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieved status directly; returning '"+statusOutStr+"' on "+SshjTool.this.toString()+" (for "+getSummary()+")"); int result = Integer.parseInt(statusOutStr); return result; if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status directly received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return null; if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status failed; returning "+statusResult+" on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return statusResult;
@Override public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) { LocalDestFile localDestFile = acquire(new GetFileAction(pathAndFileOnRemoteServer, localFile)); return 0; }
@Override public Command create() throws Exception { try { session = acquire(newSessionAction());
@Override public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) { /* sshj needs to: * 1) to know the length of the InputStream to copy the file to perform copy; and * 2) re-read the input stream on retry if the first attempt fails. * For now, write it to a file, unless caller supplies a KnownSizeInputStream * * (We could have a switch where we hold it in memory if less than some max size, * but most the routines should supply a string or byte array or similar, * so we probably don't come here too often.) */ if (contents instanceof KnownSizeInputStream) { return copyToServer(props, Suppliers.ofInstance(contents), ((KnownSizeInputStream)contents).length(), pathAndFileOnRemoteServer); } else { File tempFile = writeTempFile(contents); try { return copyToServer(props, tempFile, pathAndFileOnRemoteServer); } finally { tempFile.delete(); } } }
@Override public int run() { String scriptContents = toScript(props, commands, env); if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as script: {}", host, scriptContents); copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath); return asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1); } }.run();
@Override @Deprecated // see super public void connect(int maxAttempts) { connect(); // FIXME Should callers instead configure sshTries? But that would apply to all ssh attempts }
@Override public void disconnect() { disconnectionCount.incrementAndGet(); super.disconnect(); }
@Override protected SshAction<Session> newSessionAction() { callCount.incrementAndGet(); if (callCount.incrementAndGet() >= successOnAttempt) { return super.newSessionAction(); } else { throw new RuntimeException("Simulating ssh execution failure"); } } };
@Override public SFTPClient create() throws IOException { checkConnected(); sftp = sshClientConnection.ssh.newSFTPClient(); return sftp; }
longPollResult = asInt(acquire(action, 3, nextPollTimeout), -1); } catch (RuntimeTimeoutException e) { if (LOG.isDebugEnabled()) LOG.debug("Long-poll timed out on "+SshjTool.this.toString()+" (for "+getSummary()+"): "+e); return null; if (LOG.isDebugEnabled()) LOG.debug("Long-poll succeeded (exit status 0) on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return longPollResult; // success if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return null; if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; most likely timeout; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return retrieveStatusCommand(); if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")"); Integer result = retrieveStatusCommand(); if (result != null) { LOG.warn("Aborting on "+consecutiveSshFailures+" consecutive ssh connection errors (return -1) when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")"); return -1; } else { LOG.info("Retrying after ssh connection error when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")"); return null;
protected <T, C extends SshAction<T>> T acquire(C action) { return acquire(action, sshTries, sshTriesTimeout == 0 ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshTriesTimeout)); }
@Override public void connect() { if (forbidden.get()) throw new IllegalStateException("forbidden at this time"); connectionCount.incrementAndGet(); super.connect(); }