You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stratos.apache.org by ni...@apache.org on 2014/12/01 14:15:01 UTC
[2/4] stratos git commit: Moving Cloud controller's iaas package back
to iaases, since this involves config changes, which we shouldn't be doing.
http://git-wip-us.apache.org/repos/asf/stratos/blob/0984bcef/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/CloudstackIaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/CloudstackIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/CloudstackIaas.java
new file mode 100644
index 0000000..a67fe8d
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/CloudstackIaas.java
@@ -0,0 +1,540 @@
+package org.apache.stratos.cloud.controller.iaases;
+
+
+import org.apache.commons.lang.StringUtils;
+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.util.ComputeServiceBuilderUtil;
+import org.apache.stratos.cloud.controller.domain.IaasProvider;
+import org.apache.stratos.cloud.controller.util.CloudControllerConstants;
+import org.apache.stratos.cloud.controller.iaases.validators.CloudstackPartitionValidator;
+import org.apache.stratos.cloud.controller.iaases.validators.PartitionValidator;
+import org.jclouds.cloudstack.CloudStackApi;
+import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
+import org.jclouds.cloudstack.domain.*;
+import org.jclouds.cloudstack.features.VolumeApi;
+import org.jclouds.cloudstack.options.ListPublicIPAddressesOptions;
+import org.jclouds.cloudstack.options.ListZonesOptions;
+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 java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+
+public class CloudstackIaas extends Iaas {
+
+ private static final Log log = LogFactory.getLog(CloudstackIaas.class);
+
+ public CloudstackIaas(IaasProvider iaasProvider) {
+ super(iaasProvider);
+ }
+
+ @Override
+ public void buildComputeServiceAndTemplate() {
+ // builds and sets Compute Service
+ ComputeServiceBuilderUtil.buildDefaultComputeService(getIaasProvider());
+ // builds and sets Template
+ buildTemplate();
+ }
+
+ @Override
+ public void buildTemplate() {
+
+ IaasProvider iaasInfo = getIaasProvider();
+
+ //if compute service is not available
+ if (iaasInfo.getComputeService() == null) {
+ String msg = "Compute service is null for IaaS provider: "
+ + iaasInfo.getName();
+ log.error(msg);
+ throw new CloudControllerException(msg);
+ }
+
+ //create templateBuilder
+ TemplateBuilder templateBuilder = iaasInfo.getComputeService()
+ .templateBuilder();
+
+ //**SET PROPERTIES TO templateBuilder OBJECT**//
+
+ /**
+ * PROPERTY - 1
+ * set image id specified
+ */
+ templateBuilder.imageId(iaasInfo.getImage());
+
+ /**
+ * PROPERTY-2
+ * if user has specified a zone in cloud-controller.xml, set the zone into templateBuilder object
+ * (user should provide the zone id for this, because zone name is not unique in cloudstack)
+ */
+ if (iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE) != null) {
+ Set<? extends Location> locations = iaasInfo.getComputeService().listAssignableLocations();
+ for (Location location : locations) {
+ if (location.getId().equals(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE))) {
+ //if the zone is valid set the zone to templateBuilder Object
+ templateBuilder.locationId(location.getId());
+ log.info("Zone has been set as " + iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE)
+ + " with id: " + location.getId());
+ break;
+ }
+ }
+ }
+
+ /**
+ * PROPERTY-3
+ * if user has specified an instance type in cloud-controller.xml, set the instance type into templateBuilder
+ * object.(service offering)
+ *Important:Specify the Service Offering type ID. Not the name. Because the name is not unique in cloudstack.
+ */
+ if (iaasInfo.getProperty(CloudControllerConstants.INSTANCE_TYPE) != null) {
+ templateBuilder.hardwareId(iaasInfo.getProperty(CloudControllerConstants.INSTANCE_TYPE));
+ }
+
+ //build the template
+ Template template = templateBuilder.build();
+
+ /**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 CLOUDSTACK SPECIFIC PROPERTIES TO TEMPLATE OBJECT**//
+
+ //set security group - If you are using basic zone
+ if (iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUP_IDS) != null) {
+ template.getOptions()
+ .as(CloudStackTemplateOptions.class)
+ .securityGroupIds(Arrays.asList(iaasInfo.getProperty(CloudControllerConstants.SECURITY_GROUP_IDS)
+ .split(CloudControllerConstants.ENTRY_SEPARATOR)));
+ }
+
+
+ /**
+ * set network ID - If you are using advanced zone
+ * in cloudstack sometimes we get unautorized exception if we didn't specify the
+ * domain ID and user name
+ */
+ if (iaasInfo.getProperty(CloudControllerConstants.NETWORK_IDS) != null) {
+ template.getOptions()
+ .as(CloudStackTemplateOptions.class)
+ .networks(Arrays.asList(iaasInfo.getProperty(CloudControllerConstants.NETWORK_IDS)
+ .split(CloudControllerConstants.ENTRY_SEPARATOR)));
+ }
+
+ //set user name
+ if (iaasInfo.getProperty(CloudControllerConstants.USER_NAME) != null) {
+ template.getOptions().as(CloudStackTemplateOptions.class)
+ .account(iaasInfo.getProperty(CloudControllerConstants.USER_NAME));
+ }
+ //set domain ID
+ if (iaasInfo.getProperty(CloudControllerConstants.DOMAIN_ID) != null) {
+ template.getOptions().as(CloudStackTemplateOptions.class)
+ .domainId(iaasInfo.getProperty(CloudControllerConstants.DOMAIN_ID));
+ }
+
+ /**
+ *Set key pair
+ * in cloudstack sometimes we get unauthorized exception if we didn't specify the
+ * domain ID and user name
+ */
+ if (iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR) != null) {
+ template.getOptions().as(CloudStackTemplateOptions.class)
+ .keyPair(iaasInfo.getProperty(CloudControllerConstants.KEY_PAIR));
+ }
+
+ // ability to define tags
+ if (iaasInfo.getProperty(CloudControllerConstants.TAGS) != null) {
+ template.getOptions()
+ .as(CloudStackTemplateOptions.class)
+ .tags(Arrays.asList(iaasInfo.getProperty(CloudControllerConstants.TAGS)
+ .split(CloudControllerConstants.ENTRY_SEPARATOR)));
+ }
+ //set disk offering to the instance
+ if (iaasInfo.getProperty(CloudControllerConstants.DISK_OFFERING) != null) {
+ template.getOptions()
+ .as(CloudStackTemplateOptions.class)
+ .diskOfferingId(iaasInfo.getProperty(CloudControllerConstants.DISK_OFFERING));
+ }
+
+ // set Template
+ iaasInfo.setTemplate(template);
+ }
+
+ @Override
+ public void setDynamicPayload() {
+ IaasProvider iaasInfo = getIaasProvider();
+ if (iaasInfo.getTemplate() != null && iaasInfo.getPayload() != null) {
+ iaasInfo.getTemplate().getOptions().as(CloudStackTemplateOptions.class)
+ .userMetadata(convertByteArrayToHashMap(iaasInfo.getPayload()));
+ }
+ }
+
+ /**
+ * IMPORTANT
+ * In cloudstack we can assign public IPs, if we are using an advanced zone only. If we are using a basic zone
+ * we cannot assign public ips.
+ * <p/>
+ * When we use an advanced zone, a public IP address will get automatically assigned to the vm. So we don't need
+ * to find an unallocated IP address and assign that address to the vm. If you are using a basic zone you cannot
+ * assign public IPs
+ * <p/>
+ * So this method will find the IP that has been assigned to the vm and return it.
+ */
+ @Override
+ public String associateAddress(NodeMetadata node) {
+
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+ String ip = null;
+
+ // get all allocated IPs
+ ListPublicIPAddressesOptions listPublicIPAddressesOptions = new ListPublicIPAddressesOptions();
+ listPublicIPAddressesOptions.zoneId(iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE));
+
+ Set<PublicIPAddress> publicIPAddresses = cloudStackApi.getAddressApi()
+ .listPublicIPAddresses(listPublicIPAddressesOptions);
+
+ String id = node.getProviderId(); //vm ID
+
+ for (PublicIPAddress publicIPAddress : publicIPAddresses) {
+ if (publicIPAddress.getVirtualMachineId().equals(id)) { //check whether this instance has
+ // already got an public ip or not
+ ip = publicIPAddress.getIPAddress(); //A public ip has been successfully assigned to the vm
+ log.info("Successfully associated an IP address " + ip
+ + " for node with id: " + node.getId());
+ break;
+ }
+
+ }
+
+ if (ip == null || ip.isEmpty()) { //IP has not been successfully assigned to VM(That means there are
+ // no more IPs available for the VM)
+ String msg = "No address associated for node with id: " + node.getId();
+ log.warn(msg);
+ throw new CloudControllerException(msg);
+ }
+
+ return ip;
+ }
+
+ @Override
+ public String associatePredefinedAddress(NodeMetadata node, String ip) {
+ return "";
+ }
+
+ @Override
+ public void releaseAddress(String ip) {
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+ cloudStackApi.getAddressApi().disassociateIPAddress(ip);
+ }
+
+ @Override
+ public boolean createKeyPairFromPublicKey(String region, String keyPairName, String publicKey) {
+
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+ SshKeyPair sshKeyPair = cloudStackApi.getSSHKeyPairApi().createSSHKeyPair(keyPairName);
+
+ if (sshKeyPair != null) {
+
+ iaasInfo.getTemplate().getOptions().as(CloudStackTemplateOptions.class)
+ .keyPair(sshKeyPair.getName());
+
+ log.info("A key-pair is created successfully - Key Pair Name: " + sshKeyPair.getName());
+ return true;
+ }
+ log.error("Key-pair is unable to create");
+ return false;
+ }
+
+ @Override
+ public boolean isValidRegion(String region) throws InvalidRegionException {
+
+ IaasProvider iaasInfo = getIaasProvider();
+ //no such method in Jclouds cloudstack api
+ 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();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+ ListZonesOptions listZonesOptions = new ListZonesOptions();
+ listZonesOptions.available(true);
+ Set<Zone> zoneSet = cloudStackApi.getZoneApi().listZones(listZonesOptions);
+
+ for (org.jclouds.cloudstack.domain.Zone configuredZone : zoneSet) {
+ if (configuredZone.getName().equalsIgnoreCase(zone)) {
+ return true;
+ }
+ }
+ String msg = "Invalid zone: " + zone + " in 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 method in jclouds cloustack api
+ 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 CloudstackPartitionValidator();
+ }
+
+ @Override
+ public String createVolume(int sizeGB, String snapshotId) {
+
+ // Snapshot id is not there in IaaS.createVolume() method in stratos 4.0.0
+ //todo return volume ID if volume is created
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService()
+ .getContext();
+
+ String zone = ComputeServiceBuilderUtil.extractZone(iaasInfo);
+ String diskOfferingID = iaasInfo.getTemplate().getOptions().as(CloudStackTemplateOptions.class)
+ .getDiskOfferingId();
+ if (zone == null && diskOfferingID == null) {
+ log.error("Could not create a volume in the , [zone] : " + zone + " of Iaas : " + iaasInfo);
+ return null;
+ }
+
+ VolumeApi volumeApi = context.unwrapApi(CloudStackApi.class).getVolumeApi();
+
+ Volume volume;
+ if (StringUtils.isEmpty(snapshotId)) {
+ if (log.isInfoEnabled()) {
+ log.info("Creating a volume in the zone " + zone);
+ }
+
+ //cloudstack jcloud api does not return a volume object
+ volumeApi.createVolumeFromCustomDiskOfferingInZone(null, diskOfferingID, zone, sizeGB);
+
+ // volume = blockStoreApi.createVolumeInAvailabilityZone(zone, sizeGB);
+ } else {
+ if (log.isInfoEnabled()) {
+ log.info("Creating a volume in the zone " + zone + " from the snapshot " + snapshotId);
+ }
+ volumeApi.createVolumeFromSnapshotInZone(null, diskOfferingID, zone);
+ }
+
+ return null;
+ }
+
+ @Override
+ public String attachVolume(String instanceId, String volumeId, String deviceName) {
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService()
+ .getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+
+ //get volume
+ org.jclouds.cloudstack.domain.Volume volume = cloudStackApi.getVolumeApi().getVolume(volumeId);
+
+ //get current volume state
+ Volume.State volumeState = volume.getState();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Volume " + volumeId + " is in state " + volumeState);
+ }
+
+ //if volume is not available, not allocated or cannot use
+ //volume state ALLOCATED means that volume has not been attached to any instance.
+
+ //TODO there is an error with logic.
+ if (!(volumeState == Volume.State.ALLOCATED || volumeState == Volume.State.CREATING
+ || volumeState == Volume.State.READY)) {
+ log.error(String.format("Volume %s can not be attached. Volume status is %s", volumeId, volumeState));
+ }
+
+ //check whether the account of volume and instance is same
+ if (!volume.getAccount().equals(cloudStackApi.getVirtualMachineApi()
+ .getVirtualMachine(instanceId).getAccount())) {
+ log.error(String.format("Volume %s can not be attached. Instance account and Volume account " +
+ "are not the same ", volumeId));
+ }
+
+ boolean volumeBecameAvailable = false, volumeBecameAttached = false;
+
+ try {
+ if (volumeState == Volume.State.CREATING) {
+
+ volumeBecameAvailable = waitForStatus(volumeId, Volume.State.ALLOCATED, 5);
+
+ } else if (volumeState == Volume.State.READY) {
+ volumeBecameAvailable = true;
+ }
+
+ } catch (TimeoutException e) {
+ log.error("[Volume ID] " + volumeId + "did not become ALLOCATED within expected timeout");
+ }
+
+ //if volume state is 'ALLOCATED'
+ if (volumeBecameAvailable) {
+
+ //attach volume into instance
+ cloudStackApi.getVolumeApi().attachVolume(volumeId, instanceId);
+
+ try {
+ volumeBecameAttached = waitForStatus(volumeId, Volume.State.READY, 2);
+ } catch (TimeoutException e) {
+ log.error("[Volume ID] " + volumeId + "did not become READY within expected timeout");
+ }
+ }
+
+ try {
+ // waiting 5seconds till volumes are actually attached.
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) {
+
+ }
+
+ //If volume state is not 'READY'
+ if (!volumeBecameAttached) {
+ log.error(String.format("[Volume ID] %s attachment is called, but not yet became attached", volumeId));
+ }
+
+ log.info(String.format("Volume [id]: %s attachment for instance [id]: %s was successful [status]: Attaching." +
+ " of Iaas : %s", volumeId, instanceId, iaasInfo));
+
+ return "Attaching";
+
+ }
+
+ @Override
+ public void detachVolume(String instanceId, String volumeId) {
+
+
+ IaasProvider iaasInfo = getIaasProvider();
+
+ ComputeServiceContext context = iaasInfo.getComputeService()
+ .getContext();
+
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Starting to detach volume %s from the instance %s", volumeId, instanceId));
+ }
+
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+
+ cloudStackApi.getVolumeApi().detachVolume(volumeId);
+
+ try {
+ //TODO this is true only for newly created volumes
+ if (waitForStatus(volumeId, Volume.State.ALLOCATED, 5)) {
+ log.info(String.format("Detachment of Volume [id]: %s from instance [id]: %s was successful of Iaas : %s", volumeId, instanceId, iaasInfo));
+ }
+ } catch (TimeoutException e) {
+ log.error(String.format("Detachment of Volume [id]: %s from instance [id]: %s was unsuccessful. [volume Status] : %s", volumeId, instanceId, iaasInfo));
+ }
+
+ }
+
+ @Override
+ public void deleteVolume(String volumeId) {
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService()
+ .getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+ cloudStackApi.getVolumeApi().deleteVolume(volumeId);
+ log.info("Deletion of Volume [id]: " + volumeId + " was successful. "
+ + " of Iaas : " + iaasInfo);
+ }
+
+ @Override
+ public String getIaasDevice(String device) {//todo implement this method(auto generated method)
+ return null;
+ }
+
+ private boolean waitForStatus(String volumeId, Volume.State expectedStatus, int timeoutInMilliseconds) throws TimeoutException {
+ int timeout = 1000 * 60 * timeoutInMilliseconds;
+ long timout = System.currentTimeMillis() + timeout;
+
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ CloudStackApi cloudStackApi = context.unwrapApi(CloudStackApi.class);
+
+ //get volume
+ org.jclouds.cloudstack.domain.Volume volume = cloudStackApi.getVolumeApi().getVolume(volumeId);
+
+ Volume.State volumeState = volume.getState();
+
+ while (volumeState != expectedStatus) {
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Volume %s is still NOT in %s. Current State=%s", volumeId, expectedStatus, volumeState));
+ }
+ if (volumeState == Volume.State.FAILED || volumeState == Volume.State.DESTROYED || volumeState == Volume.State.UNRECOGNIZED) {
+ log.error("Volume " + volumeId + " is in state" + volumeState);
+ return false;
+ }
+
+ Thread.sleep(1000);
+ volumeState = volume.getState();
+ if (System.currentTimeMillis() > timout) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ // Ignoring the exception
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Volume %s status became %s", volumeId, expectedStatus));
+ }
+
+ return true;
+ }
+
+ private Map<String, String> convertByteArrayToHashMap(byte[] byteArray) {
+
+ Map<String, String> map = new HashMap<String, String>();
+
+ String stringFromByteArray = new String(byteArray);
+ String[] keyValuePairs = stringFromByteArray.split(",");
+
+ for (String keyValuePair : keyValuePairs) {
+ String[] keyValue = keyValuePair.split("=");
+ if (keyValue.length > 1) {
+ map.put(keyValue[0], keyValue[1]);
+ }
+ }
+
+ return map;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/stratos/blob/0984bcef/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/DockerIaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/DockerIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/DockerIaas.java
new file mode 100644
index 0000000..da2916b
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/DockerIaas.java
@@ -0,0 +1,151 @@
+/*
+ * 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 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.util.ComputeServiceBuilderUtil;
+import org.apache.stratos.cloud.controller.domain.IaasProvider;
+import org.apache.stratos.cloud.controller.iaases.validators.DockerPartitionValidator;
+import org.apache.stratos.cloud.controller.iaases.validators.PartitionValidator;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+
+import java.util.Set;
+
+/**
+ * Docker iaas provider definition.
+ */
+public class DockerIaas extends Iaas {
+
+ private static final Log log = LogFactory.getLog(AWSEC2Iaas.class);
+
+ public DockerIaas(IaasProvider iaasProvider) {
+ super(iaasProvider);
+ }
+
+ @Override
+ public void buildComputeServiceAndTemplate() {
+ // builds and sets Compute Service
+ ComputeServiceBuilderUtil.buildDefaultComputeService(getIaasProvider());
+
+ // builds and sets Template
+ buildTemplate();
+ }
+
+ @Override
+ public void setDynamicPayload() {
+ log.warn("Not implemented: DockerIaas.setDynamicPayload()");
+ }
+
+ @Override
+ public String associateAddress(NodeMetadata node) {
+ log.warn("Not implemented: DockerIaas.associateAddress()");
+ return null;
+ }
+
+ @Override
+ public String associatePredefinedAddress(NodeMetadata node, String ip) {
+ log.warn("Not implemented: DockerIaas.associatePredefinedAddress()");
+ return null;
+ }
+
+ @Override
+ public void releaseAddress(String ip) {
+ log.warn("Not implemented: DockerIaas.releaseAddress()");
+ }
+
+ @Override
+ public boolean createKeyPairFromPublicKey(String region, String keyPairName, String publicKey) {
+ return false;
+ }
+
+ @Override
+ public boolean isValidRegion(String region) throws InvalidRegionException {
+ return true;
+ }
+
+ @Override
+ public boolean isValidZone(String region, String zone) throws InvalidZoneException {
+ return true;
+ }
+
+ @Override
+ public boolean isValidHost(String zone, String host) throws InvalidHostException {
+ return true;
+ }
+
+ @Override
+ public PartitionValidator getPartitionValidator() {
+ return new DockerPartitionValidator();
+ }
+
+ @Override
+ public void buildTemplate() {
+ IaasProvider iaasProvider = getIaasProvider();
+ ComputeService computeService = iaasProvider.getComputeService();
+ Set<? extends Image> images = computeService.listImages();
+ Image image = findImage(images, iaasProvider.getImage());
+ if(image == null) {
+ throw new CloudControllerException(String.format("Docker image not found: %s", iaasProvider.getImage()));
+ }
+ Template template = computeService.templateBuilder().fromImage(image).build();
+ iaasProvider.setTemplate(template);
+ }
+
+ private Image findImage(Set<? extends Image> images, String name) {
+ for(Image image : images) {
+ if(image.getDescription().contains(name))
+ return image;
+ }
+ return null;
+ }
+
+ @Override
+ public String createVolume(int sizeGB, String snapshotId) {
+ return null;
+ }
+
+ @Override
+ public String attachVolume(String instanceId, String volumeId, String deviceName) {
+ return null;
+ }
+
+ @Override
+ public void detachVolume(String instanceId, String volumeId) {
+
+ }
+
+ @Override
+ public void deleteVolume(String volumeId) {
+
+ }
+
+ @Override
+ public String getIaasDevice(String device) {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/stratos/blob/0984bcef/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/GCEIaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/GCEIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/GCEIaas.java
new file mode 100644
index 0000000..ba186b2
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/GCEIaas.java
@@ -0,0 +1,459 @@
+/*
+ * 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 java.util.*;
+
+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.util.ComputeServiceBuilderUtil;
+import org.apache.stratos.cloud.controller.domain.IaasProvider;
+import org.apache.stratos.cloud.controller.domain.NetworkInterface;
+import org.apache.stratos.cloud.controller.iaases.validators.GCEPartitionValidator;
+import org.apache.stratos.cloud.controller.iaases.validators.PartitionValidator;
+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.jclouds.ContextBuilder;
+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.apache.stratos.cloud.controller.util.CloudControllerConstants;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.features.DiskApi;
+import org.jclouds.googlecomputeengine.features.InstanceApi;
+import org.jclouds.googlecomputeengine.features.RegionApi;
+import org.jclouds.googlecomputeengine.features.ZoneApi;
+import org.jclouds.googlecomputeengine.domain.Disk;
+import org.jclouds.googlecomputeengine.domain.Instance;
+import org.jclouds.googlecomputeengine.domain.Region;
+import org.jclouds.googlecomputeengine.domain.Zone;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.options.AttachDiskOptions;
+import org.jclouds.googlecomputeengine.options.AttachDiskOptions.DiskType;
+
+import com.google.inject.Key;
+import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+import static org.jclouds.util.Predicates2.retry;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.common.base.Predicate;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.google.common.util.concurrent.Atomics;
+
+public class GCEIaas extends Iaas {
+
+
+ private static final Log log = LogFactory.getLog(GCEIaas.class);
+
+ private static final String PROJECTNAME = "projectName";
+
+ public GCEIaas(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) {
+ String msg = "Compute service is null for IaaS provider: "
+ + iaasInfo.getName();
+ log.fatal(msg);
+ throw new CloudControllerException(msg);
+ }
+
+ log.info("gce buildTemplate");
+
+ TemplateBuilder templateBuilder = iaasInfo.getComputeService()
+ .templateBuilder();
+
+ // set image id specified
+ templateBuilder.imageId(iaasInfo.getImage());
+
+ String zone = iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE);
+ if(zone != null) {
+ Set<? extends Location> locations = iaasInfo.getComputeService().listAssignableLocations();
+ for(Location location : locations) {
+ if(location.getScope().toString().equalsIgnoreCase(CloudControllerConstants.ZONE_ELEMENT) &&
+ location.getId().equals(zone)) {
+ templateBuilder.locationId(location.getId());
+ log.info("ZONE has been set as " + 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(zone != null) {
+ if(!template.getLocation().getId().equals(zone)) {
+ log.warn("couldn't find assignable ZONE of id :" + 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? - default mode is
+ // non-blocking
+ boolean blockUntilRunning = Boolean.parseBoolean(iaasInfo
+ .getProperty("autoAssignIp"));
+ 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(22, 80, 8080, 443, 8243);
+
+ if (zone != null) {
+ templateBuilder.locationId(zone);
+ log.debug("setting location to " + zone);
+ }
+
+ // 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()
+ .userMetadata(keyValuePairTagsMap);
+ }
+ log.info("usermeta data key:"+ propertyKey + " value: " + iaasInfo.getProperties().get(propertyKey));
+ }
+
+ if (iaasInfo.getNetworkInterfaces() != null) {
+ List<String> networks = new ArrayList<String>(iaasInfo.getNetworkInterfaces().length);
+ for (NetworkInterface ni:iaasInfo.getNetworkInterfaces()) {
+ networks.add(ni.getNetworkUuid());
+ log.info("using network interface " + ni.getNetworkUuid());
+ }
+ template.getOptions().as(TemplateOptions.class).networks(networks);
+ log.info("using network interface " + networks);
+ }
+
+ // set Template
+ iaasInfo.setTemplate(template);
+ }
+
+ @Override
+ public void setDynamicPayload() {
+ // in vCloud case we need to run a script
+ IaasProvider iaasInfo = getIaasProvider();
+
+ if (iaasInfo.getTemplate() == null || iaasInfo.getPayload() == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Payload for GCE not found");
+ }
+ return;
+ }
+
+ // Payload is a String value
+ String payload = new String(iaasInfo.getPayload());
+
+ log.info("setDynamicPayload " + payload);
+
+ Map<String, String> keyValuePairTagsMap = new HashMap<String, String>();
+ keyValuePairTagsMap.put("stratos_usermetadata", payload);
+ iaasInfo.getTemplate().getOptions().userMetadata(keyValuePairTagsMap);
+ }
+
+ @Override
+ public boolean createKeyPairFromPublicKey(String region, String keyPairName, String publicKey) {
+
+ // Not applicable for GCE - Not called by stratos cloud controller as well
+ return false;
+ }
+
+ @Override
+ public String associateAddress(NodeMetadata node) {
+
+ // TODO
+ return "";
+
+ }
+
+ @Override
+ public String associatePredefinedAddress(NodeMetadata node, String ip) {
+ return "";
+ }
+
+ @Override
+ public void releaseAddress(String ip) {
+ // TODO
+ }
+
+ @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);
+ }
+
+ GoogleComputeEngineApi api = getGCEApi();
+ RegionApi regionApi = api.getRegionApiForProject(iaasInfo.getProperty(PROJECTNAME));
+
+ for(IterableWithMarker<Region> page : regionApi.list()) {
+ for(Region r : page) {
+ if (region.equalsIgnoreCase(r.getName())) {
+ 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: region: " + region +
+ " zone: " + zone + " - IaaSProvider: " + iaasInfo;
+ log.error(msg);
+ throw new InvalidZoneException(msg);
+ }
+
+ GoogleComputeEngineApi api = getGCEApi();
+ ZoneApi zoneApi = api.getZoneApiForProject(iaasInfo.getProperty(PROJECTNAME));
+
+ for(IterableWithMarker<Zone> page : zoneApi.list()) {
+ for(Zone z : page) {
+ if (zone.equalsIgnoreCase(z.getName())) {
+ 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();
+
+ // Not called by cloud controller
+ // there's no such concept in GCE
+
+ 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 GCEPartitionValidator();
+ }
+
+ @Override
+ public String createVolume(int sizeGB, String snapshotId) {
+ // generate a random diskname
+ Random rand = new Random();
+ String diskName = "stratos-disk-" + rand.nextInt(100000);
+ DiskApi diskApi = getGCEDiskApi();
+ String zone = getZone();
+
+ log.debug("Creating volume: " + diskName + " in zone: " + zone + " of size: " + sizeGB);
+
+ Operation oper = diskApi.createInZone(diskName, sizeGB, zone);
+
+ oper = waitGCEOperationDone(oper);
+ if (oper.getStatus() != Operation.Status.DONE) {
+ log.error("Failed to create volume: " + diskName + " of size: " + sizeGB +
+ " in zone: " + zone + " operation: " + oper);
+ return null;
+ }
+
+ return diskName;
+ }
+
+ @Override
+ public String attachVolume(String instanceId, String volumeId, String deviceName) {
+ DiskApi diskApi = getGCEDiskApi();
+ InstanceApi instApi = getGCEInstanceApi();
+ String zone = getZone();
+
+ log.debug("Trying to attach volume: " + volumeId + " to instance: " + instanceId +
+ " in zone: " + zone + " at devicename: " + deviceName);
+
+ Disk disk = diskApi.getInZone(zone, volumeId);
+ if (disk == null) {
+ log.error("Failed to get volume: " + volumeId + " in zone: " + zone);
+ return null;
+ }
+
+ log.debug("Found volumeId: " + volumeId + " volume: " + disk);
+
+ Operation oper = instApi.attachDiskInZone(zone, instanceId,
+ new AttachDiskOptions().type(DiskType.PERSISTENT)
+ .source(disk.getSelfLink())
+ .mode(AttachDiskOptions.DiskMode.READ_WRITE)
+ .deviceName(deviceName));
+ oper = waitGCEOperationDone(oper);
+ if (oper.getStatus() != Operation.Status.DONE) {
+ log.error("Failed to attach volume: " + volumeId + " to instance: " + instanceId +
+ " in zone: " + zone + " at device: " + deviceName + " operation: " + oper);
+ return null;
+ }
+
+ return volumeId;
+ }
+
+ @Override
+ public void detachVolume(String instanceId, String volumeId) {
+ InstanceApi instApi = getGCEInstanceApi();
+ String zone = getZone();
+ Instance inst = instApi.getInZone(zone, instanceId);
+
+ log.debug("Trying to detach volume: " + volumeId + " from instance: " + instanceId +
+ " " + inst + " in zone: " + zone);
+
+ if (inst == null) {
+ log.error("Failed to find instance: " + instanceId + " in zone: " + zone);
+ return;
+ }
+
+ for(Instance.AttachedDisk disk : inst.getDisks()) {
+ Instance.PersistentAttachedDisk persistentDisk = (Instance.PersistentAttachedDisk)disk;
+
+ log.debug("Found disk - src: " + persistentDisk.getSourceDiskName() +
+ " devicename: " + persistentDisk.getDeviceName());
+
+ if (persistentDisk.getSourceDiskName().equals(volumeId)) {
+ Operation oper = instApi.detachDiskInZone(zone, instanceId, persistentDisk.getDeviceName().get());
+ oper = waitGCEOperationDone(oper);
+ if (oper.getStatus() != Operation.Status.DONE) {
+ log.error("Failed to detach volume: " + volumeId + " to instance: " + instanceId +
+ " in zone: " + zone + " at device: " + persistentDisk.getDeviceName() +
+ " result operation: " + oper);
+ }
+ return;
+ }
+ }
+
+ log.error("Cannot find volume: " + volumeId + " in instance: " + instanceId);
+ }
+
+ @Override
+ public void deleteVolume(String volumeId) {
+ DiskApi diskApi = getGCEDiskApi();
+ String zone = getZone();
+
+ log.debug("Deleting volume: " + volumeId + " in zone: " + zone);
+
+ Operation oper = diskApi.deleteInZone(zone, volumeId);
+
+ oper = waitGCEOperationDone(oper);
+ if (oper.getStatus() != Operation.Status.DONE) {
+ log.error("Failed to delete volume: " + volumeId + " in zone: " + zone +
+ " operation: " + oper);
+ }
+ }
+
+ @Override
+ public String getIaasDevice(String device) {
+ return device;
+ }
+
+ private String getZone() {
+ IaasProvider iaasInfo = getIaasProvider();
+ return iaasInfo.getProperty(CloudControllerConstants.AVAILABILITY_ZONE);
+ }
+
+ private GoogleComputeEngineApi getGCEApi() {
+ IaasProvider iaasInfo = getIaasProvider();
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ GoogleComputeEngineApi api = context.unwrapApi(GoogleComputeEngineApi.class);
+
+ return api;
+ }
+
+ private DiskApi getGCEDiskApi() {
+ IaasProvider iaasInfo = getIaasProvider();
+ String projectName = iaasInfo.getProperty(PROJECTNAME);
+ return getGCEApi().getDiskApiForProject(projectName);
+ }
+
+ private InstanceApi getGCEInstanceApi() {
+ IaasProvider iaasInfo = getIaasProvider();
+ String projectName = iaasInfo.getProperty(PROJECTNAME);
+ return getGCEApi().getInstanceApiForProject(projectName);
+ }
+
+ private Operation waitGCEOperationDone(Operation operation) {
+ int maxWaitTime = 15; // 15 seconds
+ IaasProvider iaasInfo = getIaasProvider();
+ Injector injector = ContextBuilder.newBuilder(iaasInfo.getProvider())
+ .credentials(iaasInfo.getIdentity(), iaasInfo.getCredential())
+ .buildInjector();
+ Predicate<AtomicReference<Operation>> zoneOperationDonePredicate =
+ injector.getInstance(Key.get(new TypeLiteral<Predicate<AtomicReference<Operation>>>() {
+ }, Names.named("zone")));
+ AtomicReference<Operation> operationReference = Atomics.newReference(operation);
+ retry(zoneOperationDonePredicate, maxWaitTime, 1, SECONDS).apply(operationReference);
+
+ return operationReference.get();
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/stratos/blob/0984bcef/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/Iaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/Iaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/Iaas.java
new file mode 100644
index 0000000..5faeca1
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/Iaas.java
@@ -0,0 +1,173 @@
+/*
+ * 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 org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+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.domain.IaasProvider;
+import org.apache.stratos.cloud.controller.iaases.validators.PartitionValidator;
+
+/**
+ * All IaaSes that are going to support by Cloud Controller, should extend this abstract class.
+ */
+public abstract class Iaas {
+ /**
+ * Reference to the corresponding {@link IaasProvider}
+ */
+ private IaasProvider iaasProvider;
+
+ public Iaas(IaasProvider iaasProvider) {
+ this.setIaasProvider(iaasProvider);
+ }
+
+ public IaasProvider getIaasProvider() {
+ return iaasProvider;
+ }
+
+ public void setIaasProvider(IaasProvider iaasProvider) {
+ this.iaasProvider = iaasProvider;
+ }
+
+ /**
+ * This should build the {@link ComputeService} object and the {@link Template} object,
+ * using the information from {@link IaasProvider} and should set the built
+ * {@link ComputeService} object in the {@link IaasProvider#setComputeService(ComputeService)}
+ * and also should set the built {@link Template} object in the
+ * {@link IaasProvider#setTemplate(Template)}.
+ */
+ public abstract void buildComputeServiceAndTemplate();
+
+ /**
+ * This method provides a way to set payload that can be obtained from {@link IaasProvider#getPayload()}
+ * in the {@link Template} of this IaaS.
+ */
+ public abstract void setDynamicPayload();
+
+ /**
+ * This will obtain an IP address from the allocated list and associate that IP with this node.
+ * @param node Node to be associated with an IP.
+ * @return associated public IP.
+ */
+ public abstract String associateAddress(NodeMetadata node);
+
+ /**
+ * This will obtain a predefined IP address and associate that IP with this node, if ip is already in use allocate ip from pool
+ * (through associateAddress())
+ * @param node Node to be associated with an IP.
+ * @ip preallocated floating Ip
+ * @return associated public IP.
+ */
+ public abstract String associatePredefinedAddress(NodeMetadata node, String ip);
+
+ /**
+ * This will deallocate/release the given IP address back to pool.
+ * @param iaasInfo corresponding {@link IaasProvider}
+ * @param ip public IP address to be released.
+ */
+ public abstract void releaseAddress(String ip);
+
+ /**
+ * This method should create a Key Pair corresponds to a given public key in the respective region having the name given.
+ * Also should override the value of the key pair in the {@link Template} of this IaaS.
+ * @param region region that the key pair will get created.
+ * @param keyPairName name of the key pair. NOTE: Jclouds adds a prefix : <code>jclouds#</code>
+ * @param publicKey public key, from which the key pair will be created.
+ * @return whether the key pair creation is successful or not.
+ */
+ public abstract boolean createKeyPairFromPublicKey(String region, String keyPairName, String publicKey);
+
+ /**
+ * Validate a given region name against a particular IaaS.
+ * If a particular IaaS doesn't have a concept called region, it can simply throw {@link InvalidRegionException}.
+ * @param region name of the region.
+ * @return whether the region is valid.
+ * @throws InvalidRegionException if the region is invalid.
+ */
+ public abstract boolean isValidRegion(String region) throws InvalidRegionException;
+
+ /**
+ * Validate a given zone name against a particular region in an IaaS.
+ * If a particular IaaS doesn't have a concept called zone, it can simply throw {@link InvalidZoneException}.
+ * @param region region of the IaaS that the zone belongs to.
+ * @param zone
+ * @return whether the zone is valid in the given region or not.
+ * @throws InvalidZoneException if the zone is invalid in a given region.
+ */
+ public abstract boolean isValidZone(String region, String zone) throws InvalidZoneException, InvalidRegionException;
+
+ /**
+ * Validate a given host id against a particular zone in an IaaS.
+ * If a particular IaaS doesn't have a concept called hosts, it can simply throw {@link InvalidHostException}.
+ * @param zone zone of the IaaS that the host belongs to.
+ * @param host
+ * @return whether the host is valid in the given zone or not.
+ * @throws InvalidHostException if the host is invalid in a given zone.
+ */
+ public abstract boolean isValidHost(String zone, String host) throws InvalidHostException;
+
+ /**
+ * provides the {@link PartitionValidator} corresponds to this particular IaaS.
+ * @return {@link PartitionValidator}
+ */
+ public abstract PartitionValidator getPartitionValidator();
+
+ /**
+ * Builds only the jclouds {@link Template}
+ */
+ public abstract void buildTemplate();
+
+ /**
+ * Create a new volume in the respective Iaas.
+ * @param sizeGB size of the volume in Giga Bytes.
+ * @return Id of the created volume.
+ */
+ public abstract String createVolume(int sizeGB, String snapshotId);
+
+ /**
+ * Attach a given volume to an instance at the specified device path.
+ * @param instanceId of the instance.
+ * @param volumeId volume id of the volume to be attached.
+ * @param deviceName name of the device that the volume would bind to.
+ * @return the status of the attachment.
+ */
+ public abstract String attachVolume(String instanceId, String volumeId, String deviceName);
+
+ /**
+ * Detach a given volume from the given instance.
+ * @param instanceId of the instance.
+ * @param volumeId volume id of the volume to be detached.
+ */
+ public abstract void detachVolume(String instanceId, String volumeId);
+
+ /**
+ * Delete a given volume.
+ * @param volumeId volume id of the volume to be detached.
+ */
+ public abstract void deleteVolume(String volumeId);
+
+ /**
+ * This returns the device of the volume specified by the user. This is depends on IAAS.
+ * For an instance /dev/sdf maps to /dev/xvdf in EC2.
+ */
+ public abstract String getIaasDevice(String device);
+}
http://git-wip-us.apache.org/repos/asf/stratos/blob/0984bcef/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/OpenstackNovaIaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/OpenstackNovaIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/OpenstackNovaIaas.java
new file mode 100644
index 0000000..46edca8
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/OpenstackNovaIaas.java
@@ -0,0 +1,740 @@
+/*
+ * 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.Optional;
+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.lang.StringUtils;
+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.util.ComputeServiceBuilderUtil;
+import org.apache.stratos.cloud.controller.domain.IaasProvider;
+import org.apache.stratos.cloud.controller.domain.NetworkInterface;
+import org.apache.stratos.cloud.controller.util.CloudControllerConstants;
+import org.apache.stratos.cloud.controller.util.CloudControllerUtil;
+import org.apache.stratos.cloud.controller.iaases.validators.OpenstackNovaPartitionValidator;
+import org.apache.stratos.cloud.controller.iaases.validators.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.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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+
+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();
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ KeyPairApi api = novaApi.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);
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ FloatingIPApi floatingIp = novaApi.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()) {
+ String defaultFloatingIpPool = iaasInfo.getProperty(CloudControllerConstants.DEFAULT_FLOATING_IP_POOL);
+ FloatingIP allocatedFloatingIP;
+ if ((defaultFloatingIpPool == null) || "".equals(defaultFloatingIpPool)) {
+ allocatedFloatingIP = floatingIp.create();
+ } else {
+ allocatedFloatingIP = floatingIp.allocateFromPool(defaultFloatingIpPool);
+ }
+ 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();
+
+ String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
+
+ FloatingIPApi floatingIp = context.unwrapApi(NovaApi.class).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(String.format("OpenstackNovaIaas:associatePredefinedAddress:floating ip in use:%s /ip:%s", fip, 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);
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ FloatingIPApi floatingIPApi = novaApi.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();
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ Set<String> zones = novaApi.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();
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ Optional<? extends AvailabilityZoneApi> availabilityZoneApi = novaApi.getAvailabilityZoneApi(region);
+ for (AvailabilityZone z : availabilityZoneApi.get().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 = String.format("Host or Zone or IaaSProvider is null: host: %s - zone: %s - IaaSProvider: %s", host, zone, iaasInfo);
+ log.error(msg);
+ throw new InvalidHostException(msg);
+ }
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ HostAggregateApi hostApi = novaApi.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 = String.format("Invalid host: %s in the zone: %s and of the iaas: %s", host, zone, iaasInfo.getType());
+ log.error(msg);
+ throw new InvalidHostException(msg);
+ }
+
+ @Override
+ public PartitionValidator getPartitionValidator() {
+ return new OpenstackNovaPartitionValidator();
+ }
+
+ @Override
+ public String createVolume(int sizeGB, String snapshotId) {
+ IaasProvider iaasInfo = getIaasProvider();
+
+ if (iaasInfo == null) {
+ log.fatal(String.format("Cannot create a new volume with snapshot ID : %s", snapshotId));
+ return null;
+ }
+
+ String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
+ String zone = ComputeServiceBuilderUtil.extractZone(iaasInfo);
+
+ if (region == null) {
+ log.fatal(String.format("Cannot create a new volume. Extracted region is null for Iaas : %s", iaasInfo));
+ return null;
+ }
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ VolumeApi volumeApi = novaApi.getVolumeExtensionForZone(region).get();
+ Volume volume;
+ if(StringUtils.isEmpty(snapshotId)){
+ if(log.isDebugEnabled()){
+ log.info("Creating a volume in the zone " + zone);
+ }
+ volume = volumeApi.create(sizeGB, CreateVolumeOptions.Builder.availabilityZone(zone));
+ }else{
+ if(log.isDebugEnabled()){
+ log.info("Creating a volume in the zone " + zone + " from the shanpshot " + snapshotId);
+ }
+ volume = volumeApi.create(sizeGB, CreateVolumeOptions.Builder.availabilityZone(zone).snapshotId(snapshotId));
+ }
+
+ if (volume == null) {
+ log.fatal(String.format("Volume creation was unsuccessful. [region] : %s [zone] : %s of Iaas : %s", region, zone, iaasInfo));
+ return null;
+ }
+
+ String volumeId = volume.getId();
+ /*
+ Volume.Status volumeStatus = this.getVolumeStatus(volumeApi, volumeId);
+
+ if(!(volumeStatus == Volume.Status.AVAILABLE || volumeStatus == Volume.Status.CREATING)){
+ log.error(String.format("Error while creating [volume id] %s. Volume status is %s", volumeId, volumeStatus));
+ return volumeId;
+ }
+ try {
+ if(!waitForStatus(volumeApi, volumeId, Volume.Status.AVAILABLE)){
+ log.error("Volume did not become AVAILABLE. Current status is " + volume.getStatus());
+ }
+ } catch (TimeoutException e) {
+ log.error("[Volume ID] " + volumeId + "did not become AVAILABLE within expected timeout");
+ return volumeId;
+ }
+ */
+ log.info(String.format("Successfully created a new volume [id]: %s in [region] : %s [zone] : %s of Iaas : %s [Volume ID]%s", volume.getId(), region, zone, iaasInfo, volume.getId()));
+ return volumeId;
+ }
+
+ private boolean waitForStatus(String volumeId, Volume.Status expectedStatus, int timeoutInMins) throws TimeoutException {
+ int timeout = 1000 * 60 * timeoutInMins;
+ long timout = System.currentTimeMillis() + timeout;
+
+ IaasProvider iaasInfo = getIaasProvider();
+ String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
+ ComputeServiceContext context = iaasInfo.getComputeService().getContext();;
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ VolumeApi volumeApi = novaApi.getVolumeExtensionForZone(region).get();
+ Volume.Status volumeStatus = this.getVolumeStatus(volumeApi, volumeId);
+
+ while(volumeStatus != expectedStatus){
+ try {
+ if(log.isDebugEnabled()){
+ log.debug(String.format("Volume %s is still NOT in %s. Current State=%s", volumeId, expectedStatus, volumeStatus));
+ }
+ if(volumeStatus == Volume.Status.ERROR){
+ log.error("Volume " + volumeId + " is in state ERROR");
+ return false;
+ }
+ Thread.sleep(1000);
+ volumeStatus = this.getVolumeStatus(volumeApi, volumeId);
+ if (System.currentTimeMillis()> timout) {
+ throw new TimeoutException();
+ }
+ } catch (InterruptedException e) {
+ // Ignoring the exception
+ }
+ }
+ if(log.isDebugEnabled()){
+ log.debug(String.format("Volume %s status became %s", volumeId, expectedStatus));
+ }
+
+ return true;
+ }
+
+ @Override
+ public String attachVolume(String instanceId, String volumeId, String deviceName) {
+ IaasProvider iaasInfo = getIaasProvider();
+
+ if (StringUtils.isEmpty(volumeId)) {
+ log.error("Volume provided to attach can not be null");
+ }
+
+ if (StringUtils.isEmpty(instanceId)) {
+ log.error("Instance provided to attach can not be null");
+ }
+
+ ComputeServiceContext context = iaasInfo.getComputeService()
+ .getContext();
+ String region = ComputeServiceBuilderUtil.extractRegion(iaasInfo);
+ String device = deviceName == null ? "/dev/vdc" : deviceName;
+
+ if (region == null) {
+ log.fatal(String.format("Cannot attach the volume [id]: %s. Extracted region is null for Iaas : %s", volumeId, iaasInfo));
+ return null;
+ }
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ VolumeApi volumeApi = novaApi.getVolumeExtensionForZone(region).get();
+ VolumeAttachmentApi volumeAttachmentApi = novaApi.getVolumeAttachmentExtensionForZone(region).get();
+
+ Volume.Status volumeStatus = this.getVolumeStatus(volumeApi, volumeId);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Volume " + volumeId + " is in state " + volumeStatus);
+ }
+
+ if (!(volumeStatus == Volume.Status.AVAILABLE || volumeStatus == Volume.Status.CREATING)) {
+ log.error(String.format("Volume %s can not be attached. Volume status is %s", volumeId, volumeStatus));
+ return null;
+ }
+
+ boolean volumeBecameAvailable = false, volumeBecameAttached = false;
+ try {
+ volumeBecameAvailable = waitForStatus(volumeId, Volume.Status.AVAILABLE, 5);
+ } catch (TimeoutException e) {
+ log.error("[Volume ID] " + volumeId + "did not become AVAILABLE within expected timeout");
+ }
+
+ VolumeAttachment attachment = null;
+ if (volumeBecameAvailable) {
+ attachment = volumeAttachmentApi.attachVolumeToServerAsDevice(volumeId, instanceId, device);
+
+ try {
+ volumeBecameAttached = waitForStatus(volumeId, Volume.Status.IN_USE, 2);
+ } catch (TimeoutException e) {
+ log.error("[Volume ID] " + volumeId + "did not become IN_USE within expected timeout");
+ }
+ }
+ try {
+ // waiting 5seconds till volumes are actually attached.
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if (attachment == null) {
+ log.fatal(String.format("Volume [id]: %s attachment for instance [id]: %s was unsuccessful. [region] : %s of Iaas : %s", volumeId, instanceId, region, iaasInfo));
+ return null;
+ }
+
+ if(! volumeBecameAttached){
+ log.error(String.format("[Volume ID] %s attachment is called, but not yet became attached", volumeId));
+ }
+
+ log.info(String.format("Volume [id]: %s attachment for instance [id]: %s was successful [status]: Attaching. [region] : %s of Iaas : %s", volumeId, instanceId, region, 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(String.format("Cannot detach the volume [id]: %s from the instance [id]: %s. Extracted region is null for Iaas : %s", volumeId, instanceId, iaasInfo));
+ return;
+ }
+ if(log.isDebugEnabled()) {
+ log.debug(String.format("Starting to detach volume %s from the instance %s", volumeId, instanceId));
+ }
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ VolumeAttachmentApi api = novaApi.getVolumeAttachmentExtensionForZone(region).get();
+ if (api.detachVolumeFromServer(volumeId, instanceId)) {
+ log.info(String.format("Detachment of Volume [id]: %s from instance [id]: %s was successful. [region] : %s of Iaas : %s", volumeId, instanceId, region, iaasInfo));
+ }else{
+ log.error(String.format("Detachment of Volume [id]: %s from instance [id]: %s was unsuccessful. [volume Status] : %s", volumeId, instanceId, region, 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(String.format("Cannot delete the volume [id]: %s. Extracted region is null for Iaas : %s", volumeId, iaasInfo));
+ return;
+ }
+
+ NovaApi novaApi = context.unwrapApi(NovaApi.class);
+ VolumeApi api = novaApi.getVolumeExtensionForZone(region).get();
+ if (api.delete(volumeId)) {
+ log.info(String.format("Deletion of Volume [id]: %s was successful. [region] : %s of Iaas : %s", volumeId, region, iaasInfo));
+ }
+ }
+
+ @Override
+ public String getIaasDevice(String device) {
+ return device;
+ }
+
+ private Volume.Status getVolumeStatus(VolumeApi volumeApi, String volumeId){
+ return volumeApi.get(volumeId).getStatus();
+ }
+}