Package org.apache.stratos.cloud.controller.iaases

Source Code of org.apache.stratos.cloud.controller.iaases.AWSEC2Iaas

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.stratos.cloud.controller.iaases;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.stratos.cloud.controller.exception.CloudControllerException;
import org.apache.stratos.cloud.controller.exception.InvalidHostException;
import org.apache.stratos.cloud.controller.exception.InvalidRegionException;
import org.apache.stratos.cloud.controller.exception.InvalidZoneException;
import org.apache.stratos.cloud.controller.interfaces.Iaas;
import org.apache.stratos.cloud.controller.jcloud.ComputeServiceBuilderUtil;
import org.apache.stratos.cloud.controller.pojo.IaasProvider;
import org.apache.stratos.cloud.controller.pojo.NetworkInterface;
import org.apache.stratos.cloud.controller.util.CloudControllerConstants;
import org.apache.stratos.cloud.controller.util.CloudControllerUtil;
import org.apache.stratos.cloud.controller.validate.AWSEC2PartitionValidator;
import org.apache.stratos.cloud.controller.validate.interfaces.PartitionValidator;
import org.jclouds.aws.ec2.AWSEC2Api;
import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
import org.jclouds.aws.ec2.features.AWSKeyPairApi;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location;
import org.jclouds.ec2.domain.Attachment;
import org.jclouds.ec2.domain.AvailabilityZoneInfo;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.PublicIpInstanceIdPair;
import org.jclouds.ec2.domain.Volume;
import org.jclouds.ec2.features.AvailabilityZoneAndRegionApi;
import org.jclouds.ec2.features.ElasticBlockStoreApi;
import org.jclouds.ec2.features.ElasticIPAddressApi;
import org.jclouds.ec2.options.DescribeAvailabilityZonesOptions;
import org.jclouds.ec2.options.DetachVolumeOptions;

import java.util.*;

@SuppressWarnings("deprecation")
public class AWSEC2Iaas extends Iaas {

  public AWSEC2Iaas(IaasProvider iaasProvider) {
    super(iaasProvider);
  }

  private static final Log log = LogFactory.getLog(AWSEC2Iaas.class);
  private static final String SUCCESSFUL_LOG_LINE = "A key-pair is created successfully in ";
  private static final String FAILED_LOG_LINE = "Key-pair is unable to create in ";

  @Override
  public void buildComputeServiceAndTemplate() {

    // builds and sets Compute Service
    ComputeServiceBuilderUtil.buildDefaultComputeService(getIaasProvider());

    // builds and sets Template
    buildTemplate();

  }

  public void buildTemplate() {
    IaasProvider iaasInfo = getIaasProvider();
    if (iaasInfo.getComputeService() == null) {
      String msg = "Compute service is null for IaaS provider: "
          + iaasInfo.getName();
      log.fatal(msg);
      throw new CloudControllerException(msg);
    }

    TemplateBuilder templateBuilder = iaasInfo.getComputeService()
        .templateBuilder();

    // set image id specified
    templateBuilder.imageId(iaasInfo.getImage());

        if(!(iaasInfo instanceof IaasProvider)) {
           templateBuilder.locationId(iaasInfo.getType());
        }

        if(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) != null) {
            Set<? extends Location> locations = iaasInfo.getComputeService().listAssignableLocations();
            for(Location location : locations) {
                if(location.getScope().toString().equalsIgnoreCase(CloudControllerConstants.ZONE_ELEMENT) &&
                        location.getId().equals(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE))) {
                    templateBuilder.locationId(location.getId());
                    log.info("ZONE has been set as " + iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE)
                            + " with id: " + location.getId());
                    break;
                }
            }
        }

    if (iaasInfo.getProperty(CloudControllerConstants.INSTANCE_TYPE) != null) {
      // set instance type eg: m1.large
      templateBuilder.hardwareId(iaasInfo.getProperty(CloudControllerConstants.INSTANCE_TYPE));
    }

    // build the Template
    Template template = templateBuilder.build();

        if(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) != null) {
            if(!template.getLocation().getId().equals(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE))) {
                log.warn("couldn't find assignable ZONE of id :" +
                        iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) + " in the IaaS. " +
                        "Hence using the default location as " + template.getLocation().getScope().toString() +
                        " with the id " + template.getLocation().getId());
            }
        }

    // if you wish to auto assign IPs, instance spawning call should be
    // blocking, but if you
    // wish to assign IPs manually, it can be non-blocking.
    // is auto-assign-ip mode or manual-assign-ip mode?
    boolean blockUntilRunning = Boolean.parseBoolean(iaasInfo
        .getProperty(CloudControllerConstants.AUTO_ASSIGN_IP));
    template.getOptions().as(TemplateOptions.class)
        .blockUntilRunning(blockUntilRunning);

    // this is required in order to avoid creation of additional security
    // groups by jclouds.
    template.getOptions().as(TemplateOptions.class)
        .inboundPorts(new int[] {});

    // set EC2 specific options


        if (iaasInfo.getProperty(CloudControllerConstants.ASSOCIATE_PUBLIC_IP_ADDRESS) != null) {
              boolean associatePublicIp =  Boolean.parseBoolean(iaasInfo.getProperty(
                      CloudControllerConstants.ASSOCIATE_PUBLIC_IP_ADDRESS));
            if(associatePublicIp){
                  template.getOptions().as(AWSEC2TemplateOptions.class)
                      .associatePublicIpAddress();
              }
        }

    if (iaasInfo.getProperty(CloudControllerConstants.SUBNET_ID) != null) {
      template.getOptions().as(AWSEC2TemplateOptions.class)
          .subnetId(iaasInfo.getProperty(CloudControllerConstants.SUBNET_ID));
    }

    if (iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) != null) {
      template.getOptions().as(AWSEC2TemplateOptions.class)
          .placementGroup(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE));
    }

        // security group names
    if (iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUPS) != null) {
      template.getOptions()
          .as(AWSEC2TemplateOptions.class)
          .securityGroups(iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUPS).split(
                            CloudControllerConstants.ENTRY_SEPARATOR));

    }

        // ability to define tags
        if (iaasInfo.getProperty(CloudControllerConstants.TAGS) != null) {
            template.getOptions()
                    .as(AWSEC2TemplateOptions.class)
                    .tags(Arrays.asList(iaasInfo.getProperty(CloudControllerConstants.TAGS)
                                        .split(CloudControllerConstants.ENTRY_SEPARATOR)));

        }

        // ability to define tags with Key-value pairs
        Map<String, String> keyValuePairTagsMap = new HashMap<String, String>();

        for (String propertyKey : iaasInfo.getProperties().keySet()){
            if(propertyKey.startsWith(CloudControllerConstants.TAGS_AS_KEY_VALUE_PAIRS_PREFIX)) {
                keyValuePairTagsMap.put(propertyKey.substring(CloudControllerConstants.TAGS_AS_KEY_VALUE_PAIRS_PREFIX.length()),
                        iaasInfo.getProperties().get(propertyKey));
                template.getOptions()
                    .as(AWSEC2TemplateOptions.class)
                    .userMetadata(keyValuePairTagsMap);
            }

        }
       

        if (iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUP_IDS) != null) {
            template.getOptions()
                    .as(AWSEC2TemplateOptions.class)
                    .securityGroupIds(iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUP_IDS)
                                        .split(CloudControllerConstants.ENTRY_SEPARATOR));

        }


    if (iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR) != null) {
      template.getOptions().as(AWSEC2TemplateOptions.class)
          .keyPair(iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR));
    }



        if (iaasInfo.getNetworkInterfaces() != null) {
            List<String> networks = new ArrayList<String>(iaasInfo.getNetworkInterfaces().length);
            for (NetworkInterface ni:iaasInfo.getNetworkInterfaces()) {
                networks.add(ni.getNetworkUuid());
            }
            template.getOptions().as(AWSEC2TemplateOptions.class).networks(networks);
        }

    // set Template
    iaasInfo.setTemplate(template);
  }

  @Override
  public void setDynamicPayload() {
    IaasProvider iaasInfo = getIaasProvider();
    if (iaasInfo.getTemplate() != null && iaasInfo.getPayload() != null) {

      iaasInfo.getTemplate().getOptions().as(AWSEC2TemplateOptions.class)
          .userData(iaasInfo.getPayload());
    }

  }

  @Override
  public synchronized boolean createKeyPairFromPublicKey(String region, String keyPairName,
      String publicKey) {
   
    IaasProvider iaasInfo = getIaasProvider();

    String ec2Msg = " ec2. Region: " + region + " - Key Pair Name: ";

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    AWSKeyPairApi keyPairApi = context.unwrapApi(AWSEC2Api.class).getKeyPairApiForRegion(region).get();

    KeyPair keyPair = keyPairApi.importKeyPairInRegion(region, keyPairName, publicKey);
   
    if (keyPair != null) {

      iaasInfo.getTemplate().getOptions().as(AWSEC2TemplateOptions.class)
          .keyPair(keyPair.getKeyName());

      log.info(SUCCESSFUL_LOG_LINE + ec2Msg + keyPair.getKeyName());
      return true;
    }

    log.error(FAILED_LOG_LINE + ec2Msg);

    return false;
  }

  @Override
  public synchronized String associateAddress(NodeMetadata node) {

    IaasProvider iaasInfo = getIaasProvider();
   
    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    ElasticIPAddressApi elasticIPAddressApi = context.unwrapApi(AWSEC2Api.class).getElasticIPAddressApi().get();
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
   
    String ip = null;

    // first try to find an unassigned IP.
    ArrayList<PublicIpInstanceIdPair> unassignedIps = Lists
        .newArrayList(Iterables.filter(elasticIPAddressApi.describeAddressesInRegion(region, new String[0]),
            new Predicate<PublicIpInstanceIdPair>() {

              @Override
              public boolean apply(PublicIpInstanceIdPair arg0) {
                return arg0.getInstanceId() == null;
              }

            }));

    if (!unassignedIps.isEmpty()) {
      // try to prevent multiple parallel launches from choosing the same
      // ip.
      Collections.shuffle(unassignedIps);
      ip = Iterables.getLast(unassignedIps).getPublicIp();
    }

    // if no unassigned IP is available, we'll try to allocate an IP.
    if (ip == null || ip.isEmpty()) {
      try {
        ip = elasticIPAddressApi
            .allocateAddressInRegion(region);
        log.info("Allocated ip [" + ip + "]");

      } catch (Exception e) {
        String msg = "Failed to allocate an IP address. All IP addresses are in use.";
        log.error(msg, e);
        throw new CloudControllerException(msg, e);
      }
    }

    String id = node.getProviderId();

    // wait till the fixed IP address gets assigned - this is needed before
    // we associate a
    // public IP

    while (node.getPrivateAddresses() == null) {
      CloudControllerUtil.sleep(1000);
    }

    int retries = 0;
    while (retries < 12 && !associatePublicIp(elasticIPAddressApi, region, ip, id)) {

      // wait for 5s
      CloudControllerUtil.sleep(5000);
      retries++;
    }

    log.debug("Successfully associated an IP address " + ip
        + " for node with id: " + node.getId());

    return ip;

  }
 
  @Override
  public String associatePredefinedAddress(NodeMetadata node, String ip) {
      return "";
    }

  /**
   * @param addressApi
   * @param region
   * @param ip
   * @param id
   */
  private boolean associatePublicIp(ElasticIPAddressApi addressApi, String region,
      String ip, String id) {
    try {
      addressApi.associateAddressInRegion(
          region, ip, id);
      log.info("Successfully associated public IP ");
      return true;
    } catch (Exception e) {
      log.debug("Exception in associating public IP " + e.getMessage());
      return false;
    }
  }

  @Override
  public synchronized void releaseAddress(String ip) {

    IaasProvider iaasInfo = getIaasProvider();
   
    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
    ElasticIPAddressApi elasticIPAddressApi = context.unwrapApi(AWSEC2Api.class).getElasticIPAddressApi().get();
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);

    elasticIPAddressApi.disassociateAddressInRegion(
        region, ip);
    elasticIPAddressApi.releaseAddressInRegion(region,
        ip);
  }

    @Override
    public boolean isValidRegion(String region) throws InvalidRegionException {
     
      IaasProvider iaasInfo = getIaasProvider();
     
        if (region == null || iaasInfo == null) {
            String msg =
                         "Region or IaaSProvider is null: region: " + region + " - IaaSProvider: " +
                                 iaasInfo;
            log.error(msg);
            throw new InvalidRegionException(msg);
        }
       
        ComputeServiceContext context = iaasInfo.getComputeService().getContext();
        Set<String> regions = context.unwrapApi(AWSEC2Api.class).getConfiguredRegions();
        for (String configuredRegion : regions) {
            if (region.equalsIgnoreCase(configuredRegion)) {
                if (log.isDebugEnabled()) {
                    log.debug("Found a matching region: " + region);
                }
                return true;
            }
        }
        String msg = "Invalid region: " + region +" in the iaas: "+iaasInfo.getType();
        log.error(msg);
        throw new InvalidRegionException(msg);
    }

    @Override
    public boolean isValidZone(String region, String zone) throws InvalidZoneException {
     
      IaasProvider iaasInfo = getIaasProvider();
     
        if (zone == null || iaasInfo == null) {
            String msg =
                         "Zone or IaaSProvider is null: zone: " + zone + " - IaaSProvider: " +
                                 iaasInfo;
            log.error(msg);
            throw new InvalidZoneException(msg);
        }
        ComputeServiceContext context = iaasInfo.getComputeService().getContext();
        AvailabilityZoneAndRegionApi zoneRegionApi = context.unwrapApi(AWSEC2Api.class).
            getAvailabilityZoneAndRegionApiForRegion(region).get();
       
        Set<AvailabilityZoneInfo> availabilityZones =
                                                      zoneRegionApi.describeAvailabilityZonesInRegion(region,
                                                              new DescribeAvailabilityZonesOptions[0]);
        for (AvailabilityZoneInfo zoneInfo : availabilityZones) {
            String configuredZone = zoneInfo.getZone();
            if (zone.equalsIgnoreCase(configuredZone)) {
                if (log.isDebugEnabled()) {
                    log.debug("Found a matching zone: " + zone);
                }
                return true;
            }
        }

        String msg = "Invalid zone: " + zone +" in the region: "+region+ " and of the iaas: "+iaasInfo.getType();
        log.error(msg);
        throw new InvalidZoneException(msg);

    }

    @Override
    public boolean isValidHost(String zone, String host) throws InvalidHostException {
     
      IaasProvider iaasInfo = getIaasProvider();
     
        // there's no such concept in EC2
        String msg = "Invalid host: " + host +" in the zone: "+zone+ " and of the iaas: "+iaasInfo.getType();
        log.error(msg);
        throw new InvalidHostException(msg);
    }

    @Override
    public PartitionValidator getPartitionValidator() {
        return new AWSEC2PartitionValidator();
    }

  @Override
  public String createVolume(int sizeGB) {
    IaasProvider iaasInfo = getIaasProvider();

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
    String zone = ComputeServiceBuilderUtil.extractZone(iaasInfo);
   
    if(region == null || zone == null) {
      log.fatal("Cannot create a new volume in the [region] : "+region
          +", [zone] : "+zone+" of Iaas : "+iaasInfo);
      return null;
    }
   
    ElasticBlockStoreApi blockStoreApi = context.unwrapApi(AWSEC2Api.class).getElasticBlockStoreApiForRegion(region).get();
   
    Volume volume = blockStoreApi.createVolumeInAvailabilityZone(zone, sizeGB);
   
    if (volume == null) {
      log.fatal("Volume creation was unsuccessful. [region] : " + region
          + ", [zone] : " + zone + " of Iaas : " + iaasInfo);
      return null;
    }
   
    log.info("Successfully created a new volume [id]: "+volume.getId()
        +" in [region] : "+region+", [zone] : "+zone+" of Iaas : "+iaasInfo);
    return volume.getId();
  }

  @Override
  public String attachVolume(String instanceId, String volumeId, String deviceName) {
    IaasProvider iaasInfo = getIaasProvider();

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
    String zone = ComputeServiceBuilderUtil.extractZone(iaasInfo);
    String device = deviceName == null ? "/dev/sdh" : deviceName;
   
    if(region == null || zone == null) {
      log.fatal("Cannot attach the volume [id]: "+volumeId+" in the [region] : "+region
          +", [zone] : "+zone+" of Iaas : "+iaasInfo);
      return null;
    }
   
    ElasticBlockStoreApi blockStoreApi = context.unwrapApi(AWSEC2Api.class).getElasticBlockStoreApiForRegion(region).get();

        Volume.Status volumeStatus = this.getVolumeStatus(blockStoreApi, region, volumeId);

        if(log.isDebugEnabled()){
            log.debug("Volume " + volumeId + " is in state " + volumeStatus);
        }

        while(volumeStatus != Volume.Status.AVAILABLE){
            try {
                // TODO Use a proper mechanism to wait till volume becomes available.
                Thread.sleep(1000);
                volumeStatus = this.getVolumeStatus(blockStoreApi, region, volumeId);
                if(log.isDebugEnabled()){
                    log.debug("Volume " + volumeId + " is still NOT in AVAILABLE. Current State=" + volumeStatus);
                }
            } catch (InterruptedException e) {
                // Ignoring the exception
            }
        }
        if(log.isDebugEnabled()){
            log.debug("Volume " + volumeId + " became  AVAILABLE");
        }

    Attachment attachment = blockStoreApi.attachVolumeInRegion(region, volumeId, instanceId, device);

    if (attachment == null) {
      log.fatal("Volume [id]: "+volumeId+" attachment for instance [id]: "+instanceId
          +" was unsuccessful. [region] : " + region
          + ", [zone] : " + zone + " of Iaas : " + iaasInfo);
      return null;
    }
   
    log.info("Volume [id]: "+volumeId+" attachment for instance [id]: "+instanceId
        +" was successful [status]: "+attachment.getStatus().value()+". [region] : " + region
        + ", [zone] : " + zone + " of Iaas : " + iaasInfo);
    return attachment.getStatus().value();
  }

    private Volume.Status getVolumeStatus(ElasticBlockStoreApi blockStoreApi, String region, String volumeId){
        Set<Volume> volumeDescriptions = blockStoreApi.describeVolumesInRegion(region, volumeId);
        Iterator<Volume> it = volumeDescriptions.iterator();
        return it.next().getStatus();
    }

  @Override
  public void detachVolume(String instanceId, String volumeId) {
    IaasProvider iaasInfo = getIaasProvider();

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
   
    if(region == null) {
      log.fatal("Cannot detach the volume [id]: "+volumeId+" from the instance [id]: "+instanceId
          +" of the [region] : "+region
          +" of Iaas : "+iaasInfo);
      return;
    }

    ElasticBlockStoreApi blockStoreApi = context.unwrapApi(AWSEC2Api.class).getElasticBlockStoreApiForRegion(region).get();

        Set<Volume> volumeDescriptions = blockStoreApi.describeVolumesInRegion(region, volumeId);
        Iterator<Volume> it = volumeDescriptions.iterator();

        while(it.hasNext()){
            Volume.Status status  = it.next().getStatus();

            if(status == Volume.Status.AVAILABLE){
                log.warn(String.format("Volume %s is already in AVAILABLE state. Volume seems to be detached somehow", volumeId));
                return;
            }
        }

    blockStoreApi.detachVolumeInRegion(region, volumeId, true, DetachVolumeOptions.Builder.fromInstance(instanceId));

    log.info("Detachment of Volume [id]: "+volumeId+" from instance [id]: "+instanceId
        +" was successful. [region] : " + region
        + " of Iaas : " + iaasInfo);
  }

  @Override
  public void deleteVolume(String volumeId) {
    IaasProvider iaasInfo = getIaasProvider();

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
   
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
   
    if(region == null) {
      log.fatal("Cannot delete the volume [id]: "+volumeId+" of the [region] : "+region
          +" of Iaas : "+iaasInfo);
      return;
    }
   
    ElasticBlockStoreApi blockStoreApi = context.unwrapApi(AWSEC2Api.class).getElasticBlockStoreApiForRegion(region).get();
    blockStoreApi.deleteVolumeInRegion(region, volumeId);
   
    log.info("Deletion of Volume [id]: "+volumeId+" was successful. [region] : " + region
        + " of Iaas : " + iaasInfo);
  }

    @Override
    /*
        Converts the user defined volume device to Ec2 specific device.
        For example /dev/sdf is converted to /dev/xvdf
     */
    public String getIaasDevice(String device) {
        String[] split = device.split("/");
        String x = split[split.length-1];
        StringBuilder ec2Device = new StringBuilder();
        ec2Device.append("/" + split[1]);
        ec2Device.append("/xvd");
        ec2Device.append(x.charAt(x.length()-1));
        return  ec2Device.toString();
    }


}
TOP

Related Classes of org.apache.stratos.cloud.controller.iaases.AWSEC2Iaas

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.