public List<String> call() throws IOException { List<String> names = new ArrayList<String>(); Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); while (nis.hasMoreElements()) { NetworkInterface ni = nis.nextElement(); LOGGER.fine("Listing up IP addresses for " + ni.getDisplayName()); Enumeration<InetAddress> e = ni.getInetAddresses(); while (e.hasMoreElements()) { InetAddress ia = e.nextElement(); if (ia.isLoopbackAddress()) { LOGGER.fine(ia + " is a loopback address"); continue; } if (!(ia instanceof Inet4Address)) { LOGGER.fine(ia + " is not an IPv4 address"); continue; } LOGGER.fine(ia + " is a viable candidate"); names.add(ia.getHostAddress()); } } return names; }
/** * This method tries to compute the name of the host that's reachable by all the other nodes. * * <p>Since it's possible that the slave is not reachable from the master (it may be behind a * firewall, connecting to master via JNLP), this method may return null. * * <p>It's surprisingly tricky for a machine to know a name that other systems can get to, * especially between things like DNS search suffix, the hosts file, and YP. * * <p>So the technique here is to compute possible interfaces and names on the slave, then try to * ping them from the master, and pick the one that worked. * * <p>The computation may take some time, so it employs caching to make the successive lookups * faster. * * @since 1.300 * @return null if the host name cannot be computed (for example because this computer is offline, * because the slave is behind the firewall, etc.) */ public String getHostName() throws IOException, InterruptedException { if (hostNameCached) // in the worst case we end up having multiple threads computing the host name simultaneously, // but that's not harmful, just wasteful. return cachedHostName; VirtualChannel channel = getChannel(); if (channel == null) return null; // can't compute right now for (String address : channel.call(new ListPossibleNames())) { try { InetAddress ia = InetAddress.getByName(address); if (!(ia instanceof Inet4Address)) { LOGGER.fine(address + " is not an IPv4 address"); continue; } if (!ComputerPinger.checkIsReachable(ia, 3)) { LOGGER.fine(address + " didn't respond to ping"); continue; } cachedHostName = ia.getCanonicalHostName(); hostNameCached = true; return cachedHostName; } catch (IOException e) { // if a given name fails to parse on this host, we get this error LOGGER.log(Level.FINE, "Failed to parse " + address, e); } } // allow the administrator to manually specify the host name as a fallback. HUDSON-5373 cachedHostName = channel.call(new GetFallbackName()); hostNameCached = true; return cachedHostName; }
/** * Check for the count of EC2 slaves and determine if a new slave can be added. Takes into account * both what Amazon reports as well as an internal count of slaves currently being "provisioned". * Check for the count of EC2 slaves and determine if a new slave can be added. Takes into account * both what Amazon reports as well as an internal count of slaves currently being "provisioned". * * @param templateDesc */ private boolean addProvisionedSlave(String ami, int amiCap, String templateDesc) throws AmazonClientException { int estimatedTotalSlaves = countCurrentEC2Slaves(null, null); int estimatedAmiSlaves = countCurrentEC2Slaves(ami, templateDesc); synchronized (provisioningAmis) { int currentProvisioning; for (int amiCount : provisioningAmis.values()) { estimatedTotalSlaves += amiCount; } try { currentProvisioning = provisioningAmis.get(ami); } catch (NullPointerException npe) { currentProvisioning = 0; } estimatedAmiSlaves += currentProvisioning; if (estimatedTotalSlaves >= instanceCap) { LOGGER.log( Level.INFO, "Total instance cap of " + instanceCap + " reached, not provisioning."); return false; // maxed out } if (estimatedAmiSlaves >= amiCap) { LOGGER.log( Level.INFO, "AMI Instance cap of " + amiCap + " reached for ami " + ami + ", not provisioning."); return false; // maxed out } LOGGER.log( Level.INFO, "Provisioning for AMI " + ami + "; " + "Estimated number of total slaves: " + String.valueOf(estimatedTotalSlaves) + "; " + "Estimated number of slaves for ami " + ami + ": " + String.valueOf(estimatedAmiSlaves)); provisioningAmis.put(ami, currentProvisioning + 1); return true; } }
protected FormValidation doTestConnection( URL ec2endpoint, boolean useInstanceProfileForCredentials, String accessId, String secretKey, String privateKey) throws IOException, ServletException { try { AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, accessId, secretKey); AmazonEC2 ec2 = connect(credentialsProvider, ec2endpoint); ec2.describeInstances(); if (privateKey == null) return FormValidation.error( "Private key is not specified. Click 'Generate Key' to generate one."); if (privateKey.trim().length() > 0) { // check if this key exists EC2PrivateKey pk = new EC2PrivateKey(privateKey); if (pk.find(ec2) == null) return FormValidation.error( "The EC2 key pair private key isn't registered to this EC2 region (fingerprint is " + pk.getFingerprint() + ")"); } return FormValidation.ok(Messages.EC2Cloud_Success()); } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to check EC2 credential", e); return FormValidation.error(e.getMessage()); } }
public FormValidation doGenerateKey( StaplerResponse rsp, URL ec2EndpointUrl, boolean useInstanceProfileForCredentials, String accessId, String secretKey) throws IOException, ServletException { try { AWSCredentialsProvider credentialsProvider = createCredentialsProvider(useInstanceProfileForCredentials, accessId, secretKey); AmazonEC2 ec2 = connect(credentialsProvider, ec2EndpointUrl); List<KeyPairInfo> existingKeys = ec2.describeKeyPairs().getKeyPairs(); int n = 0; while (true) { boolean found = false; for (KeyPairInfo k : existingKeys) { if (k.getKeyName().equals("hudson-" + n)) found = true; } if (!found) break; n++; } CreateKeyPairRequest request = new CreateKeyPairRequest("hudson-" + n); KeyPair key = ec2.createKeyPair(request).getKeyPair(); rsp.addHeader( "script", "findPreviousFormItem(button,'privateKey').value='" + key.getKeyMaterial().replace("\n", "\\n") + "'"); return FormValidation.ok(Messages.EC2Cloud_Success()); } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to check EC2 credential", e); return FormValidation.error(e.getMessage()); } }
@Override public Collection<PlannedNode> provision(Label label, int excessWorkload) { try { // Count number of pending executors from spot requests for (EC2SpotSlave n : NodeIterator.nodes(EC2SpotSlave.class)) { // If the slave is online then it is already counted by Jenkins // We only want to count potential additional Spot instance // slaves if (n.getComputer().isOffline() && label.matches(n.getAssignedLabels())) { DescribeSpotInstanceRequestsRequest dsir = new DescribeSpotInstanceRequestsRequest() .withSpotInstanceRequestIds(n.getSpotInstanceRequestId()); for (SpotInstanceRequest sir : connect().describeSpotInstanceRequests(dsir).getSpotInstanceRequests()) { // Count Spot requests that are open and still have a // chance to be active // A request can be active and not yet registered as a // slave. We check above // to ensure only unregistered slaves get counted if (sir.getState().equals("open") || sir.getState().equals("active")) { excessWorkload -= n.getNumExecutors(); } } } } LOGGER.log(Level.INFO, "Excess workload after pending Spot instances: " + excessWorkload); List<PlannedNode> r = new ArrayList<PlannedNode>(); final SlaveTemplate t = getTemplate(label); int amiCap = t.getInstanceCap(); while (excessWorkload > 0) { if (!addProvisionedSlave(t.ami, amiCap, t.description)) { break; } r.add( new PlannedNode( t.getDisplayName(), Computer.threadPoolForRemoting.submit( new Callable<Node>() { public Node call() throws Exception { // TODO: record the output somewhere try { EC2AbstractSlave s = t.provision(StreamTaskListener.fromStdout()); Hudson.getInstance().addNode(s); // EC2 instances may have a long init script. If we // declare // the provisioning complete by returning without // the connect // operation, NodeProvisioner may decide that it // still wants // one more instance, because it sees that (1) all // the slaves // are offline (because it's still being launched) // and // (2) there's no capacity provisioned yet. // // deferring the completion of provisioning until // the launch // goes successful prevents this problem. s.toComputer().connect(false).get(); return s; } finally { decrementAmiSlaveProvision(t.ami); } } }), t.getNumExecutors())); excessWorkload -= t.getNumExecutors(); } return r; } catch (AmazonClientException e) { LOGGER.log(Level.WARNING, "Failed to count the # of live instances on EC2", e); return Collections.emptyList(); } }