// 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.vpc; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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.api.commands.ListPrivateGatewaysCmd; import com.cloud.api.commands.ListStaticRoutesCmd; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.deploy.DeployDestination; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.UnsupportedServiceException; import com.cloud.network.IPAddressVO; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; 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.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.element.VpcProvider; import com.cloud.network.vpc.VpcOffering.State; import com.cloud.network.vpc.Dao.PrivateIpDao; import com.cloud.network.vpc.Dao.StaticRouteDao; import com.cloud.network.vpc.Dao.VpcDao; import com.cloud.network.vpc.Dao.VpcGatewayDao; import com.cloud.network.vpc.Dao.VpcOfferingDao; import com.cloud.network.vpc.Dao.VpcOfferingServiceMapDao; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingServiceMapVO; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.org.Grouping; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Ternary; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.dao.DomainRouterDao; @Local(value = { VpcManager.class, VpcService.class }) public class VpcManagerImpl implements VpcManager, Manager{ private static final Logger s_logger = Logger.getLogger(VpcManagerImpl.class); @Inject VpcOfferingDao _vpcOffDao; @Inject VpcOfferingServiceMapDao _vpcOffSvcMapDao; @Inject VpcDao _vpcDao; @Inject ConfigurationDao _configDao; @Inject ConfigurationManager _configMgr; @Inject AccountManager _accountMgr; @Inject NetworkDao _ntwkDao; @Inject NetworkManager _ntwkMgr; @Inject IPAddressDao _ipAddressDao; @Inject DomainRouterDao _routerDao; @Inject VpcGatewayDao _vpcGatewayDao; @Inject PrivateIpDao _privateIpDao; @Inject StaticRouteDao _staticRouteDao; @Inject NetworkOfferingServiceMapDao _ntwkOffServiceDao ; @Inject VpcOfferingServiceMapDao _vpcOffServiceDao; @Inject PhysicalNetworkDao _pNtwkDao; private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker")); private VpcProvider vpcElement = null; String _name; int _cleanupInterval; @Override @DB public boolean configure(String name, Map params) throws ConfigurationException { _name = name; //configure default vpc offering Transaction txn = Transaction.currentTxn(); txn.start(); if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCOfferingName) == null) { s_logger.debug("Creating default VPC offering " + VpcOffering.defaultVPCOfferingName); Map> svcProviderMap = new HashMap>(); Set defaultProviders = new HashSet(); defaultProviders.add(Provider.VPCVirtualRouter); for (Service svc : getSupportedServices()) { if (svc == Service.Lb) { Set lbProviders = new HashSet(); lbProviders.add(Provider.VPCVirtualRouter); lbProviders.add(Provider.Netscaler); svcProviderMap.put(svc, lbProviders); } else { svcProviderMap.put(svc, defaultProviders); } } createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled); } txn.commit(); ComponentLocator locator = ComponentLocator.getCurrentLocator(); ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); Map configs = configDao.getConfiguration(params); String value = configs.get(Config.VpcCleanupInterval.key()); _cleanupInterval = NumbersUtil.parseInt(value, 60 * 60); // 1 hour return true; } @Override public boolean start() { _executor.scheduleAtFixedRate(new VpcCleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS); return true; } @Override public boolean stop() { return true; } @Override public String getName() { return _name; } @Override public List getVpcNetworks(long vpcId) { return _ntwkDao.listByVpc(vpcId); } @Override public VpcOffering getVpcOffering(long vpcOffId) { return _vpcOffDao.findById(vpcOffId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create=true) public VpcOffering createVpcOffering(String name, String displayText, List supportedServices) { Map> svcProviderMap = new HashMap>(); Set defaultProviders = new HashSet(); defaultProviders.add(Provider.VPCVirtualRouter); boolean sourceNatSvc = false; boolean firewallSvs = false; // populate the services first for (String serviceName : supportedServices) { // validate if the service is supported Service service = Network.Service.getService(serviceName); if (service == null || service == Service.Gateway) { throw new InvalidParameterValueException("Invalid service " + serviceName); } //don't allow security group service for vpc if (service == Service.SecurityGroup) { throw new UnsupportedServiceException("Service " + Service.SecurityGroup.getName() + " is not supported by VPC"); } svcProviderMap.put(service, defaultProviders); if (service == Service.NetworkACL) { firewallSvs = true; } if (service == Service.SourceNat) { sourceNatSvc = true; } } if (!sourceNatSvc) { s_logger.debug("Automatically adding source nat service to the list of VPC services"); svcProviderMap.put(Service.SourceNat, defaultProviders); } if (!firewallSvs) { s_logger.debug("Automatically adding network ACL service to the list of VPC services"); svcProviderMap.put(Service.NetworkACL, defaultProviders); } svcProviderMap.put(Service.Gateway, defaultProviders); return createVpcOffering(name, displayText, svcProviderMap, false, null); } @Override @DB public VpcOffering createVpcOffering(String name, String displayText, Map> svcProviderMap, boolean isDefault, State state) { Transaction txn = Transaction.currentTxn(); txn.start(); // create vpc offering object VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, null); if (state != null) { offering.setState(state); } s_logger.debug("Adding vpc offering " + offering); offering = _vpcOffDao.persist(offering); // populate services and providers if (svcProviderMap != null) { for (Network.Service service : svcProviderMap.keySet()) { Set providers = svcProviderMap.get(service); if (providers != null && !providers.isEmpty()) { for (Network.Provider provider : providers) { VpcOfferingServiceMapVO offService = new VpcOfferingServiceMapVO(offering.getId(), service, provider); _vpcOffSvcMapDao.persist(offService); s_logger.trace("Added service for the vpc offering: " + offService + " with provider " + provider.getName()); } } else { throw new InvalidParameterValueException("Provider is missing for the VPC offering service " + service.getName()); } } } txn.commit(); UserContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); return offering; } @Override public Vpc getVpc(long vpcId) { return _vpcDao.findById(vpcId); } @Override public Vpc getActiveVpc(long vpcId) { return _vpcDao.getActiveVpcById(vpcId); } @Override public Map> getVpcOffSvcProvidersMap(long vpcOffId) { Map> serviceProviderMap = new HashMap>(); List map = _vpcOffSvcMapDao.listByVpcOffId(vpcOffId); for (VpcOfferingServiceMapVO instance : map) { String service = instance.getService(); Set providers; providers = serviceProviderMap.get(service); if (providers == null) { providers = new HashSet(); } providers.add(Provider.getProvider(instance.getProvider())); serviceProviderMap.put(Service.getService(service), providers); } return serviceProviderMap; } @Override public List listVpcOfferings(Long id, String name, String displayText, List supportedServicesStr, Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal) { Filter searchFilter = new Filter(VpcOfferingVO.class, "created", false, startIndex, pageSizeVal); SearchCriteria sc = _vpcOffDao.createSearchCriteria(); if (keyword != null) { SearchCriteria ssc = _vpcOffDao.createSearchCriteria(); ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } if (name != null) { sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + name + "%"); } if (displayText != null) { sc.addAnd("displayText", SearchCriteria.Op.LIKE, "%" + displayText + "%"); } if (isDefault != null) { sc.addAnd("isDefault", SearchCriteria.Op.EQ, isDefault); } if (state != null) { sc.addAnd("state", SearchCriteria.Op.EQ, state); } if (id != null) { sc.addAnd("id", SearchCriteria.Op.EQ, id); } List offerings = _vpcOffDao.search(sc, searchFilter); // filter by supported services boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !offerings.isEmpty()); if (listBySupportedServices) { List supportedOfferings = new ArrayList(); Service[] supportedServices = null; if (listBySupportedServices) { supportedServices = new Service[supportedServicesStr.size()]; int i = 0; for (String supportedServiceStr : supportedServicesStr) { Service service = Service.getService(supportedServiceStr); if (service == null) { throw new InvalidParameterValueException("Invalid service specified " + supportedServiceStr); } else { supportedServices[i] = service; } i++; } } for (VpcOfferingVO offering : offerings) { if (areServicesSupportedByVpcOffering(offering.getId(), supportedServices)) { supportedOfferings.add(offering); } } return supportedOfferings; } else { return offerings; } } @Override public boolean areServicesSupportedByVpcOffering(long vpcOffId, Service... services) { return (_vpcOffSvcMapDao.areServicesSupportedByNetworkOffering(vpcOffId, services)); } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_DELETE, eventDescription = "deleting vpc offering") public boolean deleteVpcOffering(long offId) { UserContext.current().setEventDetails(" Id: " + offId); // Verify vpc offering id VpcOfferingVO offering = _vpcOffDao.findById(offId); if (offering == null) { throw new InvalidParameterValueException("unable to find vpc offering " + offId); } // Don't allow to delete default vpc offerings if (offering.isDefault() == true) { throw new InvalidParameterValueException("Default network offering can't be deleted"); } // don't allow to delete vpc offering if it's in use by existing vpcs (the offering can be disabled though) int vpcCount = _vpcDao.getVpcCountByOfferingId(offId); if (vpcCount > 0) { throw new InvalidParameterValueException("Can't delete vpc offering " + offId + " as its used by " + vpcCount + " vpcs. " + "To make the network offering unavaiable, disable it"); } if (_vpcOffDao.remove(offId)) { return true; } else { return false; } } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_UPDATE, eventDescription = "updating vpc offering") public VpcOffering updateVpcOffering(long vpcOffId, String vpcOfferingName, String displayText, String state) { UserContext.current().setEventDetails(" Id: " + vpcOffId); // Verify input parameters VpcOfferingVO offeringToUpdate = _vpcOffDao.findById(vpcOffId); if (offeringToUpdate == null) { throw new InvalidParameterValueException("Unable to find vpc offering " + vpcOffId); } VpcOfferingVO offering = _vpcOffDao.createForUpdate(vpcOffId); if (vpcOfferingName != null) { offering.setName(vpcOfferingName); } if (displayText != null) { offering.setDisplayText(displayText); } if (state != null) { boolean validState = false; for (VpcOffering.State st : VpcOffering.State.values()) { if (st.name().equalsIgnoreCase(state)) { validState = true; offering.setState(st); } } if (!validState) { throw new InvalidParameterValueException("Incorrect state value: " + state); } } if (_vpcOffDao.update(vpcOffId, offering)) { s_logger.debug("Updated VPC offeirng id=" + vpcOffId); return _vpcOffDao.findById(vpcOffId); } else { return null; } } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create=true) public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain) { Account caller = UserContext.current().getCaller(); Account owner = _accountMgr.getAccount(vpcOwnerId); //Verify that caller can perform actions in behalf of vpc owner _accountMgr.checkAccess(caller, null, false, owner); // Validate vpc offering VpcOfferingVO vpcOff = _vpcOffDao.findById(vpcOffId); if (vpcOff == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find vpc offering by specified id"); ex.addProxyObject("vpc_offerings", vpcOffId, "vpcOfferingId"); throw ex; } //Validate zone DataCenter zone = _configMgr.getZone(zoneId); if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // See DataCenterVO.java PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled"); ex.addProxyObject("data_center", zone.getId(), "zoneId"); throw ex; } if (networkDomain == null) { // 1) Get networkDomain from the corresponding account networkDomain = _ntwkMgr.getAccountNetworkDomain(owner.getId(), zoneId); // 2) If null, generate networkDomain using domain suffix from the global config variables if (networkDomain == null) { networkDomain = "cs" + Long.toHexString(owner.getId()) + _ntwkMgr.getDefaultNetworkDomain(); } } return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain); } @Override public Vpc createVpc(long zoneId, long vpcOffId, Account vpcOwner, String vpcName, String displayText, String cidr, String networkDomain) { //the provider has to be enabled at least in one network in the zone boolean providerEnabled = false; for (PhysicalNetwork pNtwk : _pNtwkDao.listByZone(zoneId)) { if (_ntwkMgr.isProviderEnabledInPhysicalNetwork(pNtwk.getId(), Provider.VPCVirtualRouter.getName())) { providerEnabled = true; break; } } if (!providerEnabled) { throw new InvalidParameterValueException("Provider " + Provider.VPCVirtualRouter.getName() + " should be enabled in at least one physical network of the zone specified"); } //Validate CIDR if (!NetUtils.isValidCIDR(cidr)) { throw new InvalidParameterValueException("Invalid CIDR specified " + cidr); } // validate network domain if (!NetUtils.verifyDomainName(networkDomain)) { throw new InvalidParameterValueException( "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', " + "the digits '0' through '9', " + "and the hyphen ('-'); can't start or end with \"-\""); } //don't allow overlapping CIDRS for the VPCs of the same account List vpcs = getVpcsForAccount(vpcOwner.getId()); for (Vpc vpc : vpcs) { if (NetUtils.isNetworksOverlap(cidr, vpc.getCidr())) { throw new InvalidParameterValueException("Account already has vpc with cidr " + vpc.getCidr() + " that overlaps the cidr specified: " + cidr); } } VpcVO vpc = new VpcVO (zoneId, vpcName, displayText, vpcOwner.getId(), vpcOwner.getDomainId(), vpcOffId, cidr, networkDomain); vpc = _vpcDao.persist(vpc); if (vpc != null) { s_logger.debug("Created VPC " + vpc); } else { s_logger.debug("Failed to create VPC"); } return vpc; } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_DELETE, eventDescription = "deleting VPC") public boolean deleteVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException { UserContext.current().setEventDetails(" Id: " + vpcId); Account caller = UserContext.current().getCaller(); // Verify vpc id Vpc vpc = getVpc(vpcId); if (vpc == null) { throw new InvalidParameterValueException("unable to find VPC id=" + vpcId); } //verify permissions _accountMgr.checkAccess(caller, null, false, vpc); return destroyVpc(vpc); } @Override public boolean destroyVpc(Vpc vpc) throws ConcurrentOperationException, ResourceUnavailableException { UserContext ctx = UserContext.current(); //don't allow to delete vpc if it's in use by existing networks int networksCount = _ntwkDao.getNetworkCountByVpcId(vpc.getId()); if (networksCount > 0) { throw new InvalidParameterValueException("Can't delete VPC " + vpc + " as its used by " + networksCount + " networks"); } //mark VPC as disabled 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); _vpcDao.update(vpc.getId(), vpcVO); //shutdown VPC if (!shutdownVpc(vpc.getId())) { s_logger.warn("Failed to shutdown vpc " + vpc + " as a part of vpc destroy process"); return false; } //cleanup vpc resources if (!cleanupVpcResources(vpc.getId(), ctx.getCaller(), ctx.getCallerUserId())) { s_logger.warn("Failed to cleanup resources for vpc " + vpc); return false; } //update the instance with removed flag only when the cleanup is executed successfully if (_vpcDao.remove(vpc.getId())) { s_logger.debug("Vpc " + vpc + " is removed succesfully"); return true; } else { return false; } } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription = "updating vpc") public Vpc updateVpc(long vpcId, String vpcName, String displayText) { UserContext.current().setEventDetails(" Id: " + vpcId); Account caller = UserContext.current().getCaller(); // Verify input parameters VpcVO vpcToUpdate = _vpcDao.findById(vpcId); if (vpcToUpdate == null) { throw new InvalidParameterValueException("Unable to find vpc offering " + vpcId); } _accountMgr.checkAccess(caller, null, false, vpcToUpdate); VpcVO vpc = _vpcDao.createForUpdate(vpcId); if (vpcName != null) { vpc.setName(vpcName); } if (displayText != null) { vpc.setDisplayText(displayText); } if (_vpcDao.update(vpcId, vpc)) { s_logger.debug("Updated VPC id=" + vpcId); return _vpcDao.findById(vpcId); } else { return null; } } @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) { Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); _accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); Filter searchFilter = new Filter(VpcVO.class, "created", false, startIndex, pageSizeVal); SearchBuilder sb = _vpcDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("displayText", sb.entity().getDisplayText(), SearchCriteria.Op.LIKE); sb.and("vpcOfferingId", sb.entity().getVpcOfferingId(), SearchCriteria.Op.EQ); sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("restartRequired", sb.entity().isRestartRequired(), SearchCriteria.Op.EQ); sb.and("cidr", sb.entity().getCidr(), SearchCriteria.Op.EQ); // now set the SC criteria... SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (keyword != null) { SearchCriteria ssc = _vpcDao.createSearchCriteria(); ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } if (vpcName != null) { sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + vpcName + "%"); } if (displayText != null) { sc.addAnd("displayText", SearchCriteria.Op.LIKE, "%" + displayText + "%"); } if (id != null) { sc.addAnd("id", SearchCriteria.Op.EQ, id); } if (vpcOffId != null) { sc.addAnd("vpcOfferingId", SearchCriteria.Op.EQ, vpcOffId); } if (zoneId != null) { sc.addAnd("zoneId", SearchCriteria.Op.EQ, zoneId); } if (state != null) { sc.addAnd("state", SearchCriteria.Op.EQ, state); } if (cidr != null) { sc.addAnd("cidr", SearchCriteria.Op.EQ, cidr); } if (restartRequired != null) { sc.addAnd("restartRequired", SearchCriteria.Op.EQ, restartRequired); } List vpcs = _vpcDao.search(sc, searchFilter); // filter by supported services boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty()); if (listBySupportedServices) { List supportedVpcs = new ArrayList(); Service[] supportedServices = null; if (listBySupportedServices) { supportedServices = new Service[supportedServicesStr.size()]; int i = 0; for (String supportedServiceStr : supportedServicesStr) { Service service = Service.getService(supportedServiceStr); if (service == null) { throw new InvalidParameterValueException("Invalid service specified " + supportedServiceStr); } else { supportedServices[i] = service; } i++; } } for (VpcVO vpc : vpcs) { if (areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), supportedServices)) { supportedVpcs.add(vpc); } } return supportedVpcs; } else { return vpcs; } } @Override public List getSupportedServices() { List services = new ArrayList(); services.add(Network.Service.Dhcp); services.add(Network.Service.Dns); services.add(Network.Service.UserData); services.add(Network.Service.NetworkACL); services.add(Network.Service.PortForwarding); services.add(Network.Service.Lb); services.add(Network.Service.SourceNat); services.add(Network.Service.StaticNat); services.add(Network.Service.Gateway); services.add(Network.Service.Vpn); return services; } @Override public boolean startVpc(long vpcId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); User callerUser = _accountMgr.getActiveUser(ctx.getCallerUserId()); //check if vpc exists Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); ex.addProxyObject("vpc", vpcId, "VPC"); throw ex; } //permission check _accountMgr.checkAccess(caller, null, false, vpc); DataCenter dc = _configMgr.getZone(vpc.getZoneId()); DeployDestination dest = new DeployDestination(dc, null, null, null); ReservationContext context = new ReservationContextImpl(null, null, callerUser, _accountMgr.getAccount(vpc.getAccountId())); boolean result = true; try { if (!startVpc(vpc, dest, context)) { s_logger.warn("Failed to start vpc " + vpc); result = false; } } catch (Exception ex) { s_logger.warn("Failed to start vpc " + vpc + " due to ", ex); result = false; } finally { //do cleanup if (!result && destroyOnFailure) { s_logger.debug("Destroying vpc " + vpc + " that failed to start"); if (destroyVpc(vpc)) { s_logger.warn("Successfully destroyed vpc " + vpc + " that failed to start"); } else { s_logger.warn("Failed to destroy vpc " + vpc + " that failed to start"); } } } return result; } protected boolean startVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { //deploy provider if (getVpcElement().implementVpc(vpc, dest, context)) { s_logger.debug("Vpc " + vpc + " has started succesfully"); return true; } else { s_logger.warn("Vpc " + vpc + " failed to start"); return false; } } @Override public boolean shutdownVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); //check if vpc exists Vpc vpc = getVpc(vpcId); if (vpc == null) { throw new InvalidParameterValueException("Unable to find vpc by id " + vpcId); } //permission check _accountMgr.checkAccess(caller, null, false, vpc); //shutdown provider boolean success = getVpcElement().shutdownVpc(vpc); //FIXME - once more features are added to vpc (gateway/firewall rules, etc - cleanup them here) if (success) { s_logger.debug("Vpc " + vpc + " has been shutdown succesfully"); } else { s_logger.warn("Vpc " + vpc + " failed to shutdown"); } return success; } @Override @DB public void validateGuestNtkwForVpc(NetworkOffering guestNtwkOff, String cidr, String networkDomain, Account networkOwner, Vpc vpc, Long networkId, String gateway) { if (networkId == null) { //1) Validate attributes that has to be passed in when create new guest network validateNewVpcGuestNetwork(cidr, gateway, networkOwner, vpc, networkDomain); } //2) Only Isolated networks with Source nat service enabled can be added to vpc if (!(guestNtwkOff.getGuestType() == GuestType.Isolated && _ntwkMgr.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.SourceNat))) { throw new InvalidParameterValueException("Only networks of type " + GuestType.Isolated + " with service " + Service.SourceNat + " can be added as a part of VPC"); } //3) No redundant router support if (guestNtwkOff.getRedundantRouter()) { throw new InvalidParameterValueException("No redunant router support when network belnogs to VPC"); } //4) Conserve mode should be off if (guestNtwkOff.isConserveMode()) { throw new InvalidParameterValueException("Only networks with conserve mode Off can belong to VPC"); } //5) Check services/providers against VPC providers List networkProviders = _ntwkOffServiceDao.listByNetworkOfferingId(guestNtwkOff.getId()); for (NetworkOfferingServiceMapVO nSvcVO : networkProviders) { String pr = nSvcVO.getProvider(); String service = nSvcVO.getService(); if (_vpcOffServiceDao.findByServiceProviderAndOfferingId(service, pr, vpc.getVpcOfferingId()) == null) { throw new InvalidParameterValueException("Service/provider combination " + service + "/" + pr + " is not supported by VPC " + vpc); } } //6) Only one network in the VPC can support LB if (_ntwkMgr.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.Lb)) { List networks = getVpcNetworks(vpc.getId()); for (Network network : networks) { if (networkId != null && network.getId() == networkId.longValue()) { //skip my own network continue; } else { if (_ntwkMgr.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { throw new InvalidParameterValueException("LB service is already supported " + "by network " + network + " in VPC " + vpc); } } } } } protected void validateNewVpcGuestNetwork(String cidr, String gateway, Account networkOwner, Vpc vpc, String networkDomain) { Vpc locked = _vpcDao.acquireInLockTable(vpc.getId()); if (locked == null) { throw new CloudRuntimeException("Unable to acquire lock on " + vpc); } try { //1) CIDR is required if (cidr == null) { throw new InvalidParameterValueException("Gateway/netmask are required when create network for VPC"); } //2) Network cidr should be within vpcCidr if (!NetUtils.isNetworkAWithinNetworkB(cidr, vpc.getCidr())) { throw new InvalidParameterValueException("Network cidr " + cidr + " is not within vpc " + vpc + " cidr"); } //3) Network cidr shouldn't cross the cidr of other vpc network cidrs List ntwks = _ntwkDao.listByVpc(vpc.getId()); for (Network ntwk : ntwks) { assert (cidr != null) : "Why the network cidr is null when it belongs to vpc?"; if (NetUtils.isNetworkAWithinNetworkB(ntwk.getCidr(), vpc.getCidr()) || NetUtils.isNetworkAWithinNetworkB(vpc.getCidr(), ntwk.getCidr())) { throw new InvalidParameterValueException("Network cidr " + cidr + " crosses other network cidr " + ntwk + " belonging to the same vpc " + vpc); } } //4) vpc and network should belong to the same owner if (vpc.getAccountId() != networkOwner.getId()) { throw new InvalidParameterValueException("Vpc " + vpc + " owner is different from the network owner " + networkOwner); } //5) network domain should be the same as VPC's if (!networkDomain.equalsIgnoreCase(vpc.getNetworkDomain())) { throw new InvalidParameterValueException("Network domain of the new network should match network" + " domain of vpc " + vpc); } //6) gateway should never be equal to the cidr subnet if (NetUtils.getCidrSubNet(cidr).equalsIgnoreCase(gateway)) { throw new InvalidParameterValueException("Invalid gateway specified. It should never be equal to the cidr subnet value"); } } finally { s_logger.debug("Releasing lock for " + locked); _vpcDao.releaseFromLockTable(locked.getId()); } } @Override public VpcProvider getVpcElement() { if (vpcElement == null) { vpcElement = ((VpcProvider)_ntwkMgr.getElementImplementingProvider(Provider.VPCVirtualRouter.getName())); } return vpcElement; } @Override public List getVpcsForAccount(long accountId) { return _vpcDao.listByAccountId(accountId); } public boolean cleanupVpcResources(long vpcId, Account caller, long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException { s_logger.debug("Cleaning up resources for vpc id=" + vpcId); boolean success = true; //1) release all ip addresses List ipsToRelease = _ipAddressDao.listByAssociatedVpc(vpcId, null); s_logger.debug("Releasing ips for vpc id=" + vpcId + " as a part of vpc cleanup"); for (IPAddressVO ipToRelease : ipsToRelease) { success = success && _ntwkMgr.disassociatePublicIpAddress(ipToRelease.getId(), callerUserId, caller); if (!success) { s_logger.warn("Failed to cleanup ip " + ipToRelease + " as a part of vpc id=" + vpcId + " cleanup"); } } if (success) { s_logger.debug("Released ip addresses for vpc id=" + vpcId + " as a part of cleanup vpc process"); } else { s_logger.warn("Failed to release ip addresses for vpc id=" + vpcId + " as a part of cleanup vpc process"); //although it failed, proceed to the next cleanup step as it doesn't depend on the public ip release } //2) Delete all static route rules if (!revokeStaticRoutesForVpc(vpcId, caller)) { s_logger.warn("Failed to revoke static routes for vpc " + vpcId + " as a part of cleanup vpc process"); return false; } //3) Delete private gateway VpcGateway gateway = getPrivateGatewayForVpc(vpcId); if (gateway != null) { s_logger.debug("Deleting private gateway " + gateway + " as a part of vpc " + vpcId + " resources cleanup"); if (!deleteVpcPrivateGateway(gateway.getId())) { success = false; s_logger.debug("Failed to delete private gateway " + gateway + " as a part of vpc " + vpcId + " resources cleanup"); } else { s_logger.debug("Deleted private gateway " + gateway + " as a part of vpc " + vpcId + " resources cleanup"); } } return success; } @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_RESTART, eventDescription = "restarting vpc") public boolean restartVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { Account caller = UserContext.current().getCaller(); // Verify input parameters Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); ex.addProxyObject("vpc", vpcId, "VPC"); throw ex; } _accountMgr.checkAccess(caller, null, false, vpc); s_logger.debug("Restarting VPC " + vpc); boolean restartRequired = false; try { s_logger.debug("Shutting down VPC " + vpc + " as a part of VPC restart process"); if (!shutdownVpc(vpcId)) { s_logger.warn("Failed to shutdown vpc as a part of VPC " + vpc + " restart process"); restartRequired = true; return false; } s_logger.debug("Starting VPC " + vpc + " as a part of VPC restart process"); if (!startVpc(vpcId, false)) { s_logger.warn("Failed to start vpc as a part of VPC " + vpc + " restart process"); restartRequired = true; return false; } s_logger.debug("VPC " + vpc + " was restarted successfully"); return true; } finally { s_logger.debug("Updating VPC " + vpc + " with restartRequired=" + restartRequired); VpcVO vo = _vpcDao.findById(vpcId); vo.setRestartRequired(restartRequired); _vpcDao.update(vpc.getId(), vo); } } @Override public List getVpcRouters(long vpcId) { return _routerDao.listByVpcId(vpcId); } @Override public PrivateGateway getVpcPrivateGateway(long id) { VpcGateway gateway = _vpcGatewayDao.findById(id); if (gateway == null || gateway.getType() != VpcGateway.Type.Private) { return null; } return getPrivateGatewayProfile(gateway); } @Override public VpcGateway getVpcGateway(long id) { return _vpcGatewayDao.findById(id); } protected PrivateGateway getPrivateGatewayProfile(VpcGateway gateway) { Network network = _ntwkMgr.getNetwork(gateway.getNetworkId()); return new PrivateGatewayProfile(gateway, network.getPhysicalNetworkId()); } @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_CREATE, eventDescription = "creating vpc private gateway", create=true) public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, String gateway, String netmask, long gatewayOwnerId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { //Validate parameters Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified"); ex.addProxyObject("vpc", vpcId, "VPC"); throw ex; } //allow only one private gateway per vpc VpcGatewayVO gatewayVO = _vpcGatewayDao.getPrivateGatewayForVpc(vpcId); if (gatewayVO != null) { throw new InvalidParameterValueException("Private ip address already exists for vpc " + vpc); } //Validate physical network if (physicalNetworkId == null) { List pNtwks = _ntwkMgr.getPhysicalNtwksSupportingTrafficType(vpc.getZoneId(), TrafficType.Guest); if (pNtwks.isEmpty() || pNtwks.size() != 1) { throw new InvalidParameterValueException("Physical network can't be determined; pass physical network id"); } physicalNetworkId = pNtwks.get(0).getId(); } Transaction txn = Transaction.currentTxn(); txn.start(); s_logger.debug("Creating Private gateway for VPC " + vpc); //1) create private network String networkName = "vpc-" + vpc.getName() + "-privateNetwork"; Network privateNtwk = _ntwkMgr.createPrivateNetwork(networkName, networkName, physicalNetworkId, vlan, ipAddress, null, gateway, netmask, gatewayOwnerId, vpcId); //2) create gateway entry gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), privateNtwk.getId(), vlan, gateway, netmask, vpc.getAccountId(), vpc.getDomainId()); _vpcGatewayDao.persist(gatewayVO); s_logger.debug("Created vpc gateway entry " + gatewayVO); txn.commit(); return getVpcPrivateGateway(gatewayVO.getId()); } @Override @DB public PrivateGateway applyVpcPrivateGateway(Long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException { VpcGatewayVO vo = _vpcGatewayDao.acquireInLockTable(gatewayId); if (vo == null) { throw new ConcurrentOperationException("Unable to lock gateway " + gatewayId); } try { PrivateGateway gateway = getVpcPrivateGateway(gatewayId); if (getVpcElement().createPrivateGateway(gateway)) { s_logger.debug("Private gateway " + gateway + " was applied succesfully on the backend"); if (vo.getState() != VpcGateway.State.Ready) { vo.setState(VpcGateway.State.Ready); _vpcGatewayDao.update(vo.getId(), vo); s_logger.debug("Marke gateway " + gateway + " with state " + VpcGateway.State.Ready); } return getVpcPrivateGateway(gatewayId); } else { s_logger.warn("Private gateway " + gateway + " failed to apply on the backend"); return null; } } finally { if (vo != null) { _vpcGatewayDao.releaseFromLockTable(gatewayId); } } } @Override @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_DELETE, eventDescription = "deleting private gateway") @DB public boolean deleteVpcPrivateGateway(Long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException { VpcGatewayVO gatewayVO = _vpcGatewayDao.acquireInLockTable(gatewayId); if (gatewayVO == null || gatewayVO.getType() != VpcGateway.Type.Private) { throw new ConcurrentOperationException("Unable to lock gateway " + gatewayId); } try { _vpcGatewayDao.update(gatewayVO.getId(), gatewayVO); s_logger.debug("Marked gateway " + gatewayVO + " with state " + VpcGateway.State.Deleting); //don't allow to remove gateway when there are static routes associated with it long routeCount = _staticRouteDao.countRoutesByGateway(gatewayVO.getId()); if (routeCount > 0) { 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); //1) delete the gateway on the backend PrivateGateway gateway = getVpcPrivateGateway(gatewayId); if (getVpcElement().deletePrivateGateway(gateway)) { s_logger.debug("Private gateway " + gateway + " was applied succesfully on the backend"); } else { s_logger.warn("Private gateway " + gateway + " failed to apply on the backend"); return false; } //2) Delete private gateway from the DB return deletePrivateGatewayFromTheDB(gateway); } finally { if (gatewayVO != null) { _vpcGatewayDao.releaseFromLockTable(gatewayId); } } } @DB protected boolean deletePrivateGatewayFromTheDB(PrivateGateway gateway) { //check if there are ips allocted in the network long networkId = gateway.getNetworkId(); boolean deleteNetwork = true; List privateIps = _privateIpDao.listByNetworkId(networkId); if (privateIps.size() > 1 || !privateIps.get(0).getIpAddress().equalsIgnoreCase(gateway.getIp4Address())) { s_logger.debug("Not removing network id=" + gateway.getNetworkId() + " as it has private ip addresses for other gateways"); deleteNetwork = false; } Transaction txn = Transaction.currentTxn(); txn.start(); PrivateIpVO ip = _privateIpDao.findByIpAndVpcId(gateway.getVpcId(), gateway.getIp4Address()); if (ip != null) { _privateIpDao.remove(ip.getId()); s_logger.debug("Deleted private ip " + ip); } if (deleteNetwork) { User callerUser = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); Account owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner); _ntwkMgr.destroyNetwork(networkId, context); s_logger.debug("Deleted private network id=" + networkId); } _vpcGatewayDao.remove(gateway.getId()); s_logger.debug("Deleted private gateway " + gateway); txn.commit(); return true; } @Override public List listPrivateGateway(ListPrivateGatewaysCmd cmd) { String ipAddress = cmd.getIpAddress(); String vlan = cmd.getVlan(); Long vpcId = cmd.getVpcId(); Long id = cmd.getId(); Boolean isRecursive = cmd.isRecursive(); Boolean listAll = cmd.listAll(); Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); String state = cmd.getState(); 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, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); SearchBuilder sb = _vpcGatewayDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (vlan != null) { SearchBuilder ntwkSearch = _ntwkDao.createSearchBuilder(); ntwkSearch.and("vlan", ntwkSearch.entity().getBroadcastUri(), SearchCriteria.Op.EQ); sb.join("networkSearch", ntwkSearch, sb.entity().getNetworkId(), ntwkSearch.entity().getId(), JoinBuilder.JoinType.INNER); } SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (id != null) { sc.addAnd("id", Op.EQ, id); } if (ipAddress != null) { sc.addAnd("ip4Address", Op.EQ, ipAddress); } if (state != null) { sc.addAnd("state", Op.EQ, state); } if (vpcId != null) { sc.addAnd("vpcId", Op.EQ, vpcId); } if (vlan != null) { sc.setJoinParameters("networkSearch", "vlan", BroadcastDomainType.Vlan.toUri(vlan)); } List vos = _vpcGatewayDao.search(sc, searchFilter); List privateGtws = new ArrayList(vos.size()); for (VpcGateway vo : vos) { privateGtws.add(getPrivateGatewayProfile(vo)); } return privateGtws; } @Override public StaticRoute getStaticRoute(long routeId) { return _staticRouteDao.findById(routeId); } @Override public boolean applyStaticRoutes(long vpcId) throws ResourceUnavailableException { Account caller = UserContext.current().getCaller(); List routes = _staticRouteDao.listByVpcId(vpcId); return applyStaticRoutes(routes, caller, true); } protected boolean applyStaticRoutes(List routes, Account caller, boolean updateRoutesInDB) throws ResourceUnavailableException { boolean success = true; List staticRouteProfiles = new ArrayList(routes.size()); Map gatewayMap = new HashMap(); for (StaticRoute route : routes) { VpcGateway gateway = gatewayMap.get(route.getVpcGatewayId()); if (gateway == null) { gateway = _vpcGatewayDao.findById(route.getVpcGatewayId()); gatewayMap.put(gateway.getId(), gateway); } staticRouteProfiles.add(new StaticRouteProfile(route, gateway)); } if (!applyStaticRoutes(staticRouteProfiles)) { s_logger.warn("Routes are not completely applied"); return false; } else { if (updateRoutesInDB) { for (StaticRoute route : routes) { if (route.getState() == StaticRoute.State.Revoke) { _staticRouteDao.remove(route.getId()); s_logger.debug("Removed route " + route + " from the DB"); } else if (route.getState() == StaticRoute.State.Add) { StaticRouteVO ruleVO = _staticRouteDao.findById(route.getId()); ruleVO.setState(StaticRoute.State.Active); _staticRouteDao.update(ruleVO.getId(), ruleVO); s_logger.debug("Marked route " + route + " with state " + StaticRoute.State.Active); } } } } return success; } protected boolean applyStaticRoutes(List routes) throws ResourceUnavailableException{ if (routes.isEmpty()) { s_logger.debug("No static routes to apply"); return true; } Vpc vpc = getVpc(routes.get(0).getVpcId()); s_logger.debug("Applying static routes for vpc " + vpc); if (getVpcElement().applyStaticRoutes(vpc, routes)) { s_logger.debug("Applied static routes for vpc " + vpc); } else { s_logger.warn("Failed to apply static routes for vpc " + vpc); return false; } return true; } @Override @ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_DELETE, eventDescription = "deleting static route") public boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException { Account caller = UserContext.current().getCaller(); StaticRouteVO route = _staticRouteDao.findById(routeId); if (route == null) { throw new InvalidParameterValueException("Unable to find static route by id"); } _accountMgr.checkAccess(caller, null, false, route); markStaticRouteForRevoke(route, caller); return applyStaticRoutes(route.getVpcId()); } @DB protected boolean revokeStaticRoutesForVpc(long vpcId, Account caller) throws ResourceUnavailableException { //get all static routes for the vpc List routes = _staticRouteDao.listByVpcId(vpcId); s_logger.debug("Found " + routes.size() + " to revoke for the vpc " + vpcId); if (!routes.isEmpty()) { //mark all of them as revoke Transaction txn = Transaction.currentTxn(); txn.start(); for (StaticRouteVO route : routes) { markStaticRouteForRevoke(route, caller); } txn.commit(); return applyStaticRoutes(vpcId); } return true; } @Override @DB @ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_CREATE, eventDescription = "creating static route", create=true) public StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException { Account caller = UserContext.current().getCaller(); //parameters validation VpcGateway gateway = _vpcGatewayDao.findById(gatewayId); if (gateway == null) { throw new InvalidParameterValueException("Invalid gateway id is given"); } if (gateway.getState() != VpcGateway.State.Ready) { throw new InvalidParameterValueException("Gateway is not in the " + VpcGateway.State.Ready + " state: " + gateway.getState()); } Vpc vpc = getActiveVpc(gateway.getVpcId()); if (vpc == null) { throw new InvalidParameterValueException("Can't add static route to VPC that is being deleted"); } _accountMgr.checkAccess(caller, null, false, vpc); if (!NetUtils.isValidCIDR(cidr)){ throw new InvalidParameterValueException("Invalid format for cidr " + cidr); } //TODO - check cidr for the conflicts Transaction txn = Transaction.currentTxn(); txn.start(); StaticRouteVO newRoute = new StaticRouteVO(gateway.getId(), cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId()); s_logger.debug("Adding static route " + newRoute); newRoute = _staticRouteDao.persist(newRoute); detectRoutesConflict(newRoute); if (!_staticRouteDao.setStateToAdd(newRoute)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRoute); } UserContext.current().setEventDetails("Static route Id: " + newRoute.getId()); txn.commit(); return newRoute; } @Override public List listStaticRoutes(ListStaticRoutesCmd cmd) { Long id = cmd.getId(); Long gatewayId = cmd.getGatewayId(); Long vpcId = cmd.getVpcId(); Long domainId = cmd.getDomainId(); Boolean isRecursive = cmd.isRecursive(); Boolean listAll = cmd.listAll(); String accountName = cmd.getAccountName(); Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); _accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); Filter searchFilter = new Filter(StaticRouteVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal()); SearchBuilder sb = _staticRouteDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("vpcId", sb.entity().getVpcId(), SearchCriteria.Op.EQ); sb.and("vpcGatewayId", sb.entity().getVpcGatewayId(), SearchCriteria.Op.EQ); SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (id != null) { sc.addAnd("id", Op.EQ, id); } if (vpcId != null) { sc.addAnd("vpcId", Op.EQ, vpcId); } if (gatewayId != null) { sc.addAnd("vpcGatewayId", Op.EQ, gatewayId); } return _staticRouteDao.search(sc, searchFilter); } protected void detectRoutesConflict(StaticRoute newRoute) throws NetworkRuleConflictException { List routes = _staticRouteDao.listByGatewayIdAndNotRevoked(newRoute.getVpcGatewayId()); assert (routes.size() >= 1) : "For static routes, we now always first persist the route and then check for " + "network conflicts so we should at least have one rule at this point."; for (StaticRoute route : routes) { if (route.getId() == newRoute.getId()) { continue; // Skips my own route. } if (NetUtils.isNetworksOverlap(route.getCidr(), newRoute.getCidr())) { throw new NetworkRuleConflictException("New static route cidr conflicts with existing route " + route); } } } protected void markStaticRouteForRevoke(StaticRouteVO route, Account caller) { s_logger.debug("Revoking static route " + route); if (caller != null) { _accountMgr.checkAccess(caller, null, false, route); } if (route.getState() == StaticRoute.State.Staged) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found a static route that is still in stage state so just removing it: " + route); } _staticRouteDao.remove(route.getId()); } else if (route.getState() == StaticRoute.State.Add || route.getState() == StaticRoute.State.Active) { route.setState(StaticRoute.State.Revoke); _staticRouteDao.update(route.getId(), route); s_logger.debug("Marked static route " + route + " with state " + StaticRoute.State.Revoke); } } protected class VpcCleanupTask implements Runnable { @Override public void run() { try { GlobalLock lock = GlobalLock.getInternLock("VpcCleanup"); 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; } Transaction txn = null; try { txn = Transaction.open(Transaction.CLOUD_DB); // Cleanup inactive VPCs List inactiveVpcs = _vpcDao.listInactiveVpcs(); s_logger.info("Found " + inactiveVpcs.size() + " removed VPCs to cleanup"); for (VpcVO vpc : inactiveVpcs) { s_logger.debug("Cleaning up " + vpc); destroyVpc(vpc); } } catch (Exception e) { s_logger.error("Exception ", e); } finally { if (txn != null) { txn.close(); } lock.unlock(); } } catch (Exception e) { s_logger.error("Exception ", e); } } } @Override public VpcGateway getPrivateGatewayForVpc(long vpcId) { return _vpcGatewayDao.getPrivateGatewayForVpc(vpcId); } }