diff --git a/agent-simulator/src/com/cloud/agent/manager/MockAgentManager.java b/agent-simulator/src/com/cloud/agent/manager/MockAgentManager.java index a97b68017ee..8900bb2d33f 100644 --- a/agent-simulator/src/com/cloud/agent/manager/MockAgentManager.java +++ b/agent-simulator/src/com/cloud/agent/manager/MockAgentManager.java @@ -46,11 +46,11 @@ public interface MockAgentManager extends Manager { Answer checkHealth(CheckHealthCommand cmd); Answer pingTest(PingTestCommand cmd); - Answer PrepareForMigration(PrepareForMigrationCommand cmd); + Answer prepareForMigrate(PrepareForMigrationCommand cmd); MockHost getHost(String guid); - Answer MaintainCommand(MaintainCommand cmd); + Answer maintain(MaintainCommand cmd); Answer checkNetworkCommand(CheckNetworkCommand cmd); } diff --git a/agent-simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java b/agent-simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java index 7fb96dd7310..e83e2aa598e 100755 --- a/agent-simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java +++ b/agent-simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java @@ -41,10 +41,9 @@ import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.PingTestCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.dc.dao.HostPodDao; import com.cloud.host.Host; -import com.cloud.host.Host.Type; import com.cloud.resource.AgentResourceBase; import com.cloud.resource.AgentRoutingResource; import com.cloud.resource.AgentStorageResource; @@ -336,7 +335,11 @@ public class MockAgentManagerImpl implements MockAgentManager { @Override - public PrepareForMigrationAnswer PrepareForMigration(PrepareForMigrationCommand cmd) { + public PrepareForMigrationAnswer prepareForMigrate(PrepareForMigrationCommand cmd) { + VirtualMachineTO vm = cmd.getVirtualMachine(); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Preparing host for migrating " + vm); + } return new PrepareForMigrationAnswer(cmd); } @@ -360,7 +363,7 @@ public class MockAgentManagerImpl implements MockAgentManager { @Override - public MaintainAnswer MaintainCommand(com.cloud.agent.api.MaintainCommand cmd) { + public MaintainAnswer maintain(com.cloud.agent.api.MaintainCommand cmd) { return new MaintainAnswer(cmd); } diff --git a/agent-simulator/src/com/cloud/agent/manager/MockVmManager.java b/agent-simulator/src/com/cloud/agent/manager/MockVmManager.java index 10ae30096e4..d7dd76569f6 100644 --- a/agent-simulator/src/com/cloud/agent/manager/MockVmManager.java +++ b/agent-simulator/src/com/cloud/agent/manager/MockVmManager.java @@ -51,7 +51,7 @@ public interface MockVmManager extends Manager { public Answer stopVM(StopCommand cmd); public Answer rebootVM(RebootCommand cmd); - public Answer checkVmState(CheckVirtualMachineCommand cmd, String hostGuid); + public Answer checkVmState(CheckVirtualMachineCommand cmd); public Map getVmStates(String hostGuid); public Answer getVncPort(GetVncPortCommand cmd); diff --git a/agent-simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java b/agent-simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java index cc98cf9371e..fff9381bd66 100644 --- a/agent-simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java +++ b/agent-simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java @@ -239,8 +239,8 @@ public class MockVmManagerImpl implements MockVmManager { } @Override - public CheckVirtualMachineAnswer checkVmState(CheckVirtualMachineCommand cmd, String hostGuid) { - MockVMVO vm = _mockVmDao.findByVmNameAndHost(cmd.getVmName(), hostGuid); + public CheckVirtualMachineAnswer checkVmState(CheckVirtualMachineCommand cmd) { + MockVMVO vm = _mockVmDao.findByVmName(cmd.getVmName()); if (vm == null) { return new CheckVirtualMachineAnswer(cmd, "can't find vm:" + cmd.getVmName()); } diff --git a/agent-simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java b/agent-simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java index 1517c94d91a..a1233dfeb28 100644 --- a/agent-simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java +++ b/agent-simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java @@ -15,7 +15,6 @@ package com.cloud.agent.manager; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; -import java.util.List; import java.util.Map; import javax.ejb.Local; @@ -29,6 +28,7 @@ import com.cloud.agent.api.AttachVolumeCommand; import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.CheckHealthCommand; import com.cloud.agent.api.CheckNetworkCommand; +import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.ClusterSyncCommand; import com.cloud.agent.api.Command; @@ -50,6 +50,7 @@ import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageVMSetupCommand; @@ -76,7 +77,6 @@ import com.cloud.agent.api.storage.DownloadCommand; import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.ListTemplateCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; -import com.cloud.agent.mockvm.MockVm; import com.cloud.simulator.MockConfigurationVO; import com.cloud.simulator.MockHost; import com.cloud.simulator.MockVMVO; @@ -193,12 +193,16 @@ public class SimulatorManagerImpl implements SimulatorManager { return _mockAgentMgr.checkHealth((CheckHealthCommand)cmd); } else if (cmd instanceof PingTestCommand) { return _mockAgentMgr.pingTest((PingTestCommand)cmd); + } else if (cmd instanceof PrepareForMigrationCommand) { + return _mockAgentMgr.prepareForMigrate((PrepareForMigrationCommand)cmd); } else if (cmd instanceof MigrateCommand) { return _mockVmMgr.Migrate((MigrateCommand)cmd, info); } else if (cmd instanceof StartCommand) { return _mockVmMgr.startVM((StartCommand)cmd, info); } else if (cmd instanceof CheckSshCommand) { return _mockVmMgr.checkSshCommand((CheckSshCommand)cmd); + } else if (cmd instanceof CheckVirtualMachineCommand) { + return _mockVmMgr.checkVmState((CheckVirtualMachineCommand)cmd); } else if (cmd instanceof SetStaticNatRulesCommand) { return _mockVmMgr.SetStaticNatRules((SetStaticNatRulesCommand)cmd); } else if (cmd instanceof SetFirewallRulesCommand) { @@ -278,7 +282,7 @@ public class SimulatorManagerImpl implements SimulatorManager { } else if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) { return _mockStorageMgr.CreatePrivateTemplateFromVolume((CreatePrivateTemplateFromVolumeCommand)cmd); } else if (cmd instanceof MaintainCommand) { - return _mockAgentMgr.MaintainCommand((MaintainCommand)cmd); + return _mockAgentMgr.maintain((MaintainCommand)cmd); } else if (cmd instanceof GetVmStatsCommand) { return _mockVmMgr.getVmStats((GetVmStatsCommand)cmd); } else if (cmd instanceof GetDomRVersionCmd) { diff --git a/agent-simulator/src/com/cloud/resource/AgentRoutingResource.java b/agent-simulator/src/com/cloud/resource/AgentRoutingResource.java index fe4534f681a..24b6cbd174c 100644 --- a/agent-simulator/src/com/cloud/resource/AgentRoutingResource.java +++ b/agent-simulator/src/com/cloud/resource/AgentRoutingResource.java @@ -29,6 +29,7 @@ import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingWithNwGroupsCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyCommand; @@ -81,8 +82,6 @@ public class AgentRoutingResource extends AgentStorageResource { return execute((StartCommand) cmd); } else if (cmd instanceof StopCommand) { return execute((StopCommand) cmd); - } else if (cmd instanceof PrepareForMigrationCommand) { - return execute((PrepareForMigrationCommand) cmd); } else if (cmd instanceof CheckVirtualMachineCommand) { return execute((CheckVirtualMachineCommand) cmd); } else if (cmd instanceof ReadyCommand) { @@ -187,7 +186,7 @@ public class AgentRoutingResource extends AgentStorageResource { String vmName = vmSpec.getName(); if (this.totalCpu < (vmSpec.getCpus() * vmSpec.getSpeed() + this.usedCpu) || this.totalMem < (vmSpec.getMaxRam() + this.usedMem)) { - return new StartAnswer(cmd, "No enough resource to start the vm"); + return new StartAnswer(cmd, "Not enough resource to start the vm"); } State state = State.Stopped; synchronized (_vms) { diff --git a/api/src/com/cloud/agent/api/NetworkUsageCommand.java b/api/src/com/cloud/agent/api/NetworkUsageCommand.java index 14621ffd1dc..324e1a590eb 100644 --- a/api/src/com/cloud/agent/api/NetworkUsageCommand.java +++ b/api/src/com/cloud/agent/api/NetworkUsageCommand.java @@ -54,6 +54,15 @@ public class NetworkUsageCommand extends Command { this.vpcCIDR = vpcCIDR; } + public NetworkUsageCommand(String privateIP, String domRName, String option, boolean forVpc, String gatewayIP) + { + this.privateIP = privateIP; + this.domRName = domRName; + this.forVpc = forVpc; + this.gatewayIP = gatewayIP; + this.option = option; + } + public String getPrivateIP() { return privateIP; } diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java index 2ca70577c14..faa4d5b71eb 100755 --- a/api/src/com/cloud/api/BaseCmd.java +++ b/api/src/com/cloud/api/BaseCmd.java @@ -36,7 +36,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.NetworkService; import com.cloud.network.StorageNetworkService; -import com.cloud.network.VirtualNetworkApplianceService; +import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.as.AutoScaleService; import com.cloud.network.firewall.FirewallService; import com.cloud.network.firewall.NetworkACLService; @@ -117,7 +117,7 @@ public abstract class BaseCmd { public static SecurityGroupService _securityGroupService; public static SnapshotService _snapshotService; public static ConsoleProxyService _consoleProxyService; - public static VirtualNetworkApplianceService _routerService; + public static VpcVirtualNetworkApplianceService _routerService; public static ResponseGenerator _responseGenerator; public static EntityManager _entityMgr; public static RulesService _rulesService; @@ -149,7 +149,7 @@ public abstract class BaseCmd { _securityGroupService = locator.getManager(SecurityGroupService.class); _snapshotService = locator.getManager(SnapshotService.class); _consoleProxyService = locator.getManager(ConsoleProxyService.class); - _routerService = locator.getManager(VirtualNetworkApplianceService.class); + _routerService = locator.getManager(VpcVirtualNetworkApplianceService.class); _entityMgr = locator.getManager(EntityManager.class); _rulesService = locator.getManager(RulesService.class); _lbService = locator.getManager(LoadBalancingRulesService.class); diff --git a/api/src/com/cloud/api/commands/CreateVPCCmd.java b/api/src/com/cloud/api/commands/CreateVPCCmd.java index 9b5fd4677b3..53e9947287b 100644 --- a/api/src/com/cloud/api/commands/CreateVPCCmd.java +++ b/api/src/com/cloud/api/commands/CreateVPCCmd.java @@ -52,6 +52,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd{ "If used with the account parameter returns the VPC associated with the account for the specified domain.") private Long domainId; + @IdentityMapper(entityTableName="projects") + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="create VPC for the project") + private Long projectId; + @IdentityMapper(entityTableName="data_center") @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="the ID of the availability zone") private Long zoneId; @@ -72,7 +76,8 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd{ @Parameter(name=ApiConstants.VPC_OFF_ID, type=CommandType.LONG, required=true, description="the ID of the VPC offering") private Long vpcOffering; - @Parameter(name=ApiConstants.NETWORK_DOMAIN, type=CommandType.STRING, description="network domain") + @Parameter(name=ApiConstants.NETWORK_DOMAIN, type=CommandType.STRING, + description="VPC network domain. All networks inside the VPC will belong to this domain") private String networkDomain; ///////////////////////////////////////////////////// @@ -174,7 +179,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd{ @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return UserContext.current().getCaller().getId(); } diff --git a/api/src/com/cloud/api/commands/ListVPCsCmd.java b/api/src/com/cloud/api/commands/ListVPCsCmd.java index e60c6e290f6..803aee689da 100644 --- a/api/src/com/cloud/api/commands/ListVPCsCmd.java +++ b/api/src/com/cloud/api/commands/ListVPCsCmd.java @@ -137,7 +137,7 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd{ List vpcs = _vpcService.listVpcs(getId(), getVpcName(), getDisplayText(), getSupportedServices(), getCidr(), getVpcOffId(), getState(), getAccountName(), getDomainId(), this.getKeyword(), this.getStartIndex(), this.getPageSizeVal(), getZoneId(), this.isRecursive(), - this.listAll(), getRestartRequired(), getTags()); + this.listAll(), getRestartRequired(), getTags(), getProjectId()); ListResponse response = new ListResponse(); List offeringResponses = new ArrayList(); for (Vpc vpc : vpcs) { diff --git a/api/src/com/cloud/api/response/DiskOfferingResponse.java b/api/src/com/cloud/api/response/DiskOfferingResponse.java index b2a34a1d862..6847646a4f1 100644 --- a/api/src/com/cloud/api/response/DiskOfferingResponse.java +++ b/api/src/com/cloud/api/response/DiskOfferingResponse.java @@ -47,6 +47,9 @@ public class DiskOfferingResponse extends BaseResponse { @SerializedName(ApiConstants.TAGS) @Param(description="the tags for the disk offering") private String tags; + @SerializedName("storagetype") @Param(description="the storage type for this disk offering") + private String storageType; + public Long getId() { return id.getValue(); } @@ -119,4 +122,11 @@ public class DiskOfferingResponse extends BaseResponse { this.customized = customized; } + public String getStorageType() { + return storageType; + } + + public void setStorageType(String storageType) { + this.storageType = storageType; + } } diff --git a/api/src/com/cloud/api/response/VpcResponse.java b/api/src/com/cloud/api/response/VpcResponse.java index 8d27ca4a7c9..149cbc30359 100644 --- a/api/src/com/cloud/api/response/VpcResponse.java +++ b/api/src/com/cloud/api/response/VpcResponse.java @@ -74,10 +74,10 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons @SerializedName(ApiConstants.NETWORK) @Param(description="the list of networks belongign to the VPC", responseObject = NetworkResponse.class) private List networks; - @SerializedName(ApiConstants.RESTART_REQUIRED) @Param(description="true network requires restart") + @SerializedName(ApiConstants.RESTART_REQUIRED) @Param(description="true VPC requires restart") private Boolean restartRequired; - @SerializedName(ApiConstants.NETWORK_DOMAIN) @Param(description="the network domain") + @SerializedName(ApiConstants.NETWORK_DOMAIN) @Param(description="the network domain of the VPC") private String networkDomain; @SerializedName(ApiConstants.TAGS) @Param(description="the list of resource tags associated with the project", responseObject = ResourceTagResponse.class) diff --git a/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java b/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java index 3123cc2432b..eaaecf1a3c0 100644 --- a/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java +++ b/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java @@ -20,7 +20,7 @@ import com.cloud.network.router.VirtualRouter; /** * @author Alena Prokharchyk */ -public interface VpcVirtualNetworkApplianceService { +public interface VpcVirtualNetworkApplianceService extends VirtualNetworkApplianceService{ /** * @param router diff --git a/api/src/com/cloud/network/vpc/VpcService.java b/api/src/com/cloud/network/vpc/VpcService.java index 707443da106..88a7fd88ed2 100644 --- a/api/src/com/cloud/network/vpc/VpcService.java +++ b/api/src/com/cloud/network/vpc/VpcService.java @@ -113,13 +113,14 @@ public interface VpcService { * @param listAll TODO * @param restartRequired TODO * @param tags TODO + * @param projectId TODO * @param vpc * @return */ public List listVpcs(Long id, String vpcName, String displayText, List supportedServicesStr, String cidr, Long vpcOffId, String state, String accountName, Long domainId, String keyword, Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, - Boolean restartRequired, Map tags); + Boolean restartRequired, Map tags, Long projectId); /** * @param vpcId diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index e24bc3e7e56..bc702705aae 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -32,7 +32,7 @@ public interface Volume extends ControlledEntity, BasedOn, StateObject getSvcOfferingByName( String name ){ + return queryEntities("from CloudStackServiceOffering where name=?", new Object[] {name}); + } public CloudStackServiceOffering getSvcOfferingById( String id ){ return queryEntity("from CloudStackServiceOffering where id=?", new Object[] {id}); diff --git a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java index e239175f131..c1c6d8c6dd9 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java +++ b/awsapi/src/com/cloud/bridge/service/EC2SoapServiceImpl.java @@ -258,6 +258,10 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface { List resourceTypeList = new ArrayList(); if (items != null) { for( int i=0; i < items.length; i++ ) { + if (!items[i].getResourceId().contains(":") || items[i].getResourceId().split(":").length != 2) { + throw new EC2ServiceException( ClientError.InvalidResourceId_Format, + "Invalid Format. ResourceId format is resource-type:resource-uuid"); + } String resourceType = items[i].getResourceId().split(":")[0]; if (resourceTypeList.isEmpty()) resourceTypeList.add(resourceType); diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index fd54eb4d61f..449eaecee5a 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -1775,8 +1775,12 @@ public class EC2Engine { if (null == instanceType) instanceType = "m1.small"; CloudStackSvcOfferingDao dao = new CloudStackSvcOfferingDao(); - return dao.getSvcOfferingByName(instanceType); - + List svcOfferingList = dao.getSvcOfferingByName(instanceType); + for (CloudStackServiceOffering svcOffering : svcOfferingList) { + if (svcOffering.getRemoved() == null) + return svcOffering; + } + return null; } catch(Exception e) { logger.error( "Error while retrieving ServiceOffering information by name - ", e); throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); diff --git a/awsapi/src/com/cloud/bridge/service/exception/EC2ServiceException.java b/awsapi/src/com/cloud/bridge/service/exception/EC2ServiceException.java index 8b26d61d40c..aca0c0a525c 100644 --- a/awsapi/src/com/cloud/bridge/service/exception/EC2ServiceException.java +++ b/awsapi/src/com/cloud/bridge/service/exception/EC2ServiceException.java @@ -77,6 +77,7 @@ public class EC2ServiceException extends RuntimeException { InvalidPermission_Malformed("Client.InvalidPermission.Malformed", 400), InvalidReservationID_Malformed("Client.InvalidReservationID.Malformed", 400), InvalidReservationID_NotFound("Client.InvalidReservationID.NotFound", 400), + InvalidResourceId_Format("Client.InvalidResourceId.Format", 400), InvalidSnapshotID_Malformed("Client.InvalidSnapshotID.Malformed", 400), InvalidSnapshot_NotFound("Client.InvalidSnapshot.NotFound", 400), InvalidUserID_Malformed("Client.InvalidUserID.Malformed", 400), diff --git a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.hbm.xml b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.hbm.xml index f3c5d4d17f3..e9c73b82434 100644 --- a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.hbm.xml +++ b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.hbm.xml @@ -11,6 +11,9 @@ + + + diff --git a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java index 9dde25a553a..e3df1aaed8d 100644 --- a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java +++ b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java @@ -57,6 +57,8 @@ public class CloudStackServiceOffering { private String systemVmType; @SerializedName(ApiConstants.TAGS) private String tags; + @SerializedName(ApiConstants.REMOVED) + private String removed; /** * @@ -196,4 +198,14 @@ public class CloudStackServiceOffering { return tags; } + /** + * @return the removed + */ + public String getRemoved() { + return removed; + } + + public void setRemoved(String removed) { + this.removed = removed; + } } diff --git a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index c2a7d609d7c..47de7d51e48 100755 --- a/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/core/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -741,7 +741,95 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return new SetStaticNatRulesAnswer(cmd, results, endResult); } + protected Answer VPCLoadBalancerConfig(final LoadBalancerConfigCommand cmd) { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + File keyFile = mgr.getSystemVMKeyFile(); + + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + String controlIp = getRouterSshControlIp(cmd); + + assert(controlIp != null); + + LoadBalancerConfigurator cfgtr = new HAProxyConfigurator(); + String[] config = cfgtr.generateConfiguration(cmd); + + String tmpCfgFilePath = "/etc/haproxy/haproxy.cfg.new"; + String tmpCfgFileContents = ""; + for (int i = 0; i < config.length; i++) { + tmpCfgFileContents += config[i]; + tmpCfgFileContents += "\n"; + } + + try { + SshHelper.scpTo(controlIp, DEFAULT_DOMR_SSHPORT, "root", keyFile, null, "/etc/haproxy/", tmpCfgFileContents.getBytes(), "haproxy.cfg.new", null); + + try { + String[][] rules = cfgtr.generateFwRules(cmd); + + String[] addRules = rules[LoadBalancerConfigurator.ADD]; + String[] removeRules = rules[LoadBalancerConfigurator.REMOVE]; + String[] statRules = rules[LoadBalancerConfigurator.STATS]; + + String args = ""; + String ip = cmd.getNic().getIp(); + args += " -i " + ip; + StringBuilder sb = new StringBuilder(); + if (addRules.length > 0) { + for (int i = 0; i < addRules.length; i++) { + sb.append(addRules[i]).append(','); + } + + args += " -a " + sb.toString(); + } + + sb = new StringBuilder(); + if (removeRules.length > 0) { + for (int i = 0; i < removeRules.length; i++) { + sb.append(removeRules[i]).append(','); + } + + args += " -d " + sb.toString(); + } + + sb = new StringBuilder(); + if (statRules.length > 0) { + for (int i = 0; i < statRules.length; i++) { + sb.append(statRules[i]).append(','); + } + + args += " -s " + sb.toString(); + } + + // Invoke the command + Pair result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, "/opt/cloud/bin/vpc_loadbalancer.sh " + args); + + if (!result.first()) { + String msg = "LoadBalancerConfigCommand on domain router " + routerIp + " failed. message: " + result.second(); + s_logger.error(msg); + + return new Answer(cmd, false, msg); + } + + if (s_logger.isInfoEnabled()) { + s_logger.info("VPCLoadBalancerConfigCommand on domain router " + routerIp + " completed"); + } + } finally { + SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, "rm " + tmpCfgFilePath); + } + return new Answer(cmd); + } catch (Throwable e) { + s_logger.error("Unexpected exception: " + e.toString(), e); + return new Answer(cmd, false, "VPCLoadBalancerConfigCommand failed due to " + VmwareHelper.getExceptionMessage(e)); + } + + + } + protected Answer execute(final LoadBalancerConfigCommand cmd) { + if ( cmd.getVpcId() != null ) { + return VPCLoadBalancerConfig(cmd); + } + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); File keyFile = mgr.getSystemVMKeyFile(); @@ -869,7 +957,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // eth0:xx.xx.xx.xx // // list IP with eth devices - // ifconfig ethx |grep -B1 "inet addr" | awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' + // ifconfig ethx |grep -B1 "inet addr" | awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' // | awk -F: '{ print $1 ": " $3 }' // // diff --git a/core/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java b/core/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java index 0bc18500b3c..15237c18905 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java +++ b/core/src/com/cloud/hypervisor/xen/resource/XenServer56Resource.java @@ -180,6 +180,8 @@ public class XenServer56Resource extends CitrixResourceBase { args += " -v " + vpcCIDR; } else if (option.equals("reset")) { args += "-r"; + } else if (option.equals("vpn")) { + args += "-n"; } else { return new NetworkUsageAnswer(cmd, "success", 0L, 0L); } diff --git a/patches/systemvm/debian/config/opt/cloud/bin/ipsectunnel.sh b/patches/systemvm/debian/config/opt/cloud/bin/ipsectunnel.sh index 1bc20025d8c..1ff4b133e43 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/ipsectunnel.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/ipsectunnel.sh @@ -142,7 +142,7 @@ ipsec_tunnel_add() { sudo echo " esp=$esppolicy" >> $vpnconffile && sudo echo " salifetime=${esplifetime}s" >> $vpnconffile && sudo echo " pfs=$pfs" >> $vpnconffile && - sudo echo " keyingtries=3" >> $vpnconffile && + sudo echo " keyingtries=2" >> $vpnconffile && sudo echo " auto=add" >> $vpnconffile && sudo echo "$leftpeer $rightpeer: PSK \"$secret\"" > $vpnsecretsfile && sudo chmod 0400 $vpnsecretsfile @@ -162,8 +162,8 @@ ipsec_tunnel_add() { logger -t cloud "$(basename $0): done ipsec tunnel entry for right peer=$rightpeer right networks=$rightnets" - #20 seconds for checking if it's ready - for i in {1..4} + #5 seconds for checking if it's ready + for i in {1..5} do logger -t cloud "$(basename $0): checking connection status..." /opt/cloud/bin/checks2svpn.sh $rightpeer @@ -172,7 +172,7 @@ ipsec_tunnel_add() { then break fi - sleep 5 + sleep 1 done if [ $result -eq 0 ] then diff --git a/patches/systemvm/debian/config/opt/cloud/bin/vpc_netusage.sh b/patches/systemvm/debian/config/opt/cloud/bin/vpc_netusage.sh index 8ec44a1b90e..c70080b5010 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/vpc_netusage.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/vpc_netusage.sh @@ -15,6 +15,7 @@ source /root/func.sh source /opt/cloud/bin/vpc_func.sh +vpnoutmark="0x525" lock="biglock" locked=$(getLockFile $lock) if [ "$locked" != "1" ] @@ -42,6 +43,22 @@ create_usage_rules () { return $? } +create_vpn_usage_rules () { + iptables -N VPN_STATS_$ethDev > /dev/null + iptables -I FORWARD -j VPN_STATS_$ethDev > /dev/null + iptables-save|grep "VPN_STATS_$ethDev -i $ethDev" > /dev/null + if [ $? -gt 0 ] + then + iptables -A VPN_STATS_$ethDev -i $ethDev -m mark --mark $vpnoutmark > /dev/null + fi + iptables-save|grep "VPN_STATS_$ethDev -o $ethDev" > /dev/null + if [ $? -gt 0 ] + then + iptables -A VPN_STATS_$ethDev -o $ethDev -m mark --mark $vpnoutmark > /dev/null + fi + return $? +} + get_usage () { iptables -L NETWORK_STATS_$ethDev -n -v -x | awk '$1 ~ /^[0-9]+$/ { printf "%s:", $2}'; > /dev/null if [ $? -gt 0 ] @@ -51,6 +68,16 @@ get_usage () { fi } +get_vpn_usage () { + iptables -L VPN_STATS_$ethDev -n -v -x | awk '$1 ~ /^[0-9]+$/ { printf "%s:", $2}'; > /dev/null + if [ $? -gt 0 ] + then + printf $? + return 1 + fi +} + + reset_usage () { iptables -Z NETWORK_STATS_$ethDev > /dev/null if [ $? -gt 0 -a $? -ne 2 ] @@ -65,9 +92,10 @@ cflag= gflag= rflag= lflag= +vflag= +nflag= - -while getopts 'cgrl:v:' OPTION +while getopts 'cgnrl:v:' OPTION do case $OPTION in c) cflag=1 @@ -82,6 +110,8 @@ do v) vflag=1 vcidr="$OPTARG" ;; + n) nflag=1 + ;; i) #Do nothing, since it's parameter for host script ;; ?) usage @@ -94,6 +124,7 @@ ethDev=$(getEthByIp $publicIp) if [ "$cflag" == "1" ] then create_usage_rules + create_vpn_usage_rules unlock_exit 0 $lock $locked fi @@ -103,6 +134,12 @@ then unlock_exit $? $lock $locked fi +if [ "$nflag" == "1" ] +then + get_vpn_usage + unlock_exit $? $lock $locked +fi + if [ "$rflag" == "1" ] then reset_usage diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 81b0901d86a..73e120e83eb 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -469,6 +469,7 @@ public class ApiResponseHelper implements ResponseGenerator { } diskOfferingResponse.setTags(offering.getTags()); diskOfferingResponse.setCustomized(offering.isCustomized()); + diskOfferingResponse.setStorageType(offering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString()); diskOfferingResponse.setObjectName("diskoffering"); return diskOfferingResponse; } 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/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 5a8a24cc524..ccf18fed0ee 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -21,7 +21,7 @@ import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.ha.HighAvailabilityManager; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.NetworkManager; -import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; import com.cloud.server.ManagementServer; import com.cloud.storage.StorageManager; import com.cloud.storage.allocator.StoragePoolAllocator; @@ -154,7 +154,7 @@ public enum Config { PingTimeout("Advanced", AgentManager.class, Float.class, "ping.timeout", "2.5", "Multiplier to ping.interval before announcing an agent has timed out", null), ClusterDeltaSyncInterval("Advanced", AgentManager.class, Integer.class, "sync.interval", "60", "Cluster Delta sync interval in seconds", null), Port("Advanced", AgentManager.class, Integer.class, "port", "8250", "Port to listen on for agent connection.", null), - RouterCpuMHz("Advanced", NetworkManager.class, Integer.class, "router.cpu.mhz", String.valueOf(VirtualNetworkApplianceManager.DEFAULT_ROUTER_CPU_MHZ), "Default CPU speed (MHz) for router VM.", null), + RouterCpuMHz("Advanced", NetworkManager.class, Integer.class, "router.cpu.mhz", String.valueOf(VpcVirtualNetworkApplianceManager.DEFAULT_ROUTER_CPU_MHZ), "Default CPU speed (MHz) for router VM.", null), RestartRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "restart.retry.interval", "600", "Time (in seconds) between retries to restart a vm", null), RouterStatsInterval("Advanced", NetworkManager.class, Integer.class, "router.stats.interval", "300", "Interval (in seconds) to report router statistics.", null), ExternalNetworkStatsInterval("Advanced", NetworkManager.class, Integer.class, "external.network.stats.interval", "300", "Interval (in seconds) to report external network statistics.", null), diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 72dcf986b2c..7d40f46e515 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -122,7 +122,6 @@ import com.cloud.network.lb.dao.ElasticLbVmMapDaoImpl; import com.cloud.network.ovs.OvsTunnelManagerImpl; import com.cloud.network.ovs.dao.OvsTunnelInterfaceDaoImpl; import com.cloud.network.ovs.dao.OvsTunnelNetworkDaoImpl; -import com.cloud.network.router.VirtualNetworkApplianceManagerImpl; import com.cloud.network.router.VpcVirtualNetworkApplianceManagerImpl; import com.cloud.network.rules.RulesManagerImpl; import com.cloud.network.rules.dao.PortForwardingRulesDaoImpl; @@ -401,7 +400,6 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addManager("Snapshot Manager", SnapshotManagerImpl.class); addManager("SnapshotScheduler", SnapshotSchedulerImpl.class); addManager("SecurityGroupManager", SecurityGroupManagerImpl2.class); - addManager("DomainRouterManager", VirtualNetworkApplianceManagerImpl.class); addManager("EntityManager", EntityManagerImpl.class); addManager("LoadBalancingRulesManager", LoadBalancingRulesManagerImpl.class); addManager("AutoScaleManager", AutoScaleManagerImpl.class); diff --git a/server/src/com/cloud/ha/UserVmDomRInvestigator.java b/server/src/com/cloud/ha/UserVmDomRInvestigator.java index 83fa0a64e98..92f6d4921ad 100644 --- a/server/src/com/cloud/ha/UserVmDomRInvestigator.java +++ b/server/src/com/cloud/ha/UserVmDomRInvestigator.java @@ -29,8 +29,8 @@ import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.NetworkManager; import com.cloud.network.Networks.TrafficType; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; import com.cloud.utils.component.Inject; import com.cloud.vm.Nic; import com.cloud.vm.UserVmVO; @@ -46,7 +46,7 @@ public class UserVmDomRInvestigator extends AbstractInvestigatorImpl { @Inject private final UserVmDao _userVmDao = null; @Inject private final AgentManager _agentMgr = null; @Inject private final NetworkManager _networkMgr = null; - @Inject private final VirtualNetworkApplianceManager _vnaMgr = null; + @Inject private final VpcVirtualNetworkApplianceManager _vnaMgr = null; @Override public Boolean isVmAlive(VMInstanceVO vm, HostVO host) { diff --git a/server/src/com/cloud/hypervisor/vmware/VmwareManagerImpl.java b/server/src/com/cloud/hypervisor/vmware/VmwareManagerImpl.java index 8ff7633dd3a..3d096a89a81 100755 --- a/server/src/com/cloud/hypervisor/vmware/VmwareManagerImpl.java +++ b/server/src/com/cloud/hypervisor/vmware/VmwareManagerImpl.java @@ -70,7 +70,6 @@ import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.network.CiscoNexusVSMDeviceVO; import com.cloud.network.NetworkManager; import com.cloud.network.dao.CiscoNexusVSMDeviceDao; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.org.Cluster.ClusterType; import com.cloud.secstorage.CommandExecLogDao; import com.cloud.serializer.GsonHelper; @@ -92,7 +91,6 @@ import com.cloud.vm.DomainRouterVO; import com.google.gson.Gson; import com.vmware.apputils.vim25.ServiceUtil; import com.vmware.vim25.HostConnectSpec; -import com.vmware.vim25.HostPortGroupSpec; import com.vmware.vim25.ManagedObjectReference; @Local(value = {VmwareManager.class}) @@ -117,7 +115,6 @@ public class VmwareManagerImpl implements VmwareManager, VmwareStorageMount, Lis @Inject CommandExecLogDao _cmdExecLogDao; @Inject ClusterManager _clusterMgr; @Inject CheckPointManager _checkPointMgr; - @Inject VirtualNetworkApplianceManager _routerMgr; @Inject SecondaryStorageVmManager _ssvmMgr; @Inject CiscoNexusVSMDeviceDao _nexusDao; @Inject ClusterVSMMapDao _vsmMapDao; diff --git a/server/src/com/cloud/network/SshKeysDistriMonitor.java b/server/src/com/cloud/network/SshKeysDistriMonitor.java index 25e0f2a78cb..cf2dfe39b52 100755 --- a/server/src/com/cloud/network/SshKeysDistriMonitor.java +++ b/server/src/com/cloud/network/SshKeysDistriMonitor.java @@ -12,9 +12,6 @@ // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.network; -import java.util.HashMap; -import java.util.Map; - import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -34,8 +31,6 @@ 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.network.router.VirtualNetworkApplianceManager; - public class SshKeysDistriMonitor implements Listener { diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java index 95e5297da2e..ba886f3e789 100644 --- a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java +++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java @@ -43,7 +43,6 @@ import com.cloud.network.NetworkManager; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.dao.NetworkDao; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.offering.NetworkOffering; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.uservm.UserVm; @@ -70,8 +69,6 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem @Inject NetworkManager _networkMgr; @Inject - VirtualNetworkApplianceManager _routerMgr; - @Inject UserVmManager _userVmMgr; @Inject UserVmDao _userVmDao; diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index c7ccc4b343f..3fa4c826978 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -53,8 +53,8 @@ import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; import com.cloud.network.lb.LoadBalancingRulesManager; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter.Role; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LbStickinessMethod; import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType; @@ -102,7 +102,7 @@ LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServ @Inject NetworkOfferingDao _networkOfferingDao; @Inject - VirtualNetworkApplianceManager _routerMgr; + VpcVirtualNetworkApplianceManager _routerMgr; @Inject ConfigurationManager _configMgr; @Inject diff --git a/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java index 8b786a2f519..ce38e6d4a52 100644 --- a/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java +++ b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java @@ -89,7 +89,6 @@ import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; import com.cloud.network.lb.dao.ElasticLbVmMapDao; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter.RedundantState; import com.cloud.network.router.VirtualRouter.Role; @@ -149,8 +148,6 @@ ElasticLoadBalancerManager, Manager, VirtualMachineGuru { @Inject LoadBalancingRulesManager _lbMgr; @Inject - VirtualNetworkApplianceManager _routerMgr; - @Inject DomainRouterDao _routerDao = null; @Inject protected HostPodDao _podDao = null; 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/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index ba25d7256f1..21e3b56dcc4 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -132,6 +132,7 @@ import com.cloud.network.RemoteAccessVpn; import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteVpnConnection; import com.cloud.network.Site2SiteVpnConnectionVO; +import com.cloud.network.Site2SiteVpnGatewayVO; import com.cloud.network.SshKeysDistriMonitor; import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.VirtualRouterProvider; @@ -763,15 +764,15 @@ VirtualMachineGuru, Listener { String privateIP = router.getPrivateIpAddress(); if (privateIP != null) { + boolean forVpc = router.getVpcId() != null; List routerNics = _nicDao.listByVmId(router.getId()); for (Nic routerNic : routerNics) { Network network = _networkMgr.getNetwork(routerNic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Public) { - boolean forVpc = router.getVpcId() != null; + if ((forVpc && network.getTrafficType() == TrafficType.Public) || (!forVpc && network.getTrafficType() == TrafficType.Guest)) { final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getHostName(), forVpc, routerNic.getIp4Address()); UserStatisticsVO previousStats = _statsDao.findBy(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), null, router.getId(), router.getType().toString()); + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), router.getType().toString()); NetworkUsageAnswer answer = null; try { answer = (NetworkUsageAnswer) _agentMgr.easySend(router.getHostId(), usageCmd); @@ -793,7 +794,7 @@ VirtualMachineGuru, Listener { } txn.start(); UserStatisticsVO stats = _statsDao.lock(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), routerNic.getIp4Address(), router.getId(), router.getType().toString()); + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), router.getType().toString()); if (stats == null) { s_logger.warn("unable to find stats for account: " + router.getAccountId()); continue; @@ -838,6 +839,81 @@ VirtualMachineGuru, Listener { txn.close(); } } + if(forVpc){ + //Get VPN gateway + Site2SiteVpnGatewayVO s2sVpn = _s2sVpnGatewayDao.findByVpcId(router.getVpcId()); + if(s2sVpn != null){ + final NetworkUsageCommand vpnUsageCmd = new NetworkUsageCommand(privateIP, router.getHostName(), "vpn", forVpc, routerNic.getIp4Address()); + previousStats = _statsDao.findBy(s2sVpn.getAccountId(), router.getDataCenterIdToDeployIn(), network.getId(), + routerNic.getIp4Address(), s2sVpn.getId(), "VPNGateway"); + answer = null; + try { + answer = (NetworkUsageAnswer) _agentMgr.easySend(router.getHostId(), vpnUsageCmd); + } catch (Exception e) { + s_logger.warn("Error while collecting vpn network stats from router: "+router.getInstanceName()+" from host: "+router.getHostId(), e); + continue; + } + + if (answer != null) { + if (!answer.getResult()) { + s_logger.warn("Error while collecting vpn network stats from router: "+router.getInstanceName()+" from host: "+router.getHostId() + "; details: " + answer.getDetails()); + continue; + } + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + try { + if ((answer.getBytesReceived() == 0) && (answer.getBytesSent() == 0)) { + s_logger.debug("Recieved and Sent bytes are both 0. Not updating user_statistics"); + continue; + } + txn.start(); + UserStatisticsVO stats = _statsDao.lock(s2sVpn.getAccountId(), router.getDataCenterIdToDeployIn(), network.getId(), + routerNic.getIp4Address(), s2sVpn.getId(), "VPNGateway"); + if (stats == null) { + s_logger.warn("unable to find vpn stats for account: " + router.getAccountId()+" vpc Id: "+router.getVpcId()); + continue; + } + + if(previousStats != null + && ((previousStats.getCurrentBytesReceived() != stats.getCurrentBytesReceived()) + || (previousStats.getCurrentBytesSent() != stats.getCurrentBytesSent()))){ + s_logger.debug("Router stats changed from the time NetworkUsageCommand was sent. " + + "Ignoring current answer. Router: "+answer.getRouterName()+" Rcvd: " + + answer.getBytesReceived()+ "Sent: " +answer.getBytesSent()); + continue; + } + + if (stats.getCurrentBytesReceived() > answer.getBytesReceived()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Router: " + + answer.getRouterName()+" Reported: " + answer.getBytesReceived() + + " Stored: " + stats.getCurrentBytesReceived()); + } + stats.setNetBytesReceived(stats.getNetBytesReceived() + stats.getCurrentBytesReceived()); + } + stats.setCurrentBytesReceived(answer.getBytesReceived()); + if (stats.getCurrentBytesSent() > answer.getBytesSent()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Received # of bytes that's less than the last one. " + + "Assuming something went wrong and persisting it. Router: " + + answer.getRouterName()+" Reported: " + answer.getBytesSent() + + " Stored: " + stats.getCurrentBytesSent()); + } + stats.setNetBytesSent(stats.getNetBytesSent() + stats.getCurrentBytesSent()); + } + stats.setCurrentBytesSent(answer.getBytesSent()); + _statsDao.update(stats.getId(), stats); + txn.commit(); + } catch (Exception e) { + txn.rollback(); + s_logger.warn("Unable to update user statistics for account: " + router.getAccountId() + + " Rx: " + answer.getBytesReceived() + "; Tx: " + answer.getBytesSent()); + } finally { + txn.close(); + } + } + } + } } } } @@ -900,6 +976,7 @@ VirtualMachineGuru, Listener { } } + @DB protected void updateSite2SiteVpnConnectionState(List routers) { for (DomainRouterVO router : routers) { List conns = _s2sVpnMgr.getConnectionsForRouter(router); @@ -949,26 +1026,34 @@ VirtualMachineGuru, Listener { continue; } for (Site2SiteVpnConnectionVO conn : conns) { - if (conn.getState() != Site2SiteVpnConnection.State.Connected && - conn.getState() != Site2SiteVpnConnection.State.Disconnected) { - continue; + Site2SiteVpnConnectionVO lock = _s2sVpnConnectionDao.acquireInLockTable(conn.getId()); + if (lock == null) { + throw new CloudRuntimeException("Unable to acquire lock on " + lock); } - Site2SiteVpnConnection.State oldState = conn.getState(); - Site2SiteCustomerGateway gw = _s2sCustomerGatewayDao.findById(conn.getCustomerGatewayId()); - if (answer.isConnected(gw.getGatewayIp())) { - conn.setState(Site2SiteVpnConnection.State.Connected); - } else { - conn.setState(Site2SiteVpnConnection.State.Disconnected); - } - _s2sVpnConnectionDao.persist(conn); - if (oldState != conn.getState()) { - String title = "Site-to-site Vpn Connection to " + gw.getName() + - " just switch from " + oldState + " to " + conn.getState(); - String context = "Site-to-site Vpn Connection to " + gw.getName() + " on router " + router.getHostName() + - "(id: " + router.getId() + ") " + " just switch from " + oldState + " to " + conn.getState(); - s_logger.info(context); - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_DOMAIN_ROUTER, - router.getDataCenterIdToDeployIn(), router.getPodIdToDeployIn(), title, context); + try { + if (conn.getState() != Site2SiteVpnConnection.State.Connected && + conn.getState() != Site2SiteVpnConnection.State.Disconnected) { + continue; + } + Site2SiteVpnConnection.State oldState = conn.getState(); + Site2SiteCustomerGateway gw = _s2sCustomerGatewayDao.findById(conn.getCustomerGatewayId()); + if (answer.isConnected(gw.getGatewayIp())) { + conn.setState(Site2SiteVpnConnection.State.Connected); + } else { + conn.setState(Site2SiteVpnConnection.State.Disconnected); + } + _s2sVpnConnectionDao.persist(conn); + if (oldState != conn.getState()) { + String title = "Site-to-site Vpn Connection to " + gw.getName() + + " just switch from " + oldState + " to " + conn.getState(); + String context = "Site-to-site Vpn Connection to " + gw.getName() + " on router " + router.getHostName() + + "(id: " + router.getId() + ") " + " just switch from " + oldState + " to " + conn.getState(); + s_logger.info(context); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_DOMAIN_ROUTER, + router.getDataCenterIdToDeployIn(), router.getPodIdToDeployIn(), title, context); + } + } finally { + _s2sVpnConnectionDao.releaseFromLockTable(lock.getId()); } } } @@ -2311,6 +2396,11 @@ VirtualMachineGuru, Listener { ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Starting router " + router); if (_itMgr.start(router, params, user, caller, planToDeploy) != null) { + // We don't want the failure of VPN Connection affect the status of router, so we try to make connection only after router start successfully + Long vpcId = router.getVpcId(); + if (vpcId != null) { + _s2sVpnMgr.reconnectDisconnectedVpnByVpc(vpcId); + } return _routerDao.findById(router.getId()); } else { return null; @@ -3282,12 +3372,12 @@ VirtualMachineGuru, Listener { List routerNics = _nicDao.listByVmId(router.getId()); for (Nic routerNic : routerNics) { Network network = _networkMgr.getNetwork(routerNic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Public) { - boolean forVpc = router.getVpcId() != null; + boolean forVpc = router.getVpcId() != null; + if ((forVpc && network.getTrafficType() == TrafficType.Public) || (!forVpc && network.getTrafficType() == TrafficType.Guest)) { final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getHostName(), forVpc, routerNic.getIp4Address()); UserStatisticsVO previousStats = _statsDao.findBy(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), null, router.getId(), router.getType().toString()); + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), router.getType().toString()); NetworkUsageAnswer answer = null; try { answer = (NetworkUsageAnswer) _agentMgr.easySend(router.getHostId(), usageCmd); @@ -3309,7 +3399,7 @@ VirtualMachineGuru, Listener { } txn.start(); UserStatisticsVO stats = _statsDao.lock(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), null, router.getId(), router.getType().toString()); + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), router.getType().toString()); if (stats == null) { s_logger.warn("unable to find stats for account: " + router.getAccountId()); continue; diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index 5e0328bb671..cae9a4b8e70 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -128,6 +128,7 @@ import com.cloud.vm.dao.VMInstanceDao; public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplianceManagerImpl implements VpcVirtualNetworkApplianceManager{ private static final Logger s_logger = Logger.getLogger(VpcVirtualNetworkApplianceManagerImpl.class); + String _name; @Inject VpcDao _vpcDao; @Inject @@ -1348,4 +1349,5 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian _s2sVpnMgr.markDisconnectVpnConnByVpc(vpcId); } } + } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 7cb7796ef1d..bb1900e02b0 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"); @@ -714,13 +714,13 @@ public class VpcManagerImpl implements VpcManager, Manager{ @Override public List listVpcs(Long id, String vpcName, String displayText, List supportedServicesStr, String cidr, Long vpcOffId, String state, String accountName, Long domainId, String keyword, - Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired, Map tags) { + Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired, Map tags, Long projectId) { Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); - _accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, + _accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); @@ -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 @@ -1390,11 +1388,12 @@ public class VpcManagerImpl implements VpcManager, Manager{ Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); String state = cmd.getState(); + Long projectId = cmd.getProjectId(); Filter searchFilter = new Filter(VpcGatewayVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal()); Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); - _accountMgr.buildACLSearchParameters(caller, null, accountName, null, permittedAccounts, domainIdRecursiveListProject, + _accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); @@ -1566,8 +1565,18 @@ public class VpcManagerImpl implements VpcManager, Manager{ if (!NetUtils.isValidCIDR(cidr)){ throw new InvalidParameterValueException("Invalid format for cidr " + cidr, null); } + + //validate the cidr + //1) CIDR should be outside of VPC cidr for guest networks + if (NetUtils.isNetworksOverlap(vpc.getCidr(), cidr)) { + throw new InvalidParameterValueException("CIDR should be outside of VPC cidr " + vpc.getCidr(), null); + } + + //2) CIDR should be outside of link-local cidr + if (NetUtils.isNetworksOverlap(vpc.getCidr(), NetUtils.getLinkLocalCIDR())) { + throw new InvalidParameterValueException("CIDR should be outside of link local cidr " + NetUtils.getLinkLocalCIDR(), null); + } - //TODO - check cidr for the conflicts Transaction txn = Transaction.currentTxn(); txn.start(); @@ -1599,10 +1608,11 @@ public class VpcManagerImpl implements VpcManager, Manager{ Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); Map tags = cmd.getTags(); + Long projectId = cmd.getProjectId(); Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); - _accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, + _accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); @@ -1734,7 +1744,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 +1793,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 +1828,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 +1845,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 +1866,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 +1890,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 +1903,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 +1936,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/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index b5fa4ea7aab..ba4d4009317 100755 --- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -49,7 +49,6 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.RemoteAccessVpnDao; import com.cloud.network.dao.VpnUserDao; import com.cloud.network.element.RemoteAccessVPNServiceProvider; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; @@ -87,7 +86,6 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag @Inject VpnUserDao _vpnUsersDao; @Inject RemoteAccessVpnDao _remoteAccessVpnDao; @Inject IPAddressDao _ipAddressDao; - @Inject VirtualNetworkApplianceManager _routerMgr; @Inject AccountManager _accountMgr; @Inject DomainManager _domainMgr; @Inject NetworkManager _networkMgr; diff --git a/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java b/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java index 922ac2c4b6e..73dbef56ff1 100644 --- a/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java +++ b/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java @@ -11,4 +11,5 @@ public interface Site2SiteVpnManager extends Site2SiteVpnService { void markDisconnectVpnConnByVpc(long vpcId); List getConnectionsForRouter(DomainRouterVO router); boolean deleteCustomerGatewayByAccount(long accountId); + void reconnectDisconnectedVpnByVpc(Long vpcId); } diff --git a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java index 45bfeb5ec18..003d948a654 100644 --- a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java @@ -49,11 +49,14 @@ import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.UserContext; +import com.cloud.user.UserStatisticsVO; import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserStatisticsDao; 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.GenericDao; import com.cloud.utils.db.JoinBuilder; @@ -76,7 +79,7 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { @Inject AccountDao _accountDao; @Inject VpcManager _vpcMgr; @Inject AccountManager _accountMgr; - + @Inject UserStatisticsDao _statsDao = null; String _name; @Override @@ -128,6 +131,13 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { Site2SiteVpnGatewayVO gw = new Site2SiteVpnGatewayVO(owner.getAccountId(), owner.getDomainId(), ips.get(0).getId(), vpcId); _vpnGatewayDao.persist(gw); + UserStatisticsVO stats = _statsDao.findBy(owner.getAccountId(), vpc.getZoneId(), ips.get(0).getSourceNetworkId(), + ips.get(0).getAddress().toString(), gw.getId(), "VPNGateway"); + if (stats == null) { + stats = new UserStatisticsVO(owner.getAccountId(), vpc.getZoneId(), ips.get(0).getAddress().toString(), gw.getId(), + "VPNGateway", ips.get(0).getSourceNetworkId()); + _statsDao.persist(stats); + } return gw; } @@ -251,30 +261,39 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { } @Override + @DB public Site2SiteVpnConnection startVpnConnection(long id) throws ResourceUnavailableException { - Site2SiteVpnConnectionVO conn = _vpnConnectionDao.findById(id); - if (conn.getState() != State.Pending && conn.getState() != State.Disconnected) { - List idList = new ArrayList(); - idList.add(new IdentityProxy(conn, id, "connectionId")); - throw new InvalidParameterValueException("Site to site VPN connection with specified connectionId not in correct state(pending or disconnected) to process!", idList); + Site2SiteVpnConnectionVO conn = _vpnConnectionDao.acquireInLockTable(id); + if (conn == null) { + throw new CloudRuntimeException("Unable to acquire lock on " + conn); } + try { + if (conn.getState() != State.Pending && conn.getState() != State.Disconnected) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(conn, id, "connectionId")); + throw new InvalidParameterValueException("Site to site VPN connection with specified connectionId not in correct state(pending or disconnected) to process!", idList); + } - conn.setState(State.Pending); - _vpnConnectionDao.persist(conn); - List elements = _networkMgr.getSite2SiteVpnElements(); - boolean result = true; - for (Site2SiteVpnServiceProvider element : elements) { - result = result & element.startSite2SiteVpn(conn); - } - - if (result) { - conn.setState(State.Connected); + conn.setState(State.Pending); _vpnConnectionDao.persist(conn); - return conn; + + List elements = _networkMgr.getSite2SiteVpnElements(); + boolean result = true; + for (Site2SiteVpnServiceProvider element : elements) { + result = result & element.startSite2SiteVpn(conn); + } + + if (result) { + conn.setState(State.Connected); + _vpnConnectionDao.persist(conn); + return conn; + } + conn.setState(State.Error); + _vpnConnectionDao.persist(conn); + throw new ResourceUnavailableException("Failed to apply site-to-site VPN", Site2SiteVpnConnection.class, id); + } finally { + _vpnConnectionDao.releaseFromLockTable(conn.getId()); } - conn.setState(State.Error); - _vpnConnectionDao.persist(conn); - throw new ResourceUnavailableException("Failed to apply site-to-site VPN", Site2SiteVpnConnection.class, id); } @Override @@ -439,26 +458,35 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { return true; } + @DB private void stopVpnConnection(Long id) throws ResourceUnavailableException { - Site2SiteVpnConnectionVO conn = _vpnConnectionDao.findById(id); - if (conn.getState() != State.Connected && conn.getState() != State.Error) { - List idList = new ArrayList(); - idList.add(new IdentityProxy(conn, id, "vpnConnectionId")); - throw new InvalidParameterValueException("Site to site VPN connection with specified id is not in correct state(connected) to process disconnect!", idList); + Site2SiteVpnConnectionVO conn = _vpnConnectionDao.acquireInLockTable(id); + if (conn == null) { + throw new CloudRuntimeException("Unable to acquire lock on " + conn); } + try { + if (conn.getState() != State.Connected && conn.getState() != State.Error) { + List idList = new ArrayList(); + idList.add(new IdentityProxy(conn, id, "vpnConnectionId")); + throw new InvalidParameterValueException("Site to site VPN connection with specified id is not in correct state(connected) to process disconnect!", idList); + } - List elements = _networkMgr.getSite2SiteVpnElements(); - boolean result = true; - conn.setState(State.Disconnected); - _vpnConnectionDao.persist(conn); - for (Site2SiteVpnServiceProvider element : elements) { - result = result & element.stopSite2SiteVpn(conn); - } - - if (!result) { - conn.setState(State.Error); + conn.setState(State.Disconnected); _vpnConnectionDao.persist(conn); - throw new ResourceUnavailableException("Failed to apply site-to-site VPN", Site2SiteVpnConnection.class, id); + + List elements = _networkMgr.getSite2SiteVpnElements(); + boolean result = true; + for (Site2SiteVpnServiceProvider element : elements) { + result = result & element.stopSite2SiteVpn(conn); + } + + if (!result) { + conn.setState(State.Error); + _vpnConnectionDao.persist(conn); + throw new ResourceUnavailableException("Failed to apply site-to-site VPN", Site2SiteVpnConnection.class, id); + } + } finally { + _vpnConnectionDao.releaseFromLockTable(conn.getId()); } } @@ -641,15 +669,24 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { } @Override + @DB public void markDisconnectVpnConnByVpc(long vpcId) { List conns = _vpnConnectionDao.listByVpcId(vpcId); for (Site2SiteVpnConnectionVO conn : conns) { if (conn == null) { continue; } - if (conn.getState() == Site2SiteVpnConnection.State.Connected) { - conn.setState(Site2SiteVpnConnection.State.Disconnected); - _vpnConnectionDao.persist(conn); + Site2SiteVpnConnectionVO lock = _vpnConnectionDao.acquireInLockTable(conn.getId()); + if (lock == null) { + throw new CloudRuntimeException("Unable to acquire lock on " + conn); + } + try { + if (conn.getState() == Site2SiteVpnConnection.State.Connected) { + conn.setState(Site2SiteVpnConnection.State.Disconnected); + _vpnConnectionDao.persist(conn); + } + } finally { + _vpnConnectionDao.releaseFromLockTable(lock.getId()); } } } @@ -675,4 +712,22 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { } return result; } + + @Override + public void reconnectDisconnectedVpnByVpc(Long vpcId) { + List conns = _vpnConnectionDao.listByVpcId(vpcId); + for (Site2SiteVpnConnectionVO conn : conns) { + if (conn == null) { + continue; + } + if (conn.getState() == Site2SiteVpnConnection.State.Disconnected) { + try { + startVpnConnection(conn.getId()); + } catch (ResourceUnavailableException e) { + Site2SiteCustomerGatewayVO gw = _customerGatewayDao.findById(conn.getCustomerGatewayId()); + s_logger.warn("Site2SiteVpnManager: Fail to re-initiate VPN connection " + conn.getId() + " which connect to " + gw.getName()); + } + } + } + } } 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/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index 31320d3f69a..1aaf97ab7ed 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -332,7 +332,7 @@ public class ConsoleProxyServlet extends HttpServlet { param.setClientTunnelSession(parsedHostInfo.third()); } - sb.append("/ajax?token=" + encryptor.encryptObject(ConsoleProxyClientParam.class, param)); + sb.append("/ajaximg?token=" + encryptor.encryptObject(ConsoleProxyClientParam.class, param)); sb.append("&w=").append(w).append("&h=").append(h); if(s_logger.isDebugEnabled()) { diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 545817e8da6..31ec7ec1607 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -121,7 +121,6 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.network.NetworkManager; -import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.org.Grouping; import com.cloud.org.Grouping.AllocationState; import com.cloud.projects.Project.ListProjectResourcesCriteria; @@ -293,8 +292,6 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag @Inject protected ClusterDao _clusterDao; @Inject - protected VirtualNetworkApplianceManager _routerMgr; - @Inject protected UsageEventDao _usageEventDao; @Inject protected VirtualMachineManager _vmMgr; @@ -732,6 +729,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, vm, avoidPools); // Copy the volume from secondary storage to the destination storage pool + stateTransitTo(volume, Event.CopyRequested); VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId()); HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId()); String secondaryStorageURL = secStorage.getStorageUrl(); diff --git a/server/src/com/cloud/upgrade/dao/Upgrade218to22.java b/server/src/com/cloud/upgrade/dao/Upgrade218to22.java index c39074bd109..48d063506c0 100644 --- a/server/src/com/cloud/upgrade/dao/Upgrade218to22.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade218to22.java @@ -37,7 +37,7 @@ import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.event.EventTypes; import com.cloud.event.EventVO; import com.cloud.event.UsageEventVO; -import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; @@ -1404,7 +1404,7 @@ public class Upgrade218to22 implements DbUpgrade { pstmt.close(); int proxyRamSize = NumbersUtil.parseInt(getConfigValue(conn, "consoleproxy.ram.size"), ConsoleProxyManager.DEFAULT_PROXY_VM_RAMSIZE); - int domrRamSize = NumbersUtil.parseInt(getConfigValue(conn, "router.ram.size"), VirtualNetworkApplianceManager.DEFAULT_ROUTER_VM_RAMSIZE); + int domrRamSize = NumbersUtil.parseInt(getConfigValue(conn, "router.ram.size"), VpcVirtualNetworkApplianceManager.DEFAULT_ROUTER_VM_RAMSIZE); int ssvmRamSize = NumbersUtil.parseInt(getConfigValue(conn, "secstorage.vm.ram.size"), SecondaryStorageVmManager.DEFAULT_SS_VM_RAMSIZE); pstmt = conn @@ -1563,7 +1563,7 @@ public class Upgrade218to22 implements DbUpgrade { pstmt.close(); int proxyCpuMhz = NumbersUtil.parseInt(getConfigValue(conn, "consoleproxy.cpu.mhz"), ConsoleProxyManager.DEFAULT_PROXY_VM_CPUMHZ); - int domrCpuMhz = NumbersUtil.parseInt(getConfigValue(conn, "router.cpu.mhz"), VirtualNetworkApplianceManager.DEFAULT_ROUTER_CPU_MHZ); + int domrCpuMhz = NumbersUtil.parseInt(getConfigValue(conn, "router.cpu.mhz"), VpcVirtualNetworkApplianceManager.DEFAULT_ROUTER_CPU_MHZ); int ssvmCpuMhz = NumbersUtil.parseInt(getConfigValue(conn, "secstorage.vm.cpu.mhz"), SecondaryStorageVmManager.DEFAULT_SS_VM_CPUMHZ); pstmt = conn 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; + } + +} diff --git a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index b79f2258baf..d04aa210381 100755 --- a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -288,12 +288,14 @@ public class DomainRouterDaoImpl extends GenericDaoBase im RouterNetworkVO routerNtwkMap = new RouterNetworkVO(router.getId(), guestNetwork.getId(), guestNetwork.getGuestType()); _routerNetworkDao.persist(routerNtwkMap); //2) create user stats entry for the network - UserStatisticsVO stats = _userStatsDao.findBy(router.getAccountId(), router.getDataCenterIdToDeployIn(), - guestNetwork.getId(), null, router.getId(), router.getType().toString()); - if (stats == null) { - stats = new UserStatisticsVO(router.getAccountId(), router.getDataCenterIdToDeployIn(), null, router.getId(), - router.getType().toString(), guestNetwork.getId()); - _userStatsDao.persist(stats); + if(router.getVpcId() == null){ + UserStatisticsVO stats = _userStatsDao.findBy(router.getAccountId(), router.getDataCenterIdToDeployIn(), + guestNetwork.getId(), null, router.getId(), router.getType().toString()); + if (stats == null) { + stats = new UserStatisticsVO(router.getAccountId(), router.getDataCenterIdToDeployIn(), null, router.getId(), + router.getType().toString(), guestNetwork.getId()); + _userStatsDao.persist(stats); + } } txn.commit(); } diff --git a/test/integration/component/test_host_high_availability.py b/test/integration/component/test_host_high_availability.py new file mode 100644 index 00000000000..a019c0970f4 --- /dev/null +++ b/test/integration/component/test_host_high_availability.py @@ -0,0 +1,796 @@ +# -*- encoding: utf-8 -*- +# 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 + +""" P1 tests for dedicated Host high availability +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin import remoteSSHClient +import datetime + + +class Services: + """ Dedicated host HA test cases """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "HA", + "lastname": "HA", + "username": "HA", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering_with_ha": { + "name": "Tiny Instance With HA Enabled", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "service_offering_without_ha": { + "name": "Tiny Instance Without HA", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": { + "displayname": "VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostypeid": '6a6b23aa-5e59-4b53-8161-0ad1aa5990f1', + "timeout": 100, + } + +class TestHostHighAvailability(cloudstackTestCase): + """ Dedicated host HA test cases """ + + @classmethod + def setUpClass(cls): + + cls.api_client = super( + TestHostHighAvailability, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain( + cls.api_client, + cls.services + ) + cls.zone = get_zone( + cls.api_client, + cls.services + ) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering_with_ha = ServiceOffering.create( + cls.api_client, + cls.services["service_offering_with_ha"], + offerha=True + ) + + cls.service_offering_without_ha = ServiceOffering.create( + cls.api_client, + cls.services["service_offering_without_ha"], + offerha=False + ) + + cls._cleanup = [ + cls.service_offering_with_ha, + cls.service_offering_without_ha, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [self.account] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + self.testClient.close() + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vm_deployment_with_compute_offering_with_ha_enabled(self): + """ Test VM deployments (Create HA enabled Compute Service Offering and VM) """ + + # Steps, + #1. Create a Compute service offering with the “Offer HA” option selected. + #2. Create a Guest VM with the compute service offering created above. + # Validations, + #1. Ensure that the offering is created and that in the UI the “Offer HA” field is enabled (Yes) + #The listServiceOffering API should list “offerha” as true. + #2. Select the newly created VM and ensure that the Compute offering field value lists the compute service offering that was selected. + # Also, check that the HA Enabled field is enabled “Yes”. + + #list and validate above created service offering with Ha enabled + list_service_response = list_service_offering( + self.apiclient, + id=self.service_offering_with_ha.id + ) + self.assertEqual( + isinstance(list_service_response, list), + True, + "listServiceOfferings returned invalid object in response." + ) + self.assertNotEqual( + len(list_service_response), + 0, + "listServiceOfferings returned empty list." + ) + self.assertEqual( + list_service_response[0].offerha, + True, + "The service offering is not HA enabled" + ) + + #create virtual machine with the service offering with Ha enabled + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "listVirtualMachines returned invalid object in response." + ) + self.assertNotEqual( + len(vms), + 0, + "listVirtualMachines returned empty list." + ) + self.debug("Deployed VM on host: %s" % vms[0].hostid) + self.assertEqual( + vms[0].haenable, + True, + "VM not created with HA enable tag" + ) + + def test_02_no_vm_creation_on_host_with_haenabled(self): + """ Verify you can not create new VMs on hosts with an ha.tag """ + + # Steps, + #1. Fresh install CS (Bonita) that supports this feature + #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage + #3. When adding host3, assign the HA host tag. + #4. You should already have a compute service offering with HA already create from above. If not, create one for HA. + #5. Create VMs with the service offering with and without the HA tag + # Validations, + #Check to make sure the newly created VM is not on any HA enabled hosts + #The VM should be created only on host1 or host2 and never host3 (HA enabled) + + #create and verify virtual machine with HA enabled service offering + virtual_machine_with_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "listVirtualMachines returned invalid object in response." + ) + + self.assertNotEqual( + len(vms), + 0, + "listVirtualMachines returned empty list." + ) + + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + + #validate the virtual machine created is host Ha enabled + list_hosts_response = list_hosts( + self.apiclient, + id=vm.hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "listHosts returned invalid object in response." + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "listHosts retuned empty list in response." + ) + + self.assertEqual( + list_hosts_response[0].hahost, + False, + "VM created on HA enabled host." + ) + + #create and verify virtual machine with Ha disabled service offering + virtual_machine_without_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_without_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_without_ha.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "listVirtualMachines returned invalid object in response." + ) + + self.assertNotEqual( + len(vms), + 0, + "listVirtualMachines returned empty list." + ) + + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + + #verify that the virtual machine created on the host is Ha disabled + list_hosts_response = list_hosts( + self.apiclient, + id=vm.hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "listHosts returned invalid object in response." + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "listHosts returned empty list." + ) + + host = list_hosts_response[0] + + self.assertEqual( + host.hahost, + False, + "VM migrated to HA enabled host." + ) + + def test_03_cant_migrate_vm_to_host_with_ha_positive(self): + """ Verify you can not migrate VMs to hosts with an ha.tag (positive) """ + + # Steps, + #1. Create a Compute service offering with the “Offer HA” option selected. + #2. Create a Guest VM with the compute service offering created above. + #3. Select the VM and migrate VM to another host. Choose a “Suitable” host (i.e. host2) + # Validations + #The option from the “Migrate instance to another host” dialog box” should list host3 as “Not Suitable” for migration. + #Confirm that the VM is migrated to the “Suitable” host you selected (i.e. host2) + + #create and verify the virtual machine with HA enabled service offering + virtual_machine_with_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True, + ) + + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + + #Find out a Suitable host for VM migration + list_hosts_response = list_hosts( + self.apiclient, + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "The listHosts API returned the invalid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "The listHosts returned nothing." + ) + suitableHost = None + for host in list_hosts_response: + if host.suitableformigration == True and host.hostid != vm.hostid: + suitableHost = host + break + + self.assertTrue(suitableHost is not None, "suitablehost should not be None") + + #Migration of the VM to a suitable host + self.debug("Migrating VM-ID: %s to Host: %s" % (self.vm.id, suitableHost.id)) + + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.hostid = suitableHost.id + cmd.virtualmachineid = self.vm.id + self.apiclient.migrateVirtualMachine(cmd) + + #Verify that the VM migrated to a targeted Suitable host + list_vm_response = list_virtual_machines( + self.apiclient, + id=vm.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "The listVirtualMachines returned the invalid list." + ) + + self.assertNotEqual( + list_vm_response, + None, + "The listVirtualMachines API returned nothing." + ) + + vm_response = list_vm_response[0] + + self.assertEqual( + vm_response.id, + vm.id, + "The virtual machine id and the the virtual machine from listVirtualMachines is not matching." + ) + + self.assertEqual( + vm_response.hostid, + suitableHost.id, + "The VM is not migrated to targeted suitable host." + ) + + def test_04_cant_migrate_vm_to_host_with_ha_negative(self): + """ Verify you can not migrate VMs to hosts with an ha.tag (negative) """ + + # Steps, + #1. Create a Compute service offering with the “Offer HA” option selected. + #2. Create a Guest VM with the compute service offering created above. + #3. Select the VM and migrate VM to another host. Choose a “Not Suitable” host. + # Validations, + #The option from the “Migrate instance to another host” dialog box” should list host3 as “Not Suitable” for migration. + #By design, The Guest VM can STILL can be migrated to host3 if the admin chooses to do so. + + #create and verify virtual machine with HA enabled service offering + virtual_machine_with_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "The listVirtualMachines returned invalid object in response." + ) + + self.assertNotEqual( + len(vms), + 0, + "The listVirtualMachines returned empty response." + ) + + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + + #Find out Non-Suitable host for VM migration + list_hosts_response = list_hosts( + self.apiclient, + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "listHosts returned invalid object in response." + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "listHosts returned empty response." + ) + + notSuitableHost = None + for host in list_hosts_response: + if not host.suitableformigration and host.hostid != vm.hostid: + notSuitableHost = host + break + + self.assertTrue(notSuitableHost is not None, "notsuitablehost should not be None") + + #Migrate VM to Non-Suitable host + self.debug("Migrating VM-ID: %s to Host: %s" % (vm.id, notSuitableHost.id)) + + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.hostid = notSuitableHost.id + cmd.virtualmachineid = vm.id + self.apiclient.migrateVirtualMachine(cmd) + + #Verify that the virtual machine got migrated to targeted Non-Suitable host + list_vm_response = list_virtual_machines( + self.apiclient, + id=vm.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "listVirtualMachine returned invalid object in response." + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "listVirtualMachines returned empty response." + ) + + self.assertEqual( + list_vm_response[0].id, + vm.id, + "Virtual machine id with the virtual machine from listVirtualMachine is not matching." + ) + + self.assertEqual( + list_vm_response[0].hostid, + notSuitableHost.id, + "The detination host id of migrated VM is not matching." + ) + + def test_05_no_vm_with_ha_gets_migrated_to_ha_host_in_live_migration(self): + """ Verify that none of the VMs with HA enabled migrate to an ha tagged host during live migration """ + + # Steps, + #1. Fresh install CS (Bonita) that supports this feature + #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage + #3. When adding host3, assign the HA host tag. + #4. Create VMs with and without the Compute Service Offering with the HA tag. + #5. Note the VMs on host1 and whether any of the VMs have their “HA enabled” flags enabled. + #6. Put host1 into maintenance mode. + # Validations, + #1. Make sure the VMs are created on either host1 or host2 and not on host3 + #2. Putting host1 into maintenance mode should trigger a live migration. Make sure the VMs are not migrated to HA enabled host3. + + # create and verify virtual machine with HA disabled service offering + virtual_machine_with_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_with_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_with_ha.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + + vm_with_ha_enabled = vms[0] + + #Verify the virtual machine got created on non HA host + list_hosts_response = list_hosts( + self.apiclient, + id=vm_with_ha_enabled.hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check Host is available" + ) + + self.assertEqual( + list_hosts_response[0].hahost, + False, + "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" + ) + + #put the Host in maintainance mode + self.debug("Enabling maintenance mode for host %s" % vm_with_ha_enabled.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = vm_with_ha_enabled.hostid + self.apiclient.prepareHostForMaintenance(cmd) + + timeout = self.services["timeout"] + + #verify the VM live migration happened to another running host + self.debug("Waiting for VM to come up") + wait_for_vm( + self.apiclient, + virtualmachineid=vm_with_ha_enabled.id, + interval=timeout + ) + + vms = VirtualMachine.list( + self.apiclient, + id=vm_with_ha_enabled.id, + listall=True, + ) + + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + + vm_with_ha_enabled1 = vms[0] + + list_hosts_response = list_hosts( + self.apiclient, + id=vm_with_ha_enabled1.hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check Host is available" + ) + + self.assertEqual( + list_hosts_response[0].hahost, + False, + "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" + ) + + self.debug("Disabling the maintenance mode for host %s" % vm_with_ha_enabled.hostid) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = vm_with_ha_enabled.hostid + self.apiclient.cancelHostMaintenance(cmd) + + def test_06_no_vm_without_ha_gets_migrated_to_ha_host_in_live_migration(self): + """ Verify that none of the VMs without HA enabled migrate to an ha tagged host during live migration """ + + # Steps, + #1. Fresh install CS (Bonita) that supports this feature + #2. Create Basic zone, pod, cluster, add 3 hosts to cluster (host1, host2, host3), secondary & primary Storage + #3. When adding host3, assign the HA host tag. + #4. Create VMs with and without the Compute Service Offering with the HA tag. + #5. Note the VMs on host1 and whether any of the VMs have their “HA enabled” flags enabled. + #6. Put host1 into maintenance mode. + # Validations, + #1. Make sure the VMs are created on either host1 or host2 and not on host3 + #2. Putting host1 into maintenance mode should trigger a live migration. Make sure the VMs are not migrated to HA enabled host3. + + # create and verify virtual machine with HA disabled service offering + virtual_machine_without_ha = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering_without_ha.id + ) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_without_ha.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + + vm_with_ha_disabled = vms[0] + + #Verify the virtual machine got created on non HA host + list_hosts_response = list_hosts( + self.apiclient, + id=vm_with_ha_disabled.hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check Host is available" + ) + + self.assertEqual( + list_hosts_response[0].hahost, + False, + "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" + ) + + #put the Host in maintainance mode + self.debug("Enabling maintenance mode for host %s" % vm_with_ha_disabled.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = vm_with_ha_disabled.hostid + self.apiclient.prepareHostForMaintenance(cmd) + + timeout = self.services["timeout"] + + #verify the VM live migration happened to another running host + self.debug("Waiting for VM to come up") + wait_for_vm( + self.apiclient, + virtualmachineid=vm_with_ha_disabled.id, + interval=timeout + ) + + vms = VirtualMachine.list( + self.apiclient, + id=vm_with_ha_disabled.id, + listall=True + ) + + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + + list_hosts_response = list_hosts( + self.apiclient, + id=vms[0].hostid + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check Host is available" + ) + + self.assertEqual( + list_hosts_response[0].hahost, + False, + "The virtual machine is not ha enabled so check if VM is created on host which is also not ha enabled" + ) + + self.debug("Disabling the maintenance mode for host %s" % vm_with_ha_disabled.hostid) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = vm_with_ha_disabled.hostid + self.apiclient.cancelHostMaintenance(cmd) diff --git a/test/integration/lib/common.py b/test/integration/lib/common.py index 5a644aaf16a..630971c606f 100644 --- a/test/integration/lib/common.py +++ b/test/integration/lib/common.py @@ -185,6 +185,27 @@ def wait_for_ssvms(apiclient, zoneid, podid, interval=60): break return +def wait_for_vm(apiclient, virtualmachineid, interval=60): + """After setup wait for VM to come Up""" + + time.sleep(interval) + timeout = 40 + while True: + list_vm_response = list_virtual_machines( + apiclient, + id=virtualmachineid + ) + vm = list_vm_response[0] + if vm.state != 'Running': + # Sleep to ensure VM is Up and Running + time.sleep(interval) + timeout = timeout - 1 + elif vm.state == 'Running': + break + elif timeout == 0: + raise Exception("VM failed to come up") + break + return def download_builtin_templates(apiclient, zoneid, hypervisor, host, linklocalip, interval=60): diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 2da4d5f8ecd..47a1857e48a 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -945,7 +945,8 @@ } }, tags: { label: 'label.storage.tags' }, - domain: { label: 'label.domain' } + domain: { label: 'label.domain' }, + storagetype: { label: 'label.storage.type' } } ], diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index c4e2f6a10db..291427ec060 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -9,7 +9,7 @@ // See the License for the specific language governing permissions and // limitations under the License. (function($, cloudStack) { - var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, featuredIsoObjs, community + var zoneObjs, hypervisorObjs, featuredTemplateObjs, communityTemplateObjs, myTemplateObjs, featuredIsoObjs, community; var selectedZoneObj, selectedTemplateObj, selectedHypervisor, selectedDiskOfferingObj; var step5ContainerType = 'nothing-to-select'; //'nothing-to-select', 'select-network', 'select-security-group' @@ -17,6 +17,17 @@ maxDiskOfferingSize: function() { return g_capabilities.customdiskofferingmaxsize; }, + + // Called in networks list, when VPC drop-down is changed + // -- if vpcID given, return true if in network specified by vpcID + // -- if vpcID == -1, return true if network is not associated with a VPC + vpcFilter: function(data, vpcID) { + return vpcID != -1? + data.vpcid == vpcID : + !data.vpcid; + }, + + // Data providers for each wizard step steps: [ // Step 1: Setup function(args) { @@ -301,11 +312,23 @@ networkData.account = g_account; } - var networkObjs; + var networkObjs, vpcObjs; + + // Get VPCs + $.ajax({ + url: createURL('listVPCs'), + data: isDomainAdmin() ? + { account: args.context.users[0].account, domainid: args.context.users[0].domainid } : + { listAll: true }, + async: false, + success: function(json) { + vpcObjs = json.listvpcsresponse.vpc ? json.listvpcsresponse.vpc : []; + } + }); + $.ajax({ url: createURL('listNetworks'), data: networkData, - dataType: "json", async: false, success: function(json) { networkObjs = json.listnetworksresponse.network ? json.listnetworksresponse.network : []; @@ -337,7 +360,8 @@ myNetworks: [], //not used any more sharedNetworks: networkObjs, securityGroups: [], - networkOfferings: networkOfferingObjs + networkOfferings: networkOfferingObjs, + vpcs: vpcObjs } }); } @@ -370,7 +394,8 @@ myNetworks: [], //not used any more sharedNetworks: [], securityGroups: securityGroupArray, - networkOfferings: [] + networkOfferings: [], + vpcs: [] } }); } @@ -382,7 +407,8 @@ myNetworks: [], //not used any more sharedNetworks: [], securityGroups: [], - networkOfferings: [] + networkOfferings: [], + vpcs: [] } }); } diff --git a/ui/scripts/network.js b/ui/scripts/network.js index d24a8ea230e..31e2c97a4fd 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1405,9 +1405,10 @@ args.$tierSelect.hide(); } - // args.$tierSelect.change(function() { + args.$tierSelect.change(function() { args.$tierSelect.closest('.list-view').listView('refresh'); - // }); + }); + args.$tierSelect.closest('.list-view').listView('refresh'); }, listView: $.extend(true, {}, cloudStack.sections.instances, { @@ -3858,10 +3859,22 @@ }, { displaytext: { label: 'label.description', isEditable: true }, + account: { label: 'label.account' }, + domain: { label: 'label.domain' }, zonename: { label: 'label.zone' }, cidr: { label: 'label.cidr' }, networkdomain: { label: 'label.network.domain' }, state: { label: 'label.state' }, + restartrequired: { + label: 'label.restart.required', + converter: function(booleanValue) { + if (booleanValue == true) { + return "Yes"; + } + + return "No"; + } + }, id: { label: 'label.id' } } ], diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 3d82e484dc9..990747d8c0d 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -103,6 +103,14 @@ .click(function() { var $radio = $(this).closest('.select').find('input[type=radio]'); + if ($(this).attr('type') == 'checkbox') { + if ($(this).closest('.select-container').hasClass('single-select')) { + $(this).closest('.select').siblings().find('input[type=checkbox]') + .attr('checked', false); + $(this).closest('.select').find('input[type=radio]').click(); + } + } + if ($radio.is(':checked') && !$(this).is(':checked')) { if (!$radio.closest('.select').index()) { return false; @@ -121,7 +129,8 @@ $('
').addClass('select-desc') .append($('
').addClass('name').html(this[fields.name])) .append($('
').addClass('desc').html(this[fields.desc])) - ); + ) + .data('json-obj', this); $selects.append($select); @@ -139,6 +148,11 @@ if (!$checkbox.is(':checked')) { $checkbox.attr('checked', true); } + + if ($(this).closest('.select-container').hasClass('single-select')) { + $(this).closest('.select').siblings().find('input[type=checkbox]') + .attr('checked', false); + } }) .after( $('
').addClass('name').html(options.secondary.desc) @@ -480,9 +494,81 @@ // Show relevant conditional sub-step if present $step.find('.wizard-step-conditional').hide(); + // Filter network list by VPC ID + var filterNetworkList = function(vpcID) { + var $selects = $step.find('.my-networks .select-container .select'); + var $visibleSelects = $($.grep($selects, function(select) { + var $select = $(select); + + return args.vpcFilter($select.data('json-obj'), vpcID); + })); + var $addNetworkForm = $step.find('.select.new-network'); + var $addNewNetworkCheck = $addNetworkForm.find('input[name=new-network]'); + + // VPC networks cannot be created via instance wizard + if (vpcID != -1) { + $step.find('.my-networks .select-container').addClass('single-select'); + $addNetworkForm.hide(); + + if ($addNewNetworkCheck.is(':checked')) { + $addNewNetworkCheck.click(); + $addNewNetworkCheck.attr('checked', false); + } + } else { + $step.find('.my-networks .select-container').removeClass('single-select'); + $addNetworkForm.show(); + } + + $selects.find('input[type=checkbox]').attr('checked', false); + $selects.hide(); + $visibleSelects.show(); + + // Select first visible item by default + $visibleSelects.filter(':first') + .find('input[type=radio]') + .click(); + + cloudStack.evenOdd($visibleSelects, 'div.select', { + even: function($elem) { + $elem.removeClass('odd'); + $elem.addClass('even'); + }, + odd: function($elem) { + $elem.removeClass('even'); + $elem.addClass('odd'); + } + }); + }; + + var $vpcSelect = $step.find('select[name=vpc-filter]'); + + $vpcSelect.unbind('change'); + $vpcSelect.change(function() { + filterNetworkList($vpcSelect.val()); + }); + return { response: { success: function(args) { + var vpcs = args.data.vpcs; + + // Populate VPC drop-down + $vpcSelect.html(''); + $(vpcs).map(function(index, vpc) { + var $option = $('