/**
* This file is part of CloudML [ http://cloudml.org ]
*
* Copyright (C) 2012 - SINTEF ICT
* Contact: Franck Chauvel <franck.chauvel@sintef.no>
*
* Module: root
*
* CloudML is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* CloudML is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with CloudML. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.cloudml.connectors;
import com.microsoft.windowsazure.Configuration;
import com.microsoft.windowsazure.core.OperationResponse;
import com.microsoft.windowsazure.core.OperationStatus;
import com.microsoft.windowsazure.core.OperationStatusResponse;
import com.microsoft.windowsazure.core.utils.Constants;
import com.microsoft.windowsazure.core.utils.KeyStoreType;
import com.microsoft.windowsazure.exception.ServiceException;
import com.microsoft.windowsazure.management.compute.ComputeManagementClient;
import com.microsoft.windowsazure.management.compute.ComputeManagementService;
import com.microsoft.windowsazure.management.compute.HostedServiceOperations;
import com.microsoft.windowsazure.management.compute.models.*;
import com.microsoft.windowsazure.management.configuration.ManagementConfiguration;
import org.cloudml.core.ComponentInstance;
import org.cloudml.core.VM;
import org.cloudml.core.VMInstance;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by ferrynico on 12/07/15.
*/
public class AzureConnector implements Connector{
private static final Logger journal = Logger.getLogger(JCloudsConnector.class.getName());
private ComputeManagementClient computeManagementClient;
private HostedServiceOperations hostedServicesOperations;
private HashMap<String,Object> runtimeInformation;
public AzureConnector(String endpoint, String provider,String login,String secretKey){
journal.log(Level.INFO, ">> Connecting to "+provider+" ...");
Configuration config = null;
try {
config = createConfiguration(endpoint,provider,login,secretKey);
} catch (Exception e) {
e.printStackTrace();
}
journal.log(Level.INFO, ">> Authenticating ...");
computeManagementClient = ComputeManagementService.create(config);
hostedServicesOperations = computeManagementClient.getHostedServicesOperations();
}
@Override
public void execCommand(String id, String command, String login, String key) {
DeploymentGetResponse response = null;
try {
response = computeManagementClient.getDeploymentsOperations().getByName(id,id);
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
SSHConnector sc=new SSHConnector(key, login, response.getVirtualIPAddresses().get(0).getAddress().getHostAddress());
sc.execCommandSsh(command);
}
@Override
public HashMap<String, Object> createInstance(VMInstance a) {
runtimeInformation=new HashMap<String, Object>();
VM vm = a.getType();
ComponentInstance.State state = ComponentInstance.State.UNRECOGNIZED;
journal.log(Level.INFO, ">> Provisioning a vm ...");
try {
createHostedService(a.getName(),vm.getLocation(),vm.getSshKey());
} catch (SAXException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
ArrayList<Role> rolelist = null;
try {
rolelist = createRoleList(a, vm, vm.getGroupName(),vm.getGroupName());
} catch (Exception e) {
e.printStackTrace();
}
VirtualMachineCreateDeploymentParameters deploymentParameters = new VirtualMachineCreateDeploymentParameters();
deploymentParameters.setDeploymentSlot(DeploymentSlot.Staging);
deploymentParameters.setName(a.getName());
deploymentParameters.setLabel(a.getName());
deploymentParameters.setRoles(rolelist);
deploymentParameters.setDeploymentSlot(DeploymentSlot.Production);
VirtualMachineCreateParameters createParameters=new VirtualMachineCreateParameters();
ArrayList<ConfigurationSet> configurationSets = new ArrayList<ConfigurationSet>();
createParameters.setConfigurationSets(configurationSets);
createParameters.setOSVirtualHardDisk(createOSVHD(vm, vm.getGroupName(),vm.getGroupName()));
createParameters.setProvisionGuestAgent(true);
createParameters.setRoleSize(VirtualMachineRoleSize.SMALL);
createParameters.setRoleName(a.getName());
configurationSets.add(createConfigOS(a.getName(),vm));
configurationSets.add(createConfigNetwork());
// Act
try {
OperationStatusResponse r;
if(!isDeploymentPresent(computeManagementClient,a.getName(),a.getName()))
r=computeManagementClient.getVirtualMachinesOperations().createDeployment(a.getName(), deploymentParameters);
else r=computeManagementClient.getVirtualMachinesOperations().create(a.getName(), a.getName(), createParameters);
waitOperationToComplete(r.getId(),60,100);
DeploymentGetResponse response = computeManagementClient.getDeploymentsOperations().getByName(a.getName(),a.getName());
String ip=response.getVirtualIPAddresses().get(0).getAddress().getHostAddress();
runtimeInformation.put("publicAddress", ip);
a.setId(a.getName());
//wait for the VM to be accessible
String login="ubuntu";
if(!vm.getLogin().equals(""))
login=vm.getLogin();
SSHConnector sc=new SSHConnector(vm.getPrivateKey(), login, ip);
while(!sc.checkConnectivity()){
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
journal.log(Level.SEVERE, e.getMessage());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
state = ComponentInstance.State.RUNNING;
runtimeInformation.put("status", state);
return runtimeInformation;
}
@Override
public void destroyVM(String id) {
}
@Override
public void closeConnection() {
}
@Override
public void updateVMMetadata(VMInstance a) {
}
@Override
public void uploadFile(String sourcePath, String destinationPath, String nodeId, String login, String key) {
}
@Override
public String createSnapshot(VMInstance a) {
return null;
}
@Override
public String createImage(VMInstance a) {
return null;
}
@Override
public void startVM(VMInstance a) {
journal.log(Level.INFO, ">> Starting VM: "+a.getName());
OperationStatusResponse r= null;
try {
r = computeManagementClient.getVirtualMachinesOperations().start(a.getName(), a.getName(), a.getName());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
waitOperationToComplete(r.getId(),60, 100);
}
@Override
public void stopVM(VMInstance a) {
journal.log(Level.INFO, ">> Starting VM: "+a.getName());
OperationStatusResponse r= null;
try {
VirtualMachineShutdownParameters params = new VirtualMachineShutdownParameters();
params.setPostShutdownAction(PostShutdownAction.StoppedDeallocated);
r = computeManagementClient.getVirtualMachinesOperations().shutdown(a.getName(), a.getName(), a.getName(),params);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
waitOperationToComplete(r.getId(),60, 100);
}
private void uploadCerts(ComputeManagementClient client, String cloudServiceName, byte[] certData){
try {
// Try to upload to hosted service
ServiceCertificateCreateParameters params = new ServiceCertificateCreateParameters();
params.setCertificateFormat(CertificateFormat.Pfx);
params.setData(certData);
client.getServiceCertificatesOperations().create(cloudServiceName, params);
} catch (Exception e) {
e.printStackTrace();
}
}
private void createHostedService(String hostedServiceName, String loc, String certPath) throws SAXException, InterruptedException, ExecutionException, TransformerException, ServiceException, URISyntaxException, ParserConfigurationException, IOException {
//hosted service required for vm deployment
HostedServiceCreateParameters createParameters = new HostedServiceCreateParameters();
//required
createParameters.setLabel(hostedServiceName);
//required
createParameters.setServiceName(hostedServiceName);
createParameters.setDescription(hostedServiceName);
//required
String location="North Europe";
if(!loc.equals(""))
location=loc;
createParameters.setLocation(location);
OperationResponse hostedServiceOperationResponse = hostedServicesOperations.create(createParameters);
uploadCerts(computeManagementClient,hostedServiceName,getCertData(certPath));
}
private static byte[] getCertData(String path){
FileInputStream fileInputStream=null;
File file = new File(path);
byte[] bFile = new byte[(int) file.length()];
try {
//convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
}catch(Exception e){
e.printStackTrace();
}
return bFile;
}
private Configuration createConfiguration(String endpoint, String provider,String login,String secretKey) throws Exception {
return ManagementConfiguration.configure(
new URI("https://management.core.windows.net"),
endpoint,
login,
secretKey,
KeyStoreType.pkcs12
);
}
private static boolean isDeploymentPresent(ComputeManagementClient computeClient, String hostedServiceName, String deploymentName) {
boolean deploymentPresent = false;
try {
computeClient.getDeploymentsOperations().getByName(hostedServiceName, deploymentName);
deploymentPresent = true;
} catch (ServiceException e) {
if (e.getMessage().contains("ResourceNotFound")) {
deploymentPresent = false;
} else {
throw new IllegalArgumentException("Unable to get VM deployment state", e);
}
} catch (Exception e) {
throw new IllegalStateException("Unable to get VM deployment state", e);
}
return deploymentPresent;
}
private OSVirtualHardDisk createOSVHD(VM vm,String storageAccountName, String storageContainer){
int random = (int)(Math.random()* 100);
URI mediaLinkUriValue = null;
try {
mediaLinkUriValue = new URI("http://"+ storageAccountName + ".blob.core.windows.net/"+storageContainer+ "/ubuntu" + random +".vhd");
String osVHarddiskName ="ubuntuoshdname"+ random;
String operatingSystemName ="Linux";
if(!vm.getOs().equals(""))
if(vm.getOs().toLowerCase().contains("win"))
operatingSystemName="Windows";
String sourceImageName = "Ubuntu";
if(!vm.getImageId().equals(""))
sourceImageName=vm.getImageId();
else if(!vm.getOs().equals(""))
sourceImageName = getOSSourceImage(vm.getOs());
OSVirtualHardDisk oSVirtualHardDisk = new OSVirtualHardDisk();
//required
oSVirtualHardDisk.setName(osVHarddiskName);
oSVirtualHardDisk.setHostCaching(VirtualHardDiskHostCaching.READWRITE);
oSVirtualHardDisk.setOperatingSystem(operatingSystemName);
//required
oSVirtualHardDisk.setMediaLink(mediaLinkUriValue);
//required
oSVirtualHardDisk.setSourceImageName(sourceImageName);
return oSVirtualHardDisk;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private ConfigurationSet createConfigNetwork(){
ConfigurationSet networkConfigset = new ConfigurationSet();
networkConfigset.setConfigurationSetType(ConfigurationSetTypes.NETWORKCONFIGURATION);
// Define endpoints
ArrayList<InputEndpoint> enpoints = new ArrayList<InputEndpoint>();
networkConfigset.setInputEndpoints(enpoints);
InputEndpoint sshPort = new InputEndpoint();
enpoints.add(sshPort);
sshPort.setPort(22);
sshPort.setLocalPort(22);
sshPort.setName("ssh");
sshPort.setProtocol(InputEndpointTransportProtocol.TCP);
return networkConfigset;
}
private ConfigurationSet createConfigOS(String vmName, VM vm){
String adminUserPassword= "ubuntu!12";
String adminUserName = "ubuntu";
if(!vm.getLogin().equals(""))
adminUserName = vm.getLogin();
if(!vm.getPasswd().equals(""))
adminUserPassword=vm.getPasswd();
//required
ArrayList<ConfigurationSet> configurationSetList = new ArrayList<ConfigurationSet>();
ConfigurationSet configurationSet = new ConfigurationSet();
String type=ConfigurationSetTypes.LINUXPROVISIONINGCONFIGURATION;
if(!vm.getOs().equals(""))
if(vm.getOs().toLowerCase().contains("win"))
type=ConfigurationSetTypes.WINDOWSPROVISIONINGCONFIGURATION;
configurationSet.setConfigurationSetType(type);
configurationSet.setDisableSshPasswordAuthentication(true);
configurationSet.setComputerName(vmName);
configurationSet.setAdminPassword(adminUserPassword);
configurationSet.setUserName(adminUserName);
configurationSet.setUserPassword(adminUserPassword);
configurationSet.setAdminUserName(adminUserName);
configurationSet.setEnableAutomaticUpdates(true);
configurationSet.setHostName(vmName+".cloudapp.net");
configurationSetList.add(configurationSet);
ArrayList<SshSettingPublicKey> pks= new ArrayList<SshSettingPublicKey>();
SshSettings ssh=new SshSettings();
ssh.setPublicKeys(pks);
SshSettingPublicKey sspk = new SshSettingPublicKey();
sspk.setFingerprint(getFingerprint(vmName));
sspk.setPath("/home/" + adminUserName + "/.ssh/authorized_keys"); //TODO: update for windows
pks.add(sspk);
ArrayList<SshSettingKeyPair> keyPairs= new ArrayList<SshSettingKeyPair>();
ssh.setKeyPairs(keyPairs);
SshSettingKeyPair keyPair = new SshSettingKeyPair();
keyPairs.add(keyPair);
keyPair.setFingerprint(getFingerprint(vmName));
keyPair.setPath("/home/"+adminUserName+"/.ssh/authorized_keys"); //TODO: update for windows
configurationSet.setSshSettings(ssh);
return configurationSet;
}
private String getFingerprint(String serviceName){
String print="";
try {
print=computeManagementClient.getServiceCertificatesOperations().list(serviceName).getCertificates().get(0).getThumbprint();
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return print;
}
private ArrayList<Role> createRoleList(VMInstance a, VM vm,String storageAccountName, String storageContainer) throws Exception {
ArrayList<Role> roleList = new ArrayList<Role>();
Role role = new Role();
String roleName = a.getName();
//required
role.setRoleName(roleName);
role.setRoleType(VirtualMachineRoleType.PersistentVMRole.toString());
String size=VirtualMachineRoleSize.SMALL;
if(!vm.getProviderSpecificTypeName().equals(""))
size=vm.getProviderSpecificTypeName();
role.setRoleSize(size);//TODO: size with mincore
role.setProvisionGuestAgent(true);
ArrayList<ConfigurationSet> configurationSets = new ArrayList<ConfigurationSet>();
role.setConfigurationSets(configurationSets);
role.setOSVirtualHardDisk(createOSVHD(vm,storageAccountName,storageContainer));
roleList.add(role);
configurationSets.add(createConfigOS(a.getName(),vm));
configurationSets.add(createConfigNetwork());
return roleList;
}
private String getOSSourceImage(String args) throws Exception {
String sourceImageName = null;
VirtualMachineOSImageListResponse virtualMachineImageListResponse = computeManagementClient.getVirtualMachineOSImagesOperations().list();
ArrayList<VirtualMachineOSImageListResponse.VirtualMachineOSImage> virtualMachineOSImagelist = virtualMachineImageListResponse.getImages();
for (VirtualMachineOSImageListResponse.VirtualMachineOSImage virtualMachineImage : virtualMachineOSImagelist) {
if ((virtualMachineImage.getName().contains(args))) {
sourceImageName = virtualMachineImage.getName();
break;
}
}
return sourceImageName;
}
private void waitOperationToComplete(String requestId, long waitTimeBetweenTriesInSeconds, int maximumNumberOfTries) {
boolean operationCompleted = false;
int tryCount =0;
while ((!operationCompleted)&&(tryCount<maximumNumberOfTries))
{
OperationStatusResponse operationStatus = null;
try {
operationStatus = computeManagementClient.getOperationStatus(requestId);
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
if ((operationStatus.getStatus() == OperationStatus.Failed) || (operationStatus.getStatus() == OperationStatus.Succeeded))
{
operationCompleted = true;
}else{
try {
Thread.sleep(waitTimeBetweenTriesInSeconds * 1000);
tryCount ++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private List<VirtualMachineOSImageListResponse.VirtualMachineOSImage> getVirtualMachineOSImageList() throws Exception {
List<VirtualMachineOSImageListResponse.VirtualMachineOSImage> imageList = new ArrayList<VirtualMachineOSImageListResponse.VirtualMachineOSImage>();
VirtualMachineOSImageListResponse response = computeManagementClient.getVirtualMachineOSImagesOperations().list();
ArrayList<VirtualMachineOSImageListResponse.VirtualMachineOSImage> osImages = response.getImages();
for (VirtualMachineOSImageListResponse.VirtualMachineOSImage image: osImages) {
imageList.add(image);
}
return imageList;
}
public String getVirtualMachineStatus(String name) throws Exception {
String status = "";
ArrayList<RoleInstance> roleInstances = computeManagementClient.getDeploymentsOperations().getBySlot(name, DeploymentSlot.Production).getRoleInstances();
for (RoleInstance instance : roleInstances) {
if (instance.getRoleName().equals(name)) {
status = instance.getInstanceStatus();
break;
}
}
return status;
}
}