/***************************************************************************
* Copyright (c) 2012-2015 VMware, Inc. All Rights Reserved.
* 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.
***************************************************************************/
package com.vmware.bdd.service.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.vmware.aurora.vc.VcCluster;
import com.vmware.bdd.apitypes.*;
import com.vmware.bdd.exception.ClusterConfigException;
import com.vmware.bdd.manager.*;
import com.vmware.bdd.service.resmgmt.IVcInventorySyncService;
import com.vmware.bdd.service.resmgmt.sync.filter.VcResourceFilters;
import com.vmware.bdd.spectypes.*;
import com.vmware.bdd.spectypes.DiskSpec;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.Gson;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.vmware.aurora.composition.CreateVMFolderSP;
import com.vmware.aurora.composition.DeleteVMFolderSP;
import com.vmware.aurora.composition.DiskSchema;
import com.vmware.aurora.composition.DiskSchema.Disk;
import com.vmware.aurora.composition.NetworkSchema.Network;
import com.vmware.aurora.composition.VmSchema;
import com.vmware.aurora.composition.concurrent.ExecutionResult;
import com.vmware.aurora.composition.concurrent.Scheduler;
import com.vmware.aurora.global.Configuration;
import com.vmware.aurora.util.worker.CmsWorker;
import com.vmware.aurora.vc.*;
import com.vmware.aurora.vc.vcevent.VcEventRouter;
import com.vmware.aurora.vc.vcservice.VcContext;
import com.vmware.aurora.vc.vcservice.VcSession;
import com.vmware.bdd.apitypes.ClusterCreate;
import com.vmware.bdd.apitypes.IpBlock;
import com.vmware.bdd.apitypes.NetworkAdd;
import com.vmware.bdd.apitypes.NodeGroupCreate;
import com.vmware.bdd.apitypes.Priority;
import com.vmware.bdd.apitypes.StorageRead.DiskType;
import com.vmware.bdd.clone.spec.VmCreateResult;
import com.vmware.bdd.clone.spec.VmCreateSpec;
import com.vmware.bdd.dal.IResourcePoolDAO;
import com.vmware.bdd.entity.ClusterEntity;
import com.vmware.bdd.entity.NicEntity;
import com.vmware.bdd.entity.NodeEntity;
import com.vmware.bdd.entity.resmgmt.ResourceReservation;
import com.vmware.bdd.exception.BddException;
import com.vmware.bdd.exception.ClusteringServiceException;
import com.vmware.bdd.exception.VcProviderException;
import com.vmware.bdd.manager.intf.IClusterEntityManager;
import com.vmware.bdd.manager.intf.IConcurrentLockedClusterEntityManager;
import com.vmware.bdd.placement.Container;
import com.vmware.bdd.placement.entity.AbstractDatacenter.AbstractHost;
import com.vmware.bdd.placement.entity.BaseNode;
import com.vmware.bdd.placement.exception.PlacementException;
import com.vmware.bdd.placement.interfaces.IPlacementService;
import com.vmware.bdd.placement.util.ContainerToStringHelper;
import com.vmware.bdd.placement.util.PlacementUtil;
import com.vmware.bdd.service.ClusterNodeUpdator;
import com.vmware.bdd.service.IClusterInitializerService;
import com.vmware.bdd.service.IClusteringService;
import com.vmware.bdd.service.event.VmEventManager;
import com.vmware.bdd.service.job.NodeOperationStatus;
import com.vmware.bdd.service.job.StatusUpdater;
import com.vmware.bdd.service.resmgmt.INetworkService;
import com.vmware.bdd.service.resmgmt.INodeTemplateService;
import com.vmware.bdd.service.resmgmt.IResourceService;
import com.vmware.bdd.service.sp.*;
import com.vmware.bdd.service.utils.VcResourceUtils;
import com.vmware.bdd.software.mgmt.plugin.intf.SoftwareManager;
import com.vmware.bdd.specpolicy.GuestMachineIdSpec;
import com.vmware.bdd.utils.AuAssert;
import com.vmware.bdd.utils.ClusterUtil;
import com.vmware.bdd.utils.CommonUtil;
import com.vmware.bdd.utils.ConfigInfo;
import com.vmware.bdd.utils.Constants;
import com.vmware.bdd.utils.JobUtils;
import com.vmware.bdd.utils.VcVmUtil;
import com.vmware.bdd.utils.Version;
import com.vmware.bdd.vmclone.service.intf.IClusterCloneService;
import com.vmware.vim.binding.vim.Folder;
import com.vmware.vim.binding.vim.vm.device.VirtualDevice;
import com.vmware.vim.binding.vim.vm.device.VirtualDisk;
import com.vmware.vim.binding.vim.vm.device.VirtualDiskOption.DiskMode;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ClusteringService implements IClusteringService {
private static final int VC_RP_MAX_NAME_LENGTH = 80;
private static final Logger logger = Logger
.getLogger(ClusteringService.class);
private ClusterConfigManager configMgr;
private IClusterEntityManager clusterEntityMgr;
private IConcurrentLockedClusterEntityManager lockClusterEntityMgr;
private IResourcePoolDAO rpDao;
private INetworkService networkMgr;
private IResourceService resMgr;
private IPlacementService placementService;
private IClusterInitializerService clusterInitializerService;
private ElasticityScheduleManager elasticityScheduleMgr;
@Autowired
private INodeTemplateService nodeTemplateService;
private volatile boolean inited = false;
private volatile Throwable initError;
private int cloneConcurrency;
private VmEventManager processor;
private Map<String, IClusterCloneService> cloneServiceMap;
private ClusterManager clusterManager;
private SoftwareManagerCollector softwareManagerCollector;
@Autowired
private ClusterNodeUpdator clusterNodeUpdator;
@Autowired
private CmsWorker cmsWorker;
@Autowired
private IVcInventorySyncService syncService;
@Autowired
private VcResourceManager vcResourceManager;
@Autowired
private VcResourceFilterBuilder vcResourceFilterBuilder;
@Autowired
public void setClusterManager(ClusterManager clusterManager) {
this.clusterManager = clusterManager;
}
public ClusterManager getClusterManager() {
return this.clusterManager;
}
public INetworkService getNetworkMgr() {
return networkMgr;
}
public void setNetworkMgr(INetworkService networkMgr) {
this.networkMgr = networkMgr;
}
public ClusterConfigManager getConfigMgr() {
return configMgr;
}
public void setConfigMgr(ClusterConfigManager configMgr) {
this.configMgr = configMgr;
}
@Autowired
public void setResMgr(IResourceService resMgr) {
this.resMgr = resMgr;
}
@Autowired
public void setPlacementService(IPlacementService placementService) {
this.placementService = placementService;
}
public IClusterInitializerService getClusterInitializerService() {
return clusterInitializerService;
}
@Autowired
public void setClusterInitializerService(
IClusterInitializerService clusterInitializerService) {
this.clusterInitializerService = clusterInitializerService;
}
public IClusterEntityManager getClusterEntityMgr() {
return clusterEntityMgr;
}
@Autowired
public void setClusterEntityMgr(IClusterEntityManager clusterEntityMgr) {
this.clusterEntityMgr = clusterEntityMgr;
}
public IConcurrentLockedClusterEntityManager getLockClusterEntityMgr() {
return lockClusterEntityMgr;
}
@Autowired
public void setLockClusterEntityMgr(
IConcurrentLockedClusterEntityManager lockClusterEntityMgr) {
this.lockClusterEntityMgr = lockClusterEntityMgr;
}
public IResourcePoolDAO getRpDao() {
return rpDao;
}
@Autowired
public void setRpDao(IResourcePoolDAO rpDao) {
this.rpDao = rpDao;
}
public Map<String, IClusterCloneService> getCloneService() {
return cloneServiceMap;
}
/**
*
* @param cloneServiceMap clone service qualifier to clone service map.
* 3 clone services so far: simpleClusterCloneService, fastClusterCloneService, instantClusterCloneService
*/
@Autowired
public void setCloneService(Map<String, IClusterCloneService> cloneServiceMap) {
this.cloneServiceMap = cloneServiceMap;
}
public ElasticityScheduleManager getElasticityScheduleManager() {
return elasticityScheduleMgr;
}
@Autowired
public void setElasticityScheduleManager(
ElasticityScheduleManager elasticityScheduleMgr) {
this.elasticityScheduleMgr = elasticityScheduleMgr;
}
public SoftwareManagerCollector getSoftwareManagerCollector() {
return softwareManagerCollector;
}
@Autowired
public void setSoftwareManagerCollector(
SoftwareManagerCollector softwareManagerCollector) {
this.softwareManagerCollector = softwareManagerCollector;
}
@Override
public boolean isInited() {
return inited;
}
@Override
public Throwable getInitError() {
return initError;
}
@Override
public synchronized void init() {
if (!inited) {
try {
// XXX hack to approve bootstrap instance id, should be moved out of Configuration
Configuration.approveBootstrapInstanceId(Configuration.BootstrapUsage.ALLOWED);
Configuration.approveBootstrapInstanceId(Configuration.BootstrapUsage.FINALIZED);
VcContext.initVcContext();
new VcEventRouter();
//disable background VC refresh.
//CmsWorker.addPeriodic(new VcInventory.SyncInventoryRequest());
VcInventory.loadInventory();
//why??
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.warn("interupted during sleep " + e.getMessage());
}
startVMEventProcessor();
int poolSize = Configuration.getInt("serengeti.scheduler.poolsize", Constants.DEFAULT_SCHEDULER_POOL_SIZE);
Scheduler.init(poolSize, poolSize);
cloneConcurrency = Configuration.getInt("serengeti.singlevm.concurrency", 1);
// refresh the cluster nodes once on bde startup,
// then add the periodic processing with default 5 minute interval
clusterNodeUpdator.syncAllClusters();
// initialize uuid
initUUID();
// refresh node templates on BDE startup
nodeTemplateService.refreshNodeTemplates();
clusterInitializerService.transformClusterStatus();
elasticityScheduleMgr.start();
configureAlarm();
inited = true;
} catch (Throwable err) {
logger.error("init ClusteringService error", err);
initError = err;
}
}
}
private void startVMEventProcessor() {
// add event handler for Serengeti after VC event handler is registered.
processor = new VmEventManager(lockClusterEntityMgr);
processor.setClusterManager(clusterManager);
processor.setSoftwareManagerCollector(softwareManagerCollector);
processor.start();
}
private void configureAlarm() {
VcDatacenter dc = VcResourceUtils.getCurrentDatacenter();
List<String> folderList = new ArrayList<String>(1);
String serengetiUUID = ConfigInfo.getSerengetiRootFolder();
folderList.add(serengetiUUID);
Folder rootFolder =
VcResourceUtils.findFolderByNameList(dc, folderList);
if (rootFolder == null) {
CreateVMFolderSP sp =
new CreateVMFolderSP(dc, null, folderList);
Callable<Void>[] storeProcedures = new Callable[1];
storeProcedures[0] = sp;
Map<String, Folder> folders =
executeFolderCreationProcedures(null, storeProcedures);
AuAssert.check(folders.size() == 1);
rootFolder = folders.get(serengetiUUID);
AuAssert.check(rootFolder != null);
}
final Folder root = rootFolder;
VcContext.inVcSessionDo(new VcSession<Boolean>() {
@Override
protected boolean isTaskSession() {
return true;
}
@Override
protected Boolean body() throws Exception {
VcUtil.configureAlarm(root);
return true;
}
});
}
@Override
synchronized public void destroy() {
Scheduler.shutdown(true);
processor.stop();
elasticityScheduleMgr.shutdown();
}
private void initUUID() {
if (ConfigInfo.isInitUUID()) {
String uuid = null;
final VcVirtualMachine serverVm = VcResourceUtils.findServerVM();
if (ConfigInfo.isDeployAsVApp()) {
VcResourcePool vApp = serverVm.getParentVApp();
uuid = vApp.getName();
} else {
Folder parentFolder = VcResourceUtils.findParentFolderOfVm(serverVm);
AuAssert.check(parentFolder != null);
uuid = parentFolder.getName();
}
ConfigInfo.setSerengetiUUID(uuid);
ConfigInfo.setInitUUID(false);
ConfigInfo.save();
}
}
private VcVirtualMachine getTemplateVM(String templateName) {
return this.nodeTemplateService.getNodeTemplateVMByName(templateName);
}
private VcVirtualMachine prepareTemplateVM(String templateName) {
VcVirtualMachine templateVM = getTemplateVM(templateName);
// update vm info in vc cache, in case snapshot is removed by others
VcVmUtil.updateVm(templateVM.getId());
try {
boolean needRemoveSnapshots = false;
if (ConfigInfo.isJustUpgraded()) {
needRemoveSnapshots = true;
ConfigInfo.setJustUpgraded(false);
ConfigInfo.save();
}
if (VcVmUtil.enableOvfEnvTransport(templateVM)) {
needRemoveSnapshots = true;
}
if (VcVmUtil.enableSyncTimeWithHost(templateVM)) {
needRemoveSnapshots = true;
}
if (needRemoveSnapshots) {
removeRootSnapshot(templateVM);
}
return templateVM;
} catch (Exception e) {
logger.error("Prepare template VM error: " + e.getMessage());
throw BddException.INTERNAL(e, "Prepare template VM error.");
}
}
private void removeRootSnapshot(final VcVirtualMachine templateVM) {
VcContext.inVcSessionDo(new VcSession<Boolean>() {
@Override
protected boolean isTaskSession() {
return true;
}
@Override
protected Boolean body() throws Exception {
VcSnapshot snapshot =
templateVM.getSnapshotByName(Constants.ROOT_SNAPSTHOT_NAME);
if (snapshot != null) {
snapshot.remove();
}
return true;
}
});
}
private BaseNode createBaseNodeFromTemplateVm(final VcVirtualMachine templateVm) {
BaseNode templateNode = new BaseNode(templateVm.getName());
List<DiskSpec> diskSpecs = new ArrayList<DiskSpec>();
for (DeviceId slot : templateVm.getVirtualDiskIds()) {
VirtualDisk vmdk = (VirtualDisk) templateVm.getVirtualDevice(slot);
DiskSpec spec = new DiskSpec();
spec.setSize((int) (vmdk.getCapacityInKB() / (1024 * 1024)));
spec.setDiskType(DiskType.SYSTEM_DISK);
spec.setController(CommonUtil.getSystemAndSwapControllerType());
diskSpecs.add(spec);
}
templateNode.setDisks(diskSpecs);
return templateNode;
}
private String getNodeTemplateNetworkLable(final VcVirtualMachine templateVm) {
return VcContext.inVcSessionDo(new VcSession<String>() {
@Override
protected String body() throws Exception {
VirtualDevice[] devices =
templateVm.getConfig().getHardware().getDevice();
for (VirtualDevice device : devices) {
if (device.getKey() == 4000) {
return device.getDeviceInfo().getLabel();
}
}
return null;
}
});
}
private void updateNicLabels(List<BaseNode> vNodes, String templateNetworkLabel) {
logger.info("Start to set network schema for template nic.");
for (BaseNode node : vNodes) {
List<Network> networks = node.getVmSchema().networkSchema.networks;
if (!networks.isEmpty()) {
networks.get(0).nicLabel = templateNetworkLabel;
for (int i = 1; i < networks.size(); i++) {
networks.get(i).nicLabel = VcVmUtil.NIC_LABEL_PREFIX + (i + 1);
}
}
logger.info(node.getVmSchema());
}
}
/**
* cluster create, resize, resume will all call this method for static ip
* allocation the network contains all allocated ip address to this cluster,
* so some of them may already be occupied by existing node. So we need to
* detect if that ip is allocated, before assign that one to one node
*
* @param vNodes
* @param networkAdds
* @param occupiedIpSets
*/
private void allocateStaticIp(List<BaseNode> vNodes,
List<NetworkAdd> networkAdds, Map<String, Set<String>> occupiedIpSets) {
int i, j;
for (i = 0; i < networkAdds.size(); i++) {
NetworkAdd networkAdd = networkAdds.get(i);
String portGroupName = networkAdd.getPortGroup();
Set<String> usedIps = null;
if (occupiedIpSets != null && !occupiedIpSets.isEmpty()) {
usedIps = occupiedIpSets.get(portGroupName);
}
if (networkAdd.getIsDhcp()) {
// no need to allocate ip for dhcp
logger.info("using dhcp for network: " + portGroupName);
} else {
logger.info("Start to allocate static ip address for each VM's "
+ i + "th network.");
List<String> availableIps =
IpBlock.getIpAddressFromIpBlock(networkAdd.getIpBlocks());
if (usedIps != null && !usedIps.isEmpty()) {
availableIps.removeAll(usedIps);
}
AuAssert.check(availableIps.size() == vNodes.size());
for (j = 0; j < availableIps.size(); j++) {
vNodes.get(j).updateNicOfPortGroup(portGroupName,
availableIps.get(j), null, null);
}
logger.info("Finished to allocate static ip address for VM's mgr network.");
}
}
}
private String getMaprActiveJobTrackerIp(final String maprNodeIP,
final String clusterName) {
String activeJobTrackerIp = "";
String errorMsg = "";
JSch jsch = new JSch();
String sshUser = Configuration.getString("mapr.ssh.user", "serengeti");
int sshPort = Configuration.getInt("mapr.ssh.port", 22);
String sudoCmd = CommonUtil.getCustomizedSudoCmd();
String prvKeyFile =
Configuration.getString("serengeti.ssh.private.key.file",
"/home/serengeti/.ssh/id_rsa");
Session session = null;
ChannelExec channel = null;
BufferedReader in = null;
try {
session = jsch.getSession(sshUser, maprNodeIP, sshPort);
jsch.addIdentity(prvKeyFile);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(15000);
session.connect();
logger.debug("SSH session is connected!");
channel = (ChannelExec) session.openChannel("exec");
if (channel != null) {
logger.debug("SSH channel is connected!");
StringBuffer buff = new StringBuffer();
String cmd =
"maprcli node list -filter \"[rp==/*]and[svc==jobtracker]\" -columns ip";
logger.debug("exec command is: " + cmd);
channel.setPty(true); //to enable sudo
channel.setCommand(sudoCmd + " " + cmd);
in =
new BufferedReader(new InputStreamReader(
channel.getInputStream()));
channel.connect();
if (!canChannelConnect(channel)) {
errorMsg =
"Cannot determine IP address of active JobTracker. No SSH channel connection.";
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
}
while (true) {
String line = in.readLine();
buff.append(line);
logger.debug("jobtracker message: " + line);
if (channel.isClosed()) {
int exitStatus = channel.getExitStatus();
logger.debug("Exit status from exec is: " + exitStatus);
break;
}
}
Pattern ipPattern = Pattern.compile(Constants.IP_PATTERN);
Matcher matcher = ipPattern.matcher(buff.toString());
if (matcher.find()) {
activeJobTrackerIp = matcher.group();
} else {
errorMsg =
"Cannot find jobtracker ip info in cluster" + clusterName;
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
}
} else {
errorMsg = "Get active Jobtracker ip: cannot open SSH channel.";
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
}
} catch (JSchException e) {
errorMsg = "SSH unknow error: " + e.getMessage();
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
} catch (IOException e) {
errorMsg = "Obtain active jobtracker ip error: " + e.getMessage();
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
} finally {
if (channel != null && channel.isConnected()) {
channel.disconnect();
}
if (session != null && session.isConnected()) {
session.disconnect();
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
errorMsg =
"Failed to release buffer while retrieving MapR JobTracker Port: "
+ e.getMessage();
logger.error(errorMsg);
}
}
}
return activeJobTrackerIp;
}
private boolean canChannelConnect(ChannelExec channel) {
if (channel == null) {
return false;
}
if (channel.isConnected()) {
return true;
}
try {
channel.connect();
} catch (JSchException e) {
String errorMsg = "SSH connection failed: " + e.getMessage();
logger.error(errorMsg);
throw BddException.INTERNAL(null, errorMsg);
}
return channel.isConnected();
}
private void updateVhmMasterMoid(String clusterName) {
ClusterEntity cluster = getClusterEntityMgr().findByName(clusterName);
if (cluster.getVhmMasterMoid() == null) {
List<NodeEntity> nodes =
getClusterEntityMgr().findAllNodes(clusterName);
for (NodeEntity node : nodes) {
if (node.getMoId() != null
&& node.getNodeGroup().getRoles() != null) {
@SuppressWarnings("unchecked")
List<String> roles =
new Gson().fromJson(node.getNodeGroup().getRoles(),
List.class);
if (cluster.getDistroVendor().equalsIgnoreCase(Constants.MAPR_VENDOR)) {
if (roles
.contains(HadoopRole.MAPR_JOBTRACKER_ROLE.toString())) {
String thisJtIp = node.getPrimaryMgtIpV4();
String activeJtIp;
try {
activeJtIp =
getMaprActiveJobTrackerIp(thisJtIp, clusterName);
logger.info("fetched active JT Ip: " + activeJtIp);
} catch (Exception e) {
continue;
}
/*
* TODO: in multiple NICs env, if portgroups are isolated,
* may not able to retrieve active IP.
*/
AuAssert.check(!CommonUtil.isBlank(thisJtIp),
"falied to query active JobTracker Ip");
for (NodeEntity jt : nodes) {
boolean isActiveJt = false;
for (NicEntity nicEntity : jt.getNics()) {
if (nicEntity.getIpv4Address() != null
&& activeJtIp.equals(nicEntity
.getIpv4Address())) {
isActiveJt = true;
break;
}
}
if (isActiveJt) {
cluster.setVhmMasterMoid(jt.getMoId());
break;
}
}
break;
}
} else {
if (roles.contains(HadoopRole.HADOOP_JOBTRACKER_ROLE
.toString())) {
cluster.setVhmMasterMoid(node.getMoId());
break;
}
}
}
}
}
getClusterEntityMgr().update(cluster);
}
@Override
@SuppressWarnings("unchecked")
public boolean setAutoElasticity(String clusterName, boolean refreshAllNodes) {
logger.info("set auto elasticity for cluster " + clusterName);
ClusterEntity cluster = getClusterEntityMgr().findByName(clusterName);
List<NodeEntity> nodes = clusterEntityMgr.findAllNodes(clusterName);
Boolean enableAutoElasticity = cluster.getAutomationEnable();
if (enableAutoElasticity == null) {
return true;
}
String masterMoId = cluster.getVhmMasterMoid();
if (masterMoId == null) {
// this will only occurs when creating cluster
updateVhmMasterMoid(clusterName);
cluster = getClusterEntityMgr().findByName(clusterName);
masterMoId = cluster.getVhmMasterMoid();
if (masterMoId == null) {
logger.error("masterMoId missed.");
throw ClusteringServiceException.SET_AUTO_ELASTICITY_FAILED(cluster
.getName());
}
}
String serengetiUUID = ConfigInfo.getSerengetiRootFolder();
int minComputeNodeNum = cluster.getVhmMinNum();
int maxComputeNodeNum = cluster.getVhmMaxNum();
String jobTrackerPort = cluster.getVhmJobTrackerPort();
VcVirtualMachine vcVm = VcCache.getIgnoreMissing(masterMoId);
if (vcVm == null) {
logger.error("cannot find vhm master node");
return false;
}
String masterUUID = vcVm.getConfig().getUuid();
Callable<Void>[] storeProcedures = new Callable[nodes.size()];
int i = 0;
for (NodeEntity node : nodes) {
VcVirtualMachine vm = VcCache.getIgnoreMissing(node.getMoId());
if (vm == null) {
logger.error("cannot find node: " + node.getVmName());
continue;
}
if (!refreshAllNodes && !vm.getId().equalsIgnoreCase(masterMoId)) {
continue;
}
List<String> roles =
new Gson().fromJson(node.getNodeGroup().getRoles(), List.class);
SoftwareManager softMgr =
softwareManagerCollector.getSoftwareManager(cluster.getAppManager());
boolean isComputeOnlyNode =
softMgr.isComputeOnlyRoles(roles);
SetAutoElasticitySP sp =
new SetAutoElasticitySP(clusterName, vm, serengetiUUID,
masterMoId, masterUUID, enableAutoElasticity,
minComputeNodeNum, maxComputeNodeNum, jobTrackerPort,
isComputeOnlyNode);
storeProcedures[i] = sp;
i++;
}
try {
// execute store procedures to set auto elasticity
logger.info("ClusteringService, start to set auto elasticity.");
boolean success = true;
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProcedures, callback);
if (result == null) {
logger.error("set auto elasticity failed.");
throw ClusteringServiceException
.SET_AUTO_ELASTICITY_FAILED(clusterName);
}
for (i = 0; i < storeProcedures.length; i++) {
if (result[i].throwable != null) {
logger.error("failed to set auto elasticity",
result[i].throwable);
success = false;
}
}
return success;
} catch (InterruptedException e) {
logger.error("error in setting auto elasticity", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
@SuppressWarnings("unchecked")
private Map<String, Folder> createVcFolders(ClusterCreate cluster) {
return createVcFolders(cluster, false);
}
/*
* addNodeGroup means it's called by expandCluster() to add new node groups
*/
@SuppressWarnings("unchecked")
private Map<String, Folder> createVcFolders(ClusterCreate cluster, boolean addNodeGroup) {
logger.info("createVcFolders, start to create cluster Folder.");
VcVirtualMachine templateVm = getTemplateVM(cluster.getTemplateName());
// get all nodegroups
Callable<Void>[] storeProcedures = new Callable[1];
Folder clusterFolder = null;
if(!addNodeGroup) {
if (cluster.getNodeGroups().length > 0) {
// create cluster folder first
NodeGroupCreate group = cluster.getNodeGroups()[0];
String path = group.getVmFolderPath();
logger.info("create folderName" + path);
String[] folderNames = path.split("/");
List<String> folderList = new ArrayList<String>();
for (int i = 0; i < folderNames.length - 1; i++) {
folderList.add(folderNames[i]);
}
CreateVMFolderSP sp =
new CreateVMFolderSP(templateVm.getDatacenter(), null,
folderList);
storeProcedures[0] = sp;
Map<String, Folder> folders =
executeFolderCreationProcedures(cluster, storeProcedures);
for (String name : folders.keySet()) {
clusterFolder = folders.get(name);
break;
}
}
}
logger.info("createVcFolders, start to create group Folders.");
storeProcedures = new Callable[cluster.getNodeGroups().length];
int i = 0;
for (NodeGroupCreate group : cluster.getNodeGroups()) {
List<String> folderList = new ArrayList<String>();
folderList.add(group.getName());
CreateVMFolderSP sp =
new CreateVMFolderSP(templateVm.getDatacenter(), clusterFolder,
folderList);
storeProcedures[i] = sp;
i++;
}
return executeFolderCreationProcedures(cluster, storeProcedures);
}
private Map<String, Folder> executeFolderCreationProcedures(
ClusterCreate cluster, Callable<Void>[] storeProcedures) {
Map<String, Folder> folders = new HashMap<String, Folder>();
try {
// execute store procedures to create vc folders
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProcedures, callback);
if (result == null) {
logger.error("No folder is created.");
if (cluster != null)
throw ClusteringServiceException.CREATE_FOLDER_FAILED(cluster
.getName());
}
int total = 0;
boolean success = true;
for (int i = 0; i < storeProcedures.length; i++) {
CreateVMFolderSP sp = (CreateVMFolderSP) storeProcedures[i];
if (result[i].finished && result[i].throwable == null) {
++total;
Folder childFolder =
sp.getResult().get(sp.getResult().size() - 1);
folders.put(childFolder.getName(), childFolder);
} else if (result[i].throwable != null) {
logger.error("Failed to create vm folder", result[i].throwable);
success = false;
}
}
logger.info(total + " Folders are created.");
if (!success) {
if (cluster != null)
throw ClusteringServiceException.CREATE_FOLDER_FAILED(cluster
.getName());
}
return folders;
} catch (InterruptedException e) {
logger.error("error in creating VC folders", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
private void executeResourcePoolStoreProcedures(Callable<Void>[] defineSPs,
String type, String clusterName) throws InterruptedException {
if (defineSPs.length == 0) {
logger.debug("no resource pool need to be created.");
return;
}
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
defineSPs, callback);
if (result == null) {
logger.error("No " + type + " resource pool is created.");
throw ClusteringServiceException
.CREATE_RESOURCE_POOL_FAILED(Constants.NO_RESOURCE_POOL_IS_CREATED);
}
int total = 0;
boolean success = true;
String errMessage = null;
for (int i = 0; i < defineSPs.length; i++) {
if (result[i].finished && result[i].throwable == null) {
++total;
} else if (result[i].throwable != null) {
if (errMessage == null) {
//Generally the error message is same for all resource pools, so here we just keep the first failure message.
errMessage = result[i].throwable.getMessage();
}
logger.error("Failed to create " + type + " resource pool(s)",
result[i].throwable);
success = false;
}
}
logger.info(total + " " + type + " resource pool(s) are created.");
if (!success) {
throw ClusteringServiceException
.CREATE_RESOURCE_POOL_FAILED(errMessage);
}
}
private Map<String, Integer> collectResourcePoolInfo(List<BaseNode> vNodes,
final String uuid, Map<String, List<String>> vcClusterRpNamesMap,
Map<Long, List<NodeGroupCreate>> rpNodeGroupsMap) {
List<String> resourcePoolNames = null;
List<NodeGroupCreate> nodeGroups = null;
int resourcePoolNameCount = 0;
int nodeGroupNameCount = 0;
for (BaseNode baseNode : vNodes) {
String vcCluster = baseNode.getTargetVcCluster();
VcCluster cluster = VcResourceUtils.findVcCluster(vcCluster);
if (!cluster.getConfig().getDRSEnabled()) {
logger.debug("DRS disabled for cluster " + vcCluster
+ ", do not create child rp for this cluster.");
continue;
}
AuAssert.check(!CommonUtil.isBlank(vcCluster),
"Vc cluster name cannot be null!");
if (!vcClusterRpNamesMap.containsKey(vcCluster)) {
resourcePoolNames = new ArrayList<String>();
} else {
resourcePoolNames = vcClusterRpNamesMap.get(vcCluster);
}
String vcRp = baseNode.getTargetRp();
logger.info("collectResourcePoolInfo baseNode.getTargetRp()" + vcRp);
String rpPath = "/" + vcCluster + "/" + vcRp + "/" + uuid;
logger.info("collectResourcePoolInfo rpPath" + rpPath);
long rpHashCode = rpPath.hashCode();
if (!rpNodeGroupsMap.containsKey(rpHashCode)) {
nodeGroups = new ArrayList<NodeGroupCreate>();
} else {
nodeGroups = rpNodeGroupsMap.get(rpHashCode);
}
NodeGroupCreate nodeGroup = baseNode.getNodeGroup();
if (!getAllNodeGroupNames(nodeGroups).contains(nodeGroup.getName())) {
nodeGroups.add(nodeGroup);
rpNodeGroupsMap.put(rpHashCode, nodeGroups);
nodeGroupNameCount++;
}
if (!resourcePoolNames.contains(vcRp)) {
resourcePoolNames.add(vcRp);
vcClusterRpNamesMap.put(vcCluster, resourcePoolNames);
resourcePoolNameCount++;
}
}
Map<String, Integer> countResult = new HashMap<String, Integer>();
countResult.put("resourcePoolNameCount", resourcePoolNameCount);
countResult.put("nodeGroupNameCount", nodeGroupNameCount);
return countResult;
}
private List<String> getAllNodeGroupNames(List<NodeGroupCreate> nodeGroups) {
List<String> nodeGroupNames = new ArrayList<String>();
for (NodeGroupCreate nodeGroup : nodeGroups) {
nodeGroupNames.add(nodeGroup.getName());
}
return nodeGroupNames;
}
private String createVcResourcePools(List<BaseNode> vNodes) {
return createVcResourcePools(vNodes, false);
}
/*
* addNodeGroup means it's called by expandCluster() to add new node groups
*/
private String createVcResourcePools(List<BaseNode> vNodes, boolean addNodeGroup) {
logger.info("createVcResourcePools, start to create VC ResourcePool(s).");
/*
* define cluster resource pool name.
*/
String clusterName = vNodes.get(0).getClusterName();
SoftwareManager softManager =
softwareManagerCollector
.getSoftwareManagerByClusterName(clusterName);
String uuid = ConfigInfo.getSerengetiUUID();
String clusterRpName = uuid + "-" + clusterName;
if (clusterRpName.length() > VC_RP_MAX_NAME_LENGTH) {
throw ClusteringServiceException.CLUSTER_NAME_TOO_LONG(clusterName);
}
/*
* prepare resource pool names and node group per resource pool for creating cluster
* resource pools and node group resource pool(s).
*/
Map<String, List<String>> vcClusterRpNamesMap =
new HashMap<String, List<String>>();
Map<Long, List<NodeGroupCreate>> rpNodeGroupsMap =
new HashMap<Long, List<NodeGroupCreate>>();
Map<String, Integer> countResult =
collectResourcePoolInfo(vNodes, uuid, vcClusterRpNamesMap,
rpNodeGroupsMap);
try {
int i = 0;
if (!addNodeGroup) {
/*
* define cluster store procedures of resource pool(s)
*/
int resourcePoolNameCount = countResult.get("resourcePoolNameCount");
Callable<Void>[] clusterSPs = new Callable[resourcePoolNameCount];
for (Entry<String, List<String>> vcClusterRpNamesEntry : vcClusterRpNamesMap
.entrySet()) {
String vcClusterName = vcClusterRpNamesEntry.getKey();
VcCluster vcCluster = VcResourceUtils.findVcCluster(vcClusterName);
if (vcCluster == null) {
String errorMsg =
"Cannot find the vCenter Server cluster " + vcClusterName
+ ".";
logger.error(errorMsg);
throw ClusteringServiceException
.CANNOT_FIND_VC_CLUSTER(vcClusterName);
}
List<String> resourcePoolNames = vcClusterRpNamesEntry.getValue();
for (String resourcePoolName : resourcePoolNames) {
VcResourcePool parentVcResourcePool =
VcResourceUtils.findRPInVCCluster(vcClusterName,
resourcePoolName);
if (parentVcResourcePool == null) {
String errorMsg =
"Cannot find the vCenter Server resource pool "
+ resourcePoolName + ".";
logger.error(errorMsg);
throw ClusteringServiceException
.CANNOT_FIND_VC_RESOURCE_POOL(resourcePoolName);
}
CreateResourcePoolSP clusterSP =
new CreateResourcePoolSP(parentVcResourcePool,
clusterRpName);
clusterSPs[i] = clusterSP;
i++;
}
}
// execute store procedures to create cluster resource pool(s)
logger.info("ClusteringService, start to create cluster resource pool(s).");
executeResourcePoolStoreProcedures(clusterSPs, "cluster", clusterName);
}
/*
* define node group store procedures of resource pool(s)
*/
int nodeGroupNameCount = countResult.get("nodeGroupNameCount");
Callable<Void>[] nodeGroupSPs = new Callable[nodeGroupNameCount];
i = 0;
for (Entry<String, List<String>> vcClusterRpNamesEntry : vcClusterRpNamesMap
.entrySet()) {
String vcClusterName = vcClusterRpNamesEntry.getKey();
VcCluster vcCluster = VcResourceUtils.findVcCluster(vcClusterName);
if (vcCluster == null) {
String errorMsg =
"Cannot find the vCenter Server cluster " + vcClusterName
+ ".";
logger.error(errorMsg);
throw ClusteringServiceException
.CANNOT_FIND_VC_CLUSTER(vcClusterName);
}
if (!vcCluster.getConfig().getDRSEnabled()) {
continue;
}
List<String> resourcePoolNames = vcClusterRpNamesEntry.getValue();
for (String resourcePoolName : resourcePoolNames) {
VcResourcePool parentVcResourcePool = null;
String vcRPName =
CommonUtil.isBlank(resourcePoolName) ? clusterRpName
: resourcePoolName + "/" + clusterRpName;
parentVcResourcePool =
VcResourceUtils.findRPInVCCluster(vcClusterName, vcRPName);
if (parentVcResourcePool == null) {
String errorMsg =
"Cannot find the vCenter Server resource pool "
+ vcRPName
+ (CommonUtil.isBlank(resourcePoolName) ? ""
: " in the vCenter Server resource pool "
+ resourcePoolName) + ".";
logger.error(errorMsg);
throw ClusteringServiceException
.CANNOT_FIND_SUB_VC_RESOURCE_POOL(vcRPName,
resourcePoolName);
}
String rpPath =
"/" + vcClusterName + "/" + resourcePoolName + "/" + uuid;
long rpHashCode = rpPath.hashCode();
for (NodeGroupCreate nodeGroup : rpNodeGroupsMap.get(rpHashCode)) {
AuAssert
.check(nodeGroup != null,
"create node group resource pool failed: node group cannot be null !");
if (nodeGroup.getName().length() > 80) {
throw ClusteringServiceException
.GROUP_NAME_TOO_LONG(nodeGroup.getName());
}
CreateResourcePoolSP nodeGroupSP =
new CreateResourcePoolSP(parentVcResourcePool,
nodeGroup.getName(), nodeGroup, softManager);
nodeGroupSPs[i] = nodeGroupSP;
i++;
}
}
}
//execute store procedures to create node group resource pool(s)
logger.info("ClusteringService, start to create node group resource pool(s).");
executeResourcePoolStoreProcedures(nodeGroupSPs, "node group",
clusterName);
} catch (InterruptedException e) {
logger.error("error in creating VC ResourcePool(s)", e);
throw ClusteringServiceException.CREATE_RESOURCE_POOL_ERROR(e
.getMessage());
}
return clusterRpName;
}
@SuppressWarnings("unchecked")
@Override
public boolean createVcVms(ClusterCreate clusterSpec,
List<BaseNode> vNodes, Map<String, Set<String>> occupiedIpSets,
boolean reserveRawDisks, StatusUpdater statusUpdator) {
if (vNodes.isEmpty()) {
logger.info("No vm to be created.");
return true;
}
List<NetworkAdd> networkAdds = clusterSpec.getNetworkings();
String clusterCloneType = clusterSpec.getClusterCloneType();
logger.info("syncCreateVMs, start to create VMs for cluster " + clusterSpec.getName());
logger.info(String.format("Preparing node template VM %s for creating cluster %s", clusterSpec.getTemplateName(), clusterSpec.getName()));
VcVirtualMachine templateVm = prepareNodeTemplate(clusterSpec.getTemplateName(), vNodes);
logger.info("node template VM moid is " + templateVm.getId());
VmCreateSpec sourceSpec = new VmCreateSpec();
sourceSpec.setVmId(templateVm.getId());
sourceSpec.setVmName(templateVm.getName());
sourceSpec.setTargetHost(templateVm.getHost());
allocateStaticIp(vNodes, networkAdds, occupiedIpSets);
Map<String, Folder> folders = createVcFolders(vNodes.get(0).getCluster());
String clusterRpName = createVcResourcePools(vNodes);
List<VmCreateSpec> specs = new ArrayList<VmCreateSpec>();
Map<String, BaseNode> nodeMap = new HashMap<String, BaseNode>();
for (BaseNode vNode : vNodes) {
// prepare for cloning result
nodeMap.put(vNode.getVmName(), vNode);
vNode.setSuccess(false);
vNode.setFinished(false);
// generate create spec for fast clone
VmCreateSpec spec = new VmCreateSpec();
VmSchema createSchema = getVmSchema(vNode, templateVm.getId());
spec.setSchema(createSchema);
GuestMachineIdSpec machineIdSpec =
new GuestMachineIdSpec(networkAdds,
vNode.fetchPortGroupToIpV4Map(),
vNode.getPrimaryMgtPgName(), vNode, networkMgr);
logger.info("machine id of vm " + vNode.getVmName() + ":\n"
+ machineIdSpec.toString());
spec.setBootupConfigs(machineIdSpec.toGuestVariable());
// timeout is 10 mintues
StartVmPostPowerOn query =
new StartVmPostPowerOn(vNode.getNics().keySet(),
Configuration.getInt(Constants.VM_POWER_ON_WAITING_SEC_KEY, Constants.VM_POWER_ON_WAITING_SEC));
spec.setPostPowerOn(query);
spec.setPrePowerOn(getPrePowerOnFunc(vNode, reserveRawDisks));
spec.setCloneType(VcVmCloneType.FULL);
spec.setTargetDs(getVcDatastore(vNode));
spec.setTargetFolder(folders.get(vNode.getGroupName()));
spec.setTargetHost(VcResourceUtils.findHost(vNode.getTargetHost()));
spec.setTargetRp(getVcResourcePool(vNode, clusterRpName));
spec.setVmName(vNode.getVmName());
specs.add(spec);
}
BaseProgressCallback callback = new BaseProgressCallback(statusUpdator);
logger.info("ClusteringService, start to clone template.");
AuAssert.check(specs.size() > 0);
VmSchema vmSchema = specs.get(0).getSchema();
VcVmUtil.checkAndCreateSnapshot(vmSchema);
// call clone service to copy templates
List<VmCreateResult<?>> results =
chooseClusterCloneService(clusterCloneType).createCopies(sourceSpec, cloneConcurrency, specs,
callback);
if (results == null || results.isEmpty()) {
for (VmCreateSpec spec : specs) {
BaseNode node = nodeMap.get(spec.getVmName());
node.setFinished(true);
node.setSuccess(false);
}
return false;
}
boolean success = true;
int total = 0;
for (VmCreateResult<?> result : results) {
VmCreateSpec spec = (VmCreateSpec) result.getSpec();
BaseNode node = nodeMap.get(spec.getVmName());
node.setVmMobId(spec.getVmId());
node.setSuccess(true);
node.setFinished(true);
boolean vmSucc = VcVmUtil.setBaseNodeForVm(node, spec.getVmId());
if (!vmSucc || !result.isSuccess()) {
success = false;
node.setSuccess(false);
if (result.getErrMessage() != null) {
node.setErrMessage(result.getErrTimestamp() + " "
+ result.getErrMessage());
} else if (!node.getErrMessage().isEmpty()) {
node.setErrMessage(CommonUtil.getCurrentTimestamp() + " "
+ node.getNodeAction());
}
} else {
total++;
}
}
logger.info(total + " VMs are successfully created.");
return success;
}
private VcVirtualMachine prepareNodeTemplate(String templateName, List<BaseNode> vNodes) {
VcVirtualMachine templateVM = prepareTemplateVM(templateName);
String lable = getNodeTemplateNetworkLable(templateVM);
updateNicLabels(vNodes, lable);
return templateVM;
}
private IClusterCloneService chooseClusterCloneService(String type) {
String qualifier = type+"ClusterCloneService";
IClusterCloneService clusterCloneService = this.cloneServiceMap.get(qualifier);
AuAssert.check(clusterCloneService != null, "ClusterCloneService not found: " + type);
return clusterCloneService;
}
private void setPersistentDiskMode(BaseNode vNode) {
DiskSchema diskSchema = vNode.getVmSchema().diskSchema;
if (diskSchema.getDisks() != null) {
for (Disk disk : diskSchema.getDisks()) {
disk.mode = DiskMode.persistent;
}
}
}
private CreateVmPrePowerOn getPrePowerOnFunc(BaseNode vNode, boolean reserveRawDisks) {
boolean persistentDiskMode = false;
String haFlag = vNode.getNodeGroup().getHaFlag();
boolean ha = false;
boolean ft = false;
if (haFlag != null && Constants.HA_FLAG_ON.equals(haFlag.toLowerCase())) {
ha = true;
}
if (haFlag != null && Constants.HA_FLAG_FT.equals(haFlag.toLowerCase())) {
ha = true;
ft = true;
Integer cpuNum = vNode.getNodeGroup().getCpuNum();
cpuNum = (cpuNum == null) ? 0 : cpuNum;
if (cpuNum > 1) {
throw ClusteringServiceException.CPU_NUMBER_MORE_THAN_ONE(vNode
.getVmName());
}
logger.debug("ft is enabled is for VM " + vNode.getVmName());
logger.debug("set disk mode to persistent for VM " + vNode.getVmName());
// change disk mode to persistent, instead of independent_persistent, since FT requires this
persistentDiskMode = true;
}
List<String> roles = vNode.getNodeGroup().getRoles();
SoftwareManager softMgr =
softwareManagerCollector.getSoftwareManagerByClusterName(vNode.getClusterName());
if (roles != null && softMgr.hasMgmtRole(roles)) {
logger.debug(vNode.getVmName() + " is a master node");
logger.debug("set disk mode to persistent for VM " + vNode.getVmName());
// change disk mode to persistent, instead of independent_persistent, to allow snapshot and clone on VM
persistentDiskMode = true;
}
if (persistentDiskMode) {
setPersistentDiskMode(vNode);
}
ClusterEntity clusterEntity =
getClusterEntityMgr().findByName(vNode.getClusterName());
CreateVmPrePowerOn prePowerOn =
new CreateVmPrePowerOn(reserveRawDisks, vNode.getVmSchema().diskSchema.getDisks(),
ha, ft, clusterEntity.getIoShares());
return prePowerOn;
}
private static VcDatastore getVcDatastore(BaseNode vNode) {
VcDatastore ds = VcResourceUtils.findDSInVcByName(vNode.getTargetDs());
if (ds != null) {
return ds;
}
logger.error("target data store " + vNode.getTargetDs()
+ " is not found.");
throw ClusteringServiceException.TARGET_VC_DATASTORE_NOT_FOUND(vNode
.getTargetDs());
}
private VcResourcePool getVcResourcePool(BaseNode vNode,
final String clusterRpName) {
try {
String vcRPName = "";
VcCluster cluster =
VcResourceUtils.findVcCluster(vNode.getTargetVcCluster());
if (!cluster.getConfig().getDRSEnabled()) {
logger.debug("DRS disabled for cluster "
+ vNode.getTargetVcCluster()
+ ", put VM under cluster directly.");
return cluster.getRootRP();
}
if (CommonUtil.isBlank(vNode.getTargetRp())) {
vcRPName = clusterRpName + "/" + vNode.getNodeGroup().getName();
} else {
vcRPName =
vNode.getTargetRp() + "/" + clusterRpName + "/"
+ vNode.getNodeGroup().getName();
}
VcResourcePool rp =
VcResourceUtils.findRPInVCCluster(vNode.getTargetVcCluster(),
vcRPName);
if (rp == null) {
throw ClusteringServiceException.TARGET_VC_RP_NOT_FOUND(
vNode.getTargetVcCluster(), vNode.getTargetRp());
}
return rp;
} catch (Exception e) {
logger.error("Failed to get VC resource pool " + vNode.getTargetRp()
+ " in vc cluster " + vNode.getTargetVcCluster(), e);
throw ClusteringServiceException.TARGET_VC_RP_NOT_FOUND(
vNode.getTargetVcCluster(), vNode.getTargetRp());
}
}
private VmSchema getVmSchema(BaseNode vNode, String templateVmId) {
VmSchema schema = vNode.getVmSchema();
schema.diskSchema.setParent(templateVmId);
schema.diskSchema.setParentSnap(Constants.ROOT_SNAPSTHOT_NAME);
return schema;
}
@Override
public List<BaseNode> getPlacementPlan(ClusterCreate clusterSpec,
List<BaseNode> existedNodes) {
try {
Thread.sleep(Configuration.getInt("vcrefresh.delay.forResume", 10) * 1000);
} catch (InterruptedException e1) {
logger.error("vcrefresh.delay.forResume interrupted.", e1);
}
List<String> dsNames = vcResourceManager.getDsNamesToBeUsed(clusterSpec.getDsNames());
if (dsNames.isEmpty()) {
throw ClusterConfigException.NO_DATASTORE_ADDED();
}
VcResourceFilters filters = vcResourceFilterBuilder.build(dsNames,
vcResourceManager.getRpNames(clusterSpec.getRpNames()), clusterSpec.getNetworkNames());
try {
syncService.refreshInventory(filters);
} catch (InterruptedException e) {
logger.error("failed to refresh vc inventory.", e);
}
logger.info("Begin to calculate provision plan.");
logger.info("Calling resource manager to get available vc hosts");
Container container = new Container();
List<VcCluster> clusters = resMgr.getAvailableClusters();
AuAssert.check(clusters != null && clusters.size() != 0);
for (VcCluster cl : clusters) {
//refresh once at beginning of cluster create/resume/resize
// VcResourceUtils.refreshDatastore(cl);
container.addResource(cl);
}
logger.info("VC resource container details:" + ContainerToStringHelper.convertToString(container));
logger.info("check time on hosts.");
// check time on hosts
int maxTimeDiffInSec = Constants.MAX_TIME_DIFF_IN_SEC;
SoftwareManager softMgr =
softwareManagerCollector.getSoftwareManager(clusterSpec
.getAppManager());
if (softMgr.hasHbase(clusterSpec.toBlueprint()))
maxTimeDiffInSec = Constants.MAX_TIME_DIFF_IN_SEC_HBASE;
List<String> outOfSyncHosts = new ArrayList<String>();
for (AbstractHost host : container.getAllHosts()) {
int hostTimeDiffInSec =
VcResourceUtils.getHostTimeDiffInSec(host.getName());
if (Math.abs(hostTimeDiffInSec) > maxTimeDiffInSec) {
logger.info("Host " + host.getName() + " has a time difference of "
+ hostTimeDiffInSec
+ " seconds and is dropped from placement.");
outOfSyncHosts.add(host.getName());
}
}
for (String host : outOfSyncHosts) {
container.removeHost(host);
}
logger.info("filter hosts by networks.");
// filter hosts by networks
List<com.vmware.bdd.spectypes.VcCluster> usedClusters = clusterSpec.getVcClusters();
List<String> noNetworkHosts = new ArrayList<String>();
noNetworkHosts = resMgr.filterHostsByNetwork(clusterSpec.getNetworkNames(), usedClusters);
for (String host : noNetworkHosts) {
container.removeHost(host);
}
Map<String, List<String>> filteredHosts =new HashMap<String, List<String>>();
if (!outOfSyncHosts.isEmpty()) filteredHosts.put(PlacementUtil.OUT_OF_SYNC_HOSTS, outOfSyncHosts);
if (!noNetworkHosts.isEmpty()) {
filteredHosts.put(PlacementUtil.NO_NETWORKS_HOSTS, noNetworkHosts);
filteredHosts.put(PlacementUtil.NETWORK_NAMES, clusterSpec.getNetworkNames());
}
VcVirtualMachine templateVm = getTemplateVM(clusterSpec.getTemplateName());
container.setTemplateNode(createBaseNodeFromTemplateVm(templateVm));
if (clusterSpec.getHostToRackMap() != null
&& clusterSpec.getHostToRackMap().size() != 0) {
container.addRackMap(clusterSpec.getHostToRackMap());
}
logger.info("rack topology file validation.");
// rack topology file validation
Set<String> validRacks = new HashSet<String>();
List<AbstractHost> hosts = container.getAllHosts();
for (AbstractHost host : hosts) {
if (container.getRack(host) != null) {
// this rack is valid as it contains at least one host
validRacks.add(container.getRack(host));
}
}
for (NodeGroupCreate nodeGroup : clusterSpec.getNodeGroups()) {
if (nodeGroup.getPlacementPolicies() != null
&& nodeGroup.getPlacementPolicies().getGroupRacks() != null
&& validRacks.size() == 0) {
throw PlacementException.INVALID_RACK_INFO(clusterSpec.getName(),
nodeGroup.getName());
}
}
// for instant clone, we need check if the target hosts are with version 6 or higher
// if user does not set the clone type in serengeti.properties, the default type
// should still be FAST clone for hosts with 5.x version
// for cluster resize, the original cloneType should be used
if ( null == existedNodes ) {
checkAndUpdateClusterCloneType(clusterSpec, container);
}
logger.info("pre-placement task.");
//pre-placement task
String clusterCloneType = clusterSpec.getClusterCloneType();
chooseClusterCloneService(clusterCloneType).preCalculatePlacements(container, clusterSpec, existedNodes);
List<BaseNode> baseNodes =
placementService.getPlacementPlan(container, clusterSpec,
existedNodes, filteredHosts);
for (BaseNode baseNode : baseNodes) {
baseNode.setNodeAction(Constants.NODE_ACTION_CLONING_VM);
}
//Check nodegroup reserved cpu&mem ratio, then set the node's cpuReservationMHz = vcpuNum * ESXcpuMhz*ratio
for (BaseNode current : baseNodes) {
Float cpuRatio = current.getNodeGroup().getReservedCpuRatio();
Float memRatio = current.getNodeGroup().getReservedMemRatio();
//if ReservedCpu_ratio is set, caculate the reservedCpu for vm
if ( cpuRatio != null && cpuRatio > 0 && cpuRatio <= 1) {
if (current.getTargetHost() != null) {
VcHost host = VcResourceUtils.findHost(current.getTargetHost());
long nodeCpuMhz =
host.getCpuHz() / (1024 * 1024) * current.getCpu();
current.getVmSchema().resourceSchema.cpuReservationMHz
= (long) Math.ceil(nodeCpuMhz * cpuRatio);
logger.info("Base Node's cpuReservationMHz is set to "
+ current.getVmSchema().resourceSchema.cpuReservationMHz);
}
}
//Check latency&ReservedMem_ratio is set, caculate the reservedMem for vm
if ( memRatio != null && memRatio > 0 && memRatio <= 1) {
if(current.getVmSchema().resourceSchema.latencySensitivity == LatencyPriority.HIGH)
current.getVmSchema().resourceSchema.memReservationSize = current.getMem();
else{
current.getVmSchema().resourceSchema.memReservationSize
= (long) Math.ceil(current.getMem() * memRatio);
}
logger.info("Base Node's memReservationM is set to "
+ current.getVmSchema().resourceSchema.memReservationSize);
}
}
logger.info("Finished calculating provision plan");
return baseNodes;
}
@Override
public boolean startCluster(final String name,
List<NodeOperationStatus> failedNodes, StatusUpdater statusUpdator) {
logger.info("startCluster, start.");
boolean isMapDistro = clusterEntityMgr.findByName(name).getDistroVendor().equalsIgnoreCase(Constants.MAPR_VENDOR);
List<NodeEntity> nodes = clusterEntityMgr.findAllNodes(name);
logger.info("startCluster, start to create store procedures.");
List<Callable<Void>> storeProcedures = new ArrayList<Callable<Void>>();
Map<String, NodeOperationStatus> nodesStatus =
new HashMap<String, NodeOperationStatus>();
for (int i = 0; i < nodes.size(); i++) {
NodeEntity node = nodes.get(i);
VcVirtualMachine vcVm = ClusterUtil.getVcVm(clusterEntityMgr, node);
if (vcVm == null) {
// cannot find VM
logger.info("VC vm does not exist for node: " + node.getVmName());
continue;
}
StartVmSP.StartVmPrePowerOn prePowerOn = new StartVmSP.StartVmPrePowerOn(isMapDistro,
(new Gson()).toJson(node.getVolumns()));
StartVmPostPowerOn query =
new StartVmPostPowerOn(node.fetchAllPortGroups(),
Configuration.getInt(Constants.VM_POWER_ON_WAITING_SEC_KEY, Constants.VM_POWER_ON_WAITING_SEC), clusterEntityMgr);
VcHost host = null;
if (node.getHostName() != null) {
host = VcResourceUtils.findHost(node.getHostName());
}
StartVmSP startSp = new StartVmSP(vcVm, prePowerOn, query, host);
storeProcedures.add(startSp);
nodesStatus.put(node.getVmName(),
new NodeOperationStatus(node.getVmName()));
}
try {
if (storeProcedures.isEmpty()) {
logger.info("no VM is available. Return directly.");
return true;
}
Callable<Void>[] storeProceduresArray =
storeProcedures.toArray(new Callable[0]);
// execute store procedures to start VMs
logger.info("ClusteringService, start to start vms.");
BaseProgressCallback callback = new BaseProgressCallback(statusUpdator);
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProceduresArray, callback);
if (result == null) {
for (NodeOperationStatus status : nodesStatus.values()) {
status.setSucceed(false);
}
logger.error("No VM is started.");
failedNodes.addAll(nodesStatus.values());
return false;
}
boolean success = true;
int total = 0;
for (int i = 0; i < storeProceduresArray.length; i++) {
StartVmSP sp = (StartVmSP) storeProceduresArray[i];
NodeOperationStatus status = nodesStatus.get(sp.getVmName());
VcVirtualMachine vm = sp.getVcVm();
if (result[i].finished && result[i].throwable == null) {
++total;
nodesStatus.remove(status.getNodeName()); // do not return success node
} else if (result[i].throwable != null) {
status.setSucceed(false);
status.setErrorMessage(getErrorMessage(result[i].throwable));
if (vm != null
&& vm.isPoweredOn() && VcVmUtil.checkIpAddresses(vm)) {
++total;
nodesStatus.remove(status.getNodeName()); // do not return success node status
} else {
if (!vm.isConnected()
|| vm.getHost().isUnavailbleForManagement()) {
logger.error("Cannot start VM " + vm.getName()
+ " in connection state " + vm.getConnectionState()
+ " or in maintenance mode. "
+ "Ignore this VM and continue cluster operations.");
continue;
}
logger.error(
"Failed to start VM " + nodes.get(i).getVmName(),
result[i].throwable);
success = false;
}
}
}
logger.info(total + " VMs are started.");
failedNodes.addAll(nodesStatus.values());
return success;
} catch (InterruptedException e) {
logger.error("error in staring VMs", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
@Override
public boolean stopCluster(final String name,
List<NodeOperationStatus> failedNodes, StatusUpdater statusUpdator) {
logger.info("stopCluster, start.");
List<NodeEntity> nodes = clusterEntityMgr.findAllNodes(name);
logger.info("stopCluster, start to create store procedures.");
List<Callable<Void>> storeProcedures = new ArrayList<Callable<Void>>();
Map<String, NodeOperationStatus> nodesStatus =
new HashMap<String, NodeOperationStatus>();
for (int i = 0; i < nodes.size(); i++) {
NodeEntity node = nodes.get(i);
VcVirtualMachine vcVm = ClusterUtil.getVcVm(clusterEntityMgr, node);
if (vcVm == null) {
logger.info("VC vm does not exist for node: " + node.getVmName());
continue;
}
StopVmSP stopSp = new StopVmSP(vcVm);
storeProcedures.add(stopSp);
nodesStatus.put(node.getVmName(),
new NodeOperationStatus(node.getVmName()));
}
try {
if (storeProcedures.isEmpty()) {
logger.info("no VM is available. Return directly.");
return true;
}
Callable<Void>[] storeProceduresArray = storeProcedures.toArray(new Callable[0]);
// execute store procedures to start VMs
logger.info("ClusteringService, start to stop vms.");
BaseProgressCallback callback = new BaseProgressCallback(statusUpdator);
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProceduresArray, callback);
if (result == null) {
logger.error("No VM is stoped.");
for (NodeOperationStatus status : nodesStatus.values()) {
status.setSucceed(false);
}
failedNodes.addAll(nodesStatus.values());
return false;
}
boolean success = true;
int total = 0;
for (int i = 0; i < storeProceduresArray.length; i++) {
StopVmSP sp = (StopVmSP) storeProceduresArray[i];
NodeOperationStatus status = nodesStatus.get(sp.getVmName());
if (result[i].finished && result[i].throwable == null) {
++total;
nodesStatus.remove(status.getNodeName()); // do not return success node
} else if (result[i].throwable != null) {
status.setSucceed(false);
status.setErrorMessage(getErrorMessage(result[i].throwable));
VcVirtualMachine vm = sp.getVcVm();
if (vm == null || vm.isPoweredOff()) {
++total;
nodesStatus.remove(status.getNodeName()); // do not return success node
} else {
if (!vm.isConnected()
|| vm.getHost().isUnavailbleForManagement()) {
logger.error("Cannot stop VM " + vm.getName()
+ " in connection state " + vm.getConnectionState()
+ " or in maintenance mode. "
+ "Ignore this VM and continue cluster operations.");
continue;
}
logger.error("Failed to stop VM " + nodes.get(i).getVmName(),
result[i].throwable);
success = false;
}
}
}
logger.info(total + " VMs are stoped.");
return success;
} catch (InterruptedException e) {
logger.error("error in stoping VMs", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
private String getErrorMessage(Throwable throwable) {
if (throwable == null) {
return null;
}
return CommonUtil.getCurrentTimestamp() + " " + throwable.getMessage();
}
@Override
public boolean removeBadNodes(ClusterCreate cluster,
List<BaseNode> existingNodes, List<BaseNode> deletedNodes,
Map<String, Set<String>> occupiedIpSets, StatusUpdater statusUpdator) {
logger.info("Start to remove node violate placement policy "
+ "or in wrong status in cluster: " + cluster.getName());
// call tm to remove bad nodes
List<BaseNode> badNodes =
placementService.getBadNodes(cluster, existingNodes);
if (badNodes == null) {
badNodes = new ArrayList<BaseNode>();
}
logger.info("violate placement policy nodes: " + badNodes);
// append node in wrong status
for (BaseNode node : deletedNodes) {
if (node.getVmMobId() != null) {
badNodes.add(node);
} else {
node.setSuccess(true);
}
}
if (badNodes != null && badNodes.size() > 0) {
boolean deleted = syncDeleteVMs(badNodes, statusUpdator, false);
afterBadVcVmDelete(existingNodes, deletedNodes, badNodes,
occupiedIpSets);
return deleted;
}
return true;
}
@Override
public List<BaseNode> getBadNodes(ClusterCreate cluster,
List<BaseNode> existingNodes) {
return placementService.getBadNodes(cluster, existingNodes);
}
private void afterBadVcVmDelete(List<BaseNode> existingNodes,
List<BaseNode> deletedNodes, List<BaseNode> vcDeletedNodes,
Map<String, Set<String>> occupiedIpSets) {
// clean up in memory node list
deletedNodes.addAll(vcDeletedNodes);
Set<String> deletedNodeNames = new HashSet<String>();
for (BaseNode vNode : vcDeletedNodes) {
deletedNodeNames.add(vNode.getVmName());
}
for (Iterator<BaseNode> ite = existingNodes.iterator(); ite.hasNext();) {
BaseNode vNode = ite.next();
if (deletedNodeNames.contains(vNode.getVmName())) {
JobUtils.adjustOccupiedIpSets(occupiedIpSets, vNode, false);
ite.remove();
}
}
}
@Override
public boolean deleteCluster(String name, List<BaseNode> vNodes, StatusUpdater statusUpdator) {
if (vNodes == null || vNodes.size() < 1) {
logger.error("no node need to be deleted.");
return true;
}
//Find folder firstly, or will fail to find it after deleting VMs.
Folder folder = findFolder(vNodes.get(0));
boolean deleted = syncDeleteVMs(vNodes, statusUpdator, true);
try {
deleteChildRps(name, vNodes);
} catch (Exception e) {
logger.error("ignore delete resource pool error.", e);
}
try {
deleteFolders(folder);
} catch (Exception e) {
logger.error("ignore delete folder error.", e);
}
return deleted;
}
private void deleteChildRps(String hadoopClusterName, List<BaseNode> vNodes) {
logger.info("Start to delete child resource pools for cluster: "
+ hadoopClusterName);
Map<String, Map<String, VcResourcePool>> clusterMap =
new HashMap<String, Map<String, VcResourcePool>>();
for (BaseNode node : vNodes) {
String vcClusterName = node.getTargetVcCluster();
AuAssert.check(vcClusterName != null);
String vcRpName = node.getTargetRp();
if (clusterMap.get(vcClusterName) == null) {
clusterMap
.put(vcClusterName, new HashMap<String, VcResourcePool>());
}
Map<String, VcResourcePool> rpMap = clusterMap.get(vcClusterName);
if (rpMap.get(vcRpName) == null) {
VcResourcePool vcRp =
VcResourceUtils.findRPInVCCluster(vcClusterName, vcRpName);
if (vcRp != null) {
rpMap.put(vcRpName, vcRp);
}
}
}
List<VcResourcePool> rps = new ArrayList<VcResourcePool>();
for (Map<String, VcResourcePool> map : clusterMap.values()) {
rps.addAll(map.values());
}
Callable<Void>[] storedProcedures = new Callable[rps.size()];
String childRp = ConfigInfo.getSerengetiUUID() + "-" + hadoopClusterName;
int i = 0;
for (VcResourcePool rp : rps) {
DeleteRpSp sp = new DeleteRpSp(rp, childRp);
storedProcedures[i] = sp;
i++;
}
try {
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storedProcedures, callback);
if (result == null || result.length == 0) {
logger.error("No rp is deleted.");
return;
}
int total = 0;
for (int j = 0; j < storedProcedures.length; j++) {
if (result[j].throwable != null) {
DeleteRpSp sp = (DeleteRpSp) storedProcedures[j];
logger.error(
"Failed to delete child resource pool "
+ sp.getDeleteRpName() + " under " + sp.getVcRp(),
result[j].throwable);
} else {
total++;
}
}
} catch (InterruptedException e) {
logger.error("error in deleting resource pools", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
private Folder findFolder(BaseNode node) throws BddException {
String path = node.getVmFolder();
String mobid = node.getVmMobId();
// path format: <serengeti...>/<cluster name>/<group name>
String[] folderNames = path.split("/");
AuAssert.check(folderNames.length == 3);
VcDatacenter dc = VcResourceUtils.findVM(mobid).getDatacenter();
List<String> deletedFolders = new ArrayList<String>();
deletedFolders.add(folderNames[0]);
deletedFolders.add(folderNames[1]);
Folder folder = null;
try {
folder = VcResourceUtils.findFolderByNameList(dc, deletedFolders);
} catch (Exception e) {
logger.error("error in finding folders", e);
throw BddException.INTERNAL(e, e.getMessage());
}
String clusterFolderName = folderNames[0] + "/" + folderNames[1];
logger.info("find cluster root folder: " + clusterFolderName + "test: folder.name=" + folder.getName() + " folder.ref=" + folder._getRef().getValue());
return folder;
}
/**
* this method will delete the cluster root folder, if there is any VM
* existed and powered on in the folder, the folder deletion will fail.
*
* @param folder
* @throws BddException
*/
private void deleteFolders(Folder folder) throws BddException {
if (folder == null) {
logger.info("No folder to delete.");
return;
}
List<Folder> folders = new ArrayList<Folder>();
folders.add(folder);
DeleteVMFolderSP sp = new DeleteVMFolderSP(folders, true, false);
Callable<Void>[] storedProcedures = new Callable[1];
storedProcedures[0] = sp;
try {
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storedProcedures, callback);
if (result == null || result.length == 0) {
logger.error("No folder is deleted.");
return;
}
if (result[0].finished && result[0].throwable == null) {
logger.info("Cluster folder " + folder.getName() + " is deleted.");
} else {
logger.info("Failed to delete cluster folder " + folder.getName(),
result[0].throwable);
}
} catch (InterruptedException e) {
logger.error("error in deleting folders", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
@Override
@SuppressWarnings("unchecked")
public boolean syncDeleteVMs(List<BaseNode> badNodes,
StatusUpdater statusUpdator, boolean ignoreUnavailableNodes) {
logger.info("syncDeleteVMs, start to create store procedures.");
List<Callable<Void>> storeProcedures = new ArrayList<Callable<Void>>();
List<BaseNode> toBeDeleted = new ArrayList<BaseNode>();
for (int i = 0; i < badNodes.size(); i++) {
BaseNode node = badNodes.get(i);
if (node.getVmMobId() == null) {
// vm is already deleted
node.setSuccess(true);
continue;
}
DeleteVmByIdSP deleteSp = new DeleteVmByIdSP(node.getVmMobId());
storeProcedures.add(deleteSp);
toBeDeleted.add(node);
}
try {
if (storeProcedures.isEmpty()) {
logger.info("no VM is created. Return directly.");
return true;
}
Callable<Void>[] storeProceduresArray =
storeProcedures.toArray(new Callable[0]);
// execute store procedures to delete VMs
logger.info("ClusteringService, start to delete vms.");
BaseProgressCallback callback =
new BaseProgressCallback(statusUpdator, 0, 50);
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProceduresArray, callback);
if (result == null) {
logger.error("No VM is deleted.");
return false;
}
int total = 0;
boolean failed = false;
for (int i = 0; i < storeProceduresArray.length; i++) {
BaseNode vNode = toBeDeleted.get(i);
vNode.setFinished(true);
if (result[i].finished && result[i].throwable == null) {
vNode.setSuccess(true);
vNode.setVmMobId(null);
++total;
} else if (result[i].throwable != null) {
vNode.setSuccess(false);
vNode.setErrMessage(getErrorMessage(result[i].throwable));
if (ignoreUnavailableNodes) {
DeleteVmByIdSP sp = (DeleteVmByIdSP) storeProceduresArray[i];
VcVirtualMachine vcVm = sp.getVcVm();
if (!vcVm.isConnected()
|| vcVm.getHost().isUnavailbleForManagement()) {
logger.error("Failed to delete VM " + vcVm.getName()
+ " in connection state "
+ vcVm.getConnectionState()
+ " or in maintenance mode.");
logger.error("Ignore this failure and continue cluster operations.");
continue;
}
}
logger.error("Failed to delete VM " + vNode.getVmName(),
result[i].throwable);
failed = true;
}
vNode.setFinished(true);
}
logger.info(total + " VMs are deleted.");
return !failed;
} catch (InterruptedException e) {
logger.error("error in deleting VMs", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
@Override
public UUID reserveResource(String clusterName) {
ResourceReservation reservation = new ResourceReservation();
reservation.setClusterName(clusterName);
return resMgr.reserveResource(reservation);
}
@Override
public void commitReservation(UUID reservationId) throws VcProviderException {
resMgr.commitReservation(reservationId);
}
@Override
public Map<String, String> configIOShares(String clusterName, List<NodeEntity> targetNodes,
Priority ioShares) {
AuAssert.check(clusterName != null && targetNodes != null
&& !targetNodes.isEmpty());
Callable<Void>[] storeProcedures = new Callable[targetNodes.size()];
int i = 0;
for (NodeEntity node : targetNodes) {
ConfigIOShareSP ioShareSP =
new ConfigIOShareSP(node.getMoId(), ioShares);
storeProcedures[i] = ioShareSP;
i++;
}
try {
// execute store procedures to configure io shares
logger.info("ClusteringService, start to reconfigure vm's io shares.");
NoProgressUpdateCallback callback = new NoProgressUpdateCallback();
ExecutionResult[] result =
Scheduler
.executeStoredProcedures(
com.vmware.aurora.composition.concurrent.Priority.BACKGROUND,
storeProcedures, callback);
if (result == null) {
logger.error("No VM's io share level is reconfigured.");
throw ClusteringServiceException
.RECONFIGURE_IO_SHARE_FAILED(clusterName);
}
int total = 0;
Map<String, String> failedNodes = new HashMap<String, String>();
for (i = 0; i < storeProcedures.length; i++) {
if (result[i].finished && result[i].throwable == null) {
++total;
} else if (result[i].throwable != null) {
logger.error("Failed to reconfigure vm", result[i].throwable);
String nodeName = targetNodes.get(i).getVmName();
String message = CommonUtil.getCurrentTimestamp() + " " + result[i].throwable.getMessage();
failedNodes.put(nodeName, message);
}
}
logger.info(total + " vms are reconfigured.");
return failedNodes;
} catch (InterruptedException e) {
logger.error("error in reconfiguring vm io shares", e);
throw BddException.INTERNAL(e, e.getMessage());
}
}
@Override
public boolean startSingleVM(String clusterName, String nodeName,
StatusUpdater statusUpdator) {
NodeEntity node = this.clusterEntityMgr.findNodeByName(nodeName);
boolean reserveRawDisks = clusterEntityMgr.findByName(clusterName).getDistroVendor().equalsIgnoreCase(Constants.MAPR_VENDOR);
// For node scale up/down, the disk info in db is not yet updated when powering on it, need to fetch from VC
List<DiskSpec> diskSpecList = VcVmUtil.toDiskSpecList(node.getDisks());
StartVmSP.StartVmPrePowerOn prePowerOn = new StartVmSP.StartVmPrePowerOn(reserveRawDisks,
VcVmUtil.getVolumesFromSpecs(node.getMoId(), diskSpecList));
StartVmPostPowerOn query =
new StartVmPostPowerOn(node.fetchAllPortGroups(),
Configuration.getInt(Constants.VM_POWER_ON_WAITING_SEC_KEY, Constants.VM_POWER_ON_WAITING_SEC), clusterEntityMgr);
VcHost host = null;
if (node.getHostName() != null) {
host = VcResourceUtils.findHost(node.getHostName());
}
VcVirtualMachine vcVm = ClusterUtil.getVcVm(clusterEntityMgr, node);
if (vcVm == null) {
logger.info("VC vm does not exist for node: " + node.getVmName());
return false;
}
StartVmSP startVMSP = new StartVmSP(vcVm, prePowerOn, query, host);
return VcVmUtil.runSPOnSingleVM(node, startVMSP);
}
@Override
public boolean stopSingleVM(String clusterName, String nodeName,
StatusUpdater statusUpdator, boolean... vmPoweroff) {
NodeEntity node = this.clusterEntityMgr.findNodeByName(nodeName);
VcVirtualMachine vcVm = ClusterUtil.getVcVm(clusterEntityMgr, node);
if (vcVm == null) {
// cannot find VM
logger.info("VC vm does not exist for node: " + node.getVmName());
return false;
}
StopVmSP stopVMSP;
if (vmPoweroff.length > 0 && vmPoweroff[0]) {
stopVMSP = new StopVmSP(vcVm, true);
} else {
stopVMSP = new StopVmSP(vcVm);
}
return VcVmUtil.runSPOnSingleVM(node, stopVMSP);
}
@Override
public VmEventManager getEventProcessor() {
return this.processor;
}
@Override
public boolean isSupportVHM(String clusterName) {
ClusterEntity cluster = getClusterEntityMgr().findByName(clusterName);
SoftwareManager softMgr =
softwareManagerCollector
.getSoftwareManagerByClusterName(clusterName);
if (!softMgr.hasComputeMasterGroup(getClusterEntityMgr()
.toClusterBluePrint(clusterName))) {
logger.warn("Use of auto elasticity, must configure Jobtracker or ResourceManager.");
return false;
}
return true;
}
@Override
public boolean addNodeGroups(ClusterCreate clusterSpec, NodeGroupCreate[] nodeGroupsAdd,
List<BaseNode> vNodes) {
boolean success = false;
List<NodeGroupCreate> newNodeGroups = new ArrayList<NodeGroupCreate>();
if (clusterSpec != null && clusterSpec.getNodeGroups() != null) {
for (NodeGroupCreate ng : clusterSpec.getNodeGroups()) {
newNodeGroups.add(ng);
}
}
if (nodeGroupsAdd != null) {
for (NodeGroupCreate ng : nodeGroupsAdd) {
newNodeGroups.add(ng);
}
}
if (clusterSpec != null) {
clusterSpec.setNodeGroups(newNodeGroups
.toArray(new NodeGroupCreate[newNodeGroups.size()]));
}
if(null != createVcFolders(clusterSpec, true)) {
if(null != createVcResourcePools(vNodes, true)) {
success = true;
}
}
return success;
}
private void checkAndUpdateClusterCloneType(ClusterCreate clusterSpec, Container container) {
String clusterName = clusterSpec.getName();
String type = Configuration.getString("cluster.clone.service");
if ( StringUtils.isBlank(type) ) {
// check if there are any ESXi hosts with version lower than 6.0
boolean isLowVersion = false;
for ( AbstractHost host : container.getAllHosts() ) {
String hostName = host.getName();
final VcHost vchost = VcResourceUtils.findHost(hostName);
AuAssert.check(vchost != null, String.format("can' find host: %1s in VC.", hostName));
String version = vchost.getVersion();
if ( Version.compare(version, Constants.VCENTER_VERSION_6 ) < 0 ) {
logger.info("Some ESXi host version is lower than 6.0.");
isLowVersion = true;
break;
}
}
// if some ESXi host version is lower than 6.0, update the cluster clone type to FAST
if ( isLowVersion ) {
ClusterEntity cluster = clusterEntityMgr.findByName(clusterName);
String advProps = cluster.getAdvancedProperties();
if (!CommonUtil.isBlank(advProps)) {
Gson gson = new Gson();
Map<String, String> advancedProperties =
gson.fromJson(advProps, Map.class);
String cloneType = advancedProperties.get("ClusterCloneType");
if ( !StringUtils.isBlank(cloneType)
&& cloneType.equals(Constants.CLUSTER_CLONE_TYPE_INSTANT_CLONE) ) {
cloneType = Constants.CLUSTER_CLONE_TYPE_FAST_CLONE;
advancedProperties.put("ClusterCloneType", cloneType);
cluster.setAdvancedProperties(gson.toJson(advancedProperties));
logger.info("Change cluster clone type to fast clone because of ESXi version lower than 6.0.");
clusterEntityMgr.update(cluster);
clusterSpec.setClusterCloneType(cloneType);
}
}
}
}
}
}