/**
* 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.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import org.apache.commons.io.FileUtils;
import org.cloudml.core.ComponentInstance;
import org.cloudml.core.Property;
import org.cloudml.core.VM;
import org.cloudml.core.VMInstance;
import org.jclouds.ContextBuilder;
import org.jclouds.cloudsigma2.domain.*;
import org.jclouds.cloudsigma2.CloudSigma2Api;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.*;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.config.NullLoggingModule;
import org.jclouds.sshj.config.SshjSshClientModule;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by nicolasf on 02.02.15.
*/
public class CloudSigmaConnector implements Connector {
private static final Logger journal = Logger.getLogger(CloudSigmaConnector.class.getName());
private ComputeServiceContext computeContext;
private ComputeService compute;
private String provider;
private HashMap<String,Object> runtimeInformation;
private CloudSigma2Api cloudSigmaApi;
public CloudSigmaConnector(String provider,String login,String secretKey){
journal.log(Level.INFO, ">> Connecting to "+provider+" ...");
Iterable<Module> modules = ImmutableSet.<Module> of(
new SshjSshClientModule(),
new NullLoggingModule());
ContextBuilder builder = ContextBuilder.newBuilder(provider)
.credentials(login, secretKey)
.modules(modules);
journal.log(Level.INFO, ">> Authenticating ...");
this.provider = provider;
cloudSigmaApi=builder.buildApi(CloudSigma2Api.class);
}
public FluentIterable<ServerInfo> listOfServers(){
return cloudSigmaApi.listServersInfo().concat();
}
@Override
public void execCommand(String id, String command, String login, String key) {
for(IP ip: cloudSigmaApi.listIPs().concat()){
if(ip.getServer().getUuid().equals(id)){
SSHConnector sc=new SSHConnector(key, login, ip.getUuid());
sc.execCommandSsh(command);
return;
}
}
}
private String findDriveByName(String name){
FluentIterable<LibraryDrive> list= cloudSigmaApi.listLibraryDrives().concat();
for(DriveInfo d: list){
if(d.getName().contains(name)){
return d.getUuid();
}
}
return null;
}
private String readSshKey(String keyPath){
BufferedReader br;
String key="";
try {
br=new BufferedReader(new FileReader(keyPath));
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
key = sb.toString();
} catch (IOException e) {
journal.log(Level.SEVERE, e.getMessage());
}
return key;
}
@Override
public HashMap<String, Object> createInstance(VMInstance a) {
VM vm = a.getType();
runtimeInformation=new HashMap<String, Object>();
ComponentInstance.State state = ComponentInstance.State.UNRECOGNIZED;
// First try to find the desired template drive in MyDrives
DriveInfo driveToClone =null;
FluentIterable<DriveInfo> drives = cloudSigmaApi.listDrivesInfo().concat();
for(DriveInfo d : drives){
if(d.getName().contains(vm.getImageId()) &&
d.getStatus() == DriveStatus.UNMOUNTED){
driveToClone =d;
break;
}
}
if(null == driveToClone) {
// We had no luck with MyDrives, so try the market place / library
FluentIterable<LibraryDrive> libraryDrives = cloudSigmaApi.listLibraryDrives().concat();
for(DriveInfo d : libraryDrives){
if(d.getName().contains(vm.getImageId())){
driveToClone =d;
break;
}
}
}
// Next step is to clone the drive and identify it in our drive list
LibraryDrive driveProperties = new LibraryDrive.Builder()
.name(a.getName() + "-root")
.description("root drive for " + vm.getImageId())
.media(MediaType.DISK)
.build();
cloudSigmaApi.cloneLibraryDrive(driveToClone.getUuid(), driveProperties);
try {//TODO: to be removed
Thread.sleep(60000);
} catch (InterruptedException e) {
journal.log(Level.SEVERE, e.getMessage());
}
FluentIterable<DriveInfo> concat = cloudSigmaApi.listDrivesInfo().concat();
DriveInfo cloned=null;
for(DriveInfo d : concat){
if(d.getName().equals(a.getName() + "-root") &&
d.getStatus() == DriveStatus.UNMOUNTED){
cloned = d;
break;
}
}
FirewallPolicy p = null;
for(FirewallPolicy fp :cloudSigmaApi.listFirewallPolicies().concat()){
if(fp.getName().toLowerCase().equals(vm.getSecurityGroup().toLowerCase())){
p=fp;
}
}
ServerDrive drive=cloned.toServerDrive(1,"0:1", DeviceEmulationType.VIRTIO);
NIC nic=new NIC.Builder()
.firewallPolicy(p)
.ipV4Configuration(new IPConfiguration(IPConfigurationType.DHCP, null))
.build();
Map<String,String> m=new HashMap<String, String>();
m.put("ssh_public_key",readSshKey(vm.getSshKey()));
org.jclouds.cloudsigma2.domain.ServerInfo serverToCreate = new org.jclouds.cloudsigma2.domain.ServerInfo.Builder()
.name(a.getName())
.memory(BigInteger.valueOf(vm.getMinRam()).multiply(BigInteger.valueOf(1024 * 1024)))
.vncPassword("cloudml")
.cpu(vm.getMinCores() * 2600)
.nics(ImmutableList.of(nic))
.meta(m)
.drives(ImmutableList.of(drive))
.build();
org.jclouds.cloudsigma2.domain.ServerInfo createdServer = cloudSigmaApi.createServer(serverToCreate);
cloudSigmaApi.startServer(createdServer.getUuid());
if(createdServer.getStatus().value().equals("Running"))
state=ComponentInstance.State.RUNNING;
a.setId(createdServer.getUuid());
for(IP ip: cloudSigmaApi.listIPs().concat()){
if(ip.getServer().getUuid().equals(a.getId())){
runtimeInformation.put("publicAddress", ip.getUuid());
//wait for the VM to be accessible
if (vm.getOs().toLowerCase().contains("windows")) {
while(!PowerShellConnector.checkConnectivity(ip.getUuid())){
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
journal.log(Level.SEVERE, e.getMessage());
}
}
} else {
SSHConnector sc=new SSHConnector(vm.getPrivateKey(), "ubuntu", ip.getUuid());
while(!sc.checkConnectivity()){
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
journal.log(Level.SEVERE, e.getMessage());
}
}
}
}
}
runtimeInformation.put("status", state);
return runtimeInformation;
}
@Override
public void destroyVM(String id) {
journal.log(Level.INFO, ">> Stopping VM: "+id);
cloudSigmaApi.stopServer(id);
while(cloudSigmaApi.getServerInfo(id).getStatus() != ServerStatus.STOPPED) {
try {
journal.log(Level.INFO, ">> Waiting for VM: "+id+ " being stopped");
Thread.sleep(15000);
} catch (InterruptedException e) {
journal.log(Level.SEVERE, e.getMessage());
}
}
journal.log(Level.INFO, ">> Terminating VM: "+id);
cloudSigmaApi.deleteServer(id);
}
@Override
public void closeConnection() {
try {
cloudSigmaApi.close();
} catch (IOException e) {
journal.log(Level.SEVERE, e.getMessage());
}
journal.log(Level.INFO, ">> Closing connection ...");
}
public ComputeMetadata getVMByName(String name){
for(ComputeMetadata n : compute.listNodes()){
if(n.getName() != null && n.getName().equals(name))
return n;
}
return null;
}
@Override
public void updateVMMetadata(VMInstance a) {
ComputeMetadata cm= getVMByName(a.getName());
if(cm != null){
a.setPublicAddress(getVMById(cm.getId()).getPublicAddresses().iterator().next());
a.setId(cm.getId());
}
}
public NodeMetadata getVMById(String id){
return compute.getNodeMetadata(id);
}
private org.jclouds.domain.LoginCredentials.Builder initCredentials(String login, String key){
String contentKey;
org.jclouds.domain.LoginCredentials.Builder b= LoginCredentials.builder();
File f = new File(key);
if(f.exists() && !f.isDirectory()) {
try {
contentKey = FileUtils.readFileToString(new File(key));
b.user(login);
b.noPassword();
b.privateKey(contentKey);
} catch (IOException e) {
journal.log(Level.SEVERE, e.getMessage());
}
}else{
b.user(login);
b.noPassword();
b.privateKey(key);
}
return b;
}
@Override
public void uploadFile(String sourcePath, String destinationPath, String nodeId, String login, String key) {
for(IP ip: cloudSigmaApi.listIPs().concat()){
if(ip.getServer().getUuid().equals(nodeId)){
SSHConnector sc=new SSHConnector(key, login, ip.getUuid());
sc.upload(sourcePath,destinationPath);
return;
}
}
}
@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());
cloudSigmaApi.startServer(a.getId());
}
@Override
public void stopVM(VMInstance a) {
journal.log(Level.INFO, ">> Stopping VM: "+a.getName());
cloudSigmaApi.stopServer(a.getId());
}
}