diff --git a/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java b/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java index 894c493baff..9519a612e8a 100755 --- a/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalPingServiceImpl.java @@ -63,7 +63,7 @@ public class BareMetalPingServiceImpl extends BareMetalPxeServiceBase implements List idList = new ArrayList(); idList.add(new IdentityProxy("pod", podId, "podId")); idList.add(new IdentityProxy(zone, zoneId, "zoneId")); - InvalidParameterValueException ex = new InvalidParameterValueException("Already had a PXE server in Pod with specified podId and zone with specified zoneId", idList); + throw new InvalidParameterValueException("Already had a PXE server in Pod with specified podId and zone with specified zoneId", idList); } diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 8ed22d3ccbd..8e7f4489e7e 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1,1549 +1,1547 @@ -// Copyright 2012 Citrix Systems, Inc. Licensed under the -// Apache License, Version 2.0 (the "License"); you may not use this -// file except in compliance with the License. Citrix Systems, Inc. -// reserves all rights not expressly granted by 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. -// -// Automatically generated by addcopyright.py at 04/03/2012 -package com.cloud.network.lb; - -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; - -import com.cloud.api.commands.CreateLBStickinessPolicyCmd; -import com.cloud.api.commands.CreateLoadBalancerRuleCmd; -import com.cloud.api.commands.ListLBStickinessPoliciesCmd; -import com.cloud.api.commands.ListLoadBalancerRuleInstancesCmd; -import com.cloud.api.commands.ListLoadBalancerRulesCmd; -import com.cloud.api.commands.UpdateLoadBalancerRuleCmd; -import com.cloud.api.response.ServiceResponse; -import com.cloud.configuration.Config; -import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.dc.dao.VlanDao; -import com.cloud.domain.dao.DomainDao; -import com.cloud.event.ActionEvent; -import com.cloud.event.EventTypes; -import com.cloud.event.UsageEventVO; -import com.cloud.event.dao.EventDao; -import com.cloud.event.dao.UsageEventDao; -import com.cloud.exception.InsufficientAddressCapacityException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.NetworkRuleConflictException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.ExternalLoadBalancerUsageManager; -import com.cloud.network.IPAddressVO; -import com.cloud.network.IpAddress; -import com.cloud.network.LBStickinessPolicyVO; -import com.cloud.network.LoadBalancerVMMapVO; -import com.cloud.network.LoadBalancerVO; -import com.cloud.network.Network; -import com.cloud.network.Network.Capability; -import com.cloud.network.Network.Provider; -import com.cloud.network.Network.Service; -import com.cloud.network.NetworkManager; -import com.cloud.network.NetworkVO; -import com.cloud.network.as.AutoScalePolicy; -import com.cloud.network.as.AutoScalePolicyConditionMapVO; -import com.cloud.network.as.AutoScaleVmGroup; -import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; -import com.cloud.network.as.AutoScaleVmGroupVO; -import com.cloud.network.as.AutoScaleVmProfile; -import com.cloud.network.as.Condition; -import com.cloud.network.as.Counter; -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.AutoScaleVmProfileDao; -import com.cloud.network.as.dao.ConditionDao; -import com.cloud.network.as.dao.CounterDao; -import com.cloud.network.dao.FirewallRulesCidrsDao; -import com.cloud.network.dao.FirewallRulesDao; -import com.cloud.network.dao.IPAddressDao; -import com.cloud.network.dao.LBStickinessPolicyDao; -import com.cloud.network.dao.LoadBalancerDao; -import com.cloud.network.dao.LoadBalancerVMMapDao; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.NetworkServiceMapDao; -import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy; -import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup; -import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile; -import com.cloud.network.lb.LoadBalancingRule.LbCondition; -import com.cloud.network.lb.LoadBalancingRule.LbDestination; -import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; -import com.cloud.network.rules.FirewallManager; -import com.cloud.network.rules.FirewallRule; -import com.cloud.network.rules.FirewallRule.FirewallRuleType; -import com.cloud.network.rules.FirewallRule.Purpose; -import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.LbStickinessMethod; -import com.cloud.network.rules.LbStickinessMethod.LbStickinessMethodParam; -import com.cloud.network.rules.LoadBalancer; -import com.cloud.network.rules.RulesManager; -import com.cloud.network.rules.StickinessPolicy; -import com.cloud.network.vpc.VpcManager; -import com.cloud.offering.NetworkOffering; -import com.cloud.projects.Project.ListProjectResourcesCriteria; -import com.cloud.server.ResourceTag.TaggedResourceType; -import com.cloud.tags.ResourceTagVO; -import com.cloud.tags.dao.ResourceTagDao; -import com.cloud.template.TemplateManager; -import com.cloud.user.Account; -import com.cloud.user.AccountManager; -import com.cloud.user.DomainService; -import com.cloud.user.User; -import com.cloud.user.UserContext; -import com.cloud.user.dao.AccountDao; -import com.cloud.user.dao.UserDao; -import com.cloud.uservm.UserVm; -import com.cloud.utils.IdentityProxy; -import com.cloud.utils.Ternary; -import com.cloud.utils.component.Inject; -import com.cloud.utils.component.Manager; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.Filter; -import com.cloud.utils.db.JoinBuilder; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.net.NetUtils; -import com.cloud.vm.Nic; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.UserVmDao; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -@Local(value = { LoadBalancingRulesManager.class, LoadBalancingRulesService.class }) -public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, LoadBalancingRulesService, Manager { - private static final Logger s_logger = Logger.getLogger(LoadBalancingRulesManagerImpl.class); - - String _name; - - @Inject - NetworkManager _networkMgr; - @Inject - RulesManager _rulesMgr; - @Inject - AccountManager _accountMgr; - @Inject - IPAddressDao _ipAddressDao; - @Inject - LoadBalancerDao _lbDao; - @Inject - VlanDao _vlanDao; - @Inject - EventDao _eventDao; - @Inject - LoadBalancerVMMapDao _lb2VmMapDao; - @Inject - LBStickinessPolicyDao _lb2stickinesspoliciesDao; - @Inject - UserVmDao _vmDao; - @Inject - AccountDao _accountDao; - @Inject - DomainDao _domainDao; - @Inject - NicDao _nicDao; - @Inject - UsageEventDao _usageEventDao; - @Inject - FirewallRulesCidrsDao _firewallCidrsDao; - @Inject - FirewallManager _firewallMgr; - @Inject - ElasticLoadBalancerManager _elbMgr; - @Inject - NetworkDao _networkDao; - @Inject - FirewallRulesDao _firewallDao; - @Inject - DomainService _domainMgr; - @Inject - ConfigurationManager _configMgr; - @Inject - TemplateManager _templateMgr; - @Inject - ExternalLoadBalancerUsageManager _externalLBUsageMgr; - @Inject - NetworkServiceMapDao _ntwkSrvcDao; - @Inject - ResourceTagDao _resourceTagDao; - @Inject - CounterDao _counterDao; - @Inject - ConditionDao _conditionDao; - @Inject - AutoScaleVmProfileDao _autoScaleVmProfileDao; - @Inject - AutoScalePolicyDao _autoScalePolicyDao; - @Inject - AutoScalePolicyConditionMapDao _autoScalePolicyConditionMapDao; - @Inject - AutoScaleVmGroupDao _autoScaleVmGroupDao; - @Inject - AutoScaleVmGroupPolicyMapDao _autoScaleVmGroupPolicyMapDao; - @Inject - ConfigurationDao _configDao; - @Inject - DataCenterDao _dcDao = null; - @Inject - UserDao _userDao; - @Inject - VpcManager _vpcMgr; - - // Will return a string. For LB Stickiness this will be a json, for autoscale this will be "," separated values - @Override - public String getLBCapability(long networkid, String capabilityName) { - Map> serviceCapabilitiesMap = _networkMgr.getNetworkCapabilities(networkid); - if (serviceCapabilitiesMap != null) { - for (Service service : serviceCapabilitiesMap.keySet()) { - ServiceResponse serviceResponse = new ServiceResponse(); - serviceResponse.setName(service.getName()); - if ("Lb".equalsIgnoreCase(service.getName())) { - Map serviceCapabilities = serviceCapabilitiesMap - .get(service); - if (serviceCapabilities != null) { - for (Capability capability : serviceCapabilities - .keySet()) { - if (capabilityName.equals(capability.getName())) { - return serviceCapabilities.get(capability); - } - } - } - } - } - } - return null; - } - - private LbAutoScaleVmGroup getLbAutoScaleVmGroup(AutoScaleVmGroup vmGroup) { - List vmGroupPolicyMapList = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(vmGroup.getId()); - List autoScalePolicies = new ArrayList(); - for (AutoScaleVmGroupPolicyMapVO vmGroupPolicyMap : vmGroupPolicyMapList) { - AutoScalePolicy autoScalePolicy = _autoScalePolicyDao.findById(vmGroupPolicyMap.getPolicyId()); - List autoScalePolicyConditionMapList = _autoScalePolicyConditionMapDao.listByAll(autoScalePolicy.getId(), null); - List lbConditions = new ArrayList(); - for (AutoScalePolicyConditionMapVO autoScalePolicyConditionMap : autoScalePolicyConditionMapList) { - Condition condition = _conditionDao.findById(autoScalePolicyConditionMap.getConditionId()); - Counter counter = _counterDao.findById(condition.getCounterid()); - lbConditions.add(new LbCondition(counter, condition)); - } - autoScalePolicies.add(new LbAutoScalePolicy(autoScalePolicy, lbConditions)); - } - AutoScaleVmProfile autoScaleVmProfile = _autoScaleVmProfileDao.findById(vmGroup.getProfileId()); - Long autoscaleUserId = autoScaleVmProfile.getAutoScaleUserId(); - User user = _userDao.findById(autoscaleUserId); - String apiKey = user.getApiKey(); - String secretKey = user.getSecretKey(); - String csUrl = _configDao.getValue(Config.EndpointeUrl.key()); - - if(apiKey == null) { - throw new InvalidParameterValueException("apiKey for user: " + user.getUsername() + " is empty. Please generate it", null); - } - - if(secretKey == null) { - throw new InvalidParameterValueException("secretKey for user: " + user.getUsername() + " is empty. Please generate it", null); - } - - if(csUrl == null || csUrl.contains("localhost")) { - throw new InvalidParameterValueException("Global setting endpointe.url has to be set to the Management Server's API end point", null); - } - - LbAutoScaleVmProfile lbAutoScaleVmProfile = new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl); - return new LbAutoScaleVmGroup(vmGroup, autoScalePolicies, lbAutoScaleVmProfile); - } - - private boolean applyAutoScaleConfig(LoadBalancerVO lb, LoadBalancingRule rule) throws ResourceUnavailableException { - if (!isRollBackAllowedForProvider(lb)) { - // this is for Netscalar type of devices. if their is failure the db entries will be rollbacked. - return false; - } - - List rules = Arrays.asList(rule); - - if (!_networkMgr.applyRules(rules, false)) { - s_logger.debug("LB rules' autoscale config are not completely applied"); - return false; - } - - return true; - } - - @Override - @DB - public boolean configureLbAutoScaleVmGroup(long vmGroupid) { - AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.findById(vmGroupid); - boolean success = false; - - LoadBalancerVO loadBalancer = _lbDao.findById(vmGroup.getLoadBalancerId()); - - FirewallRule.State backupState = loadBalancer.getState(); - - if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - } - else if (loadBalancer.getState() == FirewallRule.State.Active && - vmGroup.getState().equals(AutoScaleVmGroup.State_Revoke)) { - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - } - - // LBTODO - try { - LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup); - LoadBalancingRule rule = new LoadBalancingRule(loadBalancer, null, null); - rule.setAutoScaleVmGroup(lbAutoScaleVmGroup); - success = applyAutoScaleConfig(loadBalancer, rule); - } catch (ResourceUnavailableException e) { - s_logger.warn("Unable to configure AutoScaleVmGroup to the lb rule: " + loadBalancer.getId() + " because resource is unavaliable:", e); - } finally { - if (!success) { - s_logger.warn("Failed to configure LB Auto Scale Vm Group with Id:" + vmGroupid); - if (isRollBackAllowedForProvider(loadBalancer)) { - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating AutoscaleVmGroup"); - } - } - } - - if (success) { - if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { - Transaction.currentTxn().start(); - loadBalancer.setState(FirewallRule.State.Active); - s_logger.debug("LB rule " + loadBalancer.getId() + " state is set to Active"); - _lbDao.persist(loadBalancer); - vmGroup.setState(AutoScaleVmGroup.State_Enabled); - _autoScaleVmGroupDao.persist(vmGroup); - s_logger.debug("LB Auto Scale Vm Group with Id: " + vmGroupid + " is set to Enabled state."); - Transaction.currentTxn().commit(); - } - s_logger.info("Successfully configured LB Autoscale Vm Group with Id: " + vmGroupid); - } - return success; - } - - private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException { - LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); - /* Validation : check for valid Method name and params */ - List stickinessMethodList = getStickinessMethods(loadBalancer - .getNetworkId()); - boolean methodMatch = false; - - if (stickinessMethodList == null) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed: No Stickiness method available for LB rule with specified id", idList); - } - for (LbStickinessMethod method : stickinessMethodList) { - if (method.getMethodName().equalsIgnoreCase(cmd.getStickinessMethodName())) { - methodMatch = true; - Map apiParamList = cmd.getparamList(); - List methodParamList = method.getParamList(); - Map tempParamList = new HashMap(); - - /* - * validation-1: check for any extra params that are not - * required by the policymethod(capability), FIXME: make the - * below loop simple without using raw data type - */ - if (apiParamList != null) { - Collection userGroupCollection = apiParamList.values(); - Iterator iter = userGroupCollection.iterator(); - while (iter.hasNext()) { - HashMap paramKVpair = (HashMap) iter.next(); - String paramName = paramKVpair.get("name"); - String paramValue = paramKVpair.get("value"); - - tempParamList.put(paramName, paramValue); - Boolean found = false; - for (LbStickinessMethodParam param : methodParamList) { - if (param.getParamName().equalsIgnoreCase(paramName)) { - if ((param.getIsflag() == false) && (paramValue == null)) { - throw new InvalidParameterValueException("Failed : Value expected for the Param :" + param.getParamName(), null); - } - found = true; - break; - } - } - if (!found) { - throw new InvalidParameterValueException("Failed : Stickiness policy does not support param name :" + paramName, null); - } - } - } - - /* validation-2: check for mandatory params */ - for (LbStickinessMethodParam param : methodParamList) { - if (param.getRequired()) { - if (tempParamList.get(param.getParamName()) == null) { - throw new InvalidParameterValueException("Failed : Missing Manadatory Param :" + param.getParamName(), null); - } - } - } - /* Successfully completed the Validation */ - break; - } - } - if (methodMatch == false) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule whose id is specified", idList); - } - - /* Validation : check for the multiple policies to the rule id */ - List stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false); - if (stickinessPolicies.size() > 0) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed to create Stickiness policy: Policy already attached", idList); - } - return true; - } - - @SuppressWarnings("rawtypes") - @Override - @DB - @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "create lb stickinesspolicy to load balancer", create = true) - public StickinessPolicy createLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) throws NetworkRuleConflictException { - UserContext caller = UserContext.current(); - - /* Validation : check corresponding load balancer rule exist */ - LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); - if (loadBalancer == null) { - throw new InvalidParameterValueException("Failed: LB rule provided not present", null); - } - - _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); - if (loadBalancer.getState() == FirewallRule.State.Revoke) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed: LB rule with specified id is in deleting state: ", idList); - } - - /* Generic validations */ - if (!genericValidator(cmd)) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation of rule with specified id failed", idList); - } - - /* Specific validations using network element validator for specific validations */ - LBStickinessPolicyVO lbpolicy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); - List policyList = new ArrayList(); - policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams())); - LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList); - if (!_networkMgr.validateRule(lbRule)) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); - throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation of rule with specified id failed ", idList); - } - - /* Finally Insert into DB */ - LBStickinessPolicyVO policy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); - policy = _lb2stickinesspoliciesDao.persist(policy); - - return policy; - } - - @Override - @DB - @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "Apply Stickinesspolicy to load balancer ", async = true) - public boolean applyLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) { - boolean success = true; - - LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); - if (loadBalancer == null) { - throw new InvalidParameterException("Invalid Load balancer Id:" + cmd.getLbRuleId()); - } - FirewallRule.State backupState = loadBalancer.getState(); - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - try { - applyLoadBalancerConfig(cmd.getLbRuleId()); - } catch (ResourceUnavailableException e) { - s_logger.warn("Unable to apply Stickiness policy to the lb rule: " + cmd.getLbRuleId() + " because resource is unavaliable:", e); - if (isRollBackAllowedForProvider(loadBalancer)) { - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating sticky policy"); - } - deleteLBStickinessPolicy(cmd.getEntityId(), false); - success = false; - } - - return success; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_DELETE, eventDescription = "revoking LB Stickiness policy ", async = true) - public boolean deleteLBStickinessPolicy(long stickinessPolicyId, boolean apply) { - boolean success = true; - - UserContext caller = UserContext.current(); - LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId); - - if (stickinessPolicy == null) { - throw new InvalidParameterException("Invalid Stickiness policy id value: " + stickinessPolicyId); - } - LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(stickinessPolicy.getLoadBalancerId())); - if (loadBalancer == null) { - throw new InvalidParameterException("Invalid Load balancer : " + stickinessPolicy.getLoadBalancerId() + " for Stickiness policy id: " + stickinessPolicyId); - } - long loadBalancerId = loadBalancer.getId(); - FirewallRule.State backupState = loadBalancer.getState(); - _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); - - if (apply) { - if (loadBalancer.getState() == FirewallRule.State.Active) { - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - } - - boolean backupStickyState = stickinessPolicy.isRevoke(); - stickinessPolicy.setRevoke(true); - _lb2stickinesspoliciesDao.persist(stickinessPolicy); - s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", stickinesspolicyID " + stickinessPolicyId); - - try { - if (!applyLoadBalancerConfig(loadBalancerId)) { - s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); - throw new CloudRuntimeException("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); - } - } catch (ResourceUnavailableException e) { - if (isRollBackAllowedForProvider(loadBalancer)) { - stickinessPolicy.setRevoke(backupStickyState); - _lb2stickinesspoliciesDao.persist(stickinessPolicy); - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while deleting sticky policy: " + stickinessPolicyId); - } - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - success = false; - } - } else { - _lb2stickinesspoliciesDao.remove(stickinessPolicy.getLoadBalancerId()); - } - - return success; - } - - private boolean isRollBackAllowedForProvider(LoadBalancerVO loadBalancer) { - Network network = _networkDao.findById(loadBalancer.getNetworkId()); - Provider provider = Network.Provider.Netscaler; - return _ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), Service.Lb, provider); - } - - @Override - @DB - @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true) - public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds) { - UserContext ctx = UserContext.current(); - Account caller = ctx.getCaller(); - - LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); - if (loadBalancer == null) { - throw new InvalidParameterValueException("Failed to assign to load balancer; the load balancer was not found.", null); - } - - List mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false); - Set mappedInstanceIds = new HashSet(); - for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { - mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId())); - } - - List vmsToAdd = new ArrayList(); - - for (Long instanceId : instanceIds) { - if (mappedInstanceIds.contains(instanceId)) { - List idList = new ArrayList(); - idList.add(new IdentityProxy("user_vm", instanceId, "vmId")); - throw new InvalidParameterValueException("VM with specified id is already mapped to load balancer", idList); - } - - UserVm vm = _vmDao.findById(instanceId); - if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { - throw new InvalidParameterValueException("Couldn't locate vm instance by id", null); - } - - _rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller); - - if (vm.getAccountId() != loadBalancer.getAccountId()) { - throw new PermissionDeniedException("Cannot add virtual machines that do not belong to the same owner."); - } - - // Let's check to make sure the vm has a nic in the same network as the load balancing rule. - List nics = _networkMgr.getNics(vm.getId()); - Nic nicInSameNetwork = null; - for (Nic nic : nics) { - if (nic.getNetworkId() == loadBalancer.getNetworkId()) { - nicInSameNetwork = nic; - break; - } - } - - if (nicInSameNetwork == null) { - List idList = new ArrayList(); - idList.add(new IdentityProxy(vm, instanceId, "vmId")); - throw new InvalidParameterValueException("VM with specified id cannot be added because it doesn't belong in the same network.", idList); - - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Adding " + vm + " to the load balancer pool"); - } - vmsToAdd.add(vm); - } - - Transaction txn = Transaction.currentTxn(); - txn.start(); - for (UserVm vm : vmsToAdd) { - LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vm.getId(), false); - map = _lb2VmMapDao.persist(map); - } - txn.commit(); - - if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { - // Nothing needs to be done for an autoscaled loadbalancer, - // just persist and proceed. - return true; - } - - boolean success = false; - FirewallRule.State backupState = loadBalancer.getState(); - try { - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - applyLoadBalancerConfig(loadBalancerId); - success = true; - } catch (ResourceUnavailableException e) { - if (isRollBackAllowedForProvider(loadBalancer)) { - List vmInstanceIds = new ArrayList(); - txn = Transaction.currentTxn(); - txn.start(); - for (UserVm vm : vmsToAdd) { - vmInstanceIds.add(vm.getId()); - } - txn.commit(); - if (!vmInstanceIds.isEmpty()) { - _lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null); - s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while attaching VM: " + vmInstanceIds); - } - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - } - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - } - - if (!success) { - CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); - // TBD: Also pack in the instanceIds in the exception using the right VO object or table name. - throw ex; - } - - return success; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, eventDescription = "removing from load balancer", async = true) - public boolean removeFromLoadBalancer(long loadBalancerId, List instanceIds) { - return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true); - } - - private boolean removeFromLoadBalancerInternal(long loadBalancerId, List instanceIds, boolean rollBack) { - UserContext caller = UserContext.current(); - - LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId)); - if (loadBalancer == null) { - throw new InvalidParameterException("Invalid load balancer value: " + loadBalancerId); - } - - _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); - - boolean success = false; - FirewallRule.State backupState = loadBalancer.getState(); - try { - loadBalancer.setState(FirewallRule.State.Add); - _lbDao.persist(loadBalancer); - - for (long instanceId : instanceIds) { - LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); - map.setRevoke(true); - _lb2VmMapDao.persist(map); - s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + instanceId); - } - - if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { - // Nothing needs to be done for an autoscaled loadbalancer, - // just persist and proceed. - return true; - } - - if (!applyLoadBalancerConfig(loadBalancerId)) { - s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds); - CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); - throw ex; - } - success = true; - } catch (ResourceUnavailableException e) { - if (rollBack && isRollBackAllowedForProvider(loadBalancer)) { - - for (long instanceId : instanceIds) { - LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); - map.setRevoke(false); - _lb2VmMapDao.persist(map); - s_logger.debug("LB Rollback rule id: " + loadBalancerId + ",while removing vmId " + instanceId); - } - - loadBalancer.setState(backupState); - _lbDao.persist(loadBalancer); - s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while removing vm instances"); - } - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - } - if (!success) { - CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); - ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); - throw ex; - } - return success; - } - - @Override - public boolean removeVmFromLoadBalancers(long instanceId) { - boolean success = true; - List maps = _lb2VmMapDao.listByInstanceId(instanceId); - if (maps == null || maps.isEmpty()) { - return true; - } - - Map> lbsToReconfigure = new HashMap>(); - - // first set all existing lb mappings with Revoke state - for (LoadBalancerVMMapVO map : maps) { - long lbId = map.getLoadBalancerId(); - List instances = lbsToReconfigure.get(lbId); - if (instances == null) { - instances = new ArrayList(); - } - instances.add(map.getInstanceId()); - lbsToReconfigure.put(lbId, instances); - - map.setRevoke(true); - _lb2VmMapDao.persist(map); - s_logger.debug("Set load balancer rule for revoke: rule id " + map.getLoadBalancerId() + ", vmId " + instanceId); - } - - // Reapply all lbs that had the vm assigned - if (lbsToReconfigure != null) { - for (Map.Entry> lb : lbsToReconfigure.entrySet()) { - if (!removeFromLoadBalancerInternal(lb.getKey(), lb.getValue(), false)) { - success = false; - } - } - } - return success; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_DELETE, eventDescription = "deleting load balancer", async = true) - public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply) { - UserContext ctx = UserContext.current(); - Account caller = ctx.getCaller(); - - LoadBalancerVO rule = _lbDao.findById(loadBalancerId); - if (rule == null) { - throw new InvalidParameterValueException("Unable to find load balancer rule by id", null); - } - - _accountMgr.checkAccess(caller, null, true, rule); - - boolean result = deleteLoadBalancerRule(loadBalancerId, apply, caller, ctx.getCallerUserId(), true); - if (!result) { - throw new CloudRuntimeException("Unable to remove load balancer rule " + loadBalancerId); - } - return result; - } - - @DB - public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply, Account caller, long callerUserId, boolean rollBack) { - LoadBalancerVO lb = _lbDao.findById(loadBalancerId); - Transaction txn = Transaction.currentTxn(); - boolean generateUsageEvent = false; - boolean success = true; - FirewallRule.State backupState = lb.getState(); - - txn.start(); - if (lb.getState() == FirewallRule.State.Staged) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found a rule that is still in stage state so just removing it: " + lb); - } - generateUsageEvent = true; - } else if (lb.getState() == FirewallRule.State.Add || lb.getState() == FirewallRule.State.Active) { - lb.setState(FirewallRule.State.Revoke); - _lbDao.persist(lb); - generateUsageEvent = true; - } - List backupMaps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); - List maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); - if (maps != null) { - for (LoadBalancerVMMapVO map : maps) { - map.setRevoke(true); - _lb2VmMapDao.persist(map); - s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + map.getInstanceId()); - } - } - - if (generateUsageEvent) { - // Generate usage event right after all rules were marked for revoke - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null); - _usageEventDao.persist(usageEvent); - } - - txn.commit(); - - // gather external network usage stats for this lb rule - NetworkVO network = _networkDao.findById(lb.getNetworkId()); - if (network != null) { - if (_networkMgr.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) { - _externalLBUsageMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId); - } - } - - if (apply) { - try { - if (!applyLoadBalancerConfig(loadBalancerId)) { - s_logger.warn("Unable to apply the load balancer config"); - return false; - } - } catch (ResourceUnavailableException e) { - if (rollBack && isRollBackAllowedForProvider(lb)) { - if (backupMaps != null) { - for (LoadBalancerVMMapVO map : backupMaps) { - _lb2VmMapDao.persist(map); - s_logger.debug("LB Rollback rule id: " + loadBalancerId + ", vmId " + map.getInstanceId()); - } - } - lb.setState(backupState); - _lbDao.persist(lb); - s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while deleting LB rule."); - } else { - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - } - return false; - } - } - - FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId()); - if (relatedRule != null) { - s_logger.warn("Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); - success = false; - } else { - _firewallMgr.removeRule(lb); - } - - _elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller); - - if (success) { - s_logger.debug("Load balancer with id " + lb.getId() + " is removed successfully"); - } - - return success; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer") - public LoadBalancer createLoadBalancerRule(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException, InsufficientAddressCapacityException { - Account lbOwner = _accountMgr.getAccount(lb.getEntityOwnerId()); - - int defPortStart = lb.getDefaultPortStart(); - int defPortEnd = lb.getDefaultPortEnd(); - - if (!NetUtils.isValidPort(defPortEnd)) { - throw new InvalidParameterValueException("privatePort is an invalid value: " + defPortEnd, null); - } - if (defPortStart > defPortEnd) { - throw new InvalidParameterValueException("private port range is invalid: " + defPortStart + "-" + defPortEnd, null); - } - if ((lb.getAlgorithm() == null) || !NetUtils.isValidAlgorithm(lb.getAlgorithm())) { - throw new InvalidParameterValueException("Invalid algorithm: " + lb.getAlgorithm(), null); - } - - Long ipAddrId = lb.getSourceIpAddressId(); - IPAddressVO ipVO = null; - if (ipAddrId != null) { - ipVO = _ipAddressDao.findById(ipAddrId); - } - - Network network = _networkMgr.getNetwork(lb.getNetworkId()); - - LoadBalancer result = _elbMgr.handleCreateLoadBalancerRule(lb, lbOwner, lb.getNetworkId()); - boolean performedIpAssoc = false; - if (result == null) { - IpAddress systemIp = null; - NetworkOffering off = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); - if (off.getElasticLb() && ipVO == null && network.getVpcId() == null) { - systemIp = _networkMgr.assignSystemIp(lb.getNetworkId(), lbOwner, true, false); - lb.setSourceIpAddressId(systemIp.getId()); - ipVO = _ipAddressDao.findById(systemIp.getId()); - } - - // Validate ip address - if (ipVO == null) { - throw new InvalidParameterValueException("Unable to create load balance rule; can't find/allocate source IP", null); - } else if (ipVO.isOneToOneNat()) { - throw new NetworkRuleConflictException("Can't do load balance on ip address: " + ipVO.getAddress()); - } - - try { - if (ipVO.getAssociatedWithNetworkId() == null) { - boolean assignToVpcNtwk = network.getVpcId() != null - && ipVO.getVpcId() != null && ipVO.getVpcId().longValue() == network.getVpcId(); - if (assignToVpcNtwk) { - // set networkId just for verification purposes - _networkMgr.checkIpForService(ipVO, Service.Lb, lb.getNetworkId()); - - s_logger.debug("The ip is not associated with the VPC network id=" + lb.getNetworkId() + " so assigning"); - ipVO = _networkMgr.associateIPToGuestNetwork(ipAddrId, lb.getNetworkId(), false); - performedIpAssoc = true; - } - } else { - _networkMgr.checkIpForService(ipVO, Service.Lb, null); - } - - if (ipVO.getAssociatedWithNetworkId() == null) { - throw new InvalidParameterValueException("Ip address " + ipVO + " is not assigned to the network " + network, null); - } - - if (lb.getSourceIpAddressId() == null) { - throw new CloudRuntimeException("No ip address is defined to assign the LB to"); - } - result = createLoadBalancer(lb, openFirewall); - } catch (Exception ex) { - s_logger.warn("Failed to create load balancer due to ", ex); - if (ex instanceof NetworkRuleConflictException) { - throw (NetworkRuleConflictException) ex; - } - } finally { - if (result == null && systemIp != null) { - s_logger.debug("Releasing system IP address " + systemIp + " as corresponding lb rule failed to create"); - _networkMgr.handleSystemIpRelease(systemIp); - } - // release ip address if ipassoc was perfored - if (performedIpAssoc) { - ipVO = _ipAddressDao.findById(ipVO.getId()); - _vpcMgr.unassignIPFromVpcNetwork(ipVO.getId(), lb.getNetworkId()); - - } - } - } - - if (result == null) { - throw new CloudRuntimeException("Failed to create load balancer rule: " + lb.getName()); - } - - return result; - } - - @Override - @DB - public LoadBalancer createLoadBalancer(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException { - UserContext caller = UserContext.current(); - int srcPortStart = lb.getSourcePortStart(); - int defPortStart = lb.getDefaultPortStart(); - int srcPortEnd = lb.getSourcePortEnd(); - long sourceIpId = lb.getSourceIpAddressId(); - - IPAddressVO ipAddr = _ipAddressDao.findById(sourceIpId); - // make sure ip address exists - if (ipAddr == null || !ipAddr.readyToUse()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id specified", null); - ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); - throw ex; - } else if (ipAddr.isOneToOneNat()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule; specified sourceip id has static nat enabled", null); - ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); - throw ex; - } - - _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), - Purpose.LoadBalancing, FirewallRuleType.User); - - Long networkId = ipAddr.getAssociatedWithNetworkId(); - if (networkId == null) { - List idList = new ArrayList(); - idList.add(new IdentityProxy(ipAddr, sourceIpId, "sourceIpid")); - throw new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network", idList); - - } - NetworkVO network = _networkDao.findById(networkId); - - _accountMgr.checkAccess(caller.getCaller(), null, true, ipAddr); - - // verify that lb service is supported by the network - if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { - List idList = new ArrayList(); - idList.add(new IdentityProxy(network, networkId, "networkId")); - throw new InvalidParameterValueException("LB service is not supported in network with specified id", idList); - } - - Transaction txn = Transaction.currentTxn(); - txn.start(); - - LoadBalancerVO newRule = new LoadBalancerVO(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourceIpAddressId(), lb.getSourcePortEnd(), lb.getDefaultPortStart(), - lb.getAlgorithm(), network.getId(), ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId()); - - newRule = _lbDao.persist(newRule); - - if (openFirewall) { - _firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCaller(), lb.getSourcePortStart(), - lb.getSourcePortEnd(), lb.getProtocol(), null, null, newRule.getId(), networkId); - } - - boolean success = true; - - try { - _firewallMgr.detectRulesConflict(newRule); - if (!_firewallDao.setStateToAdd(newRule)) { - throw new CloudRuntimeException("Unable to update the state to add for " + newRule); - } - s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPortStart + ", private port " + defPortStart + " is added successfully."); - UserContext.current().setEventDetails("Load balancer Id: " + newRule.getId()); - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null); - _usageEventDao.persist(usageEvent); - txn.commit(); - - return newRule; - } catch (Exception e) { - success = false; - if (e instanceof NetworkRuleConflictException) { - throw (NetworkRuleConflictException) e; - } - throw new CloudRuntimeException("Unable to add rule for ip address id=" + newRule.getSourceIpAddressId(), e); - } finally { - if (!success && newRule != null) { - - txn.start(); - _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false); - removeLBRule(newRule); - - txn.commit(); - } - } - } - - @Override - public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException { - LoadBalancerVO lb = _lbDao.findById(lbRuleId); - List lbs; - if (isRollBackAllowedForProvider(lb)) { - // this is for Netscalar type of devices. if their is failure the db entries will be rollbacked. - lbs = Arrays.asList(lb); - } else { - // get all rules in transition state - lbs = _lbDao.listInTransitionStateByNetworkId(lb.getNetworkId()); - } - return applyLoadBalancerRules(lbs, true); - } - - @Override - public boolean applyLoadBalancersForNetwork(long networkId) throws ResourceUnavailableException { - List lbs = _lbDao.listByNetworkId(networkId); - if (lbs != null) { - return applyLoadBalancerRules(lbs, true); - } else { - s_logger.info("Network id=" + networkId + " doesn't have load balancer rules, nothing to apply"); - return true; - } - } - - @DB - protected boolean applyLoadBalancerRules(List lbs, boolean updateRulesInDB) throws ResourceUnavailableException { - Transaction txn = Transaction.currentTxn(); - List rules = new ArrayList(); - for (LoadBalancerVO lb : lbs) { - List dstList = getExistingDestinations(lb.getId()); - List policyList = getStickinessPolicies(lb.getId()); - - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); - rules.add(loadBalancing); - } - - if (!_networkMgr.applyRules(rules, false)) { - s_logger.debug("LB rules are not completely applied"); - return false; - } - - if (updateRulesInDB) { - for (LoadBalancerVO lb : lbs) { - boolean checkForReleaseElasticIp = false; - txn.start(); - if (lb.getState() == FirewallRule.State.Revoke) { - removeLBRule(lb); - s_logger.debug("LB " + lb.getId() + " is successfully removed"); - checkForReleaseElasticIp = true; - } else if (lb.getState() == FirewallRule.State.Add) { - lb.setState(FirewallRule.State.Active); - s_logger.debug("LB rule " + lb.getId() + " state is set to Active"); - _lbDao.persist(lb); - } - - // remove LB-Vm mappings that were state to revoke - List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId(), true); - List instanceIds = new ArrayList(); - - for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { - instanceIds.add(lbVmMap.getInstanceId()); - } - - if (!instanceIds.isEmpty()) { - _lb2VmMapDao.remove(lb.getId(), instanceIds, null); - s_logger.debug("Load balancer rule id " + lb.getId() + " is removed for vms " + instanceIds); - } - - if (_lb2VmMapDao.listByLoadBalancerId(lb.getId()).isEmpty()) { - lb.setState(FirewallRule.State.Add); - _lbDao.persist(lb); - s_logger.debug("LB rule " + lb.getId() + " state is set to Add as there are no more active LB-VM mappings"); - } - - // remove LB-Stickiness policy mapping that were state to revoke - List stickinesspolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lb.getId(), true); - if (!stickinesspolicies.isEmpty()) { - _lb2stickinesspoliciesDao.remove(lb.getId(), true); - s_logger.debug("Load balancer rule id " + lb.getId() + " is removed stickiness policies"); - } - - txn.commit(); - - if (checkForReleaseElasticIp) { - boolean success = true; - long count = _firewallDao.countRulesByIpId(lb.getSourceIpAddressId()); - if (count == 0) { - try { - success = handleSystemLBIpRelease(lb); - } catch (Exception ex) { - s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion due to exception ", ex); - success = false; - } finally { - if (!success) { - s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion"); - } - } - } - } - } - } - - return true; - } - - protected boolean handleSystemLBIpRelease(LoadBalancerVO lb) { - IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId()); - boolean success = true; - if (ip.getSystem()) { - s_logger.debug("Releasing system ip address " + lb.getSourceIpAddressId() + " as a part of delete lb rule"); - if (!_networkMgr.disassociatePublicIpAddress(lb.getSourceIpAddressId(), UserContext.current().getCallerUserId(), UserContext.current().getCaller())) { - s_logger.warn("Unable to release system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); - success = false; - } else { - s_logger.warn("Successfully released system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); - } - } - - return success; - } - - @Override - public boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId) { - List rules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.LoadBalancing); - if (rules != null) { - s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); - } - for (FirewallRule rule : rules) { - boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); - if (result == false) { - s_logger.warn("Unable to remove load balancer rule " + rule.getId()); - return false; - } - } - return true; - } - - @Override - public boolean removeAllLoadBalanacersForNetwork(long networkId, Account caller, long callerUserId) { - List rules = _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.LoadBalancing); - if (rules != null) { - s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); - } - for (FirewallRule rule : rules) { - boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); - if (result == false) { - s_logger.warn("Unable to remove load balancer rule " + rule.getId()); - return false; - } - } - return true; - } - - @Override - public List getStickinessPolicies(long lbId) { - List stickinessPolicies = new ArrayList(); - List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lbId); - - for (LBStickinessPolicyVO sDbPolicy : sDbpolicies) { - LbStickinessPolicy sPolicy = new LbStickinessPolicy(sDbPolicy.getMethodName(), sDbPolicy.getParams(), sDbPolicy.isRevoke()); - stickinessPolicies.add(sPolicy); - } - return stickinessPolicies; - } - - @Override - public List getExistingDestinations(long lbId) { - List dstList = new ArrayList(); - List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId); - LoadBalancerVO lb = _lbDao.findById(lbId); - - String dstIp = null; - for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { - UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); - Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); - dstIp = nic.getIp4Address(); - LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke()); - dstList.add(lbDst); - } - return dstList; - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - return true; - } - - @Override - public boolean start() { - return true; - } - - @Override - public boolean stop() { - return true; - } - - @Override - public String getName() { - return _name; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_UPDATE, eventDescription = "updating load balancer", async = true) - public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) { - Account caller = UserContext.current().getCaller(); - Long lbRuleId = cmd.getId(); - String name = cmd.getLoadBalancerName(); - String description = cmd.getDescription(); - String algorithm = cmd.getAlgorithm(); - LoadBalancerVO lb = _lbDao.findById(lbRuleId); - LoadBalancerVO lbBackup = _lbDao.findById(lbRuleId); - - if (lb == null) { - throw new InvalidParameterValueException("Unable to find lb rule by id", null); - } - - // check permissions - _accountMgr.checkAccess(caller, null, true, lb); - - if (name != null) { - lb.setName(name); - } - - if (description != null) { - lb.setDescription(description); - } - - if (algorithm != null) { - lb.setAlgorithm(algorithm); - } - - boolean success = _lbDao.update(lbRuleId, lb); - - // If algorithm is changed, have to reapply the lb config - if (algorithm != null) { - try { - lb.setState(FirewallRule.State.Add); - _lbDao.persist(lb); - applyLoadBalancerConfig(lbRuleId); - } catch (ResourceUnavailableException e) { - if (isRollBackAllowedForProvider(lb)) { - /* - * NOTE : We use lb object to update db instead of lbBackup object since db layer will fail to - * update if there is no change in the object. - */ - if (lbBackup.getName() != null) { - lb.setName(lbBackup.getName()); - } - if (lbBackup.getDescription() != null) { - lb.setDescription(lbBackup.getDescription()); - } - if (lbBackup.getAlgorithm() != null) { - lb.setAlgorithm(lbBackup.getAlgorithm()); - } - lb.setState(lbBackup.getState()); - _lbDao.update(lb.getId(), lb); - _lbDao.persist(lb); - - s_logger.debug("LB Rollback rule id: " + lbRuleId + " while updating LB rule."); - } - s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); - success = false; - } - } - - if (!success) { - throw new CloudRuntimeException("Failed to update load balancer rule: " + lbRuleId); - } - - return lb; - } - - @Override - public List listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException { - Account caller = UserContext.current().getCaller(); - Long loadBalancerId = cmd.getId(); - Boolean applied = cmd.isApplied(); - - if (applied == null) { - applied = Boolean.TRUE; - } - - LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); - if (loadBalancer == null) { - return null; - } - - _accountMgr.checkAccess(caller, null, true, loadBalancer); - - List loadBalancerInstances = new ArrayList(); - List vmLoadBalancerMappings = null; - - vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); - - List appliedInstanceIdList = new ArrayList(); - if ((vmLoadBalancerMappings != null) && !vmLoadBalancerMappings.isEmpty()) { - for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) { - appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId()); - } - } - - IPAddressVO addr = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId()); - List userVms = _vmDao.listVirtualNetworkInstancesByAcctAndZone(loadBalancer.getAccountId(), addr.getDataCenterId(), loadBalancer.getNetworkId()); - - for (UserVmVO userVm : userVms) { - // if the VM is destroyed, being expunged, in an error state, or in an unknown state, skip it - switch (userVm.getState()) { - case Destroyed: - case Expunging: - case Error: - case Unknown: - continue; - } - - boolean isApplied = appliedInstanceIdList.contains(userVm.getId()); - if ((isApplied && applied) || (!isApplied && !applied)) { - loadBalancerInstances.add(userVm); - } - } - - return loadBalancerInstances; - } - - public List getSupportedAutoScaleCounters(long networkid) - { - String capability = getLBCapability(networkid, Capability.AutoScaleCounters.getName()); - if (capability == null || capability.length() == 0) { - return null; - } - return Arrays.asList(capability.split(",")); - } - - @Override - public List getStickinessMethods(long networkid) - { - String capability = getLBCapability(networkid, Capability.SupportedStickinessMethods.getName()); - if (capability == null) { - return null; - } - Gson gson = new Gson(); - java.lang.reflect.Type listType = new TypeToken>() { - }.getType(); - List result = gson.fromJson(capability, listType); - return result; - } - - @Override - public List searchForLBStickinessPolicies(ListLBStickinessPoliciesCmd cmd) throws PermissionDeniedException { - Account caller = UserContext.current().getCaller(); - Long loadBalancerId = cmd.getLbRuleId(); - LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); - if (loadBalancer == null) { - return null; - } - - _accountMgr.checkAccess(caller, null, true, loadBalancer); - - List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId()); - - return sDbpolicies; - } - - @Override - public List searchForLoadBalancers(ListLoadBalancerRulesCmd cmd) { - Long ipId = cmd.getPublicIpId(); - Long zoneId = cmd.getZoneId(); - Long id = cmd.getId(); - String name = cmd.getLoadBalancerRuleName(); - String keyword = cmd.getKeyword(); - Long instanceId = cmd.getVirtualMachineId(); - Map tags = cmd.getTags(); - - Account caller = UserContext.current().getCaller(); - List permittedAccounts = new ArrayList(); - - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); - _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); - Long domainId = domainIdRecursiveListProject.first(); - Boolean isRecursive = domainIdRecursiveListProject.second(); - ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); - - Filter searchFilter = new Filter(LoadBalancerVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); - SearchBuilder sb = _lbDao.createSearchBuilder(); - _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - - sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); - sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); - sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ); - - if (instanceId != null) { - SearchBuilder lbVMSearch = _lb2VmMapDao.createSearchBuilder(); - lbVMSearch.and("instanceId", lbVMSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - sb.join("lbVMSearch", lbVMSearch, sb.entity().getId(), lbVMSearch.entity().getLoadBalancerId(), JoinBuilder.JoinType.INNER); - } - - if (zoneId != null) { - SearchBuilder ipSearch = _ipAddressDao.createSearchBuilder(); - ipSearch.and("zoneId", ipSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - sb.join("ipSearch", ipSearch, sb.entity().getSourceIpAddressId(), ipSearch.entity().getId(), JoinBuilder.JoinType.INNER); - } - - if (tags != null && !tags.isEmpty()) { - SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); - for (int count = 0; count < tags.size(); count++) { - tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ); - tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ); - tagSearch.cp(); - } - tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); - sb.groupBy(sb.entity().getId()); - sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); - } - - SearchCriteria sc = sb.create(); - _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - - if (keyword != null) { - SearchCriteria ssc = _lbDao.createSearchCriteria(); - ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - sc.addAnd("name", SearchCriteria.Op.SC, ssc); - } - - if (name != null) { - sc.setParameters("name", "%" + name + "%"); - } - - if (id != null) { - sc.setParameters("id", id); - } - - if (ipId != null) { - sc.setParameters("sourceIpAddress", ipId); - } - - if (instanceId != null) { - sc.setJoinParameters("lbVMSearch", "instanceId", instanceId); - } - - if (zoneId != null) { - sc.setJoinParameters("ipSearch", "zoneId", zoneId); - } - - if (tags != null && !tags.isEmpty()) { - int count = 0; - sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.LoadBalancer.toString()); - for (String key : tags.keySet()) { - sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); - sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); - count++; - } - } - - return _lbDao.search(sc, searchFilter); - } - - @Override - public List listByNetworkId(long networkId) { - List lbs = _lbDao.listByNetworkId(networkId); - List lbRules = new ArrayList(); - for (LoadBalancerVO lb : lbs) { - List dstList = getExistingDestinations(lb.getId()); - List policyList = this.getStickinessPolicies(lb.getId()); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); - lbRules.add(loadBalancing); - } - return lbRules; - } - - @Override - public LoadBalancerVO findById(long lbId) { - return _lbDao.findById(lbId); - } - - protected void removeLBRule(LoadBalancerVO rule) { - - //remove the rule - _lbDao.remove(rule.getId()); - - // if the rule is the last one for the ip address assigned to VPC, unassign it from the network - IpAddress ip = _ipAddressDao.findById(rule.getSourceIpAddressId()); - _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), rule.getNetworkId()); - - } -} +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by 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. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.network.lb; + +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.api.commands.CreateLBStickinessPolicyCmd; +import com.cloud.api.commands.CreateLoadBalancerRuleCmd; +import com.cloud.api.commands.ListLBStickinessPoliciesCmd; +import com.cloud.api.commands.ListLoadBalancerRuleInstancesCmd; +import com.cloud.api.commands.ListLoadBalancerRulesCmd; +import com.cloud.api.commands.UpdateLoadBalancerRuleCmd; +import com.cloud.api.response.ServiceResponse; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventVO; +import com.cloud.event.dao.EventDao; +import com.cloud.event.dao.UsageEventDao; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.ExternalLoadBalancerUsageManager; +import com.cloud.network.IPAddressVO; +import com.cloud.network.IpAddress; +import com.cloud.network.LBStickinessPolicyVO; +import com.cloud.network.LoadBalancerVMMapVO; +import com.cloud.network.LoadBalancerVO; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; +import com.cloud.network.as.AutoScalePolicy; +import com.cloud.network.as.AutoScalePolicyConditionMapVO; +import com.cloud.network.as.AutoScaleVmGroup; +import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; +import com.cloud.network.as.AutoScaleVmGroupVO; +import com.cloud.network.as.AutoScaleVmProfile; +import com.cloud.network.as.Condition; +import com.cloud.network.as.Counter; +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.AutoScaleVmProfileDao; +import com.cloud.network.as.dao.ConditionDao; +import com.cloud.network.as.dao.CounterDao; +import com.cloud.network.dao.FirewallRulesCidrsDao; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.LBStickinessPolicyDao; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy; +import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup; +import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile; +import com.cloud.network.lb.LoadBalancingRule.LbCondition; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.rules.FirewallManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.FirewallRuleType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.LbStickinessMethod; +import com.cloud.network.rules.LbStickinessMethod.LbStickinessMethodParam; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.network.rules.RulesManager; +import com.cloud.network.rules.StickinessPolicy; +import com.cloud.network.vpc.VpcManager; +import com.cloud.offering.NetworkOffering; +import com.cloud.projects.Project.ListProjectResourcesCriteria; +import com.cloud.server.ResourceTag.TaggedResourceType; +import com.cloud.tags.ResourceTagVO; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.template.TemplateManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainService; +import com.cloud.user.User; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.IdentityProxy; +import com.cloud.utils.Ternary; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.Nic; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +@Local(value = { LoadBalancingRulesManager.class, LoadBalancingRulesService.class }) +public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, LoadBalancingRulesService, Manager { + private static final Logger s_logger = Logger.getLogger(LoadBalancingRulesManagerImpl.class); + + String _name; + + @Inject + NetworkManager _networkMgr; + @Inject + RulesManager _rulesMgr; + @Inject + AccountManager _accountMgr; + @Inject + IPAddressDao _ipAddressDao; + @Inject + LoadBalancerDao _lbDao; + @Inject + VlanDao _vlanDao; + @Inject + EventDao _eventDao; + @Inject + LoadBalancerVMMapDao _lb2VmMapDao; + @Inject + LBStickinessPolicyDao _lb2stickinesspoliciesDao; + @Inject + UserVmDao _vmDao; + @Inject + AccountDao _accountDao; + @Inject + DomainDao _domainDao; + @Inject + NicDao _nicDao; + @Inject + UsageEventDao _usageEventDao; + @Inject + FirewallRulesCidrsDao _firewallCidrsDao; + @Inject + FirewallManager _firewallMgr; + @Inject + ElasticLoadBalancerManager _elbMgr; + @Inject + NetworkDao _networkDao; + @Inject + FirewallRulesDao _firewallDao; + @Inject + DomainService _domainMgr; + @Inject + ConfigurationManager _configMgr; + @Inject + TemplateManager _templateMgr; + @Inject + ExternalLoadBalancerUsageManager _externalLBUsageMgr; + @Inject + NetworkServiceMapDao _ntwkSrvcDao; + @Inject + ResourceTagDao _resourceTagDao; + @Inject + CounterDao _counterDao; + @Inject + ConditionDao _conditionDao; + @Inject + AutoScaleVmProfileDao _autoScaleVmProfileDao; + @Inject + AutoScalePolicyDao _autoScalePolicyDao; + @Inject + AutoScalePolicyConditionMapDao _autoScalePolicyConditionMapDao; + @Inject + AutoScaleVmGroupDao _autoScaleVmGroupDao; + @Inject + AutoScaleVmGroupPolicyMapDao _autoScaleVmGroupPolicyMapDao; + @Inject + ConfigurationDao _configDao; + @Inject + DataCenterDao _dcDao = null; + @Inject + UserDao _userDao; + @Inject + VpcManager _vpcMgr; + + // Will return a string. For LB Stickiness this will be a json, for autoscale this will be "," separated values + @Override + public String getLBCapability(long networkid, String capabilityName) { + Map> serviceCapabilitiesMap = _networkMgr.getNetworkCapabilities(networkid); + if (serviceCapabilitiesMap != null) { + for (Service service : serviceCapabilitiesMap.keySet()) { + ServiceResponse serviceResponse = new ServiceResponse(); + serviceResponse.setName(service.getName()); + if ("Lb".equalsIgnoreCase(service.getName())) { + Map serviceCapabilities = serviceCapabilitiesMap + .get(service); + if (serviceCapabilities != null) { + for (Capability capability : serviceCapabilities + .keySet()) { + if (capabilityName.equals(capability.getName())) { + return serviceCapabilities.get(capability); + } + } + } + } + } + } + return null; + } + + private LbAutoScaleVmGroup getLbAutoScaleVmGroup(AutoScaleVmGroup vmGroup) { + List vmGroupPolicyMapList = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(vmGroup.getId()); + List autoScalePolicies = new ArrayList(); + for (AutoScaleVmGroupPolicyMapVO vmGroupPolicyMap : vmGroupPolicyMapList) { + AutoScalePolicy autoScalePolicy = _autoScalePolicyDao.findById(vmGroupPolicyMap.getPolicyId()); + List autoScalePolicyConditionMapList = _autoScalePolicyConditionMapDao.listByAll(autoScalePolicy.getId(), null); + List lbConditions = new ArrayList(); + for (AutoScalePolicyConditionMapVO autoScalePolicyConditionMap : autoScalePolicyConditionMapList) { + Condition condition = _conditionDao.findById(autoScalePolicyConditionMap.getConditionId()); + Counter counter = _counterDao.findById(condition.getCounterid()); + lbConditions.add(new LbCondition(counter, condition)); + } + autoScalePolicies.add(new LbAutoScalePolicy(autoScalePolicy, lbConditions)); + } + AutoScaleVmProfile autoScaleVmProfile = _autoScaleVmProfileDao.findById(vmGroup.getProfileId()); + Long autoscaleUserId = autoScaleVmProfile.getAutoScaleUserId(); + User user = _userDao.findById(autoscaleUserId); + String apiKey = user.getApiKey(); + String secretKey = user.getSecretKey(); + String csUrl = _configDao.getValue(Config.EndpointeUrl.key()); + + if(apiKey == null) { + throw new InvalidParameterValueException("apiKey for user: " + user.getUsername() + " is empty. Please generate it", null); + } + + if(secretKey == null) { + throw new InvalidParameterValueException("secretKey for user: " + user.getUsername() + " is empty. Please generate it", null); + } + + if(csUrl == null || csUrl.contains("localhost")) { + throw new InvalidParameterValueException("Global setting endpointe.url has to be set to the Management Server's API end point", null); + } + + LbAutoScaleVmProfile lbAutoScaleVmProfile = new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl); + return new LbAutoScaleVmGroup(vmGroup, autoScalePolicies, lbAutoScaleVmProfile); + } + + private boolean applyAutoScaleConfig(LoadBalancerVO lb, LoadBalancingRule rule) throws ResourceUnavailableException { + if (!isRollBackAllowedForProvider(lb)) { + // this is for Netscalar type of devices. if their is failure the db entries will be rollbacked. + return false; + } + + List rules = Arrays.asList(rule); + + if (!_networkMgr.applyRules(rules, false)) { + s_logger.debug("LB rules' autoscale config are not completely applied"); + return false; + } + + return true; + } + + @Override + @DB + public boolean configureLbAutoScaleVmGroup(long vmGroupid) { + AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.findById(vmGroupid); + boolean success = false; + + LoadBalancerVO loadBalancer = _lbDao.findById(vmGroup.getLoadBalancerId()); + + FirewallRule.State backupState = loadBalancer.getState(); + + if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + } + else if (loadBalancer.getState() == FirewallRule.State.Active && + vmGroup.getState().equals(AutoScaleVmGroup.State_Revoke)) { + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + } + + // LBTODO + try { + LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup); + LoadBalancingRule rule = new LoadBalancingRule(loadBalancer, null, null); + rule.setAutoScaleVmGroup(lbAutoScaleVmGroup); + success = applyAutoScaleConfig(loadBalancer, rule); + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to configure AutoScaleVmGroup to the lb rule: " + loadBalancer.getId() + " because resource is unavaliable:", e); + } finally { + if (!success) { + s_logger.warn("Failed to configure LB Auto Scale Vm Group with Id:" + vmGroupid); + if (isRollBackAllowedForProvider(loadBalancer)) { + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating AutoscaleVmGroup"); + } + } + } + + if (success) { + if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { + Transaction.currentTxn().start(); + loadBalancer.setState(FirewallRule.State.Active); + s_logger.debug("LB rule " + loadBalancer.getId() + " state is set to Active"); + _lbDao.persist(loadBalancer); + vmGroup.setState(AutoScaleVmGroup.State_Enabled); + _autoScaleVmGroupDao.persist(vmGroup); + s_logger.debug("LB Auto Scale Vm Group with Id: " + vmGroupid + " is set to Enabled state."); + Transaction.currentTxn().commit(); + } + s_logger.info("Successfully configured LB Autoscale Vm Group with Id: " + vmGroupid); + } + return success; + } + + private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException { + LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); + /* Validation : check for valid Method name and params */ + List stickinessMethodList = getStickinessMethods(loadBalancer + .getNetworkId()); + boolean methodMatch = false; + + if (stickinessMethodList == null) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed: No Stickiness method available for LB rule with specified id", idList); + } + for (LbStickinessMethod method : stickinessMethodList) { + if (method.getMethodName().equalsIgnoreCase(cmd.getStickinessMethodName())) { + methodMatch = true; + Map apiParamList = cmd.getparamList(); + List methodParamList = method.getParamList(); + Map tempParamList = new HashMap(); + + /* + * validation-1: check for any extra params that are not + * required by the policymethod(capability), FIXME: make the + * below loop simple without using raw data type + */ + if (apiParamList != null) { + Collection userGroupCollection = apiParamList.values(); + Iterator iter = userGroupCollection.iterator(); + while (iter.hasNext()) { + HashMap paramKVpair = (HashMap) iter.next(); + String paramName = paramKVpair.get("name"); + String paramValue = paramKVpair.get("value"); + + tempParamList.put(paramName, paramValue); + Boolean found = false; + for (LbStickinessMethodParam param : methodParamList) { + if (param.getParamName().equalsIgnoreCase(paramName)) { + if ((param.getIsflag() == false) && (paramValue == null)) { + throw new InvalidParameterValueException("Failed : Value expected for the Param :" + param.getParamName(), null); + } + found = true; + break; + } + } + if (!found) { + throw new InvalidParameterValueException("Failed : Stickiness policy does not support param name :" + paramName, null); + } + } + } + + /* validation-2: check for mandatory params */ + for (LbStickinessMethodParam param : methodParamList) { + if (param.getRequired()) { + if (tempParamList.get(param.getParamName()) == null) { + throw new InvalidParameterValueException("Failed : Missing Manadatory Param :" + param.getParamName(), null); + } + } + } + /* Successfully completed the Validation */ + break; + } + } + if (methodMatch == false) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule whose id is specified", idList); + } + + /* Validation : check for the multiple policies to the rule id */ + List stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false); + if (stickinessPolicies.size() > 0) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed to create Stickiness policy: Policy already attached", idList); + } + return true; + } + + @SuppressWarnings("rawtypes") + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "create lb stickinesspolicy to load balancer", create = true) + public StickinessPolicy createLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) throws NetworkRuleConflictException { + UserContext caller = UserContext.current(); + + /* Validation : check corresponding load balancer rule exist */ + LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); + if (loadBalancer == null) { + throw new InvalidParameterValueException("Failed: LB rule provided not present", null); + } + + _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); + if (loadBalancer.getState() == FirewallRule.State.Revoke) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed: LB rule with specified id is in deleting state: ", idList); + } + + /* Generic validations */ + if (!genericValidator(cmd)) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation of rule with specified id failed", idList); + } + + /* Specific validations using network element validator for specific validations */ + LBStickinessPolicyVO lbpolicy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); + List policyList = new ArrayList(); + policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams())); + LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList); + if (!_networkMgr.validateRule(lbRule)) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("firewall_rules", cmd.getLbRuleId(), "ruleId")); + throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation of rule with specified id failed ", idList); + } + + /* Finally Insert into DB */ + LBStickinessPolicyVO policy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); + policy = _lb2stickinesspoliciesDao.persist(policy); + + return policy; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "Apply Stickinesspolicy to load balancer ", async = true) + public boolean applyLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) { + boolean success = true; + + LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid Load balancer Id:" + cmd.getLbRuleId()); + } + FirewallRule.State backupState = loadBalancer.getState(); + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + try { + applyLoadBalancerConfig(cmd.getLbRuleId()); + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to apply Stickiness policy to the lb rule: " + cmd.getLbRuleId() + " because resource is unavaliable:", e); + if (isRollBackAllowedForProvider(loadBalancer)) { + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating sticky policy"); + } + deleteLBStickinessPolicy(cmd.getEntityId(), false); + success = false; + } + + return success; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_DELETE, eventDescription = "revoking LB Stickiness policy ", async = true) + public boolean deleteLBStickinessPolicy(long stickinessPolicyId, boolean apply) { + boolean success = true; + + UserContext caller = UserContext.current(); + LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId); + + if (stickinessPolicy == null) { + throw new InvalidParameterException("Invalid Stickiness policy id value: " + stickinessPolicyId); + } + LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(stickinessPolicy.getLoadBalancerId())); + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid Load balancer : " + stickinessPolicy.getLoadBalancerId() + " for Stickiness policy id: " + stickinessPolicyId); + } + long loadBalancerId = loadBalancer.getId(); + FirewallRule.State backupState = loadBalancer.getState(); + _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); + + if (apply) { + if (loadBalancer.getState() == FirewallRule.State.Active) { + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + } + + boolean backupStickyState = stickinessPolicy.isRevoke(); + stickinessPolicy.setRevoke(true); + _lb2stickinesspoliciesDao.persist(stickinessPolicy); + s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", stickinesspolicyID " + stickinessPolicyId); + + try { + if (!applyLoadBalancerConfig(loadBalancerId)) { + s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); + throw new CloudRuntimeException("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); + } + } catch (ResourceUnavailableException e) { + if (isRollBackAllowedForProvider(loadBalancer)) { + stickinessPolicy.setRevoke(backupStickyState); + _lb2stickinesspoliciesDao.persist(stickinessPolicy); + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while deleting sticky policy: " + stickinessPolicyId); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + success = false; + } + } else { + _lb2stickinesspoliciesDao.remove(stickinessPolicy.getLoadBalancerId()); + } + + return success; + } + + private boolean isRollBackAllowedForProvider(LoadBalancerVO loadBalancer) { + Network network = _networkDao.findById(loadBalancer.getNetworkId()); + Provider provider = Network.Provider.Netscaler; + return _ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), Service.Lb, provider); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true) + public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds) { + UserContext ctx = UserContext.current(); + Account caller = ctx.getCaller(); + + LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); + if (loadBalancer == null) { + throw new InvalidParameterValueException("Failed to assign to load balancer; the load balancer was not found.", null); + } + + List mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false); + Set mappedInstanceIds = new HashSet(); + for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { + mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId())); + } + + List vmsToAdd = new ArrayList(); + + for (Long instanceId : instanceIds) { + if (mappedInstanceIds.contains(instanceId)) { + List idList = new ArrayList(); + idList.add(new IdentityProxy("user_vm", instanceId, "vmId")); + throw new InvalidParameterValueException("VM with specified id is already mapped to load balancer", idList); + } + + UserVm vm = _vmDao.findById(instanceId); + if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { + throw new InvalidParameterValueException("Couldn't locate vm instance by id", null); + } + + _rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller); + + if (vm.getAccountId() != loadBalancer.getAccountId()) { + throw new PermissionDeniedException("Cannot add virtual machines that do not belong to the same owner."); + } + + // Let's check to make sure the vm has a nic in the same network as the load balancing rule. + List nics = _networkMgr.getNics(vm.getId()); + Nic nicInSameNetwork = null; + for (Nic nic : nics) { + if (nic.getNetworkId() == loadBalancer.getNetworkId()) { + nicInSameNetwork = nic; + break; + } + } + + if (nicInSameNetwork == null) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(vm, instanceId, "vmId")); + throw new InvalidParameterValueException("VM with specified id cannot be added because it doesn't belong in the same network.", idList); + + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Adding " + vm + " to the load balancer pool"); + } + vmsToAdd.add(vm); + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + for (UserVm vm : vmsToAdd) { + LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vm.getId(), false); + map = _lb2VmMapDao.persist(map); + } + txn.commit(); + + if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { + // Nothing needs to be done for an autoscaled loadbalancer, + // just persist and proceed. + return true; + } + + boolean success = false; + FirewallRule.State backupState = loadBalancer.getState(); + try { + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + applyLoadBalancerConfig(loadBalancerId); + success = true; + } catch (ResourceUnavailableException e) { + if (isRollBackAllowedForProvider(loadBalancer)) { + List vmInstanceIds = new ArrayList(); + txn = Transaction.currentTxn(); + txn.start(); + for (UserVm vm : vmsToAdd) { + vmInstanceIds.add(vm.getId()); + } + txn.commit(); + if (!vmInstanceIds.isEmpty()) { + _lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while attaching VM: " + vmInstanceIds); + } + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + } + + if (!success) { + CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " + instanceIds); + ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + // TBD: Also pack in the instanceIds in the exception using the right VO object or table name. + throw ex; + } + + return success; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, eventDescription = "removing from load balancer", async = true) + public boolean removeFromLoadBalancer(long loadBalancerId, List instanceIds) { + return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true); + } + + private boolean removeFromLoadBalancerInternal(long loadBalancerId, List instanceIds, boolean rollBack) { + UserContext caller = UserContext.current(); + + LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId)); + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid load balancer value: " + loadBalancerId); + } + + _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); + + boolean success = false; + FirewallRule.State backupState = loadBalancer.getState(); + try { + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + + for (long instanceId : instanceIds) { + LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); + map.setRevoke(true); + _lb2VmMapDao.persist(map); + s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + instanceId); + } + + if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { + // Nothing needs to be done for an autoscaled loadbalancer, + // just persist and proceed. + return true; + } + + if (!applyLoadBalancerConfig(loadBalancerId)) { + s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds); + CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); + ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + throw ex; + } + success = true; + } catch (ResourceUnavailableException e) { + if (rollBack && isRollBackAllowedForProvider(loadBalancer)) { + + for (long instanceId : instanceIds) { + LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); + map.setRevoke(false); + _lb2VmMapDao.persist(map); + s_logger.debug("LB Rollback rule id: " + loadBalancerId + ",while removing vmId " + instanceId); + } + + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while removing vm instances"); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + } + if (!success) { + CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); + ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); + throw ex; + } + return success; + } + + @Override + public boolean removeVmFromLoadBalancers(long instanceId) { + boolean success = true; + List maps = _lb2VmMapDao.listByInstanceId(instanceId); + if (maps == null || maps.isEmpty()) { + return true; + } + + Map> lbsToReconfigure = new HashMap>(); + + // first set all existing lb mappings with Revoke state + for (LoadBalancerVMMapVO map : maps) { + long lbId = map.getLoadBalancerId(); + List instances = lbsToReconfigure.get(lbId); + if (instances == null) { + instances = new ArrayList(); + } + instances.add(map.getInstanceId()); + lbsToReconfigure.put(lbId, instances); + + map.setRevoke(true); + _lb2VmMapDao.persist(map); + s_logger.debug("Set load balancer rule for revoke: rule id " + map.getLoadBalancerId() + ", vmId " + instanceId); + } + + // Reapply all lbs that had the vm assigned + if (lbsToReconfigure != null) { + for (Map.Entry> lb : lbsToReconfigure.entrySet()) { + if (!removeFromLoadBalancerInternal(lb.getKey(), lb.getValue(), false)) { + success = false; + } + } + } + return success; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_DELETE, eventDescription = "deleting load balancer", async = true) + public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply) { + UserContext ctx = UserContext.current(); + Account caller = ctx.getCaller(); + + LoadBalancerVO rule = _lbDao.findById(loadBalancerId); + if (rule == null) { + throw new InvalidParameterValueException("Unable to find load balancer rule by id", null); + } + + _accountMgr.checkAccess(caller, null, true, rule); + + boolean result = deleteLoadBalancerRule(loadBalancerId, apply, caller, ctx.getCallerUserId(), true); + if (!result) { + throw new CloudRuntimeException("Unable to remove load balancer rule " + loadBalancerId); + } + return result; + } + + @DB + public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply, Account caller, long callerUserId, boolean rollBack) { + LoadBalancerVO lb = _lbDao.findById(loadBalancerId); + Transaction txn = Transaction.currentTxn(); + boolean generateUsageEvent = false; + boolean success = true; + FirewallRule.State backupState = lb.getState(); + + txn.start(); + if (lb.getState() == FirewallRule.State.Staged) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found a rule that is still in stage state so just removing it: " + lb); + } + generateUsageEvent = true; + } else if (lb.getState() == FirewallRule.State.Add || lb.getState() == FirewallRule.State.Active) { + lb.setState(FirewallRule.State.Revoke); + _lbDao.persist(lb); + generateUsageEvent = true; + } + List backupMaps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); + List maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); + if (maps != null) { + for (LoadBalancerVMMapVO map : maps) { + map.setRevoke(true); + _lb2VmMapDao.persist(map); + s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + map.getInstanceId()); + } + } + + if (generateUsageEvent) { + // Generate usage event right after all rules were marked for revoke + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null); + _usageEventDao.persist(usageEvent); + } + + txn.commit(); + + // gather external network usage stats for this lb rule + NetworkVO network = _networkDao.findById(lb.getNetworkId()); + if (network != null) { + if (_networkMgr.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) { + _externalLBUsageMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId); + } + } + + if (apply) { + try { + if (!applyLoadBalancerConfig(loadBalancerId)) { + s_logger.warn("Unable to apply the load balancer config"); + return false; + } + } catch (ResourceUnavailableException e) { + if (rollBack && isRollBackAllowedForProvider(lb)) { + if (backupMaps != null) { + for (LoadBalancerVMMapVO map : backupMaps) { + _lb2VmMapDao.persist(map); + s_logger.debug("LB Rollback rule id: " + loadBalancerId + ", vmId " + map.getInstanceId()); + } + } + lb.setState(backupState); + _lbDao.persist(lb); + s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while deleting LB rule."); + } else { + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + } + return false; + } + } + + FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId()); + if (relatedRule != null) { + s_logger.warn("Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); + success = false; + } else { + _firewallMgr.removeRule(lb); + } + + _elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller); + + if (success) { + s_logger.debug("Load balancer with id " + lb.getId() + " is removed successfully"); + } + + return success; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer") + public LoadBalancer createLoadBalancerRule(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException, InsufficientAddressCapacityException { + Account lbOwner = _accountMgr.getAccount(lb.getEntityOwnerId()); + + int defPortStart = lb.getDefaultPortStart(); + int defPortEnd = lb.getDefaultPortEnd(); + + if (!NetUtils.isValidPort(defPortEnd)) { + throw new InvalidParameterValueException("privatePort is an invalid value: " + defPortEnd, null); + } + if (defPortStart > defPortEnd) { + throw new InvalidParameterValueException("private port range is invalid: " + defPortStart + "-" + defPortEnd, null); + } + if ((lb.getAlgorithm() == null) || !NetUtils.isValidAlgorithm(lb.getAlgorithm())) { + throw new InvalidParameterValueException("Invalid algorithm: " + lb.getAlgorithm(), null); + } + + Long ipAddrId = lb.getSourceIpAddressId(); + IPAddressVO ipVO = null; + if (ipAddrId != null) { + ipVO = _ipAddressDao.findById(ipAddrId); + } + + Network network = _networkMgr.getNetwork(lb.getNetworkId()); + + LoadBalancer result = _elbMgr.handleCreateLoadBalancerRule(lb, lbOwner, lb.getNetworkId()); + boolean performedIpAssoc = false; + if (result == null) { + IpAddress systemIp = null; + NetworkOffering off = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); + if (off.getElasticLb() && ipVO == null && network.getVpcId() == null) { + systemIp = _networkMgr.assignSystemIp(lb.getNetworkId(), lbOwner, true, false); + lb.setSourceIpAddressId(systemIp.getId()); + ipVO = _ipAddressDao.findById(systemIp.getId()); + } + + // Validate ip address + if (ipVO == null) { + throw new InvalidParameterValueException("Unable to create load balance rule; can't find/allocate source IP", null); + } else if (ipVO.isOneToOneNat()) { + throw new NetworkRuleConflictException("Can't do load balance on ip address: " + ipVO.getAddress()); + } + + try { + if (ipVO.getAssociatedWithNetworkId() == null) { + boolean assignToVpcNtwk = network.getVpcId() != null + && ipVO.getVpcId() != null && ipVO.getVpcId().longValue() == network.getVpcId(); + if (assignToVpcNtwk) { + // set networkId just for verification purposes + _networkMgr.checkIpForService(ipVO, Service.Lb, lb.getNetworkId()); + + s_logger.debug("The ip is not associated with the VPC network id=" + lb.getNetworkId() + " so assigning"); + ipVO = _networkMgr.associateIPToGuestNetwork(ipAddrId, lb.getNetworkId(), false); + performedIpAssoc = true; + } + } else { + _networkMgr.checkIpForService(ipVO, Service.Lb, null); + } + + if (ipVO.getAssociatedWithNetworkId() == null) { + throw new InvalidParameterValueException("Ip address " + ipVO + " is not assigned to the network " + network, null); + } + + if (lb.getSourceIpAddressId() == null) { + throw new CloudRuntimeException("No ip address is defined to assign the LB to"); + } + result = createLoadBalancer(lb, openFirewall); + } catch (Exception ex) { + s_logger.warn("Failed to create load balancer due to ", ex); + if (ex instanceof NetworkRuleConflictException) { + throw (NetworkRuleConflictException) ex; + } + } finally { + if (result == null && systemIp != null) { + s_logger.debug("Releasing system IP address " + systemIp + " as corresponding lb rule failed to create"); + _networkMgr.handleSystemIpRelease(systemIp); + } + // release ip address if ipassoc was perfored + if (performedIpAssoc) { + ipVO = _ipAddressDao.findById(ipVO.getId()); + _vpcMgr.unassignIPFromVpcNetwork(ipVO.getId(), lb.getNetworkId()); + + } + } + } + + if (result == null) { + throw new CloudRuntimeException("Failed to create load balancer rule: " + lb.getName()); + } + + return result; + } + + @Override + @DB + public LoadBalancer createLoadBalancer(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException { + UserContext caller = UserContext.current(); + int srcPortStart = lb.getSourcePortStart(); + int defPortStart = lb.getDefaultPortStart(); + int srcPortEnd = lb.getSourcePortEnd(); + long sourceIpId = lb.getSourceIpAddressId(); + + IPAddressVO ipAddr = _ipAddressDao.findById(sourceIpId); + // make sure ip address exists + if (ipAddr == null || !ipAddr.readyToUse()) { + throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id specified", null); + } else if (ipAddr.isOneToOneNat()) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(ipAddr, sourceIpId, "sourceIpId")); + throw new InvalidParameterValueException("Unable to create load balancer rule; specified sourceip id has static nat enabled", idList); + } + + _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), + Purpose.LoadBalancing, FirewallRuleType.User); + + Long networkId = ipAddr.getAssociatedWithNetworkId(); + if (networkId == null) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(ipAddr, sourceIpId, "sourceIpid")); + throw new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network", idList); + + } + NetworkVO network = _networkDao.findById(networkId); + + _accountMgr.checkAccess(caller.getCaller(), null, true, ipAddr); + + // verify that lb service is supported by the network + if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(network, networkId, "networkId")); + throw new InvalidParameterValueException("LB service is not supported in network with specified id", idList); + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + LoadBalancerVO newRule = new LoadBalancerVO(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourceIpAddressId(), lb.getSourcePortEnd(), lb.getDefaultPortStart(), + lb.getAlgorithm(), network.getId(), ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId()); + + newRule = _lbDao.persist(newRule); + + if (openFirewall) { + _firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCaller(), lb.getSourcePortStart(), + lb.getSourcePortEnd(), lb.getProtocol(), null, null, newRule.getId(), networkId); + } + + boolean success = true; + + try { + _firewallMgr.detectRulesConflict(newRule); + if (!_firewallDao.setStateToAdd(newRule)) { + throw new CloudRuntimeException("Unable to update the state to add for " + newRule); + } + s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPortStart + ", private port " + defPortStart + " is added successfully."); + UserContext.current().setEventDetails("Load balancer Id: " + newRule.getId()); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null); + _usageEventDao.persist(usageEvent); + txn.commit(); + + return newRule; + } catch (Exception e) { + success = false; + if (e instanceof NetworkRuleConflictException) { + throw (NetworkRuleConflictException) e; + } + throw new CloudRuntimeException("Unable to add rule for ip address id=" + newRule.getSourceIpAddressId(), e); + } finally { + if (!success && newRule != null) { + + txn.start(); + _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false); + removeLBRule(newRule); + + txn.commit(); + } + } + } + + @Override + public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException { + LoadBalancerVO lb = _lbDao.findById(lbRuleId); + List lbs; + if (isRollBackAllowedForProvider(lb)) { + // this is for Netscalar type of devices. if their is failure the db entries will be rollbacked. + lbs = Arrays.asList(lb); + } else { + // get all rules in transition state + lbs = _lbDao.listInTransitionStateByNetworkId(lb.getNetworkId()); + } + return applyLoadBalancerRules(lbs, true); + } + + @Override + public boolean applyLoadBalancersForNetwork(long networkId) throws ResourceUnavailableException { + List lbs = _lbDao.listByNetworkId(networkId); + if (lbs != null) { + return applyLoadBalancerRules(lbs, true); + } else { + s_logger.info("Network id=" + networkId + " doesn't have load balancer rules, nothing to apply"); + return true; + } + } + + @DB + protected boolean applyLoadBalancerRules(List lbs, boolean updateRulesInDB) throws ResourceUnavailableException { + Transaction txn = Transaction.currentTxn(); + List rules = new ArrayList(); + for (LoadBalancerVO lb : lbs) { + List dstList = getExistingDestinations(lb.getId()); + List policyList = getStickinessPolicies(lb.getId()); + + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); + rules.add(loadBalancing); + } + + if (!_networkMgr.applyRules(rules, false)) { + s_logger.debug("LB rules are not completely applied"); + return false; + } + + if (updateRulesInDB) { + for (LoadBalancerVO lb : lbs) { + boolean checkForReleaseElasticIp = false; + txn.start(); + if (lb.getState() == FirewallRule.State.Revoke) { + removeLBRule(lb); + s_logger.debug("LB " + lb.getId() + " is successfully removed"); + checkForReleaseElasticIp = true; + } else if (lb.getState() == FirewallRule.State.Add) { + lb.setState(FirewallRule.State.Active); + s_logger.debug("LB rule " + lb.getId() + " state is set to Active"); + _lbDao.persist(lb); + } + + // remove LB-Vm mappings that were state to revoke + List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId(), true); + List instanceIds = new ArrayList(); + + for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { + instanceIds.add(lbVmMap.getInstanceId()); + } + + if (!instanceIds.isEmpty()) { + _lb2VmMapDao.remove(lb.getId(), instanceIds, null); + s_logger.debug("Load balancer rule id " + lb.getId() + " is removed for vms " + instanceIds); + } + + if (_lb2VmMapDao.listByLoadBalancerId(lb.getId()).isEmpty()) { + lb.setState(FirewallRule.State.Add); + _lbDao.persist(lb); + s_logger.debug("LB rule " + lb.getId() + " state is set to Add as there are no more active LB-VM mappings"); + } + + // remove LB-Stickiness policy mapping that were state to revoke + List stickinesspolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lb.getId(), true); + if (!stickinesspolicies.isEmpty()) { + _lb2stickinesspoliciesDao.remove(lb.getId(), true); + s_logger.debug("Load balancer rule id " + lb.getId() + " is removed stickiness policies"); + } + + txn.commit(); + + if (checkForReleaseElasticIp) { + boolean success = true; + long count = _firewallDao.countRulesByIpId(lb.getSourceIpAddressId()); + if (count == 0) { + try { + success = handleSystemLBIpRelease(lb); + } catch (Exception ex) { + s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion due to exception ", ex); + success = false; + } finally { + if (!success) { + s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion"); + } + } + } + } + } + } + + return true; + } + + protected boolean handleSystemLBIpRelease(LoadBalancerVO lb) { + IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId()); + boolean success = true; + if (ip.getSystem()) { + s_logger.debug("Releasing system ip address " + lb.getSourceIpAddressId() + " as a part of delete lb rule"); + if (!_networkMgr.disassociatePublicIpAddress(lb.getSourceIpAddressId(), UserContext.current().getCallerUserId(), UserContext.current().getCaller())) { + s_logger.warn("Unable to release system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); + success = false; + } else { + s_logger.warn("Successfully released system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); + } + } + + return success; + } + + @Override + public boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId) { + List rules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.LoadBalancing); + if (rules != null) { + s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); + } + for (FirewallRule rule : rules) { + boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); + if (result == false) { + s_logger.warn("Unable to remove load balancer rule " + rule.getId()); + return false; + } + } + return true; + } + + @Override + public boolean removeAllLoadBalanacersForNetwork(long networkId, Account caller, long callerUserId) { + List rules = _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.LoadBalancing); + if (rules != null) { + s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); + } + for (FirewallRule rule : rules) { + boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); + if (result == false) { + s_logger.warn("Unable to remove load balancer rule " + rule.getId()); + return false; + } + } + return true; + } + + @Override + public List getStickinessPolicies(long lbId) { + List stickinessPolicies = new ArrayList(); + List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lbId); + + for (LBStickinessPolicyVO sDbPolicy : sDbpolicies) { + LbStickinessPolicy sPolicy = new LbStickinessPolicy(sDbPolicy.getMethodName(), sDbPolicy.getParams(), sDbPolicy.isRevoke()); + stickinessPolicies.add(sPolicy); + } + return stickinessPolicies; + } + + @Override + public List getExistingDestinations(long lbId) { + List dstList = new ArrayList(); + List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId); + LoadBalancerVO lb = _lbDao.findById(lbId); + + String dstIp = null; + for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { + UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); + Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); + dstIp = nic.getIp4Address(); + LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke()); + dstList.add(lbDst); + } + return dstList; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_UPDATE, eventDescription = "updating load balancer", async = true) + public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) { + Account caller = UserContext.current().getCaller(); + Long lbRuleId = cmd.getId(); + String name = cmd.getLoadBalancerName(); + String description = cmd.getDescription(); + String algorithm = cmd.getAlgorithm(); + LoadBalancerVO lb = _lbDao.findById(lbRuleId); + LoadBalancerVO lbBackup = _lbDao.findById(lbRuleId); + + if (lb == null) { + throw new InvalidParameterValueException("Unable to find lb rule by id", null); + } + + // check permissions + _accountMgr.checkAccess(caller, null, true, lb); + + if (name != null) { + lb.setName(name); + } + + if (description != null) { + lb.setDescription(description); + } + + if (algorithm != null) { + lb.setAlgorithm(algorithm); + } + + boolean success = _lbDao.update(lbRuleId, lb); + + // If algorithm is changed, have to reapply the lb config + if (algorithm != null) { + try { + lb.setState(FirewallRule.State.Add); + _lbDao.persist(lb); + applyLoadBalancerConfig(lbRuleId); + } catch (ResourceUnavailableException e) { + if (isRollBackAllowedForProvider(lb)) { + /* + * NOTE : We use lb object to update db instead of lbBackup object since db layer will fail to + * update if there is no change in the object. + */ + if (lbBackup.getName() != null) { + lb.setName(lbBackup.getName()); + } + if (lbBackup.getDescription() != null) { + lb.setDescription(lbBackup.getDescription()); + } + if (lbBackup.getAlgorithm() != null) { + lb.setAlgorithm(lbBackup.getAlgorithm()); + } + lb.setState(lbBackup.getState()); + _lbDao.update(lb.getId(), lb); + _lbDao.persist(lb); + + s_logger.debug("LB Rollback rule id: " + lbRuleId + " while updating LB rule."); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + success = false; + } + } + + if (!success) { + throw new CloudRuntimeException("Failed to update load balancer rule: " + lbRuleId); + } + + return lb; + } + + @Override + public List listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException { + Account caller = UserContext.current().getCaller(); + Long loadBalancerId = cmd.getId(); + Boolean applied = cmd.isApplied(); + + if (applied == null) { + applied = Boolean.TRUE; + } + + LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); + if (loadBalancer == null) { + return null; + } + + _accountMgr.checkAccess(caller, null, true, loadBalancer); + + List loadBalancerInstances = new ArrayList(); + List vmLoadBalancerMappings = null; + + vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); + + List appliedInstanceIdList = new ArrayList(); + if ((vmLoadBalancerMappings != null) && !vmLoadBalancerMappings.isEmpty()) { + for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) { + appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId()); + } + } + + IPAddressVO addr = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId()); + List userVms = _vmDao.listVirtualNetworkInstancesByAcctAndZone(loadBalancer.getAccountId(), addr.getDataCenterId(), loadBalancer.getNetworkId()); + + for (UserVmVO userVm : userVms) { + // if the VM is destroyed, being expunged, in an error state, or in an unknown state, skip it + switch (userVm.getState()) { + case Destroyed: + case Expunging: + case Error: + case Unknown: + continue; + } + + boolean isApplied = appliedInstanceIdList.contains(userVm.getId()); + if ((isApplied && applied) || (!isApplied && !applied)) { + loadBalancerInstances.add(userVm); + } + } + + return loadBalancerInstances; + } + + public List getSupportedAutoScaleCounters(long networkid) + { + String capability = getLBCapability(networkid, Capability.AutoScaleCounters.getName()); + if (capability == null || capability.length() == 0) { + return null; + } + return Arrays.asList(capability.split(",")); + } + + @Override + public List getStickinessMethods(long networkid) + { + String capability = getLBCapability(networkid, Capability.SupportedStickinessMethods.getName()); + if (capability == null) { + return null; + } + Gson gson = new Gson(); + java.lang.reflect.Type listType = new TypeToken>() { + }.getType(); + List result = gson.fromJson(capability, listType); + return result; + } + + @Override + public List searchForLBStickinessPolicies(ListLBStickinessPoliciesCmd cmd) throws PermissionDeniedException { + Account caller = UserContext.current().getCaller(); + Long loadBalancerId = cmd.getLbRuleId(); + LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); + if (loadBalancer == null) { + return null; + } + + _accountMgr.checkAccess(caller, null, true, loadBalancer); + + List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId()); + + return sDbpolicies; + } + + @Override + public List searchForLoadBalancers(ListLoadBalancerRulesCmd cmd) { + Long ipId = cmd.getPublicIpId(); + Long zoneId = cmd.getZoneId(); + Long id = cmd.getId(); + String name = cmd.getLoadBalancerRuleName(); + String keyword = cmd.getKeyword(); + Long instanceId = cmd.getVirtualMachineId(); + Map tags = cmd.getTags(); + + Account caller = UserContext.current().getCaller(); + List permittedAccounts = new ArrayList(); + + Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + + Filter searchFilter = new Filter(LoadBalancerVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); + SearchBuilder sb = _lbDao.createSearchBuilder(); + _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); + + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ); + + if (instanceId != null) { + SearchBuilder lbVMSearch = _lb2VmMapDao.createSearchBuilder(); + lbVMSearch.and("instanceId", lbVMSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + sb.join("lbVMSearch", lbVMSearch, sb.entity().getId(), lbVMSearch.entity().getLoadBalancerId(), JoinBuilder.JoinType.INNER); + } + + if (zoneId != null) { + SearchBuilder ipSearch = _ipAddressDao.createSearchBuilder(); + ipSearch.and("zoneId", ipSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.join("ipSearch", ipSearch, sb.entity().getSourceIpAddressId(), ipSearch.entity().getId(), JoinBuilder.JoinType.INNER); + } + + if (tags != null && !tags.isEmpty()) { + SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); + for (int count = 0; count < tags.size(); count++) { + tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ); + tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ); + tagSearch.cp(); + } + tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + sb.groupBy(sb.entity().getId()); + sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); + } + + SearchCriteria sc = sb.create(); + _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); + + if (keyword != null) { + SearchCriteria ssc = _lbDao.createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + + if (name != null) { + sc.setParameters("name", "%" + name + "%"); + } + + if (id != null) { + sc.setParameters("id", id); + } + + if (ipId != null) { + sc.setParameters("sourceIpAddress", ipId); + } + + if (instanceId != null) { + sc.setJoinParameters("lbVMSearch", "instanceId", instanceId); + } + + if (zoneId != null) { + sc.setJoinParameters("ipSearch", "zoneId", zoneId); + } + + if (tags != null && !tags.isEmpty()) { + int count = 0; + sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.LoadBalancer.toString()); + for (String key : tags.keySet()) { + sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); + sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); + count++; + } + } + + return _lbDao.search(sc, searchFilter); + } + + @Override + public List listByNetworkId(long networkId) { + List lbs = _lbDao.listByNetworkId(networkId); + List lbRules = new ArrayList(); + for (LoadBalancerVO lb : lbs) { + List dstList = getExistingDestinations(lb.getId()); + List policyList = this.getStickinessPolicies(lb.getId()); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); + lbRules.add(loadBalancing); + } + return lbRules; + } + + @Override + public LoadBalancerVO findById(long lbId) { + return _lbDao.findById(lbId); + } + + protected void removeLBRule(LoadBalancerVO rule) { + + //remove the rule + _lbDao.remove(rule.getId()); + + // if the rule is the last one for the ip address assigned to VPC, unassign it from the network + IpAddress ip = _ipAddressDao.findById(rule.getSourceIpAddressId()); + _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), rule.getNetworkId()); + + } +} diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 7cb7796ef1d..05b4331c6b0 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -207,8 +207,8 @@ public class VpcManagerImpl implements VpcManager, Manager{ String maxNtwks = configs.get(Config.VpcMaxNetworks.key()); _maxNetworks = NumbersUtil.parseInt(maxNtwks, 3); // max=3 is default - - + + IpAddressSearch = _ipAddressDao.createSearchBuilder(); IpAddressSearch.and("accountId", IpAddressSearch.entity().getAllocatedToAccountId(), Op.EQ); IpAddressSearch.and("dataCenterId", IpAddressSearch.entity().getDataCenterId(), Op.EQ); @@ -218,7 +218,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), Op.EQ); IpAddressSearch.join("virtualNetworkVlanSB", virtualNetworkVlanSB, IpAddressSearch.entity().getVlanId(), virtualNetworkVlanSB.entity().getId(), JoinBuilder.JoinType.INNER); IpAddressSearch.done(); - + return true; } @@ -517,7 +517,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ //Verify that caller can perform actions in behalf of vpc owner _accountMgr.checkAccess(caller, null, false, owner); - + //check resource limit _resourceLimitMgr.checkResourceLimit(owner, ResourceType.vpc); @@ -647,16 +647,16 @@ public class VpcManagerImpl implements VpcManager, Manager{ s_logger.debug("Updating VPC " + vpc + " with state " + Vpc.State.Inactive + " as a part of vpc delete"); VpcVO vpcVO = _vpcDao.findById(vpc.getId()); vpcVO.setState(Vpc.State.Inactive); - + Transaction txn = Transaction.currentTxn(); txn.start(); _vpcDao.update(vpc.getId(), vpcVO); - + //decrement resource count _resourceLimitMgr.decrementResourceCount(vpc.getAccountId(), ResourceType.vpc); txn.commit(); } - + //shutdown VPC if (!shutdownVpc(vpc.getId())) { s_logger.warn("Failed to shutdown vpc " + vpc + " as a part of vpc destroy process"); @@ -839,7 +839,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ } } - + protected List getSupportedServices() { List services = new ArrayList(); services.add(Network.Service.Dhcp); @@ -865,9 +865,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ //check if vpc exists Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified", null); - ex.addProxyObject("vpc", vpcId, "VPC"); - throw ex; + throw new InvalidParameterValueException("Unable to find Enabled VPC", null); } //permission check @@ -944,7 +942,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ @DB public void validateNtkwOffForVpc(long ntwkOffId, String cidr, String networkDomain, Account networkOwner, Vpc vpc, Long networkId, String gateway) { - + NetworkOffering guestNtwkOff = _configMgr.getNetworkOffering(ntwkOffId); if (networkId == null) { @@ -1007,7 +1005,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ @DB protected void validateNewVpcGuestNetwork(String cidr, String gateway, Account networkOwner, Vpc vpc, String networkDomain) { - + Transaction txn = Transaction.currentTxn(); txn.start(); Vpc locked = _vpcDao.acquireInLockTable(vpc.getId()); @@ -1154,7 +1152,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ // Verify input parameters Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id", null); + throw new InvalidParameterValueException("Unable to find Enabled VPC", null); } _accountMgr.checkAccess(caller, null, false, vpc); @@ -1220,7 +1218,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ //Validate parameters Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id", null); + throw new InvalidParameterValueException("Unable to find Enabled VPC", null); } //allow only one private gateway per vpc @@ -1300,7 +1298,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_DELETE, eventDescription = "deleting private gateway") @DB public boolean deleteVpcPrivateGateway(long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException { - + Transaction txn = Transaction.currentTxn(); txn.start(); VpcGatewayVO gatewayVO = _vpcGatewayDao.acquireInLockTable(gatewayId); @@ -1315,11 +1313,11 @@ public class VpcManagerImpl implements VpcManager, Manager{ throw new CloudRuntimeException("Can't delete private gateway " + gatewayVO + " as it has " + routeCount + " static routes applied. Remove the routes first"); } - + gatewayVO.setState(VpcGateway.State.Deleting); _vpcGatewayDao.update(gatewayVO.getId(), gatewayVO); s_logger.debug("Marked gateway " + gatewayVO + " with state " + VpcGateway.State.Deleting); - + txn.commit(); //1) delete the gateway on the backend @@ -1734,7 +1732,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ public VpcGateway getPrivateGatewayForVpc(long vpcId) { return _vpcGatewayDao.getPrivateGatewayForVpc(vpcId); } - + @DB @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "associating Ip", async = true) @@ -1783,8 +1781,8 @@ public class VpcManagerImpl implements VpcManager, Manager{ return _ipAddressDao.findById(ipId); } - - + + @Override public void unassignIPFromVpcNetwork(long ipId, long networkId) { IPAddressVO ip = _ipAddressDao.findById(ipId); @@ -1818,13 +1816,13 @@ public class VpcManagerImpl implements VpcManager, Manager{ } s_logger.debug("Successfully released VPC ip address " + ip + " back to VPC pool "); } - + @Override public boolean ipUsedInVpc(IpAddress ip) { return (ip != null && ip.getVpcId() != null && (ip.isOneToOneNat() || !_firewallDao.listByIp(ip.getId()).isEmpty())); } - + @DB @Override public Network createVpcGuestNetwork(long ntwkOffId, String name, String displayText, String gateway, @@ -1835,20 +1833,18 @@ public class VpcManagerImpl implements VpcManager, Manager{ Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC ", null); - ex.addProxyObject("vpc", vpcId, "VPC"); - throw ex; + throw new InvalidParameterValueException("Unable to find Enabled VPC ", null); } _accountMgr.checkAccess(caller, null, false, vpc); - + if (networkDomain == null) { networkDomain = vpc.getNetworkDomain(); } - + if (vpc.getZoneId() != zoneId) { throw new InvalidParameterValueException("New network doesn't belong to vpc zone", null); } - + //1) Validate if network can be created for VPC validateNtkwOffForVpc(ntwkOffId, cidr, networkDomain, owner, vpc, null, gateway); @@ -1858,12 +1854,12 @@ public class VpcManagerImpl implements VpcManager, Manager{ return guestNetwork; } - - + + protected IPAddressVO getExistingSourceNatInVpc(long ownerId, long vpcId) { List addrs = listPublicIpsAssignedToVpc(ownerId, true, vpcId); - + IPAddressVO sourceNatIp = null; if (addrs.isEmpty()) { return null; @@ -1882,7 +1878,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ return sourceNatIp; } - + protected List listPublicIpsAssignedToVpc(long accountId, Boolean sourceNat, long vpcId) { SearchCriteria sc = IpAddressSearch.create(); sc.setParameters("accountId", accountId); @@ -1895,8 +1891,8 @@ public class VpcManagerImpl implements VpcManager, Manager{ return _ipAddressDao.search(sc, null); } - - + + @Override public PublicIp assignSourceNatIpAddressToVpc(Account owner, Vpc vpc) throws InsufficientAddressCapacityException, ConcurrentOperationException { long dcId = vpc.getZoneId(); @@ -1928,7 +1924,7 @@ public class VpcManagerImpl implements VpcManager, Manager{ Vpc vpc = getVpc(network.getVpcId()); validateNtkwOffForVpc(ntwkOffId, null, null, null, vpc, networkId, null); } - + return _ntwkMgr.updateGuestNetwork(networkId, name, displayText, callerAccount, callerUser, domainSuffix, ntwkOffId, changeCidr); } diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 942247ed2e4..cfc3a1dbbd5 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -611,7 +611,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma if (clusterId != null) { ClusterVO cluster = _clusterDao.findById(clusterId); if (cluster == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("can not find cluster by Id", null); + throw new InvalidParameterValueException("can not find cluster", null); } else { if (cluster.getGuid() == null) { List hosts = listAllHostsInCluster(clusterId); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 368dad87d71..a4edb3b3777 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1303,9 +1303,9 @@ public class ManagementServerImpl implements ManagementServer { }// If ISO requested then it should be ISO. if (isIso && template.getFormat() != ImageFormat.ISO) { s_logger.error("Template Id " + templateId + " is not an ISO"); - InvalidParameterValueException ex = new InvalidParameterValueException("Specified Template Id is not an ISO", null); - ex.addProxyObject(template, templateId, "templateId"); - throw ex; + List idList = new ArrayList(); + idList.add(new IdentityProxy(template, templateId, "templateId")); + throw new InvalidParameterValueException("Specified Template Id is not an ISO", idList); }// If ISO not requested then it shouldn't be an ISO. if (!isIso && template.getFormat() == ImageFormat.ISO) { s_logger.error("Incorrect format of the template id " + templateId); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 83de4d63b8d..9ba38e486e2 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -525,7 +525,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager return maxDataVolumesSupported.intValue(); } - + @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) public Volume attachVolumeToVM(AttachVolumeCmd command) { @@ -2928,9 +2928,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager // Verify input parameters UserVmVO vm = _vmDao.findById(vmId); if (vm == null || vm.getRemoved() != null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a virtual machine with specified vmId", null); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; + throw new InvalidParameterValueException("Unable to find a virtual machine with specified vmId", null); } if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { @@ -3138,11 +3136,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (id != null) { sc.setParameters("id", id); } - + if (templateId != null) { sc.setParameters("templateId", templateId); } - + if (isoId != null) { sc.setParameters("isoId", isoId); } @@ -3314,9 +3312,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager if (s_logger.isDebugEnabled()) { s_logger.debug("VM is not Running, unable to migrate the vm " + vm); } - InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, unable to migrate the vm with specified id", null); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; + List idList = new ArrayList(); + idList.add(new IdentityProxy(vm, vmId, "vmId")); + throw new InvalidParameterValueException("VM is not Running, unable to migrate the vm with specified id", idList); } if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm)) { if (s_logger.isDebugEnabled()) { @@ -3396,9 +3394,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } //don't allow to move the vm from the project if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { - InvalidParameterValueException ex = new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved", null); - ex.addProxyObject(vm, cmd.getVmId(), "vmId"); - throw ex; + List idList = new ArrayList(); + idList.add(new IdentityProxy(vm, cmd.getVmId(), "vmId")); + throw new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved", idList); } Account newAccount = _accountService.getActiveAccountByName(cmd.getAccountName(), cmd.getDomainId()); if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { @@ -3680,9 +3678,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager long vmId = cmd.getVmId(); UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Cann not find VM by ID ", null); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; + throw new InvalidParameterValueException("Cann not find VM", null); } Account owner = _accountDao.findById(vm.getAccountId()); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index f63f182c7d0..1931a36d0be 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1,2560 +1,2560 @@ -// Copyright 2012 Citrix Systems, Inc. Licensed under the -// Apache License, Version 2.0 (the "License"); you may not use this -// file except in compliance with the License. Citrix Systems, Inc. -// reserves all rights not expressly granted by 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. -// -// Automatically generated by addcopyright.py at 04/03/2012 -package com.cloud.vm; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; - -import com.cloud.agent.AgentManager; -import com.cloud.agent.AgentManager.OnError; -import com.cloud.agent.Listener; -import com.cloud.agent.api.AgentControlAnswer; -import com.cloud.agent.api.AgentControlCommand; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.CheckVirtualMachineAnswer; -import com.cloud.agent.api.CheckVirtualMachineCommand; -import com.cloud.agent.api.ClusterSyncAnswer; -import com.cloud.agent.api.ClusterSyncCommand; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.MigrateAnswer; -import com.cloud.agent.api.MigrateCommand; -import com.cloud.agent.api.PingRoutingCommand; -import com.cloud.agent.api.PrepareForMigrationAnswer; -import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.RebootAnswer; -import com.cloud.agent.api.RebootCommand; -import com.cloud.agent.api.StartAnswer; -import com.cloud.agent.api.StartCommand; -import com.cloud.agent.api.StartupCommand; -import com.cloud.agent.api.StartupRoutingCommand; -import com.cloud.agent.api.StartupRoutingCommand.VmState; -import com.cloud.agent.api.StopAnswer; -import com.cloud.agent.api.StopCommand; -import com.cloud.agent.api.to.NicTO; -import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.agent.manager.Commands; -import com.cloud.agent.manager.allocator.HostAllocator; -import com.cloud.alert.AlertManager; -import com.cloud.capacity.CapacityManager; -import com.cloud.cluster.ClusterManager; -import com.cloud.cluster.StackMaid; -import com.cloud.configuration.Config; -import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.consoleproxy.ConsoleProxyManager; -import com.cloud.dc.DataCenter; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.HostPodVO; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.dc.dao.HostPodDao; -import com.cloud.deploy.DataCenterDeployment; -import com.cloud.deploy.DeployDestination; -import com.cloud.deploy.DeploymentPlan; -import com.cloud.deploy.DeploymentPlanner; -import com.cloud.deploy.DeploymentPlanner.ExcludeList; -import com.cloud.domain.dao.DomainDao; -import com.cloud.exception.AgentUnavailableException; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.ConnectionException; -import com.cloud.exception.InsufficientAddressCapacityException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InsufficientServerCapacityException; -import com.cloud.exception.InsufficientVirtualNetworkCapcityException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ManagementServerException; -import com.cloud.exception.OperationTimedoutException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.VirtualMachineMigrationException; -import com.cloud.ha.HighAvailabilityManager; -import com.cloud.ha.HighAvailabilityManager.WorkType; -import com.cloud.host.Host; -import com.cloud.host.HostVO; -import com.cloud.host.Status; -import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.hypervisor.HypervisorGuru; -import com.cloud.hypervisor.HypervisorGuruManager; -import com.cloud.network.Network; -import com.cloud.network.NetworkManager; -import com.cloud.network.NetworkVO; -import com.cloud.network.dao.NetworkDao; -import com.cloud.offering.ServiceOffering; -import com.cloud.org.Cluster; -import com.cloud.resource.ResourceManager; -import com.cloud.service.ServiceOfferingVO; -import com.cloud.service.dao.ServiceOfferingDao; -import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.StorageManager; -import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolVO; -import com.cloud.storage.VMTemplateVO; -import com.cloud.storage.Volume; -import com.cloud.storage.Volume.Type; -import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.GuestOSCategoryDao; -import com.cloud.storage.dao.GuestOSDao; -import com.cloud.storage.dao.StoragePoolDao; -import com.cloud.storage.dao.VMTemplateDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.user.Account; -import com.cloud.user.AccountManager; -import com.cloud.user.User; -import com.cloud.user.dao.AccountDao; -import com.cloud.user.dao.UserDao; -import com.cloud.utils.Journal; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Pair; -import com.cloud.utils.Ternary; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; -import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.GlobalLock; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.exception.ExecutionException; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.ItWorkVO.Step; -import com.cloud.vm.VirtualMachine.Event; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.dao.ConsoleProxyDao; -import com.cloud.vm.dao.DomainRouterDao; -import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.SecondaryStorageVmDao; -import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDao; - -@Local(value = VirtualMachineManager.class) -public class VirtualMachineManagerImpl implements VirtualMachineManager, Listener { - private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class); - - String _name; - @Inject - protected StorageManager _storageMgr; - @Inject - protected NetworkManager _networkMgr; - @Inject - protected AgentManager _agentMgr; - @Inject - protected VMInstanceDao _vmDao; - @Inject - protected ServiceOfferingDao _offeringDao; - @Inject - protected VMTemplateDao _templateDao; - @Inject - protected UserDao _userDao; - @Inject - protected AccountDao _accountDao; - @Inject - protected DomainDao _domainDao; - @Inject - protected ClusterManager _clusterMgr; - @Inject - protected ItWorkDao _workDao; - @Inject - protected UserVmDao _userVmDao; - @Inject - protected DomainRouterDao _routerDao; - @Inject - protected ConsoleProxyDao _consoleDao; - @Inject - protected SecondaryStorageVmDao _secondaryDao; - @Inject - protected NicDao _nicsDao; - @Inject - protected AccountManager _accountMgr; - @Inject - protected HostDao _hostDao; - @Inject - protected AlertManager _alertMgr; - @Inject - protected GuestOSCategoryDao _guestOsCategoryDao; - @Inject - protected GuestOSDao _guestOsDao; - @Inject - protected VolumeDao _volsDao; - @Inject - protected ConsoleProxyManager _consoleProxyMgr; - @Inject - protected ConfigurationManager _configMgr; - @Inject - protected CapacityManager _capacityMgr; - @Inject - protected HighAvailabilityManager _haMgr; - @Inject - protected HostPodDao _podDao; - @Inject - protected DataCenterDao _dcDao; - @Inject - protected StoragePoolDao _storagePoolDao; - @Inject - protected HypervisorGuruManager _hvGuruMgr; - @Inject - protected NetworkDao _networkDao; - - @Inject(adapter = DeploymentPlanner.class) - protected Adapters _planners; - - @Inject(adapter = HostAllocator.class) - protected Adapters _hostAllocators; - - @Inject - protected ResourceManager _resourceMgr; - - Map> _vmGurus = new HashMap>(); - protected StateMachine2 _stateMachine; - - ScheduledExecutorService _executor = null; - protected int _operationTimeout; - - protected int _retry; - protected long _nodeId; - protected long _cleanupWait; - protected long _cleanupInterval; - protected long _cancelWait; - protected long _opWaitInterval; - protected int _lockStateRetry; - protected boolean _forceStop; - - @Override - public void registerGuru(VirtualMachine.Type type, VirtualMachineGuru guru) { - synchronized (_vmGurus) { - _vmGurus.put(type, guru); - } - } - - @Override - @DB - public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, Pair rootDiskOffering, - List> dataDiskOfferings, List> networks, Map params, DeploymentPlan plan, - HypervisorType hyperType, Account owner) throws InsufficientCapacityException { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Allocating entries for VM: " + vm); - } - - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, serviceOffering, owner, params); - - vm.setDataCenterId(plan.getDataCenterId()); - if (plan.getPodId() != null) { - vm.setPodId(plan.getPodId()); - } - assert (plan.getClusterId() == null && plan.getPoolId() == null) : "We currently don't support cluster and pool preset yet"; - - @SuppressWarnings("unchecked") - VirtualMachineGuru guru = (VirtualMachineGuru) _vmGurus.get(vm.getType()); - - Transaction txn = Transaction.currentTxn(); - txn.start(); - vm = guru.persist(vm); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Allocating nics for " + vm); - } - - try { - _networkMgr.allocate(vmProfile, networks); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operation while trying to allocate resources for the VM", e); - } - - if (dataDiskOfferings == null) { - dataDiskOfferings = new ArrayList>(0); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Allocaing disks for " + vm); - } - - if (template.getFormat() == ImageFormat.ISO) { - _storageMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vm.getId(), rootDiskOffering.first(), rootDiskOffering.second(), vm, owner); - } else if (template.getFormat() == ImageFormat.BAREMETAL) { - // Do nothing - } else { - _storageMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vm.getId(), rootDiskOffering.first(), template, vm, owner); - } - - for (Pair offering : dataDiskOfferings) { - _storageMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vm.getId(), offering.first(), offering.second(), vm, owner); - } - - txn.commit(); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Allocation completed for VM: " + vm); - } - - return vm; - } - - @Override - public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, Long rootSize, Pair dataDiskOffering, - List> networks, DeploymentPlan plan, HypervisorType hyperType, Account owner) throws InsufficientCapacityException { - List> diskOfferings = new ArrayList>(1); - if (dataDiskOffering != null) { - diskOfferings.add(dataDiskOffering); - } - return allocate(vm, template, serviceOffering, new Pair(serviceOffering, rootSize), diskOfferings, networks, null, plan, hyperType, owner); - } - - @Override - public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, List> networks, DeploymentPlan plan, - HypervisorType hyperType, Account owner) throws InsufficientCapacityException { - return allocate(vm, template, serviceOffering, new Pair(serviceOffering, null), null, networks, null, plan, hyperType, owner); - } - - @SuppressWarnings("unchecked") - private VirtualMachineGuru getVmGuru(T vm) { - return (VirtualMachineGuru) _vmGurus.get(vm.getType()); - } - - @SuppressWarnings("unchecked") - private VirtualMachineGuru getBareMetalVmGuru(T vm) { - return (VirtualMachineGuru) _vmGurus.get(VirtualMachine.Type.UserBareMetal); - } - - @Override - public boolean expunge(T vm, User caller, Account account) throws ResourceUnavailableException { - try { - if (advanceExpunge(vm, caller, account)) { - // Mark vms as removed - remove(vm, caller, account); - return true; - } else { - s_logger.info("Did not expunge " + vm); - return false; - } - } catch (OperationTimedoutException e) { - throw new CloudRuntimeException("Operation timed out", e); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Concurrent operation ", e); - } - } - - @Override - public boolean advanceExpunge(T vm, User caller, Account account) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { - if (vm == null || vm.getRemoved() != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find vm or vm is destroyed: " + vm); - } - return true; - } - - if (!this.advanceStop(vm, false, caller, account)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to stop the VM so we can't expunge it."); - } - } - - try { - if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); - return false; - } - } catch (NoTransitionException e) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); - return false; - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Destroying vm " + vm); - } - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - s_logger.debug("Cleaning up NICS"); - _networkMgr.cleanupNics(profile); - // Clean up volumes based on the vm's instance id - _storageMgr.cleanupVolumes(vm.getId()); - - VirtualMachineGuru guru = getVmGuru(vm); - guru.finalizeExpunge(vm); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Expunged " + vm); - } - - return true; - } - - @Override - public boolean start() { - _executor.scheduleAtFixedRate(new CleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS); - cancelWorkItems(_nodeId); - return true; - } - - @Override - public boolean stop() { - return true; - } - - @Override - public boolean configure(String name, Map xmlParams) throws ConfigurationException { - _name = name; - - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); - Map params = configDao.getConfiguration(xmlParams); - - _retry = NumbersUtil.parseInt(params.get(Config.StartRetry.key()), 10); - - ReservationContextImpl.setComponents(_userDao, _domainDao, _accountDao); - VirtualMachineProfileImpl.setComponents(_offeringDao, _templateDao, _accountDao); - - _cancelWait = NumbersUtil.parseLong(params.get(Config.VmOpCancelInterval.key()), 3600); - _cleanupWait = NumbersUtil.parseLong(params.get(Config.VmOpCleanupWait.key()), 3600); - _cleanupInterval = NumbersUtil.parseLong(params.get(Config.VmOpCleanupInterval.key()), 86400) * 1000; - _opWaitInterval = NumbersUtil.parseLong(params.get(Config.VmOpWaitInterval.key()), 120) * 1000; - _lockStateRetry = NumbersUtil.parseInt(params.get(Config.VmOpLockStateRetry.key()), 5); - _operationTimeout = NumbersUtil.parseInt(params.get(Config.Wait.key()), 1800) * 2; - _forceStop = Boolean.parseBoolean(params.get(Config.VmDestroyForcestop.key())); - - _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vm-Operations-Cleanup")); - _nodeId = _clusterMgr.getManagementNodeId(); - - _agentMgr.registerForHostEvents(this, true, true, true); - - return true; - } - - @Override - public String getName() { - return _name; - } - - protected VirtualMachineManagerImpl() { - setStateMachine(); - } - - @Override - public T start(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException { - return start(vm, params, caller, account, null); - } - - @Override - public T start(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) throws InsufficientCapacityException, - ResourceUnavailableException { - try { - return advanceStart(vm, params, caller, account, planToDeploy); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Unable to start a VM due to concurrent operation", e); - } - } - - protected boolean checkWorkItems(VMInstanceVO vm, State state) throws ConcurrentOperationException { - while (true) { - ItWorkVO vo = _workDao.findByOutstandingWork(vm.getId(), state); - if (vo == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find work for VM: " + vm + " and state: " + state); - } - return true; - } - - if (vo.getStep() == Step.Done) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Work for " + vm + " is " + vo.getStep()); - } - return true; - } - - if (vo.getSecondsTaskIsInactive() > _cancelWait) { - s_logger.warn("The task item for vm " + vm + " has been inactive for " + vo.getSecondsTaskIsInactive()); - return false; - } - - try { - Thread.sleep(_opWaitInterval); - } catch (InterruptedException e) { - s_logger.info("Waiting for " + vm + " but is interrupted"); - throw new ConcurrentOperationException("Waiting for " + vm + " but is interrupted"); - } - s_logger.debug("Waiting some more to make sure there's no activity on " + vm); - } - - } - - @DB - protected Ternary changeToStartState(VirtualMachineGuru vmGuru, T vm, User caller, Account account) - throws ConcurrentOperationException { - long vmId = vm.getId(); - - ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), vm.getId()); - int retry = _lockStateRetry; - while (retry-- != 0) { - Transaction txn = Transaction.currentTxn(); - Ternary result = null; - txn.start(); - try { - Journal journal = new Journal.LogJournal("Creating " + vm, s_logger); - work = _workDao.persist(work); - ReservationContextImpl context = new ReservationContextImpl(work.getId(), journal, caller, account); - - if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); - } - result = new Ternary(vmGuru.findById(vmId), context, work); - txn.commit(); - return result; - } - } catch (NoTransitionException e) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to transition into Starting state due to " + e.getMessage()); - } - } finally { - if (result == null) { - txn.rollback(); - } - } - - VMInstanceVO instance = _vmDao.findById(vmId); - if (instance == null) { - throw new ConcurrentOperationException("Unable to acquire lock on " + vm); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Determining why we're unable to update the state to Starting for " + instance + ". Retry=" + retry); - } - - State state = instance.getState(); - if (state == State.Running) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is already started: " + vm); - } - return null; - } - - if (state.isTransitional()) { - if (!checkWorkItems(vm, state)) { - throw new ConcurrentOperationException("There are concurrent operations on " + vm); - } else { - continue; - } - } - - if (state != State.Stopped) { - s_logger.debug("VM " + vm + " is not in a state to be started: " + state); - return null; - } - } - - throw new ConcurrentOperationException("Unable to change the state of " + vm); - } - - protected boolean changeState(T vm, Event event, Long hostId, ItWorkVO work, Step step) throws NoTransitionException { - // FIXME: We should do this better. - Step previousStep = work.getStep(); - _workDao.updateStep(work, step); - boolean result = false; - try { - result = stateTransitTo(vm, event, hostId); - return result; - } finally { - if (!result) { - _workDao.updateStep(work, previousStep); - } - } - } - - @Override - public T advanceStart(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException { - return advanceStart(vm, params, caller, account, null); - } - - @Override - public T advanceStart(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { - long vmId = vm.getId(); - VirtualMachineGuru vmGuru; - if (vm.getHypervisorType() == HypervisorType.BareMetal) { - vmGuru = getBareMetalVmGuru(vm); - } else { - vmGuru = getVmGuru(vm); - } - - vm = vmGuru.findById(vm.getId()); - Ternary start = changeToStartState(vmGuru, vm, caller, account); - if (start == null) { - return vmGuru.findById(vmId); - } - - vm = start.first(); - ReservationContext ctx = start.second(); - ItWorkVO work = start.third(); - - T startedVm = null; - ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); - VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Trying to deploy VM, vm has dcId: " + vm.getDataCenterIdToDeployIn() + " and podId: " + vm.getPodIdToDeployIn()); - } - DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), null, null, null, null, ctx); - if(planToDeploy != null && planToDeploy.getDataCenterId() != 0){ - if (s_logger.isDebugEnabled()) { - s_logger.debug("advanceStart: DeploymentPlan is provided, using dcId:" + planToDeploy.getDataCenterId() + ", podId: " + planToDeploy.getPodId() + ", clusterId: " - + planToDeploy.getClusterId() + ", hostId: " + planToDeploy.getHostId() + ", poolId: " + planToDeploy.getPoolId()); - } - plan = new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(), planToDeploy.getPoolId(), planToDeploy.getPhysicalNetworkId(), ctx); - } - - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); - - boolean canRetry = true; - try { - Journal journal = start.second().getJournal(); - - ExcludeList avoids = null; - if (planToDeploy != null) { - avoids = planToDeploy.getAvoids(); - } - if (avoids == null) { - avoids = new ExcludeList(); - } - if (s_logger.isDebugEnabled()) { - s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid()); - } - - - boolean planChangedByVolume = false; - boolean reuseVolume = true; - DataCenterDeployment originalPlan = plan; - - int retry = _retry; - boolean recreate = false; - while (retry-- != 0) { // It's != so that it can match -1. - - if(reuseVolume){ - // edit plan if this vm's ROOT volume is in READY state already - List vols = _volsDao.findReadyRootVolumesByInstance(vm.getId()); - for (VolumeVO vol : vols) { - // make sure if the templateId is unchanged. If it is changed, - // let planner - // reassign pool for the volume even if it ready. - Long volTemplateId = vol.getTemplateId(); - if (volTemplateId != null && volTemplateId.longValue() != template.getId()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug(vol + " of " + vm + " is READY, but template ids don't match, let the planner reassign a new pool"); - } - continue; - } - - StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); - if (!pool.isInMaintenance()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Root volume is ready, need to place VM in volume's cluster"); - } - long rootVolDcId = pool.getDataCenterId(); - Long rootVolPodId = pool.getPodId(); - Long rootVolClusterId = pool.getClusterId(); - if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) { - Long clusterIdSpecified = planToDeploy.getClusterId(); - if (clusterIdSpecified != null && rootVolClusterId != null) { - if (rootVolClusterId.longValue() != clusterIdSpecified.longValue()) { - // cannot satisfy the plan passed in to the - // planner - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cannot satisfy the deployment plan passed in since the ready Root volume is in different cluster. volume's cluster: " + rootVolClusterId - + ", cluster specified: " + clusterIdSpecified); - } - throw new ResourceUnavailableException("Root volume is ready in different cluster, Deployment plan provided cannot be satisfied, unable to create a deployment for " - + vm, Cluster.class, clusterIdSpecified); - } - } - plan = new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(), vol.getPoolId(), null, ctx); - }else{ - plan = new DataCenterDeployment(rootVolDcId, rootVolPodId, rootVolClusterId, null, vol.getPoolId(), null, ctx); - if (s_logger.isDebugEnabled()) { - s_logger.debug(vol + " is READY, changing deployment plan to use this pool's dcId: " + rootVolDcId + " , podId: " + rootVolPodId + " , and clusterId: " + rootVolClusterId); - } - planChangedByVolume = true; - } - } - } - } - - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, account, params); - DeployDestination dest = null; - for (DeploymentPlanner planner : _planners) { - if (planner.canHandle(vmProfile, plan, avoids)) { - dest = planner.plan(vmProfile, plan, avoids); - } else { - continue; - } - if (dest != null) { - avoids.addHost(dest.getHost().getId()); - journal.record("Deployment found ", vmProfile, dest); - break; - } - } - - if (dest == null) { - if (planChangedByVolume) { - plan = originalPlan; - planChangedByVolume = false; - //do not enter volume reuse for next retry, since we want to look for resorces outside the volume's cluster - reuseVolume = false; - continue; - } - throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); - } - - long destHostId = dest.getHost().getId(); - vm.setPodId(dest.getPod().getId()); - - try { - if (!changeState(vm, Event.OperationRetry, destHostId, work, Step.Prepare)) { - throw new ConcurrentOperationException("Unable to update the state of the Virtual Machine"); - } - } catch (NoTransitionException e1) { - throw new ConcurrentOperationException(e1.getMessage()); - } - - try { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is being created in podId: " + vm.getPodIdToDeployIn()); - } - _networkMgr.prepare(vmProfile, dest, ctx); - if (vm.getHypervisorType() != HypervisorType.BareMetal) { - _storageMgr.prepare(vmProfile, dest, recreate); - recreate = false; - } - //since StorageMgr succeeded in volume creation, reuse Volume for further tries until current cluster has capacity - if(!reuseVolume){ - reuseVolume = true; - } - - Commands cmds = null; - vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); - - VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - - cmds = new Commands(OnError.Stop); - cmds.addCommand(new StartCommand(vmTO)); - - vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); - - - work = _workDao.findById(work.getId()); - if (work == null || work.getStep() != Step.Prepare) { - throw new ConcurrentOperationException("Work steps have been changed: " + work); - } - _workDao.updateStep(work, Step.Starting); - - _agentMgr.send(destHostId, cmds); - - _workDao.updateStep(work, Step.Started); - - - StartAnswer startAnswer = cmds.getAnswer(StartAnswer.class); - if (startAnswer != null && startAnswer.getResult()) { - String host_guid = startAnswer.getHost_guid(); - if( host_guid != null ) { - HostVO finalHost = _resourceMgr.findHostByGuid(host_guid); - if (finalHost == null ) { - throw new CloudRuntimeException("Host Guid " + host_guid + " doesn't exist in DB, something wrong here"); - } - destHostId = finalHost.getId(); - } - if (vmGuru.finalizeStart(vmProfile, destHostId, cmds, ctx)) { - if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) { - throw new ConcurrentOperationException("Unable to transition to a new state."); - } - startedVm = vm; - if (s_logger.isDebugEnabled()) { - s_logger.debug("Start completed for VM " + vm); - } - return startedVm; - } else { - if (s_logger.isDebugEnabled()) { - s_logger.info("The guru did not like the answers so stopping " + vm); - } - - StopCommand cmd = new StopCommand(vm.getInstanceName()); - StopAnswer answer = (StopAnswer) _agentMgr.easySend(destHostId, cmd); - if (answer == null || !answer.getResult()) { - s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers")); - _haMgr.scheduleStop(vm, destHostId, WorkType.ForceStop); - throw new ExecutionException("Unable to stop " + vm + " so we are unable to retry the start operation"); - } - if (vmGuru.recreateNeeded(vmProfile, destHostId, cmds, ctx)) { - recreate = true; - } else { - throw new ExecutionException("Unable to start " + vm + " due to error in finalizeStart, not retrying"); - } - } - } - s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + (startAnswer == null ? " no start answer" : startAnswer.getDetails())); - - } catch (OperationTimedoutException e) { - s_logger.debug("Unable to send the start command to host " + dest.getHost()); - if (e.isActive()) { - _haMgr.scheduleStop(vm, destHostId, WorkType.CheckStop); - } - canRetry = false; - throw new AgentUnavailableException("Unable to start " + vm.getHostName(), destHostId, e); - } catch (ResourceUnavailableException e) { - s_logger.info("Unable to contact resource.", e); - if (!avoids.add(e)) { - if (e.getScope() == Volume.class || e.getScope() == Nic.class) { - throw e; - } else { - s_logger.warn("unexpected ResourceUnavailableException : " + e.getScope().getName(), e); - throw e; - } - } - } catch (InsufficientCapacityException e) { - s_logger.info("Insufficient capacity ", e); - if (!avoids.add(e)) { - if (e.getScope() == Volume.class || e.getScope() == Nic.class) { - throw e; - } else { - s_logger.warn("unexpected InsufficientCapacityException : " + e.getScope().getName(), e); - } - } - } catch (Exception e) { - s_logger.error("Failed to start instance " + vm, e); - throw new AgentUnavailableException("Unable to start instance due to " + e.getMessage(), destHostId, e); - } finally { - if (startedVm == null && canRetry) { - Step prevStep = work.getStep(); - _workDao.updateStep(work, Step.Release); - if (prevStep == Step.Started || prevStep == Step.Starting) { - cleanup(vmGuru, vmProfile, work, Event.OperationFailed, false, caller, account); - } else { - //if step is not starting/started, send cleanup command with force=true - cleanup(vmGuru, vmProfile, work, Event.OperationFailed, true, caller, account); - } - } - } - } - } finally { - if (startedVm == null) { - if (canRetry) { - try { - changeState(vm, Event.OperationFailed, null, work, Step.Done); - } catch (NoTransitionException e) { - throw new ConcurrentOperationException(e.getMessage()); - } - } - } - } - - return startedVm; - } - - @Override - public boolean stop(T vm, User user, Account account) throws ResourceUnavailableException { - try { - return advanceStop(vm, false, user, account); - } catch (OperationTimedoutException e) { - throw new AgentUnavailableException("Unable to stop vm because the operation to stop timed out", vm.getHostId(), e); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Unable to stop vm because of a concurrent operation", e); - } - } - - protected boolean sendStop(VirtualMachineGuru guru, VirtualMachineProfile profile, boolean force) { - VMInstanceVO vm = profile.getVirtualMachine(); - StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null); - try { - Answer answer = _agentMgr.send(vm.getHostId(), stop); - if (!answer.getResult()) { - s_logger.debug("Unable to stop VM due to " + answer.getDetails()); - return false; - } - - guru.finalizeStop(profile, (StopAnswer) answer); - } catch (AgentUnavailableException e) { - if (!force) { - return false; - } - } catch (OperationTimedoutException e) { - if (!force) { - return false; - } - } - - return true; - } - - protected boolean cleanup(VirtualMachineGuru guru, VirtualMachineProfile profile, ItWorkVO work, Event event, boolean force, User user, Account account) { - T vm = profile.getVirtualMachine(); - State state = vm.getState(); - s_logger.debug("Cleaning up resources for the vm " + vm + " in " + state + " state"); - if (state == State.Starting) { - Step step = work.getStep(); - if (step == Step.Starting && !force) { - s_logger.warn("Unable to cleanup vm " + vm + "; work state is incorrect: " + step); - return false; - } - - if (step == Step.Started || step == Step.Starting || step == Step.Release) { - if (vm.getHostId() != null) { - if (!sendStop(guru, profile, force)) { - s_logger.warn("Failed to stop vm " + vm + " in " + State.Starting + " state as a part of cleanup process"); - return false; - } - } - } - - if (step != Step.Release && step != Step.Prepare && step != Step.Started && step != Step.Starting) { - s_logger.debug("Cleanup is not needed for vm " + vm + "; work state is incorrect: " + step); - return true; - } - } else if (state == State.Stopping) { - if (vm.getHostId() != null) { - if (!sendStop(guru, profile, force)) { - s_logger.warn("Failed to stop vm " + vm + " in " + State.Stopping + " state as a part of cleanup process"); - return false; - } - } - } else if (state == State.Migrating) { - if (vm.getHostId() != null) { - if (!sendStop(guru, profile, force)) { - s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process"); - return false; - } - } - if (vm.getLastHostId() != null) { - if (!sendStop(guru, profile, force)) { - s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process"); - return false; - } - } - } else if (state == State.Running) { - if (!sendStop(guru, profile, force)) { - s_logger.warn("Failed to stop vm " + vm + " in " + State.Running + " state as a part of cleanup process"); - return false; - } - } - - try { - _networkMgr.release(profile, force); - s_logger.debug("Successfully released network resources for the vm " + vm); - } catch (Exception e) { - s_logger.warn("Unable to release some network resources.", e); - } - - _storageMgr.release(profile); - s_logger.debug("Successfully cleanued up resources for the vm " + vm + " in " + state + " state"); - return true; - } - - @Override - public boolean advanceStop(T vm, boolean forced, User user, Account account) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { - State state = vm.getState(); - if (state == State.Stopped) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is already stopped: " + vm); - } - return true; - } - - if (state == State.Destroyed || state == State.Expunging || state == State.Error) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Stopped called on " + vm + " but the state is " + state); - } - return true; - } - // grab outstanding work item if any - ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found an outstanding work item for this vm " + vm + " with state:" + vm.getState() + ", work id:" + work.getId()); - } - } - Long hostId = vm.getHostId(); - if (hostId == null) { - if (!forced) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("HostId is null but this is not a forced stop, cannot stop vm " + vm + " with state:" + vm.getState()); - } - return false; - } - try { - stateTransitTo(vm, Event.AgentReportStopped, null, null); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - // mark outstanding work item if any as done - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updating work item to Done, id:" + work.getId()); - } - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - return true; - } - - VirtualMachineGuru vmGuru = getVmGuru(vm); - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - - try { - if (!stateTransitTo(vm, Event.StopRequested, vm.getHostId())) { - throw new ConcurrentOperationException("VM is being operated on."); - } - } catch (NoTransitionException e1) { - if (!forced) { - throw new CloudRuntimeException("We cannot stop " + vm + " when it is in state " + vm.getState()); - } - boolean doCleanup = false; - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to transition the state but we're moving on because it's forced stop"); - } - if (state == State.Starting || state == State.Migrating) { - if (work != null) { - doCleanup = true; - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to cleanup VM: " + vm + " ,since outstanding work item is not found"); - } - throw new CloudRuntimeException("Work item not found, We cannot stop " + vm + " when it is in state " + vm.getState()); - } - } else if (state == State.Stopping) { - doCleanup = true; - } - - if (doCleanup) { - if (cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.StopRequested, forced, user, account)) { - try { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updating work item to Done, id:" + work.getId()); - } - return changeState(vm, Event.AgentReportStopped, null, work, Step.Done); - } catch (NoTransitionException e) { - s_logger.warn("Unable to cleanup " + vm); - return false; - } - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Failed to cleanup VM: " + vm); - } - throw new CloudRuntimeException("Failed to cleanup " + vm + " , current state " + vm.getState()); - } - } - } - - if (vm.getState() != State.Stopping) { - throw new CloudRuntimeException("We cannot proceed with stop VM " + vm + " since it is not in 'Stopping' state, current state: " + vm.getState()); - } - - vmGuru.prepareStop(profile); - - StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null); - boolean stopped = false; - StopAnswer answer = null; - try { - answer = (StopAnswer) _agentMgr.send(vm.getHostId(), stop); - stopped = answer.getResult(); - if (!stopped) { - throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); - } - vmGuru.finalizeStop(profile, answer); - - } catch (AgentUnavailableException e) { - } catch (OperationTimedoutException e) { - } finally { - if (!stopped) { - if (!forced) { - s_logger.warn("Unable to stop vm " + vm); - try { - stateTransitTo(vm, Event.OperationFailed, vm.getHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unable to transition the state " + vm); - } - return false; - } else { - s_logger.warn("Unable to actually stop " + vm + " but continue with release because it's a force stop"); - vmGuru.finalizeStop(profile, answer); - } - } - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug(vm + " is stopped on the host. Proceeding to release resource held."); - } - - try { - _networkMgr.release(profile, forced); - s_logger.debug("Successfully released network resources for the vm " + vm); - } catch (Exception e) { - s_logger.warn("Unable to release some network resources.", e); - } - - try { - if (vm.getHypervisorType() != HypervisorType.BareMetal) { - _storageMgr.release(profile); - s_logger.debug("Successfully released storage resources for the vm " + vm); - } - } catch (Exception e) { - s_logger.warn("Unable to release storage resources.", e); - } - - try { - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updating the outstanding work item to Done, id:" + work.getId()); - } - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - - return stateTransitTo(vm, Event.OperationSucceeded, null, null); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - return false; - } - } - - private void setStateMachine() { - _stateMachine = VirtualMachine.State.getStateMachine(); - } - - protected boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException { - vm.setReservationId(reservationId); - return _stateMachine.transitTo(vm, e, new Pair(vm.getHostId(), hostId), _vmDao); - } - - @Override - public boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId) throws NoTransitionException { - State oldState = vm.getState(); - if (oldState == State.Starting) { - if (e == Event.OperationSucceeded) { - vm.setLastHostId(hostId); - } - } else if (oldState == State.Stopping) { - if (e == Event.OperationSucceeded) { - vm.setLastHostId(vm.getHostId()); - } - } - return _stateMachine.transitTo(vm, e, new Pair(vm.getHostId(), hostId), _vmDao); - } - - @Override - public boolean remove(T vm, User user, Account caller) { - return _vmDao.remove(vm.getId()); - } - - @Override - public boolean destroy(T vm, User user, Account caller) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Destroying vm " + vm); - } - if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find vm or vm is destroyed: " + vm); - } - return true; - } - - if (!advanceStop(vm, _forceStop, user, caller)) { - s_logger.debug("Unable to stop " + vm); - return false; - } - - try { - if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); - return false; - } - } catch (NoTransitionException e) { - s_logger.debug(e.getMessage()); - return false; - } - - return true; - } - - protected boolean checkVmOnHost(VirtualMachine vm, long hostId) throws AgentUnavailableException, OperationTimedoutException { - CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) _agentMgr.send(hostId, new CheckVirtualMachineCommand(vm.getInstanceName())); - if (!answer.getResult() || answer.getState() == State.Stopped) { - return false; - } - - return true; - } - - @Override - public T storageMigration(T vm, StoragePool destPool) { - VirtualMachineGuru vmGuru = getVmGuru(vm); - - long vmId = vm.getId(); - vm = vmGuru.findById(vmId); - - try { - stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null); - } catch (NoTransitionException e) { - s_logger.debug("Unable to migrate vm: " + e.toString()); - throw new CloudRuntimeException("Unable to migrate vm: " + e.toString()); - } - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - boolean migrationResult = false; - try { - migrationResult = _storageMgr.StorageMigration(profile, destPool); - - if (migrationResult) { - //if the vm is migrated to different pod in basic mode, need to reallocate ip - - if (vm.getPodIdToDeployIn() != destPool.getPodId()) { - DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destPool.getPodId(), null, null, null, null); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, null, null, null, null); - _networkMgr.reallocate(vmProfile, plan); - } - - //when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool - vm.setLastHostId(null); - vm.setPodId(destPool.getPodId()); - } else { - s_logger.debug("Storage migration failed"); - } - } catch (ConcurrentOperationException e) { - s_logger.debug("Failed to migration: " + e.toString()); - throw new CloudRuntimeException("Failed to migration: " + e.toString()); - } catch (InsufficientVirtualNetworkCapcityException e) { - s_logger.debug("Failed to migration: " + e.toString()); - throw new CloudRuntimeException("Failed to migration: " + e.toString()); - } catch (InsufficientAddressCapacityException e) { - s_logger.debug("Failed to migration: " + e.toString()); - throw new CloudRuntimeException("Failed to migration: " + e.toString()); - } catch (InsufficientCapacityException e) { - s_logger.debug("Failed to migration: " + e.toString()); - throw new CloudRuntimeException("Failed to migration: " + e.toString()); - } finally { - try { - stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null); - } catch (NoTransitionException e) { - s_logger.debug("Failed to change vm state: " + e.toString()); - throw new CloudRuntimeException("Failed to change vm state: " + e.toString()); - } - } - - return vm; - } - - @Override - public T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, - VirtualMachineMigrationException { - s_logger.info("Migrating " + vm + " to " + dest); - - long dstHostId = dest.getHost().getId(); - Host fromHost = _hostDao.findById(srcHostId); - if (fromHost == null) { - s_logger.info("Unable to find the host to migrate from: " + srcHostId); - throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId); - } - - if (fromHost.getClusterId().longValue() != dest.getCluster().getId()) { - s_logger.info("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId()); - throw new CloudRuntimeException("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId()); - } - - VirtualMachineGuru vmGuru = getVmGuru(vm); - - long vmId = vm.getId(); - vm = vmGuru.findById(vmId); - if (vm == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find the vm " + vm); - } - throw new ManagementServerException("Unable to find a virtual machine with id " + vmId); - } - - if (vm.getState() != State.Running) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is not Running, unable to migrate the vm " + vm); - } - throw new VirtualMachineMigrationException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString()); - } - - short alertType = AlertManager.ALERT_TYPE_USERVM_MIGRATE; - if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) { - alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE; - } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) { - alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY_MIGRATE; - } - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - _networkMgr.prepareNicForMigration(profile, dest); - _storageMgr.prepareForMigration(profile, dest); - - VirtualMachineTO to = toVmTO(profile); - PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to); - - ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId()); - work.setStep(Step.Prepare); - work.setResourceType(ItWorkVO.ResourceType.Host); - work.setResourceId(dstHostId); - work = _workDao.persist(work); - - PrepareForMigrationAnswer pfma = null; - try { - pfma = (PrepareForMigrationAnswer) _agentMgr.send(dstHostId, pfmc); - if (!pfma.getResult()) { - String msg = "Unable to prepare for migration due to " + pfma.getDetails(); - pfma = null; - throw new AgentUnavailableException(msg, dstHostId); - } - } catch (OperationTimedoutException e1) { - throw new AgentUnavailableException("Operation timed out", dstHostId); - } finally { - if (pfma == null) { - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - } - - vm.setLastHostId(srcHostId); - try { - if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) { - s_logger.info("Migration cancelled because state has changed: " + vm); - throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm); - } - } catch (NoTransitionException e1) { - s_logger.info("Migration cancelled because " + e1.getMessage()); - throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage()); - } - - boolean migrated = false; - try { - boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); - MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows); - mc.setHostGuid(dest.getHost().getGuid()); - - try { - MigrateAnswer ma = (MigrateAnswer) _agentMgr.send(vm.getLastHostId(), mc); - if (!ma.getResult()) { - s_logger.error("Unable to migrate due to " + ma.getDetails()); - return null; - } - } catch (OperationTimedoutException e) { - if (e.isActive()) { - s_logger.warn("Active migration command so scheduling a restart for " + vm); - _haMgr.scheduleRestart(vm, true); - } - throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId); - } - - try { - if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) { - throw new ConcurrentOperationException("Unable to change the state for " + vm); - } - } catch (NoTransitionException e1) { - throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage()); - } - - try { - if (!checkVmOnHost(vm, dstHostId)) { - s_logger.error("Unable to complete migration for " + vm); - try { - _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null); - } catch (AgentUnavailableException e) { - s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId); - } - cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); - return null; - } - } catch (OperationTimedoutException e) { - } - - migrated = true; - return vm; - } finally { - if (!migrated) { - s_logger.info("Migration was unsuccessful. Cleaning up: " + vm); - - _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(), "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " - + dest.getDataCenter().getName() + " and pod " + dest.getPod().getName(), "Migrate Command failed. Please check logs."); - try { - _agentMgr.send(dstHostId, new Commands(cleanup(vm.getInstanceName())), null); - } catch (AgentUnavailableException ae) { - s_logger.info("Looks like the destination Host is unavailable for cleanup"); - } - - try { - stateTransitTo(vm, Event.OperationFailed, srcHostId); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - } - - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - } - - @Override - public VirtualMachineTO toVmTO(VirtualMachineProfile profile) { - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(profile.getVirtualMachine().getHypervisorType()); - VirtualMachineTO to = hvGuru.implement(profile); - return to; - } - - protected void cancelWorkItems(long nodeId) { - GlobalLock scanLock = GlobalLock.getInternLock("vmmgr.cancel.workitem"); - - try { - if (scanLock.lock(3)) { - try { - List works = _workDao.listWorkInProgressFor(nodeId); - for (ItWorkVO work : works) { - s_logger.info("Handling unfinished work item: " + work); - try { - VMInstanceVO vm = _vmDao.findById(work.getInstanceId()); - if (vm != null) { - if (work.getType() == State.Starting) { - _haMgr.scheduleRestart(vm, true); - work.setManagementServerId(_nodeId); - _workDao.update(work.getId(), work); - } else if (work.getType() == State.Stopping) { - _haMgr.scheduleStop(vm, vm.getHostId(), WorkType.CheckStop); - work.setManagementServerId(_nodeId); - _workDao.update(work.getId(), work); - } else if (work.getType() == State.Migrating) { - _haMgr.scheduleMigration(vm); - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - } - } catch (Exception e) { - s_logger.error("Error while handling " + work, e); - } - } - } finally { - scanLock.unlock(); - } - } - } finally { - scanLock.releaseRef(); - } - } - - @Override - public boolean migrateAway(VirtualMachine.Type vmType, long vmId, long srcHostId) throws InsufficientServerCapacityException, VirtualMachineMigrationException { - VirtualMachineGuru vmGuru = _vmGurus.get(vmType); - VMInstanceVO vm = vmGuru.findById(vmId); - if (vm == null) { - s_logger.debug("Unable to find a VM for " + vmId); - return true; - } - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - - Long hostId = vm.getHostId(); - if (hostId == null) { - s_logger.debug("Unable to migrate because the VM doesn't have a host id: " + vm); - return true; - } - - Host host = _hostDao.findById(hostId); - - DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null); - ExcludeList excludes = new ExcludeList(); - excludes.addHost(hostId); - - DeployDestination dest = null; - while (true) { - for (DeploymentPlanner planner : _planners) { - if (planner.canHandle(profile, plan, excludes)) { - dest = planner.plan(profile, plan, excludes); - } else { - continue; - } - - if (dest != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Planner " + planner + " found " + dest + " for migrating to."); - } - break; - } - if (s_logger.isDebugEnabled()) { - s_logger.debug("Planner " + planner + " was unable to find anything."); - } - } - - if (dest == null) { - throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", host.getClusterId()); - } - - excludes.addHost(dest.getHost().getId()); - VMInstanceVO vmInstance = null; - try { - vmInstance = migrate(vm, srcHostId, dest); - } catch (ResourceUnavailableException e) { - s_logger.debug("Unable to migrate to unavailable " + dest); - } catch (ConcurrentOperationException e) { - s_logger.debug("Unable to migrate VM due to: " + e.getMessage()); - } catch (ManagementServerException e) { - s_logger.debug("Unable to migrate VM: " + e.getMessage()); - } catch (VirtualMachineMigrationException e) { - s_logger.debug("Got VirtualMachineMigrationException, Unable to migrate: " + e.getMessage()); - if (vm.getState() == State.Starting) { - s_logger.debug("VM seems to be still Starting, we should retry migration later"); - throw e; - } else { - s_logger.debug("Unable to migrate VM, VM is not in Running or even Starting state, current state: " + vm.getState().toString()); - } - } - if (vmInstance != null) { - return true; - } - try { - boolean result = advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); - return result; - } catch (ResourceUnavailableException e) { - s_logger.debug("Unable to stop VM due to " + e.getMessage()); - } catch (ConcurrentOperationException e) { - s_logger.debug("Unable to stop VM due to " + e.getMessage()); - } catch (OperationTimedoutException e) { - s_logger.debug("Unable to stop VM due to " + e.getMessage()); - } - return false; - } - } - - protected class CleanupTask implements Runnable { - @Override - public void run() { - s_logger.trace("VM Operation Thread Running"); - try { - _workDao.cleanup(_cleanupWait); - } catch (Exception e) { - s_logger.error("VM Operations failed due to ", e); - } - } - } - - @Override - public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) { - Enumeration en = _hostAllocators.enumeration(); - boolean isMachineUpgradable = true; - while (isMachineUpgradable && en.hasMoreElements()) { - final HostAllocator allocator = en.nextElement(); - isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); - } - - return isMachineUpgradable; - } - - @Override - public T reboot(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException { - try { - return advanceReboot(vm, params, caller, account); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException("Unable to reboot a VM due to concurrent operation", e); - } - } - - @Override - public T advanceReboot(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException { - T rebootedVm = null; - - DataCenter dc = _configMgr.getZone(vm.getDataCenterIdToDeployIn()); - Host host = _hostDao.findById(vm.getHostId()); - Cluster cluster = null; - if (host != null) { - cluster = _configMgr.getCluster(host.getClusterId()); - } - HostPodVO pod = _configMgr.getPod(host.getPodId()); - DeployDestination dest = new DeployDestination(dc, pod, cluster, host); - - try { - - Commands cmds = new Commands(OnError.Stop); - cmds.addCommand(new RebootCommand(vm.getInstanceName())); - _agentMgr.send(host.getId(), cmds); - - Answer rebootAnswer = cmds.getAnswer(RebootAnswer.class); - if (rebootAnswer != null && rebootAnswer.getResult()) { - rebootedVm = vm; - return rebootedVm; - } - s_logger.info("Unable to reboot VM " + vm + " on " + dest.getHost() + " due to " + (rebootAnswer == null ? " no reboot answer" : rebootAnswer.getDetails())); - } catch (OperationTimedoutException e) { - s_logger.warn("Unable to send the reboot command to host " + dest.getHost() + " for the vm " + vm + " due to operation timeout", e); - throw new CloudRuntimeException("Failed to reboot the vm on host " + dest.getHost()); - } - - return rebootedVm; - } - - @Override - public VMInstanceVO findByIdAndType(VirtualMachine.Type type, long vmId) { - VirtualMachineGuru guru = _vmGurus.get(type); - return guru.findById(vmId); - } - - public Command cleanup(String vmName) { - return new StopCommand(vmName); - } - - public Commands fullHostSync(final long hostId, StartupRoutingCommand startup) { - Commands commands = new Commands(OnError.Continue); - - Map infos = convertToInfos(startup); - - final List vms = _vmDao.listByHostId(hostId); - s_logger.debug("Found " + vms.size() + " VMs for host " + hostId); - for (VMInstanceVO vm : vms) { - AgentVmInfo info = infos.remove(vm.getId()); - VMInstanceVO castedVm = null; - if (info == null) { - info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); - } - castedVm = info.guru.findById(vm.getId()); - - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); - Command command = compareState(hostId, castedVm, info, true, hvGuru.trackVmHostChange()); - if (command != null) { - commands.addCommand(command); - } - } - - for (final AgentVmInfo left : infos.values()) { - boolean found = false; - for (VirtualMachineGuru vmGuru : _vmGurus.values()) { - VMInstanceVO vm = vmGuru.findByName(left.name); - if (vm != null) { - found = true; - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); - if(hvGuru.trackVmHostChange()) { - Command command = compareState(hostId, vm, left, true, true); - if (command != null) { - commands.addCommand(command); - } - } else { - s_logger.warn("Stopping a VM, VM " + left.name + " migrate from Host " + vm.getHostId() + " to Host " + hostId ); - commands.addCommand(cleanup(left.name)); - } - break; - } - } - if ( ! found ) { - s_logger.warn("Stopping a VM that we have no record of : " + left.name); - commands.addCommand(cleanup(left.name)); - } - } - - return commands; - } - - public Commands deltaHostSync(long hostId, Map newStates) { - Map states = convertDeltaToInfos(newStates); - Commands commands = new Commands(OnError.Continue); - - for (Map.Entry entry : states.entrySet()) { - AgentVmInfo info = entry.getValue(); - - VMInstanceVO vm = info.vm; - - Command command = null; - if (vm != null) { - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); - command = compareState(hostId, vm, info, false, hvGuru.trackVmHostChange()); - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cleaning up a VM that is no longer found: " + info.name); - } - command = cleanup(info.name); - } - - if (command != null) { - commands.addCommand(command); - } - } - - return commands; - } - - - - public void deltaSync(Map> newStates) { - Map states = convertToInfos(newStates); - - for (Map.Entry entry : states.entrySet()) { - AgentVmInfo info = entry.getValue(); - VMInstanceVO vm = info.vm; - Command command = null; - if (vm != null) { - Host host = _resourceMgr.findHostByGuid(info.getHostUuid()); - long hId = host.getId(); - - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); - command = compareState(hId, vm, info, false, hvGuru.trackVmHostChange()); - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Cleaning up a VM that is no longer found : " + info.name); - } - command = cleanup(info.name); - } - if (command != null){ - try { - Host host = _resourceMgr.findHostByGuid(info.getHostUuid()); - if (host != null){ - Answer answer = _agentMgr.send(host.getId(), cleanup(info.name)); - if (!answer.getResult()) { - s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); - } - } - } catch (Exception e) { - s_logger.warn("Unable to stop a VM due to " + e.getMessage()); - } - } - } - } - - - public void fullSync(final long clusterId, Map> newStates) { - if (newStates==null)return; - Map infos = convertToInfos(newStates); - Set set_vms = Collections.synchronizedSet(new HashSet()); - set_vms.addAll(_vmDao.listByClusterId(clusterId)); - set_vms.addAll(_vmDao.listLHByClusterId(clusterId)); - - for (VMInstanceVO vm : set_vms) { - if (vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) continue; - AgentVmInfo info = infos.remove(vm.getId()); - VMInstanceVO castedVm = null; - if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting)) - || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) - { - s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); - info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); - - // Bug 13850- grab outstanding work item if any for this VM state so that we mark it as DONE after we change VM state, else it will remain pending - ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found an outstanding work item for this vm " + vm + " in state:" + vm.getState() + ", work id:" + work.getId()); - } - } - vm.setState(State.Running); // set it as running and let HA take care of it - _vmDao.persist(vm); - - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updating outstanding work item to Done, id:" + work.getId()); - } - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - - castedVm = info.guru.findById(vm.getId()); - try { - Host host = _hostDao.findByGuid(info.getHostUuid()); - long hostId = host == null ? (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()) : host.getId(); - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); - Command command = compareState(hostId, castedVm, info, true, hvGuru.trackVmHostChange()); - if (command != null){ - Answer answer = _agentMgr.send(hostId, command); - if (!answer.getResult()) { - s_logger.warn("Failed to update state of the VM due to " + answer.getDetails()); - } - } - } catch (Exception e) { - s_logger.warn("Unable to update state of the VM due to exception " + e.getMessage()); - e.printStackTrace(); - } - } - else if (info != null && (vm.getState() == State.Stopped || vm.getState() == State.Stopping)) { - Host host = _hostDao.findByGuid(info.getHostUuid()); - if (host != null){ - s_logger.warn("Stopping a VM which is stopped/stopping " + info.name); - vm.setState(State.Stopped); // set it as stop and clear it from host - vm.setHostId(null); - _vmDao.persist(vm); - try { - Answer answer = _agentMgr.send(host.getId(), cleanup(info.name)); - if (!answer.getResult()) { - s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); - } - } - catch (Exception e) { - s_logger.warn("Unable to stop a VM due to " + e.getMessage()); - } - } - } - else - // host id can change - if (info != null && vm.getState() == State.Running){ - // check for host id changes - Host host = _hostDao.findByGuid(info.getHostUuid()); - if (host != null && (vm.getHostId() == null || host.getId() != vm.getHostId())){ - s_logger.info("Found vm " + vm.getInstanceName() + " with inconsistent host in db, new host is " + host.getId()); - try { - stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, host.getId()); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - } - } - /* else if(info == null && vm.getState() == State.Stopping) { //Handling CS-13376 - s_logger.warn("Marking the VM as Stopped as it was still stopping on the CS" +vm.getName()); - vm.setState(State.Stopped); // Setting the VM as stopped on the DB and clearing it from the host - vm.setLastHostId(vm.getHostId()); - vm.setHostId(null); - _vmDao.persist(vm); - }*/ - } - - for (final AgentVmInfo left : infos.values()) { - if (VirtualMachineName.isValidVmName(left.name)) continue; // if the vm follows cloudstack naming ignore it for stopping - try { - Host host = _hostDao.findByGuid(left.getHostUuid()); - if (host != null){ - s_logger.warn("Stopping a VM which we do not have any record of " + left.name); - Answer answer = _agentMgr.send(host.getId(), cleanup(left.name)); - if (!answer.getResult()) { - s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); - } - } - } catch (Exception e) { - s_logger.warn("Unable to stop a VM due to " + e.getMessage()); - } - } - - } - - - - protected Map convertToInfos(final Map> newStates) { - final HashMap map = new HashMap(); - if (newStates == null) { - return map; - } - Collection> vmGurus = _vmGurus.values(); - boolean is_alien_vm = true; - long alien_vm_count = -1; - for (Map.Entry> entry : newStates.entrySet()) { - is_alien_vm = true; - for (VirtualMachineGuru vmGuru : vmGurus) { - String name = entry.getKey(); - VMInstanceVO vm = vmGuru.findByName(name); - if (vm != null) { - map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue().second(), entry.getValue().first())); - is_alien_vm = false; - break; - } - Long id = vmGuru.convertToId(name); - if (id != null) { - map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null, entry.getValue().second(), entry.getValue().first())); - is_alien_vm = false; - break; - } - } - // alien VMs - if (is_alien_vm){ - map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, null, entry.getValue().second(), entry.getValue().first())); - s_logger.warn("Found an alien VM " + entry.getKey()); - } - } - return map; - } - - protected Map convertToInfos(StartupRoutingCommand cmd) { - final Map states = cmd.getVmStates(); - final HashMap map = new HashMap(); - if (states == null) { - return map; - } - Collection> vmGurus = _vmGurus.values(); - - for (Map.Entry entry : states.entrySet()) { - for (VirtualMachineGuru vmGuru : vmGurus) { - String name = entry.getKey(); - VMInstanceVO vm = vmGuru.findByName(name); - if (vm != null) { - map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue().getState(), entry.getValue().getHost() )); - break; - } - Long id = vmGuru.convertToId(name); - if (id != null) { - map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null,entry.getValue().getState(), entry.getValue().getHost() )); - break; - } - } - } - - return map; - } - - protected Map convertDeltaToInfos(final Map states) { - final HashMap map = new HashMap(); - - if (states == null) { - return map; - } - - Collection> vmGurus = _vmGurus.values(); - - for (Map.Entry entry : states.entrySet()) { - for (VirtualMachineGuru vmGuru : vmGurus) { - String name = entry.getKey(); - - VMInstanceVO vm = vmGuru.findByName(name); - - if (vm != null) { - map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue())); - break; - } - - Long id = vmGuru.convertToId(name); - if (id != null) { - map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null,entry.getValue())); - break; - } - } - } - - return map; - } - - - - /** - * compareState does as its name suggests and compares the states between - * management server and agent. It returns whether something should be - * cleaned up - * - */ - protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean trackExternalChange) { - State agentState = info.state; - final String agentName = info.name; - final State serverState = vm.getState(); - final String serverName = vm.getInstanceName(); - - Command command = null; - s_logger.debug("VM " + serverName + ": cs state = " + serverState + " and realState = " + agentState); - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM " + serverName + ": cs state = " + serverState + " and realState = " + agentState); - } - - if (agentState == State.Error) { - agentState = State.Stopped; - - short alertType = AlertManager.ALERT_TYPE_USERVM; - if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) { - alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER; - } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) { - alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY; - } else if (VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) { - alertType = AlertManager.ALERT_TYPE_SSVM; - } - - HostPodVO podVO = _podDao.findById(vm.getPodIdToDeployIn()); - DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); - HostVO hostVO = _hostDao.findById(vm.getHostId()); - - String hostDesc = "name: " + hostVO.getName() + " (id:" + hostVO.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); - _alertMgr.sendAlert(alertType, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), "VM (name: " + vm.getInstanceName() + ", id: " + vm.getId() + ") stopped on host " + hostDesc - + " due to storage failure", "Virtual Machine " + vm.getInstanceName() + " (id: " + vm.getId() + ") running on host [" + vm.getHostId() + "] stopped due to storage failure."); - } - - if (trackExternalChange) { - if (serverState == State.Starting) { - if (vm.getHostId() != null && vm.getHostId() != hostId) { - s_logger.info("CloudStack is starting VM on host " + vm.getHostId() + ", but status report comes from a different host " + hostId + ", skip status sync for vm: " - + vm.getInstanceName()); - return null; - } - } - if (vm.getHostId() == null || hostId != vm.getHostId()) { - try { - stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId); - } catch (NoTransitionException e) { - } - } - } - - // during VM migration time, don't sync state will agent status update - if (serverState == State.Migrating) { - s_logger.debug("Skipping vm in migrating state: " + vm); - return null; - } - - if (trackExternalChange) { - if (serverState == State.Starting) { - if (vm.getHostId() != null && vm.getHostId() != hostId) { - s_logger.info("CloudStack is starting VM on host " + vm.getHostId() + ", but status report comes from a different host " + hostId + ", skip status sync for vm: " - + vm.getInstanceName()); - return null; - } - } - - if (serverState == State.Running) { - try { - // - // we had a bug that sometimes VM may be at Running State - // but host_id is null, we will cover it here. - // means that when CloudStack DB lost of host information, - // we will heal it with the info reported from host - // - if (vm.getHostId() == null || hostId != vm.getHostId()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("detected host change when VM " + vm + " is at running state, VM could be live-migrated externally from host " + vm.getHostId() + " to host " + hostId); - } - - stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId); - } - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - } - } - - if (agentState == serverState) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Both states are " + agentState + " for " + vm); - } - assert (agentState == State.Stopped || agentState == State.Running) : "If the states we send up is changed, this must be changed."; - if (agentState == State.Running) { - try { - stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, hostId); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - // FIXME: What if someone comes in and sets it to stopping? Then - // what? - return null; - } - - s_logger.debug("State matches but the agent said stopped so let's send a cleanup command anyways."); - return cleanup(agentName); - } - - if (agentState == State.Shutdowned) { - if (serverState == State.Running || serverState == State.Starting || serverState == State.Stopping) { - try { - advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); - } catch (AgentUnavailableException e) { - assert (false) : "How do we hit this with forced on?"; - return null; - } catch (OperationTimedoutException e) { - assert (false) : "How do we hit this with forced on?"; - return null; - } catch (ConcurrentOperationException e) { - assert (false) : "How do we hit this with forced on?"; - return null; - } - } else { - s_logger.debug("Sending cleanup to a shutdowned vm: " + agentName); - command = cleanup(agentName); - } - } else if (agentState == State.Stopped) { - // This state means the VM on the agent was detected previously - // and now is gone. This is slightly different than if the VM - // was never completed but we still send down a Stop Command - // to ensure there's cleanup. - if (serverState == State.Running) { - // Our records showed that it should be running so let's restart - // it. - _haMgr.scheduleRestart(vm, false); - } else if (serverState == State.Stopping) { - _haMgr.scheduleStop(vm, hostId, WorkType.ForceStop); - s_logger.debug("Scheduling a check stop for VM in stopping mode: " + vm); - } else if (serverState == State.Starting) { - s_logger.debug("Ignoring VM in starting mode: " + vm.getInstanceName()); - _haMgr.scheduleRestart(vm, false); - } - command = cleanup(agentName); - } else if (agentState == State.Running) { - if (serverState == State.Starting) { - if (fullSync) { - try { - ensureVmRunningContext(hostId, vm, Event.AgentReportRunning); - } catch (OperationTimedoutException e) { - s_logger.error("Exception during update for running vm: " + vm, e); - return null; - } catch (ResourceUnavailableException e) { - s_logger.error("Exception during update for running vm: " + vm, e); - return null; - }catch (InsufficientAddressCapacityException e) { - s_logger.error("Exception during update for running vm: " + vm, e); - return null; - }catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - } - } else if (serverState == State.Stopping) { - s_logger.debug("Scheduling a stop command for " + vm); - _haMgr.scheduleStop(vm, hostId, WorkType.Stop); - } else { - s_logger.debug("server VM state " + serverState + " does not meet expectation of a running VM report from agent"); - - // just be careful not to stop VM for things we don't handle - // command = cleanup(agentName); - } - } - return command; - } - - private void ensureVmRunningContext(long hostId, VMInstanceVO vm, Event cause) throws OperationTimedoutException, ResourceUnavailableException, NoTransitionException, InsufficientAddressCapacityException { - VirtualMachineGuru vmGuru = getVmGuru(vm); - - s_logger.debug("VM state is starting on full sync so updating it to running"); - vm = findByIdAndType(vm.getType(), vm.getId()); - - // grab outstanding work item if any - ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found an outstanding work item for this vm " + vm + " in state:" + vm.getState() + ", work id:" + work.getId()); - } - } - - try { - stateTransitTo(vm, cause, hostId); - } catch (NoTransitionException e1) { - s_logger.warn(e1.getMessage()); - } - - s_logger.debug("VM's " + vm + " state is starting on full sync so updating it to Running"); - vm = vmGuru.findById(vm.getId()); // this should ensure vm has the most - // up to date info - - VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); - List nics = _nicsDao.listByVmId(profile.getId()); - for (NicVO nic : nics) { - Network network = _networkMgr.getNetwork(nic.getNetworkId()); - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, - _networkMgr.isSecurityGroupSupportedInNetwork(network), _networkMgr.getNetworkTag(profile.getHypervisorType(), network)); - profile.addNic(nicProfile); - } - - Commands cmds = new Commands(OnError.Stop); - s_logger.debug("Finalizing commands that need to be send to complete Start process for the vm " + vm); - - if (vmGuru.finalizeCommandsOnStart(cmds, profile)) { - if (cmds.size() != 0) { - _agentMgr.send(vm.getHostId(), cmds); - } - - if (vmGuru.finalizeStart(profile, vm.getHostId(), cmds, null)) { - stateTransitTo(vm, cause, vm.getHostId()); - } else { - s_logger.error("Unable to finish finialization for running vm: " + vm); - } - } else { - s_logger.error("Unable to finalize commands on start for vm: " + vm); - } - - if (work != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updating outstanding work item to Done, id:" + work.getId()); - } - work.setStep(Step.Done); - _workDao.update(work.getId(), work); - } - } - - @Override - public boolean isRecurring() { - return true; - } - - @Override - public boolean processAnswers(long agentId, long seq, Answer[] answers) { - for (final Answer answer : answers) { - if (answer instanceof ClusterSyncAnswer) { - ClusterSyncAnswer hs = (ClusterSyncAnswer) answer; - if (!hs.isExceuted()){ - deltaSync(hs.getNewStates()); - hs.setExecuted(); - } - } - } - return true; - } - - @Override - public boolean processTimeout(long agentId, long seq) { - return true; - } - - @Override - public int getTimeout() { - return -1; - } - - @Override - public boolean processCommands(long agentId, long seq, Command[] cmds) { - boolean processed = false; - for (Command cmd : cmds) { - if (cmd instanceof PingRoutingCommand) { - PingRoutingCommand ping = (PingRoutingCommand) cmd; - if (ping.getNewStates() != null && ping.getNewStates().size() > 0) { - Commands commands = deltaHostSync(agentId, ping.getNewStates()); - if (commands.size() > 0) { - try { - _agentMgr.send(agentId, commands, this); - } catch (final AgentUnavailableException e) { - s_logger.warn("Agent is now unavailable", e); - } - } - } - processed = true; - } - } - return processed; - } - - @Override - public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { - return null; - } - - @Override - public boolean processDisconnect(long agentId, Status state) { - return true; - } - - @Override - public void processConnect(HostVO agent, StartupCommand cmd, boolean forRebalance) throws ConnectionException { - if (!(cmd instanceof StartupRoutingCommand)) { - return; - } - - if (forRebalance) { - s_logger.debug("Not processing listener " + this + " as connect happens on rebalance process"); - return; - } - - if (forRebalance) { - s_logger.debug("Not processing listener " + this + " as connect happens on rebalance process"); - return; - } - - Long clusterId = agent.getClusterId(); - long agentId = agent.getId(); - if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen - StartupRoutingCommand startup = (StartupRoutingCommand) cmd; - HashMap> allStates = startup.getClusterVMStateChanges(); - if (allStates != null){ - this.fullSync(clusterId, allStates); - } - - // initiate the cron job - ClusterSyncCommand syncCmd = new ClusterSyncCommand(Integer.parseInt(Config.ClusterDeltaSyncInterval.getDefaultValue()), clusterId); - try { - long seq_no = _agentMgr.send(agentId, new Commands(syncCmd), this); - s_logger.debug("Cluster VM sync started with jobid " + seq_no); - } catch (AgentUnavailableException e) { - s_logger.fatal("The Cluster VM sync process failed for cluster id " + clusterId + " with ", e); - } - } - else { // for others KVM and VMWare - StartupRoutingCommand startup = (StartupRoutingCommand) cmd; - Commands commands = fullHostSync(agentId, startup); - - if (commands.size() > 0) { - s_logger.debug("Sending clean commands to the agent"); - - try { - boolean error = false; - Answer[] answers = _agentMgr.send(agentId, commands); - for (Answer answer : answers) { - if (!answer.getResult()) { - s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); - error = true; - } - } - if (error) { - throw new ConnectionException(true, "Unable to stop VMs"); - } - } catch (final AgentUnavailableException e) { - s_logger.warn("Agent is unavailable now", e); - throw new ConnectionException(true, "Unable to sync", e); - } catch (final OperationTimedoutException e) { - s_logger.warn("Agent is unavailable now", e); - throw new ConnectionException(true, "Unable to sync", e); - } - } - - } - } - - protected class TransitionTask implements Runnable { - @Override - public void run() { - GlobalLock lock = GlobalLock.getInternLock("TransitionChecking"); - if (lock == null) { - s_logger.debug("Couldn't get the global lock"); - return; - } - - if (!lock.lock(30)) { - s_logger.debug("Couldn't lock the db"); - return; - } - try { - lock.addRef(); - List instances = _vmDao.findVMInTransition(new Date(new Date().getTime() - (_operationTimeout * 1000)), State.Starting, State.Stopping); - for (VMInstanceVO instance : instances) { - State state = instance.getState(); - if (state == State.Stopping) { - _haMgr.scheduleStop(instance, instance.getHostId(), WorkType.CheckStop); - } else if (state == State.Starting) { - _haMgr.scheduleRestart(instance, true); - } - } - } catch (Exception e) { - s_logger.warn("Caught the following exception on transition checking", e); - } finally { - StackMaid.current().exitCleanup(); - lock.unlock(); - } - } - } - - protected class AgentVmInfo { - public String name; - public State state; - public String hostUuid; - public VMInstanceVO vm; - public VirtualMachineGuru guru; - - @SuppressWarnings("unchecked") - public AgentVmInfo(String name, VirtualMachineGuru guru, VMInstanceVO vm, State state, String host) { - this.name = name; - this.state = state; - this.vm = vm; - this.guru = (VirtualMachineGuru) guru; - this.hostUuid = host; - } - - public AgentVmInfo(String name, VirtualMachineGuru guru, VMInstanceVO vm, State state) { - this(name, guru, vm, state, null); - } - - public String getHostUuid() { - return hostUuid; - } - } - - @Override - public VMInstanceVO findById(long vmId) { - return _vmDao.findById(vmId); - } - - @Override - public void checkIfCanUpgrade(VirtualMachine vmInstance, long newServiceOfferingId) { - ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId); - if (newServiceOffering == null) { - throw new InvalidParameterValueException("Unable to find a service offering by id", null); - } - - // Check that the VM is stopped - if (!vmInstance.getState().equals(State.Stopped)) { - s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); - throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + - "in state " + vmInstance.getState() - + "; make sure the virtual machine is stopped and not in an error state before upgrading.", null); - } - - // Check if the service offering being upgraded to is what the VM is already running with - if (vmInstance.getServiceOfferingId() == newServiceOffering.getId()) { - if (s_logger.isInfoEnabled()) { - s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested " + - "service offering (" + newServiceOffering.getName() + ")"); - } - - throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already " + - "has the requested service offering (" + newServiceOffering.getName() + ")", null); - } - - ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); - - // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering - // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore. - /* - * if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { String errorMsg = - * "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); errorMsg += - * ". Please select a service offering with the same guest IP type as the VM's current service offering (" + - * currentServiceOffering.getGuestIpType() + ")."; throw new InvalidParameterValueException(errorMsg); } - */ - - // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service - // offering - if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() - + ", cannot switch between local storage and shared storage service offerings. Current offering " + - "useLocalStorage=" + currentServiceOffering.getUseLocalStorage() - + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage(), null); - } - - // if vm is a system vm, check if it is a system service offering, if yes return with error as it cannot be used for user vms - if (currentServiceOffering.getSystemUse() != newServiceOffering.getSystemUse()) { - throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering", null); - } - - // Check that there are enough resources to upgrade the service offering - if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + - "for an offering of " + newServiceOffering.getCpu() + " cpu(s) at " - + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory", null); - } - - // Check that the service offering being upgraded to has all the tags of the current service offering - List currentTags = _configMgr.csvTagsToList(currentServiceOffering.getTags()); - List newTags = _configMgr.csvTagsToList(newServiceOffering.getTags()); - if (!newTags.containsAll(currentTags)) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine; the new service offering " + - "does not have all the tags of the " - + "current service offering. Current service offering tags: " + currentTags + "; " + "new service " + - "offering tags: " + newTags, null); - } - } - - @Override - public boolean upgradeVmDb(long vmId, long serviceOfferingId) { - VMInstanceVO vmForUpdate = _vmDao.createForUpdate(); - vmForUpdate.setServiceOfferingId(serviceOfferingId); - ServiceOffering newSvcOff = _configMgr.getServiceOffering(serviceOfferingId); - vmForUpdate.setHaEnabled(newSvcOff.getOfferHA()); - vmForUpdate.setLimitCpuUse(newSvcOff.getLimitCpuUse()); - vmForUpdate.setServiceOfferingId(newSvcOff.getId()); - return _vmDao.update(vmId, vmForUpdate); - } - - @Override - public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { - - s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); - VMInstanceVO vmVO = _vmDao.findById(vm.getId()); - ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), - _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, - null, null, null); - - DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); - DeployDestination dest = new DeployDestination(dc, null, null, host); - - //check vm state - if (vm.getState() == State.Running) { - //1) allocate and prepare nic - NicProfile nic = _networkMgr.createNicForVm(network, requested, context, vmProfile, true); - - //2) Convert vmProfile to vmTO - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); - VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - - //3) Convert nicProfile to NicTO - NicTO nicTO = toNicTO(nic, vmProfile.getVirtualMachine().getHypervisorType()); - - //4) plug the nic to the vm - VirtualMachineGuru vmGuru = getVmGuru(vmVO); - - s_logger.debug("Plugging nic for vm " + vm + " in network " + network); - if (vmGuru.plugNic(network, nicTO, vmTO, context, dest)) { - s_logger.debug("Nic is plugged successfully for vm " + vm + " in network " + network + ". Vm is a part of network now"); - return nic; - } else { - s_logger.warn("Failed to plug nic to the vm " + vm + " in network " + network); - return null; - } - } else if (vm.getState() == State.Stopped) { - //1) allocate nic - return _networkMgr.createNicForVm(network, requested, context, vmProfile, false); - } else { - s_logger.warn("Unable to add vm " + vm + " to network " + network); - throw new ResourceUnavailableException("Unable to add vm " + vm + " to network, is not in the right state", - DataCenter.class, vm.getDataCenterIdToDeployIn()); - } - } - - - @Override - public NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType) { - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType); - - NicTO nicTO = hvGuru.toNicTO(nic); - return nicTO; - } - - @Override - public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { - VMInstanceVO vmVO = _vmDao.findById(vm.getId()); - ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), - _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, - null, null, null); - - DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); - DeployDestination dest = new DeployDestination(dc, null, null, host); - VirtualMachineGuru vmGuru = getVmGuru(vmVO); - HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); - VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - - Nic nic = null; - - if (broadcastUri != null) { - nic = _nicsDao.findByNetworkIdInstanceIdAndBroadcastUri(network.getId(), vm.getId(), broadcastUri.toString()); - } else { - nic = _networkMgr.getNicInNetwork(vm.getId(), network.getId()); - } - - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), - _networkMgr.getNetworkRate(network.getId(), vm.getId()), - _networkMgr.isSecurityGroupSupportedInNetwork(network), - _networkMgr.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); - - //1) Unplug the nic - NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); - s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); - boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); - if (result) { - s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); - } else { - s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); - return false; - } - - //2) Release the nic - _networkMgr.releaseNic(vmProfile, nic); - s_logger.debug("Successfully released nic " + nic + "for vm " + vm); - - //3) Remove the nic - _networkMgr.removeNic(vmProfile, nic); - return result; - } - -} +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by 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. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.vm; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.ClusterSyncAnswer; +import com.cloud.agent.api.ClusterSyncCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.PingRoutingCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.StartupRoutingCommand.VmState; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.manager.Commands; +import com.cloud.agent.manager.allocator.HostAllocator; +import com.cloud.alert.AlertManager; +import com.cloud.capacity.CapacityManager; +import com.cloud.cluster.ClusterManager; +import com.cloud.cluster.StackMaid; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.consoleproxy.ConsoleProxyManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ConnectionException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.ha.HighAvailabilityManager; +import com.cloud.ha.HighAvailabilityManager.WorkType; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.HypervisorGuru; +import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.network.Network; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.offering.ServiceOffering; +import com.cloud.org.Cluster; +import com.cloud.resource.ResourceManager; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; +import com.cloud.storage.Volume.Type; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.StoragePoolDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.User; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.Journal; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.ItWorkVO.Step; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.ConsoleProxyDao; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.SecondaryStorageVmDao; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = VirtualMachineManager.class) +public class VirtualMachineManagerImpl implements VirtualMachineManager, Listener { + private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class); + + String _name; + @Inject + protected StorageManager _storageMgr; + @Inject + protected NetworkManager _networkMgr; + @Inject + protected AgentManager _agentMgr; + @Inject + protected VMInstanceDao _vmDao; + @Inject + protected ServiceOfferingDao _offeringDao; + @Inject + protected VMTemplateDao _templateDao; + @Inject + protected UserDao _userDao; + @Inject + protected AccountDao _accountDao; + @Inject + protected DomainDao _domainDao; + @Inject + protected ClusterManager _clusterMgr; + @Inject + protected ItWorkDao _workDao; + @Inject + protected UserVmDao _userVmDao; + @Inject + protected DomainRouterDao _routerDao; + @Inject + protected ConsoleProxyDao _consoleDao; + @Inject + protected SecondaryStorageVmDao _secondaryDao; + @Inject + protected NicDao _nicsDao; + @Inject + protected AccountManager _accountMgr; + @Inject + protected HostDao _hostDao; + @Inject + protected AlertManager _alertMgr; + @Inject + protected GuestOSCategoryDao _guestOsCategoryDao; + @Inject + protected GuestOSDao _guestOsDao; + @Inject + protected VolumeDao _volsDao; + @Inject + protected ConsoleProxyManager _consoleProxyMgr; + @Inject + protected ConfigurationManager _configMgr; + @Inject + protected CapacityManager _capacityMgr; + @Inject + protected HighAvailabilityManager _haMgr; + @Inject + protected HostPodDao _podDao; + @Inject + protected DataCenterDao _dcDao; + @Inject + protected StoragePoolDao _storagePoolDao; + @Inject + protected HypervisorGuruManager _hvGuruMgr; + @Inject + protected NetworkDao _networkDao; + + @Inject(adapter = DeploymentPlanner.class) + protected Adapters _planners; + + @Inject(adapter = HostAllocator.class) + protected Adapters _hostAllocators; + + @Inject + protected ResourceManager _resourceMgr; + + Map> _vmGurus = new HashMap>(); + protected StateMachine2 _stateMachine; + + ScheduledExecutorService _executor = null; + protected int _operationTimeout; + + protected int _retry; + protected long _nodeId; + protected long _cleanupWait; + protected long _cleanupInterval; + protected long _cancelWait; + protected long _opWaitInterval; + protected int _lockStateRetry; + protected boolean _forceStop; + + @Override + public void registerGuru(VirtualMachine.Type type, VirtualMachineGuru guru) { + synchronized (_vmGurus) { + _vmGurus.put(type, guru); + } + } + + @Override + @DB + public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, Pair rootDiskOffering, + List> dataDiskOfferings, List> networks, Map params, DeploymentPlan plan, + HypervisorType hyperType, Account owner) throws InsufficientCapacityException { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Allocating entries for VM: " + vm); + } + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, serviceOffering, owner, params); + + vm.setDataCenterId(plan.getDataCenterId()); + if (plan.getPodId() != null) { + vm.setPodId(plan.getPodId()); + } + assert (plan.getClusterId() == null && plan.getPoolId() == null) : "We currently don't support cluster and pool preset yet"; + + @SuppressWarnings("unchecked") + VirtualMachineGuru guru = (VirtualMachineGuru) _vmGurus.get(vm.getType()); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + vm = guru.persist(vm); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Allocating nics for " + vm); + } + + try { + _networkMgr.allocate(vmProfile, networks); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operation while trying to allocate resources for the VM", e); + } + + if (dataDiskOfferings == null) { + dataDiskOfferings = new ArrayList>(0); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Allocaing disks for " + vm); + } + + if (template.getFormat() == ImageFormat.ISO) { + _storageMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vm.getId(), rootDiskOffering.first(), rootDiskOffering.second(), vm, owner); + } else if (template.getFormat() == ImageFormat.BAREMETAL) { + // Do nothing + } else { + _storageMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vm.getId(), rootDiskOffering.first(), template, vm, owner); + } + + for (Pair offering : dataDiskOfferings) { + _storageMgr.allocateRawVolume(Type.DATADISK, "DATA-" + vm.getId(), offering.first(), offering.second(), vm, owner); + } + + txn.commit(); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Allocation completed for VM: " + vm); + } + + return vm; + } + + @Override + public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, Long rootSize, Pair dataDiskOffering, + List> networks, DeploymentPlan plan, HypervisorType hyperType, Account owner) throws InsufficientCapacityException { + List> diskOfferings = new ArrayList>(1); + if (dataDiskOffering != null) { + diskOfferings.add(dataDiskOffering); + } + return allocate(vm, template, serviceOffering, new Pair(serviceOffering, rootSize), diskOfferings, networks, null, plan, hyperType, owner); + } + + @Override + public T allocate(T vm, VMTemplateVO template, ServiceOfferingVO serviceOffering, List> networks, DeploymentPlan plan, + HypervisorType hyperType, Account owner) throws InsufficientCapacityException { + return allocate(vm, template, serviceOffering, new Pair(serviceOffering, null), null, networks, null, plan, hyperType, owner); + } + + @SuppressWarnings("unchecked") + private VirtualMachineGuru getVmGuru(T vm) { + return (VirtualMachineGuru) _vmGurus.get(vm.getType()); + } + + @SuppressWarnings("unchecked") + private VirtualMachineGuru getBareMetalVmGuru(T vm) { + return (VirtualMachineGuru) _vmGurus.get(VirtualMachine.Type.UserBareMetal); + } + + @Override + public boolean expunge(T vm, User caller, Account account) throws ResourceUnavailableException { + try { + if (advanceExpunge(vm, caller, account)) { + // Mark vms as removed + remove(vm, caller, account); + return true; + } else { + s_logger.info("Did not expunge " + vm); + return false; + } + } catch (OperationTimedoutException e) { + throw new CloudRuntimeException("Operation timed out", e); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Concurrent operation ", e); + } + } + + @Override + public boolean advanceExpunge(T vm, User caller, Account account) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { + if (vm == null || vm.getRemoved() != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find vm or vm is destroyed: " + vm); + } + return true; + } + + if (!this.advanceStop(vm, false, caller, account)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to stop the VM so we can't expunge it."); + } + } + + try { + if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { + s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); + return false; + } + } catch (NoTransitionException e) { + s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); + return false; + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Destroying vm " + vm); + } + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + s_logger.debug("Cleaning up NICS"); + _networkMgr.cleanupNics(profile); + // Clean up volumes based on the vm's instance id + _storageMgr.cleanupVolumes(vm.getId()); + + VirtualMachineGuru guru = getVmGuru(vm); + guru.finalizeExpunge(vm); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Expunged " + vm); + } + + return true; + } + + @Override + public boolean start() { + _executor.scheduleAtFixedRate(new CleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS); + cancelWorkItems(_nodeId); + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public boolean configure(String name, Map xmlParams) throws ConfigurationException { + _name = name; + + ComponentLocator locator = ComponentLocator.getCurrentLocator(); + ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); + Map params = configDao.getConfiguration(xmlParams); + + _retry = NumbersUtil.parseInt(params.get(Config.StartRetry.key()), 10); + + ReservationContextImpl.setComponents(_userDao, _domainDao, _accountDao); + VirtualMachineProfileImpl.setComponents(_offeringDao, _templateDao, _accountDao); + + _cancelWait = NumbersUtil.parseLong(params.get(Config.VmOpCancelInterval.key()), 3600); + _cleanupWait = NumbersUtil.parseLong(params.get(Config.VmOpCleanupWait.key()), 3600); + _cleanupInterval = NumbersUtil.parseLong(params.get(Config.VmOpCleanupInterval.key()), 86400) * 1000; + _opWaitInterval = NumbersUtil.parseLong(params.get(Config.VmOpWaitInterval.key()), 120) * 1000; + _lockStateRetry = NumbersUtil.parseInt(params.get(Config.VmOpLockStateRetry.key()), 5); + _operationTimeout = NumbersUtil.parseInt(params.get(Config.Wait.key()), 1800) * 2; + _forceStop = Boolean.parseBoolean(params.get(Config.VmDestroyForcestop.key())); + + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vm-Operations-Cleanup")); + _nodeId = _clusterMgr.getManagementNodeId(); + + _agentMgr.registerForHostEvents(this, true, true, true); + + return true; + } + + @Override + public String getName() { + return _name; + } + + protected VirtualMachineManagerImpl() { + setStateMachine(); + } + + @Override + public T start(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException { + return start(vm, params, caller, account, null); + } + + @Override + public T start(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) throws InsufficientCapacityException, + ResourceUnavailableException { + try { + return advanceStart(vm, params, caller, account, planToDeploy); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Unable to start a VM due to concurrent operation", e); + } + } + + protected boolean checkWorkItems(VMInstanceVO vm, State state) throws ConcurrentOperationException { + while (true) { + ItWorkVO vo = _workDao.findByOutstandingWork(vm.getId(), state); + if (vo == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find work for VM: " + vm + " and state: " + state); + } + return true; + } + + if (vo.getStep() == Step.Done) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Work for " + vm + " is " + vo.getStep()); + } + return true; + } + + if (vo.getSecondsTaskIsInactive() > _cancelWait) { + s_logger.warn("The task item for vm " + vm + " has been inactive for " + vo.getSecondsTaskIsInactive()); + return false; + } + + try { + Thread.sleep(_opWaitInterval); + } catch (InterruptedException e) { + s_logger.info("Waiting for " + vm + " but is interrupted"); + throw new ConcurrentOperationException("Waiting for " + vm + " but is interrupted"); + } + s_logger.debug("Waiting some more to make sure there's no activity on " + vm); + } + + } + + @DB + protected Ternary changeToStartState(VirtualMachineGuru vmGuru, T vm, User caller, Account account) + throws ConcurrentOperationException { + long vmId = vm.getId(); + + ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), vm.getId()); + int retry = _lockStateRetry; + while (retry-- != 0) { + Transaction txn = Transaction.currentTxn(); + Ternary result = null; + txn.start(); + try { + Journal journal = new Journal.LogJournal("Creating " + vm, s_logger); + work = _workDao.persist(work); + ReservationContextImpl context = new ReservationContextImpl(work.getId(), journal, caller, account); + + if (stateTransitTo(vm, Event.StartRequested, null, work.getId())) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); + } + result = new Ternary(vmGuru.findById(vmId), context, work); + txn.commit(); + return result; + } + } catch (NoTransitionException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to transition into Starting state due to " + e.getMessage()); + } + } finally { + if (result == null) { + txn.rollback(); + } + } + + VMInstanceVO instance = _vmDao.findById(vmId); + if (instance == null) { + throw new ConcurrentOperationException("Unable to acquire lock on " + vm); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Determining why we're unable to update the state to Starting for " + instance + ". Retry=" + retry); + } + + State state = instance.getState(); + if (state == State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is already started: " + vm); + } + return null; + } + + if (state.isTransitional()) { + if (!checkWorkItems(vm, state)) { + throw new ConcurrentOperationException("There are concurrent operations on " + vm); + } else { + continue; + } + } + + if (state != State.Stopped) { + s_logger.debug("VM " + vm + " is not in a state to be started: " + state); + return null; + } + } + + throw new ConcurrentOperationException("Unable to change the state of " + vm); + } + + protected boolean changeState(T vm, Event event, Long hostId, ItWorkVO work, Step step) throws NoTransitionException { + // FIXME: We should do this better. + Step previousStep = work.getStep(); + _workDao.updateStep(work, step); + boolean result = false; + try { + result = stateTransitTo(vm, event, hostId); + return result; + } finally { + if (!result) { + _workDao.updateStep(work, previousStep); + } + } + } + + @Override + public T advanceStart(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { + return advanceStart(vm, params, caller, account, null); + } + + @Override + public T advanceStart(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + long vmId = vm.getId(); + VirtualMachineGuru vmGuru; + if (vm.getHypervisorType() == HypervisorType.BareMetal) { + vmGuru = getBareMetalVmGuru(vm); + } else { + vmGuru = getVmGuru(vm); + } + + vm = vmGuru.findById(vm.getId()); + Ternary start = changeToStartState(vmGuru, vm, caller, account); + if (start == null) { + return vmGuru.findById(vmId); + } + + vm = start.first(); + ReservationContext ctx = start.second(); + ItWorkVO work = start.third(); + + T startedVm = null; + ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); + VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Trying to deploy VM, vm has dcId: " + vm.getDataCenterIdToDeployIn() + " and podId: " + vm.getPodIdToDeployIn()); + } + DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), null, null, null, null, ctx); + if(planToDeploy != null && planToDeploy.getDataCenterId() != 0){ + if (s_logger.isDebugEnabled()) { + s_logger.debug("advanceStart: DeploymentPlan is provided, using dcId:" + planToDeploy.getDataCenterId() + ", podId: " + planToDeploy.getPodId() + ", clusterId: " + + planToDeploy.getClusterId() + ", hostId: " + planToDeploy.getHostId() + ", poolId: " + planToDeploy.getPoolId()); + } + plan = new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(), planToDeploy.getPoolId(), planToDeploy.getPhysicalNetworkId(), ctx); + } + + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); + + boolean canRetry = true; + try { + Journal journal = start.second().getJournal(); + + ExcludeList avoids = null; + if (planToDeploy != null) { + avoids = planToDeploy.getAvoids(); + } + if (avoids == null) { + avoids = new ExcludeList(); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid()); + } + + + boolean planChangedByVolume = false; + boolean reuseVolume = true; + DataCenterDeployment originalPlan = plan; + + int retry = _retry; + boolean recreate = false; + while (retry-- != 0) { // It's != so that it can match -1. + + if(reuseVolume){ + // edit plan if this vm's ROOT volume is in READY state already + List vols = _volsDao.findReadyRootVolumesByInstance(vm.getId()); + for (VolumeVO vol : vols) { + // make sure if the templateId is unchanged. If it is changed, + // let planner + // reassign pool for the volume even if it ready. + Long volTemplateId = vol.getTemplateId(); + if (volTemplateId != null && volTemplateId.longValue() != template.getId()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(vol + " of " + vm + " is READY, but template ids don't match, let the planner reassign a new pool"); + } + continue; + } + + StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); + if (!pool.isInMaintenance()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Root volume is ready, need to place VM in volume's cluster"); + } + long rootVolDcId = pool.getDataCenterId(); + Long rootVolPodId = pool.getPodId(); + Long rootVolClusterId = pool.getClusterId(); + if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) { + Long clusterIdSpecified = planToDeploy.getClusterId(); + if (clusterIdSpecified != null && rootVolClusterId != null) { + if (rootVolClusterId.longValue() != clusterIdSpecified.longValue()) { + // cannot satisfy the plan passed in to the + // planner + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot satisfy the deployment plan passed in since the ready Root volume is in different cluster. volume's cluster: " + rootVolClusterId + + ", cluster specified: " + clusterIdSpecified); + } + throw new ResourceUnavailableException("Root volume is ready in different cluster, Deployment plan provided cannot be satisfied, unable to create a deployment for " + + vm, Cluster.class, clusterIdSpecified); + } + } + plan = new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(), vol.getPoolId(), null, ctx); + }else{ + plan = new DataCenterDeployment(rootVolDcId, rootVolPodId, rootVolClusterId, null, vol.getPoolId(), null, ctx); + if (s_logger.isDebugEnabled()) { + s_logger.debug(vol + " is READY, changing deployment plan to use this pool's dcId: " + rootVolDcId + " , podId: " + rootVolPodId + " , and clusterId: " + rootVolClusterId); + } + planChangedByVolume = true; + } + } + } + } + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, account, params); + DeployDestination dest = null; + for (DeploymentPlanner planner : _planners) { + if (planner.canHandle(vmProfile, plan, avoids)) { + dest = planner.plan(vmProfile, plan, avoids); + } else { + continue; + } + if (dest != null) { + avoids.addHost(dest.getHost().getId()); + journal.record("Deployment found ", vmProfile, dest); + break; + } + } + + if (dest == null) { + if (planChangedByVolume) { + plan = originalPlan; + planChangedByVolume = false; + //do not enter volume reuse for next retry, since we want to look for resorces outside the volume's cluster + reuseVolume = false; + continue; + } + throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, DataCenter.class, plan.getDataCenterId()); + } + + long destHostId = dest.getHost().getId(); + vm.setPodId(dest.getPod().getId()); + + try { + if (!changeState(vm, Event.OperationRetry, destHostId, work, Step.Prepare)) { + throw new ConcurrentOperationException("Unable to update the state of the Virtual Machine"); + } + } catch (NoTransitionException e1) { + throw new ConcurrentOperationException(e1.getMessage()); + } + + try { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is being created in podId: " + vm.getPodIdToDeployIn()); + } + _networkMgr.prepare(vmProfile, dest, ctx); + if (vm.getHypervisorType() != HypervisorType.BareMetal) { + _storageMgr.prepare(vmProfile, dest, recreate); + recreate = false; + } + //since StorageMgr succeeded in volume creation, reuse Volume for further tries until current cluster has capacity + if(!reuseVolume){ + reuseVolume = true; + } + + Commands cmds = null; + vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); + + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + cmds = new Commands(OnError.Stop); + cmds.addCommand(new StartCommand(vmTO)); + + vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); + + + work = _workDao.findById(work.getId()); + if (work == null || work.getStep() != Step.Prepare) { + throw new ConcurrentOperationException("Work steps have been changed: " + work); + } + _workDao.updateStep(work, Step.Starting); + + _agentMgr.send(destHostId, cmds); + + _workDao.updateStep(work, Step.Started); + + + StartAnswer startAnswer = cmds.getAnswer(StartAnswer.class); + if (startAnswer != null && startAnswer.getResult()) { + String host_guid = startAnswer.getHost_guid(); + if( host_guid != null ) { + HostVO finalHost = _resourceMgr.findHostByGuid(host_guid); + if (finalHost == null ) { + throw new CloudRuntimeException("Host Guid " + host_guid + " doesn't exist in DB, something wrong here"); + } + destHostId = finalHost.getId(); + } + if (vmGuru.finalizeStart(vmProfile, destHostId, cmds, ctx)) { + if (!changeState(vm, Event.OperationSucceeded, destHostId, work, Step.Done)) { + throw new ConcurrentOperationException("Unable to transition to a new state."); + } + startedVm = vm; + if (s_logger.isDebugEnabled()) { + s_logger.debug("Start completed for VM " + vm); + } + return startedVm; + } else { + if (s_logger.isDebugEnabled()) { + s_logger.info("The guru did not like the answers so stopping " + vm); + } + + StopCommand cmd = new StopCommand(vm.getInstanceName()); + StopAnswer answer = (StopAnswer) _agentMgr.easySend(destHostId, cmd); + if (answer == null || !answer.getResult()) { + s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers")); + _haMgr.scheduleStop(vm, destHostId, WorkType.ForceStop); + throw new ExecutionException("Unable to stop " + vm + " so we are unable to retry the start operation"); + } + if (vmGuru.recreateNeeded(vmProfile, destHostId, cmds, ctx)) { + recreate = true; + } else { + throw new ExecutionException("Unable to start " + vm + " due to error in finalizeStart, not retrying"); + } + } + } + s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + (startAnswer == null ? " no start answer" : startAnswer.getDetails())); + + } catch (OperationTimedoutException e) { + s_logger.debug("Unable to send the start command to host " + dest.getHost()); + if (e.isActive()) { + _haMgr.scheduleStop(vm, destHostId, WorkType.CheckStop); + } + canRetry = false; + throw new AgentUnavailableException("Unable to start " + vm.getHostName(), destHostId, e); + } catch (ResourceUnavailableException e) { + s_logger.info("Unable to contact resource.", e); + if (!avoids.add(e)) { + if (e.getScope() == Volume.class || e.getScope() == Nic.class) { + throw e; + } else { + s_logger.warn("unexpected ResourceUnavailableException : " + e.getScope().getName(), e); + throw e; + } + } + } catch (InsufficientCapacityException e) { + s_logger.info("Insufficient capacity ", e); + if (!avoids.add(e)) { + if (e.getScope() == Volume.class || e.getScope() == Nic.class) { + throw e; + } else { + s_logger.warn("unexpected InsufficientCapacityException : " + e.getScope().getName(), e); + } + } + } catch (Exception e) { + s_logger.error("Failed to start instance " + vm, e); + throw new AgentUnavailableException("Unable to start instance due to " + e.getMessage(), destHostId, e); + } finally { + if (startedVm == null && canRetry) { + Step prevStep = work.getStep(); + _workDao.updateStep(work, Step.Release); + if (prevStep == Step.Started || prevStep == Step.Starting) { + cleanup(vmGuru, vmProfile, work, Event.OperationFailed, false, caller, account); + } else { + //if step is not starting/started, send cleanup command with force=true + cleanup(vmGuru, vmProfile, work, Event.OperationFailed, true, caller, account); + } + } + } + } + } finally { + if (startedVm == null) { + if (canRetry) { + try { + changeState(vm, Event.OperationFailed, null, work, Step.Done); + } catch (NoTransitionException e) { + throw new ConcurrentOperationException(e.getMessage()); + } + } + } + } + + return startedVm; + } + + @Override + public boolean stop(T vm, User user, Account account) throws ResourceUnavailableException { + try { + return advanceStop(vm, false, user, account); + } catch (OperationTimedoutException e) { + throw new AgentUnavailableException("Unable to stop vm because the operation to stop timed out", vm.getHostId(), e); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Unable to stop vm because of a concurrent operation", e); + } + } + + protected boolean sendStop(VirtualMachineGuru guru, VirtualMachineProfile profile, boolean force) { + VMInstanceVO vm = profile.getVirtualMachine(); + StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null); + try { + Answer answer = _agentMgr.send(vm.getHostId(), stop); + if (!answer.getResult()) { + s_logger.debug("Unable to stop VM due to " + answer.getDetails()); + return false; + } + + guru.finalizeStop(profile, (StopAnswer) answer); + } catch (AgentUnavailableException e) { + if (!force) { + return false; + } + } catch (OperationTimedoutException e) { + if (!force) { + return false; + } + } + + return true; + } + + protected boolean cleanup(VirtualMachineGuru guru, VirtualMachineProfile profile, ItWorkVO work, Event event, boolean force, User user, Account account) { + T vm = profile.getVirtualMachine(); + State state = vm.getState(); + s_logger.debug("Cleaning up resources for the vm " + vm + " in " + state + " state"); + if (state == State.Starting) { + Step step = work.getStep(); + if (step == Step.Starting && !force) { + s_logger.warn("Unable to cleanup vm " + vm + "; work state is incorrect: " + step); + return false; + } + + if (step == Step.Started || step == Step.Starting || step == Step.Release) { + if (vm.getHostId() != null) { + if (!sendStop(guru, profile, force)) { + s_logger.warn("Failed to stop vm " + vm + " in " + State.Starting + " state as a part of cleanup process"); + return false; + } + } + } + + if (step != Step.Release && step != Step.Prepare && step != Step.Started && step != Step.Starting) { + s_logger.debug("Cleanup is not needed for vm " + vm + "; work state is incorrect: " + step); + return true; + } + } else if (state == State.Stopping) { + if (vm.getHostId() != null) { + if (!sendStop(guru, profile, force)) { + s_logger.warn("Failed to stop vm " + vm + " in " + State.Stopping + " state as a part of cleanup process"); + return false; + } + } + } else if (state == State.Migrating) { + if (vm.getHostId() != null) { + if (!sendStop(guru, profile, force)) { + s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process"); + return false; + } + } + if (vm.getLastHostId() != null) { + if (!sendStop(guru, profile, force)) { + s_logger.warn("Failed to stop vm " + vm + " in " + State.Migrating + " state as a part of cleanup process"); + return false; + } + } + } else if (state == State.Running) { + if (!sendStop(guru, profile, force)) { + s_logger.warn("Failed to stop vm " + vm + " in " + State.Running + " state as a part of cleanup process"); + return false; + } + } + + try { + _networkMgr.release(profile, force); + s_logger.debug("Successfully released network resources for the vm " + vm); + } catch (Exception e) { + s_logger.warn("Unable to release some network resources.", e); + } + + _storageMgr.release(profile); + s_logger.debug("Successfully cleanued up resources for the vm " + vm + " in " + state + " state"); + return true; + } + + @Override + public boolean advanceStop(T vm, boolean forced, User user, Account account) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { + State state = vm.getState(); + if (state == State.Stopped) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is already stopped: " + vm); + } + return true; + } + + if (state == State.Destroyed || state == State.Expunging || state == State.Error) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Stopped called on " + vm + " but the state is " + state); + } + return true; + } + // grab outstanding work item if any + ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found an outstanding work item for this vm " + vm + " with state:" + vm.getState() + ", work id:" + work.getId()); + } + } + Long hostId = vm.getHostId(); + if (hostId == null) { + if (!forced) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("HostId is null but this is not a forced stop, cannot stop vm " + vm + " with state:" + vm.getState()); + } + return false; + } + try { + stateTransitTo(vm, Event.AgentReportStopped, null, null); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + // mark outstanding work item if any as done + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updating work item to Done, id:" + work.getId()); + } + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + return true; + } + + VirtualMachineGuru vmGuru = getVmGuru(vm); + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + + try { + if (!stateTransitTo(vm, Event.StopRequested, vm.getHostId())) { + throw new ConcurrentOperationException("VM is being operated on."); + } + } catch (NoTransitionException e1) { + if (!forced) { + throw new CloudRuntimeException("We cannot stop " + vm + " when it is in state " + vm.getState()); + } + boolean doCleanup = false; + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to transition the state but we're moving on because it's forced stop"); + } + if (state == State.Starting || state == State.Migrating) { + if (work != null) { + doCleanup = true; + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to cleanup VM: " + vm + " ,since outstanding work item is not found"); + } + throw new CloudRuntimeException("Work item not found, We cannot stop " + vm + " when it is in state " + vm.getState()); + } + } else if (state == State.Stopping) { + doCleanup = true; + } + + if (doCleanup) { + if (cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.StopRequested, forced, user, account)) { + try { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updating work item to Done, id:" + work.getId()); + } + return changeState(vm, Event.AgentReportStopped, null, work, Step.Done); + } catch (NoTransitionException e) { + s_logger.warn("Unable to cleanup " + vm); + return false; + } + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Failed to cleanup VM: " + vm); + } + throw new CloudRuntimeException("Failed to cleanup " + vm + " , current state " + vm.getState()); + } + } + } + + if (vm.getState() != State.Stopping) { + throw new CloudRuntimeException("We cannot proceed with stop VM " + vm + " since it is not in 'Stopping' state, current state: " + vm.getState()); + } + + vmGuru.prepareStop(profile); + + StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null); + boolean stopped = false; + StopAnswer answer = null; + try { + answer = (StopAnswer) _agentMgr.send(vm.getHostId(), stop); + stopped = answer.getResult(); + if (!stopped) { + throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); + } + vmGuru.finalizeStop(profile, answer); + + } catch (AgentUnavailableException e) { + } catch (OperationTimedoutException e) { + } finally { + if (!stopped) { + if (!forced) { + s_logger.warn("Unable to stop vm " + vm); + try { + stateTransitTo(vm, Event.OperationFailed, vm.getHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unable to transition the state " + vm); + } + return false; + } else { + s_logger.warn("Unable to actually stop " + vm + " but continue with release because it's a force stop"); + vmGuru.finalizeStop(profile, answer); + } + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug(vm + " is stopped on the host. Proceeding to release resource held."); + } + + try { + _networkMgr.release(profile, forced); + s_logger.debug("Successfully released network resources for the vm " + vm); + } catch (Exception e) { + s_logger.warn("Unable to release some network resources.", e); + } + + try { + if (vm.getHypervisorType() != HypervisorType.BareMetal) { + _storageMgr.release(profile); + s_logger.debug("Successfully released storage resources for the vm " + vm); + } + } catch (Exception e) { + s_logger.warn("Unable to release storage resources.", e); + } + + try { + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updating the outstanding work item to Done, id:" + work.getId()); + } + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + + return stateTransitTo(vm, Event.OperationSucceeded, null, null); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + return false; + } + } + + private void setStateMachine() { + _stateMachine = VirtualMachine.State.getStateMachine(); + } + + protected boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException { + vm.setReservationId(reservationId); + return _stateMachine.transitTo(vm, e, new Pair(vm.getHostId(), hostId), _vmDao); + } + + @Override + public boolean stateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId) throws NoTransitionException { + State oldState = vm.getState(); + if (oldState == State.Starting) { + if (e == Event.OperationSucceeded) { + vm.setLastHostId(hostId); + } + } else if (oldState == State.Stopping) { + if (e == Event.OperationSucceeded) { + vm.setLastHostId(vm.getHostId()); + } + } + return _stateMachine.transitTo(vm, e, new Pair(vm.getHostId(), hostId), _vmDao); + } + + @Override + public boolean remove(T vm, User user, Account caller) { + return _vmDao.remove(vm.getId()); + } + + @Override + public boolean destroy(T vm, User user, Account caller) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Destroying vm " + vm); + } + if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find vm or vm is destroyed: " + vm); + } + return true; + } + + if (!advanceStop(vm, _forceStop, user, caller)) { + s_logger.debug("Unable to stop " + vm); + return false; + } + + try { + if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { + s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); + return false; + } + } catch (NoTransitionException e) { + s_logger.debug(e.getMessage()); + return false; + } + + return true; + } + + protected boolean checkVmOnHost(VirtualMachine vm, long hostId) throws AgentUnavailableException, OperationTimedoutException { + CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) _agentMgr.send(hostId, new CheckVirtualMachineCommand(vm.getInstanceName())); + if (!answer.getResult() || answer.getState() == State.Stopped) { + return false; + } + + return true; + } + + @Override + public T storageMigration(T vm, StoragePool destPool) { + VirtualMachineGuru vmGuru = getVmGuru(vm); + + long vmId = vm.getId(); + vm = vmGuru.findById(vmId); + + try { + stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null); + } catch (NoTransitionException e) { + s_logger.debug("Unable to migrate vm: " + e.toString()); + throw new CloudRuntimeException("Unable to migrate vm: " + e.toString()); + } + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + boolean migrationResult = false; + try { + migrationResult = _storageMgr.StorageMigration(profile, destPool); + + if (migrationResult) { + //if the vm is migrated to different pod in basic mode, need to reallocate ip + + if (vm.getPodIdToDeployIn() != destPool.getPodId()) { + DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destPool.getPodId(), null, null, null, null); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, null, null, null, null); + _networkMgr.reallocate(vmProfile, plan); + } + + //when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool + vm.setLastHostId(null); + vm.setPodId(destPool.getPodId()); + } else { + s_logger.debug("Storage migration failed"); + } + } catch (ConcurrentOperationException e) { + s_logger.debug("Failed to migration: " + e.toString()); + throw new CloudRuntimeException("Failed to migration: " + e.toString()); + } catch (InsufficientVirtualNetworkCapcityException e) { + s_logger.debug("Failed to migration: " + e.toString()); + throw new CloudRuntimeException("Failed to migration: " + e.toString()); + } catch (InsufficientAddressCapacityException e) { + s_logger.debug("Failed to migration: " + e.toString()); + throw new CloudRuntimeException("Failed to migration: " + e.toString()); + } catch (InsufficientCapacityException e) { + s_logger.debug("Failed to migration: " + e.toString()); + throw new CloudRuntimeException("Failed to migration: " + e.toString()); + } finally { + try { + stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null); + } catch (NoTransitionException e) { + s_logger.debug("Failed to change vm state: " + e.toString()); + throw new CloudRuntimeException("Failed to change vm state: " + e.toString()); + } + } + + return vm; + } + + @Override + public T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, + VirtualMachineMigrationException { + s_logger.info("Migrating " + vm + " to " + dest); + + long dstHostId = dest.getHost().getId(); + Host fromHost = _hostDao.findById(srcHostId); + if (fromHost == null) { + s_logger.info("Unable to find the host to migrate from: " + srcHostId); + throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId); + } + + if (fromHost.getClusterId().longValue() != dest.getCluster().getId()) { + s_logger.info("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId()); + throw new CloudRuntimeException("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId()); + } + + VirtualMachineGuru vmGuru = getVmGuru(vm); + + long vmId = vm.getId(); + vm = vmGuru.findById(vmId); + if (vm == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find the vm " + vm); + } + throw new ManagementServerException("Unable to find a virtual machine with id " + vmId); + } + + if (vm.getState() != State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is not Running, unable to migrate the vm " + vm); + } + throw new VirtualMachineMigrationException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString()); + } + + short alertType = AlertManager.ALERT_TYPE_USERVM_MIGRATE; + if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) { + alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE; + } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) { + alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY_MIGRATE; + } + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + _networkMgr.prepareNicForMigration(profile, dest); + _storageMgr.prepareForMigration(profile, dest); + + VirtualMachineTO to = toVmTO(profile); + PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to); + + ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId()); + work.setStep(Step.Prepare); + work.setResourceType(ItWorkVO.ResourceType.Host); + work.setResourceId(dstHostId); + work = _workDao.persist(work); + + PrepareForMigrationAnswer pfma = null; + try { + pfma = (PrepareForMigrationAnswer) _agentMgr.send(dstHostId, pfmc); + if (!pfma.getResult()) { + String msg = "Unable to prepare for migration due to " + pfma.getDetails(); + pfma = null; + throw new AgentUnavailableException(msg, dstHostId); + } + } catch (OperationTimedoutException e1) { + throw new AgentUnavailableException("Operation timed out", dstHostId); + } finally { + if (pfma == null) { + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + } + + vm.setLastHostId(srcHostId); + try { + if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) { + s_logger.info("Migration cancelled because state has changed: " + vm); + throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm); + } + } catch (NoTransitionException e1) { + s_logger.info("Migration cancelled because " + e1.getMessage()); + throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage()); + } + + boolean migrated = false; + try { + boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); + MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows); + mc.setHostGuid(dest.getHost().getGuid()); + + try { + MigrateAnswer ma = (MigrateAnswer) _agentMgr.send(vm.getLastHostId(), mc); + if (!ma.getResult()) { + s_logger.error("Unable to migrate due to " + ma.getDetails()); + return null; + } + } catch (OperationTimedoutException e) { + if (e.isActive()) { + s_logger.warn("Active migration command so scheduling a restart for " + vm); + _haMgr.scheduleRestart(vm, true); + } + throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId); + } + + try { + if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) { + throw new ConcurrentOperationException("Unable to change the state for " + vm); + } + } catch (NoTransitionException e1) { + throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage()); + } + + try { + if (!checkVmOnHost(vm, dstHostId)) { + s_logger.error("Unable to complete migration for " + vm); + try { + _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null); + } catch (AgentUnavailableException e) { + s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId); + } + cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); + return null; + } + } catch (OperationTimedoutException e) { + } + + migrated = true; + return vm; + } finally { + if (!migrated) { + s_logger.info("Migration was unsuccessful. Cleaning up: " + vm); + + _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(), "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " + + dest.getDataCenter().getName() + " and pod " + dest.getPod().getName(), "Migrate Command failed. Please check logs."); + try { + _agentMgr.send(dstHostId, new Commands(cleanup(vm.getInstanceName())), null); + } catch (AgentUnavailableException ae) { + s_logger.info("Looks like the destination Host is unavailable for cleanup"); + } + + try { + stateTransitTo(vm, Event.OperationFailed, srcHostId); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + } + + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + } + + @Override + public VirtualMachineTO toVmTO(VirtualMachineProfile profile) { + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(profile.getVirtualMachine().getHypervisorType()); + VirtualMachineTO to = hvGuru.implement(profile); + return to; + } + + protected void cancelWorkItems(long nodeId) { + GlobalLock scanLock = GlobalLock.getInternLock("vmmgr.cancel.workitem"); + + try { + if (scanLock.lock(3)) { + try { + List works = _workDao.listWorkInProgressFor(nodeId); + for (ItWorkVO work : works) { + s_logger.info("Handling unfinished work item: " + work); + try { + VMInstanceVO vm = _vmDao.findById(work.getInstanceId()); + if (vm != null) { + if (work.getType() == State.Starting) { + _haMgr.scheduleRestart(vm, true); + work.setManagementServerId(_nodeId); + _workDao.update(work.getId(), work); + } else if (work.getType() == State.Stopping) { + _haMgr.scheduleStop(vm, vm.getHostId(), WorkType.CheckStop); + work.setManagementServerId(_nodeId); + _workDao.update(work.getId(), work); + } else if (work.getType() == State.Migrating) { + _haMgr.scheduleMigration(vm); + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + } + } catch (Exception e) { + s_logger.error("Error while handling " + work, e); + } + } + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + } + + @Override + public boolean migrateAway(VirtualMachine.Type vmType, long vmId, long srcHostId) throws InsufficientServerCapacityException, VirtualMachineMigrationException { + VirtualMachineGuru vmGuru = _vmGurus.get(vmType); + VMInstanceVO vm = vmGuru.findById(vmId); + if (vm == null) { + s_logger.debug("Unable to find a VM for " + vmId); + return true; + } + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + + Long hostId = vm.getHostId(); + if (hostId == null) { + s_logger.debug("Unable to migrate because the VM doesn't have a host id: " + vm); + return true; + } + + Host host = _hostDao.findById(hostId); + + DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null); + ExcludeList excludes = new ExcludeList(); + excludes.addHost(hostId); + + DeployDestination dest = null; + while (true) { + for (DeploymentPlanner planner : _planners) { + if (planner.canHandle(profile, plan, excludes)) { + dest = planner.plan(profile, plan, excludes); + } else { + continue; + } + + if (dest != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Planner " + planner + " found " + dest + " for migrating to."); + } + break; + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Planner " + planner + " was unable to find anything."); + } + } + + if (dest == null) { + throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", host.getClusterId()); + } + + excludes.addHost(dest.getHost().getId()); + VMInstanceVO vmInstance = null; + try { + vmInstance = migrate(vm, srcHostId, dest); + } catch (ResourceUnavailableException e) { + s_logger.debug("Unable to migrate to unavailable " + dest); + } catch (ConcurrentOperationException e) { + s_logger.debug("Unable to migrate VM due to: " + e.getMessage()); + } catch (ManagementServerException e) { + s_logger.debug("Unable to migrate VM: " + e.getMessage()); + } catch (VirtualMachineMigrationException e) { + s_logger.debug("Got VirtualMachineMigrationException, Unable to migrate: " + e.getMessage()); + if (vm.getState() == State.Starting) { + s_logger.debug("VM seems to be still Starting, we should retry migration later"); + throw e; + } else { + s_logger.debug("Unable to migrate VM, VM is not in Running or even Starting state, current state: " + vm.getState().toString()); + } + } + if (vmInstance != null) { + return true; + } + try { + boolean result = advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); + return result; + } catch (ResourceUnavailableException e) { + s_logger.debug("Unable to stop VM due to " + e.getMessage()); + } catch (ConcurrentOperationException e) { + s_logger.debug("Unable to stop VM due to " + e.getMessage()); + } catch (OperationTimedoutException e) { + s_logger.debug("Unable to stop VM due to " + e.getMessage()); + } + return false; + } + } + + protected class CleanupTask implements Runnable { + @Override + public void run() { + s_logger.trace("VM Operation Thread Running"); + try { + _workDao.cleanup(_cleanupWait); + } catch (Exception e) { + s_logger.error("VM Operations failed due to ", e); + } + } + } + + @Override + public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) { + Enumeration en = _hostAllocators.enumeration(); + boolean isMachineUpgradable = true; + while (isMachineUpgradable && en.hasMoreElements()) { + final HostAllocator allocator = en.nextElement(); + isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); + } + + return isMachineUpgradable; + } + + @Override + public T reboot(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException { + try { + return advanceReboot(vm, params, caller, account); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException("Unable to reboot a VM due to concurrent operation", e); + } + } + + @Override + public T advanceReboot(T vm, Map params, User caller, Account account) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { + T rebootedVm = null; + + DataCenter dc = _configMgr.getZone(vm.getDataCenterIdToDeployIn()); + Host host = _hostDao.findById(vm.getHostId()); + Cluster cluster = null; + if (host != null) { + cluster = _configMgr.getCluster(host.getClusterId()); + } + HostPodVO pod = _configMgr.getPod(host.getPodId()); + DeployDestination dest = new DeployDestination(dc, pod, cluster, host); + + try { + + Commands cmds = new Commands(OnError.Stop); + cmds.addCommand(new RebootCommand(vm.getInstanceName())); + _agentMgr.send(host.getId(), cmds); + + Answer rebootAnswer = cmds.getAnswer(RebootAnswer.class); + if (rebootAnswer != null && rebootAnswer.getResult()) { + rebootedVm = vm; + return rebootedVm; + } + s_logger.info("Unable to reboot VM " + vm + " on " + dest.getHost() + " due to " + (rebootAnswer == null ? " no reboot answer" : rebootAnswer.getDetails())); + } catch (OperationTimedoutException e) { + s_logger.warn("Unable to send the reboot command to host " + dest.getHost() + " for the vm " + vm + " due to operation timeout", e); + throw new CloudRuntimeException("Failed to reboot the vm on host " + dest.getHost()); + } + + return rebootedVm; + } + + @Override + public VMInstanceVO findByIdAndType(VirtualMachine.Type type, long vmId) { + VirtualMachineGuru guru = _vmGurus.get(type); + return guru.findById(vmId); + } + + public Command cleanup(String vmName) { + return new StopCommand(vmName); + } + + public Commands fullHostSync(final long hostId, StartupRoutingCommand startup) { + Commands commands = new Commands(OnError.Continue); + + Map infos = convertToInfos(startup); + + final List vms = _vmDao.listByHostId(hostId); + s_logger.debug("Found " + vms.size() + " VMs for host " + hostId); + for (VMInstanceVO vm : vms) { + AgentVmInfo info = infos.remove(vm.getId()); + VMInstanceVO castedVm = null; + if (info == null) { + info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); + } + castedVm = info.guru.findById(vm.getId()); + + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); + Command command = compareState(hostId, castedVm, info, true, hvGuru.trackVmHostChange()); + if (command != null) { + commands.addCommand(command); + } + } + + for (final AgentVmInfo left : infos.values()) { + boolean found = false; + for (VirtualMachineGuru vmGuru : _vmGurus.values()) { + VMInstanceVO vm = vmGuru.findByName(left.name); + if (vm != null) { + found = true; + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); + if(hvGuru.trackVmHostChange()) { + Command command = compareState(hostId, vm, left, true, true); + if (command != null) { + commands.addCommand(command); + } + } else { + s_logger.warn("Stopping a VM, VM " + left.name + " migrate from Host " + vm.getHostId() + " to Host " + hostId ); + commands.addCommand(cleanup(left.name)); + } + break; + } + } + if ( ! found ) { + s_logger.warn("Stopping a VM that we have no record of : " + left.name); + commands.addCommand(cleanup(left.name)); + } + } + + return commands; + } + + public Commands deltaHostSync(long hostId, Map newStates) { + Map states = convertDeltaToInfos(newStates); + Commands commands = new Commands(OnError.Continue); + + for (Map.Entry entry : states.entrySet()) { + AgentVmInfo info = entry.getValue(); + + VMInstanceVO vm = info.vm; + + Command command = null; + if (vm != null) { + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); + command = compareState(hostId, vm, info, false, hvGuru.trackVmHostChange()); + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cleaning up a VM that is no longer found: " + info.name); + } + command = cleanup(info.name); + } + + if (command != null) { + commands.addCommand(command); + } + } + + return commands; + } + + + + public void deltaSync(Map> newStates) { + Map states = convertToInfos(newStates); + + for (Map.Entry entry : states.entrySet()) { + AgentVmInfo info = entry.getValue(); + VMInstanceVO vm = info.vm; + Command command = null; + if (vm != null) { + Host host = _resourceMgr.findHostByGuid(info.getHostUuid()); + long hId = host.getId(); + + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType()); + command = compareState(hId, vm, info, false, hvGuru.trackVmHostChange()); + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cleaning up a VM that is no longer found : " + info.name); + } + command = cleanup(info.name); + } + if (command != null){ + try { + Host host = _resourceMgr.findHostByGuid(info.getHostUuid()); + if (host != null){ + Answer answer = _agentMgr.send(host.getId(), cleanup(info.name)); + if (!answer.getResult()) { + s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); + } + } + } catch (Exception e) { + s_logger.warn("Unable to stop a VM due to " + e.getMessage()); + } + } + } + } + + + public void fullSync(final long clusterId, Map> newStates) { + if (newStates==null)return; + Map infos = convertToInfos(newStates); + Set set_vms = Collections.synchronizedSet(new HashSet()); + set_vms.addAll(_vmDao.listByClusterId(clusterId)); + set_vms.addAll(_vmDao.listLHByClusterId(clusterId)); + + for (VMInstanceVO vm : set_vms) { + if (vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) continue; + AgentVmInfo info = infos.remove(vm.getId()); + VMInstanceVO castedVm = null; + if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting)) + || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) + { + s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); + info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); + + // Bug 13850- grab outstanding work item if any for this VM state so that we mark it as DONE after we change VM state, else it will remain pending + ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found an outstanding work item for this vm " + vm + " in state:" + vm.getState() + ", work id:" + work.getId()); + } + } + vm.setState(State.Running); // set it as running and let HA take care of it + _vmDao.persist(vm); + + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updating outstanding work item to Done, id:" + work.getId()); + } + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + + castedVm = info.guru.findById(vm.getId()); + try { + Host host = _hostDao.findByGuid(info.getHostUuid()); + long hostId = host == null ? (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()) : host.getId(); + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); + Command command = compareState(hostId, castedVm, info, true, hvGuru.trackVmHostChange()); + if (command != null){ + Answer answer = _agentMgr.send(hostId, command); + if (!answer.getResult()) { + s_logger.warn("Failed to update state of the VM due to " + answer.getDetails()); + } + } + } catch (Exception e) { + s_logger.warn("Unable to update state of the VM due to exception " + e.getMessage()); + e.printStackTrace(); + } + } + else if (info != null && (vm.getState() == State.Stopped || vm.getState() == State.Stopping)) { + Host host = _hostDao.findByGuid(info.getHostUuid()); + if (host != null){ + s_logger.warn("Stopping a VM which is stopped/stopping " + info.name); + vm.setState(State.Stopped); // set it as stop and clear it from host + vm.setHostId(null); + _vmDao.persist(vm); + try { + Answer answer = _agentMgr.send(host.getId(), cleanup(info.name)); + if (!answer.getResult()) { + s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); + } + } + catch (Exception e) { + s_logger.warn("Unable to stop a VM due to " + e.getMessage()); + } + } + } + else + // host id can change + if (info != null && vm.getState() == State.Running){ + // check for host id changes + Host host = _hostDao.findByGuid(info.getHostUuid()); + if (host != null && (vm.getHostId() == null || host.getId() != vm.getHostId())){ + s_logger.info("Found vm " + vm.getInstanceName() + " with inconsistent host in db, new host is " + host.getId()); + try { + stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, host.getId()); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + } + } + /* else if(info == null && vm.getState() == State.Stopping) { //Handling CS-13376 + s_logger.warn("Marking the VM as Stopped as it was still stopping on the CS" +vm.getName()); + vm.setState(State.Stopped); // Setting the VM as stopped on the DB and clearing it from the host + vm.setLastHostId(vm.getHostId()); + vm.setHostId(null); + _vmDao.persist(vm); + }*/ + } + + for (final AgentVmInfo left : infos.values()) { + if (VirtualMachineName.isValidVmName(left.name)) continue; // if the vm follows cloudstack naming ignore it for stopping + try { + Host host = _hostDao.findByGuid(left.getHostUuid()); + if (host != null){ + s_logger.warn("Stopping a VM which we do not have any record of " + left.name); + Answer answer = _agentMgr.send(host.getId(), cleanup(left.name)); + if (!answer.getResult()) { + s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); + } + } + } catch (Exception e) { + s_logger.warn("Unable to stop a VM due to " + e.getMessage()); + } + } + + } + + + + protected Map convertToInfos(final Map> newStates) { + final HashMap map = new HashMap(); + if (newStates == null) { + return map; + } + Collection> vmGurus = _vmGurus.values(); + boolean is_alien_vm = true; + long alien_vm_count = -1; + for (Map.Entry> entry : newStates.entrySet()) { + is_alien_vm = true; + for (VirtualMachineGuru vmGuru : vmGurus) { + String name = entry.getKey(); + VMInstanceVO vm = vmGuru.findByName(name); + if (vm != null) { + map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue().second(), entry.getValue().first())); + is_alien_vm = false; + break; + } + Long id = vmGuru.convertToId(name); + if (id != null) { + map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null, entry.getValue().second(), entry.getValue().first())); + is_alien_vm = false; + break; + } + } + // alien VMs + if (is_alien_vm){ + map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, null, entry.getValue().second(), entry.getValue().first())); + s_logger.warn("Found an alien VM " + entry.getKey()); + } + } + return map; + } + + protected Map convertToInfos(StartupRoutingCommand cmd) { + final Map states = cmd.getVmStates(); + final HashMap map = new HashMap(); + if (states == null) { + return map; + } + Collection> vmGurus = _vmGurus.values(); + + for (Map.Entry entry : states.entrySet()) { + for (VirtualMachineGuru vmGuru : vmGurus) { + String name = entry.getKey(); + VMInstanceVO vm = vmGuru.findByName(name); + if (vm != null) { + map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue().getState(), entry.getValue().getHost() )); + break; + } + Long id = vmGuru.convertToId(name); + if (id != null) { + map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null,entry.getValue().getState(), entry.getValue().getHost() )); + break; + } + } + } + + return map; + } + + protected Map convertDeltaToInfos(final Map states) { + final HashMap map = new HashMap(); + + if (states == null) { + return map; + } + + Collection> vmGurus = _vmGurus.values(); + + for (Map.Entry entry : states.entrySet()) { + for (VirtualMachineGuru vmGuru : vmGurus) { + String name = entry.getKey(); + + VMInstanceVO vm = vmGuru.findByName(name); + + if (vm != null) { + map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vmGuru, vm, entry.getValue())); + break; + } + + Long id = vmGuru.convertToId(name); + if (id != null) { + map.put(id, new AgentVmInfo(entry.getKey(), vmGuru, null,entry.getValue())); + break; + } + } + } + + return map; + } + + + + /** + * compareState does as its name suggests and compares the states between + * management server and agent. It returns whether something should be + * cleaned up + * + */ + protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean trackExternalChange) { + State agentState = info.state; + final String agentName = info.name; + final State serverState = vm.getState(); + final String serverName = vm.getInstanceName(); + + Command command = null; + s_logger.debug("VM " + serverName + ": cs state = " + serverState + " and realState = " + agentState); + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM " + serverName + ": cs state = " + serverState + " and realState = " + agentState); + } + + if (agentState == State.Error) { + agentState = State.Stopped; + + short alertType = AlertManager.ALERT_TYPE_USERVM; + if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) { + alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER; + } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) { + alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY; + } else if (VirtualMachine.Type.SecondaryStorageVm.equals(vm.getType())) { + alertType = AlertManager.ALERT_TYPE_SSVM; + } + + HostPodVO podVO = _podDao.findById(vm.getPodIdToDeployIn()); + DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); + HostVO hostVO = _hostDao.findById(vm.getHostId()); + + String hostDesc = "name: " + hostVO.getName() + " (id:" + hostVO.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); + _alertMgr.sendAlert(alertType, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), "VM (name: " + vm.getInstanceName() + ", id: " + vm.getId() + ") stopped on host " + hostDesc + + " due to storage failure", "Virtual Machine " + vm.getInstanceName() + " (id: " + vm.getId() + ") running on host [" + vm.getHostId() + "] stopped due to storage failure."); + } + + if (trackExternalChange) { + if (serverState == State.Starting) { + if (vm.getHostId() != null && vm.getHostId() != hostId) { + s_logger.info("CloudStack is starting VM on host " + vm.getHostId() + ", but status report comes from a different host " + hostId + ", skip status sync for vm: " + + vm.getInstanceName()); + return null; + } + } + if (vm.getHostId() == null || hostId != vm.getHostId()) { + try { + stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId); + } catch (NoTransitionException e) { + } + } + } + + // during VM migration time, don't sync state will agent status update + if (serverState == State.Migrating) { + s_logger.debug("Skipping vm in migrating state: " + vm); + return null; + } + + if (trackExternalChange) { + if (serverState == State.Starting) { + if (vm.getHostId() != null && vm.getHostId() != hostId) { + s_logger.info("CloudStack is starting VM on host " + vm.getHostId() + ", but status report comes from a different host " + hostId + ", skip status sync for vm: " + + vm.getInstanceName()); + return null; + } + } + + if (serverState == State.Running) { + try { + // + // we had a bug that sometimes VM may be at Running State + // but host_id is null, we will cover it here. + // means that when CloudStack DB lost of host information, + // we will heal it with the info reported from host + // + if (vm.getHostId() == null || hostId != vm.getHostId()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("detected host change when VM " + vm + " is at running state, VM could be live-migrated externally from host " + vm.getHostId() + " to host " + hostId); + } + + stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId); + } + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + } + } + + if (agentState == serverState) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Both states are " + agentState + " for " + vm); + } + assert (agentState == State.Stopped || agentState == State.Running) : "If the states we send up is changed, this must be changed."; + if (agentState == State.Running) { + try { + stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, hostId); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + // FIXME: What if someone comes in and sets it to stopping? Then + // what? + return null; + } + + s_logger.debug("State matches but the agent said stopped so let's send a cleanup command anyways."); + return cleanup(agentName); + } + + if (agentState == State.Shutdowned) { + if (serverState == State.Running || serverState == State.Starting || serverState == State.Stopping) { + try { + advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); + } catch (AgentUnavailableException e) { + assert (false) : "How do we hit this with forced on?"; + return null; + } catch (OperationTimedoutException e) { + assert (false) : "How do we hit this with forced on?"; + return null; + } catch (ConcurrentOperationException e) { + assert (false) : "How do we hit this with forced on?"; + return null; + } + } else { + s_logger.debug("Sending cleanup to a shutdowned vm: " + agentName); + command = cleanup(agentName); + } + } else if (agentState == State.Stopped) { + // This state means the VM on the agent was detected previously + // and now is gone. This is slightly different than if the VM + // was never completed but we still send down a Stop Command + // to ensure there's cleanup. + if (serverState == State.Running) { + // Our records showed that it should be running so let's restart + // it. + _haMgr.scheduleRestart(vm, false); + } else if (serverState == State.Stopping) { + _haMgr.scheduleStop(vm, hostId, WorkType.ForceStop); + s_logger.debug("Scheduling a check stop for VM in stopping mode: " + vm); + } else if (serverState == State.Starting) { + s_logger.debug("Ignoring VM in starting mode: " + vm.getInstanceName()); + _haMgr.scheduleRestart(vm, false); + } + command = cleanup(agentName); + } else if (agentState == State.Running) { + if (serverState == State.Starting) { + if (fullSync) { + try { + ensureVmRunningContext(hostId, vm, Event.AgentReportRunning); + } catch (OperationTimedoutException e) { + s_logger.error("Exception during update for running vm: " + vm, e); + return null; + } catch (ResourceUnavailableException e) { + s_logger.error("Exception during update for running vm: " + vm, e); + return null; + }catch (InsufficientAddressCapacityException e) { + s_logger.error("Exception during update for running vm: " + vm, e); + return null; + }catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + } + } else if (serverState == State.Stopping) { + s_logger.debug("Scheduling a stop command for " + vm); + _haMgr.scheduleStop(vm, hostId, WorkType.Stop); + } else { + s_logger.debug("server VM state " + serverState + " does not meet expectation of a running VM report from agent"); + + // just be careful not to stop VM for things we don't handle + // command = cleanup(agentName); + } + } + return command; + } + + private void ensureVmRunningContext(long hostId, VMInstanceVO vm, Event cause) throws OperationTimedoutException, ResourceUnavailableException, NoTransitionException, InsufficientAddressCapacityException { + VirtualMachineGuru vmGuru = getVmGuru(vm); + + s_logger.debug("VM state is starting on full sync so updating it to running"); + vm = findByIdAndType(vm.getType(), vm.getId()); + + // grab outstanding work item if any + ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found an outstanding work item for this vm " + vm + " in state:" + vm.getState() + ", work id:" + work.getId()); + } + } + + try { + stateTransitTo(vm, cause, hostId); + } catch (NoTransitionException e1) { + s_logger.warn(e1.getMessage()); + } + + s_logger.debug("VM's " + vm + " state is starting on full sync so updating it to Running"); + vm = vmGuru.findById(vm.getId()); // this should ensure vm has the most + // up to date info + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + List nics = _nicsDao.listByVmId(profile.getId()); + for (NicVO nic : nics) { + Network network = _networkMgr.getNetwork(nic.getNetworkId()); + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, + _networkMgr.isSecurityGroupSupportedInNetwork(network), _networkMgr.getNetworkTag(profile.getHypervisorType(), network)); + profile.addNic(nicProfile); + } + + Commands cmds = new Commands(OnError.Stop); + s_logger.debug("Finalizing commands that need to be send to complete Start process for the vm " + vm); + + if (vmGuru.finalizeCommandsOnStart(cmds, profile)) { + if (cmds.size() != 0) { + _agentMgr.send(vm.getHostId(), cmds); + } + + if (vmGuru.finalizeStart(profile, vm.getHostId(), cmds, null)) { + stateTransitTo(vm, cause, vm.getHostId()); + } else { + s_logger.error("Unable to finish finialization for running vm: " + vm); + } + } else { + s_logger.error("Unable to finalize commands on start for vm: " + vm); + } + + if (work != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updating outstanding work item to Done, id:" + work.getId()); + } + work.setStep(Step.Done); + _workDao.update(work.getId(), work); + } + } + + @Override + public boolean isRecurring() { + return true; + } + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + for (final Answer answer : answers) { + if (answer instanceof ClusterSyncAnswer) { + ClusterSyncAnswer hs = (ClusterSyncAnswer) answer; + if (!hs.isExceuted()){ + deltaSync(hs.getNewStates()); + hs.setExecuted(); + } + } + } + return true; + } + + @Override + public boolean processTimeout(long agentId, long seq) { + return true; + } + + @Override + public int getTimeout() { + return -1; + } + + @Override + public boolean processCommands(long agentId, long seq, Command[] cmds) { + boolean processed = false; + for (Command cmd : cmds) { + if (cmd instanceof PingRoutingCommand) { + PingRoutingCommand ping = (PingRoutingCommand) cmd; + if (ping.getNewStates() != null && ping.getNewStates().size() > 0) { + Commands commands = deltaHostSync(agentId, ping.getNewStates()); + if (commands.size() > 0) { + try { + _agentMgr.send(agentId, commands, this); + } catch (final AgentUnavailableException e) { + s_logger.warn("Agent is now unavailable", e); + } + } + } + processed = true; + } + } + return processed; + } + + @Override + public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { + return null; + } + + @Override + public boolean processDisconnect(long agentId, Status state) { + return true; + } + + @Override + public void processConnect(HostVO agent, StartupCommand cmd, boolean forRebalance) throws ConnectionException { + if (!(cmd instanceof StartupRoutingCommand)) { + return; + } + + if (forRebalance) { + s_logger.debug("Not processing listener " + this + " as connect happens on rebalance process"); + return; + } + + if (forRebalance) { + s_logger.debug("Not processing listener " + this + " as connect happens on rebalance process"); + return; + } + + Long clusterId = agent.getClusterId(); + long agentId = agent.getId(); + if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen + StartupRoutingCommand startup = (StartupRoutingCommand) cmd; + HashMap> allStates = startup.getClusterVMStateChanges(); + if (allStates != null){ + this.fullSync(clusterId, allStates); + } + + // initiate the cron job + ClusterSyncCommand syncCmd = new ClusterSyncCommand(Integer.parseInt(Config.ClusterDeltaSyncInterval.getDefaultValue()), clusterId); + try { + long seq_no = _agentMgr.send(agentId, new Commands(syncCmd), this); + s_logger.debug("Cluster VM sync started with jobid " + seq_no); + } catch (AgentUnavailableException e) { + s_logger.fatal("The Cluster VM sync process failed for cluster id " + clusterId + " with ", e); + } + } + else { // for others KVM and VMWare + StartupRoutingCommand startup = (StartupRoutingCommand) cmd; + Commands commands = fullHostSync(agentId, startup); + + if (commands.size() > 0) { + s_logger.debug("Sending clean commands to the agent"); + + try { + boolean error = false; + Answer[] answers = _agentMgr.send(agentId, commands); + for (Answer answer : answers) { + if (!answer.getResult()) { + s_logger.warn("Unable to stop a VM due to " + answer.getDetails()); + error = true; + } + } + if (error) { + throw new ConnectionException(true, "Unable to stop VMs"); + } + } catch (final AgentUnavailableException e) { + s_logger.warn("Agent is unavailable now", e); + throw new ConnectionException(true, "Unable to sync", e); + } catch (final OperationTimedoutException e) { + s_logger.warn("Agent is unavailable now", e); + throw new ConnectionException(true, "Unable to sync", e); + } + } + + } + } + + protected class TransitionTask implements Runnable { + @Override + public void run() { + GlobalLock lock = GlobalLock.getInternLock("TransitionChecking"); + if (lock == null) { + s_logger.debug("Couldn't get the global lock"); + return; + } + + if (!lock.lock(30)) { + s_logger.debug("Couldn't lock the db"); + return; + } + try { + lock.addRef(); + List instances = _vmDao.findVMInTransition(new Date(new Date().getTime() - (_operationTimeout * 1000)), State.Starting, State.Stopping); + for (VMInstanceVO instance : instances) { + State state = instance.getState(); + if (state == State.Stopping) { + _haMgr.scheduleStop(instance, instance.getHostId(), WorkType.CheckStop); + } else if (state == State.Starting) { + _haMgr.scheduleRestart(instance, true); + } + } + } catch (Exception e) { + s_logger.warn("Caught the following exception on transition checking", e); + } finally { + StackMaid.current().exitCleanup(); + lock.unlock(); + } + } + } + + protected class AgentVmInfo { + public String name; + public State state; + public String hostUuid; + public VMInstanceVO vm; + public VirtualMachineGuru guru; + + @SuppressWarnings("unchecked") + public AgentVmInfo(String name, VirtualMachineGuru guru, VMInstanceVO vm, State state, String host) { + this.name = name; + this.state = state; + this.vm = vm; + this.guru = (VirtualMachineGuru) guru; + this.hostUuid = host; + } + + public AgentVmInfo(String name, VirtualMachineGuru guru, VMInstanceVO vm, State state) { + this(name, guru, vm, state, null); + } + + public String getHostUuid() { + return hostUuid; + } + } + + @Override + public VMInstanceVO findById(long vmId) { + return _vmDao.findById(vmId); + } + + @Override + public void checkIfCanUpgrade(VirtualMachine vmInstance, long newServiceOfferingId) { + ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId); + if (newServiceOffering == null) { + throw new InvalidParameterValueException("Unable to find a service offering by id", null); + } + + // Check that the VM is stopped + if (!vmInstance.getState().equals(State.Stopped)) { + s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); + throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + + "in state " + vmInstance.getState() + + "; make sure the virtual machine is stopped and not in an error state before upgrading.", null); + } + + // Check if the service offering being upgraded to is what the VM is already running with + if (vmInstance.getServiceOfferingId() == newServiceOffering.getId()) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested " + + "service offering (" + newServiceOffering.getName() + ")"); + } + + throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already " + + "has the requested service offering (" + newServiceOffering.getName() + ")", null); + } + + ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + + // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering + // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore. + /* + * if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { String errorMsg = + * "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); errorMsg += + * ". Please select a service offering with the same guest IP type as the VM's current service offering (" + + * currentServiceOffering.getGuestIpType() + ")."; throw new InvalidParameterValueException(errorMsg); } + */ + + // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service + // offering + if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + + ", cannot switch between local storage and shared storage service offerings. Current offering " + + "useLocalStorage=" + currentServiceOffering.getUseLocalStorage() + + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage(), null); + } + + // if vm is a system vm, check if it is a system service offering, if yes return with error as it cannot be used for user vms + if (currentServiceOffering.getSystemUse() != newServiceOffering.getSystemUse()) { + throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering", null); + } + + // Check that there are enough resources to upgrade the service offering + if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + + "for an offering of " + newServiceOffering.getCpu() + " cpu(s) at " + + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory", null); + } + + // Check that the service offering being upgraded to has all the tags of the current service offering + List currentTags = _configMgr.csvTagsToList(currentServiceOffering.getTags()); + List newTags = _configMgr.csvTagsToList(newServiceOffering.getTags()); + if (!newTags.containsAll(currentTags)) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine; the new service offering " + + "does not have all the tags of the " + + "current service offering. Current service offering tags: " + currentTags + "; " + "new service " + + "offering tags: " + newTags, null); + } + } + + @Override + public boolean upgradeVmDb(long vmId, long serviceOfferingId) { + VMInstanceVO vmForUpdate = _vmDao.createForUpdate(); + vmForUpdate.setServiceOfferingId(serviceOfferingId); + ServiceOffering newSvcOff = _configMgr.getServiceOffering(serviceOfferingId); + vmForUpdate.setHaEnabled(newSvcOff.getOfferHA()); + vmForUpdate.setLimitCpuUse(newSvcOff.getLimitCpuUse()); + vmForUpdate.setServiceOfferingId(newSvcOff.getId()); + return _vmDao.update(vmId, vmForUpdate); + } + + @Override + public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + + s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); + VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + null, null, null); + + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + Host host = _hostDao.findById(vm.getHostId()); + DeployDestination dest = new DeployDestination(dc, null, null, host); + + //check vm state + if (vm.getState() == State.Running) { + //1) allocate and prepare nic + NicProfile nic = _networkMgr.createNicForVm(network, requested, context, vmProfile, true); + + //2) Convert vmProfile to vmTO + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + //3) Convert nicProfile to NicTO + NicTO nicTO = toNicTO(nic, vmProfile.getVirtualMachine().getHypervisorType()); + + //4) plug the nic to the vm + VirtualMachineGuru vmGuru = getVmGuru(vmVO); + + s_logger.debug("Plugging nic for vm " + vm + " in network " + network); + if (vmGuru.plugNic(network, nicTO, vmTO, context, dest)) { + s_logger.debug("Nic is plugged successfully for vm " + vm + " in network " + network + ". Vm is a part of network now"); + return nic; + } else { + s_logger.warn("Failed to plug nic to the vm " + vm + " in network " + network); + return null; + } + } else if (vm.getState() == State.Stopped) { + //1) allocate nic + return _networkMgr.createNicForVm(network, requested, context, vmProfile, false); + } else { + s_logger.warn("Unable to add vm " + vm + " to network " + network); + throw new ResourceUnavailableException("Unable to add vm " + vm + " to network, is not in the right state", + DataCenter.class, vm.getDataCenterIdToDeployIn()); + } + } + + + @Override + public NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType) { + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType); + + NicTO nicTO = hvGuru.toNicTO(nic); + return nicTO; + } + + @Override + public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { + VMInstanceVO vmVO = _vmDao.findById(vm.getId()); + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); + + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + null, null, null); + + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + Host host = _hostDao.findById(vm.getHostId()); + DeployDestination dest = new DeployDestination(dc, null, null, host); + VirtualMachineGuru vmGuru = getVmGuru(vmVO); + HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); + VirtualMachineTO vmTO = hvGuru.implement(vmProfile); + + Nic nic = null; + + if (broadcastUri != null) { + nic = _nicsDao.findByNetworkIdInstanceIdAndBroadcastUri(network.getId(), vm.getId(), broadcastUri.toString()); + } else { + nic = _networkMgr.getNicInNetwork(vm.getId(), network.getId()); + } + + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + _networkMgr.getNetworkRate(network.getId(), vm.getId()), + _networkMgr.isSecurityGroupSupportedInNetwork(network), + _networkMgr.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); + + //1) Unplug the nic + NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); + s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); + boolean result = vmGuru.unplugNic(network, nicTO, vmTO, context, dest); + if (result) { + s_logger.debug("Nic is unplugged successfully for vm " + vm + " in network " + network ); + } else { + s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); + return false; + } + + //2) Release the nic + _networkMgr.releaseNic(vmProfile, nic); + s_logger.debug("Successfully released nic " + nic + "for vm " + vm); + + //3) Remove the nic + _networkMgr.removeNic(vmProfile, nic); + return result; + } + +}