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

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

/*
* 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.ImmutableSet;
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.OpenstackNovaPartitionValidator;
import org.apache.stratos.cloud.controller.validate.interfaces.PartitionValidator;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.NovaApiMetadata;
import org.jclouds.openstack.nova.v2_0.NovaAsyncApi;
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
import org.jclouds.openstack.nova.v2_0.domain.HostAggregate;
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
import org.jclouds.openstack.nova.v2_0.domain.Network;
import org.jclouds.openstack.nova.v2_0.domain.Volume;
import org.jclouds.openstack.nova.v2_0.domain.VolumeAttachment;
import org.jclouds.openstack.nova.v2_0.domain.zonescoped.AvailabilityZone;
import org.jclouds.openstack.nova.v2_0.extensions.AvailabilityZoneAPI;
import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
import org.jclouds.openstack.nova.v2_0.extensions.HostAggregateApi;
import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
import org.jclouds.openstack.nova.v2_0.extensions.VolumeApi;
import org.jclouds.openstack.nova.v2_0.extensions.VolumeAttachmentApi;
import org.jclouds.openstack.nova.v2_0.options.CreateVolumeOptions;
import org.jclouds.rest.RestContext;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

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

  private static final Log log = LogFactory.getLog(OpenstackNovaIaas.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 ";

  public OpenstackNovaIaas(IaasProvider iaasProvider) {
    super(iaasProvider);
  }
 
  @Override
  public void buildComputeServiceAndTemplate() {

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

    // builds and sets Template
    buildTemplate();

  }

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

    TemplateBuilder templateBuilder = iaasInfo.getComputeService()
        .templateBuilder();
    templateBuilder.imageId(iaasInfo.getImage());
        if(!(iaasInfo instanceof IaasProvider)) {
           templateBuilder.locationId(iaasInfo.getType());
        }
       
        // to avoid creation of template objects in each and every time, we
        // create all at once!

    String instanceType;

    // set instance type
    if (((instanceType = iaasInfo.getProperty(CloudControllerConstants.INSTANCE_TYPE)) != null)) {

      templateBuilder.hardwareId(instanceType);
    }

    Template template = templateBuilder.build();

    // In Openstack the call to IaaS should be blocking, in order to retrieve
    // IP addresses.
    boolean blockUntilRunning = true;
    if(iaasInfo.getProperty(CloudControllerConstants.BLOCK_UNTIL_RUNNING) != null) {
      blockUntilRunning = Boolean.parseBoolean(iaasInfo.getProperty(
          CloudControllerConstants.BLOCK_UNTIL_RUNNING));
    }
    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[] {});

    if (iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUPS) != null) {
      template.getOptions()
          .as(NovaTemplateOptions.class)
          .securityGroupNames(
              iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUPS).split(
                  CloudControllerConstants.ENTRY_SEPARATOR));
    }

    if (iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR) != null) {
      template.getOptions().as(NovaTemplateOptions.class)
          .keyPairName(iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR));
    }
   
        if (iaasInfo.getNetworkInterfaces() != null) {
            Set<Network> novaNetworksSet = new LinkedHashSet<Network>(iaasInfo.getNetworkInterfaces().length);
            for (NetworkInterface ni:iaasInfo.getNetworkInterfaces()) {
                novaNetworksSet.add(Network.builder().networkUuid(ni.getNetworkUuid()).fixedIp(ni.getFixedIp())
                        .portUuid(ni.getPortUuid()).build());
            }
            template.getOptions().as(NovaTemplateOptions.class).novaNetworks(novaNetworksSet);
        }
   
    if (iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) != null) {
      template.getOptions().as(NovaTemplateOptions.class)
          .availabilityZone(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE));
    }
   
    //TODO
//    if (iaas.getProperty(CloudControllerConstants.HOST) != null) {
//            template.getOptions().as(NovaTemplateOptions.class)
//                    .(CloudControllerConstants.HOST);
//        }

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

    @Override
  public void setDynamicPayload() {

      IaasProvider iaasInfo = getIaasProvider();
     
    if (iaasInfo.getTemplate() != null && iaasInfo.getPayload() != null) {

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

  }

  @Override
  public synchronized boolean createKeyPairFromPublicKey(String region, String keyPairName,
      String publicKey) {

    IaasProvider iaasInfo = getIaasProvider();
   
    String openstackNovaMsg = " Openstack-nova. Region: " + region
        + " - Name: ";

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();
    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
    KeyPairApi api = nova.getApi().getKeyPairExtensionForZone(region).get();

    KeyPair keyPair = api.createWithPublicKey(keyPairName, publicKey);

    if (keyPair != null) {

      iaasInfo.getTemplate().getOptions().as(NovaTemplateOptions.class)
          .keyPairName(keyPair.getName());

      log.info(SUCCESSFUL_LOG_LINE + openstackNovaMsg + keyPair.getName());
      return true;
    }

    log.error(FAILED_LOG_LINE + openstackNovaMsg);
    return false;

  }

  @Override
  public synchronized String associateAddress(NodeMetadata node) {
   
    IaasProvider iaasInfo = getIaasProvider();

    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();

    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);

    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
    FloatingIPApi floatingIp = nova.getApi().getFloatingIPExtensionForZone(
        region).get();

    String ip = null;
    // first try to find an unassigned IP.
    ArrayList<FloatingIP> unassignedIps = Lists.newArrayList(Iterables
        .filter(floatingIp.list(),
            new Predicate<FloatingIP>() {

              @Override
              public boolean apply(FloatingIP 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).getIp();
    }

    // if no unassigned IP is available, we'll try to allocate an IP.
    if (ip == null || ip.isEmpty()) {
      FloatingIP allocatedFloatingIP = floatingIp.create();
      if (allocatedFloatingIP == null) {
        String msg = "Failed to allocate an IP address.";
        log.error(msg);
        throw new CloudControllerException(msg);
      }
      ip = allocatedFloatingIP.getIp();
    }

    // wait till the fixed IP address gets assigned - this is needed before
    // we associate a public IP
    while (node.getPrivateAddresses() == null) {
      CloudControllerUtil.sleep(1000);
    }
   
    if (node.getPublicAddresses() != null
        && node.getPublicAddresses().iterator().hasNext()) {
      log.info("A public IP ("
          + node.getPublicAddresses().iterator().next()
          + ") is already allocated to the instance [id] : "
          + node.getId());
      return null;
    }

    int retries = 0;
    //TODO make 5 configurable
    while (retries < 5
        && !associateIp(floatingIp, ip, node.getProviderId())) {

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

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

    return ip;
  }
 
  @Override
  public synchronized String associatePredefinedAddress (NodeMetadata node, String ip) {
    if(log.isDebugEnabled()) {
      log.debug("OpenstackNovaIaas:associatePredefinedAddress:ip:" + ip);
    }
   
    IaasProvider iaasInfo = getIaasProvider();
   
    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();

    @SuppressWarnings("deprecation")
        NovaApi novaClient = context.unwrap(NovaApiMetadata.CONTEXT_TOKEN).getApi();
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);

    FloatingIPApi floatingIp = novaClient.getFloatingIPExtensionForZone(
        region).get();

    if(log.isDebugEnabled()) {
      log.debug("OpenstackNovaIaas:associatePredefinedAddress:floatingip:" + floatingIp);
    }
   
    // get the list of all unassigned IP.
    ArrayList<FloatingIP> unassignedIps = Lists.newArrayList(Iterables
        .filter(floatingIp.list(),
            new Predicate<FloatingIP>() {

              @Override
              public boolean apply(FloatingIP arg0) {
                // FIXME is this the correct filter?
                return arg0.getFixedIp() == null;
              }

            }));
   
    boolean isAvailable = false;
    for (FloatingIP fip : unassignedIps) {
      if(log.isDebugEnabled()) {
        log.debug("OpenstackNovaIaas:associatePredefinedAddress:iterating over available floatingip:" + fip);
      }
      if (ip.equals(fip.getIp())) {
        if(log.isDebugEnabled()) {
          log.debug("OpenstackNovaIaas:associatePredefinedAddress:floating ip in use:" + fip + " /ip:" + ip);
        }
        isAvailable = true;
        break;
      }
    }
   
    if (isAvailable) {
      // assign ip
      if(log.isDebugEnabled()) {
        log.debug("OpenstackNovaIaas:associatePredefinedAddress:assign floating ip:" + ip);
      }
      // exercise same code as in associateAddress()
      // 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 < 5
          && !associateIp(floatingIp, ip, node.getProviderId())) {

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

      NodeMetadataBuilder.fromNodeMetadata(node)
          .publicAddresses(ImmutableSet.of(ip)).build();

      log.info("OpenstackNovaIaas:associatePredefinedAddress:Successfully associated an IP address " + ip
          + " for node with id: " + node.getId());
    } else {
      // unable to allocate predefined ip,
      log.info("OpenstackNovaIaas:associatePredefinedAddress:Unable to allocate predefined ip:"
          + " for node with id: " + node.getId());
      return "";
    }

   
    NodeMetadataBuilder.fromNodeMetadata(node)
        .publicAddresses(ImmutableSet.of(ip)).build();

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

    return ip;
   
 

  @Override
  public synchronized void releaseAddress(String ip) {

    IaasProvider iaasInfo = getIaasProvider();
   
    ComputeServiceContext context = iaasInfo.getComputeService()
        .getContext();

    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);

    @SuppressWarnings("deprecation")
    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
    @SuppressWarnings("deprecation")
    FloatingIPApi floatingIPApi = nova.getApi()
        .getFloatingIPExtensionForZone(region).get();

    for (FloatingIP floatingIP : floatingIPApi.list()) {
      if (floatingIP.getIp().equals(ip)) {
        floatingIPApi.delete(floatingIP.getId());
        break;
      }
    }

  }

  private boolean associateIp(FloatingIPApi api, String ip, String id) {
    try {
      api.addToServer(ip, id);
      return true;
    } catch (RuntimeException ex) {
      return false;
    }
  }

    @Override
    public boolean isValidRegion(String region) throws InvalidRegionException {
      IaasProvider iaasInfo = getIaasProvider();
     
        // jclouds' zone = region in openstack
        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();
        RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        Set<String> zones = nova.getApi().getConfiguredZones();
        for (String configuredZone : zones) {
            if (region.equalsIgnoreCase(configuredZone)) {
                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();
     
      // jclouds availability zone = stratos zone
      if (region == null || zone == null || iaasInfo == null) {
            String msg = "Host or Zone or IaaSProvider is null: region: " + region + " - zone: " +
                    zone + " - IaaSProvider: " + iaasInfo;
            log.error(msg);
            throw new InvalidZoneException(msg);
        }
        ComputeServiceContext context = iaasInfo.getComputeService().getContext();
        RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        AvailabilityZoneAPI zoneApi = nova.getApi().getAvailabilityZoneApi(region);
        for (AvailabilityZone z : zoneApi.list()) {
     
          if (zone.equalsIgnoreCase(z.getName())) {
            if (log.isDebugEnabled()) {
              log.debug("Found a matching availability 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();
     
        if (host == null || zone == null || iaasInfo == null) {
            String msg = "Host or Zone or IaaSProvider is null: host: " + host + " - zone: " +
                    zone + " - IaaSProvider: " + iaasInfo;
            log.error(msg);
            throw new InvalidHostException(msg);
        }
        ComputeServiceContext context = iaasInfo.getComputeService().getContext();
        RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        HostAggregateApi hostApi = nova.getApi().getHostAggregateExtensionForZone(zone).get();
        for (HostAggregate hostAggregate : hostApi.list()) {
            for (String configuredHost : hostAggregate.getHosts()) {
                if (host.equalsIgnoreCase(configuredHost)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Found a matching host: " + host);
                    }
                    return true;
                }
            }
        }
       
        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 OpenstackNovaPartitionValidator();
    }

  @Override
  public String createVolume(int sizeGB) {
    IaasProvider iaasInfo = getIaasProvider();
    String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
    String zone = ComputeServiceBuilderUtil.extractZone(iaasInfo);
   
        if (region == null || iaasInfo == null) {
          log.fatal("Cannot create a new volume in the [region] : "+region
          +" of Iaas : "+iaasInfo);
            return null;
        }
        ComputeServiceContext context = iaasInfo.getComputeService().getContext();
       
        RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        VolumeApi api = nova.getApi().getVolumeExtensionForZone(region).get();
        Volume volume = api.create(sizeGB, CreateVolumeOptions.Builder.availabilityZone(zone));
        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 device = deviceName == null ? "/dev/vdc" : deviceName;
   
    if(region == null) {
      log.fatal("Cannot attach the volume [id]: "+volumeId+" in the [region] : "+region
          +" of Iaas : "+iaasInfo);
      return null;
    }
   
    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        VolumeAttachmentApi api = nova.getApi().getVolumeAttachmentExtensionForZone(region).get();
        VolumeAttachment attachment = api.attachVolumeToServerAsDevice(volumeId, instanceId, device);
       
        if (attachment == null) {
      log.fatal("Volume [id]: "+volumeId+" attachment for instance [id]: "+instanceId
          +" was unsuccessful. [region] : " + region
          + " of Iaas : " + iaasInfo);
      return null;
    }
   
    log.info("Volume [id]: "+volumeId+" attachment for instance [id]: "+instanceId
        +" was successful [status]: "+"Attaching"+". [region] : " + region
        + " of Iaas : " + iaasInfo);
    return "Attaching";
  }

  @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;
    }
   
    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
        VolumeAttachmentApi api = nova.getApi().getVolumeAttachmentExtensionForZone(region).get();
        if (api.detachVolumeFromServer(volumeId, 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;
    }
   
    RestContext<NovaApi, NovaAsyncApi> nova = context.unwrap();
    VolumeApi api = nova.getApi().getVolumeExtensionForZone(region).get();
        if (api.delete(volumeId)) {
          log.info("Deletion of Volume [id]: "+volumeId+" was successful. [region] : " + region
            + " of Iaas : " + iaasInfo);
        }
  }

    @Override
    public String getIaasDevice(String device) {
        return device;
    }

}
TOP

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

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.