示例#1
0
    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;
    }
示例#2
0
  /**
   * 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;
  }
示例#3
0
  /**
   * 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;
    }
  }
示例#4
0
    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());
      }
    }
示例#5
0
    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());
      }
    }
示例#6
0
  @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();
    }
  }