/*
* 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_VIRTUAL_POOL;
import static com.emc.sa.service.ServiceParams.HLU;
import static com.emc.sa.service.ServiceParams.NAME;
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.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.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.FqdnTable;
import com.emc.storageos.db.client.model.Cluster;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.model.vpool.ComputeVirtualPoolRestRep;
import com.google.common.collect.ImmutableList;
@Service("CreateBareMetalCluster")
public class CreateBareMetalClusterService extends ViPRService {
@Param(PROJECT)
protected URI project;
@Param(NAME)
protected String name;
@Param(VIRTUAL_ARRAY)
protected URI virtualArray;
@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;
@Bindable(itemType = FqdnTable.class)
protected FqdnTable[] fqdnValues;
private Cluster cluster = null;
private List<String> hostNames = null;
private List<String> copyOfHostNames = null;
@Override
public void precheck() throws Exception {
StringBuilder preCheckErrors = new StringBuilder();
hostNames = ComputeUtils.getHostNamesFromFqdn(fqdnValues);
copyOfHostNames = ImmutableList.copyOf(hostNames);
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"));
}
if ((cluster != null) && !hostNames.containsAll(hostNamesInCluster)) {
preCheckErrors.append(ExecutionUtils.getMessage("compute.cluster.unknown.host"));
}
if (hostNames == null || hostNames.isEmpty()) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.baremetal.hostname.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) + " ");
}
}
if (hostNames != null && !hostNames.isEmpty() && !existingHostNames.isEmpty() &&
hostNamesInCluster.containsAll(existingHostNames) && (hostNames.size() == hostNamesInCluster.size())) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.host.already.in.cluster") + " ");
}
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) + " ");
}
}
}
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") + " ");
}
for (String existingHostName : existingHostNames) {
if (!hostNamesInCluster.contains(existingHostName)) {
preCheckErrors.append(
ExecutionUtils.getMessage("compute.cluster.hosts.exists.elsewhere",
existingHostName) + " ");
}
}
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
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 and set san boot targets.
hosts = ComputeUtils.setHostBootVolumes(hostToBootVolumeIdMap, true);
ComputeUtils.addHostsToCluster(hosts, cluster);
hosts = ComputeUtils.deactivateHostsNotAddedToCluster(hosts, cluster);
if (ComputeUtils.findHostNamesInCluster(cluster).isEmpty()) {
logInfo("compute.cluster.removing.empty.cluster");
ComputeUtils.deactivateCluster(cluster);
}
String orderErrors = ComputeUtils.getOrderErrors(cluster, copyOfHostNames, null, null);
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);
setPartialSuccess();
}
}
}
public URI getProject() {
return project;
}
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 getVirtualPool() {
return virtualPool;
}
public void setVirtualPool(URI virtualPool) {
this.virtualPool = virtualPool;
}
public URI getVirtualComputePool() {
return computeVirtualPool;
}
public void setVirtualComputePool(URI computeVirtualPool) {
this.computeVirtualPool = computeVirtualPool;
}
public Double getSize() {
return size;
}
public void setSize(Double size) {
this.size = size;
}
public FqdnTable[] getFqdnValues() {
return safeArrayCopy(fqdnValues);
}
public void setFqdnValues(FqdnTable[] fqdnValues) {
this.fqdnValues = safeArrayCopy(fqdnValues);
}
public Cluster getCluster() {
return cluster;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public List<String> getHostNames() {
return hostNames;
}
public void setHostNames(List<String> hostNames) {
this.hostNames = hostNames;
}
/**
* @return the copyOfHostNames
*/
public List<String> getCopyOfHostNames() {
return copyOfHostNames;
}
/**
* @param copyOfHostNames the copyOfHostNames to set
*/
public void setCopyOfHostNames(List<String> copyOfHostNames) {
this.copyOfHostNames = copyOfHostNames;
}
}