cloudstack/server/src/com/cloud/network/vpc/VpcManagerImpl.java

1314 lines
53 KiB
Java

// 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 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.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.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.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.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.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;
/**
* @author Alena Prokharchyk
*/
@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;
private VpcProvider vpcElement = null;
String _name;
@Override
@DB
public boolean configure(String name, Map<String, Object> 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<Service, Set<Provider>> svcProviderMap = new HashMap<Service, Set<Provider>>();
Set<Provider> provider = new HashSet<Provider>();
provider.add(Provider.VPCVirtualRouter);
for (Service svc : getSupportedServices()) {
svcProviderMap.put(svc, provider);
}
createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap,
true, State.Enabled);
}
txn.commit();
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
@Override
public List<? extends Network> 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<String> supportedServices) {
Map<Network.Service, Set<Network.Provider>> svcProviderMap = new HashMap<Network.Service, Set<Network.Provider>>();
Set<Network.Provider> defaultProviders = new HashSet<Network.Provider>();
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 (!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 firewall service to the list of VPC services");
svcProviderMap.put(Service.Firewall, defaultProviders);
}
svcProviderMap.put(Service.Gateway, defaultProviders);
return createVpcOffering(name, displayText, svcProviderMap, false, null);
}
@Override
@DB
public VpcOffering createVpcOffering(String name, String displayText, Map<Network.Service,
Set<Network.Provider>> 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<Provider> 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.findById(vpcId);
}
@Override
public Map<Service, Set<Provider>> getVpcOffSvcProvidersMap(long vpcOffId) {
Map<Service, Set<Provider>> serviceProviderMap = new HashMap<Service, Set<Provider>>();
List<VpcOfferingServiceMapVO> map = _vpcOffSvcMapDao.listByVpcOffId(vpcOffId);
for (VpcOfferingServiceMapVO instance : map) {
String service = instance.getService();
Set<Provider> providers;
providers = serviceProviderMap.get(service);
if (providers == null) {
providers = new HashSet<Provider>();
}
providers.add(Provider.getProvider(instance.getProvider()));
serviceProviderMap.put(Service.getService(service), providers);
}
return serviceProviderMap;
}
@Override
public List<? extends VpcOffering> listVpcOfferings(Long id, String name, String displayText, List<String> supportedServicesStr,
Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal) {
Filter searchFilter = new Filter(VpcOfferingVO.class, "created", false, startIndex, pageSizeVal);
SearchCriteria<VpcOfferingVO> sc = _vpcOffDao.createSearchCriteria();
if (keyword != null) {
SearchCriteria<VpcOfferingVO> 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<VpcOfferingVO> offerings = _vpcOffDao.search(sc, searchFilter);
// filter by supported services
boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !offerings.isEmpty());
if (listBySupportedServices) {
List<VpcOfferingVO> supportedOfferings = new ArrayList<VpcOfferingVO>();
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) {
//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 \"-\"");
}
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.Disabled + " as a part of vpc delete");
VpcVO vpcVO = _vpcDao.findById(vpc.getId());
vpcVO.setState(Vpc.State.Disabled);
_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;
}
if (_vpcDao.remove(vpc.getId())) {
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<? extends Vpc> listVpcs(Long id, String vpcName, String displayText, List<String> 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<Long> permittedAccounts = new ArrayList<Long>();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean,
ListProjectResourcesCriteria>(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<VpcVO> 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);
// now set the SC criteria...
SearchCriteria<VpcVO> sc = sb.create();
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
if (keyword != null) {
SearchCriteria<VpcVO> 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 (restartRequired != null) {
sc.addAnd("restartRequired", SearchCriteria.Op.EQ, restartRequired);
}
List<VpcVO> vpcs = _vpcDao.search(sc, searchFilter);
// filter by supported services
boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty());
if (listBySupportedServices) {
List<VpcVO> supportedVpcs = new ArrayList<VpcVO>();
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<Service> getSupportedServices() {
List<Service> services = new ArrayList<Service>();
services.add(Network.Service.Dhcp);
services.add(Network.Service.Dns);
services.add(Network.Service.UserData);
services.add(Network.Service.Firewall);
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) 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) {
throw new InvalidParameterValueException("Unable to find Enabled vpc by id " + vpcId);
}
//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) {
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) throws ConcurrentOperationException {
Vpc locked = _vpcDao.acquireInLockTable(vpc.getId());
if (locked == null) {
throw new ConcurrentOperationException("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<? extends Network> 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.isNetworksOverlap(ntwk.getCidr(), cidr)) {
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) 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");
}
//6) Only VPC VR can be a provider for the network offering
List<Provider> ntwkOffProviders = _ntwkMgr.getNtwkOffDistinctProviders(guestNtwkOff.getId());
for (Provider provider : ntwkOffProviders) {
if (provider != Provider.VPCVirtualRouter) {
throw new InvalidParameterValueException("Only VPCVirtualRouter provider is supported in VPC network;" +
" while network offering " + guestNtwkOff + " has " + provider.getName() + " enabled.");
}
}
//7) No redundant router support
if (guestNtwkOff.getRedundantRouter()) {
throw new InvalidParameterValueException("No redunant router support when network belnogs to VPC");
}
//8) Conserve mode should be off
if (guestNtwkOff.isConserveMode()) {
throw new InvalidParameterValueException("Only networks with conserve mode Off can belong to VPC");
}
} 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<? extends Vpc> getVpcsForAccount(long accountId) {
return _vpcDao.listByAccountId(accountId);
}
public boolean cleanupVpcResources(long vpcId, Account caller, long callerUserId) {
s_logger.debug("Cleaning up resources for vpc id=" + vpcId);
boolean success = true;
// release all ip addresses
List<IPAddressVO> 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");
}
}
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
VpcVO vpc = _vpcDao.findById(vpcId);
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find vpc offering " + vpcId);
}
_accountMgr.checkAccess(caller, null, false, vpc);
s_logger.debug("Restarting VPC " + vpc);
boolean restartRequired = false;
try {
s_logger.debug("Shuttign 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)) {
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);
vpc.setRestartRequired(restartRequired);
_vpcDao.update(vpc.getId(), vpc);
}
}
@Override
public List<DomainRouterVO> 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);
}
protected PrivateGateway getPrivateGatewayProfile(VpcGateway gateway) {
Network network = _ntwkMgr.getNetwork(gateway.getNetworkId());
String vlanTag = network.getBroadcastUri().getHost();
String netmask = NetUtils.getCidrNetmask(network.getCidr());
return new PrivateGatewayProfile(gateway, vlanTag, network.getGateway(),netmask, 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 = getVpc(vpcId);
if (vpc == null) {
throw new InvalidParameterValueException("Unable to find VPC by id given");
}
//allow only one private gateway per vpc
VpcGatewayVO gatewayVO = _vpcGatewayDao.getPrivateGateway(vpcId);
if (gatewayVO != null) {
throw new InvalidParameterValueException("Private ip address already exists for vpc " + vpc);
}
//Validate physical network
if (physicalNetworkId == null) {
List<? extends PhysicalNetwork> 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);
//2) create gateway entry
gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(),
privateNtwk.getId());
_vpcGatewayDao.persist(gatewayVO);
s_logger.debug("Created vpc gateway entry " + gatewayVO);
txn.commit();
return getVpcPrivateGateway(gatewayVO.getId());
}
@Override
public PrivateGateway applyVpcGateway(Long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException {
PrivateGateway gateway = getVpcPrivateGateway(gatewayId);
if (getVpcElement().createPrivateGateway(gateway)) {
s_logger.debug("Private gateway " + gateway + " was applied succesfully on the backend");
return gateway;
} else {
s_logger.warn("Private gateway " + gateway + " failed to apply on the backend");
return null;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_DELETE, eventDescription = "deleting private gateway")
public boolean deleteVpcPrivateGateway(Long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException {
VpcGatewayVO gatewayVO = _vpcGatewayDao.findById(gatewayId);
if (gatewayVO == null || gatewayVO.getType() != VpcGateway.Type.Private) {
throw new InvalidParameterValueException("Can't find private gateway by id specified");
}
//1) delete the gateaway 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
return deletePrivateGateway(gateway);
}
@DB
public boolean deletePrivateGateway(PrivateGateway gateway) {
//check if there are ips allocted in the network
long networkId = gateway.getNetworkId();
//don't allow to remove gateway when there are static routes associated with it
long routeCount = _staticRouteDao.countRoutesByGateway(gateway.getId());
if (routeCount > 0) {
throw new CloudRuntimeException("Can't delete private gateway " + gateway + " as it has " + routeCount +
" static routes applied. Remove the routes first");
}
boolean deleteNetwork = true;
List<PrivateIpVO> 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.findByIpAndSourceNetworkId(gateway.getNetworkId(), 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<PrivateGateway> listPrivateGateway(ListPrivateGatewaysCmd cmd) {
String ipAddress = cmd.getIpAddress();
String vlan = cmd.getVlan();
Long vpcId = cmd.getVpcId();
Filter searchFilter = new Filter(VpcGatewayVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal());
SearchBuilder<VpcGatewayVO> sb = _vpcGatewayDao.createSearchBuilder();
if (vlan != null) {
SearchBuilder<NetworkVO> 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<VpcGatewayVO> sc = sb.create();
if (ipAddress != null) {
sc.addAnd("ip4Address", Op.EQ, ipAddress);
}
if (vpcId != null) {
sc.addAnd("vpcId", Op.EQ, vpcId);
}
if (vlan != null) {
sc.setJoinParameters("networkSearch", "vlan", BroadcastDomainType.Vlan.toUri(vlan));
}
List<VpcGatewayVO> vos = _vpcGatewayDao.search(sc, searchFilter);
List<PrivateGateway> privateGtws = new ArrayList<PrivateGateway>(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<? extends StaticRoute> routes = _staticRouteDao.listByVpcId(vpcId);
return applyStaticRoutes(routes, caller);
}
protected boolean applyStaticRoutes(List<? extends StaticRoute> routes, Account caller) throws ResourceUnavailableException {
boolean success = true;
List<StaticRouteProfile> staticRouteProfiles = new ArrayList<StaticRouteProfile>(routes.size());
Map<Long, PrivateGateway> gatewayMap = new HashMap<Long, PrivateGateway>();
for (StaticRoute route : routes) {
PrivateGateway gateway = gatewayMap.get(route.getVpcGatewayId());
if (gateway == null) {
gateway = getVpcPrivateGateway(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 {
for (StaticRoute route : routes) {
if (route.getState() == StaticRoute.State.Revoke) {
_staticRouteDao.remove(route.getId());
} else if (route.getState() == StaticRoute.State.Add) {
StaticRouteVO ruleVO = _staticRouteDao.findById(route.getId());
ruleVO.setState(StaticRoute.State.Active);
_staticRouteDao.update(ruleVO.getId(), ruleVO);
}
}
}
return success;
}
protected boolean applyStaticRoutes(List<StaticRouteProfile> 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);
revokeStaticRoute(route, caller);
return applyStaticRoutes(route.getVpcId());
}
@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
PrivateGateway gateway = getVpcPrivateGateway(gatewayId);
if (gateway == null) {
throw new InvalidParameterValueException("Invalid gateway id is given");
}
Vpc vpc = getVpc(gateway.getVpcId());
_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<? extends StaticRoute> 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<Long> permittedAccounts = new ArrayList<Long>();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean,
ListProjectResourcesCriteria>(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<StaticRouteVO> 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<StaticRouteVO> 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, vpcId);
}
return _staticRouteDao.search(sc, searchFilter);
}
protected void detectRoutesConflict(StaticRoute newRoute) throws NetworkRuleConflictException {
List<? extends StaticRoute> 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 revokeStaticRoute(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);
}
}
}