/*************************************************************************** * 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.manager; import java.util.ArrayList; import java.util.List; import com.vmware.bdd.aop.annotation.ClusterManagerPointcut; import org.apache.log4j.Logger; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import com.vmware.bdd.apitypes.ClusterStatus; import com.vmware.bdd.apitypes.NodeStatus; import com.vmware.bdd.apitypes.ResourceScale; import com.vmware.bdd.entity.NodeEntity; import com.vmware.bdd.entity.NodeGroupEntity; import com.vmware.bdd.exception.ScaleServiceException; import com.vmware.bdd.exception.VcProviderException; import com.vmware.bdd.manager.intf.IClusterEntityManager; import com.vmware.bdd.service.job.JobConstants; import com.vmware.bdd.service.utils.VcResourceUtils; import com.vmware.bdd.utils.ValidationUtils; import com.vmware.bdd.utils.VcVmUtil; /** * @author Jarred Li * @version 0.9 * @since 0.9 * */ public class ScaleManager { private static final Logger logger = Logger.getLogger(ScaleManager.class); private IClusterEntityManager clusterEntityMgr; private JobManager jobManager; @Autowired private UnsupportedOpsBlocker opsBlocker; @ClusterManagerPointcut public long scaleNodeGroupResource(ResourceScale scale) throws Exception { String clusterName = scale.getClusterName(); opsBlocker.blockUnsupportedOpsByCluster("scale Up/Down", clusterName); ValidationUtils.validateVersion(clusterEntityMgr, clusterName); ClusterStatus originalStatus = clusterEntityMgr.findByName(clusterName).getStatus(); logger.info("before scaling, cluster status is:" + originalStatus); if (scale.getMemory() > 0) { //VM's memory must be divisible by 4, otherwise VM can not be started long converted = VcVmUtil.makeVmMemoryDivisibleBy4(scale.getMemory()); logger.info("user's setting for memory: " + scale.getMemory() + " converted to : " + converted); scale.setMemory(converted); } List<JobParameters> jobParametersList = buildJobParameters(scale); if (jobParametersList.size() == 0) { throw ScaleServiceException.NOT_NEEDED(clusterName); } String nodeGroupName = scale.getNodeGroupName(); List<NodeEntity> nodes = clusterEntityMgr.findAllNodes(clusterName, nodeGroupName); if (nodes.size() > 0) { // vm max configuration check VcResourceUtils.checkVmMaxConfiguration(nodes.get(0).getMoId(), scale.getCpuNumber(), scale.getMemory()); if (scale.getCpuNumber() > 1) { //cpu number check for vm with FT enabled for (NodeEntity nodeEntity : nodes) { VcResourceUtils.checkVmFTAndCpuNumber(nodeEntity.getMoId(), nodeEntity.getVmName(), scale.getCpuNumber()); if (!VcVmUtil.validateCPU(nodeEntity.getMoId(), scale.getCpuNumber())) { throw VcProviderException.CPU_NUM_NOT_MULTIPLE_OF_CORES_PER_SOCKET(scale.getNodeGroupName(), nodeEntity.getVmName()); } } } } updateNodeGroupResource(scale); //launch sub job to scale node one by one try { logger.info("set cluster to maintenace"); clusterEntityMgr.updateClusterStatus(clusterName, ClusterStatus.MAINTENANCE); logger.info("current cluster status: " + clusterEntityMgr.findByName(clusterName).getStatus()); return jobManager.runSubJobForNodes(JobConstants.NODE_SCALE_JOB_NAME, jobParametersList, clusterName, originalStatus, ClusterStatus.ERROR); } catch (Throwable t) { logger.error("Failed to start cluster " + clusterName, t); clusterEntityMgr.updateClusterStatus(clusterName, originalStatus); throw ScaleServiceException.JOB_LAUNCH_FAILURE(clusterName, t, t.getMessage()); } } /** * @param scale * @return */ @Transactional public List<JobParameters> buildJobParameters(ResourceScale scale) { String clusterName = scale.getClusterName(); String nodeGroupName = scale.getNodeGroupName(); List<NodeEntity> nodes = clusterEntityMgr.findAllNodes(clusterName, nodeGroupName); List<JobParameters> jobParametersList = new ArrayList<JobParameters>(); JobParametersBuilder parametersBuilder = new JobParametersBuilder(); for (NodeEntity nodeEntity : nodes) { if (nodeEntity.isObsoleteNode()) { logger.info("Ingore node " + nodeEntity.getVmName() + ", for it violate VM name convention " + "or exceed defined group instance number. "); continue; } if ((nodeEntity.getCpuNum() != scale.getCpuNumber() && scale .getCpuNumber() > 0) || (nodeEntity.getMemorySize() != scale.getMemory() && scale .getMemory() > 0)) { logger.info("original cpu number :" + nodeEntity.getCpuNum() + ". Expected cpu number: " + scale.getCpuNumber()); String nodeName = nodeEntity.getVmName(); boolean vmPowerOn = (nodeEntity.getStatus().ordinal() != NodeStatus.POWERED_OFF .ordinal()); logger.debug("orginal vm power on? " + vmPowerOn); JobParameters nodeParameters = parametersBuilder .addString(JobConstants.SUB_JOB_NODE_NAME, nodeName) .addString(JobConstants.TARGET_NAME_JOB_PARAM, nodeName) .addString(JobConstants.CLUSTER_NAME_JOB_PARAM, clusterName) .addString(JobConstants.NODE_SCALE_CPU_NUMBER, String.valueOf(scale.getCpuNumber())) .addString(JobConstants.NODE_SCALE_MEMORY_SIZE, String.valueOf(scale.getMemory())) .addString(JobConstants.IS_VM_POWER_ON, String.valueOf(vmPowerOn)).toJobParameters(); jobParametersList.add(nodeParameters); } else { logger.info("This node does need to be scaled. " + nodeEntity.getVmName()); continue; } } return jobParametersList; } public void updateNodeGroupResource(ResourceScale scale) { NodeGroupEntity nodeGroup = clusterEntityMgr.findByName(scale.getClusterName(), scale.getNodeGroupName()); if (scale.getCpuNumber() > 0) { nodeGroup.setCpuNum(scale.getCpuNumber()); } if (scale.getMemory() > 0) { nodeGroup.setMemorySize((int) scale.getMemory()); } clusterEntityMgr.update(nodeGroup); } /** * @return the clusterEntityMgr */ public IClusterEntityManager getClusterEntityMgr() { return clusterEntityMgr; } /** * @param clusterEntityMgr * the clusterEntityMgr to set */ public void setClusterEntityMgr(IClusterEntityManager clusterEntityMgr) { this.clusterEntityMgr = clusterEntityMgr; } /** * @return the jobManager */ public JobManager getJobManager() { return jobManager; } /** * @param jobManager * the jobManager to set */ public void setJobManager(JobManager jobManager) { this.jobManager = jobManager; } }