| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you 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.cloud.network.as; | |
| import java.security.SecureRandom; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.Date; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.concurrent.BlockingQueue; | |
| import java.util.concurrent.Callable; | |
| import java.util.concurrent.CompletionService; | |
| import java.util.concurrent.ExecutionException; | |
| import java.util.concurrent.ExecutorCompletionService; | |
| import java.util.concurrent.ExecutorService; | |
| import java.util.concurrent.Executors; | |
| import java.util.concurrent.Future; | |
| import java.util.concurrent.LinkedBlockingQueue; | |
| import java.util.concurrent.ScheduledExecutorService; | |
| import java.util.concurrent.TimeUnit; | |
| import javax.inject.Inject; | |
| import com.cloud.network.NetworkModel; | |
| import org.apache.cloudstack.acl.ControlledEntity; | |
| import org.apache.cloudstack.affinity.AffinityGroupVO; | |
| import org.apache.cloudstack.affinity.dao.AffinityGroupDao; | |
| import org.apache.cloudstack.annotation.AnnotationService; | |
| import org.apache.cloudstack.annotation.dao.AnnotationDao; | |
| import org.apache.cloudstack.api.ApiCommandResourceType; | |
| import org.apache.cloudstack.api.ApiConstants; | |
| import org.apache.cloudstack.api.ApiErrorCode; | |
| import org.apache.cloudstack.api.BaseCmd.HTTPMethod; | |
| import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; | |
| import org.apache.cloudstack.api.ServerApiException; | |
| import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScaleVmGroupCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScaleVmProfileCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.CreateConditionCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.ListAutoScalePoliciesCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.ListAutoScaleVmGroupsCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.ListAutoScaleVmProfilesCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.ListConditionsCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.ListCountersCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScalePolicyCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScaleVmGroupCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScaleVmProfileCmd; | |
| import org.apache.cloudstack.api.command.user.autoscale.UpdateConditionCmd; | |
| import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; | |
| import org.apache.cloudstack.config.ApiServiceConfiguration; | |
| import org.apache.cloudstack.context.CallContext; | |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; | |
| import org.apache.cloudstack.framework.config.ConfigKey; | |
| import org.apache.cloudstack.framework.config.Configurable; | |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao; | |
| import org.apache.cloudstack.managed.context.ManagedContextRunnable; | |
| import org.apache.cloudstack.userdata.UserDataManager; | |
| import org.apache.commons.collections.CollectionUtils; | |
| import org.apache.commons.collections.MapUtils; | |
| import org.apache.commons.lang3.RandomStringUtils; | |
| import org.apache.commons.lang3.StringUtils; | |
| import com.cloud.agent.AgentManager; | |
| import com.cloud.agent.api.PerformanceMonitorAnswer; | |
| import com.cloud.agent.api.PerformanceMonitorCommand; | |
| import com.cloud.agent.api.VmStatsEntry; | |
| import com.cloud.agent.api.routing.GetAutoScaleMetricsAnswer; | |
| import com.cloud.agent.api.routing.GetAutoScaleMetricsCommand; | |
| import com.cloud.agent.api.to.LoadBalancerTO.AutoScalePolicyTO; | |
| import com.cloud.agent.api.to.LoadBalancerTO.AutoScaleVmGroupTO; | |
| import com.cloud.agent.api.to.LoadBalancerTO.AutoScaleVmProfileTO; | |
| import com.cloud.agent.api.to.LoadBalancerTO.ConditionTO; | |
| import com.cloud.agent.api.to.LoadBalancerTO.CounterTO; | |
| import com.cloud.api.ApiDBUtils; | |
| import com.cloud.api.dispatch.DispatchChainFactory; | |
| import com.cloud.api.dispatch.DispatchTask; | |
| import com.cloud.configuration.ConfigurationManager; | |
| import com.cloud.dc.DataCenter; | |
| import com.cloud.dc.DataCenter.NetworkType; | |
| import com.cloud.dc.dao.DataCenterDao; | |
| import com.cloud.event.ActionEvent; | |
| import com.cloud.event.ActionEventUtils; | |
| import com.cloud.event.EventTypes; | |
| import com.cloud.event.EventVO; | |
| import com.cloud.exception.ConcurrentOperationException; | |
| import com.cloud.exception.InsufficientCapacityException; | |
| import com.cloud.exception.InsufficientServerCapacityException; | |
| import com.cloud.exception.InvalidParameterValueException; | |
| import com.cloud.exception.ResourceAllocationException; | |
| import com.cloud.exception.ResourceInUseException; | |
| import com.cloud.exception.ResourceUnavailableException; | |
| import com.cloud.host.HostVO; | |
| import com.cloud.host.dao.HostDao; | |
| import com.cloud.hypervisor.Hypervisor.HypervisorType; | |
| import com.cloud.network.Network; | |
| import com.cloud.network.Network.Capability; | |
| import com.cloud.network.Network.IpAddresses; | |
| import com.cloud.network.as.AutoScaleCounter.AutoScaleCounterParam; | |
| import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; | |
| import com.cloud.network.as.dao.AutoScalePolicyDao; | |
| import com.cloud.network.as.dao.AutoScaleVmGroupDao; | |
| import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; | |
| import com.cloud.network.as.dao.AutoScaleVmGroupStatisticsDao; | |
| import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; | |
| import com.cloud.network.as.dao.AutoScaleVmProfileDao; | |
| import com.cloud.network.as.dao.ConditionDao; | |
| import com.cloud.network.as.dao.CounterDao; | |
| import com.cloud.network.dao.IPAddressDao; | |
| import com.cloud.network.dao.IPAddressVO; | |
| import com.cloud.network.dao.LoadBalancerDao; | |
| import com.cloud.network.dao.LoadBalancerVMMapDao; | |
| import com.cloud.network.dao.LoadBalancerVMMapVO; | |
| import com.cloud.network.dao.LoadBalancerVO; | |
| import com.cloud.network.dao.NetworkDao; | |
| import com.cloud.network.lb.LoadBalancingRulesManager; | |
| import com.cloud.network.lb.LoadBalancingRulesService; | |
| import com.cloud.network.router.RouterControlHelper; | |
| import com.cloud.network.router.VirtualRouter; | |
| import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleMetrics; | |
| import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleMetricsValue; | |
| import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleValueType; | |
| import com.cloud.network.router.VirtualRouterAutoScale.VirtualRouterAutoScaleCounter; | |
| import com.cloud.offering.DiskOffering; | |
| import com.cloud.offering.NetworkOffering; | |
| import com.cloud.offering.ServiceOffering; | |
| import com.cloud.offerings.dao.NetworkOfferingDao; | |
| import com.cloud.projects.Project.ListProjectResourcesCriteria; | |
| import com.cloud.server.ResourceTag; | |
| import com.cloud.service.ServiceOfferingVO; | |
| import com.cloud.service.dao.ServiceOfferingDao; | |
| import com.cloud.storage.dao.DiskOfferingDao; | |
| import com.cloud.template.TemplateManager; | |
| import com.cloud.template.VirtualMachineTemplate; | |
| import com.cloud.user.Account; | |
| import com.cloud.user.AccountManager; | |
| import com.cloud.user.AccountService; | |
| import com.cloud.user.SSHKeyPairVO; | |
| import com.cloud.user.User; | |
| import com.cloud.user.dao.AccountDao; | |
| import com.cloud.user.dao.SSHKeyPairDao; | |
| import com.cloud.user.dao.UserDao; | |
| import com.cloud.uservm.UserVm; | |
| import com.cloud.utils.Pair; | |
| import com.cloud.utils.Ternary; | |
| import com.cloud.utils.component.ComponentContext; | |
| import com.cloud.utils.component.ManagerBase; | |
| import com.cloud.utils.concurrency.NamedThreadFactory; | |
| import com.cloud.utils.db.DB; | |
| import com.cloud.utils.db.EntityManager; | |
| import com.cloud.utils.db.Filter; | |
| import com.cloud.utils.db.GenericDao; | |
| import com.cloud.utils.db.GenericSearchBuilder; | |
| import com.cloud.utils.db.JoinBuilder; | |
| import com.cloud.utils.db.SearchBuilder; | |
| import com.cloud.utils.db.SearchCriteria; | |
| import com.cloud.utils.db.SearchCriteria.Op; | |
| import com.cloud.utils.db.Transaction; | |
| import com.cloud.utils.db.TransactionCallback; | |
| import com.cloud.utils.db.TransactionStatus; | |
| import com.cloud.utils.exception.CloudRuntimeException; | |
| import com.cloud.utils.net.NetUtils; | |
| import com.cloud.vm.DomainRouterVO; | |
| import com.cloud.vm.UserVmManager; | |
| import com.cloud.vm.UserVmService; | |
| import com.cloud.vm.UserVmVO; | |
| import com.cloud.vm.VMInstanceVO; | |
| import com.cloud.vm.VirtualMachine; | |
| import com.cloud.vm.VirtualMachineManager; | |
| import com.cloud.vm.VmDetailConstants; | |
| import com.cloud.vm.VmStats; | |
| import com.cloud.vm.dao.DomainRouterDao; | |
| import com.cloud.vm.dao.UserVmDao; | |
| import com.cloud.vm.dao.VMInstanceDao; | |
| import com.google.gson.Gson; | |
| import com.google.gson.reflect.TypeToken; | |
| public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManager, AutoScaleService, Configurable { | |
| @Inject | |
| protected DispatchChainFactory dispatchChainFactory = null; | |
| @Inject | |
| EntityManager entityMgr; | |
| @Inject | |
| AccountDao accountDao; | |
| @Inject | |
| AccountManager accountMgr; | |
| @Inject | |
| ConfigurationManager configMgr; | |
| @Inject | |
| TemplateManager templateMgr; | |
| @Inject | |
| LoadBalancingRulesManager lbRulesMgr; | |
| @Inject | |
| NetworkDao networkDao; | |
| @Inject | |
| CounterDao counterDao; | |
| @Inject | |
| ConditionDao conditionDao; | |
| @Inject | |
| LoadBalancerDao lbDao; | |
| @Inject | |
| AutoScaleVmProfileDao autoScaleVmProfileDao; | |
| @Inject | |
| AutoScalePolicyDao autoScalePolicyDao; | |
| @Inject | |
| AutoScalePolicyConditionMapDao autoScalePolicyConditionMapDao; | |
| @Inject | |
| AutoScaleVmGroupDao autoScaleVmGroupDao; | |
| @Inject | |
| AutoScaleVmGroupPolicyMapDao autoScaleVmGroupPolicyMapDao; | |
| @Inject | |
| AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao; | |
| @Inject | |
| DataCenterDao dcDao = null; | |
| @Inject | |
| UserDao userDao; | |
| @Inject | |
| ConfigurationDao configDao; | |
| @Inject | |
| IPAddressDao ipAddressDao; | |
| @Inject | |
| AccountService accountService; | |
| @Inject | |
| UserVmService userVmService; | |
| @Inject | |
| LoadBalancerVMMapDao lbVmMapDao; | |
| @Inject | |
| LoadBalancingRulesService loadBalancingRulesService; | |
| @Inject | |
| private VMInstanceDao vmInstanceDao; | |
| @Inject | |
| private AgentManager agentMgr; | |
| @Inject | |
| private ServiceOfferingDao serviceOfferingDao; | |
| @Inject | |
| NetworkOrchestrationService networkMgr; | |
| @Inject | |
| NetworkModel networkModel; | |
| @Inject | |
| private UserVmManager userVmMgr; | |
| @Inject | |
| private UserDataManager userDataMgr; | |
| @Inject | |
| private UserVmDao userVmDao; | |
| @Inject | |
| private HostDao hostDao; | |
| @Inject | |
| private AutoScaleVmGroupStatisticsDao asGroupStatisticsDao; | |
| @Inject | |
| private DomainRouterDao routerDao; | |
| @Inject | |
| private AnnotationDao annotationDao; | |
| @Inject | |
| protected RouterControlHelper routerControlHelper; | |
| @Inject | |
| private DiskOfferingDao diskOfferingDao; | |
| @Inject | |
| private SSHKeyPairDao sshKeyPairDao; | |
| @Inject | |
| private AffinityGroupDao affinityGroupDao; | |
| @Inject | |
| private NetworkOfferingDao networkOfferingDao; | |
| @Inject | |
| private VirtualMachineManager virtualMachineManager; | |
| private static final String PARAM_ROOT_DISK_SIZE = "rootdisksize"; | |
| private static final String PARAM_DISK_OFFERING_ID = "diskofferingid"; | |
| private static final String PARAM_DISK_SIZE = "disksize"; | |
| private static final String PARAM_OVERRIDE_DISK_OFFERING_ID = "overridediskofferingid"; | |
| private static final String PARAM_SECURITY_GROUP_IDS = "securitygroupids"; | |
| private static final String PARAM_SSH_KEYPAIRS = "keypairs"; | |
| private static final String PARAM_AFFINITY_GROUP_IDS = "affinitygroupids"; | |
| private static final String PARAM_NETWORK_IDS = "networkids"; | |
| private static final List<String> supportedDeployParams = Arrays.asList(PARAM_ROOT_DISK_SIZE, PARAM_DISK_OFFERING_ID, PARAM_DISK_SIZE, PARAM_SECURITY_GROUP_IDS, | |
| PARAM_OVERRIDE_DISK_OFFERING_ID, PARAM_SSH_KEYPAIRS, PARAM_AFFINITY_GROUP_IDS, PARAM_NETWORK_IDS); | |
| protected static final String VM_HOSTNAME_PREFIX = "autoScaleVm-"; | |
| protected static final int VM_HOSTNAME_RANDOM_SUFFIX_LENGTH = 6; | |
| private static final Long DEFAULT_HOST_ID = -1L; | |
| ExecutorService groupExecutor; | |
| CompletionService<Pair<Long, Boolean>> completionService; | |
| Map<Long, ScheduledExecutorService> vmGroupMonitorMaps = new HashMap<>(); | |
| @Override | |
| public boolean start() { | |
| // create thread pool and blocking queue | |
| final int workersCount = AutoScaleStatsWorker.value(); | |
| groupExecutor = Executors.newFixedThreadPool(workersCount); | |
| logger.info("AutoScale Manager created a thread pool to check autoscale vm groups. The pool size is : " + workersCount); | |
| final BlockingQueue<Future<Pair<Long, Boolean>>> queue = new LinkedBlockingQueue<>(workersCount); | |
| logger.info("AutoScale Manager created a blocking queue to check autoscale vm groups. The queue size is : " + workersCount); | |
| completionService = new ExecutorCompletionService<>(groupExecutor, queue); | |
| scheduleMonitorTasks(); | |
| return true; | |
| } | |
| @Override | |
| public boolean stop() { | |
| if (groupExecutor != null) { | |
| groupExecutor.shutdown(); | |
| } | |
| return true; | |
| } | |
| public List<AutoScaleCounter> getSupportedAutoScaleCounters(long networkid) { | |
| String capability = lbRulesMgr.getLBCapability(networkid, Capability.AutoScaleCounters.getName()); | |
| if (capability == null) { | |
| return null; | |
| } | |
| Gson gson = new Gson(); | |
| java.lang.reflect.Type listType = new TypeToken<List<AutoScaleCounter>>() { | |
| }.getType(); | |
| List<AutoScaleCounter> result = gson.fromJson(capability, listType); | |
| return result; | |
| } | |
| public void validateNetworkCapability(long networkId) { | |
| Network network = networkDao.findById(networkId); | |
| if (network == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find network with id: %s ", networkId)); | |
| } | |
| NetworkOffering offering = networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); | |
| if (!offering.isSupportsVmAutoScaling()) { | |
| throw new InvalidParameterValueException("Vm AutoScaling is not supported by this network."); | |
| } | |
| } | |
| public void validateAutoScaleCounters(long networkid, List<Counter> counters, List<Pair<String, String>> counterParamPassed) { | |
| List<AutoScaleCounter> supportedCounters = getSupportedAutoScaleCounters(networkid); | |
| if (supportedCounters == null) { | |
| throw new InvalidParameterValueException("AutoScale is not supported in the network"); | |
| } | |
| for (Counter counter : counters) { | |
| String counterName = counter.getSource().name(); | |
| boolean isCounterSupported = false; | |
| for (AutoScaleCounter autoScaleCounter : supportedCounters) { | |
| if (autoScaleCounter.getName().equals(counterName)) { | |
| isCounterSupported = true; | |
| List<AutoScaleCounterParam> counterParams = autoScaleCounter.getParamList(); | |
| for (AutoScaleCounterParam autoScaleCounterParam : counterParams) { | |
| boolean isRequiredParameter = autoScaleCounterParam.getRequired(); | |
| if (isRequiredParameter) { | |
| boolean isRequiredParamPresent = false; | |
| for (Pair<String, String> pair : counterParamPassed) { | |
| if (pair.first().equals(autoScaleCounterParam.getParamName())) | |
| isRequiredParamPresent = true; | |
| } | |
| if (!isRequiredParamPresent) { | |
| throw new InvalidParameterValueException("Parameter " + autoScaleCounterParam.getParamName() + " has to be set in AutoScaleVmProfile's " + | |
| ApiConstants.COUNTERPARAM_LIST); | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| if (!isCounterSupported) { | |
| throw new InvalidParameterValueException("AutoScale counter with source='" + counter.getSource().name() + "' is not supported " + "in the network"); | |
| } | |
| } | |
| } | |
| private <VO extends ControlledEntity> VO getEntityInDatabase(Account caller, String paramName, Long id, GenericDao<VO, Long> dao) { | |
| VO vo = dao.findById(id); | |
| if (vo == null) { | |
| throw new InvalidParameterValueException("Unable to find " + paramName); | |
| } | |
| accountMgr.checkAccess(caller, null, false, (ControlledEntity)vo); | |
| return vo; | |
| } | |
| private boolean isAutoScaleScaleUpPolicy(AutoScalePolicy policyVO) { | |
| return policyVO.getAction().equals(AutoScalePolicy.Action.SCALEUP); | |
| } | |
| private List<AutoScalePolicyVO> getAutoScalePolicies(List<Long> policyIds, List<Counter> counters, int interval, boolean scaleUpPolicies) { | |
| SearchBuilder<AutoScalePolicyVO> policySearch = autoScalePolicyDao.createSearchBuilder(); | |
| policySearch.and("ids", policySearch.entity().getId(), Op.IN); | |
| policySearch.done(); | |
| SearchCriteria<AutoScalePolicyVO> sc = policySearch.create(); | |
| sc.setParameters("ids", policyIds.toArray(new Object[0])); | |
| List<AutoScalePolicyVO> policies = autoScalePolicyDao.search(sc, null); | |
| for (AutoScalePolicyVO policy : policies) { | |
| int duration = policy.getDuration(); | |
| if (duration < interval) { | |
| throw new InvalidParameterValueException("duration : " + duration + " specified in a policy cannot be less than vm group's interval : " + interval); | |
| } | |
| if (scaleUpPolicies) { | |
| if (!isAutoScaleScaleUpPolicy(policy)) { | |
| throw new InvalidParameterValueException("Only scaleup policies can be specified in scaleuppolicyids"); | |
| } | |
| } else { | |
| if (isAutoScaleScaleUpPolicy(policy)) { | |
| throw new InvalidParameterValueException("Only scaledown policies can be specified in scaledownpolicyids"); | |
| } | |
| } | |
| List<AutoScalePolicyConditionMapVO> policyConditionMapVOs = autoScalePolicyConditionMapDao.listByAll(policy.getId(), null); | |
| for (AutoScalePolicyConditionMapVO policyConditionMapVO : policyConditionMapVOs) { | |
| long conditionid = policyConditionMapVO.getConditionId(); | |
| Condition condition = conditionDao.findById(conditionid); | |
| Counter counter = counterDao.findById(condition.getCounterId()); | |
| counters.add(counter); | |
| } | |
| } | |
| return policies; | |
| } | |
| @DB | |
| protected AutoScaleVmProfileVO checkValidityAndPersist(AutoScaleVmProfileVO vmProfile, boolean isNew) { | |
| long templateId = vmProfile.getTemplateId(); | |
| Long autoscaleUserId = vmProfile.getAutoScaleUserId(); | |
| int expungeVmGracePeriod = vmProfile.getExpungeVmGracePeriod(); | |
| Long serviceOfferingId = vmProfile.getServiceOfferingId(); | |
| if (serviceOfferingId != null) { | |
| ServiceOffering serviceOffering = entityMgr.findByIdIncludingRemoved(ServiceOffering.class, serviceOfferingId); | |
| if (serviceOffering == null) { | |
| throw new InvalidParameterValueException("Unable to find service offering by id"); | |
| } | |
| if (serviceOffering.isDynamic()) { | |
| throw new InvalidParameterValueException("Unable to use dynamic service offering in AutoScale vm profile"); | |
| } | |
| } | |
| VirtualMachineTemplate template = entityMgr.findById(VirtualMachineTemplate.class, templateId); | |
| // Make sure a valid template ID was specified | |
| if (template == null) { | |
| throw new InvalidParameterValueException("Unable to use the given template."); | |
| } | |
| if (expungeVmGracePeriod < 0) { | |
| throw new InvalidParameterValueException("Expunge Vm Grace Period cannot be less than 0."); | |
| } | |
| if (!isNew) { | |
| List<AutoScaleVmGroupVO> vmGroups = autoScaleVmGroupDao.listByProfile(vmProfile.getId()); | |
| for (AutoScaleVmGroupVO vmGroup : vmGroups) { | |
| Network.Provider provider = getLoadBalancerServiceProvider(vmGroup.getLoadBalancerId()); | |
| if (Network.Provider.Netscaler.equals(provider)) { | |
| checkAutoScaleUser(autoscaleUserId, vmProfile.getAccountId()); | |
| } | |
| } | |
| } | |
| Integer autoScaleStatsInterval = AutoScaleStatsInterval.value(); | |
| if (autoScaleStatsInterval <= 0) { | |
| throw new InvalidParameterValueException(String.format("Global setting %s has to be set to larger than 0", AutoScaleStatsInterval.key())); | |
| } | |
| List<Pair<String, String>> otherDeployParams = vmProfile.getOtherDeployParamsList(); | |
| if (CollectionUtils.isNotEmpty(otherDeployParams)) { | |
| for (Pair<String, String> kvPair : otherDeployParams) { | |
| String key = kvPair.first(); | |
| if (!supportedDeployParams.contains(key)) { | |
| throw new InvalidParameterValueException(String.format("Unsupported otherdeployparams key: %s. Supported values are %s", key, supportedDeployParams)); | |
| } | |
| } | |
| } | |
| vmProfile = autoScaleVmProfileDao.persist(vmProfile); | |
| return vmProfile; | |
| } | |
| @Override | |
| public void checkAutoScaleUser(Long autoscaleUserId, long accountId) { | |
| if (autoscaleUserId == null) { | |
| throw new InvalidParameterValueException("autoscaleuserid is required but not passed"); | |
| } | |
| User user = userDao.findById(autoscaleUserId); | |
| if (user == null) { | |
| throw new InvalidParameterValueException("Unable to find autoscale user id: " + autoscaleUserId); | |
| } | |
| if (user.getAccountId() != accountId) { | |
| throw new InvalidParameterValueException("AutoScale User id does not belong to the same account"); | |
| } | |
| String apiKey = user.getApiKey(); | |
| String secretKey = user.getSecretKey(); | |
| String csUrl = ApiServiceConfiguration.ApiServletPath.value(); | |
| if (apiKey == null) { | |
| throw new InvalidParameterValueException("apiKey for user: " + user.getUsername() + " is empty. Please generate it"); | |
| } | |
| if (secretKey == null) { | |
| throw new InvalidParameterValueException("secretKey for user: " + user.getUsername() + " is empty. Please generate it"); | |
| } | |
| if (csUrl == null || csUrl.contains("localhost")) { | |
| throw new InvalidParameterValueException(String.format("Global setting %s has to be set to the Management Server's API end point", ApiServiceConfiguration.ApiServletPath.key())); | |
| } | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMPROFILE_CREATE, eventDescription = "creating autoscale vm profile", create = true) | |
| public AutoScaleVmProfile createAutoScaleVmProfile(CreateAutoScaleVmProfileCmd cmd) { | |
| Account caller = CallContext.current().getCallingAccount(); | |
| Account owner = accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); | |
| accountMgr.checkAccess(caller, null, true, owner); | |
| long zoneId = cmd.getZoneId(); | |
| long serviceOfferingId = cmd.getServiceOfferingId(); | |
| Long autoscaleUserId = cmd.getAutoscaleUserId(); | |
| DataCenter zone = entityMgr.findById(DataCenter.class, zoneId); | |
| if (zone == null) { | |
| throw new InvalidParameterValueException("Unable to find zone by id"); | |
| } | |
| ServiceOffering serviceOffering = entityMgr.findById(ServiceOffering.class, serviceOfferingId); | |
| if (serviceOffering == null) { | |
| throw new InvalidParameterValueException("Unable to find service offering by id"); | |
| } | |
| VirtualMachineTemplate template = entityMgr.findById(VirtualMachineTemplate.class, cmd.getTemplateId()); | |
| if (template == null) { | |
| throw new InvalidParameterValueException("Unable to find template by id " + cmd.getTemplateId()); | |
| } | |
| if (HypervisorType.External.equals(template.getHypervisorType())) { | |
| logger.error("Cannot create AutoScale Vm Profile with {} as it is an {} hypervisor template", template, HypervisorType.External); | |
| throw new InvalidParameterValueException(String.format("Unable to create AutoScale Vm Profile with template: %s", template.getName())); | |
| } | |
| // validations | |
| HashMap<String, String> deployParams = cmd.getDeployParamMap(); | |
| /* | |
| * Just for making sure the values are right in other deploy params. | |
| * For ex. if projectId is given as a string instead of an long value, this | |
| * will be throwing an error. | |
| */ | |
| dispatchChainFactory.getStandardDispatchChain().dispatch(new DispatchTask(ComponentContext.inject(DeployVMCmd.class), deployParams)); | |
| AutoScaleVmProfileVO profileVO = | |
| new AutoScaleVmProfileVO(cmd.getZoneId(), owner.getDomainId(), owner.getAccountId(), cmd.getServiceOfferingId(), cmd.getTemplateId(), cmd.getOtherDeployParams(), | |
| cmd.getCounterParamList(), cmd.getUserData(), cmd.getExpungeVmGracePeriod(), autoscaleUserId); | |
| if (cmd.getDisplay() != null) { | |
| profileVO.setDisplay(cmd.getDisplay()); | |
| } | |
| String userData = cmd.getUserData(); | |
| Long userDataId = cmd.getUserDataId(); | |
| String userDataDetails = null; | |
| if (MapUtils.isNotEmpty(cmd.getUserDataDetails())) { | |
| userDataDetails = cmd.getUserDataDetails().toString(); | |
| } | |
| userData = userVmMgr.finalizeUserData(userData, userDataId, template); | |
| userData = userDataMgr.validateUserData(userData, cmd.getHttpMethod()); | |
| if (userData != null) { | |
| profileVO.setUserData(userData); | |
| } | |
| if (userDataId != null) { | |
| profileVO.setUserDataId(userDataId); | |
| } | |
| if (userDataDetails != null) { | |
| profileVO.setUserDataDetails(userDataDetails); | |
| } | |
| profileVO = checkValidityAndPersist(profileVO, true); | |
| logger.info("Successfully create AutoScale Vm Profile: {}", profileVO); | |
| return profileVO; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMPROFILE_UPDATE, eventDescription = "updating autoscale vm profile") | |
| public AutoScaleVmProfile updateAutoScaleVmProfile(UpdateAutoScaleVmProfileCmd cmd) { | |
| Long profileId = cmd.getId(); | |
| Long serviceOfferingId = cmd.getServiceOfferingId(); | |
| Long templateId = cmd.getTemplateId(); | |
| Long autoscaleUserId = cmd.getAutoscaleUserId(); | |
| Map<String, HashMap<String, String>> otherDeployParams = cmd.getOtherDeployParams(); | |
| Map counterParamList = cmd.getCounterParamList(); | |
| String userData = cmd.getUserData(); | |
| Long userDataId = cmd.getUserDataId(); | |
| String userDataDetails = null; | |
| if (MapUtils.isNotEmpty(cmd.getUserDataDetails())) { | |
| userDataDetails = cmd.getUserDataDetails().toString(); | |
| } | |
| boolean userdataUpdate = userData != null || userDataId != null || MapUtils.isNotEmpty(cmd.getUserDataDetails()); | |
| Integer expungeVmGracePeriod = cmd.getExpungeVmGracePeriod(); | |
| AutoScaleVmProfileVO vmProfile = getEntityInDatabase(CallContext.current().getCallingAccount(), "Auto Scale Vm Profile", profileId, autoScaleVmProfileDao); | |
| boolean physicalParameterUpdate = (templateId != null || autoscaleUserId != null || counterParamList != null | |
| || otherDeployParams != null || expungeVmGracePeriod != null || userdataUpdate); | |
| if (serviceOfferingId != null) { | |
| vmProfile.setServiceOfferingId(serviceOfferingId); | |
| } | |
| if (templateId != null) { | |
| vmProfile.setTemplateId(templateId); | |
| } | |
| if (autoscaleUserId != null) { | |
| vmProfile.setAutoscaleUserId(autoscaleUserId); | |
| } | |
| if (otherDeployParams != null) { | |
| vmProfile.setOtherDeployParamsForUpdate(otherDeployParams); | |
| } | |
| if (counterParamList != null) { | |
| vmProfile.setCounterParamsForUpdate(counterParamList); | |
| } | |
| if (expungeVmGracePeriod != null) { | |
| vmProfile.setExpungeVmGracePeriod(expungeVmGracePeriod); | |
| } | |
| if (cmd.getCustomId() != null) { | |
| vmProfile.setUuid(cmd.getCustomId()); | |
| } | |
| if (cmd.getDisplay() != null) { | |
| vmProfile.setDisplay(cmd.getDisplay()); | |
| } | |
| if (userdataUpdate) { | |
| if (templateId == null) { | |
| templateId = vmProfile.getTemplateId(); | |
| } | |
| VirtualMachineTemplate template = entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, templateId); | |
| userData = userVmMgr.finalizeUserData(userData, userDataId, template); | |
| userData = userDataMgr.validateUserData(userData, cmd.getHttpMethod()); | |
| vmProfile.setUserDataId(userDataId); | |
| vmProfile.setUserData(userData); | |
| vmProfile.setUserDataDetails(userDataDetails); | |
| } | |
| List<AutoScaleVmGroupVO> vmGroupList = autoScaleVmGroupDao.listByAll(null, profileId); | |
| for (AutoScaleVmGroupVO vmGroupVO : vmGroupList) { | |
| if (physicalParameterUpdate && !vmGroupVO.getState().equals(AutoScaleVmGroup.State.DISABLED)) { | |
| throw new InvalidParameterValueException("The AutoScale Vm Profile can be updated only if the Vm Group it is associated with is disabled in state"); | |
| } | |
| } | |
| vmProfile = checkValidityAndPersist(vmProfile, false); | |
| logger.info("Updated Auto Scale Vm Profile: {}", vmProfile); | |
| return vmProfile; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMPROFILE_DELETE, eventDescription = "deleting autoscale vm profile") | |
| public boolean deleteAutoScaleVmProfile(long id) { | |
| /* Check if entity is in database */ | |
| AutoScaleVmProfileVO vmProfile = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Profile", id, autoScaleVmProfileDao); | |
| if (autoScaleVmGroupDao.isProfileInUse(id)) { | |
| throw new InvalidParameterValueException("Cannot delete AutoScale Vm Profile when it is in use by one more vm groups"); | |
| } | |
| boolean success = autoScaleVmProfileDao.remove(id); | |
| if (success) { | |
| logger.info("Successfully deleted AutoScale Vm Profile: {}", vmProfile); | |
| } | |
| return success; | |
| } | |
| @Override | |
| public List<? extends AutoScaleVmProfile> listAutoScaleVmProfiles(ListAutoScaleVmProfilesCmd cmd) { | |
| Long id = cmd.getId(); | |
| Long templateId = cmd.getTemplateId(); | |
| String otherDeployParams = cmd.getOtherDeployParams(); | |
| Long serviceOffId = cmd.getServiceOfferingId(); | |
| Long zoneId = cmd.getZoneId(); | |
| Boolean display = cmd.getDisplay(); | |
| SearchWrapper<AutoScaleVmProfileVO> searchWrapper = new SearchWrapper<>(autoScaleVmProfileDao, AutoScaleVmProfileVO.class, cmd, cmd.getId()); | |
| SearchBuilder<AutoScaleVmProfileVO> sb = searchWrapper.getSearchBuilder(); | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); | |
| sb.and("serviceOfferingId", sb.entity().getServiceOfferingId(), SearchCriteria.Op.EQ); | |
| sb.and("otherDeployParams", sb.entity().getOtherDeployParams(), SearchCriteria.Op.LIKE); | |
| sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ); | |
| sb.and("display", sb.entity().isDisplay(), SearchCriteria.Op.EQ); | |
| SearchCriteria<AutoScaleVmProfileVO> sc = searchWrapper.buildSearchCriteria(); | |
| if (id != null) { | |
| sc.setParameters("id", id); | |
| } | |
| if (templateId != null) { | |
| sc.setParameters("templateId", templateId); | |
| } | |
| if (otherDeployParams != null) { | |
| sc.addAnd("otherDeployParams", SearchCriteria.Op.LIKE, "%" + otherDeployParams + "%"); | |
| } | |
| if (serviceOffId != null) { | |
| sc.setParameters("serviceOfferingId", serviceOffId); | |
| } | |
| if (zoneId != null) { | |
| sc.setParameters("zoneId", zoneId); | |
| } | |
| if (display != null) { | |
| sc.setParameters("display", display); | |
| } | |
| return searchWrapper.search(); | |
| } | |
| @DB | |
| protected AutoScalePolicyVO checkValidityAndPersist(final AutoScalePolicyVO autoScalePolicyVOFinal, final List<Long> conditionIds) { | |
| final int duration = autoScalePolicyVOFinal.getDuration(); | |
| final int quietTime = autoScalePolicyVOFinal.getQuietTime(); | |
| if (duration < 0) { | |
| throw new InvalidParameterValueException("duration is an invalid value: " + duration); | |
| } | |
| if (quietTime < 0) { | |
| throw new InvalidParameterValueException("quietTime is an invalid value: " + quietTime); | |
| } | |
| return Transaction.execute(new TransactionCallback<AutoScalePolicyVO>() { | |
| @Override | |
| public AutoScalePolicyVO doInTransaction(TransactionStatus status) { | |
| AutoScalePolicyVO autoScalePolicyVO = autoScalePolicyDao.persist(autoScalePolicyVOFinal); | |
| if (conditionIds != null) { | |
| if (CollectionUtils.isEmpty(conditionIds)) { | |
| throw new InvalidParameterValueException("AutoScale policy must have at least one condition"); | |
| } | |
| SearchBuilder<ConditionVO> conditionsSearch = conditionDao.createSearchBuilder(); | |
| conditionsSearch.and("ids", conditionsSearch.entity().getId(), Op.IN); | |
| conditionsSearch.done(); | |
| SearchCriteria<ConditionVO> sc = conditionsSearch.create(); | |
| sc.setParameters("ids", conditionIds.toArray(new Object[0])); | |
| List<ConditionVO> conditions = conditionDao.search(sc, null); | |
| ControlledEntity[] sameOwnerEntities = conditions.toArray(new ControlledEntity[conditions.size() + 1]); | |
| sameOwnerEntities[sameOwnerEntities.length - 1] = autoScalePolicyVO; | |
| accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, sameOwnerEntities); | |
| if (conditionIds.size() != conditions.size()) { | |
| // TODO report the condition id which could not be found | |
| throw new InvalidParameterValueException("Unable to find the condition specified"); | |
| } | |
| ArrayList<Long> counterIds = new ArrayList<Long>(); | |
| for (ConditionVO condition : conditions) { | |
| if (counterIds.contains(condition.getCounterId())) { | |
| throw new InvalidParameterValueException( | |
| "at least two conditions in the conditionids have the same counter. It is not right to apply two different conditions for the same counter"); | |
| } | |
| counterIds.add(condition.getCounterId()); | |
| } | |
| /* For update case remove the existing mappings and create fresh ones */ | |
| autoScalePolicyConditionMapDao.removeByAutoScalePolicyId(autoScalePolicyVO.getId()); | |
| for (Long conditionId : conditionIds) { | |
| AutoScalePolicyConditionMapVO policyConditionMapVO = new AutoScalePolicyConditionMapVO(autoScalePolicyVO.getId(), conditionId); | |
| autoScalePolicyConditionMapDao.persist(policyConditionMapVO); | |
| } | |
| } | |
| return autoScalePolicyVO; | |
| } | |
| }); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEPOLICY_CREATE, eventDescription = "creating autoscale policy", create = true) | |
| public AutoScalePolicy createAutoScalePolicy(CreateAutoScalePolicyCmd cmd) { | |
| int duration = cmd.getDuration(); | |
| Integer quietTime = cmd.getQuietTime(); | |
| String action = cmd.getAction(); | |
| if (quietTime == null) { | |
| quietTime = NetUtils.DEFAULT_AUTOSCALE_POLICY_QUIET_TIME; | |
| } | |
| AutoScalePolicy.Action scaleAction = null; | |
| try { | |
| scaleAction = AutoScalePolicy.Action.fromValue(action); | |
| } catch (IllegalArgumentException ex) { | |
| throw new InvalidParameterValueException("action is invalid. Supported actions are: " + Arrays.toString(AutoScalePolicy.Action.values())); | |
| } | |
| AutoScalePolicyVO policyVO = new AutoScalePolicyVO(cmd.getName(), cmd.getDomainId(), cmd.getAccountId(), duration, quietTime, null, scaleAction); | |
| policyVO = checkValidityAndPersist(policyVO, cmd.getConditionIds()); | |
| logger.info("Successfully created AutoScale Policy: {}", policyVO); | |
| return policyVO; | |
| } | |
| @Override | |
| @DB | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEPOLICY_DELETE, eventDescription = "deleting autoscale policy") | |
| public boolean deleteAutoScalePolicy(final long id) { | |
| /* Check if entity is in database */ | |
| AutoScalePolicyVO policy = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Policy", id, autoScalePolicyDao); | |
| if (autoScaleVmGroupPolicyMapDao.isAutoScalePolicyInUse(id)) { | |
| throw new InvalidParameterValueException("Cannot delete AutoScale Policy when it is in use by one or more AutoScale Vm Groups"); | |
| } | |
| return Transaction.execute(new TransactionCallback<Boolean>() { | |
| @Override | |
| public Boolean doInTransaction(TransactionStatus status) { | |
| boolean success = true; | |
| success = autoScalePolicyDao.remove(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Policy db object"); | |
| return false; | |
| } | |
| success = autoScalePolicyConditionMapDao.removeByAutoScalePolicyId(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Policy Condition mappings"); | |
| return false; | |
| } | |
| logger.info("Successfully deleted autoscale policy: {}", policy); | |
| return success; | |
| } | |
| }); | |
| } | |
| private class SearchWrapper<VO extends ControlledEntity> { | |
| GenericDao<VO, Long> dao; | |
| SearchBuilder<VO> searchBuilder; | |
| SearchCriteria<VO> searchCriteria; | |
| Long domainId; | |
| boolean isRecursive; | |
| List<Long> permittedAccounts = new ArrayList<Long>(); | |
| ListProjectResourcesCriteria listProjectResourcesCriteria; | |
| Filter searchFilter; | |
| public SearchWrapper(GenericDao<VO, Long> dao, Class<VO> entityClass, BaseListProjectAndAccountResourcesCmd cmd, Long id) | |
| { | |
| this.dao = dao; | |
| this.searchBuilder = dao.createSearchBuilder(); | |
| domainId = cmd.getDomainId(); | |
| String accountName = cmd.getAccountName(); | |
| isRecursive = cmd.isRecursive(); | |
| boolean listAll = cmd.listAll(); | |
| long startIndex = cmd.getStartIndex(); | |
| long pageSizeVal = cmd.getPageSizeVal(); | |
| Account caller = CallContext.current().getCallingAccount(); | |
| Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, | |
| ListProjectResourcesCriteria>(domainId, isRecursive, null); | |
| accountMgr.buildACLSearchParameters(caller, id, accountName, cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, | |
| listAll, false); | |
| domainId = domainIdRecursiveListProject.first(); | |
| isRecursive = domainIdRecursiveListProject.second(); | |
| listProjectResourcesCriteria = domainIdRecursiveListProject.third(); | |
| accountMgr.buildACLSearchBuilder(searchBuilder, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); | |
| searchFilter = new Filter(entityClass, "id", false, startIndex, pageSizeVal); | |
| } | |
| public SearchBuilder<VO> getSearchBuilder() { | |
| return searchBuilder; | |
| } | |
| public SearchCriteria<VO> buildSearchCriteria() { | |
| searchCriteria = searchBuilder.create(); | |
| accountMgr.buildACLSearchCriteria(searchCriteria, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); | |
| return searchCriteria; | |
| } | |
| public List<VO> search() { | |
| return dao.search(searchCriteria, searchFilter); | |
| } | |
| } | |
| @Override | |
| public List<? extends AutoScalePolicy> listAutoScalePolicies(ListAutoScalePoliciesCmd cmd) { | |
| SearchWrapper<AutoScalePolicyVO> searchWrapper = new SearchWrapper<>(autoScalePolicyDao, AutoScalePolicyVO.class, cmd, cmd.getId()); | |
| SearchBuilder<AutoScalePolicyVO> sb = searchWrapper.getSearchBuilder(); | |
| Long id = cmd.getId(); | |
| String name = cmd.getName(); | |
| Long conditionId = cmd.getConditionId(); | |
| String action = cmd.getAction(); | |
| Long vmGroupId = cmd.getVmGroupId(); | |
| AutoScalePolicy.Action scaleAction = null; | |
| if (action != null) { | |
| try { | |
| scaleAction = AutoScalePolicy.Action.fromValue(action); | |
| } catch (IllegalArgumentException ex) { | |
| throw new InvalidParameterValueException("action is invalid. Supported actions are: " + Arrays.toString(AutoScalePolicy.Action.values())); | |
| } | |
| } | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); | |
| sb.and("action", sb.entity().getAction(), SearchCriteria.Op.EQ); | |
| if (conditionId != null) { | |
| SearchBuilder<AutoScalePolicyConditionMapVO> asPolicyConditionSearch = autoScalePolicyConditionMapDao.createSearchBuilder(); | |
| asPolicyConditionSearch.and("conditionId", asPolicyConditionSearch.entity().getConditionId(), SearchCriteria.Op.EQ); | |
| sb.join("asPolicyConditionSearch", asPolicyConditionSearch, sb.entity().getId(), asPolicyConditionSearch.entity().getPolicyId(), JoinBuilder.JoinType.INNER); | |
| } | |
| if (vmGroupId != null) { | |
| SearchBuilder<AutoScaleVmGroupPolicyMapVO> asVmGroupPolicySearch = autoScaleVmGroupPolicyMapDao.createSearchBuilder(); | |
| asVmGroupPolicySearch.and("vmGroupId", asVmGroupPolicySearch.entity().getVmGroupId(), SearchCriteria.Op.EQ); | |
| sb.join("asVmGroupPolicySearch", asVmGroupPolicySearch, sb.entity().getId(), asVmGroupPolicySearch.entity().getPolicyId(), JoinBuilder.JoinType.INNER); | |
| } | |
| SearchCriteria<AutoScalePolicyVO> sc = searchWrapper.buildSearchCriteria(); | |
| if (id != null) { | |
| sc.setParameters("id", id); | |
| } | |
| if (name != null) { | |
| sc.setParameters("name", name); | |
| } | |
| if (scaleAction != null) { | |
| sc.setParameters("action", scaleAction); | |
| } | |
| if (conditionId != null) { | |
| sc.setJoinParameters("asPolicyConditionSearch", "conditionId", conditionId); | |
| } | |
| if (vmGroupId != null) { | |
| sc.setJoinParameters("asVmGroupPolicySearch", "vmGroupId", vmGroupId); | |
| } | |
| return searchWrapper.search(); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEPOLICY_UPDATE, eventDescription = "updating autoscale policy") | |
| public AutoScalePolicy updateAutoScalePolicy(UpdateAutoScalePolicyCmd cmd) { | |
| Long policyId = cmd.getId(); | |
| String name = cmd.getName(); | |
| Integer duration = cmd.getDuration(); | |
| Integer quietTime = cmd.getQuietTime(); | |
| List<Long> conditionIds = cmd.getConditionIds(); | |
| AutoScalePolicyVO policy = getEntityInDatabase(CallContext.current().getCallingAccount(), "Auto Scale Policy", policyId, autoScalePolicyDao); | |
| if (name != null) { | |
| policy.setName(name); | |
| } | |
| if (duration != null) { | |
| policy.setDuration(duration); | |
| } | |
| if (quietTime != null) { | |
| policy.setQuietTime(quietTime); | |
| } | |
| List<AutoScaleVmGroupPolicyMapVO> vmGroupPolicyList = autoScaleVmGroupPolicyMapDao.listByPolicyId(policyId); | |
| for (AutoScaleVmGroupPolicyMapVO vmGroupPolicy : vmGroupPolicyList) { | |
| AutoScaleVmGroupVO vmGroupVO = autoScaleVmGroupDao.findById(vmGroupPolicy.getVmGroupId()); | |
| if (vmGroupVO == null) { | |
| logger.warn("Stale database entry! There is an entry in VmGroupPolicyMap but the vmGroup is missing:" + vmGroupPolicy.getVmGroupId()); | |
| continue; | |
| } | |
| if (!vmGroupVO.getState().equals(AutoScaleVmGroup.State.DISABLED)) { | |
| throw new InvalidParameterValueException("The AutoScale Policy can be updated only if the Vm Group it is associated with is disabled in state"); | |
| } | |
| if (policy.getDuration() < vmGroupVO.getInterval()) { | |
| throw new InvalidParameterValueException("duration is less than the associated AutoScaleVmGroup's interval"); | |
| } | |
| } | |
| policy = checkValidityAndPersist(policy, conditionIds); | |
| logger.info("Successfully updated Auto Scale Policy: {}", policy); | |
| if (CollectionUtils.isNotEmpty(conditionIds)) { | |
| markStatisticsAsInactive(null, policyId); | |
| } | |
| return policy; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_CREATE, eventDescription = "creating autoscale vm group", create = true) | |
| public AutoScaleVmGroup createAutoScaleVmGroup(CreateAutoScaleVmGroupCmd cmd) { | |
| String name = cmd.getName(); | |
| int minMembers = cmd.getMinMembers(); | |
| int maxMembers = cmd.getMaxMembers(); | |
| Integer interval = cmd.getInterval(); | |
| Boolean forDisplay = cmd.getDisplay(); | |
| if (interval == null) { | |
| interval = NetUtils.DEFAULT_AUTOSCALE_POLICY_INTERVAL_TIME; | |
| } | |
| LoadBalancerVO loadBalancer = getEntityInDatabase(CallContext.current().getCallingAccount(), ApiConstants.LBID, cmd.getLbRuleId(), lbDao); | |
| Long zoneId = ipAddressDao.findById(loadBalancer.getSourceIpAddressId()).getDataCenterId(); | |
| if (autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancer.getId())) { | |
| throw new InvalidParameterValueException("an AutoScaleVmGroup is already attached to the lb rule, the existing vm group has to be first deleted"); | |
| } | |
| if (lbVmMapDao.isVmAttachedToLoadBalancer(loadBalancer.getId())) { | |
| throw new InvalidParameterValueException( | |
| "there are Vms already bound to the specified LoadBalancing Rule. User bound Vms and AutoScaled Vm Group cannot co-exist on a Load Balancing Rule"); | |
| } | |
| AutoScaleVmGroupVO vmGroupVO = new AutoScaleVmGroupVO(cmd.getLbRuleId(), zoneId, loadBalancer.getDomainId(), loadBalancer.getAccountId(), name, minMembers, maxMembers, | |
| loadBalancer.getDefaultPortStart(), interval, null, cmd.getProfileId(), AutoScaleVmGroup.State.NEW); | |
| if (forDisplay != null) { | |
| vmGroupVO.setDisplay(forDisplay); | |
| } | |
| vmGroupVO = checkValidityAndPersist(vmGroupVO, cmd.getScaleUpPolicyIds(), cmd.getScaleDownPolicyIds()); | |
| logger.info("Successfully created Autoscale Vm Group: {}", vmGroupVO); | |
| createInactiveDummyRecord(vmGroupVO.getId()); | |
| scheduleMonitorTask(vmGroupVO.getId()); | |
| return vmGroupVO; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_CREATE, eventDescription = "creating autoscale vm group", async = true) | |
| public boolean configureAutoScaleVmGroup(CreateAutoScaleVmGroupCmd cmd) throws ResourceUnavailableException { | |
| return configureAutoScaleVmGroup(cmd.getEntityId(), AutoScaleVmGroup.State.NEW); | |
| } | |
| public boolean isLoadBalancerBasedAutoScaleVmGroup(AutoScaleVmGroup vmGroup) { | |
| return vmGroup.getLoadBalancerId() != null; | |
| } | |
| protected boolean configureAutoScaleVmGroup(long vmGroupid, AutoScaleVmGroup.State currentState) throws ResourceUnavailableException { | |
| AutoScaleVmGroup vmGroup = autoScaleVmGroupDao.findById(vmGroupid); | |
| if (isLoadBalancerBasedAutoScaleVmGroup(vmGroup)) { | |
| try { | |
| return lbRulesMgr.configureLbAutoScaleVmGroup(vmGroupid, currentState); | |
| } catch (ResourceUnavailableException re) { | |
| throw re; | |
| } catch (Exception e) { | |
| logger.warn("Exception during configureLbAutoScaleVmGroup in lb rules manager", e); | |
| return false; | |
| } | |
| } | |
| // This should never happen, because today loadbalancerruleid is manadatory for AutoScaleVmGroup. | |
| throw new InvalidParameterValueException("Only LoadBalancer based AutoScale is supported"); | |
| } | |
| @Override | |
| @DB | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_DELETE, eventDescription = "deleting autoscale vm group", async = true) | |
| public boolean deleteAutoScaleVmGroup(final long id, final Boolean cleanup) { | |
| AutoScaleVmGroupVO autoScaleVmGroupVO = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", id, autoScaleVmGroupDao); | |
| if (autoScaleVmGroupVO.getState().equals(AutoScaleVmGroup.State.NEW)) { | |
| /* This condition is for handling failures during creation command */ | |
| return autoScaleVmGroupDao.remove(id); | |
| } | |
| if (!autoScaleVmGroupVO.getState().equals(AutoScaleVmGroup.State.DISABLED) && !Boolean.TRUE.equals(cleanup)) { | |
| throw new InvalidParameterValueException(String.format("Cannot delete autoscale vm group : %s because it is in %s state. Please disable it or pass cleanup=true flag which will destroy all VMs.", autoScaleVmGroupVO, autoScaleVmGroupVO.getState())); | |
| } | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countByGroup(id); | |
| if (currentVM > 0 && !Boolean.TRUE.equals(cleanup)) { | |
| throw new InvalidParameterValueException(String.format("Cannot delete autoscale vm group : %s because there are %d VMs. Please remove the VMs or pass cleanup=true flag which will destroy all VMs.", autoScaleVmGroupVO, currentVM)); | |
| } | |
| AutoScaleVmGroup.State bakupState = autoScaleVmGroupVO.getState(); | |
| autoScaleVmGroupVO.setState(AutoScaleVmGroup.State.REVOKE); | |
| autoScaleVmGroupDao.persist(autoScaleVmGroupVO); | |
| boolean success = false; | |
| if (Boolean.TRUE.equals(cleanup)) { | |
| List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = autoScaleVmGroupVmMapDao.listByGroup(id); | |
| for (AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs) { | |
| Long vmId = asGroupVmVO.getInstanceId(); | |
| destroyVm(vmId); | |
| } | |
| } | |
| try { | |
| success = configureAutoScaleVmGroup(id, bakupState); | |
| } catch (ResourceUnavailableException e) { | |
| autoScaleVmGroupVO.setState(bakupState); | |
| autoScaleVmGroupDao.persist(autoScaleVmGroupVO); | |
| } finally { | |
| if (!success) { | |
| logger.warn("Could not delete AutoScale Vm Group : {}", autoScaleVmGroupVO); | |
| return false; | |
| } | |
| } | |
| // Remove comments (if any) | |
| annotationDao.removeByEntityType(AnnotationService.EntityType.AUTOSCALE_VM_GROUP.name(), autoScaleVmGroupVO.getUuid()); | |
| return Transaction.execute(new TransactionCallback<Boolean>() { | |
| @Override | |
| public Boolean doInTransaction(TransactionStatus status) { | |
| boolean success = autoScaleVmGroupDao.remove(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Group db object"); | |
| return false; | |
| } | |
| cancelMonitorTask(id); | |
| success = autoScaleVmGroupPolicyMapDao.removeByGroupId(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Group Policy mappings"); | |
| return false; | |
| } | |
| success = autoScaleVmGroupVmMapDao.removeByGroup(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Group VM mappings"); | |
| return false; | |
| } | |
| success = asGroupStatisticsDao.removeByGroupId(id); | |
| if (!success) { | |
| logger.warn("Failed to remove AutoScale Group statistics"); | |
| return false; | |
| } | |
| logger.info("Successfully deleted autoscale vm group: {}", autoScaleVmGroupVO); | |
| return success; // Successfull | |
| } | |
| }); | |
| } | |
| @Override | |
| public List<? extends AutoScaleVmGroup> listAutoScaleVmGroups(ListAutoScaleVmGroupsCmd cmd) { | |
| Long id = cmd.getId(); | |
| String name = cmd.getName(); | |
| Long policyId = cmd.getPolicyId(); | |
| Long loadBalancerId = cmd.getLoadBalancerId(); | |
| Long profileId = cmd.getProfileId(); | |
| Long zoneId = cmd.getZoneId(); | |
| Boolean forDisplay = cmd.getDisplay(); | |
| String keyword = cmd.getKeyword(); | |
| SearchWrapper<AutoScaleVmGroupVO> searchWrapper = new SearchWrapper<>(autoScaleVmGroupDao, AutoScaleVmGroupVO.class, cmd, cmd.getId()); | |
| SearchBuilder<AutoScaleVmGroupVO> sb = searchWrapper.getSearchBuilder(); | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); | |
| sb.and("loadBalancerId", sb.entity().getLoadBalancerId(), SearchCriteria.Op.EQ); | |
| sb.and("profileId", sb.entity().getProfileId(), SearchCriteria.Op.EQ); | |
| sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ); | |
| sb.and("display", sb.entity().isDisplay(), SearchCriteria.Op.EQ); | |
| sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE); | |
| if (policyId != null) { | |
| SearchBuilder<AutoScaleVmGroupPolicyMapVO> asVmGroupPolicySearch = autoScaleVmGroupPolicyMapDao.createSearchBuilder(); | |
| asVmGroupPolicySearch.and("policyId", asVmGroupPolicySearch.entity().getPolicyId(), SearchCriteria.Op.EQ); | |
| sb.join("asVmGroupPolicySearch", asVmGroupPolicySearch, sb.entity().getId(), asVmGroupPolicySearch.entity().getVmGroupId(), JoinBuilder.JoinType.INNER); | |
| } | |
| SearchCriteria<AutoScaleVmGroupVO> sc = searchWrapper.buildSearchCriteria(); | |
| if (id != null) { | |
| sc.setParameters("id", id); | |
| } | |
| if (name != null) { | |
| sc.setParameters("name", name); | |
| } | |
| if (loadBalancerId != null) { | |
| sc.setParameters("loadBalancerId", loadBalancerId); | |
| } | |
| if (profileId != null) { | |
| sc.setParameters("profileId", profileId); | |
| } | |
| if (zoneId != null) { | |
| sc.setParameters("zoneId", zoneId); | |
| } | |
| if (policyId != null) { | |
| sc.setJoinParameters("asVmGroupPolicySearch", "policyId", policyId); | |
| } | |
| if (forDisplay != null) { | |
| sc.setParameters("display", forDisplay); | |
| } | |
| if (StringUtils.isNotBlank(keyword)) { | |
| sc.setParameters("keyword", "%" + keyword + "%"); | |
| } | |
| return searchWrapper.search(); | |
| } | |
| @DB | |
| protected AutoScaleVmGroupVO checkValidityAndPersist(final AutoScaleVmGroupVO vmGroup, final List<Long> passedScaleUpPolicyIds, | |
| final List<Long> passedScaleDownPolicyIds) { | |
| checkAutoScaleVmGroupName(vmGroup.getName()); | |
| int minMembers = vmGroup.getMinMembers(); | |
| int maxMembers = vmGroup.getMaxMembers(); | |
| int interval = vmGroup.getInterval(); | |
| List<Counter> counters = new ArrayList<Counter>(); | |
| List<AutoScalePolicyVO> policies = new ArrayList<AutoScalePolicyVO>(); | |
| final List<Long> policyIds = new ArrayList<Long>(); | |
| List<Long> currentScaleUpPolicyIds = new ArrayList<Long>(); | |
| List<Long> currentScaleDownPolicyIds = new ArrayList<Long>(); | |
| if (vmGroup.getCreated() != null) { | |
| ApiDBUtils.getAutoScaleVmGroupPolicyIds(vmGroup.getId(), currentScaleUpPolicyIds, currentScaleDownPolicyIds); | |
| } | |
| if (minMembers <= 0) { | |
| throw new InvalidParameterValueException(ApiConstants.MIN_MEMBERS + " is an invalid value: " + minMembers); | |
| } | |
| if (maxMembers <= 0) { | |
| throw new InvalidParameterValueException(ApiConstants.MAX_MEMBERS + " is an invalid value: " + maxMembers); | |
| } | |
| if (minMembers > maxMembers) { | |
| throw new InvalidParameterValueException(ApiConstants.MIN_MEMBERS + " (" + minMembers + ")cannot be greater than " + ApiConstants.MAX_MEMBERS + " (" + | |
| maxMembers + ")"); | |
| } | |
| if (interval <= 0) { | |
| throw new InvalidParameterValueException("interval is an invalid value: " + interval); | |
| } | |
| if (passedScaleUpPolicyIds != null) { | |
| if (CollectionUtils.isEmpty(passedScaleUpPolicyIds)) { | |
| throw new InvalidParameterValueException("AutoScale VM group must have at least one ScaleUp policy"); | |
| } | |
| policies.addAll(getAutoScalePolicies(passedScaleUpPolicyIds, counters, interval, true)); | |
| policyIds.addAll(passedScaleUpPolicyIds); | |
| } else { | |
| // Run the interval check for existing policies | |
| getAutoScalePolicies(currentScaleUpPolicyIds, counters, interval, true); | |
| policyIds.addAll(currentScaleUpPolicyIds); | |
| } | |
| if (passedScaleDownPolicyIds != null) { | |
| if (CollectionUtils.isEmpty(passedScaleDownPolicyIds)) { | |
| throw new InvalidParameterValueException("AutoScale VM group must have at least one ScaleDown policy"); | |
| } | |
| policies.addAll(getAutoScalePolicies(passedScaleDownPolicyIds, counters, interval, false)); | |
| policyIds.addAll(passedScaleDownPolicyIds); | |
| } else { | |
| // Run the interval check for existing policies | |
| getAutoScalePolicies(currentScaleDownPolicyIds, counters, interval, false); | |
| policyIds.addAll(currentScaleDownPolicyIds); | |
| } | |
| AutoScaleVmProfileVO profileVO = | |
| getEntityInDatabase(CallContext.current().getCallingAccount(), ApiConstants.VMPROFILE_ID, vmGroup.getProfileId(), autoScaleVmProfileDao); | |
| LoadBalancerVO loadBalancer = getEntityInDatabase(CallContext.current().getCallingAccount(), ApiConstants.LBID, vmGroup.getLoadBalancerId(), lbDao); | |
| validateNetworkCapability(loadBalancer.getNetworkId()); | |
| validateAutoScaleCounters(loadBalancer.getNetworkId(), counters, profileVO.getCounterParams()); | |
| Network.Provider provider = getLoadBalancerServiceProvider(vmGroup.getLoadBalancerId()); | |
| if (Network.Provider.Netscaler.equals(provider)) { | |
| checkAutoScaleUser(profileVO.getAutoScaleUserId(), vmGroup.getAccountId()); | |
| } | |
| ControlledEntity[] sameOwnerEntities = policies.toArray(new ControlledEntity[policies.size() + 2]); | |
| sameOwnerEntities[sameOwnerEntities.length - 2] = loadBalancer; | |
| sameOwnerEntities[sameOwnerEntities.length - 1] = profileVO; | |
| accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, sameOwnerEntities); | |
| return Transaction.execute(new TransactionCallback<AutoScaleVmGroupVO>() { | |
| @Override | |
| public AutoScaleVmGroupVO doInTransaction(TransactionStatus status) { | |
| AutoScaleVmGroupVO vmGroupNew = autoScaleVmGroupDao.persist(vmGroup); | |
| if (passedScaleUpPolicyIds != null || passedScaleDownPolicyIds != null) { | |
| autoScaleVmGroupPolicyMapDao.removeByGroupId(vmGroupNew.getId()); | |
| for (Long policyId : policyIds) { | |
| autoScaleVmGroupPolicyMapDao.persist(new AutoScaleVmGroupPolicyMapVO(vmGroupNew.getId(), policyId)); | |
| } | |
| } | |
| return vmGroupNew; | |
| } | |
| }); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_UPDATE, eventDescription = "updating autoscale vm group", async = true) | |
| public AutoScaleVmGroup updateAutoScaleVmGroup(UpdateAutoScaleVmGroupCmd cmd) { | |
| Long vmGroupId = cmd.getId(); | |
| String name = cmd.getName(); | |
| Integer minMembers = cmd.getMinMembers(); | |
| Integer maxMembers = cmd.getMaxMembers(); | |
| Integer interval = cmd.getInterval(); | |
| Boolean forDisplay = cmd.getDisplay(); | |
| List<Long> scaleUpPolicyIds = cmd.getScaleUpPolicyIds(); | |
| List<Long> scaleDownPolicyIds = cmd.getScaleDownPolicyIds(); | |
| AutoScaleVmGroupVO vmGroupVO = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", vmGroupId, autoScaleVmGroupDao); | |
| int currentInterval = vmGroupVO.getInterval(); | |
| boolean physicalParametersUpdate = (minMembers != null || maxMembers != null || (interval != null && interval != currentInterval) || CollectionUtils.isNotEmpty(scaleUpPolicyIds) || CollectionUtils.isNotEmpty(scaleDownPolicyIds)); | |
| if (physicalParametersUpdate && !vmGroupVO.getState().equals(AutoScaleVmGroup.State.DISABLED)) { | |
| throw new InvalidParameterValueException("An AutoScale Vm Group can be updated with minMembers/maxMembers/Interval only when it is in disabled state"); | |
| } | |
| if (StringUtils.isNotBlank(name)) { | |
| vmGroupVO.setName(name); | |
| } | |
| if (minMembers != null) { | |
| vmGroupVO.setMinMembers(minMembers); | |
| } | |
| if (maxMembers != null) { | |
| vmGroupVO.setMaxMembers(maxMembers); | |
| } | |
| if (interval != null) { | |
| vmGroupVO.setInterval(interval); | |
| } | |
| if (cmd.getCustomId() != null) { | |
| vmGroupVO.setUuid(cmd.getCustomId()); | |
| } | |
| if (forDisplay != null) { | |
| vmGroupVO.setDisplay(forDisplay); | |
| } | |
| vmGroupVO = checkValidityAndPersist(vmGroupVO, scaleUpPolicyIds, scaleDownPolicyIds); | |
| if (vmGroupVO != null) { | |
| logger.debug("Updated Auto Scale VmGroup: {}", vmGroupVO); | |
| if ((interval != null && interval != currentInterval) || CollectionUtils.isNotEmpty(scaleUpPolicyIds) || CollectionUtils.isNotEmpty(scaleDownPolicyIds)) { | |
| markStatisticsAsInactive(vmGroupId, null); | |
| } | |
| return vmGroupVO; | |
| } else | |
| return null; | |
| } | |
| @Override | |
| @DB | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_ENABLE, eventDescription = "enabling autoscale vm group", async = true) | |
| public AutoScaleVmGroup enableAutoScaleVmGroup(Long id) { | |
| AutoScaleVmGroupVO vmGroup = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", id, autoScaleVmGroupDao); | |
| boolean success = false; | |
| if (vmGroup.getState().equals(AutoScaleVmGroup.State.ENABLED)) { | |
| return vmGroup; | |
| } | |
| if (!vmGroup.getState().equals(AutoScaleVmGroup.State.DISABLED)) { | |
| throw new InvalidParameterValueException("Only a AutoScale Vm Group which is in Disabled state can be enabled."); | |
| } | |
| try { | |
| vmGroup.setState(AutoScaleVmGroup.State.ENABLED); | |
| vmGroup = autoScaleVmGroupDao.persist(vmGroup); | |
| success = configureAutoScaleVmGroup(id, AutoScaleVmGroup.State.DISABLED); | |
| scheduleMonitorTask(vmGroup.getId()); | |
| } catch (ResourceUnavailableException e) { | |
| vmGroup.setState(AutoScaleVmGroup.State.DISABLED); | |
| autoScaleVmGroupDao.persist(vmGroup); | |
| } finally { | |
| if (!success) { | |
| logger.warn("Failed to enable AutoScale Vm Group: {}", vmGroup); | |
| return null; | |
| } | |
| logger.info("Successfully enabled AutoScale Vm Group: {}", vmGroup); | |
| createInactiveDummyRecord(vmGroup.getId()); | |
| } | |
| return vmGroup; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_AUTOSCALEVMGROUP_DISABLE, eventDescription = "disabling autoscale vm group", async = true) | |
| @DB | |
| public AutoScaleVmGroup disableAutoScaleVmGroup(Long id) { | |
| AutoScaleVmGroupVO vmGroup = getEntityInDatabase(CallContext.current().getCallingAccount(), "AutoScale Vm Group", id, autoScaleVmGroupDao); | |
| boolean success = false; | |
| if (vmGroup.getState().equals(AutoScaleVmGroup.State.DISABLED)) { | |
| return vmGroup; | |
| } | |
| if (!vmGroup.getState().equals(AutoScaleVmGroup.State.ENABLED) && !vmGroup.getState().equals(AutoScaleVmGroup.State.SCALING)) { | |
| throw new InvalidParameterValueException("Only a AutoScale Vm Group which is in Enabled or Scaling state can be disabled."); | |
| } | |
| try { | |
| vmGroup.setState(AutoScaleVmGroup.State.DISABLED); | |
| vmGroup = autoScaleVmGroupDao.persist(vmGroup); | |
| success = configureAutoScaleVmGroup(id, AutoScaleVmGroup.State.ENABLED); | |
| createInactiveDummyRecord(vmGroup.getId()); | |
| cancelMonitorTask(vmGroup.getId()); | |
| } catch (ResourceUnavailableException e) { | |
| vmGroup.setState(AutoScaleVmGroup.State.ENABLED); | |
| autoScaleVmGroupDao.persist(vmGroup); | |
| } finally { | |
| if (!success) { | |
| logger.warn("Failed to disable AutoScale Vm Group: {}", vmGroup); | |
| return null; | |
| } | |
| logger.info("Successfully disabled AutoScale Vm Group: {}", vmGroup); | |
| } | |
| return vmGroup; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_COUNTER_CREATE, eventDescription = "Counter", create = true) | |
| @DB | |
| public Counter createCounter(CreateCounterCmd cmd) { | |
| String source = cmd.getSource().toUpperCase(); | |
| String name = cmd.getName(); | |
| Counter.Source src; | |
| // Validate Source | |
| try { | |
| src = Counter.Source.valueOf(source); | |
| } catch (Exception ex) { | |
| throw new InvalidParameterValueException("The Source " + source + " does not exist; Unable to create Counter"); | |
| } | |
| // Validate Provider | |
| Network.Provider provider = Network.Provider.getProvider(cmd.getProvider()); | |
| if (provider == null) { | |
| throw new InvalidParameterValueException("The Provider " + cmd.getProvider() + " does not exist; Unable to create Counter"); | |
| } | |
| CounterVO counter = null; | |
| logger.debug("Adding Counter " + name); | |
| counter = counterDao.persist(new CounterVO(src, name, cmd.getValue(), provider)); | |
| CallContext.current().setEventDetails(" Id: " + counter.getId() + " Name: " + name); | |
| return counter; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_CONDITION_CREATE, eventDescription = "Condition", create = true) | |
| public Condition createCondition(CreateConditionCmd cmd) { | |
| Account caller = CallContext.current().getCallingAccount(); | |
| Account owner = accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); | |
| accountMgr.checkAccess(caller, null, true, owner); | |
| String opr = cmd.getRelationalOperator().toUpperCase(); | |
| long cid = cmd.getCounterId(); | |
| long threshold = cmd.getThreshold(); | |
| Condition.Operator op; | |
| // Validate Relational Operator | |
| try { | |
| op = Condition.Operator.valueOf(opr); | |
| } catch (IllegalArgumentException ex) { | |
| throw new InvalidParameterValueException("The Operator " + opr + " does not exist; Unable to create Condition."); | |
| } | |
| if (threshold < 0) { | |
| throw new InvalidParameterValueException("The threshold " + threshold + " must be equal to or greater than 0."); | |
| } | |
| CounterVO counter = counterDao.findById(cid); | |
| if (counter == null) { | |
| throw new InvalidParameterValueException("Unable to find counter"); | |
| } | |
| ConditionVO condition = null; | |
| condition = conditionDao.persist(new ConditionVO(cid, threshold, owner.getAccountId(), owner.getDomainId(), op)); | |
| logger.info("Successfully created condition: {}", condition); | |
| CallContext.current().setEventDetails(" Id: " + condition.getId()); | |
| return condition; | |
| } | |
| @Override | |
| public List<? extends Counter> listCounters(ListCountersCmd cmd) { | |
| String name = cmd.getName(); | |
| Long id = cmd.getId(); | |
| String source = cmd.getSource(); | |
| if (source != null) { | |
| source = source.toUpperCase(); | |
| } | |
| String providerStr = cmd.getProvider(); | |
| if (providerStr != null) { | |
| Network.Provider provider = Network.Provider.getProvider(providerStr); | |
| if (provider == null) { | |
| throw new InvalidParameterValueException("The Provider " + providerStr + " does not exist; Unable to list Counter"); | |
| } | |
| providerStr = provider.getName(); | |
| } | |
| Filter searchFilter = new Filter(CounterVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal()); | |
| return counterDao.listCounters(id, name, source, providerStr, cmd.getKeyword(), searchFilter); | |
| } | |
| @Override | |
| public List<? extends Condition> listConditions(ListConditionsCmd cmd) { | |
| Long id = cmd.getId(); | |
| Long counterId = cmd.getCounterId(); | |
| Long policyId = cmd.getPolicyId(); | |
| SearchWrapper<ConditionVO> searchWrapper = new SearchWrapper<>(conditionDao, ConditionVO.class, cmd, cmd.getId()); | |
| SearchBuilder<ConditionVO> sb = searchWrapper.getSearchBuilder(); | |
| if (policyId != null) { | |
| SearchBuilder<AutoScalePolicyConditionMapVO> asPolicyConditionSearch = autoScalePolicyConditionMapDao.createSearchBuilder(); | |
| asPolicyConditionSearch.and("policyId", asPolicyConditionSearch.entity().getPolicyId(), SearchCriteria.Op.EQ); | |
| sb.join("asPolicyConditionSearch", asPolicyConditionSearch, sb.entity().getId(), asPolicyConditionSearch.entity().getConditionId(), | |
| JoinBuilder.JoinType.INNER); | |
| } | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("counterId", sb.entity().getCounterId(), SearchCriteria.Op.EQ); | |
| // now set the SC criteria... | |
| SearchCriteria<ConditionVO> sc = searchWrapper.buildSearchCriteria(); | |
| if (id != null) { | |
| sc.setParameters("id", id); | |
| } | |
| if (counterId != null) { | |
| sc.setParameters("counterId", counterId); | |
| } | |
| if (policyId != null) { | |
| sc.setJoinParameters("asPolicyConditionSearch", "policyId", policyId); | |
| } | |
| return searchWrapper.search(); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_COUNTER_DELETE, eventDescription = "counter") | |
| public boolean deleteCounter(long counterId) throws ResourceInUseException { | |
| // Verify Counter id | |
| CounterVO counter = counterDao.findById(counterId); | |
| if (counter == null) { | |
| throw new InvalidParameterValueException("Unable to find Counter"); | |
| } | |
| // Verify if it is used in any Condition | |
| ConditionVO condition = conditionDao.findByCounterId(counterId); | |
| if (condition != null) { | |
| logger.info("Cannot delete counter {} as it is being used in a condition.", counter); | |
| throw new ResourceInUseException("Counter is in use."); | |
| } | |
| boolean success = counterDao.remove(counterId); | |
| if (success) { | |
| logger.info("Successfully deleted counter: {}", counter); | |
| } | |
| return success; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_CONDITION_DELETE, eventDescription = "delete a condition") | |
| public boolean deleteCondition(long conditionId) throws ResourceInUseException { | |
| /* Check if entity is in database */ | |
| ConditionVO condition = getEntityInDatabase(CallContext.current().getCallingAccount(), "Condition", conditionId, conditionDao); | |
| if (condition == null) { | |
| throw new InvalidParameterValueException("Unable to find Condition"); | |
| } | |
| // Verify if condition is used in any autoscale policy | |
| if (autoScalePolicyConditionMapDao.isConditionInUse(conditionId)) { | |
| logger.info("Cannot delete condition {} as it is being used in a condition.", condition); | |
| throw new ResourceInUseException("Cannot delete Condition when it is in use by one or more AutoScale Policies."); | |
| } | |
| boolean success = conditionDao.remove(conditionId); | |
| if (success) { | |
| logger.info("Successfully deleted condition {}", condition); | |
| } | |
| return success; | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_CONDITION_UPDATE, eventDescription = "update a condition") | |
| public Condition updateCondition(UpdateConditionCmd cmd) throws ResourceInUseException { | |
| Long conditionId = cmd.getId(); | |
| /* Check if entity is in database */ | |
| ConditionVO condition = getEntityInDatabase(CallContext.current().getCallingAccount(), "Condition", conditionId, conditionDao); | |
| String operator = cmd.getRelationalOperator().toUpperCase(); | |
| Long threshold = cmd.getThreshold(); | |
| Condition.Operator op; | |
| // Validate Relational Operator | |
| try { | |
| op = Condition.Operator.valueOf(operator); | |
| } catch (IllegalArgumentException ex) { | |
| throw new InvalidParameterValueException("The Operator " + operator + " does not exist; Unable to update Condition."); | |
| } | |
| if (threshold < 0) { | |
| throw new InvalidParameterValueException("The threshold " + threshold + " must be equal to or greater than 0."); | |
| } | |
| // Verify if condition is used in any autoscale vmgroup | |
| GenericSearchBuilder<AutoScalePolicyConditionMapVO, Long> conditionSearch = autoScalePolicyConditionMapDao.createSearchBuilder(Long.class); | |
| conditionSearch.selectFields(conditionSearch.entity().getPolicyId()); | |
| conditionSearch.and("conditionId", conditionSearch.entity().getConditionId(), Op.EQ); | |
| SearchCriteria<Long> sc = conditionSearch.create(); | |
| sc.setParameters("conditionId", conditionId); | |
| List<Long> policyIds = autoScalePolicyConditionMapDao.customSearch(sc, null); | |
| if (CollectionUtils.isNotEmpty(policyIds)) { | |
| SearchBuilder<AutoScaleVmGroupPolicyMapVO> policySearch = autoScaleVmGroupPolicyMapDao.createSearchBuilder(); | |
| policySearch.and("policyId", policySearch.entity().getPolicyId(), Op.IN); | |
| SearchBuilder<AutoScaleVmGroupVO> vmGroupSearch = autoScaleVmGroupDao.createSearchBuilder(); | |
| vmGroupSearch.and("stateNEQ", vmGroupSearch.entity().getState(), Op.NEQ); | |
| vmGroupSearch.join("policySearch", policySearch, vmGroupSearch.entity().getId(), policySearch.entity().getVmGroupId(), JoinBuilder.JoinType.INNER); | |
| vmGroupSearch.done(); | |
| SearchCriteria<AutoScaleVmGroupVO> sc2 = vmGroupSearch.create(); | |
| sc2.setParameters("stateNEQ", AutoScaleVmGroup.State.DISABLED); | |
| sc2.setJoinParameters("policySearch", "policyId", policyIds.toArray((new Object[policyIds.size()]))); | |
| List<AutoScaleVmGroupVO> groups = autoScaleVmGroupDao.search(sc2, null); | |
| if (CollectionUtils.isNotEmpty(groups)) { | |
| String msg = String.format("Cannot update condition %s as it is being used in %d vm groups NOT in Disabled state.", condition, groups.size()); | |
| logger.info(msg); | |
| throw new ResourceInUseException(msg); | |
| } | |
| } | |
| condition.setRelationalOperator(op); | |
| condition.setThreshold(threshold); | |
| boolean success = conditionDao.update(conditionId, condition); | |
| if (success) { | |
| logger.info("Successfully updated condition {}", condition); | |
| for (Long policyId : policyIds) { | |
| markStatisticsAsInactive(null, policyId); | |
| } | |
| } | |
| return condition; | |
| } | |
| @Override | |
| public boolean deleteAutoScaleVmGroupsByAccount(Account account) { | |
| boolean success = true; | |
| List<AutoScaleVmGroupVO> groups = autoScaleVmGroupDao.listByAccount(account.getId()); | |
| for (AutoScaleVmGroupVO group : groups) { | |
| logger.debug("Deleting AutoScale Vm Group {} for account: {}", group, account); | |
| try { | |
| deleteAutoScaleVmGroup(group.getId(), true); | |
| logger.debug("AutoScale Vm Group {} has been successfully deleted for account: {}", group, account); | |
| } catch (Exception e) { | |
| logger.warn("Failed to delete AutoScale Vm Group {} for account: {} due to: ", group, account, e); | |
| success = false; | |
| } | |
| } | |
| return success; | |
| } | |
| @Override | |
| public void cleanUpAutoScaleResources(Account account) { | |
| // cleans Autoscale VmProfiles, AutoScale Policies and Conditions belonging to an account | |
| int count = 0; | |
| count = autoScaleVmProfileDao.removeByAccountId(account.getId()); | |
| if (count > 0) { | |
| logger.debug("Deleted {} AutoScale Vm Profile for account: {}", count, account); | |
| } | |
| count = autoScalePolicyDao.removeByAccountId(account.getId()); | |
| if (count > 0) { | |
| logger.debug("Deleted {} AutoScale Policies for account: {}", count, account); | |
| } | |
| count = conditionDao.removeByAccountId(account.getId()); | |
| if (count > 0) { | |
| logger.debug("Deleted {} Conditions for account: {}", count, account); | |
| } | |
| } | |
| private boolean checkConditionUp(AutoScaleVmGroupVO asGroup, Integer numVm) { | |
| // check maximum | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countAvailableVmsByGroup(asGroup.getId()); | |
| Integer maxVm = asGroup.getMaxMembers(); | |
| if (currentVM + numVm > maxVm) { | |
| logger.warn("number of VM will greater than the maximum in this group if scaling up, so do nothing more"); | |
| return false; | |
| } | |
| int erroredInstanceCount = autoScaleVmGroupVmMapDao.getErroredInstanceCount(asGroup.getId()); | |
| if (erroredInstanceCount > AutoScaleManager.AutoScaleErroredInstanceThreshold.value()) { | |
| logger.warn("Number of Errored Instances are greater than the threshold in this group for scaling up, so do nothing more"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| private boolean checkConditionDown(AutoScaleVmGroupVO asGroup) { | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countAvailableVmsByGroup(asGroup.getId()); | |
| Integer minVm = asGroup.getMinMembers(); | |
| if (currentVM - 1 < minVm) { | |
| logger.warn("number of VM will less than the minimum in this group if scaling down, so do nothing more"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| protected Map<String, String> getDeployParams (String otherDeployParams) { | |
| Map<String, String> deployParams = new HashMap<>(); | |
| if (StringUtils.isNotBlank(otherDeployParams)) { | |
| for (String param : otherDeployParams.split("&")) { | |
| if (param.split("=").length >= 2) { | |
| deployParams.put(param.split("=")[0], param.split("=")[1]); | |
| } | |
| } | |
| } | |
| return deployParams; | |
| } | |
| protected UserVm createNewVM(AutoScaleVmGroupVO asGroup) { | |
| AutoScaleVmProfileVO profileVo = autoScaleVmProfileDao.findById(asGroup.getProfileId()); | |
| long templateId = profileVo.getTemplateId(); | |
| long serviceOfferingId = profileVo.getServiceOfferingId(); | |
| if (templateId == -1) { | |
| return null; | |
| } | |
| // create new VM into DB | |
| try { | |
| //Verify that all objects exist before passing them to the service | |
| Account owner = accountService.getActiveAccountById(profileVo.getAccountId()); | |
| DataCenter zone = entityMgr.findById(DataCenter.class, profileVo.getZoneId()); | |
| if (zone == null) { | |
| throw new InvalidParameterValueException("Unable to find zone by id=" + profileVo.getZoneId()); | |
| } | |
| ServiceOffering serviceOffering = entityMgr.findById(ServiceOffering.class, serviceOfferingId); | |
| if (serviceOffering == null) { | |
| throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId); | |
| } | |
| DiskOffering diskOffering = entityMgr.findById(DiskOffering.class, serviceOffering.getDiskOfferingId()); | |
| if (diskOffering == null) { | |
| throw new InvalidParameterValueException("Unable to find disk offering: " + serviceOffering.getDiskOfferingId()); | |
| } | |
| VirtualMachineTemplate template = entityMgr.findById(VirtualMachineTemplate.class, templateId); | |
| // Make sure a valid template ID was specified | |
| if (template == null) { | |
| throw new InvalidParameterValueException("Unable to use template " + templateId); | |
| } | |
| if (!zone.isLocalStorageEnabled()) { | |
| if (diskOffering.isUseLocalStorage()) { | |
| throw new InvalidParameterValueException(String.format("Zone is not configured to use local storage but disk offering %s associated to the service offering %s uses it", diskOffering, serviceOffering)); | |
| } | |
| } | |
| String userData = profileVo.getUserData(); | |
| Long userDataId = profileVo.getUserDataId(); | |
| String userDataDetails = profileVo.getUserDataDetails(); | |
| UserVm vm = null; | |
| IpAddresses addrs = new IpAddresses(null, null); | |
| HypervisorType hypervisorType = template.getHypervisorType(); | |
| final Network network = getNetwork(asGroup.getLoadBalancerId()); | |
| final Map<String, String> customParameters = new HashMap<>(); | |
| final Map<String, String> deployParams = getDeployParams(profileVo.getOtherDeployParams()); | |
| final List<Long> networkIds = getVmNetworkIds(deployParams, network.getId()); | |
| Long overrideDiskOfferingId = getVmOverrideDiskOfferingId(deployParams); | |
| Long diskOfferingId = getVmDiskOfferingId(deployParams); | |
| Long dataDiskSize = getVmDataDiskSize(deployParams); | |
| List<String> sshKeyPairs = getVmSshKeyPairs(deployParams, owner); | |
| List<Long> affinityGroupIdList = getVmAffinityGroupId(deployParams); | |
| updateVmDetails(deployParams, customParameters); | |
| String vmHostName = getNextVmHostName(asGroup); | |
| asGroup.setNextVmSeq(asGroup.getNextVmSeq() + 1); | |
| autoScaleVmGroupDao.persist(asGroup); | |
| if (zone.getNetworkType() == NetworkType.Basic) { | |
| vm = userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, vmHostName, | |
| vmHostName, diskOfferingId, dataDiskSize, null, null, | |
| hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs, | |
| null, null, true, null, affinityGroupIdList, customParameters, null, null, null, | |
| null, true, overrideDiskOfferingId, null, null); | |
| } else { | |
| if (networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds, | |
| Collections.emptyList())) { | |
| vm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, null, | |
| owner, vmHostName,vmHostName, diskOfferingId, dataDiskSize, null, null, | |
| hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs, | |
| null, null, true, null, affinityGroupIdList, customParameters, null, null, null, | |
| null, true, overrideDiskOfferingId, null, null, null); | |
| } else { | |
| vm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, vmHostName, vmHostName, | |
| diskOfferingId, dataDiskSize, null, null, | |
| hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs, | |
| null, addrs, true, null, affinityGroupIdList, customParameters, null, null, null, | |
| null, true, null, overrideDiskOfferingId, null, null); | |
| } | |
| } | |
| return vm; | |
| } catch (InsufficientCapacityException ex) { | |
| logger.info(ex); | |
| logger.trace(ex.getMessage(), ex); | |
| throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); | |
| } catch (ResourceUnavailableException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); | |
| } catch (ConcurrentOperationException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); | |
| } catch (ResourceAllocationException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); | |
| } | |
| } | |
| protected List<Long> getVmNetworkIds(Map<String, String> deployParams, long defaultNetworkId) { | |
| final List<Long> networkIds = new ArrayList<>(); | |
| networkIds.add(defaultNetworkId); | |
| if (deployParams.get(PARAM_NETWORK_IDS) != null) { // networkids, append to LB networkid | |
| String[] networkids = deployParams.get(PARAM_NETWORK_IDS).split(","); | |
| for (String networkid : networkids) { | |
| Network otherNetwork = networkDao.findByUuid(networkid); | |
| if (otherNetwork != null && otherNetwork.getId() != defaultNetworkId) { | |
| networkIds.add(otherNetwork.getId()); | |
| } | |
| } | |
| } | |
| return networkIds; | |
| } | |
| protected Long getVmOverrideDiskOfferingId(Map<String, String> deployParams) { | |
| Long overrideDiskOfferingId = null; // override ROOT disk offering | |
| if (deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID) != null) { | |
| String overrideDiskOfferingUuid = deployParams.get(PARAM_OVERRIDE_DISK_OFFERING_ID); | |
| DiskOffering overrideDiskOfferingInParam = diskOfferingDao.findByUuid(overrideDiskOfferingUuid); | |
| if (overrideDiskOfferingInParam != null) { | |
| overrideDiskOfferingId = overrideDiskOfferingInParam.getId(); | |
| } else { | |
| logger.warn("Cannot find disk offering by overridediskofferingid from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| return overrideDiskOfferingId; | |
| } | |
| protected final Long getVmDiskOfferingId(Map<String, String> deployParams) { | |
| Long diskOfferingId = null; // DATA disk offering ID | |
| if (deployParams.get(PARAM_DISK_OFFERING_ID) != null) { | |
| String diskOfferingUuid = deployParams.get(PARAM_DISK_OFFERING_ID); | |
| DiskOffering diskOfferingInParam = diskOfferingDao.findByUuid(diskOfferingUuid); | |
| if (diskOfferingInParam != null) { | |
| diskOfferingId = diskOfferingInParam.getId(); | |
| } else { | |
| logger.warn("Cannot find disk offering by diskofferingid from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| return diskOfferingId; | |
| } | |
| protected Long getVmDataDiskSize(Map<String, String> deployParams) { | |
| Long dataDiskSize = null; // DATA disk size | |
| if (deployParams.get(PARAM_DISK_SIZE) != null) { | |
| String dataDiskSizeInParam = deployParams.get(PARAM_DISK_SIZE); | |
| try { | |
| dataDiskSize = Long.parseLong(dataDiskSizeInParam); | |
| } catch (NumberFormatException ex) { | |
| logger.warn("Cannot parse size from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| return dataDiskSize; | |
| } | |
| protected List<String> getVmSshKeyPairs(Map<String, String> deployParams, Account owner) { | |
| List<String> sshKeyPairs = new ArrayList<>(); | |
| if (deployParams.get(PARAM_SSH_KEYPAIRS) != null) { // SSH keypairs | |
| String[] keypairs = deployParams.get(PARAM_SSH_KEYPAIRS).split(","); | |
| for (String keypair : keypairs) { | |
| SSHKeyPairVO s = sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), keypair); | |
| if (s != null) { | |
| sshKeyPairs.add(s.getName()); | |
| } else { | |
| logger.warn("Cannot find ssh keypair by name in sshkeypairs from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| } | |
| return sshKeyPairs; | |
| } | |
| protected List<Long> getVmAffinityGroupId(Map<String, String> deployParams) { | |
| List<Long> affinityGroupIdList = new ArrayList<>(); | |
| if (deployParams.get(PARAM_AFFINITY_GROUP_IDS) != null) { // Affinity groups | |
| String[] affinityGroupIds = deployParams.get(PARAM_AFFINITY_GROUP_IDS).split(","); | |
| for (String affinityGroupId : affinityGroupIds) { | |
| AffinityGroupVO affintyGroup = affinityGroupDao.findByUuid(affinityGroupId); | |
| if (affintyGroup != null) { | |
| affinityGroupIdList.add(affintyGroup.getId()); | |
| } else { | |
| logger.warn("Cannot find affinity group by affinitygroupids from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| } | |
| return affinityGroupIdList; | |
| } | |
| public void updateVmDetails(Map<String, String> deployParams, Map<String, String> customParameters) { | |
| if (deployParams.get(PARAM_ROOT_DISK_SIZE) != null) { // ROOT disk size | |
| String value = deployParams.get(PARAM_ROOT_DISK_SIZE); | |
| try { | |
| Long rootDiskSize = Long.parseLong(value); | |
| customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSize)); | |
| } catch (NumberFormatException ex) { | |
| logger.warn("Cannot parse rootdisksize from otherdeployparams in AutoScale Vm profile"); | |
| } | |
| } | |
| } | |
| @Override | |
| public String getNextVmHostName(AutoScaleVmGroupVO asGroup) { | |
| String vmHostNameSuffix = "-" + asGroup.getNextVmSeq() + "-" + | |
| RandomStringUtils.random(VM_HOSTNAME_RANDOM_SUFFIX_LENGTH, 0, 0, true, false, (char[])null, new SecureRandom()).toLowerCase(); | |
| // Truncate vm group name because max length of vm name is 63 | |
| int subStringLength = Math.min(asGroup.getName().length(), 63 - VM_HOSTNAME_PREFIX.length() - vmHostNameSuffix.length()); | |
| return VM_HOSTNAME_PREFIX + asGroup.getName().substring(0, subStringLength) + vmHostNameSuffix; | |
| } | |
| @Override | |
| public void checkAutoScaleVmGroupName(String groupName) { | |
| String errorMessage = ""; | |
| if (groupName == null || groupName.length() > 255 || groupName.length() < 1) { | |
| errorMessage = "AutoScale Vm Group name must be between 1 and 255 characters long"; | |
| } else if (!groupName.toLowerCase().matches("[a-z0-9-]*")) { | |
| errorMessage = "AutoScale Vm Group name may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner), " + | |
| "the digits '0' through '9' and the hyphen ('-')"; | |
| } | |
| if (StringUtils.isNotBlank(errorMessage)) { | |
| logger.warn(errorMessage); | |
| throw new InvalidParameterValueException("Invalid AutoScale VM group name. It can contain the ASCII letters 'a' through 'z', " + | |
| "'A' through 'Z', the digits '0' through '9' and the hyphen ('-'), must be between 1 and 255 characters long."); | |
| } | |
| } | |
| private UserVmVO startNewVM(long vmId) { | |
| try { | |
| CallContext.current().setEventDetails("Vm Id: " + vmId); | |
| return userVmMgr.startVirtualMachine(vmId, null, new HashMap<>(), null).first(); | |
| } catch (final ResourceUnavailableException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); | |
| } catch (ResourceAllocationException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); | |
| } catch (ConcurrentOperationException ex) { | |
| logger.warn("Exception: ", ex); | |
| throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); | |
| } catch (InsufficientCapacityException ex) { | |
| StringBuilder message = new StringBuilder(ex.getMessage()); | |
| if (ex instanceof InsufficientServerCapacityException) { | |
| if (((InsufficientServerCapacityException)ex).isAffinityApplied()) { | |
| message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); | |
| } | |
| } | |
| logger.info(ex); | |
| logger.info(message.toString(), ex); | |
| throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); | |
| } | |
| } | |
| private boolean assignLBruleToNewVm(long vmId, AutoScaleVmGroupVO asGroup) { | |
| List<Long> lstVmId = new ArrayList<Long>(); | |
| long lbId = asGroup.getLoadBalancerId(); | |
| List<LoadBalancerVMMapVO> lbVmMapVos = lbVmMapDao.listByLoadBalancerId(lbId); | |
| if (CollectionUtils.isNotEmpty(lbVmMapVos)) { | |
| for (LoadBalancerVMMapVO LbVmMapVo : lbVmMapVos) { | |
| long instanceId = LbVmMapVo.getInstanceId(); | |
| if (instanceId == vmId) { | |
| logger.warn("the new VM is already mapped to LB rule. What's wrong?"); | |
| return true; | |
| } | |
| } | |
| } | |
| lstVmId.add(new Long(vmId)); | |
| try { | |
| return loadBalancingRulesService.assignToLoadBalancer(lbId, lstVmId, new HashMap<>(), true); | |
| } catch (CloudRuntimeException ex) { | |
| logger.warn("Caught exception: ", ex); | |
| return false; | |
| } | |
| } | |
| private long removeLBrule(AutoScaleVmGroupVO asGroup) { | |
| long lbId = asGroup.getLoadBalancerId(); | |
| long instanceId = -1; | |
| List<LoadBalancerVMMapVO> lbVmMapVos = lbVmMapDao.listByLoadBalancerId(lbId); | |
| if (CollectionUtils.isNotEmpty(lbVmMapVos)) { | |
| for (LoadBalancerVMMapVO LbVmMapVo : lbVmMapVos) { | |
| instanceId = LbVmMapVo.getInstanceId(); | |
| } | |
| } | |
| // take last VM out of the list | |
| List<Long> lstVmId = new ArrayList<Long>(); | |
| if (instanceId != -1) | |
| lstVmId.add(instanceId); | |
| if (loadBalancingRulesService.removeFromLoadBalancer(lbId, lstVmId, new HashMap<>(), true)) | |
| return instanceId; | |
| else | |
| return -1; | |
| } | |
| @Override | |
| public void doScaleUp(long groupId, Integer numVm) { | |
| AutoScaleVmGroupVO asGroup = autoScaleVmGroupDao.findById(groupId); | |
| if (asGroup == null) { | |
| logger.error("Can not find the groupid " + groupId + " for scaling up"); | |
| return; | |
| } | |
| if (!checkConditionUp(asGroup, numVm)) { | |
| return; | |
| } | |
| AutoScaleVmGroup.State oldState = asGroup.getState(); | |
| AutoScaleVmGroup.State newState = AutoScaleVmGroup.State.SCALING; | |
| if (!autoScaleVmGroupDao.updateState(groupId, oldState, newState)) { | |
| logger.error("Can not update vmgroup state from {} to {}, groupId: {}", oldState, newState, asGroup); | |
| return; | |
| } | |
| try { | |
| for (int i = 0; i < numVm; i++) { | |
| ActionEventUtils.onStartedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP, | |
| "Scaling Up AutoScale VM group " + groupId, groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), | |
| true, 0); | |
| UserVm vm = createNewVM(asGroup); | |
| if (vm == null) { | |
| logger.error("Can not deploy new VM for scaling up in the group {}. Waiting for next round", asGroup); | |
| break; | |
| } | |
| // persist to DB | |
| AutoScaleVmGroupVmMapVO groupVmMapVO = new AutoScaleVmGroupVmMapVO(asGroup.getId(), vm.getId()); | |
| autoScaleVmGroupVmMapDao.persist(groupVmMapVO); | |
| // Add an Inactive-dummy record to statistics table | |
| createInactiveDummyRecord(asGroup.getId()); | |
| try { | |
| startNewVM(vm.getId()); | |
| createInactiveDummyRecord(asGroup.getId()); | |
| if (assignLBruleToNewVm(vm.getId(), asGroup)) { | |
| // update last_quietTime | |
| List<AutoScaleVmGroupPolicyMapVO> groupPolicyVOs = autoScaleVmGroupPolicyMapDao | |
| .listByVmGroupId(groupId); | |
| for (AutoScaleVmGroupPolicyMapVO groupPolicyVO : groupPolicyVOs) { | |
| AutoScalePolicyVO vo = autoScalePolicyDao | |
| .findById(groupPolicyVO.getPolicyId()); | |
| if (vo.getAction().equals(AutoScalePolicy.Action.SCALEUP)) { | |
| vo.setLastQuietTime(new Date()); | |
| autoScalePolicyDao.persist(vo); | |
| break; | |
| } | |
| } | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP, | |
| String.format("Started and assigned LB rule for VM %s in AutoScale VM group %s", vm, asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| } else { | |
| logger.error("Can not assign LB rule for this new VM"); | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP, | |
| String.format("Failed to assign LB rule for VM %s in AutoScale VM group %s", vm, asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| break; | |
| } | |
| } catch (ServerApiException e) { | |
| logger.error("Can not deploy new VM for scaling up in the group {}. Waiting for next round", asGroup); | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEUP, | |
| String.format("Failed to start VM %s in AutoScale VM group %s", vm, asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| destroyVm(vm.getId()); | |
| break; | |
| } | |
| } | |
| } finally { | |
| if (!autoScaleVmGroupDao.updateState(groupId, newState, oldState)) { | |
| logger.error("Can not update vmgroup state from {} back to {}, group: {}", newState, oldState, asGroup); | |
| } | |
| } | |
| } | |
| @Override | |
| public void doScaleDown(final long groupId) { | |
| AutoScaleVmGroupVO asGroup = autoScaleVmGroupDao.findById(groupId); | |
| if (asGroup == null) { | |
| logger.error("Can not find the groupid " + groupId + " for scaling down"); | |
| return; | |
| } | |
| if (!checkConditionDown(asGroup)) { | |
| return; | |
| } | |
| AutoScaleVmGroup.State oldState = asGroup.getState(); | |
| AutoScaleVmGroup.State newState = AutoScaleVmGroup.State.SCALING; | |
| if (!autoScaleVmGroupDao.updateState(groupId, oldState, newState)) { | |
| logger.error("Can not update vmgroup state from {} to {}, groupId: {}", oldState, newState, asGroup); | |
| return; | |
| } | |
| ActionEventUtils.onStartedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, | |
| String.format("Scaling down AutoScale VM group %s", asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), | |
| true, 0); | |
| try { | |
| long vmId = -1; | |
| try { | |
| vmId = removeLBrule(asGroup); | |
| } catch (Exception ex) { | |
| logger.info("Got exception when remove LB rule for a VM in AutoScale VM group: {}", asGroup, ex); | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, | |
| String.format("Failed to remove LB rule for a VM in AutoScale VM group %s", asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| throw ex; | |
| } | |
| if (vmId != -1) { | |
| long profileId = asGroup.getProfileId(); | |
| // update group-vm mapping | |
| autoScaleVmGroupVmMapDao.remove(groupId, vmId); | |
| // update last_quietTime | |
| List<AutoScaleVmGroupPolicyMapVO> groupPolicyVOs = autoScaleVmGroupPolicyMapDao.listByVmGroupId(groupId); | |
| for (AutoScaleVmGroupPolicyMapVO groupPolicyVO : groupPolicyVOs) { | |
| AutoScalePolicyVO vo = autoScalePolicyDao.findById(groupPolicyVO.getPolicyId()); | |
| if (vo.getAction().equals(AutoScalePolicy.Action.SCALEUP)) { | |
| vo.setLastQuietTime(new Date()); | |
| autoScalePolicyDao.persist(vo); | |
| break; | |
| } | |
| } | |
| // Add an Inactive-dummy record to statistics table | |
| createInactiveDummyRecord(asGroup.getId()); | |
| // get expungeVmGracePeriod param | |
| AutoScaleVmProfileVO asProfile = autoScaleVmProfileDao.findById(profileId); | |
| Integer expungeVmGracePeriod = asProfile.getExpungeVmGracePeriod(); | |
| if (expungeVmGracePeriod > 0) { | |
| try { | |
| Thread.sleep(expungeVmGracePeriod * 1000L); | |
| } catch (InterruptedException e) { | |
| Thread.currentThread().interrupt(); | |
| throw new CloudRuntimeException(String.format("Error while waiting %s seconds to destroy the VM %s", expungeVmGracePeriod, vmId)); | |
| } | |
| } | |
| if (destroyVm(vmId)) { | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, | |
| String.format("Destroyed VM %d in AutoScale VM group %s", vmId, asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| } else { | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, | |
| String.format("Failed to destroy VM %d in AutoScale VM group %s", vmId, asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| } | |
| } else { | |
| logger.error("Can not remove LB rule for the VM being destroyed. Do nothing more."); | |
| ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, asGroup.getAccountId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_AUTOSCALEVMGROUP_SCALEDOWN, | |
| String.format("Failed to remove LB rule for a VM in AutoScale VM group %s", asGroup), groupId, ApiCommandResourceType.AutoScaleVmGroup.toString(), 0); | |
| } | |
| } finally { | |
| if (!autoScaleVmGroupDao.updateState(groupId, newState, oldState)) { | |
| logger.error("Can not update vmgroup state from {} back to {}, groupId: {}", newState, oldState, asGroup); | |
| } | |
| } | |
| } | |
| @Override | |
| public String getConfigComponentName() { | |
| return AutoScaleManager.class.getSimpleName(); | |
| } | |
| @Override | |
| public ConfigKey<?>[] getConfigKeys() { | |
| return new ConfigKey<?>[] { | |
| AutoScaleStatsInterval, | |
| AutoScaleStatsCleanupDelay, | |
| AutoScaleStatsWorker, | |
| AutoScaleErroredInstanceThreshold | |
| }; | |
| } | |
| @Override | |
| public void checkAllAutoScaleVmGroups() { | |
| // list all AS VMGroups | |
| List<AutoScaleVmGroupVO> asGroups = autoScaleVmGroupDao.listAll(); | |
| for (AutoScaleVmGroupVO asGroup : asGroups) { | |
| completionService.submit(new CheckAutoScaleVmGroupAsync(asGroup)); | |
| } | |
| for (int i = 0; i < asGroups.size(); i++) { | |
| try { | |
| Future<Pair<Long, Boolean>> future = completionService.take(); | |
| Pair<Long, Boolean> result = future.get(); | |
| logger.debug("Checked AutoScale vm group " + result.first() + " with result: " + result.second()); | |
| } catch (ExecutionException ex) { | |
| logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex); | |
| } catch (InterruptedException ex) { | |
| logger.warn("Failed to get result of checking AutoScale vm group due to Exception: " , ex); | |
| Thread.currentThread().interrupt(); | |
| } | |
| } | |
| } | |
| protected class CheckAutoScaleVmGroupAsync implements Callable<Pair<Long, Boolean>> { | |
| AutoScaleVmGroupVO asGroup; | |
| public CheckAutoScaleVmGroupAsync(AutoScaleVmGroupVO asGroup) { | |
| this.asGroup = asGroup; | |
| } | |
| @Override | |
| public Pair<Long, Boolean> call() { | |
| try { | |
| logger.debug("Checking AutoScale vm group " + asGroup); | |
| checkAutoScaleVmGroup(asGroup); | |
| } catch (Exception ex) { | |
| logger.warn("Failed to check AutoScale vm group " + asGroup + " due to Exception: " , ex); | |
| return new Pair<>(asGroup.getId(), false); | |
| } | |
| return new Pair<>(asGroup.getId(), true); | |
| } | |
| } | |
| @Override | |
| public void checkAutoScaleVmGroup(AutoScaleVmGroupVO asGroup) { | |
| // check group state | |
| if (asGroup.getState().equals(AutoScaleVmGroup.State.ENABLED)) { | |
| Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId()); | |
| if (Network.Provider.Netscaler.equals(provider)) { | |
| checkNetScalerAsGroup(asGroup); | |
| } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider) || | |
| Network.Provider.Netris.equals(provider)) { | |
| checkAutoscalingGroup(asGroup); | |
| } | |
| } | |
| } | |
| protected boolean isNative(AutoScaleVmGroupTO groupTO) { | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counterTO = conditionTO.getCounter(); | |
| if (Counter.NativeSources.contains(counterTO.getSource())) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| protected boolean hasSourceVirtualRouter(AutoScaleVmGroupTO groupTO) { | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counterTO = conditionTO.getCounter(); | |
| if (Counter.Source.VIRTUALROUTER.equals(counterTO.getSource())) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| protected Map<Long, List<Long>> getHostAndVmIdsMap(AutoScaleVmGroupTO groupTO) { | |
| Map<Long, List<Long>> hostAndVmIdsMap = new HashMap<>(); | |
| List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = autoScaleVmGroupVmMapDao.listByGroup(groupTO.getId()); | |
| for (AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs) { | |
| updateHostAndVmIdsMap(hostAndVmIdsMap, asGroupVmVO); | |
| } | |
| return hostAndVmIdsMap; | |
| } | |
| protected void updateHostAndVmIdsMap(Map<Long, List<Long>> hostAndVmIdsMap, AutoScaleVmGroupVmMapVO asGroupVmVO) { | |
| Long vmId = asGroupVmVO.getInstanceId(); | |
| UserVmVO vm = userVmDao.findById(vmId); | |
| Long vmHostId = DEFAULT_HOST_ID; | |
| if (VirtualMachine.State.Running.equals(vm.getState())) { | |
| vmHostId = vm.getHostId(); | |
| } else if (VirtualMachine.State.Migrating.equals(vm.getState())) { | |
| vmHostId = vm.getLastHostId(); | |
| } | |
| List<Long> vmIds = hostAndVmIdsMap.get(vmHostId); | |
| if (vmIds == null) { | |
| vmIds = new ArrayList<>(); | |
| } | |
| vmIds.add(vmId); | |
| hostAndVmIdsMap.put(vmHostId, vmIds); | |
| } | |
| protected Map<Long, List<CounterTO>> getPolicyCounters(AutoScaleVmGroupTO groupTO) { | |
| Map<Long, List<CounterTO>> counters = new HashMap<>(); | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| List<CounterTO> counterTOs = new ArrayList<>(); | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counterTO = conditionTO.getCounter(); | |
| counterTOs.add(counterTO); | |
| } | |
| counters.put(policyTO.getId(), counterTOs); | |
| } | |
| return counters; | |
| } | |
| protected AutoScalePolicy.Action getAutoscaleAction(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO) { | |
| logger.debug("[AutoScale] Getting autoscale action for group [id={}, uuid={}]", groupTO.getId(), groupTO.getUuid()); | |
| Network.Provider provider = getLoadBalancerServiceProvider(groupTO.getLoadBalancerId()); | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| if (isQuitTimePassForPolicy(policyTO)) { | |
| AutoScalePolicy.Action determinedAction = checkConditionsForPolicy(countersMap, countersNumberMap, policyTO, provider); | |
| if (determinedAction != null) { | |
| return determinedAction; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| protected boolean isQuitTimePassForPolicy(AutoScalePolicyTO policyTO) { | |
| int quietTime = policyTO.getQuietTime(); | |
| Date quietTimeDate = policyTO.getLastQuietTime(); | |
| long lastQuiettime = 0L; | |
| if (quietTimeDate != null) { | |
| lastQuiettime = policyTO.getLastQuietTime().getTime(); | |
| } | |
| long currentTime = (new Date()).getTime(); | |
| return (currentTime - lastQuiettime) >= quietTime * 1000L; | |
| } | |
| protected AutoScalePolicy.Action checkConditionsForPolicy(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScalePolicyTO policyTO, Network.Provider provider) { | |
| boolean bValid = true; | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counter = conditionTO.getCounter(); | |
| long thresholdValue = conditionTO.getThreshold(); | |
| Double thresholdPercent = (double)thresholdValue; | |
| String key = generateKeyFromPolicyAndConditionAndCounter(policyTO.getId(), conditionTO.getId(), counter.getId()); | |
| if (Network.Provider.Netscaler.equals(provider)) { | |
| key = generateKeyFromPolicyAndConditionAndCounter(0L, conditionTO.getId(), counter.getId()); | |
| } | |
| Double sum = countersMap.get(key); | |
| Integer number = countersNumberMap.get(key); | |
| logger.debug("Checking policy [id: {}, uuid: {}], condition [id: {}, uuid: {}], counter = \"{}\", sum = {}, number = {}", | |
| policyTO.getId(), policyTO.getUuid(), conditionTO.getId(), conditionTO.getUuid(), counter.getName(), sum, number); | |
| if (number == null || number == 0) { | |
| bValid = false; | |
| logger.debug("Skipping policy [id: {}, uuid: {}], condition [id: {}, uuid: {}], counter = \"{}\" because the number is {}", | |
| policyTO.getId(), policyTO.getUuid(), conditionTO.getId(), conditionTO.getUuid(), counter.getName(), number); | |
| break; | |
| } | |
| Double avg = sum / number; | |
| Condition.Operator op = conditionTO.getRelationalOperator(); | |
| boolean bConditionCheck = ((op == com.cloud.network.as.Condition.Operator.EQ) && (thresholdPercent.equals(avg))) | |
| || ((op == com.cloud.network.as.Condition.Operator.GE) && (avg.doubleValue() >= thresholdPercent.doubleValue())) | |
| || ((op == com.cloud.network.as.Condition.Operator.GT) && (avg.doubleValue() > thresholdPercent.doubleValue())) | |
| || ((op == com.cloud.network.as.Condition.Operator.LE) && (avg.doubleValue() <= thresholdPercent.doubleValue())) | |
| || ((op == com.cloud.network.as.Condition.Operator.LT) && (avg.doubleValue() < thresholdPercent.doubleValue())); | |
| logger.debug("Check result on policy [id: {}, uuid: {}], condition [id: {}, uuid: {}], counter = {} is : {}" + | |
| " (actual result = {}, operator = {}, threshold = {})", | |
| policyTO.getId(), policyTO.getUuid(), conditionTO.getId(), conditionTO.getUuid(), counter.getSource(), bConditionCheck, avg, op, thresholdPercent); | |
| if (!bConditionCheck) { | |
| bValid = false; | |
| break; | |
| } | |
| } | |
| AutoScalePolicy.Action action = bValid ? policyTO.getAction() : null; | |
| logger.debug("Check result on policy [id: {}, uuid: {}] is {}", policyTO.getId(), policyTO.getUuid(), action); | |
| return action; | |
| } | |
| private List<Pair<String, Integer>> getPairofCounternameAndDuration(AutoScaleVmGroupTO groupTO) { | |
| List<Pair<String, Integer>> result = new ArrayList<>(); | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| //get duration | |
| Integer duration = policyTO.getDuration(); | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counter = conditionTO.getCounter(); | |
| StringBuilder buff = new StringBuilder(); | |
| buff.append(counter.getName()); | |
| buff.append(","); | |
| buff.append(conditionTO.getId()); | |
| // add to result | |
| Pair<String, Integer> pair = new Pair<>(buff.toString(), duration); | |
| result.add(pair); | |
| } | |
| } | |
| return result; | |
| } | |
| protected Network getNetwork(Long loadBalancerId) { | |
| final LoadBalancerVO loadBalancer = lbDao.findById(loadBalancerId); | |
| if (loadBalancer == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId)); | |
| } | |
| Network network = networkDao.findById(loadBalancer.getNetworkId()); | |
| if (network == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find network with id: %s for load balancer: %s", loadBalancer.getNetworkId(), loadBalancer)); | |
| } | |
| return network; | |
| } | |
| protected Pair<String, Integer> getPublicIpAndPort(Long loadBalancerId) { | |
| final LoadBalancerVO loadBalancer = lbDao.findById(loadBalancerId); | |
| if (loadBalancer == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId)); | |
| } | |
| IPAddressVO ipAddress = ipAddressDao.findById(loadBalancer.getSourceIpAddressId()); | |
| if (ipAddress == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find IP Address with id: %s for load balancer: %s", loadBalancer.getSourceIpAddressId(), loadBalancer)); | |
| } | |
| return new Pair<>(ipAddress.getAddress().addr(), loadBalancer.getSourcePortStart()); | |
| } | |
| protected Network.Provider getLoadBalancerServiceProvider(Long loadBalancerId) { | |
| final LoadBalancerVO loadBalancer = lbDao.findById(loadBalancerId); | |
| if (loadBalancer == null) { | |
| throw new CloudRuntimeException(String.format("Unable to find load balancer with id: %s ", loadBalancerId)); | |
| } | |
| return lbRulesMgr.getLoadBalancerServiceProvider(loadBalancer); | |
| } | |
| protected boolean checkAsGroupMaxAndMinMembers(AutoScaleVmGroupVO asGroup) { | |
| // check minimum vm of group | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countAvailableVmsByGroup(asGroup.getId()); | |
| if (currentVM < asGroup.getMinMembers()) { | |
| logger.debug(String.format("There are currently %s available VMs which is less than the minimum member of " + | |
| "the AS group (%s), scaling up %d VMs", currentVM, asGroup.getMinMembers(), asGroup.getMinMembers() - currentVM)); | |
| doScaleUp(asGroup.getId(), asGroup.getMinMembers() - currentVM); | |
| return false; | |
| } | |
| // check maximum vm of group | |
| if (currentVM > asGroup.getMaxMembers()) { | |
| logger.debug(String.format("There are currently %s available VMs which is more than the maximum member of " + | |
| "the AS group (%s), scaling down %d VMs", currentVM, asGroup.getMaxMembers(), currentVM - asGroup.getMaxMembers())); | |
| for (int i = 0; i < currentVM - asGroup.getMaxMembers(); i++) { | |
| doScaleDown(asGroup.getId()); | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| protected void checkNetScalerAsGroup(AutoScaleVmGroupVO asGroup) { | |
| AutoScaleVmGroupTO groupTO = lbRulesMgr.toAutoScaleVmGroupTO(asGroup); | |
| if (!isNative(groupTO)) { | |
| return; | |
| } | |
| if (!checkAsGroupMaxAndMinMembers(asGroup)) { | |
| return; | |
| } | |
| //check interval | |
| long now = (new Date()).getTime(); | |
| if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) { | |
| return; | |
| } | |
| // update last_interval | |
| asGroup.setLastInterval(new Date()); | |
| autoScaleVmGroupDao.persist(asGroup); | |
| if (logger.isDebugEnabled()) { | |
| logger.debug("[Netscaler AutoScale] Collecting RRDs data..."); | |
| } | |
| Map<String, String> params = new HashMap<>(); | |
| List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = autoScaleVmGroupVmMapDao.listByGroup(asGroup.getId()); | |
| params.put("total_vm", String.valueOf(asGroupVmVOs.size())); | |
| for (int i = 0; i < asGroupVmVOs.size(); i++) { | |
| long vmId = asGroupVmVOs.get(i).getInstanceId(); | |
| VMInstanceVO vmVO = vmInstanceDao.findById(vmId); | |
| //xe vm-list | grep vmname -B 1 | head -n 1 | awk -F':' '{print $2}' | |
| params.put("vmname" + (i + 1), vmVO.getInstanceName()); | |
| params.put("vmid" + (i + 1), String.valueOf(vmVO.getId())); | |
| } | |
| // get random hostid because all vms are in a cluster | |
| long vmId = asGroupVmVOs.get(0).getInstanceId(); | |
| VMInstanceVO vmVO = vmInstanceDao.findById(vmId); | |
| Long receiveHost = vmVO.getHostId(); | |
| setPerformanceMonitorCommandParams(groupTO, params); | |
| PerformanceMonitorCommand perfMon = new PerformanceMonitorCommand(params, 20); | |
| try { | |
| PerformanceMonitorAnswer answer = (PerformanceMonitorAnswer) agentMgr.send(receiveHost, perfMon); | |
| if (answer == null || !answer.getResult()) { | |
| logger.debug("Failed to send data to node !"); | |
| } else { | |
| String result = answer.getDetails(); | |
| logger.debug("[AutoScale] RRDs collection answer: " + result); | |
| HashMap<String, Double> countersMap = new HashMap<>(); | |
| HashMap<String, Integer> countersNumberMap = new HashMap<>(); | |
| processPerformanceMonitorAnswer(countersMap, countersNumberMap, groupTO, params, result); | |
| AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO); | |
| if (scaleAction != null) { | |
| logger.debug("[AutoScale] Doing scale action: {} for group {}", scaleAction, asGroup); | |
| if (AutoScalePolicy.Action.SCALEUP.equals(scaleAction)) { | |
| doScaleUp(asGroup.getId(), 1); | |
| } else { | |
| doScaleDown(asGroup.getId()); | |
| } | |
| } | |
| } | |
| } catch (Exception e) { | |
| logger.error("Cannot sent PerformanceMonitorCommand to host " + receiveHost + " or process the answer due to Exception: ", e); | |
| } | |
| } | |
| protected void setPerformanceMonitorCommandParams(AutoScaleVmGroupTO groupTO, Map<String, String> params) { | |
| // setup parameters phase: duration and counter | |
| // list pair [counter, duration] | |
| List<Pair<String, Integer>> lstPair = getPairofCounternameAndDuration(groupTO); | |
| int totalCounter = 0; | |
| String[] lstCounter = new String[lstPair.size()]; | |
| for (int i = 0; i < lstPair.size(); i++) { | |
| Pair<String, Integer> pair = lstPair.get(i); | |
| String strCounterNames = pair.first(); | |
| Integer duration = pair.second(); | |
| lstCounter[i] = strCounterNames.split(",")[0]; // counter name | |
| totalCounter++; | |
| params.put("duration" + totalCounter, duration.toString()); | |
| params.put("counter" + totalCounter, lstCounter[i]); | |
| params.put("con" + totalCounter, strCounterNames.split(",")[1]); // condition id | |
| } | |
| params.put("totalCounter", String.valueOf(totalCounter)); | |
| } | |
| protected void processPerformanceMonitorAnswer(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, | |
| AutoScaleVmGroupTO groupTO, Map<String, String> params, String details) { | |
| // extract data | |
| String[] counterElements = details.split(","); | |
| if ((counterElements != null) && (counterElements.length > 0)) { | |
| for (String string : counterElements) { | |
| try { | |
| String[] counterVals = string.split(":"); | |
| String[] counterVm = counterVals[0].split("\\."); | |
| Long counterId = Long.parseLong(counterVm[1]); | |
| ConditionTO condition = new ConditionTO(Long.parseLong(params.get("con" + counterVm[0])), null, 0L, null, null); | |
| // For NetScaler, the policyId is not returned in PerformanceMonitorAnswer | |
| AutoScalePolicyTO policy = new AutoScalePolicyTO(0L, null, 0, 0, null, null, null, false); | |
| Double coVal = Double.parseDouble(counterVals[1]); | |
| updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counterId, condition, policy, coVal, AutoScaleValueType.INSTANT_VM); | |
| } catch (Exception e) { | |
| logger.error("Cannot process PerformanceMonitorAnswer due to Exception: ", e); | |
| } | |
| } | |
| } | |
| } | |
| protected void updateCountersMapWithInstantData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, AutoScaleVmGroupTO groupTO, | |
| Long counterId, ConditionTO condition, AutoScalePolicyTO policy, Double coVal, AutoScaleValueType valueType) { | |
| // Summary of all counter by counterId key | |
| String key = generateKeyFromPolicyAndConditionAndCounter(policy.getId(), condition.getId(), counterId); | |
| CounterVO counter = counterDao.findById(counterId); | |
| if (counter == null) { | |
| return; | |
| } | |
| if (Counter.Source.MEMORY.equals(counter.getSource())) { | |
| // calculate memory in percent | |
| AutoScaleVmProfileTO profile = groupTO.getProfile(); | |
| ServiceOfferingVO serviceOff = serviceOfferingDao.findByUuidIncludingRemoved(profile.getServiceOfferingId()); | |
| int maxRAM = serviceOff.getRamSize(); | |
| // get current RAM percent | |
| coVal = coVal / maxRAM * 100; | |
| } else if (Counter.Source.CPU.equals(counter.getSource())) { | |
| // cpu | |
| coVal = coVal * 100; | |
| } | |
| if (AutoScaleValueType.INSTANT_VM_GROUP.equals(valueType)) { | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countAvailableVmsByGroup(groupTO.getId()); | |
| if (currentVM == 0) { | |
| logger.debug("Skipping updating countersMap for group [id={}, uuid={}] " + | |
| "and policy [id={}, uuid={}] and counter [id={}, uuid={}] due to no VMs", | |
| groupTO.getId(), groupTO.getUuid(), policy.getId(), policy.getUuid(), counter.getId(), counter.getUuid()); | |
| return; | |
| } | |
| coVal = coVal / currentVM; | |
| } | |
| updateCountersMapWithProcessedData(countersMap, countersNumberMap, key, coVal); | |
| } | |
| protected void updateCountersMapWithProcessedData(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, String key, Double coVal) { | |
| /* initialize if data is not set */ | |
| countersMap.computeIfAbsent(key, k -> Double.valueOf(0)); | |
| countersNumberMap.computeIfAbsent(key, k -> 0); | |
| // update data entry | |
| countersMap.put(key, countersMap.get(key) + coVal); | |
| countersNumberMap.put(key, countersNumberMap.get(key) + 1); | |
| } | |
| protected void monitorAutoscalingGroup(AutoScaleVmGroupVO asGroup) { | |
| if (!checkAsGroupMaxAndMinMembers(asGroup)) { | |
| return; | |
| } | |
| //check interval | |
| long now = (new Date()).getTime(); | |
| if (asGroup.getLastInterval() != null && (now - asGroup.getLastInterval().getTime()) < asGroup.getInterval()) { | |
| return; | |
| } | |
| // update last_interval | |
| asGroup.setLastInterval(new Date()); | |
| autoScaleVmGroupDao.persist(asGroup); | |
| logger.debug("[AutoScale] Collecting performance data ..."); | |
| AutoScaleVmGroupTO groupTO = lbRulesMgr.toAutoScaleVmGroupTO(asGroup); | |
| if (isNative(groupTO)) { | |
| logger.debug("[AutoScale] Collecting performance data from hosts ..."); | |
| getVmStatsFromHosts(groupTO); | |
| } | |
| if (hasSourceVirtualRouter(groupTO)) { | |
| logger.debug("[AutoScale] Collecting performance data from virtual router ..."); | |
| getNetworkStatsFromVirtualRouter(groupTO); | |
| } | |
| } | |
| protected void checkAutoscalingGroup(AutoScaleVmGroupVO asGroup) { | |
| AutoScaleVmGroupTO groupTO = lbRulesMgr.toAutoScaleVmGroupTO(asGroup); | |
| Map<String, Double> countersMap = new HashMap<>(); | |
| Map<String, Integer> countersNumberMap = new HashMap<>(); | |
| try { | |
| // update counter maps in memory | |
| if (!updateCountersMap(groupTO, countersMap, countersNumberMap)) { | |
| return; | |
| } | |
| } finally { | |
| // Remove old statistics from database | |
| cleanupAsVmGroupStatistics(groupTO); | |
| } | |
| // get scale action | |
| AutoScalePolicy.Action scaleAction = getAutoscaleAction(countersMap, countersNumberMap, groupTO); | |
| if (scaleAction != null) { | |
| logger.debug("[AutoScale] Doing scale action: {} for group {}", scaleAction, asGroup); | |
| if (AutoScalePolicy.Action.SCALEUP.equals(scaleAction)) { | |
| doScaleUp(asGroup.getId(), 1); | |
| } else { | |
| doScaleDown(asGroup.getId()); | |
| } | |
| } | |
| } | |
| protected void getVmStatsFromHosts(AutoScaleVmGroupTO groupTO) { | |
| // group vms by host id | |
| Map<Long, List<Long>> hostAndVmIdsMap = getHostAndVmIdsMap(groupTO); | |
| Map<Long, List<CounterTO>> policyCountersMap = getPolicyCounters(groupTO); | |
| // get vm stats from each host and update database | |
| for (Map.Entry<Long, List<Long>> hostAndVmIds : hostAndVmIdsMap.entrySet()) { | |
| Long hostId = hostAndVmIds.getKey(); | |
| List<Long> vmIds = hostAndVmIds.getValue(); | |
| if (!DEFAULT_HOST_ID.equals(hostId)) { | |
| Map<Long, ? extends VmStats> vmStatsById = getVmStatsByIdFromHost(hostId, vmIds); | |
| processVmStatsByIdFromHost(groupTO, vmIds, vmStatsById, policyCountersMap); | |
| } | |
| } | |
| } | |
| protected Map<Long, ? extends VmStats> getVmStatsByIdFromHost(Long hostId, List<Long> vmIds) { | |
| Map<Long, ? extends VmStats> vmStatsById = new HashMap<>(); | |
| HostVO host = hostDao.findById(hostId); | |
| if (host == null) { | |
| logger.debug("Failed to get VM stats from non-existing host : " + hostId); | |
| return vmStatsById; | |
| } | |
| try { | |
| vmStatsById = virtualMachineManager.getVirtualMachineStatistics(host, vmIds); | |
| if (MapUtils.isEmpty(vmStatsById)) { | |
| logger.warn("Got empty result for virtual machine statistics from host: " + host); | |
| } | |
| } catch (Exception e) { | |
| logger.debug("Failed to get VM stats from host : {}", host); | |
| } | |
| return vmStatsById; | |
| } | |
| protected void processVmStatsByIdFromHost(AutoScaleVmGroupTO groupTO, List<Long> vmIds, Map<Long, ? extends VmStats> vmStatsById, Map<Long, List<CounterTO>> policyCountersMap) { | |
| Date timestamp = new Date(); | |
| for (Long vmId : vmIds) { | |
| VmStatsEntry vmStats = vmStatsById == null ? null : (VmStatsEntry)vmStatsById.get(vmId); | |
| for (Map.Entry<Long, List<CounterTO>> policyCounters : policyCountersMap.entrySet()) { | |
| Long policyId = policyCounters.getKey(); | |
| List<CounterTO> counters = policyCounters.getValue(); | |
| for (CounterTO counter : counters) { | |
| if (!Counter.NativeSources.contains(counter.getSource())) { | |
| continue; | |
| } | |
| Double counterValue = null; | |
| if (vmStats != null) { | |
| if (Counter.Source.CPU.equals(counter.getSource())) { | |
| counterValue = vmStats.getCPUUtilization() / 100; | |
| } else if (Counter.Source.MEMORY.equals(counter.getSource())) { | |
| if (vmStats.getIntFreeMemoryKBs() >= 0 && vmStats.getIntFreeMemoryKBs() <= vmStats.getMemoryKBs()) { | |
| counterValue = (vmStats.getMemoryKBs() - vmStats.getIntFreeMemoryKBs()) / 1024; | |
| } else { | |
| // In some scenarios, the free memory is greater than VM memory | |
| // see https://github.com/apache/cloudstack/issues/4566 | |
| logger.warn(String.format("Getting virtual machine statistics return invalid free memory KBs for VM %d: %f", vmId, vmStats.getIntFreeMemoryKBs())); | |
| } | |
| } | |
| } | |
| if (counterValue == null) { | |
| asGroupStatisticsDao.persist(new AutoScaleVmGroupStatisticsVO(groupTO.getId(), policyId, counter.getId(), vmId, ResourceTag.ResourceObjectType.UserVm, | |
| AutoScaleValueType.INSTANT_VM, timestamp)); | |
| } else { | |
| asGroupStatisticsDao.persist(new AutoScaleVmGroupStatisticsVO(groupTO.getId(), policyId, counter.getId(), vmId, ResourceTag.ResourceObjectType.UserVm, | |
| counterValue, AutoScaleValueType.INSTANT_VM, timestamp)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| protected void getNetworkStatsFromVirtualRouter(AutoScaleVmGroupTO groupTO) { | |
| Network network = getNetwork(groupTO.getLoadBalancerId()); | |
| Pair<String, Integer> publicIpAddr = getPublicIpAndPort(groupTO.getLoadBalancerId()); | |
| List<DomainRouterVO> routers = routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER); | |
| if (CollectionUtils.isEmpty(routers)) { | |
| processGetAutoScaleMetricsAnswer(groupTO, new ArrayList<>(), null); | |
| return; | |
| } | |
| List<AutoScaleMetrics> metrics = setGetAutoScaleMetricsCommandMetrics(groupTO); | |
| for (DomainRouterVO router : routers) { | |
| if (VirtualMachine.State.Running.equals(router.getState())) { | |
| final GetAutoScaleMetricsCommand command = new GetAutoScaleMetricsCommand(router.getPrivateIpAddress(), network.getVpcId() != null, publicIpAddr.first(), publicIpAddr.second(), metrics); | |
| command.setWait(30); | |
| GetAutoScaleMetricsAnswer answer = (GetAutoScaleMetricsAnswer) agentMgr.easySend(router.getHostId(), command); | |
| if (answer == null || !answer.getResult()) { | |
| logger.error("Failed to get autoscale metrics from virtual router {}", router); | |
| processGetAutoScaleMetricsAnswer(groupTO, new ArrayList<>(), router.getId()); | |
| } else { | |
| processGetAutoScaleMetricsAnswer(groupTO, answer.getValues(), router.getId()); | |
| } | |
| } else { | |
| processGetAutoScaleMetricsAnswer(groupTO, new ArrayList<>(), router.getId()); | |
| } | |
| } | |
| } | |
| protected List<AutoScaleMetrics> setGetAutoScaleMetricsCommandMetrics(AutoScaleVmGroupTO groupTO) { | |
| List<AutoScaleMetrics> metrics = new ArrayList<>(); | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| CounterTO counter = conditionTO.getCounter(); | |
| String provider = counter.getProvider(); | |
| if (! Network.Provider.VirtualRouter.getName().equals(provider) && ! Network.Provider.VPCVirtualRouter.getName().equals(provider)) { | |
| continue; | |
| } | |
| VirtualRouterAutoScaleCounter vrCounter = VirtualRouterAutoScaleCounter.fromValue(counter.getValue()); | |
| if (vrCounter == null) { | |
| continue; | |
| } | |
| metrics.add(new AutoScaleMetrics(vrCounter, policyTO.getId(), conditionTO.getId(), counter.getId(), policyTO.getDuration())); | |
| } | |
| } | |
| return metrics; | |
| } | |
| protected void processGetAutoScaleMetricsAnswer(AutoScaleVmGroupTO groupTO, List<AutoScaleMetricsValue> values, Long routerId) { | |
| Date timestamp = new Date(); | |
| Map<Long, List<CounterTO>> policyCountersMap = getPolicyCounters(groupTO); | |
| for (Map.Entry<Long, List<CounterTO>> policyCounters : policyCountersMap.entrySet()) { | |
| Long policyId = policyCounters.getKey(); | |
| List<CounterTO> counters = policyCounters.getValue(); | |
| for (CounterTO counter : counters) { | |
| if (!Counter.Source.VIRTUALROUTER.equals(counter.getSource())) { | |
| continue; | |
| } | |
| boolean found = false; | |
| for (AutoScaleMetricsValue value : values) { | |
| AutoScaleMetrics metrics = value.getMetrics(); | |
| if (policyId.equals(metrics.getPolicyId()) && counter.getId().equals(metrics.getCounterId())) { | |
| asGroupStatisticsDao.persist(new AutoScaleVmGroupStatisticsVO(groupTO.getId(), policyId, counter.getId(), routerId, | |
| ResourceTag.ResourceObjectType.DomainRouter, value.getValue(), value.getType(), timestamp)); | |
| found = true; | |
| break; | |
| } | |
| } | |
| if (!found) { | |
| asGroupStatisticsDao.persist(new AutoScaleVmGroupStatisticsVO(groupTO.getId(), policyId, counter.getId(), routerId, | |
| ResourceTag.ResourceObjectType.DomainRouter, AutoScaleValueType.INSTANT_VM, timestamp)); | |
| } | |
| } | |
| } | |
| } | |
| protected boolean updateCountersMap(AutoScaleVmGroupTO groupTO, Map<String, Double> countersMap, Map<String, Integer> countersNumberMap) { | |
| logger.debug("Updating countersMap for as group [id={}, uuid={}]", groupTO.getId(), groupTO.getUuid()); | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| Date afterDate = new Date(System.currentTimeMillis() - ((long)policyTO.getDuration() << 10)); | |
| List<AutoScaleVmGroupStatisticsVO> dummyStats = asGroupStatisticsDao.listDummyRecordsByVmGroup(groupTO.getId(), afterDate); | |
| if (CollectionUtils.isNotEmpty(dummyStats)) { | |
| logger.error("Failed to update counters map as there are {} dummy statistics in as group {}", dummyStats.size(), groupTO.getId()); | |
| return false; | |
| } | |
| List<AutoScaleVmGroupStatisticsVO> inactiveStats = asGroupStatisticsDao.listInactiveByVmGroupAndPolicy(groupTO.getId(), policyTO.getId(), afterDate); | |
| if (CollectionUtils.isNotEmpty(inactiveStats)) { | |
| logger.error("Failed to update counters map as there are {} Inactive " + | |
| "statistics in as group [id={}, uuid={}] and policy [id={}, uuid={}]", | |
| inactiveStats.size(), groupTO.getId(), groupTO.getUuid(), policyTO.getId(), policyTO.getUuid()); | |
| continue; | |
| } | |
| for (ConditionTO conditionTO : policyTO.getConditions()) { | |
| updateCountersMapPerCondition(groupTO, policyTO, conditionTO, afterDate, countersMap, countersNumberMap); | |
| } | |
| } | |
| logger.debug("DONE Updating countersMap for as group [id={}, uuid={}]", groupTO.getId(), groupTO.getUuid()); | |
| return true; | |
| } | |
| private void updateCountersMapPerCondition(AutoScaleVmGroupTO groupTO, AutoScalePolicyTO policyTO, ConditionTO conditionTO, Date afterDate, | |
| Map<String, Double> countersMap, Map<String, Integer> countersNumberMap) { | |
| Long conditionId = conditionTO.getId(); | |
| CounterTO counter = conditionTO.getCounter(); | |
| List<AutoScaleVmGroupStatisticsVO> stats = asGroupStatisticsDao.listByVmGroupAndPolicyAndCounter(groupTO.getId(), policyTO.getId(), counter.getId(), afterDate); | |
| if (CollectionUtils.isEmpty(stats)) { | |
| logger.debug("Skipping updating countersMap for group {} and policy {} and " + | |
| "counter {} due to no stats", groupTO.getId(), groupTO.getUuid(), policyTO.getId()); | |
| return; | |
| } | |
| logger.debug("Updating countersMap with {} stats for group {} and policy {} and " + | |
| "counter {}", stats.size(), groupTO.getId(), groupTO.getUuid(), policyTO.getId()); | |
| Map<String, List<AutoScaleVmGroupStatisticsVO>> aggregatedRecords = new HashMap<>(); | |
| List<String> incorrectRecords = new ArrayList<>(); | |
| for (AutoScaleVmGroupStatisticsVO stat : stats) { | |
| if (Arrays.asList(AutoScaleValueType.INSTANT_VM, AutoScaleValueType.INSTANT_VM_GROUP).contains(stat.getValueType())) { | |
| updateCountersMapWithInstantData(countersMap, countersNumberMap, groupTO, counter.getId(), conditionTO, policyTO, stat.getRawValue(), stat.getValueType()); | |
| } else if (Arrays.asList(AutoScaleValueType.AGGREGATED_VM, AutoScaleValueType.AGGREGATED_VM_GROUP).contains(stat.getValueType())) { | |
| String key = stat.getCounterId() + "-" + stat.getResourceId(); | |
| if (incorrectRecords.contains(key)) { | |
| continue; | |
| } | |
| if (!aggregatedRecords.containsKey(key)) { | |
| List<AutoScaleVmGroupStatisticsVO> aggregatedRecordList = new ArrayList<>(); | |
| aggregatedRecordList.add(stat); | |
| aggregatedRecords.put(key, aggregatedRecordList); | |
| continue; | |
| } | |
| List<AutoScaleVmGroupStatisticsVO> aggregatedRecordList = aggregatedRecords.get(key); | |
| AutoScaleVmGroupStatisticsVO lastRecord = aggregatedRecordList.get(aggregatedRecordList.size() - 1); | |
| if (stat.getCreated().after(lastRecord.getCreated())) { | |
| if (stat.getRawValue() >= lastRecord.getRawValue()) { | |
| aggregatedRecordList.add(stat); | |
| } else { | |
| logger.info("The new raw value is less than the previous raw value, which means the data is incorrect. The key is " + key); | |
| aggregatedRecords.remove(key); | |
| incorrectRecords.add(key); | |
| } | |
| } | |
| } | |
| } | |
| updateCountersMapByAggregatedRecords(countersMap, countersNumberMap, aggregatedRecords, conditionTO, policyTO, groupTO); | |
| } | |
| public void updateCountersMapByAggregatedRecords(Map<String, Double> countersMap, Map<String, Integer> countersNumberMap, | |
| Map<String, List<AutoScaleVmGroupStatisticsVO>> aggregatedRecords, | |
| ConditionTO condition, AutoScalePolicyTO policy, AutoScaleVmGroupTO group) { | |
| if (MapUtils.isNotEmpty(aggregatedRecords)) { | |
| logger.debug("Processing aggregated data"); | |
| for (Map.Entry<String, List<AutoScaleVmGroupStatisticsVO>> aggregatedRecord : aggregatedRecords.entrySet()) { | |
| String recordKey = aggregatedRecord.getKey(); | |
| Long counterId = Long.valueOf(recordKey.split("-")[0]); | |
| List<AutoScaleVmGroupStatisticsVO> records = aggregatedRecord.getValue(); | |
| if (records.size() <= 1) { | |
| logger.info(String.format("Ignoring aggregated records, condition [id=%d, uuid=%s], counterId = %s", | |
| condition.getId(), condition.getUuid(), counterId)); | |
| continue; | |
| } | |
| AutoScaleVmGroupStatisticsVO firstRecord = records.get(0); | |
| AutoScaleVmGroupStatisticsVO lastRecord = records.get(records.size() - 1); | |
| Double coVal = (lastRecord.getRawValue() - firstRecord.getRawValue()) * 1000 / (lastRecord.getCreated().getTime() - firstRecord.getCreated().getTime()); | |
| if (AutoScaleValueType.AGGREGATED_VM_GROUP.equals(firstRecord.getValueType())) { | |
| Integer currentVM = autoScaleVmGroupVmMapDao.countAvailableVmsByGroup(group.getId()); | |
| if (currentVM == 0) { | |
| logger.debug("Skipping updating countersMap for group [id={}," + | |
| " uuid={}] and policy [id={}, uuid={}] and counter {} due to no VMs", | |
| group.getId(), group.getUuid(), policy.getId(), policy.getUuid(), counterId); | |
| return; | |
| } | |
| coVal = coVal / currentVM; | |
| } | |
| String key = generateKeyFromPolicyAndConditionAndCounter(policy.getId(), condition.getId(), counterId); | |
| updateCountersMapWithProcessedData(countersMap, countersNumberMap, key, coVal); | |
| } | |
| } | |
| } | |
| private String generateKeyFromPolicyAndConditionAndCounter(Long policyId, Long conditionId, Long counterId) { | |
| return policyId + "-" + conditionId + "-" + counterId; | |
| } | |
| protected void cleanupAsVmGroupStatistics(AutoScaleVmGroupTO groupTO) { | |
| Integer cleanupDelay = AutoScaleStatsCleanupDelay.value(); | |
| Integer maxDelaySecs = cleanupDelay; | |
| for (AutoScalePolicyTO policyTO : groupTO.getPolicies()) { | |
| Integer duration = policyTO.getDuration(); | |
| Integer delaySecs = cleanupDelay >= duration ? cleanupDelay : duration; | |
| Date beforeDate = new Date(System.currentTimeMillis() - ((long)delaySecs * 1000)); | |
| logger.debug("Removing stats for policy [id={}, uuid={}] in as group [id={}, uuid={}], before {}", | |
| policyTO.getId(), policyTO.getUuid(), groupTO.getId(), groupTO.getUuid(), beforeDate); | |
| asGroupStatisticsDao.removeByGroupAndPolicy(groupTO.getId(), policyTO.getId(), beforeDate); | |
| if (delaySecs > maxDelaySecs) { | |
| maxDelaySecs = delaySecs; | |
| } | |
| } | |
| Date beforeDate = new Date(System.currentTimeMillis() - ((long)maxDelaySecs * 1000)); | |
| logger.debug(String.format("Removing stats for other policies in as group [id=%d, uuid=%s], before %s", groupTO.getId(), groupTO.getUuid(), beforeDate)); | |
| asGroupStatisticsDao.removeByGroupId(groupTO.getId(), beforeDate); | |
| } | |
| protected void scheduleMonitorTasks() { | |
| List<AutoScaleVmGroupVO> vmGroups = autoScaleVmGroupDao.listAll(); | |
| for (AutoScaleVmGroupVO vmGroup : vmGroups) { | |
| if (vmGroup.getState().equals(AutoScaleVmGroup.State.ENABLED)) { | |
| scheduleMonitorTask(vmGroup.getId()); | |
| } | |
| } | |
| } | |
| protected void scheduleMonitorTask(Long groupId) { | |
| ScheduledExecutorService vmGroupExecutor = vmGroupMonitorMaps.get(groupId); | |
| if (vmGroupExecutor == null) { | |
| AutoScaleVmGroupVO vmGroup = autoScaleVmGroupDao.findById(groupId); | |
| logger.debug("Scheduling monitor task for autoscale vm group " + vmGroup); | |
| vmGroupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VmGroup-Monitor-" + groupId)); | |
| vmGroupExecutor.scheduleWithFixedDelay(new MonitorTask(groupId), vmGroup.getInterval(), vmGroup.getInterval(), TimeUnit.SECONDS); | |
| vmGroupMonitorMaps.put(groupId, vmGroupExecutor); | |
| } | |
| } | |
| protected void cancelMonitorTask(Long groupId) { | |
| ScheduledExecutorService vmGroupExecutor = vmGroupMonitorMaps.get(groupId); | |
| if (vmGroupExecutor != null) { | |
| logger.debug("Cancelling monitor task for autoscale vm group " + groupId); | |
| vmGroupExecutor.shutdown(); | |
| vmGroupMonitorMaps.remove(groupId); | |
| } | |
| } | |
| protected class MonitorTask extends ManagedContextRunnable { | |
| Long groupId; | |
| public MonitorTask(Long groupId) { | |
| this.groupId = groupId; | |
| } | |
| @Override | |
| protected synchronized void runInContext() { | |
| try { | |
| AutoScaleVmGroupVO asGroup = autoScaleVmGroupDao.findById(groupId); | |
| if (asGroup == null) { | |
| logger.error("Can not find the groupid " + groupId + " for monitoring"); | |
| return; | |
| } | |
| logger.debug("Start monitoring on AutoScale VmGroup " + asGroup); | |
| // check group state | |
| if (asGroup.getState().equals(AutoScaleVmGroup.State.ENABLED)) { | |
| Network.Provider provider = getLoadBalancerServiceProvider(asGroup.getLoadBalancerId()); | |
| if (Network.Provider.Netscaler.equals(provider)) { | |
| logger.debug("Skipping the monitoring on AutoScale VmGroup with Netscaler provider: " + asGroup); | |
| } else if (Network.Provider.VirtualRouter.equals(provider) || Network.Provider.VPCVirtualRouter.equals(provider) || | |
| Network.Provider.Netris.equals(provider)) { | |
| monitorAutoscalingGroup(asGroup); | |
| } | |
| } | |
| } catch (final Exception e) { | |
| logger.warn("Caught the following exception on monitoring AutoScale Vm Group", e); | |
| } | |
| } | |
| } | |
| @Override | |
| public void checkIfVmActionAllowed(Long vmId) { | |
| List<AutoScaleVmGroupVmMapVO> asGroupVmVOs = autoScaleVmGroupVmMapDao.listByVm(vmId); | |
| if (CollectionUtils.isNotEmpty(asGroupVmVOs)) { | |
| for (AutoScaleVmGroupVmMapVO asGroupVmVO : asGroupVmVOs) { | |
| AutoScaleVmGroupVO group = autoScaleVmGroupDao.findById(asGroupVmVO.getVmGroupId()); | |
| if (group != null && !AutoScaleVmGroup.State.DISABLED.equals(group.getState())) { | |
| throw new InvalidParameterValueException("Cannot perform actions on VM in an AutoScale VM group, Please disable the VM group and retry."); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void removeVmFromVmGroup(Long vmId) { | |
| autoScaleVmGroupVmMapDao.removeByVm(vmId); | |
| } | |
| protected boolean destroyVm(Long vmId) { | |
| UserVmVO vm = userVmDao.findById(vmId); | |
| try { | |
| if (vm != null) { | |
| userVmMgr.destroyVm(vmId, true); | |
| userVmMgr.expunge(vm); | |
| } | |
| return true; | |
| } catch (Exception ex) { | |
| logger.error("Cannot destroy vm {} with id: {} due to Exception: ", vm, vmId, ex); | |
| return false; | |
| } | |
| } | |
| private void markStatisticsAsInactive(Long groupId, Long policyId) { | |
| asGroupStatisticsDao.updateStateByGroup(groupId, policyId, AutoScaleVmGroupStatisticsVO.State.INACTIVE); | |
| } | |
| private void createInactiveDummyRecord(Long groupId) { | |
| asGroupStatisticsDao.createInactiveDummyRecord(groupId); | |
| } | |
| } |