/*
* Copyright (c) 2012-2015 iWave Software LLC
* All Rights Reserved
*/
package com.emc.sa.service.vipr.compute;
import static com.emc.sa.service.ServiceParams.COMPUTE_IMAGE;
import static com.emc.sa.service.ServiceParams.COMPUTE_VIRTUAL_POOL;
import static com.emc.sa.service.ServiceParams.DATACENTER;
import static com.emc.sa.service.ServiceParams.DNS_SERVERS;
import static com.emc.sa.service.ServiceParams.GATEWAY;
import static com.emc.sa.service.ServiceParams.HLU;
import static com.emc.sa.service.ServiceParams.HOST_PASSWORD;
import static com.emc.sa.service.ServiceParams.MANAGEMENT_NETWORK;
import static com.emc.sa.service.ServiceParams.NAME;
import static com.emc.sa.service.ServiceParams.NETMASK;
import static com.emc.sa.service.ServiceParams.NTP_SERVER;
import static com.emc.sa.service.ServiceParams.PROJECT;
import static com.emc.sa.service.ServiceParams.SIZE_IN_GB;
import static com.emc.sa.service.ServiceParams.VCENTER;
import static com.emc.sa.service.ServiceParams.VIRTUAL_ARRAY;
import static com.emc.sa.service.ServiceParams.VIRTUAL_POOL;
import static com.emc.sa.util.ArrayUtil.safeArrayCopy;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.emc.sa.engine.ExecutionUtils;
import com.emc.sa.engine.bind.Bindable;
import com.emc.sa.engine.bind.Param;
import com.emc.sa.engine.service.Service;
import com.emc.sa.service.vipr.ViPRService;
import com.emc.sa.service.vipr.compute.ComputeUtils.FqdnToIpTable;
import com.emc.storageos.db.client.model.Cluster;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Vcenter;
import com.emc.storageos.db.client.model.VcenterDataCenter;
import com.emc.storageos.model.compute.OsInstallParam;
import com.emc.storageos.model.host.cluster.ClusterRestRep;
import com.emc.storageos.model.vpool.ComputeVirtualPoolRestRep;
import com.google.common.collect.ImmutableList;
@Service("CreateComputeCluster")
public class CreateComputeClusterService extends ViPRService {
@Param(PROJECT)
protected URI project;
@Param(NAME)
protected String name;
@Param(VIRTUAL_ARRAY)
protected URI virtualArray;
@Param(COMPUTE_IMAGE)
protected URI computeImage;
@Param(NETMASK)
protected String netmask;
@Param(GATEWAY)
protected String gateway;
@Param(NTP_SERVER)
protected String ntpServer;
@Param(MANAGEMENT_NETWORK)
protected String managementNetwork;
@Param(DNS_SERVERS)
protected String dnsServers;
@Param(VIRTUAL_POOL)
protected URI virtualPool;
@Param(COMPUTE_VIRTUAL_POOL)
protected URI computeVirtualPool;
@Param(SIZE_IN_GB)
protected Double size;
@Param(value = HLU, required = false)
protected Integer hlu;
@Param(HOST_PASSWORD)
protected String rootPassword;
@Bindable(itemType = FqdnToIpTable.class)
protected FqdnToIpTable[] fqdnToIps;
@Param(value = VCENTER, required = false)
protected URI vcenterId;
@Param(value = DATACENTER, required = false)
protected URI datacenterId;
private Cluster cluster = null;
private List<String> hostNames = null;
private List<String> hostIps = null;
private List<String> copyOfHostNames = null;
@Override
public void precheck() throws Exception {
StringBuilder preCheckErrors = new StringBuilder();
hostNames = ComputeUtils.getHostNamesFromFqdnToIps(fqdnToIps);
copyOfHostNames = ImmutableList.copyOf(hostNames);
hostIps = ComputeUtils.getIpsFromFqdnToIps(fqdnToIps);
List<String> existingHostNames = ComputeUtils.getHostNamesByName(getClient(), hostNames);
cluster = ComputeUtils.getCluster(name);
List<String> hostNamesInCluster = ComputeUtils.findHostNamesInCluster(cluster);
if ((cluster != null) && hostNamesInCluster.isEmpty()) {
preCheckErrors.append(ExecutionUtils.getMessage("compute.cluster.empty.cluster.exists"));
}
//TODO convert hostnames to lower case here before proceeding
// and also inform it to the user
if ((cluster != null) && !hostNames.containsAll(hostNamesInCluster)) {
preCheckErrors.append(ExecutionUtils.getMessage("compute.cluster.unknown.host"));
}
if (hostNames == null || hostNames.isEmpty() || hostIps == null || hostIps.isEmpty()) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.osinstall.host.required") + " ");
}
// Check for validity of host names and host Ips
for (String hostName : hostNames) {
if (!ComputeUtils.isValidHostIdentifier(hostName)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.hostname.invalid", hostName) + " ");
}
}
for (String hostIp : hostIps) {
if (!ComputeUtils.isValidIpAddress(hostIp)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.ip.invalid", hostIp) + " ");
}
}
if (!ComputeUtils.isCapacityAvailable(getClient(), virtualPool,
virtualArray, size, hostNames.size() - existingHostNames.size())) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.insufficient.storage.capacity") + " ");
}
if (!ComputeUtils.isComputePoolCapacityAvailable(getClient(), computeVirtualPool,
hostNames.size() - existingHostNames.size())) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.insufficient.compute.capacity") + " ");
}
if (!ComputeUtils.isValidIpAddress(netmask)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.invalid.netmask") + " ");
}
if (!ComputeUtils.isValidHostIdentifier(gateway)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.invalid.gateway") + " ");
}
if (ntpServer != null && ntpServer.trim().length() == 0) {
ntpServer = null;
}
else if (ntpServer != null && ntpServer.trim().length() > 0) {
// allowing user to specify comma separated list - use only use the first valid one
// TODO why do we take only the first NTP server? Check
String[] ntpServerList = ntpServer.split(",");
String validServer = null;
for (String ntpServerx : ntpServerList) {
if (ComputeUtils.isValidHostIdentifier(ntpServerx.trim())) {
validServer = ntpServerx.trim();
//TODO the break should be moved inside the 'if'
}
break;
}
if (validServer == null) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.invalid.ntp") + " ");
}
else {
ntpServer = validServer;
}
}
if (dnsServers != null && dnsServers.trim().length() == 0) {
dnsServers = null;
}
else if (dnsServers != null && dnsServers.trim().length() > 0) {
String[] dnsServerList = dnsServers.split(",");
for (String dnsServer : dnsServerList) {
if (!ComputeUtils.isValidIpAddress(dnsServer.trim()) && !ComputeUtils.isValidHostIdentifier(dnsServer.trim())) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.invalid.dns") + " ");
}
}
}
if (hostNamesInCluster != null && !hostNamesInCluster.isEmpty() && !existingHostNames.isEmpty()) {
for (String hostName : hostNamesInCluster) {
if (existingHostNames.contains(hostName)){
preCheckErrors.append(ExecutionUtils.getMessage("compute.cluster.hostname.already.in.cluster", hostName) + " ");
}
}
}
for (String existingHostName : existingHostNames) {
if (!hostNamesInCluster.contains(existingHostName)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.hosts.exists.elsewhere",
existingHostName) + " ");
}
}
if (vcenterId != null && datacenterId == null) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.datacenter.id.null") + " ");
}
ComputeVirtualPoolRestRep cvp = ComputeUtils.getComputeVirtualPool(getClient(), computeVirtualPool);
if (cvp.getServiceProfileTemplates().isEmpty()) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.service.profile.templates.null", cvp.getName()) + " ");
}
if (preCheckErrors.length() > 0) {
throw new IllegalStateException(preCheckErrors.toString() +
ComputeUtils.getContextErrors(getModelClient()));
}
}
@Override
public void execute() throws Exception {
// Note: creates ordered lists of hosts, bootVolumes & exports
// host[0] goes with bootVolume[0] and export[0], etc
// elements are set to null if they fail
Map<String, String> hostToIPs = new HashMap<String, String>();
//TODO move this to the precheck section
if (hostNames.size() != hostIps.size()) {
throw new IllegalStateException(ExecutionUtils.getMessage("compute.cluster.host.ip.mismatch"));
}
int index = 0;
for (String hostname : hostNames) {
hostToIPs.put(hostname, hostIps.get(index));
index++;
}
if (cluster == null) {
cluster = ComputeUtils.createCluster(name);
logInfo("compute.cluster.created", name);
} else {
// If the hostName already exists, we remove it from the hostnames list.
hostNames = ComputeUtils.removeExistingHosts(hostNames, cluster);
}
acquireClusterLock(cluster);
List<Host> hosts = ComputeUtils.createHosts(cluster, computeVirtualPool, hostNames, virtualArray);
for (Host host : hosts) {
acquireHostLock(host, cluster);
}
logInfo("compute.cluster.hosts.created", ComputeUtils.nonNull(hosts).size());
Map<Host, URI> hostToBootVolumeIdMap = ComputeUtils.makeBootVolumes(project, virtualArray, virtualPool, size, hosts,
getClient());
logInfo("compute.cluster.boot.volumes.created",
hostToBootVolumeIdMap != null ? ComputeUtils.nonNull(hostToBootVolumeIdMap.values()).size() : 0);
// Deactivate hosts with no boot volume, return list of hosts remaining.
hostToBootVolumeIdMap = ComputeUtils.deactivateHostsWithNoBootVolume(hostToBootVolumeIdMap, cluster);
// Export the boot volume, return a map of hosts and their EG IDs
Map<Host, URI> hostToEgIdMap = ComputeUtils.exportBootVols(hostToBootVolumeIdMap, project, virtualArray, hlu);
logInfo("compute.cluster.exports.created",
hostToEgIdMap != null ? ComputeUtils.nonNull(hostToEgIdMap.values()).size(): 0);
// Deactivate any hosts where the export failed, return list of hosts remaining
hostToBootVolumeIdMap = ComputeUtils.deactivateHostsWithNoExport(hostToBootVolumeIdMap, hostToEgIdMap, cluster);
// Set host boot volume ids, but do not set san boot targets. They will get set post os install.
hosts = ComputeUtils.setHostBootVolumes(hostToBootVolumeIdMap, false);
logInfo("compute.cluster.exports.installing.os");
installOSForHosts(hostToIPs, ComputeUtils.getHostNameBootVolume(hosts), hosts);
hosts = ComputeUtils.deactivateHostsWithNoOS(hosts);
logInfo("compute.cluster.exports.installed.os", ComputeUtils.nonNull(hosts).size());
ComputeUtils.addHostsToCluster(hosts, cluster);
hosts = ComputeUtils.deactivateHostsNotAddedToCluster(hosts, cluster);
// VBDU [DONE]: COP-28400, Potential DU if external host is added to vCenter cluster not under ViPR mgmt.
// ClusterService has a precheck to verify the matching environments before deactivating
try {
if (ComputeUtils.findHostNamesInCluster(cluster).isEmpty()) {
logInfo("compute.cluster.removing.empty.cluster");
ComputeUtils.deactivateCluster(cluster);
} else {
if (!ComputeUtils.nonNull(hosts).isEmpty()) {
pushToVcenter();
ComputeUtils.discoverHosts(hosts);
} else {
logWarn("compute.cluster.newly.provisioned.hosts.none");
}
}
} catch (Exception ex) {
logError(ex.getMessage());
setPartialSuccess();
}
String orderErrors = ComputeUtils.getOrderErrors(cluster, copyOfHostNames, computeImage, vcenterId);
if (orderErrors.length() > 0) { // fail order so user can resubmit
if (ComputeUtils.nonNull(hosts).isEmpty()) {
throw new IllegalStateException(
ExecutionUtils.getMessage("compute.cluster.order.incomplete", orderErrors));
} else {
logError("compute.cluster.order.incomplete", orderErrors);
// VBDU TODO: COP-28433, change criteria for partial success
setPartialSuccess();
}
}
}
public String getRootPassword() {
return rootPassword;
}
public void setRootPassword(String rootPassword) {
this.rootPassword = rootPassword;
}
private void installOSForHosts(Map<String, String> hostToIps, Map<String, URI> hostNameToBootVolumeMap, List<Host> createdHosts) {
Map<Host,OsInstallParam> osInstallParamMap = new HashMap<Host, OsInstallParam>();
for (Host host : createdHosts) {
if ((host != null) && (
(host.getType() == null) ||
host.getType().isEmpty() ||
host.getType().equals(Host.HostType.No_OS.name())
)) {
OsInstallParam param = new OsInstallParam();
String hostIp = hostToIps.get(host.getHostName());
param.setComputeImage(computeImage);
param.setHostName(host.getHostName());
param.setDnsServers(dnsServers);
param.setGateway(gateway);
param.setNetmask(netmask);
param.setHostIp(hostIp);
param.setVolume(hostNameToBootVolumeMap.get(host.getHostName()));
param.setManagementNetwork(managementNetwork);
param.setNtpServer(ntpServer);
param.setRootPassword(rootPassword);
osInstallParamMap.put(host,param);
}
else {
osInstallParamMap.put(host,null);
}
}
try {
ComputeUtils.installOsOnHosts(osInstallParamMap);
} catch (Exception e) {
logError(e.getMessage());
}
}
private void pushToVcenter() {
if (vcenterId != null) {
boolean isVCenterUpdate = false;
List<ClusterRestRep> clusters = getClient().clusters().getByDataCenter(datacenterId);
for (ClusterRestRep resp : clusters) {
if (cluster.getLabel().equals(resp.getName())) {
isVCenterUpdate = true;
break;
}
}
try {
Vcenter vcenter = null;
VcenterDataCenter dataCenter = null;
vcenter = ComputeUtils.getVcenter(vcenterId);
if (null != datacenterId) {
dataCenter = ComputeUtils.getVcenterDataCenter(datacenterId);
}
boolean status = true;
if (isVCenterUpdate) {
logInfo("vcenter.cluster.update", cluster.getLabel());
if (dataCenter == null) {
status = ComputeUtils.updateVcenterCluster(cluster, datacenterId);
} else {
status = ComputeUtils.updateVcenterCluster(cluster, dataCenter);
}
if (!status) {
throw new IllegalStateException(
ExecutionUtils.getMessage("vcenter.cluster.update.failed", cluster.getLabel()));
}
} else {
logInfo("compute.cluster.create.vcenter.cluster.datacenter",
(vcenter != null ? vcenter.getLabel() : vcenterId),
(dataCenter != null ? dataCenter.getLabel() : datacenterId));
if (dataCenter == null) {
status = ComputeUtils.createVcenterCluster(cluster, datacenterId);
} else {
status = ComputeUtils.createVcenterCluster(cluster, dataCenter);
}
if (!status) {
throw new IllegalStateException(
ExecutionUtils.getMessage("vcenter.cluster.create.failed", cluster.getLabel()));
}
}
} catch (Exception e) {
logError("compute.cluster.vcenter.sync.failed.corrective.user.message", cluster.getLabel());
logError("compute.cluster.vcenter.push.failed", e.getMessage());
throw e;
}
}
}
public void setProject(URI project) {
this.project = project;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public URI getVirtualArray() {
return virtualArray;
}
public void setVirtualArray(URI virtualArray) {
this.virtualArray = virtualArray;
}
public URI getComputeImage() {
return computeImage;
}
public void setComputeImage(URI computeImage) {
this.computeImage = computeImage;
}
public String getNetmask() {
return netmask;
}
public void setNetmask(String netmask) {
this.netmask = netmask;
}
public String getGateway() {
return gateway;
}
public void setGateway(String gateway) {
this.gateway = gateway;
}
public String getDnsServers() {
return dnsServers;
}
public void setDnsServers(String dnsServers) {
this.dnsServers = dnsServers;
}
public URI getVirtualPool() {
return virtualPool;
}
public void setVirtualPool(URI virtualPool) {
this.virtualPool = virtualPool;
}
public URI getComputeVirtualPool() {
return computeVirtualPool;
}
public void setComputeVirtualPool(URI computeVirtualPool) {
this.computeVirtualPool = computeVirtualPool;
}
public Double getSize() {
return size;
}
public void setSize(Double size) {
this.size = size;
}
public String getHostPassword() {
return rootPassword;
}
public void setHostPassword(String hostPassword) {
this.rootPassword = hostPassword;
}
public String getNtpServer() {
return ntpServer;
}
public void setNtpServer(String ntpServer) {
this.ntpServer = ntpServer;
}
public String getManagementNetwork() {
return managementNetwork;
}
public void setManagementNetwork(String managementNetwork) {
this.managementNetwork = managementNetwork;
}
public FqdnToIpTable[] getFqdnToIps() {
return safeArrayCopy(fqdnToIps);
}
public void setFqdnToIps(FqdnToIpTable[] fqdnToIps) {
this.fqdnToIps = safeArrayCopy(fqdnToIps);
}
public URI getVcenterId() {
return vcenterId;
}
public void setVcenterId(URI vcenterId) {
this.vcenterId = vcenterId;
}
public URI getDatacenterId() {
return datacenterId;
}
public void setDatacenterId(URI datacenterId) {
this.datacenterId = datacenterId;
}
public URI getProject() {
return project;
}
/**
* @return the cluster
*/
public Cluster getCluster() {
return cluster;
}
/**
* @param cluster the cluster to set
*/
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
/**
* @return the hostNames
*/
public List<String> getHostNames() {
return hostNames;
}
/**
* @param hostNames the hostNames to set
*/
public void setHostNames(List<String> hostNames) {
this.hostNames = hostNames;
}
/**
* @return the hostIps
*/
public List<String> getHostIps() {
return hostIps;
}
/**
* @param hostIps the hostIps to set
*/
public void setHostIps(List<String> hostIps) {
this.hostIps = hostIps;
}
/**
* @return the copyOfHostNames
*/
public List<String> getCopyOfHostNames() {
return copyOfHostNames;
}
/**
* @param copyOfHostNames the copyOfHostNames to set
*/
public void setCopyOfHostNames(List<String> copyOfHostNames) {
this.copyOfHostNames = copyOfHostNames;
}
}