package com.sixsq.slipstream.connector;
/*
* +=================================================================+
* SlipStream Server (WAR)
* =====
* Copyright (C) 2014 SixSq Sarl (sixsq.com)
* =====
* 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.
* -=================================================================-
*/
import com.sixsq.slipstream.configuration.Configuration;
import com.sixsq.slipstream.cookie.CookieUtils;
import com.sixsq.slipstream.credentials.Credentials;
import com.sixsq.slipstream.exceptions.*;
import com.sixsq.slipstream.persistence.*;
import com.sixsq.slipstream.run.RuntimeParameterMediator;
import com.sixsq.slipstream.util.FileUtil;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.logging.Logger;
public abstract class ConnectorBase implements Connector {
abstract public String getCloudServiceName();
abstract public Run launch(Run run, User user) throws SlipStreamException;
abstract public Credentials getCredentials(User user);
abstract public void terminate(Run run, User user) throws SlipStreamException;
abstract public Map<String, Properties> describeInstances(User user, int timeout) throws SlipStreamException;
public static final String VM_STATE = "state";
public static final String VM_IP = "ip";
public static final String VM_CPU = "cpu";
public static final String VM_RAM = "ram";
public static final String VM_DISK = "disk";
public static final String VM_INSTANCE_TYPE = "instance-type";
private static Logger log = Logger.getLogger(ConnectorBase.class.toString());
protected static Logger getLog() {
return log;
}
private static final String MACHINE_INSTANCE_ID_NAME = Run.MACHINE_NAME_PREFIX + RuntimeParameter.INSTANCE_ID_KEY;
protected static final String MACHINE_INSTANCE_HOSTNAME = Run.MACHINE_NAME_PREFIX + RuntimeParameter.HOSTNAME_KEY;
protected static final String MACHINE_INSTANCE_URL_SSH = Run.MACHINE_NAME_PREFIX + RuntimeParameter.URL_SSH_KEY;
// TODO: shouldn't be there!!
private Map<String, Map<String, String>> extraDisksInfo = new HashMap<String, Map<String, String>>();
private File tempSshKeyFile;
private final String instanceName;
/**
* Is a deployment or a build image, where both cases require an orchestrator
*/
public static boolean isInOrchestrationContext(Run run) {
return run.getType() == RunType.Orchestration || run.getType() == RunType.Machine;
}
public ConnectorBase(String instanceName) {
this.instanceName = instanceName;
}
protected static Credentials getCredentialsObject(User user) throws ConfigurationException, ValidationException {
return ConnectorFactory.getCurrentConnector(user).getCredentials(user);
}
protected String getImageId(Run run, User user) throws ConfigurationException, ValidationException {
String imageId;
if (isInOrchestrationContext(run)) {
imageId = getOrchestratorImageId(user);
} else {
String cloudService = run.getCloudServiceNameForNode(Run.MACHINE_NAME);
imageId = ((ImageModule) run.getModule()).extractBaseImageId(cloudService);
}
return imageId;
}
protected String getOrchestratorImageId(User user) throws ValidationException {
return getCloudParameterValue(user, UserParametersFactoryBase.ORCHESTRATOR_IMAGEID_PARAMETER_NAME);
}
protected String getCloudParameterValue(User user, String paramName)
throws ConfigurationException, ValidationException {
String qualifiedParamName = constructKey(paramName);
String paramValue = user.getParameterValue(qualifiedParamName, null);
if (paramValue == null) {
throw (new ConfigurationException("Missing parameter '" + qualifiedParamName + "'."));
}
return paramValue;
}
protected String getCloudParameterValue(User user, String paramName, String defaultValue)
throws ValidationException {
String qualifiedParamName = constructKey(paramName);
return user.getParameterValue(qualifiedParamName, defaultValue);
}
protected String getDefaultCloudServiceName(User user) throws ValidationException {
return user.getDefaultCloudService();
}
public String getConnectorInstanceName() {
return instanceName;
}
public void checkCredentials(Credentials credentials) throws InvalidElementException {
if (credentials.getKey() == null) {
throw (new InvalidElementException("Missing Cloud account key."));
}
if (credentials.getSecret() == null) {
throw (new InvalidElementException("Missing Cloud account secret."));
}
}
protected Run updateInstanceIdAndIpOnRun(Run run, String instanceId, String ipAddress)
throws NotFoundException, ValidationException, ServerExecutionEnginePluginException {
return updateInstanceIdAndIpOnRun(run, instanceId, ipAddress, getOrchestratorName(run));
}
protected Run updateInstanceIdAndIpOnRun(Run run, String instanceId, String ipAddress, String orchestratorName)
throws NotFoundException, ValidationException, ServerExecutionEnginePluginException {
if (isInOrchestrationContext(run)) {
updateOrchestratorInstanceIdOnRun(run, instanceId, orchestratorName);
updateOrchestratorInstanceIpOnRun(run, ipAddress, orchestratorName);
updateOrchestratorUrlSshOnRun(run, ipAddress, orchestratorName);
} else {
updateMachineInstanceIdOnRun(run, instanceId);
updateMachineInstanceIpOnRun(run, ipAddress);
updateMachineInstanceUrlSshOnRun(run, ipAddress);
}
return run;
}
private void updateOrchestratorInstanceIdOnRun(Run run, String instanceId, String orchestratorName) throws
NotFoundException, ValidationException {
String orchestratorInstanceIdName = orchestratorName + RuntimeParameter.NODE_PROPERTY_SEPARATOR +
RuntimeParameter.INSTANCE_ID_KEY;
setRuntimeParameterValue(orchestratorInstanceIdName, instanceId, run);
}
private void updateOrchestratorInstanceIpOnRun(Run run, String instanceHostname, String orchestratorName) throws
NotFoundException, ValidationException {
String machineInstanceHostname = orchestratorName + RuntimeParameter.NODE_PROPERTY_SEPARATOR +
RuntimeParameter.HOSTNAME_KEY;
setRuntimeParameterValue(machineInstanceHostname, instanceHostname, run);
}
private void updateOrchestratorUrlSshOnRun(Run run, String instanceHostname, String orchestratorName) throws
NotFoundException, ValidationException {
String machineInstanceUrlSshKey = orchestratorName
.trim() + RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter.URL_SSH_KEY;
String url = getSshUrl(run, instanceHostname);
setRuntimeParameterValue(machineInstanceUrlSshKey, url, run);
}
private void setRuntimeParameterValue(String key, String value, Run run) {
RuntimeParameter p = RuntimeParameter.loadFromUuidAndKey(run.getUuid(), key);
p.setValue(value);
RuntimeParameterMediator.processSpecialValue(p);
p.store();
}
private void updateMachineInstanceIdOnRun(Run run, String instanceId) throws NotFoundException,
ValidationException {
setRuntimeParameterValue(MACHINE_INSTANCE_ID_NAME, instanceId, run);
}
private void updateMachineInstanceIpOnRun(Run run, String instanceHostname) throws NotFoundException,
ValidationException {
setRuntimeParameterValue(MACHINE_INSTANCE_HOSTNAME, instanceHostname, run);
}
private void updateMachineInstanceUrlSshOnRun(Run run, String instanceHostname) throws NotFoundException,
ValidationException {
String url = getSshUrl(run, instanceHostname);
setRuntimeParameterValue(MACHINE_INSTANCE_URL_SSH, url, run);
}
private String getSshUrl(Run run, String instanceHostname) {
String user = null;
try {
user = getLoginUsername(run);
} catch (SlipStreamClientException e) {
user = "root";
} catch (ConfigurationException e) {
user = "root";
}
return String.format("ssh://%s@%s", user.trim(), instanceHostname.trim());
}
protected void defineExtraDisk(String name, String description, String regex, String regexError) {
Map<String, String> diskInfo = new HashMap<String, String>();
diskInfo.put(ExtraDisk.EXTRADISK_KEY_DESCRIPTION, description);
diskInfo.put(ExtraDisk.EXTRADISK_KEY_REGEX, regex);
diskInfo.put(ExtraDisk.EXTRADISK_KEY_REGEXERROR, regexError);
extraDisksInfo.put(name, Collections.unmodifiableMap(diskInfo));
}
public List<ExtraDisk> getExtraDisks() {
List<ExtraDisk> disks = new ArrayList<ExtraDisk>();
for (Map.Entry<String, Map<String, String>> entry : extraDisksInfo.entrySet()) {
String name = entry.getKey();
Map<String, String> valuesList = entry.getValue();
ExtraDisk disk = new ExtraDisk(name, valuesList.get(ExtraDisk.EXTRADISK_KEY_DESCRIPTION));
disks.add(disk);
}
return disks;
}
public void validateExtraDiskParameter(String name, String param) throws ValidationException {
if (param == null || param.isEmpty()) {
return;
}
Map<String, String> diskInfo = extraDisksInfo.get(name);
if (!param.matches(diskInfo.get(ExtraDisk.EXTRADISK_KEY_REGEX))) {
throw (new ValidationException(diskInfo.get(ExtraDisk.EXTRADISK_KEY_REGEXERROR)));
}
}
protected String getPrivateSshKey() throws ConfigurationException, ValidationException {
String privateSshKeyFile = getPrivateSshKeyFileName();
return FileUtil.fileToString(privateSshKeyFile);
}
protected String getPrivateSshKeyFileName() throws ConfigurationException, ValidationException {
String privateSshKeyFile = Configuration.getInstance()
.getProperty(ServiceConfiguration.CLOUD_CONNECTOR_ORCHESTRATOR_PRIVATESSHKEY);
return privateSshKeyFile;
}
public static String getServerPublicSshKeyFilename() throws ConfigurationException, ValidationException {
return Configuration.getInstance().getProperty(ServiceConfiguration.CLOUD_CONNECTOR_ORCHESTRATOR_PUBLICSSHKEY);
}
private String getUserPublicSshKey(User user) throws ValidationException, IOException {
return user.getParameter(
ExecutionControlUserParametersFactory.CATEGORY + "." + UserParametersFactoryBase.SSHKEY_PARAMETER_NAME)
.getValue();
}
private String getUserOrServerPublicSshKey(User user) throws ValidationException, IOException {
String userPublicSshKey = getUserPublicSshKey(user);
if (userPublicSshKey == null || userPublicSshKey.trim().isEmpty()) {
return FileUtil.fileToString(getServerPublicSshKeyFilename());
} else {
return userPublicSshKey;
}
}
protected String getPublicSshKey(Run run, User user) throws ValidationException, IOException {
if (run.getType() == RunType.Run) {
return getUserOrServerPublicSshKey(user);
} else {
String publicSshKeyFile = getPublicSshKeyFileName(run, user);
return FileUtil.fileToString(publicSshKeyFile);
}
}
protected String getPublicSshKeyFileName(Run run, User user) throws IOException, ValidationException {
String publicSshKeyFilename;
if (run.getType() == RunType.Run) {
tempSshKeyFile = File.createTempFile("sshkey", ".tmp");
BufferedWriter out = new BufferedWriter(new FileWriter(tempSshKeyFile));
String sshPublicKey = getUserOrServerPublicSshKey(user);
out.write(sshPublicKey);
out.close();
publicSshKeyFilename = tempSshKeyFile.getPath();
} else {
publicSshKeyFilename = getServerPublicSshKeyFilename();
}
return publicSshKeyFilename;
}
protected void deleteTempSshKeyFile() {
if (tempSshKeyFile != null) {
if (!tempSshKeyFile.delete()) {
getLog().warning("cannot delete SSH key file: " + tempSshKeyFile.getPath());
}
}
}
protected List<String> getCloudNodeInstanceIds(Run run) throws NotFoundException, ValidationException {
List<String> ids = new ArrayList<String>();
for (String nodeName : run.getNodeInstanceNamesList()) {
nodeName = nodeName.trim();
String idKey = nodeName + RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter.INSTANCE_ID_KEY;
String cloudServiceKey = nodeName + RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter
.CLOUD_SERVICE_NAME;
String id = run.getRuntimeParameterValueIgnoreAbort(idKey);
String cloudService = run.getRuntimeParameterValueIgnoreAbort(cloudServiceKey);
if (id != null && !id.equals("") && this.getConnectorInstanceName().equals(cloudService)) {
ids.add(id);
}
}
return ids;
}
public Map<String, UserParameter> getUserParametersTemplate() throws ValidationException {
throw (new NotImplementedException());
}
public Map<String, ServiceConfigurationParameter> getServiceConfigurationParametersTemplate() throws
ValidationException {
throw (new NotImplementedException());
}
/**
* Implement in connector class if extra parameters are required to be set during
* XML serialization of the User object when UserResource is called to get XML.
*/
public void setExtraUserParameters(User user) throws ValidationException {
}
public Map<String, ModuleParameter> getImageParametersTemplate() throws ValidationException {
return new HashMap<String, ModuleParameter>();
}
protected String generateCookie(String identifier, String runId) {
Properties extraProperties = new Properties();
extraProperties.put(CookieUtils.COOKIE_IS_MACHINE, "true");
extraProperties.put(CookieUtils.COOKIE_RUN_ID, runId);
extraProperties.put(CookieUtils.COOKIE_EXPIRY_DATE, "0");
String cookie = CookieUtils.createCookie(identifier, getConnectorInstanceName(), extraProperties);
getLog().info("Generated cookie = " + cookie);
return cookie;
}
protected String getCookieForEnvironmentVariable(String identifier, String runId) {
return "\"" + generateCookie(identifier, runId) + "\"";
}
protected abstract String constructKey(String key) throws ValidationException;
protected String getVerboseParameterValue(User user) {
return user.getParameterValue(Parameter.constructKey(ExecutionControlUserParametersFactory.VERBOSITY_LEVEL),
ExecutionControlUserParametersFactory.VERBOSITY_LEVEL_DEFAULT
);
}
protected String getInstanceName(Run run) {
return (isInOrchestrationContext(run)) ? getOrchestratorName(run) : Run.MACHINE_NAME;
}
public String getOrchestratorName(Run run) {
String orchestratorName = Run.ORCHESTRATOR_NAME;
if (isInOrchestrationContext(run)) {
orchestratorName = Run.constructOrchestratorName(getConnectorInstanceName());
}
return orchestratorName;
}
protected String getInstanceType() {
return null;
}
protected String getInstanceType(ImageModule image) throws ValidationException {
return getParameterValue(ImageModule.INSTANCE_TYPE_KEY, image);
}
protected String getCpu(ImageModule image) throws ValidationException {
return getParameterValue(ImageModule.CPU_KEY, image);
}
protected String getRam(ImageModule image) throws ValidationException {
return getParameterValue(ImageModule.RAM_KEY, image);
}
protected String getRootDisk(ImageModule image) throws ValidationException {
return image.getParameterValue(ImageModule.DISK_PARAM, null);
}
protected String getExtraDiskVolatile(ImageModule image) throws ValidationException {
return image.getParameterValue(ImageModule.EXTRADISK_VOLATILE_PARAM, null);
}
protected String getParameterValue(String parameterName, ImageModule image) throws ValidationException {
ModuleParameter parameter = image.getParameter(constructKey(parameterName));
return parameter == null ? null : parameter.getValue();
}
protected String getKey(User user) {
try {
return getCredentials(user).getKey();
} catch (InvalidElementException e) {
return null;
} catch (ConfigurationException e) {
return null;
}
}
protected String getSecret(User user) {
try {
return getCredentials(user).getSecret();
} catch (InvalidElementException e) {
return null;
} catch (ConfigurationException e) {
return null;
}
}
@Override
public boolean isCredentialsSet(User user) {
String key = getKey(user);
String secret = getSecret(user);
return !(key == null || "".equals(key) || secret == null || "".equals(secret));
}
protected String getLoginUsername(Run run) throws ConfigurationException, ValidationException {
if (isInOrchestrationContext(run)) {
return getOrchestratorImageLoginUsername();
} else {
return getMachineImageLoginUsername(run);
}
}
private String getOrchestratorImageLoginUsername() throws ConfigurationException, ValidationException {
String key = constructKey(SystemConfigurationParametersFactoryBase.ORCHESTRATOR_USERNAME_KEY);
return Configuration.getInstance().getRequiredProperty(key);
}
private String getMachineImageLoginUsername(Run run) throws ValidationException {
ImageModule machine = ImageModule.load(run.getModuleResourceUrl());
String username = machine.getLoginUser();
if (username == null) {
throw new ValidationException("Module " + machine.getName() + " is missing login username");
}
return username;
}
protected String getLoginPassword(Run run) throws ConfigurationException, ValidationException {
if (isInOrchestrationContext(run)) {
return getOrchestratorImageLoginPassword();
} else {
return getMachineImageLoginPassword(run);
}
}
private String getOrchestratorImageLoginPassword() throws ConfigurationException, ValidationException {
String key = constructKey(SystemConfigurationParametersFactoryBase.ORCHESTRATOR_PASSWORD_KEY);
return Configuration.getInstance().getRequiredProperty(key);
}
private String getMachineImageLoginPassword(Run run) throws ValidationException {
ImageModule machine = ImageModule.load(run.getModuleResourceUrl());
String password = machine.getParameterValue(constructKey(ImageModule.LOGINPASSWORD_KEY), null);
if (password == null) {
throw new ValidationException("Module " + machine.getName() + " is missing ssh login password");
}
return password;
}
protected String getEndpoint(User user) throws ValidationException {
String paramName = getConnectorInstanceName() + "." + UserParametersFactoryBase.ENDPOINT_PARAMETER_NAME;
UserParameter endpointParam = user.getParameter(paramName);
if (endpointParam != null) {
return endpointParam.getValue();
}
throw new ValidationException("Failed to get endpoint. Parameter not found: " + paramName);
}
@Override
public boolean isVmUsable(String vmState) {
return "running".equalsIgnoreCase(vmState)
|| "active".equalsIgnoreCase(vmState)
|| "on".equalsIgnoreCase(vmState);
}
}