/*
* Copyright (C) 2011 Citrix Systems, Inc. All rights reserved.
*
* Licensed 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 com.cloud.bridge.service.core.ec2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.hibernate.ejb.criteria.expression.UnaryArithmeticOperation.Operation;
import org.xml.sax.SAXException;
import com.cloud.bridge.persist.dao.CloudStackSvcOfferingDao;
import com.cloud.bridge.persist.dao.CloudStackAccountDao;
import com.cloud.bridge.persist.dao.OfferingDao;
import com.cloud.bridge.service.UserContext;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute;
import com.cloud.bridge.service.exception.EC2ServiceException;
import com.cloud.bridge.service.exception.EC2ServiceException.ClientError;
import com.cloud.bridge.service.exception.EC2ServiceException.ServerError;
import com.cloud.bridge.util.ConfigurationHelper;
import com.cloud.stack.CloudStackApi;
import com.cloud.stack.models.CloudStackAccount;
import com.cloud.stack.models.CloudStackDiskOffering;
import com.cloud.stack.models.CloudStackInfoResponse;
import com.cloud.stack.models.CloudStackIngressRule;
import com.cloud.stack.models.CloudStackIpAddress;
import com.cloud.stack.models.CloudStackKeyPair;
import com.cloud.stack.models.CloudStackKeyValue;
import com.cloud.stack.models.CloudStackNetwork;
import com.cloud.stack.models.CloudStackNetworkOffering;
import com.cloud.stack.models.CloudStackNic;
import com.cloud.stack.models.CloudStackOsType;
import com.cloud.stack.models.CloudStackPasswordData;
import com.cloud.stack.models.CloudStackResourceLimit;
import com.cloud.stack.models.CloudStackSecurityGroup;
import com.cloud.stack.models.CloudStackSecurityGroupIngress;
import com.cloud.stack.models.CloudStackServiceOffering;
import com.cloud.stack.models.CloudStackSnapshot;
import com.cloud.stack.models.CloudStackTemplate;
import com.cloud.stack.models.CloudStackTemplatePermission;
import com.cloud.stack.models.CloudStackUser;
import com.cloud.stack.models.CloudStackUserVm;
import com.cloud.stack.models.CloudStackVolume;
import com.cloud.stack.models.CloudStackZone;
/**
* EC2Engine processes the ec2 commands and calls their cloudstack analogs
*
*/
public class EC2Engine {
protected final static Logger logger = Logger.getLogger(EC2Engine.class);
String managementServer = null;
String cloudAPIPort = null;
private CloudStackApi _eng = null;
private CloudStackAccount currentAccount = null;
public EC2Engine() throws IOException {
loadConfigValues();
}
/**
* Which management server to we talk to?
* Load a mapping form Amazon values for 'instanceType' to cloud defined
* diskOfferingId and serviceOfferingId.
*
* @throws IOException
*/
private void loadConfigValues() throws IOException {
File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties");
if (null != propertiesFile) {
logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath());
Properties EC2Prop = new Properties();
try {
EC2Prop.load( new FileInputStream( propertiesFile ));
} catch (FileNotFoundException e) {
logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e);
} catch (IOException e) {
logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e);
}
managementServer = EC2Prop.getProperty( "managementServer" );
cloudAPIPort = EC2Prop.getProperty( "cloudAPIPort", null );
OfferingDao ofDao = new OfferingDao();
try {
if(ofDao.getOfferingCount() == 0) {
String strValue = EC2Prop.getProperty("m1.small.serviceId");
if(strValue != null) ofDao.setOfferMapping("m1.small", strValue);
strValue = EC2Prop.getProperty("m1.large.serviceId");
if(strValue != null) ofDao.setOfferMapping("m1.large", strValue);
strValue = EC2Prop.getProperty("m1.xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("m1.xlarge", strValue);
strValue = EC2Prop.getProperty("c1.medium.serviceId");
if(strValue != null) ofDao.setOfferMapping("c1.medium", strValue);
strValue = EC2Prop.getProperty("c1.xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("c1.xlarge", strValue);
strValue = EC2Prop.getProperty("m2.xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("m2.xlarge", strValue);
strValue = EC2Prop.getProperty("m2.2xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("m2.2xlarge", strValue);
strValue = EC2Prop.getProperty("m2.4xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("m2.4xlarge", strValue);
strValue = EC2Prop.getProperty("cc1.4xlarge.serviceId");
if(strValue != null) ofDao.setOfferMapping("cc1.4xlarge", strValue);
}
} catch(Exception e) {
logger.error("Unexpected exception ", e);
}
} else logger.error( "ec2-service.properties not found" );
}
/**
* Helper function to manage the api connection
*
* @return
*/
private CloudStackApi getApi() {
if (_eng == null) {
_eng = new CloudStackApi(managementServer, cloudAPIPort, false);
}
// regardless of whether _eng is initialized, we must make sure
// access/secret keys are current with what's in the UserCredentials
_eng.setApiKey(UserContext.current().getAccessKey());
_eng.setSecretKey(UserContext.current().getSecretKey());
return _eng;
}
/**
* Verifies account can access CloudStack
*
* @param accessKey
* @param secretKey
* @return
* @throws EC2ServiceException
*/
public boolean validateAccount( String accessKey, String secretKey ) throws EC2ServiceException {
String oldApiKey = null;
String oldSecretKey = null;
if (accessKey == null || secretKey == null) {
return false;
}
// okay, instead of using the getApi() nonsense for validate, we are going to manage _eng
if (_eng == null) {
_eng = new CloudStackApi(managementServer, cloudAPIPort, false);
}
try {
oldApiKey = _eng.getApiKey();
oldSecretKey = _eng.getSecretKey();
} catch(Exception e) {
// we really don't care, and expect this
}
try {
_eng.setApiKey(accessKey);
_eng.setSecretKey(secretKey);
List<CloudStackAccount> accts = _eng.listAccounts(null, null, null, null, null, null, null, null);
if (oldApiKey != null && oldSecretKey != null) {
_eng.setApiKey(oldApiKey);
_eng.setSecretKey(oldSecretKey);
}
if (accts == null) {
return false;
}
return true;
} catch(Exception e) {
logger.error("Validate account failed!");
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Creates a security group
*
* @param groupName
* @param groupDesc
* @return
*/
public Boolean createSecurityGroup(String groupName, String groupDesc) {
try {
CloudStackSecurityGroup grp = getApi().createSecurityGroup(groupName, null, groupDesc, null);
if (grp != null && grp.getId() != null) {
return true;
}
return false;
} catch( Exception e ) {
logger.error( "EC2 CreateSecurityGroup - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Deletes a security group
*
* @param groupName
* @return
*/
public boolean deleteSecurityGroup(String groupName) {
try {
CloudStackInfoResponse resp = getApi().deleteSecurityGroup(null, null, null, groupName);
if (resp != null) {
return resp.getSuccess();
}
return false;
} catch( Exception e ) {
logger.error( "EC2 DeleteSecurityGroup - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* returns a list of security groups
*
* @param request
* @return
*/
public EC2DescribeSecurityGroupsResponse describeSecurityGroups(EC2DescribeSecurityGroups request)
{
try {
EC2DescribeSecurityGroupsResponse response = listSecurityGroups( request.getGroupSet());
EC2GroupFilterSet gfs = request.getFilterSet();
if ( null == gfs )
return response;
else return gfs.evaluate( response );
} catch( Exception e ) {
logger.error( "EC2 DescribeSecurityGroups - ", e);
throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred.");
}
}
/**
* CloudStack supports revoke only by using the ruleid of the ingress rule.
* We list all security groups and find the matching group and use the first ruleId we find.
*
* @param request
* @return
*/
public boolean revokeSecurityGroup( EC2AuthorizeRevokeSecurityGroup request )
{
if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter");
try {
String[] groupSet = new String[1];
groupSet[0] = request.getName();
String ruleId = null;
EC2IpPermission[] items = request.getIpPermissionSet();
EC2DescribeSecurityGroupsResponse response = listSecurityGroups( groupSet );
EC2SecurityGroup[] groups = response.getGroupSet();
for (EC2SecurityGroup group : groups) {
EC2IpPermission[] perms = group.getIpPermissionSet();
for (EC2IpPermission perm : perms) {
ruleId = doesRuleMatch( items[0], perm );
if (ruleId != null) break;
}
}
if (null == ruleId)
throw new EC2ServiceException(ClientError.InvalidGroup_NotFound, "Cannot find matching ruleid.");
CloudStackInfoResponse resp = getApi().revokeSecurityGroupIngress(ruleId);
if (resp != null && resp.getId() != null) {
return resp.getSuccess();
}
return false;
} catch( Exception e ) {
logger.error( "EC2 revokeSecurityGroupIngress" + " - " + e.getMessage());
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* authorizeSecurityGroup
*
* @param request - ip permission parameters
*/
public boolean authorizeSecurityGroup(EC2AuthorizeRevokeSecurityGroup request )
{
if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter");
EC2IpPermission[] items = request.getIpPermissionSet();
try {
for (EC2IpPermission ipPerm : items) {
EC2SecurityGroup[] groups = ipPerm.getUserSet();
List<CloudStackKeyValue> secGroupList = new ArrayList<CloudStackKeyValue>();
for (EC2SecurityGroup group : groups) {
CloudStackKeyValue pair = new CloudStackKeyValue();
pair.setKeyValue(group.getAccount(), group.getName());
secGroupList.add(pair);
}
CloudStackSecurityGroupIngress resp = null;
if (ipPerm.getProtocol().equalsIgnoreCase("icmp")) {
resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, null,
ipPerm.getIcmpCode(), ipPerm.getIcmpType(), ipPerm.getProtocol(), null,
request.getName(), null, secGroupList);
} else {
resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null,
ipPerm.getToPort().longValue(), null, null, ipPerm.getProtocol(), null, request.getName(),
ipPerm.getFromPort().longValue(), secGroupList);
}
if (resp != null && resp.getRuleId() != null) {
return true;
}
return false;
}
} catch(Exception e) {
logger.error( "EC2 AuthorizeSecurityGroupIngress - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return true;
}
/**
* Does the permission from the request (left) match the permission from the cloudStack query (right).
* If the cloudStack rule matches then we return its ruleId.
*
* @param permLeft
* @param permRight
* @return ruleId of the cloudstack rule
*/
private String doesRuleMatch(EC2IpPermission permLeft, EC2IpPermission permRight)
{
int matches = 0;
if (null != permLeft.getIcmpType() && null != permLeft.getIcmpCode()) {
if (null == permRight.getIcmpType() || null == permRight.getIcmpCode()) return null;
if (!permLeft.getIcmpType().equalsIgnoreCase( permRight.getIcmpType())) return null;
if (!permLeft.getIcmpCode().equalsIgnoreCase( permRight.getIcmpCode())) return null;
matches++;
}
// -> "Valid Values for EC2 security groups: tcp | udp | icmp or the corresponding protocol number (6 | 17 | 1)."
if (null != permLeft.getProtocol()) {
if (null == permRight.getProtocol()) return null;
String protocol = permLeft.getProtocol();
if (protocol.equals( "6" )) protocol = "tcp";
else if (protocol.equals( "17" )) protocol = "udp";
else if (protocol.equals( "1" )) protocol = "icmp";
if (!protocol.equalsIgnoreCase( permRight.getProtocol())) return null;
matches++;
}
if (null != permLeft.getCIDR()) {
if (null == permRight.getCIDR()) return null;
if (!permLeft.getCIDR().equalsIgnoreCase( permRight.getCIDR())) return null;
matches++;
}
// -> is the port(s) from the request (left) a match of the rule's port(s)
if (0 != permLeft.getFromPort()) {
// -> -1 means all ports match
if (-1 != permLeft.getFromPort()) {
if (permLeft.getFromPort().compareTo(permRight.getFromPort()) != 0 ||
permLeft.getToPort().compareTo(permRight.getToPort()) != 0)
return null;
}
matches++;
}
// -> was permLeft set up properly with at least one property to match?
if ( 0 == matches )
return null;
else return permRight.getRuleId();
}
/**
* Returns a list of all snapshots
*
* @param request
* @return
*/
public EC2DescribeSnapshotsResponse handleRequest( EC2DescribeSnapshots request )
{
EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse();
EC2SnapshotFilterSet sfs = request.getFilterSet();
try {
// -> query to get the volume size for each snapshot
EC2DescribeSnapshotsResponse response = listSnapshots( request.getSnapshotSet());
if (response == null) {
return new EC2DescribeSnapshotsResponse();
}
EC2Snapshot[] snapshots = response.getSnapshotSet();
for (EC2Snapshot snap : snapshots) {
volumes = listVolumes(snap.getVolumeId(), null, volumes);
EC2Volume[] volSet = volumes.getVolumeSet();
if (0 < volSet.length) snap.setVolumeSize(volSet[0].getSize());
volumes.reset();
}
if ( null == sfs )
return response;
else return sfs.evaluate( response );
} catch( EC2ServiceException error ) {
logger.error( "EC2 DescribeSnapshots - ", error);
throw error;
} catch( Exception e ) {
logger.error( "EC2 DescribeSnapshots - ", e);
throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred.");
}
}
/**
* Creates a snapshot
*
* @param volumeId
* @return
*/
public EC2Snapshot createSnapshot( String volumeId ) {
try {
CloudStackSnapshot snap = getApi().createSnapshot(volumeId, null, null, null);
if (snap == null) {
throw new EC2ServiceException(ServerError.InternalError, "Unable to create snapshot!");
}
EC2Snapshot ec2Snapshot = new EC2Snapshot();
ec2Snapshot.setId(snap.getId());
ec2Snapshot.setName(snap.getName());
ec2Snapshot.setType(snap.getSnapshotType());
ec2Snapshot.setAccountName(snap.getAccountName());
ec2Snapshot.setDomainId(snap.getDomainId());
ec2Snapshot.setCreated(snap.getCreated());
ec2Snapshot.setVolumeId(snap.getVolumeId());
List<CloudStackVolume> vols = getApi().listVolumes(null, null, null, snap.getVolumeId(), null, null, null, null, null, null, null);
if(vols.size() > 0) {
assert(vols.get(0).getSize() != null);
Long sizeInGB = vols.get(0).getSize().longValue()/1073741824;
ec2Snapshot.setVolumeSize(sizeInGB);
}
return ec2Snapshot;
} catch( Exception e ) {
logger.error( "EC2 CreateSnapshot - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Deletes a snapshot
*
* @param snapshotId
* @return
*/
public boolean deleteSnapshot(String snapshotId) {
try {
CloudStackInfoResponse resp = getApi().deleteSnapshot(snapshotId);
if(resp != null) {
return resp.getSuccess();
}
return false;
} catch(Exception e) {
logger.error( "EC2 DeleteSnapshot - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/** REST API calls this method.
* Modify an existing template
*
* @param request
* @return
*/
public boolean modifyImageAttribute( EC2Image request )
{
// TODO: This is incomplete
EC2DescribeImagesResponse images = new EC2DescribeImagesResponse();
try {
images = listTemplates( request.getId(), images );
EC2Image[] imageSet = images.getImageSet();
CloudStackTemplate resp = getApi().updateTemplate(request.getId(), null, request.getDescription(), null, imageSet[0].getName(), null, null);
if (resp != null) {
return true;
}
return false;
} catch( Exception e ) {
logger.error( "EC2 ModifyImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Modify an existing template
*
* @param request
* @return
*/
public boolean modifyImageAttribute( EC2ModifyImageAttribute request )
{
try {
if(request.getAttribute().equals(ImageAttribute.launchPermission)){
String accounts = "";
Boolean isPublic = null;
EC2ModifyImageAttribute.Operation operation = request.getLaunchPermOperation();
List<String> accountOrGroupList = request.getLaunchPermissionAccountsList();
if(accountOrGroupList != null && !accountOrGroupList.isEmpty()){
boolean first = true;
for(String accountOrGroup : accountOrGroupList){
if("all".equalsIgnoreCase(accountOrGroup)){
if(operation.equals(EC2ModifyImageAttribute.Operation.add)){
isPublic = true;
}else{
isPublic = false;
}
}else{
if(!first){
accounts = accounts + ",";
}
accounts = accounts + accountOrGroup;
first = false;
}
}
}
CloudStackInfoResponse resp = getApi().updateTemplatePermissions(request.getImageId(), accounts, null, null, isPublic, operation.toString());
return resp.getSuccess();
}else if(request.getAttribute().equals(ImageAttribute.description)){
CloudStackTemplate resp = getApi().updateTemplate(request.getImageId(), null, request.getDescription(), null, null, null, null);
if (resp != null) {
return true;
}
return false;
}
} catch (Exception e) {
logger.error( "EC2 modifyImageAttribute - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return false;
}
public EC2ImageAttributes describeImageAttribute(EC2DescribeImageAttribute request) {
EC2ImageAttributes imageAtts = new EC2ImageAttributes();
try {
imageAtts.setImageId(request.getImageId());
if(request.getAttribute().equals(ImageAttribute.launchPermission)){
CloudStackTemplatePermission tempPerm = getApi().listTemplatePermissions(request.getImageId(), null, null);
if(tempPerm != null){
imageAtts.setDomainId(tempPerm.getDomainId());
List<String> accntList = tempPerm.getAccounts();
imageAtts.setAccountNamesWithLaunchPermission(accntList);
imageAtts.setIsPublic(tempPerm.getIsPublic());
}
}else if(request.getAttribute().equals(ImageAttribute.description)){
EC2DescribeImagesResponse descriptionResp = new EC2DescribeImagesResponse();
listTemplates(request.getImageId(), descriptionResp);
if(descriptionResp.getImageSet() != null){
EC2Image[] images = descriptionResp.getImageSet();
imageAtts.setDescription(images[0].getDescription());
}
}
} catch (Exception e) {
logger.error( "EC2 describeImageAttribute - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return imageAtts;
}
/**
* If given a specific list of snapshots of interest, then only values from those snapshots are returned.
*
* @param interestedShots - can be null, should be a subset of all snapshots
*/
private EC2DescribeSnapshotsResponse listSnapshots( String[] interestedShots ) throws Exception {
EC2DescribeSnapshotsResponse snapshots = new EC2DescribeSnapshotsResponse();
List<CloudStackSnapshot> cloudSnaps;
if (interestedShots == null || interestedShots.length == 0) {
cloudSnaps = getApi().listSnapshots(null, null, null, null, null, null, null, null, null);
} else {
cloudSnaps = new ArrayList<CloudStackSnapshot>();
for(String id : interestedShots) {
List<CloudStackSnapshot> tmpList = getApi().listSnapshots(null, null, id, null, null, null, null, null, null);
cloudSnaps.addAll(tmpList);
}
}
if (cloudSnaps == null) {
return null;
}
for(CloudStackSnapshot cloudSnapshot : cloudSnaps) {
EC2Snapshot shot = new EC2Snapshot();
shot.setId(cloudSnapshot.getId());
shot.setName(cloudSnapshot.getName());
shot.setVolumeId(cloudSnapshot.getVolumeId());
shot.setType(cloudSnapshot.getSnapshotType());
shot.setState(cloudSnapshot.getState());
shot.setCreated(cloudSnapshot.getCreated());
shot.setAccountName(cloudSnapshot.getAccountName());
shot.setDomainId(cloudSnapshot.getDomainId());
snapshots.addSnapshot(shot);
}
return snapshots;
}
// handlers
/**
* return password data from the instance
*
* @param instanceId
* @return
*/
public EC2PasswordData getPasswordData(String instanceId) {
try {
CloudStackPasswordData resp = getApi().getVMPassword(instanceId);
EC2PasswordData passwdData = new EC2PasswordData();
if (resp != null) {
passwdData.setInstanceId(instanceId);
passwdData.setEncryptedPassword(resp.getEncryptedpassword());
}
return passwdData;
} catch(Exception e) {
logger.error("EC2 GetPasswordData - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Lists SSH KeyPairs on the systme
*
* @param request
* @return
*/
public EC2DescribeKeyPairsResponse describeKeyPairs( EC2DescribeKeyPairs request ) {
try {
EC2KeyPairFilterSet filterSet = request.getKeyFilterSet();
String[] keyNames = request.getKeyNames();
List<CloudStackKeyPair> keyPairs = getApi().listSSHKeyPairs(null, null, null);
List<EC2SSHKeyPair> keyPairsList = new ArrayList<EC2SSHKeyPair>();
if (keyPairs != null) {
// Let's trim the list of keypairs to only the ones listed in keyNames
List<CloudStackKeyPair> matchedKeyPairs = new ArrayList<CloudStackKeyPair>();
if (keyNames != null && keyNames.length > 0) {
for (CloudStackKeyPair keyPair : keyPairs) {
boolean matched = false;
for (String keyName : keyNames) {
if (keyPair.getName().equalsIgnoreCase(keyName)) {
matched = true;
break;
}
}
if (matched) {
matchedKeyPairs.add(keyPair);
}
}
if (matchedKeyPairs.isEmpty()) {
throw new EC2ServiceException(ServerError.InternalError, "No matching keypairs found");
}
}else{
matchedKeyPairs = keyPairs;
}
// this should be reworked... converting from CloudStackKeyPairResponse to EC2SSHKeyPair is dumb
for (CloudStackKeyPair respKeyPair: matchedKeyPairs) {
EC2SSHKeyPair ec2KeyPair = new EC2SSHKeyPair();
ec2KeyPair.setFingerprint(respKeyPair.getFingerprint());
ec2KeyPair.setKeyName(respKeyPair.getName());
ec2KeyPair.setPrivateKey(respKeyPair.getPrivatekey());
keyPairsList.add(ec2KeyPair);
}
}
return filterSet.evaluate(keyPairsList);
} catch(Exception e) {
logger.error("EC2 DescribeKeyPairs - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Delete SSHKeyPair
*
* @param request
* @return
*/
public boolean deleteKeyPair( EC2DeleteKeyPair request ) {
try {
CloudStackInfoResponse resp = getApi().deleteSSHKeyPair(request.getKeyName(), null, null);
if (resp == null) {
throw new Exception("Ivalid CloudStack API response");
}
return resp.getSuccess();
} catch(Exception e) {
logger.error("EC2 DeleteKeyPair - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Create SSHKeyPair
*
* @param request
* @return
*/
public EC2SSHKeyPair createKeyPair(EC2CreateKeyPair request) {
try {
CloudStackKeyPair resp = getApi().createSSHKeyPair(request.getKeyName(), null, null);
if (resp == null) {
throw new Exception("Ivalid CloudStack API response");
}
EC2SSHKeyPair response = new EC2SSHKeyPair();
response.setFingerprint(resp.getFingerprint());
response.setKeyName(resp.getName());
response.setPrivateKey(resp.getPrivatekey());
return response;
} catch (Exception e) {
logger.error("EC2 CreateKeyPair - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Import an existing SSH KeyPair
*
* @param request
* @return
*/
public EC2SSHKeyPair importKeyPair( EC2ImportKeyPair request ) {
try {
CloudStackKeyPair resp = getApi().registerSSHKeyPair(request.getKeyName(), request.getPublicKeyMaterial());
if (resp == null) {
throw new Exception("Ivalid CloudStack API response");
}
EC2SSHKeyPair response = new EC2SSHKeyPair();
response.setFingerprint(resp.getFingerprint());
response.setKeyName(resp.getName());
response.setPrivateKey(resp.getPrivatekey());
return response;
} catch (Exception e) {
logger.error("EC2 ImportKeyPair - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* list ip addresses that have been allocated
*
* @param request
* @return
*/
public EC2DescribeAddressesResponse describeAddresses( EC2DescribeAddresses request ) {
try {
List<CloudStackIpAddress> addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null);
EC2AddressFilterSet filterSet = request.getFilterSet();
List<EC2Address> addressList = new ArrayList<EC2Address>();
if (addrList != null && addrList.size() > 0) {
for (CloudStackIpAddress addr: addrList) {
// remember, if no filters are set, request.inPublicIpSet always returns true
if (request.inPublicIpSet(addr.getIpAddress())) {
EC2Address ec2Address = new EC2Address();
ec2Address.setIpAddress(addr.getIpAddress());
if (addr.getVirtualMachineId() != null)
ec2Address.setAssociatedInstanceId(addr.getVirtualMachineId().toString());
addressList.add(ec2Address);
}
}
}
return filterSet.evaluate(addressList);
} catch(Exception e) {
logger.error("EC2 DescribeAddresses - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* release an IP Address
*
* @param request
* @return
*/
public boolean releaseAddress(EC2ReleaseAddress request) {
try {
CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0);
CloudStackInfoResponse resp = getApi().disassociateIpAddress(cloudIp.getId());
if (resp != null) {
return resp.getSuccess();
}
} catch(Exception e) {
logger.error("EC2 ReleaseAddress - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return false;
}
/**
* Associate an address with an instance
*
* @param request
* @return
*/
public boolean associateAddress( EC2AssociateAddress request ) {
try {
CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0);
CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null).get(0);
CloudStackInfoResponse resp = getApi().enableStaticNat(cloudIp.getId(), cloudVm.getId());
if (resp != null) {
return resp.getSuccess();
}
} catch(Exception e) {
logger.error( "EC2 AssociateAddress - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
return false;
}
/**
* Disassociate an address from an instance
*
* @param request
* @return
*/
public boolean disassociateAddress( EC2DisassociateAddress request ) {
try {
CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0);
CloudStackInfoResponse resp = getApi().disableStaticNat(cloudIp.getId());
if (resp != null) {
return resp.getSuccess();
}
} catch(Exception e) {
logger.error( "EC2 DisassociateAddress - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
return false;
}
/**
* Allocate an address
*
* @param request
* @return
*/
public EC2Address allocateAddress()
{
try {
EC2Address ec2Address = new EC2Address();
// this gets our networkId
CloudStackAccount caller = getCurrentAccount();
CloudStackZone zone = findZone();
CloudStackNetwork net = findNetwork(zone);
// CloudStackIpAddress resp = getApi().associateIpAddress(null, null, null, "0036952d-48df-4422-9fd0-94b0885e18cb");
CloudStackIpAddress resp = getApi().associateIpAddress(zone.getId(), caller.getName(), caller.getDomainId(), net != null ? net.getId():null);
ec2Address.setAssociatedInstanceId(resp.getId());
if (resp.getIpAddress() == null) {
List<CloudStackIpAddress> addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null);
if (addrList != null && addrList.size() > 0) {
for (CloudStackIpAddress addr: addrList) {
if (addr.getId().equalsIgnoreCase(resp.getId())) {
ec2Address.setIpAddress(addr.getIpAddress());
}
}
}
} else {
ec2Address.setIpAddress(resp.getIpAddress());
}
return ec2Address;
} catch(Exception e) {
logger.error( "EC2 AllocateAddress - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* List of templates available. We only support the imageSet version of this call or when no search parameters are passed
* which results in asking for all templates.
*
* @param request
* @return
*/
public EC2DescribeImagesResponse describeImages(EC2DescribeImages request)
{
EC2DescribeImagesResponse images = new EC2DescribeImagesResponse();
try {
String[] templateIds = request.getImageSet();
if ( 0 == templateIds.length ) {
return listTemplates(null, images);
}
for (String s : templateIds) {
images = listTemplates(s, images);
}
return images;
} catch( Exception e ) {
logger.error( "EC2 DescribeImages - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Create a template
* Amazon API just gives us the instanceId to create the template from.
* But our createTemplate function requires the volumeId and osTypeId.
* So to get that we must make the following sequence of cloud API calls:
* 1) listVolumes&virtualMachineId= -- gets the volumeId
* 2) listVirtualMachinees&id= -- gets the templateId
* 3) listTemplates&id= -- gets the osTypeId
*
* If we have to start and stop the VM in question then this function is
* going to take a long time to complete.
*
* @param request
* @return
*/
public EC2CreateImageResponse createImage(EC2CreateImage request)
{
EC2CreateImageResponse response = null;
boolean needsRestart = false;
String volumeId = null;
try {
// [A] Creating a template from a VM volume should be from the ROOT volume
// Also for this to work the VM must be in a Stopped state so we 'reboot' it if its not
EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse();
volumes = listVolumes( null, request.getInstanceId(), volumes );
EC2Volume[] volSet = volumes.getVolumeSet();
for (EC2Volume vol : volSet) {
if (vol.getType().equalsIgnoreCase( "ROOT" )) {
String vmState = vol.getVMState();
if (vmState.equalsIgnoreCase( "running" ) || vmState.equalsIgnoreCase( "starting" )) {
needsRestart = true;
if (!stopVirtualMachine( request.getInstanceId() ))
throw new EC2ServiceException(ClientError.IncorrectState, "CreateImage - instance must be in a stopped state");
}
volumeId = vol.getId();
break;
}
}
// [B] The parameters must be in sorted order for proper signature generation
EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse();
instances = lookupInstances( request.getInstanceId(), instances );
EC2Instance[] instanceSet = instances.getInstanceSet();
String templateId = instanceSet[0].getTemplateId();
EC2DescribeImagesResponse images = new EC2DescribeImagesResponse();
images = listTemplates( templateId, images );
EC2Image[] imageSet = images.getImageSet();
String osTypeId = imageSet[0].getOsTypeId();
CloudStackTemplate resp = getApi().createTemplate((request.getDescription() == null ? "" : request.getDescription()), request.getName(),
osTypeId, null, null, null, null, null, null, volumeId);
if (resp == null || resp.getId() == null) {
throw new EC2ServiceException(ServerError.InternalError, "An upexpected error occurred.");
}
//if template was created succesfully, create the new image response
response = new EC2CreateImageResponse();
response.setId(resp.getId());
// [C] If we stopped the virtual machine now we need to restart it
if (needsRestart) {
if (!startVirtualMachine( request.getInstanceId() ))
throw new EC2ServiceException(ServerError.InternalError,
"CreateImage - restarting instance " + request.getInstanceId() + " failed");
}
return response;
} catch( Exception e ) {
logger.error( "EC2 CreateImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Register a template
*
* @param request
* @return
*/
public EC2CreateImageResponse registerImage(EC2RegisterImage request)
{
try {
CloudStackAccount caller = getCurrentAccount();
if (null == request.getFormat() || null == request.getName() || null == request.getOsTypeName() ||
null == request.getLocation() || null == request.getZoneName())
throw new EC2ServiceException(ServerError.InternalError, "Missing parameter - location/architecture/name");
List<CloudStackTemplate> templates = getApi().registerTemplate((request.getDescription() == null ? request.getName() : request.getDescription()),
request.getFormat(), request.getHypervisor(), request.getName(), toOSTypeId(request.getOsTypeName()), request.getLocation(),
toZoneId(request.getZoneName(), null), null, null, null, null, null, null, null, null, null);
if (templates != null) {
// technically we will only ever register a single template...
for (CloudStackTemplate template : templates) {
if (template != null && template.getId() != null) {
EC2CreateImageResponse image = new EC2CreateImageResponse();
image.setId(template.getId().toString());
return image;
}
}
}
return null;
} catch( Exception e ) {
logger.error( "EC2 RegisterImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Deregister a template(image)
* Our implementation is different from Amazon in that we do delete the template
* when we deregister it. The cloud API has not deregister call.
*
* @param image
* @return
*/
public boolean deregisterImage( EC2Image image )
{
try {
CloudStackInfoResponse resp = getApi().deleteTemplate(image.getId(), null);
return resp.getSuccess();
} catch( Exception e ) {
logger.error( "EC2 DeregisterImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* list instances
*
* @param request
* @return
*/
public EC2DescribeInstancesResponse describeInstances(EC2DescribeInstances request ) {
try {
return listVirtualMachines( request.getInstancesSet(), request.getFilterSet());
} catch( Exception e ) {
logger.error( "EC2 DescribeInstances - " ,e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* list Zones
*
* @param request
* @return
*/
public EC2DescribeAvailabilityZonesResponse handleRequest(EC2DescribeAvailabilityZones request) {
try {
CloudStackAccount caller = getCurrentAccount();
return listZones(request.getZoneSet(), null);
} catch( EC2ServiceException error ) {
logger.error( "EC2 DescribeAvailabilityZones - ", error);
throw error;
} catch( Exception e ) {
logger.error( "EC2 DescribeAvailabilityZones - " ,e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* list volumes
*
* @param request
* @return
*/
public EC2DescribeVolumesResponse handleRequest( EC2DescribeVolumes request ) {
EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse();
EC2VolumeFilterSet vfs = request.getFilterSet();
try {
String[] volumeIds = request.getVolumeSet();
if ( 0 == volumeIds.length ){
volumes = listVolumes( null, null, volumes );
} else {
for (String s : volumeIds)
volumes = listVolumes(s, null, volumes );
}
if ( null == vfs )
return volumes;
else return vfs.evaluate( volumes );
} catch( Exception e ) {
logger.error( "EC2 DescribeVolumes - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Attach a volume to an instance
*
* @param request
* @return
*/
public EC2Volume attachVolume( EC2Volume request ) {
try {
request.setDeviceId(mapDeviceToCloudDeviceId(request.getDevice()));
EC2Volume resp = new EC2Volume();
CloudStackVolume vol = getApi().attachVolume(request.getId(), request.getInstanceId(), request.getDeviceId());
if(vol != null) {
resp.setAttached(vol.getAttached());
resp.setCreated(vol.getCreated());
resp.setDevice(request.getDevice());
resp.setDeviceId(vol.getDeviceId());
resp.setHypervisor(vol.getHypervisor());
resp.setId(vol.getId());
resp.setInstanceId(vol.getVirtualMachineId());
resp.setSize(vol.getSize());
resp.setSnapshotId(vol.getSnapshotId());
resp.setState(vol.getState());
resp.setType(vol.getVolumeType());
resp.setVMState(vol.getVirtualMachineState());
resp.setZoneName(vol.getZoneName());
return resp;
}
throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." );
} catch( Exception e ) {
logger.error( "EC2 AttachVolume 2 - ", e);
throw new EC2ServiceException( ServerError.InternalError, e.getMessage() != null ? e.getMessage() : e.toString());
}
}
/**
* Detach a volume from an instance
*
* @param request
* @return
*/
public EC2Volume detachVolume(EC2Volume request) {
try {
CloudStackVolume vol = getApi().detachVolume(null, request.getId(), null);
EC2Volume resp = new EC2Volume();
if(vol != null) {
resp.setAttached(vol.getAttached());
resp.setCreated(vol.getCreated());
resp.setDevice(request.getDevice());
resp.setDeviceId(vol.getDeviceId());
resp.setHypervisor(vol.getHypervisor());
resp.setId(vol.getId());
resp.setInstanceId(vol.getVirtualMachineId());
resp.setSize(vol.getSize());
resp.setSnapshotId(vol.getSnapshotId());
resp.setState(vol.getState());
resp.setType(vol.getVolumeType());
resp.setVMState(vol.getVirtualMachineState());
resp.setZoneName(vol.getZoneName());
return resp;
}
throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." );
} catch( Exception e ) {
logger.error( "EC2 DetachVolume - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Create a volume
*
* @param request
* @return
*/
public EC2Volume createVolume( EC2CreateVolume request ) {
try {
CloudStackAccount caller = getCurrentAccount();
// -> put either snapshotid or diskofferingid on the request
String snapshotId = request.getSnapshotId();
Long size = request.getSize();
String diskOfferingId = null;
if (snapshotId == null) {
List<CloudStackDiskOffering> disks = getApi().listDiskOfferings(null, null, null, null);
for (CloudStackDiskOffering offer : disks) {
if (offer.isCustomized()) {
diskOfferingId = offer.getId();
}
}
if (diskOfferingId == null) throw new EC2ServiceException(ServerError.InternalError, "No Customize Disk Offering Found");
}
// // -> no volume name is given in the Amazon request but is required in the cloud API
CloudStackVolume vol = getApi().createVolume(UUID.randomUUID().toString(), null, diskOfferingId, null, size, snapshotId, toZoneId(request.getZoneName(), null));
if (vol != null) {
EC2Volume resp = new EC2Volume();
resp.setAttached(vol.getAttached());
resp.setCreated(vol.getCreated());
// resp.setDevice();
resp.setDeviceId(vol.getDeviceId());
resp.setHypervisor(vol.getHypervisor());
resp.setId(vol.getId());
resp.setInstanceId(vol.getVirtualMachineId());
resp.setSize(vol.getSize());
resp.setSnapshotId(vol.getSnapshotId());
resp.setState(vol.getState());
resp.setType(vol.getVolumeType());
resp.setVMState(vol.getVirtualMachineState());
resp.setZoneName(vol.getZoneName());
return resp;
}
return null;
} catch( Exception e ) {
logger.error( "EC2 CreateVolume - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Delete a volume
*
* @param request
* @return
*/
public EC2Volume deleteVolume( EC2Volume request ) {
try {
CloudStackInfoResponse resp = getApi().deleteVolume(request.getId());
if(resp != null) {
request.setState("deleted");
return request;
}
throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred.");
} catch( Exception e ) {
logger.error( "EC2 DeleteVolume 2 - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Reboot an instance or instances
*
* @param request
* @return
*/
public boolean rebootInstances(EC2RebootInstances request)
{
EC2Instance[] vms = null;
// -> reboot is not allowed on destroyed (i.e., terminated) instances
try {
String[] instanceSet = request.getInstancesSet();
EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null );
vms = previousState.getInstanceSet();
// -> send reboot requests for each found VM
for (EC2Instance vm : vms) {
if (vm.getState().equalsIgnoreCase( "Destroyed" )) continue;
CloudStackUserVm resp = getApi().rebootVirtualMachine(vm.getId());
if (logger.isDebugEnabled())
logger.debug("Rebooting VM " + resp.getId() + " job " + resp.getJobId());
}
// -> if some specified VMs where not found we have to tell the caller
if (instanceSet.length != vms.length)
throw new EC2ServiceException(ClientError.InvalidAMIID_NotFound, "One or more instanceIds do not exist, other instances rebooted.");
return true;
} catch( Exception e ) {
logger.error( "EC2 RebootInstances - ", e );
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Using a template (AMI), launch n instances
*
* @param request
* @return
*/
public EC2RunInstancesResponse runInstances(EC2RunInstances request) {
EC2RunInstancesResponse instances = new EC2RunInstancesResponse();
int createInstances = 0;
int canCreateInstances = -1;
int countCreated = 0;
try {
CloudStackAccount caller = getCurrentAccount();
// ugly...
canCreateInstances = calculateAllowedInstances();
if (-1 == canCreateInstances) canCreateInstances = request.getMaxCount();
if (canCreateInstances < request.getMinCount()) {
logger.info( "EC2 RunInstances - min count too big (" + request.getMinCount() + "), " + canCreateInstances + " left to allocate");
throw new EC2ServiceException(ClientError.InstanceLimitExceeded ,"Only " + canCreateInstances + " instance(s) left to allocate");
}
if ( canCreateInstances < request.getMaxCount())
createInstances = request.getMinCount();
else
createInstances = request.getMaxCount();
//find CS service Offering ID
String instanceType = "m1.small";
if(request.getInstanceType() != null){
instanceType = request.getInstanceType();
}
CloudStackServiceOffering svcOffering = getCSServiceOfferingId(instanceType);
if(svcOffering == null){
logger.info("No ServiceOffering found to be defined by name, please contact the administrator "+instanceType );
throw new EC2ServiceException(ClientError.Unsupported, "instanceType: [" + instanceType + "] not found!");
}
// zone stuff
String zoneId = toZoneId(request.getZoneName(), null);
List<CloudStackZone> zones = getApi().listZones(null, null, zoneId, null);
if (zones == null || zones.size() == 0) {
logger.info("EC2 RunInstances - zone [" + request.getZoneName() + "] not found!");
throw new EC2ServiceException(ClientError.InvalidZone_NotFound, "ZoneId [" + request.getZoneName() + "] not found!");
}
// we choose first zone?
CloudStackZone zone = zones.get(0);
// network
CloudStackNetwork network = findNetwork(zone);
// now actually deploy the vms
for( int i=0; i < createInstances; i++ ) {
CloudStackUserVm resp = getApi().deployVirtualMachine(svcOffering.getId(),
request.getTemplateId(), zoneId, null, null, null, null,
null, null, null, request.getKeyName(), null, (network != null ? network.getId() : null),
null, constructList(request.getGroupSet()), request.getSize().longValue(), request.getUserData());
EC2Instance vm = new EC2Instance();
vm.setId(resp.getId().toString());
vm.setName(resp.getName());
vm.setZoneName(resp.getZoneName());
vm.setTemplateId(resp.getTemplateId().toString());
if (resp.getSecurityGroupList() != null && resp.getSecurityGroupList().size() > 0) {
// TODO, we have a list of security groups, just return the first one?
List<CloudStackSecurityGroup> securityGroupList = resp.getSecurityGroupList();
for (CloudStackSecurityGroup securityGroup : securityGroupList) {
vm.addGroupName(securityGroup.getName());
}
}
vm.setState(resp.getState());
vm.setCreated(resp.getCreated());
List <CloudStackNic> nicList = resp.getNics();
for (CloudStackNic nic : nicList) {
if (nic.getIsDefault()) {
vm.setPrivateIpAddress(nic.getIpaddress());
break;
}
}
vm.setIpAddress(resp.getIpAddress());
vm.setAccountName(resp.getAccountName());
vm.setDomainId(resp.getDomainId());
vm.setHypervisor(resp.getHypervisor());
vm.setServiceOffering( svcOffering.getName());
instances.addInstance(vm);
countCreated++;
}
if (0 == countCreated) {
// TODO, we actually need to destroy left-over VMs when the exception is thrown
throw new EC2ServiceException(ServerError.InsufficientInstanceCapacity, "Insufficient Instance Capacity" );
}
return instances;
} catch( Exception e ) {
logger.error( "EC2 RunInstances - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Start an instance or instances
*
* @param request
* @return
*/
public EC2StartInstancesResponse startInstances(EC2StartInstances request) {
EC2StartInstancesResponse instances = new EC2StartInstancesResponse();
EC2Instance[] vms = null;
// -> first determine the current state of each VM (becomes it previous state)
try {
EC2DescribeInstancesResponse previousState = listVirtualMachines( request.getInstancesSet(), null );
vms = previousState.getInstanceSet();
// -> send start requests for each item
for (EC2Instance vm : vms) {
vm.setPreviousState(vm.getState());
// -> if its already running then we don't care
if (vm.getState().equalsIgnoreCase( "Running" ) || vm.getState().equalsIgnoreCase( "Destroyed" )) {
instances.addInstance(vm);
continue;
}
CloudStackUserVm resp = getApi().startVirtualMachine(vm.getId());
if(resp != null){
vm.setState(resp.getState());
if(logger.isDebugEnabled())
logger.debug("Starting VM " + vm.getId() + " job " + resp.getJobId());
}
instances.addInstance(vm);
}
return instances;
} catch( Exception e ) {
logger.error( "EC2 StartInstances - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Stop an instance or instances
*
* @param request
* @return
*/
public EC2StopInstancesResponse stopInstances(EC2StopInstances request) {
EC2StopInstancesResponse instances = new EC2StopInstancesResponse();
EC2Instance[] virtualMachines = null;
// -> first determine the current state of each VM (becomes it previous state)
try {
String[] instanceSet = request.getInstancesSet();
EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null );
virtualMachines = previousState.getInstanceSet();
// -> send stop requests for each item
for (EC2Instance vm : virtualMachines) {
vm.setPreviousState( vm.getState());
CloudStackUserVm resp = null;
if (request.getDestroyInstances()) {
if (vm.getState().equalsIgnoreCase( "Destroyed" )) {
instances.addInstance(vm);
continue;
}
resp = getApi().destroyVirtualMachine(vm.getId());
if(logger.isDebugEnabled())
logger.debug("Destroying VM " + vm.getId() + " job " + resp.getJobId());
} else {
if (vm.getState().equalsIgnoreCase("Stopped") || vm.getState().equalsIgnoreCase("Destroyed")) {
instances.addInstance(vm);
continue;
}
resp = getApi().stopVirtualMachine(vm.getId(), false);
if(logger.isDebugEnabled())
logger.debug("Stopping VM " + vm.getId() + " job " + resp.getJobId());
}
if (resp != null) {
vm.setState(resp.getState());
instances.addInstance(vm);
}
}
return instances;
} catch( Exception e ) {
logger.error( "EC2 StopInstances - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() + ", might already be destroyed" : "An unexpected error occurred.");
}
}
/**
* RunInstances includes a min and max count of requested instances to create.
* We have to be able to create the min number for the user or none at all. So
* here we determine what the user has left to create.
*
* @return -1 means no limit exists, other positive numbers give max number left that
* the user can create.
*/
private int calculateAllowedInstances() throws Exception {
int maxAllowed = -1;
CloudStackAccount ourAccount = getCurrentAccount();
if (ourAccount == null) {
// This should never happen, but
// we will return -99999 if this happens...
return -99999;
}
// if accountType is Admin == 1, then let's return -1
if (ourAccount.getAccountType() == 1) return -1;
// -> get the user limits on instances
// "0" represents instances:
// http://download.cloud.com/releases/2.2.0/api_2.2.8/user/listResourceLimits.html
List<CloudStackResourceLimit> limits = getApi().listResourceLimits(null, null, null, null, "0");
if (limits != null && limits.size() > 0) {
maxAllowed = (int)limits.get(0).getMax().longValue();
if (maxAllowed == -1)
return -1; // no limit
EC2DescribeInstancesResponse existingVMS = listVirtualMachines( null, null );
EC2Instance[] vmsList = existingVMS.getInstanceSet();
return (maxAllowed - vmsList.length);
} else {
return 0;
}
}
/**
* Performs the cloud API listVirtualMachines one or more times.
*
* @param virtualMachineIds - an array of instances we are interested in getting information on
* @param ifs - filter out unwanted instances
*/
private EC2DescribeInstancesResponse listVirtualMachines( String[] virtualMachineIds, EC2InstanceFilterSet ifs ) throws Exception
{
EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse();
if (null == virtualMachineIds || 0 == virtualMachineIds.length) {
instances = lookupInstances( null, instances );
} else {
for( int i=0; i < virtualMachineIds.length; i++ ) {
instances = lookupInstances( virtualMachineIds[i], instances );
}
}
if ( null == ifs )
return instances;
else return ifs.evaluate( instances );
}
/**
* Get one or more templates depending on the volumeId parameter.
*
* @param volumeId - if interested in one specific volume, null if want to list all volumes
* @param instanceId - if interested in volumes for a specific instance, null if instance is not important
*/
private EC2DescribeVolumesResponse listVolumes(String volumeId, String instanceId, EC2DescribeVolumesResponse volumes)throws Exception {
List<CloudStackVolume> vols = getApi().listVolumes(null, null, null, volumeId, null, null, null, null, null, instanceId, null);
if(vols != null && vols.size() > 0) {
for(CloudStackVolume vol : vols) {
EC2Volume ec2Vol = new EC2Volume();
ec2Vol.setId(vol.getId());
if(vol.getAttached() != null)
ec2Vol.setAttached(vol.getAttached());
ec2Vol.setCreated(vol.getCreated());
if(vol.getDeviceId() != null)
ec2Vol.setDeviceId(vol.getDeviceId());
ec2Vol.setHypervisor(vol.getHypervisor());
if(vol.getSnapshotId() != null)
ec2Vol.setSnapshotId(vol.getSnapshotId());
ec2Vol.setState(mapToAmazonVolState(vol.getState()));
ec2Vol.setSize(vol.getSize());
ec2Vol.setType(vol.getVolumeType());
if(vol.getVirtualMachineId() != null)
ec2Vol.setInstanceId(vol.getVirtualMachineId());
if(vol.getVirtualMachineState() != null)
ec2Vol.setVMState(vol.getVirtualMachineState());
ec2Vol.setZoneName(vol.getZoneName());
volumes.addVolume(ec2Vol);
}
}
return volumes;
}
/**
* Translate the given zone name into the required zoneId. Query for
* a list of all zones and match the zone name given. Amazon uses zone
* names while the Cloud API often requires the zoneId.
*
* @param zoneName - (e.g., 'AH'), if null return the first zone in the available list
*
* @return the zoneId that matches the given zone name
*/
private String toZoneId(String zoneName, String domainId) throws Exception {
EC2DescribeAvailabilityZonesResponse zones = null;
String[] interestedZones = null;
if ( null != zoneName) {
interestedZones = new String[1];
interestedZones[0] = zoneName;
}else {
CloudStackZone zone = findZone();
if(zone != null){
return zone.getId();
}
}
zones = listZones(interestedZones, domainId);
if (zones == null || zones.getZoneIdAt( 0 ) == null)
throw new EC2ServiceException(ClientError.InvalidParameterValue, "Unknown zoneName value - " + zoneName);
return zones.getZoneIdAt(0);
}
/**
* Convert from the Amazon instanceType strings to Cloud serviceOfferingId
*
*/
private CloudStackServiceOffering getCSServiceOfferingId(String instanceType){
try {
if (null == instanceType) instanceType = "m1.small";
CloudStackSvcOfferingDao dao = new CloudStackSvcOfferingDao();
return dao.getSvcOfferingByName(instanceType);
} catch(Exception e) {
logger.error( "Error while retrieving ServiceOffering information by name - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Convert from the Cloud serviceOfferingId to the Amazon instanceType strings based
* on the loaded map.
*
* @param serviceOfferingId
* @return A valid value for the Amazon defined instanceType
* @throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException
*/
private String serviceOfferingIdToInstanceType( String serviceOfferingId ){
try{
CloudStackSvcOfferingDao dao = new CloudStackSvcOfferingDao();
CloudStackServiceOffering offering = dao.getSvcOfferingById(serviceOfferingId);
if(offering == null){
logger.warn( "No instanceType match for serviceOfferingId: [" + serviceOfferingId + "]" );
return "m1.small";
}
return offering.getName();
}
catch(Exception e) {
logger.error( "sError while retrieving ServiceOffering information by id - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Match the value in the 'description' field of the listOsTypes response to get
* the osTypeId.
*
* @param osTypeName
* @return the Cloud.com API osTypeId
*/
private String toOSTypeId( String osTypeName ) throws Exception {
try {
List<CloudStackOsType> osTypes = getApi().listOsTypes(null, null, null);
for (CloudStackOsType osType : osTypes) {
if (osType.getDescription().toLowerCase().indexOf(osTypeName.toLowerCase()) != -1)
return osType.getId();
}
return null;
} catch(Exception e) {
logger.error( "List OS Types - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* More than one place we need to access the defined list of zones. If given a specific
* list of zones of interest, then only values from those zones are returned.
*
* @param interestedZones - can be null, should be a subset of all zones
*
* @return EC2DescribeAvailabilityZonesResponse
*/
private EC2DescribeAvailabilityZonesResponse listZones(String[] interestedZones, String domainId) throws Exception
{
EC2DescribeAvailabilityZonesResponse zones = new EC2DescribeAvailabilityZonesResponse();
List<CloudStackZone> cloudZones = getApi().listZones(true, domainId, null, null);
if(cloudZones != null) {
for(CloudStackZone cloudZone : cloudZones) {
if ( null != interestedZones && 0 < interestedZones.length ) {
for( int j=0; j < interestedZones.length; j++ ) {
if (interestedZones[j].equalsIgnoreCase( cloudZone.getName())) {
zones.addZone(cloudZone.getId().toString(), cloudZone.getName());
break;
}
}
} else {
zones.addZone(cloudZone.getId().toString(), cloudZone.getName());
}
}
}
return zones;
}
/**
* Get information on one or more virtual machines depending on the instanceId parameter.
*
* @param instanceId - if null then return information on all existing instances, otherwise
* just return information on the matching instance.
* @param instances - a container object to fill with one or more EC2Instance objects
*
* @return the same object passed in as the "instances" parameter modified with one or more
* EC2Instance objects loaded.
*/
private EC2DescribeInstancesResponse lookupInstances( String instanceId, EC2DescribeInstancesResponse instances )
throws Exception {
String instId = instanceId != null ? instanceId : null;
List<CloudStackUserVm> vms = getApi().listVirtualMachines(null, null, null, null, null, null,
instId, null, null, null, null, null, null, null, null);
if(vms != null && vms.size() > 0) {
for(CloudStackUserVm cloudVm : vms) {
EC2Instance ec2Vm = new EC2Instance();
ec2Vm.setId(cloudVm.getId().toString());
ec2Vm.setName(cloudVm.getName());
ec2Vm.setZoneName(cloudVm.getZoneName());
ec2Vm.setTemplateId(cloudVm.getTemplateId().toString());
ec2Vm.setGroup(cloudVm.getGroup());
ec2Vm.setState(cloudVm.getState());
ec2Vm.setCreated(cloudVm.getCreated());
ec2Vm.setIpAddress(cloudVm.getIpAddress());
ec2Vm.setAccountName(cloudVm.getAccountName());
ec2Vm.setDomainId(cloudVm.getDomainId());
ec2Vm.setHypervisor(cloudVm.getHypervisor());
ec2Vm.setRootDeviceType(cloudVm.getRootDeviceType());
ec2Vm.setRootDeviceId(cloudVm.getRootDeviceId());
ec2Vm.setServiceOffering(serviceOfferingIdToInstanceType(cloudVm.getServiceOfferingId().toString()));
List<CloudStackNic> nics = cloudVm.getNics();
for(CloudStackNic nic : nics) {
if(nic.getIsDefault()) {
ec2Vm.setPrivateIpAddress(nic.getIpaddress());
break;
}
}
if (cloudVm.getSecurityGroupList() != null && cloudVm.getSecurityGroupList().size() > 0) {
// TODO, we have a list of security groups, just return the first one?
List<CloudStackSecurityGroup> securityGroupList = cloudVm.getSecurityGroupList();
for (CloudStackSecurityGroup securityGroup : securityGroupList) {
ec2Vm.addGroupName(securityGroup.getName());
}
}
instances.addInstance(ec2Vm);
}
}else{
if(instanceId != null){
//no such instance found
throw new EC2ServiceException(ServerError.InternalError, "Instance:" + instanceId + " not found");
}
}
return instances;
}
/**
* Get one or more templates depending on the templateId parameter.
*
* @param templateId - if null then return information on all existing templates, otherwise
* just return information on the matching template.
* @param images - a container object to fill with one or more EC2Image objects
*
* @return the same object passed in as the "images" parameter modified with one or more
* EC2Image objects loaded.
*/
private EC2DescribeImagesResponse listTemplates( String templateId, EC2DescribeImagesResponse images ) throws EC2ServiceException {
try {
List<CloudStackTemplate> result = new ArrayList<CloudStackTemplate>();
if(templateId != null){
List<CloudStackTemplate> template = getApi().listTemplates("executable", null, null, null, templateId , null, null, null);
if(template != null){
result.addAll(template);
}
}else{
List<CloudStackTemplate> selfExecutable = getApi().listTemplates("selfexecutable", null, null, null, null, null, null, null);
if(selfExecutable != null){
result.addAll(selfExecutable);
}
List<CloudStackTemplate> featured = getApi().listTemplates("featured", null, null, null, null, null, null, null);
if(featured != null){
result.addAll(featured);
}
List<CloudStackTemplate> sharedExecutable = getApi().listTemplates("sharedexecutable", null, null, null, null, null, null, null);
if(sharedExecutable != null){
result.addAll(sharedExecutable);
}
List<CloudStackTemplate> community = getApi().listTemplates("community", null, null, null, null, null, null, null);
if(community != null){
result.addAll(community);
}
}
if (result != null && result.size() > 0) {
for (CloudStackTemplate temp : result) {
EC2Image ec2Image = new EC2Image();
ec2Image.setId(temp.getId().toString());
ec2Image.setAccountName(temp.getAccount());
ec2Image.setName(temp.getName());
ec2Image.setDescription(temp.getDisplayText());
ec2Image.setOsTypeId(temp.getOsTypeId().toString());
ec2Image.setIsPublic(temp.getIsPublic());
ec2Image.setIsReady(temp.getIsReady());
ec2Image.setDomainId(temp.getDomainId());
images.addImage(ec2Image);
}
}
return images;
} catch(Exception e) {
logger.error( "List Templates - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* List security groups
*
* @param interestedGroups
* @return
* @throws EC2ServiceException
* @throws UnsupportedEncodingException
* @throws SignatureException
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
* @throws ParseException
*/
public EC2DescribeSecurityGroupsResponse listSecurityGroups( String[] interestedGroups ) throws Exception {
try {
EC2DescribeSecurityGroupsResponse groupSet = new EC2DescribeSecurityGroupsResponse();
List<CloudStackSecurityGroup> groups = getApi().listSecurityGroups(null, null, null, null, null, null);
if (groups != null && groups.size() > 0)
for (CloudStackSecurityGroup group : groups) {
boolean matched = false;
if (interestedGroups.length > 0) {
for (String groupName :interestedGroups) {
if (groupName.equalsIgnoreCase(group.getName())) {
matched = true;
break;
}
}
} else {
matched = true;
}
if (!matched) continue;
EC2SecurityGroup ec2Group = new EC2SecurityGroup();
// not sure if we should set both account and account name to accountname
ec2Group.setAccount(group.getAccountName());
ec2Group.setAccountName(group.getAccountName());
ec2Group.setName(group.getName());
ec2Group.setDescription(group.getDescription());
ec2Group.setDomainId(group.getDomainId());
ec2Group.setId(group.getId().toString());
toPermission(ec2Group, group);
groupSet.addGroup(ec2Group);
}
return groupSet;
} catch(Exception e) {
logger.error( "List Security Groups - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Convert ingress rule to EC2IpPermission records
*
* @param response
* @param group
* @return
*/
private boolean toPermission(EC2SecurityGroup response, CloudStackSecurityGroup group ) {
List<CloudStackIngressRule> rules = group.getIngressRules();
if (rules == null || rules.isEmpty()) return false;
for (CloudStackIngressRule rule : rules) {
EC2IpPermission perm = new EC2IpPermission();
perm.setProtocol(rule.getProtocol());
perm.setFromPort(rule.getStartPort());
perm.setToPort(rule.getEndPort());
perm.setRuleId(rule.getRuleId() != null ? rule.getRuleId().toString() : new String());
perm.setIcmpCode(rule.getIcmpCode() != null ? rule.getIcmpCode().toString() : new String());
perm.setIcmpType(rule.getIcmpType() != null ? rule.getIcmpType().toString() : new String());
perm.setCIDR(rule.getCidr());
perm.addIpRange(rule.getCidr());
if (rule.getAccountName() != null && rule.getSecurityGroupName() != null) {
EC2SecurityGroup newGroup = new EC2SecurityGroup();
newGroup.setAccount(rule.getAccountName());
newGroup.setName(rule.getSecurityGroupName());
perm.addUser(newGroup);
}
response.addIpPermission(perm);
}
return true;
}
/**
* Find the current account based on the SecretKey
*
* @return
* @throws Exception
*/
public CloudStackAccount getCurrentAccount() throws Exception {
if (currentAccount != null) {
// verify this is the same account!!!
for (CloudStackUser user : currentAccount.getUser()) {
if (user.getSecretkey() != null && user.getSecretkey().equalsIgnoreCase(UserContext.current().getSecretKey())) {
return currentAccount;
}
}
}
// otherwise let's find this user/account
List<CloudStackAccount> accounts = getApi().listAccounts(null, null, null, null, null, null, null, null);
for (CloudStackAccount account : accounts) {
CloudStackUser[] users = account.getUser();
for (CloudStackUser user : users) {
String userSecretKey = user.getSecretkey();
if (userSecretKey != null && userSecretKey.equalsIgnoreCase(UserContext.current().getSecretKey())) {
currentAccount = account;
return account;
}
}
}
// if we get here, there is something wrong...
return null;
}
/**
* List networkOfferings by zone with securityGroup enabled
*
* @param zoneId
* @return
* @throws Exception
*/
private CloudStackNetwork getNetworksWithSecurityGroupEnabled(String zoneId) throws Exception {
List<CloudStackNetwork> networks = getApi().listNetworks(null, null, null, null, null, null, null, null, null, zoneId);
List<CloudStackNetwork> netWithSecGroup = new ArrayList<CloudStackNetwork>();
for (CloudStackNetwork network : networks ) {
if (!network.getNetworkOfferingAvailability().equalsIgnoreCase("unavailable") && network.getSecurityGroupEnabled())
netWithSecGroup.add(network);
}
// we'll take the first one
return netWithSecGroup.get(0);
}
/**
* Create a network
*
* @param zoneId
* @param offering
* @param owner
* @return
* @throws Exception
*/
private CloudStackNetwork createDefaultGuestNetwork(String zoneId, CloudStackNetworkOffering offering, CloudStackAccount owner) throws Exception {
return getApi().createNetwork(owner.getName() + "-network", owner.getName() + "-network", offering.getId(), zoneId, owner.getName(),
owner.getDomainId(), true, null, null, null, null, null, null, null, null);
}
/**
* List of networks without securityGroup enabled by zone
*
* @param zoneId
* @return
* @throws Exception
*/
private CloudStackNetwork getNetworksWithoutSecurityGroupEnabled(String zoneId) throws Exception {
// grab current account
CloudStackAccount caller = getCurrentAccount();
//check if account has any networks in the system
List<CloudStackNetwork> networks = getApi().listNetworks(caller.getName(), caller.getDomainId(), null, true, null, null, null, null, null, zoneId);
//listRequired offerings in the system - the network created from this offering has to be specified in deployVm command
List<CloudStackNetworkOffering> reuquiredOfferings = getApi().listNetworkOfferings("Required", null, null, null, true, null, null, null, null, null, zoneId);
if (reuquiredOfferings != null && !reuquiredOfferings.isEmpty()) {
if (networks != null && !networks.isEmpty()) {
//pick up the first required network from the network list
for (CloudStackNetwork network : networks) {
for (CloudStackNetworkOffering requiredOffering : reuquiredOfferings) {
logger.debug("[reqd/virtual} offering: " + requiredOffering.getId() + " network " + network.getNetworkOfferingId());
if (network.getNetworkOfferingId().equals(requiredOffering.getId())) {
return network;
}
}
}
} else {
//create new network and return it
return createDefaultGuestNetwork(zoneId, reuquiredOfferings.get(0), caller);
}
} else {
//find all optional network offerings in the system
List<CloudStackNetworkOffering> optionalOfferings = getApi().listNetworkOfferings("Optional", null, null, null, true, null, null, null, null, null, zoneId);
if (optionalOfferings != null && !optionalOfferings.isEmpty()) {
if (networks != null && !networks.isEmpty()) {
for (CloudStackNetwork network : networks) {
for (CloudStackNetworkOffering optionalOffering : optionalOfferings) {
logger.debug("[optional] offering: " + optionalOffering.getId() + " network " + network.getNetworkOfferingId());
if (network.getNetworkOfferingId().equals(optionalOffering.getId())) {
return network;
}
}
}
}
}
}
// if we get this far and haven't returned already return an error
throw new EC2ServiceException(ServerError.InternalError, "Unable to find an appropriate network for account " + caller.getName());
}
/**
* Find a suitable network to use for deployVM
*
* @param zone
* @return
* @throws Exception
*/
private CloudStackNetwork findNetwork(CloudStackZone zone) throws Exception {
if (zone == null) return null;
// for basic networking, we don't specify a networkid for deployvm
if (zone.getNetworkType().equalsIgnoreCase("basic")) return null;
if (zone.getSecurityGroupsEnabled()) {
// find system security group enabled network
return getNetworksWithSecurityGroupEnabled(zone.getId());
} else {
return getNetworksWithoutSecurityGroupEnabled(zone.getId());
}
}
private CloudStackZone findZone() throws Exception {
CloudStackAccount caller = getCurrentAccount();
List<CloudStackZone> cloudZones;
String defaultZoneId = getDefaultZoneId(caller.getId());
if (defaultZoneId != null) {
cloudZones = getApi().listZones(true, null, defaultZoneId, null);
} else {
// caller.getDomainId doesn't work in user mode
// List<CloudStackZone> cloudZones = getApi().listZones(true, caller.getDomainId(), null, null);
cloudZones = getApi().listZones(true, null, null, null);
}
if (cloudZones != null && cloudZones.size() > 0) {
return cloudZones.get(0);
}
return null;
}
/**
* Finds the defaultZone marked for the account
*/
private String getDefaultZoneId(String accountId) {
try {
CloudStackAccountDao dao = new CloudStackAccountDao();
CloudStackAccount account = dao.getdefaultZoneId(accountId);
return account.getDefaultZoneId();
} catch(Exception e) {
logger.error( "Error while retrieving Account information by id - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Windows has its own device strings.
*
* @param hypervisor
* @param deviceId
* @return
*/
public String cloudDeviceIdToDevicePath( String hypervisor, String deviceId )
{
Integer devId = new Integer(deviceId);
if (null != hypervisor && hypervisor.toLowerCase().contains( "windows" )) {
switch( devId ) {
case 1: return "xvdb";
case 2: return "xvdc";
case 3: return "xvdd";
case 4: return "xvde";
case 5: return "xvdf";
case 6: return "xvdg";
case 7: return "xvdh";
case 8: return "xvdi";
case 9: return "xvdj";
default: return new String( "" + deviceId );
}
} else { // -> assume its unix
switch( devId ) {
case 1: return "/dev/sdb";
case 2: return "/dev/sdc";
case 3: return "/dev/sdd";
case 4: return "/dev/sde";
case 5: return "/dev/sdf";
case 6: return "/dev/sdg";
case 7: return "/dev/sdh";
case 8: return "/dev/sdi";
case 9: return "/dev/sdj";
default: return new String( "" + deviceId );
}
}
}
/**
* Translate the device name string into a Cloud Stack deviceId.
* deviceId 3 is reserved for CDROM and 0 for the ROOT disk
*
* @param device string
* @return deviceId value
*/
private String mapDeviceToCloudDeviceId( String device )
{
if (device.equalsIgnoreCase( "/dev/sdb" )) return "1";
else if (device.equalsIgnoreCase( "/dev/sdc" )) return "2";
else if (device.equalsIgnoreCase( "/dev/sde" )) return "4";
else if (device.equalsIgnoreCase( "/dev/sdf" )) return "5";
else if (device.equalsIgnoreCase( "/dev/sdg" )) return "6";
else if (device.equalsIgnoreCase( "/dev/sdh" )) return "7";
else if (device.equalsIgnoreCase( "/dev/sdi" )) return "8";
else if (device.equalsIgnoreCase( "/dev/sdj" )) return "9";
else if (device.equalsIgnoreCase( "/dev/xvdb" )) return "1";
else if (device.equalsIgnoreCase( "/dev/xvdc" )) return "2";
else if (device.equalsIgnoreCase( "/dev/xvde" )) return "4";
else if (device.equalsIgnoreCase( "/dev/xvdf" )) return "5";
else if (device.equalsIgnoreCase( "/dev/xvdg" )) return "6";
else if (device.equalsIgnoreCase( "/dev/xvdh" )) return "7";
else if (device.equalsIgnoreCase( "/dev/xvdi" )) return "8";
else if (device.equalsIgnoreCase( "/dev/xvdj" )) return "9";
else if (device.equalsIgnoreCase( "xvdb" )) return "1";
else if (device.equalsIgnoreCase( "xvdc" )) return "2";
else if (device.equalsIgnoreCase( "xvde" )) return "4";
else if (device.equalsIgnoreCase( "xvdf" )) return "5";
else if (device.equalsIgnoreCase( "xvdg" )) return "6";
else if (device.equalsIgnoreCase( "xvdh" )) return "7";
else if (device.equalsIgnoreCase( "xvdi" )) return "8";
else if (device.equalsIgnoreCase( "xvdj" )) return "9";
else throw new EC2ServiceException( ClientError.Unsupported, device + " is not supported" );
}
/**
* Map CloudStack instance state to Amazon state strings
*
* @param state
* @return
*/
private String mapToAmazonVolState( String state )
{
if (state.equalsIgnoreCase( "Allocated" ) ||
state.equalsIgnoreCase( "Creating" ) ||
state.equalsIgnoreCase( "Ready" )) return "available";
if (state.equalsIgnoreCase( "Destroy" )) return "deleting";
return "error";
}
/**
* Stop an instance
* Wait until one specific VM has stopped
*
* @param instanceId
* @return
* @throws Exception
*/
private boolean stopVirtualMachine( String instanceId) throws Exception {
try {
CloudStackUserVm resp = getApi().stopVirtualMachine(instanceId, false);
if (logger.isDebugEnabled())
logger.debug("Stopping VM " + instanceId );
return resp != null;
} catch(Exception e) {
logger.error( "StopVirtualMachine - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Start an existing stopped instance(VM)
*
* @param instanceId
* @return
* @throws Exception
*/
private boolean startVirtualMachine( String instanceId ) throws Exception {
try {
CloudStackUserVm resp = getApi().startVirtualMachine(instanceId);
if (logger.isDebugEnabled())
logger.debug("Starting VM " + instanceId );
return resp != null;
} catch(Exception e) {
logger.error("StartVirtualMachine - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/**
* Cloud Stack API takes a comma separated list as a parameter.
*
* @throws UnsupportedEncodingException
*/
private String constructList( String[] elements ) throws UnsupportedEncodingException {
if (null == elements || 0 == elements.length) return null;
StringBuffer elementList = new StringBuffer();
for( int i=0; i < elements.length; i++ ) {
if (0 < i) elementList.append( "," );
elementList.append( elements[i] );
}
return elementList.toString();
}
}