mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
This introduces a rolling restart of VRs when networks are restarted with cleanup option for isolated and VPC networks. A make redundant option is shown for isolated networks now in UI. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2499 lines
116 KiB
Java
2499 lines
116 KiB
Java
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
package com.cloud.network.vpc;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import javax.annotation.PostConstruct;
|
|
import javax.inject.Inject;
|
|
import javax.naming.ConfigurationException;
|
|
|
|
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
|
import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
|
|
import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd;
|
|
import org.apache.cloudstack.context.CallContext;
|
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
|
import org.apache.commons.collections.CollectionUtils;
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.cloud.configuration.Config;
|
|
import com.cloud.configuration.Resource.ResourceType;
|
|
import com.cloud.dc.DataCenter;
|
|
import com.cloud.dc.DataCenterVO;
|
|
import com.cloud.dc.Vlan.VlanType;
|
|
import com.cloud.dc.VlanVO;
|
|
import com.cloud.dc.dao.DataCenterDao;
|
|
import com.cloud.dc.dao.VlanDao;
|
|
import com.cloud.deploy.DeployDestination;
|
|
import com.cloud.event.ActionEvent;
|
|
import com.cloud.event.EventTypes;
|
|
import com.cloud.exception.ConcurrentOperationException;
|
|
import com.cloud.exception.InsufficientAddressCapacityException;
|
|
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.hypervisor.Hypervisor.HypervisorType;
|
|
import com.cloud.network.IpAddress;
|
|
import com.cloud.network.IpAddressManager;
|
|
import com.cloud.network.Network;
|
|
import com.cloud.network.Network.Capability;
|
|
import com.cloud.network.Network.GuestType;
|
|
import com.cloud.network.Network.Provider;
|
|
import com.cloud.network.Network.Service;
|
|
import com.cloud.network.NetworkModel;
|
|
import com.cloud.network.NetworkService;
|
|
import com.cloud.network.Networks.BroadcastDomainType;
|
|
import com.cloud.network.Networks.TrafficType;
|
|
import com.cloud.network.PhysicalNetwork;
|
|
import com.cloud.network.addr.PublicIp;
|
|
import com.cloud.network.dao.FirewallRulesDao;
|
|
import com.cloud.network.dao.IPAddressDao;
|
|
import com.cloud.network.dao.IPAddressVO;
|
|
import com.cloud.network.dao.NetworkDao;
|
|
import com.cloud.network.dao.NetworkVO;
|
|
import com.cloud.network.element.NetworkElement;
|
|
import com.cloud.network.element.StaticNatServiceProvider;
|
|
import com.cloud.network.element.VpcProvider;
|
|
import com.cloud.network.router.VpcVirtualNetworkApplianceManager;
|
|
import com.cloud.network.vpc.VpcOffering.State;
|
|
import com.cloud.network.vpc.dao.NetworkACLDao;
|
|
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.network.vpc.dao.VpcServiceMapDao;
|
|
import com.cloud.network.vpn.Site2SiteVpnManager;
|
|
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.server.ResourceTag.ResourceObjectType;
|
|
import com.cloud.tags.ResourceTagVO;
|
|
import com.cloud.tags.dao.ResourceTagDao;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.user.AccountManager;
|
|
import com.cloud.user.ResourceLimitService;
|
|
import com.cloud.user.User;
|
|
import com.cloud.utils.NumbersUtil;
|
|
import com.cloud.utils.Pair;
|
|
import com.cloud.utils.StringUtils;
|
|
import com.cloud.utils.Ternary;
|
|
import com.cloud.utils.component.ManagerBase;
|
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
|
import com.cloud.utils.db.DB;
|
|
import com.cloud.utils.db.EntityManager;
|
|
import com.cloud.utils.db.Filter;
|
|
import com.cloud.utils.db.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.db.TransactionCallback;
|
|
import com.cloud.utils.db.TransactionCallbackNoReturn;
|
|
import com.cloud.utils.db.TransactionCallbackWithException;
|
|
import com.cloud.utils.db.TransactionStatus;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
import com.cloud.utils.exception.ExceptionUtil;
|
|
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;
|
|
|
|
public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvisioningService, VpcService {
|
|
private static final Logger s_logger = Logger.getLogger(VpcManagerImpl.class);
|
|
|
|
public static final String SERVICE = "service";
|
|
public static final String CAPABILITYTYPE = "capabilitytype";
|
|
public static final String CAPABILITYVALUE = "capabilityvalue";
|
|
public static final String TRUE_VALUE = "true";
|
|
public static final String FALSE_VALUE = "false";
|
|
|
|
@Inject
|
|
EntityManager _entityMgr;
|
|
@Inject
|
|
VpcOfferingDao _vpcOffDao;
|
|
@Inject
|
|
VpcOfferingServiceMapDao _vpcOffSvcMapDao;
|
|
@Inject
|
|
VpcDao _vpcDao;
|
|
@Inject
|
|
ConfigurationDao _configDao;
|
|
@Inject
|
|
AccountManager _accountMgr;
|
|
@Inject
|
|
NetworkDao _ntwkDao;
|
|
@Inject
|
|
NetworkOrchestrationService _ntwkMgr;
|
|
@Inject
|
|
NetworkModel _ntwkModel;
|
|
@Inject
|
|
NetworkService _ntwkSvc;
|
|
@Inject
|
|
IPAddressDao _ipAddressDao;
|
|
@Inject
|
|
VpcGatewayDao _vpcGatewayDao;
|
|
@Inject
|
|
PrivateIpDao _privateIpDao;
|
|
@Inject
|
|
StaticRouteDao _staticRouteDao;
|
|
@Inject
|
|
NetworkOfferingServiceMapDao _ntwkOffServiceDao;
|
|
@Inject
|
|
VpcOfferingServiceMapDao _vpcOffServiceDao;
|
|
@Inject
|
|
ResourceTagDao _resourceTagDao;
|
|
@Inject
|
|
FirewallRulesDao _firewallDao;
|
|
@Inject
|
|
Site2SiteVpnManager _s2sVpnMgr;
|
|
@Inject
|
|
VlanDao _vlanDao = null;
|
|
@Inject
|
|
ResourceLimitService _resourceLimitMgr;
|
|
@Inject
|
|
VpcServiceMapDao _vpcSrvcDao;
|
|
@Inject
|
|
DataCenterDao _dcDao;
|
|
@Inject
|
|
NetworkACLDao _networkAclDao;
|
|
@Inject
|
|
NetworkACLManager _networkAclMgr;
|
|
@Inject
|
|
IpAddressManager _ipAddrMgr;
|
|
@Inject
|
|
VpcVirtualNetworkApplianceManager _routerService;
|
|
@Inject
|
|
DomainRouterDao _routerDao;
|
|
|
|
@Inject
|
|
private VpcPrivateGatewayTransactionCallable vpcTxCallable;
|
|
|
|
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker"));
|
|
private List<VpcProvider> vpcElements = null;
|
|
private final List<Service> nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall);
|
|
private final List<Provider> supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler,
|
|
Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.NuageVsp, Provider.BigSwitchBcf, Provider.ConfigDrive);
|
|
|
|
int _cleanupInterval;
|
|
int _maxNetworks;
|
|
SearchBuilder<IPAddressVO> IpAddressSearch;
|
|
|
|
protected final List<HypervisorType> hTypes = new ArrayList<HypervisorType>();
|
|
|
|
@PostConstruct
|
|
protected void setupSupportedVpcHypervisorsList() {
|
|
hTypes.add(HypervisorType.XenServer);
|
|
hTypes.add(HypervisorType.VMware);
|
|
hTypes.add(HypervisorType.KVM);
|
|
hTypes.add(HypervisorType.Simulator);
|
|
hTypes.add(HypervisorType.LXC);
|
|
hTypes.add(HypervisorType.Hyperv);
|
|
hTypes.add(HypervisorType.Ovm3);
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
|
// configure default vpc offering
|
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
|
|
if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCOfferingName) == null) {
|
|
s_logger.debug("Creating default VPC offering " + VpcOffering.defaultVPCOfferingName);
|
|
|
|
final Map<Service, Set<Provider>> svcProviderMap = new HashMap<Service, Set<Provider>>();
|
|
final Set<Provider> defaultProviders = new HashSet<Provider>();
|
|
defaultProviders.add(Provider.VPCVirtualRouter);
|
|
for (final Service svc : getSupportedServices()) {
|
|
if (svc == Service.Lb) {
|
|
final Set<Provider> lbProviders = new HashSet<Provider>();
|
|
lbProviders.add(Provider.VPCVirtualRouter);
|
|
lbProviders.add(Provider.InternalLbVm);
|
|
svcProviderMap.put(svc, lbProviders);
|
|
} else {
|
|
svcProviderMap.put(svc, defaultProviders);
|
|
}
|
|
}
|
|
createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null, false, false, false);
|
|
}
|
|
|
|
// configure default vpc offering with Netscaler as LB Provider
|
|
if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCNSOfferingName) == null) {
|
|
s_logger.debug("Creating default VPC offering with Netscaler as LB Provider" + VpcOffering.defaultVPCNSOfferingName);
|
|
final Map<Service, Set<Provider>> svcProviderMap = new HashMap<Service, Set<Provider>>();
|
|
final Set<Provider> defaultProviders = new HashSet<Provider>();
|
|
defaultProviders.add(Provider.VPCVirtualRouter);
|
|
for (final Service svc : getSupportedServices()) {
|
|
if (svc == Service.Lb) {
|
|
final Set<Provider> lbProviders = new HashSet<Provider>();
|
|
lbProviders.add(Provider.Netscaler);
|
|
lbProviders.add(Provider.InternalLbVm);
|
|
svcProviderMap.put(svc, lbProviders);
|
|
} else {
|
|
svcProviderMap.put(svc, defaultProviders);
|
|
}
|
|
}
|
|
createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false, false, false);
|
|
|
|
}
|
|
|
|
if (_vpcOffDao.findByUniqueName(VpcOffering.redundantVPCOfferingName) == null) {
|
|
s_logger.debug("Creating Redundant VPC offering " + VpcOffering.redundantVPCOfferingName);
|
|
|
|
final Map<Service, Set<Provider>> svcProviderMap = new HashMap<Service, Set<Provider>>();
|
|
final Set<Provider> defaultProviders = new HashSet<Provider>();
|
|
defaultProviders.add(Provider.VPCVirtualRouter);
|
|
for (final Service svc : getSupportedServices()) {
|
|
if (svc == Service.Lb) {
|
|
final Set<Provider> lbProviders = new HashSet<Provider>();
|
|
lbProviders.add(Provider.VPCVirtualRouter);
|
|
lbProviders.add(Provider.InternalLbVm);
|
|
svcProviderMap.put(svc, lbProviders);
|
|
} else {
|
|
svcProviderMap.put(svc, defaultProviders);
|
|
}
|
|
}
|
|
createVpcOffering(VpcOffering.redundantVPCOfferingName, VpcOffering.redundantVPCOfferingName, svcProviderMap, true, State.Enabled, null, false, false, true);
|
|
}
|
|
}
|
|
});
|
|
|
|
final Map<String, String> configs = _configDao.getConfiguration(params);
|
|
final String value = configs.get(Config.VpcCleanupInterval.key());
|
|
_cleanupInterval = NumbersUtil.parseInt(value, 60 * 60); // 1 hour
|
|
|
|
final String maxNtwks = configs.get(Config.VpcMaxNetworks.key());
|
|
_maxNetworks = NumbersUtil.parseInt(maxNtwks, 3); // max=3 is default
|
|
|
|
IpAddressSearch = _ipAddressDao.createSearchBuilder();
|
|
IpAddressSearch.and("accountId", IpAddressSearch.entity().getAllocatedToAccountId(), Op.EQ);
|
|
IpAddressSearch.and("dataCenterId", IpAddressSearch.entity().getDataCenterId(), Op.EQ);
|
|
IpAddressSearch.and("vpcId", IpAddressSearch.entity().getVpcId(), Op.EQ);
|
|
IpAddressSearch.and("associatedWithNetworkId", IpAddressSearch.entity().getAssociatedWithNetworkId(), Op.EQ);
|
|
final SearchBuilder<VlanVO> virtualNetworkVlanSB = _vlanDao.createSearchBuilder();
|
|
virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), Op.EQ);
|
|
IpAddressSearch
|
|
.join("virtualNetworkVlanSB", virtualNetworkVlanSB, IpAddressSearch.entity().getVlanId(), virtualNetworkVlanSB.entity().getId(), JoinBuilder.JoinType.INNER);
|
|
IpAddressSearch.done();
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean start() {
|
|
_executor.scheduleAtFixedRate(new VpcCleanupTask(), _cleanupInterval, _cleanupInterval, TimeUnit.SECONDS);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean stop() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public List<? extends Network> getVpcNetworks(final long vpcId) {
|
|
return _ntwkDao.listByVpc(vpcId);
|
|
}
|
|
|
|
@Override
|
|
public VpcOffering getVpcOffering(final long vpcOffId) {
|
|
return _vpcOffDao.findById(vpcOffId);
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true)
|
|
public VpcOffering createVpcOffering(final String name, final String displayText, final List<String> supportedServices, final Map<String, List<String>> serviceProviders,
|
|
final Map serviceCapabilitystList, final Long serviceOfferingId) {
|
|
|
|
final Map<Network.Service, Set<Network.Provider>> svcProviderMap = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
final Set<Network.Provider> defaultProviders = new HashSet<Network.Provider>();
|
|
defaultProviders.add(Provider.VPCVirtualRouter);
|
|
// Just here for 4.1, replaced by commit 836ce6c1 in newer versions
|
|
final Set<Network.Provider> sdnProviders = new HashSet<Network.Provider>();
|
|
sdnProviders.add(Provider.NiciraNvp);
|
|
sdnProviders.add(Provider.JuniperContrailVpcRouter);
|
|
sdnProviders.add(Provider.NuageVsp);
|
|
|
|
boolean sourceNatSvc = false;
|
|
boolean firewallSvs = false;
|
|
// populate the services first
|
|
for (final String serviceName : supportedServices) {
|
|
// validate if the service is supported
|
|
final Service service = Network.Service.getService(serviceName);
|
|
if (service == null || nonSupportedServices.contains(service)) {
|
|
throw new InvalidParameterValueException("Service " + serviceName + " is not supported in VPC");
|
|
}
|
|
|
|
if (service == Service.Connectivity) {
|
|
s_logger.debug("Applying Connectivity workaround, setting provider to NiciraNvp");
|
|
svcProviderMap.put(service, sdnProviders);
|
|
} else {
|
|
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);
|
|
}
|
|
|
|
if (serviceProviders != null) {
|
|
for (final Entry<String, List<String>> serviceEntry : serviceProviders.entrySet()) {
|
|
final Network.Service service = Network.Service.getService(serviceEntry.getKey());
|
|
if (svcProviderMap.containsKey(service)) {
|
|
final Set<Provider> providers = new HashSet<Provider>();
|
|
for (final String prvNameStr : serviceEntry.getValue()) {
|
|
// check if provider is supported
|
|
final Network.Provider provider = Network.Provider.getProvider(prvNameStr);
|
|
if (provider == null) {
|
|
throw new InvalidParameterValueException("Invalid service provider: " + prvNameStr);
|
|
}
|
|
|
|
providers.add(provider);
|
|
}
|
|
svcProviderMap.put(service, providers);
|
|
} else {
|
|
throw new InvalidParameterValueException("Service " + serviceEntry.getKey() + " is not enabled for the network " + "offering, can't add a provider to it");
|
|
}
|
|
}
|
|
}
|
|
|
|
// add gateway provider (if sourceNat provider is enabled)
|
|
final Set<Provider> sourceNatServiceProviders = svcProviderMap.get(Service.SourceNat);
|
|
if (CollectionUtils.isNotEmpty(sourceNatServiceProviders)) {
|
|
svcProviderMap.put(Service.Gateway, sourceNatServiceProviders);
|
|
}
|
|
|
|
validateConnectivtyServiceCapabilities(svcProviderMap.get(Service.Connectivity), serviceCapabilitystList);
|
|
|
|
final boolean supportsDistributedRouter = isVpcOfferingSupportsDistributedRouter(serviceCapabilitystList);
|
|
final boolean offersRegionLevelVPC = isVpcOfferingForRegionLevelVpc(serviceCapabilitystList);
|
|
final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilitystList);
|
|
final VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC,
|
|
redundantRouter);
|
|
CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name);
|
|
|
|
return offering;
|
|
}
|
|
|
|
@DB
|
|
protected VpcOffering createVpcOffering(final String name, final String displayText, final Map<Network.Service, Set<Network.Provider>> svcProviderMap,
|
|
final boolean isDefault, final State state, final Long serviceOfferingId, final boolean supportsDistributedRouter, final boolean offersRegionLevelVPC,
|
|
final boolean redundantRouter) {
|
|
|
|
return Transaction.execute(new TransactionCallback<VpcOffering>() {
|
|
@Override
|
|
public VpcOffering doInTransaction(final TransactionStatus status) {
|
|
// create vpc offering object
|
|
VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC, redundantRouter);
|
|
|
|
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 (final Network.Service service : svcProviderMap.keySet()) {
|
|
final Set<Provider> providers = svcProviderMap.get(service);
|
|
if (providers != null && !providers.isEmpty()) {
|
|
for (final Network.Provider provider : providers) {
|
|
final 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());
|
|
}
|
|
}
|
|
}
|
|
|
|
return offering;
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void checkCapabilityPerServiceProvider(final Set<Provider> providers, final Capability capability, final Service service) {
|
|
// TODO Shouldn't it fail it there are no providers?
|
|
if (providers != null) {
|
|
for (final Provider provider : providers) {
|
|
final NetworkElement element = _ntwkModel.getElementImplementingProvider(provider.getName());
|
|
final Map<Service, Map<Capability, String>> capabilities = element.getCapabilities();
|
|
if (capabilities != null && !capabilities.isEmpty()) {
|
|
final Map<Capability, String> connectivityCapabilities = capabilities.get(service);
|
|
if (connectivityCapabilities == null || connectivityCapabilities != null && !connectivityCapabilities.keySet().contains(capability)) {
|
|
throw new InvalidParameterValueException(String.format("Provider %s does not support %s capability.", provider.getName(), capability.getName()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void validateConnectivtyServiceCapabilities(final Set<Provider> providers, final Map serviceCapabilitystList) {
|
|
if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) {
|
|
final Collection serviceCapabilityCollection = serviceCapabilitystList.values();
|
|
final Iterator iter = serviceCapabilityCollection.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
final HashMap<String, String> svcCapabilityMap = (HashMap<String, String>) iter.next();
|
|
Capability capability = null;
|
|
final String svc = svcCapabilityMap.get(SERVICE);
|
|
final String capabilityName = svcCapabilityMap.get(CAPABILITYTYPE);
|
|
final String capabilityValue = svcCapabilityMap.get(CAPABILITYVALUE);
|
|
if (capabilityName != null) {
|
|
capability = Capability.getCapability(capabilityName);
|
|
}
|
|
|
|
if (capability == null || capabilityValue == null) {
|
|
throw new InvalidParameterValueException("Invalid capability:" + capabilityName + " capability value:" + capabilityValue);
|
|
}
|
|
final Service usedService = Service.getService(svc);
|
|
|
|
checkCapabilityPerServiceProvider(providers, capability, usedService);
|
|
|
|
if (!capabilityValue.equalsIgnoreCase(TRUE_VALUE) && !capabilityValue.equalsIgnoreCase(FALSE_VALUE)) {
|
|
throw new InvalidParameterValueException("Invalid Capability value:" + capabilityValue + " specified.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean findCapabilityForService(final Map serviceCapabilitystList, final Capability capability, final Service service) {
|
|
boolean foundCapability = false;
|
|
if (serviceCapabilitystList != null && !serviceCapabilitystList.isEmpty()) {
|
|
final Iterator iter = serviceCapabilitystList.values().iterator();
|
|
while (iter.hasNext()) {
|
|
final HashMap<String, String> currentCapabilityMap = (HashMap<String, String>) iter.next();
|
|
final String currentCapabilityService = currentCapabilityMap.get(SERVICE);
|
|
final String currentCapabilityName = currentCapabilityMap.get(CAPABILITYTYPE);
|
|
final String currentCapabilityValue = currentCapabilityMap.get(CAPABILITYVALUE);
|
|
|
|
if (currentCapabilityName == null || currentCapabilityService == null || currentCapabilityValue == null) {
|
|
throw new InvalidParameterValueException(String.format("Invalid capability with name %s, value %s and service %s", currentCapabilityName,
|
|
currentCapabilityValue, currentCapabilityService));
|
|
}
|
|
|
|
if (currentCapabilityName.equalsIgnoreCase(capability.getName())) {
|
|
foundCapability = currentCapabilityValue.equalsIgnoreCase(TRUE_VALUE);
|
|
|
|
if (!currentCapabilityService.equalsIgnoreCase(service.getName())) {
|
|
throw new InvalidParameterValueException(String.format("Invalid Service: %s specified. Capability %s can be specified only for service %s",
|
|
currentCapabilityService, service.getName(), currentCapabilityName));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return foundCapability;
|
|
}
|
|
|
|
private boolean isVpcOfferingForRegionLevelVpc(final Map serviceCapabilitystList) {
|
|
return findCapabilityForService(serviceCapabilitystList, Capability.RegionLevelVpc, Service.Connectivity);
|
|
}
|
|
|
|
private boolean isVpcOfferingSupportsDistributedRouter(final Map serviceCapabilitystList) {
|
|
return findCapabilityForService(serviceCapabilitystList, Capability.DistributedRouter, Service.Connectivity);
|
|
}
|
|
|
|
private boolean isVpcOfferingRedundantRouter(final Map serviceCapabilitystList) {
|
|
return findCapabilityForService(serviceCapabilitystList, Capability.RedundantRouter, Service.SourceNat);
|
|
}
|
|
|
|
@Override
|
|
public Vpc getActiveVpc(final long vpcId) {
|
|
return _vpcDao.getActiveVpcById(vpcId);
|
|
}
|
|
|
|
@Override
|
|
public Map<Service, Set<Provider>> getVpcOffSvcProvidersMap(final long vpcOffId) {
|
|
final Map<Service, Set<Provider>> serviceProviderMap = new HashMap<Service, Set<Provider>>();
|
|
final List<VpcOfferingServiceMapVO> map = _vpcOffSvcMapDao.listByVpcOffId(vpcOffId);
|
|
|
|
for (final VpcOfferingServiceMapVO instance : map) {
|
|
final Service service = Service.getService(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, providers);
|
|
}
|
|
|
|
return serviceProviderMap;
|
|
}
|
|
|
|
@Override
|
|
public Pair<List<? extends VpcOffering>, Integer> listVpcOfferings(final Long id, final String name, final String displayText, final List<String> supportedServicesStr,
|
|
final Boolean isDefault, final String keyword, final String state, final Long startIndex, final Long pageSizeVal) {
|
|
final Filter searchFilter = new Filter(VpcOfferingVO.class, "created", false, null, null);
|
|
final SearchCriteria<VpcOfferingVO> sc = _vpcOffDao.createSearchCriteria();
|
|
|
|
if (keyword != null) {
|
|
final 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);
|
|
}
|
|
|
|
final List<VpcOfferingVO> offerings = _vpcOffDao.search(sc, searchFilter);
|
|
|
|
// filter by supported services
|
|
final boolean listBySupportedServices = supportedServicesStr != null && !supportedServicesStr.isEmpty() && !offerings.isEmpty();
|
|
|
|
if (listBySupportedServices) {
|
|
final List<VpcOfferingVO> supportedOfferings = new ArrayList<VpcOfferingVO>();
|
|
Service[] supportedServices = null;
|
|
|
|
if (listBySupportedServices) {
|
|
supportedServices = new Service[supportedServicesStr.size()];
|
|
int i = 0;
|
|
for (final String supportedServiceStr : supportedServicesStr) {
|
|
final Service service = Service.getService(supportedServiceStr);
|
|
if (service == null) {
|
|
throw new InvalidParameterValueException("Invalid service specified " + supportedServiceStr);
|
|
} else {
|
|
supportedServices[i] = service;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
for (final VpcOfferingVO offering : offerings) {
|
|
if (areServicesSupportedByVpcOffering(offering.getId(), supportedServices)) {
|
|
supportedOfferings.add(offering);
|
|
}
|
|
}
|
|
|
|
final List<? extends VpcOffering> wPagination = StringUtils.applyPagination(supportedOfferings, startIndex, pageSizeVal);
|
|
if (wPagination != null) {
|
|
final Pair<List<? extends VpcOffering>, Integer> listWPagination = new Pair<List<? extends VpcOffering>, Integer>(wPagination, supportedOfferings.size());
|
|
return listWPagination;
|
|
}
|
|
return new Pair<List<? extends VpcOffering>, Integer>(supportedOfferings, supportedOfferings.size());
|
|
} else {
|
|
final List<? extends VpcOffering> wPagination = StringUtils.applyPagination(offerings, startIndex, pageSizeVal);
|
|
if (wPagination != null) {
|
|
final Pair<List<? extends VpcOffering>, Integer> listWPagination = new Pair<List<? extends VpcOffering>, Integer>(wPagination, offerings.size());
|
|
return listWPagination;
|
|
}
|
|
return new Pair<List<? extends VpcOffering>, Integer>(offerings, offerings.size());
|
|
}
|
|
}
|
|
|
|
protected boolean areServicesSupportedByVpcOffering(final long vpcOffId, final Service... services) {
|
|
return _vpcOffSvcMapDao.areServicesSupportedByNetworkOffering(vpcOffId, services);
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_DELETE, eventDescription = "deleting vpc offering")
|
|
public boolean deleteVpcOffering(final long offId) {
|
|
CallContext.current().setEventDetails(" Id: " + offId);
|
|
|
|
// Verify vpc offering id
|
|
final 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)
|
|
final 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(final long vpcOffId, final String vpcOfferingName, final String displayText, final String state) {
|
|
CallContext.current().setEventDetails(" Id: " + vpcOffId);
|
|
|
|
// Verify input parameters
|
|
final VpcOfferingVO offeringToUpdate = _vpcOffDao.findById(vpcOffId);
|
|
if (offeringToUpdate == null) {
|
|
throw new InvalidParameterValueException("Unable to find vpc offering " + vpcOffId);
|
|
}
|
|
|
|
final VpcOfferingVO offering = _vpcOffDao.createForUpdate(vpcOffId);
|
|
|
|
if (vpcOfferingName != null) {
|
|
offering.setName(vpcOfferingName);
|
|
}
|
|
|
|
if (displayText != null) {
|
|
offering.setDisplayText(displayText);
|
|
}
|
|
|
|
if (state != null) {
|
|
boolean validState = false;
|
|
for (final 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(final long zoneId, final long vpcOffId, final long vpcOwnerId, final String vpcName, final String displayText, final String cidr, String networkDomain,
|
|
final Boolean displayVpc) throws ResourceAllocationException {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
final Account owner = _accountMgr.getAccount(vpcOwnerId);
|
|
|
|
// Verify that caller can perform actions in behalf of vpc owner
|
|
_accountMgr.checkAccess(caller, null, false, owner);
|
|
|
|
// check resource limit
|
|
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.vpc);
|
|
|
|
// Validate vpc offering
|
|
final VpcOfferingVO vpcOff = _vpcOffDao.findById(vpcOffId);
|
|
if (vpcOff == null || vpcOff.getState() != State.Enabled) {
|
|
final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find vpc offering in " + State.Enabled + " state by specified id");
|
|
if (vpcOff == null) {
|
|
ex.addProxyObject(String.valueOf(vpcOffId), "vpcOfferingId");
|
|
} else {
|
|
ex.addProxyObject(vpcOff.getUuid(), "vpcOfferingId");
|
|
}
|
|
throw ex;
|
|
}
|
|
|
|
final boolean isRegionLevelVpcOff = vpcOff.offersRegionLevelVPC();
|
|
if (isRegionLevelVpcOff && networkDomain == null) {
|
|
throw new InvalidParameterValueException("Network domain must be specified for region level VPC");
|
|
}
|
|
|
|
// Validate zone
|
|
final DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
|
|
if (zone == null) {
|
|
throw new InvalidParameterValueException("Can't find zone by id specified");
|
|
}
|
|
|
|
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
|
|
// See DataCenterVO.java
|
|
final PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
|
|
ex.addProxyObject(zone.getUuid(), "zoneId");
|
|
throw ex;
|
|
}
|
|
|
|
if (networkDomain == null) {
|
|
// 1) Get networkDomain from the corresponding account
|
|
networkDomain = _ntwkModel.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()) + NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId);
|
|
}
|
|
}
|
|
|
|
final boolean useDistributedRouter = vpcOff.supportsDistributedRouter();
|
|
final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff,
|
|
vpcOff.getRedundantRouter());
|
|
|
|
return createVpc(displayVpc, vpc);
|
|
}
|
|
|
|
@DB
|
|
protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) {
|
|
final String cidr = vpc.getCidr();
|
|
// Validate CIDR
|
|
if (!NetUtils.isValidIp4Cidr(cidr)) {
|
|
throw new InvalidParameterValueException("Invalid CIDR specified " + cidr);
|
|
}
|
|
|
|
// cidr has to be RFC 1918 complient
|
|
if (!NetUtils.validateGuestCidr(cidr)) {
|
|
throw new InvalidParameterValueException("Guest Cidr " + cidr + " is not RFC1918 compliant");
|
|
}
|
|
|
|
// validate network domain
|
|
if (!NetUtils.verifyDomainName(vpc.getNetworkDomain())) {
|
|
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 \"-\"");
|
|
}
|
|
|
|
return Transaction.execute(new TransactionCallback<VpcVO>() {
|
|
@Override
|
|
public VpcVO doInTransaction(final TransactionStatus status) {
|
|
if (displayVpc != null) {
|
|
vpc.setDisplay(displayVpc);
|
|
}
|
|
|
|
final VpcVO persistedVpc = _vpcDao.persist(vpc, finalizeServicesAndProvidersForVpc(vpc.getZoneId(), vpc.getVpcOfferingId()));
|
|
_resourceLimitMgr.incrementResourceCount(vpc.getAccountId(), ResourceType.vpc);
|
|
s_logger.debug("Created VPC " + persistedVpc);
|
|
|
|
return persistedVpc;
|
|
}
|
|
});
|
|
}
|
|
|
|
private Map<String, List<String>> finalizeServicesAndProvidersForVpc(final long zoneId, final long offeringId) {
|
|
final Map<String, List<String>> svcProviders = new HashMap<>();
|
|
final List<VpcOfferingServiceMapVO> servicesMap = _vpcOffSvcMapDao.listByVpcOffId(offeringId);
|
|
|
|
for (final VpcOfferingServiceMapVO serviceMap : servicesMap) {
|
|
final String service = serviceMap.getService();
|
|
String provider = serviceMap.getProvider();
|
|
|
|
if (provider == null) {
|
|
// Default to VPCVirtualRouter
|
|
provider = Provider.VPCVirtualRouter.getName();
|
|
}
|
|
|
|
if (!_ntwkModel.isProviderEnabledInZone(zoneId, provider)) {
|
|
throw new InvalidParameterValueException("Provider " + provider + " should be enabled in at least one physical network of the zone specified");
|
|
}
|
|
|
|
List<String> providers = null;
|
|
if (svcProviders.get(service) == null) {
|
|
providers = new ArrayList<String>();
|
|
} else {
|
|
providers = svcProviders.get(service);
|
|
}
|
|
providers.add(provider);
|
|
svcProviders.put(service, providers);
|
|
}
|
|
|
|
return svcProviders;
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VPC_DELETE, eventDescription = "deleting VPC")
|
|
public boolean deleteVpc(final long vpcId) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
CallContext.current().setEventDetails(" Id: " + vpcId);
|
|
final CallContext ctx = CallContext.current();
|
|
|
|
// Verify vpc id
|
|
final Vpc vpc = _vpcDao.findById(vpcId);
|
|
if (vpc == null) {
|
|
throw new InvalidParameterValueException("unable to find VPC id=" + vpcId);
|
|
}
|
|
|
|
// verify permissions
|
|
_accountMgr.checkAccess(ctx.getCallingAccount(), null, false, vpc);
|
|
_resourceTagDao.removeByIdAndType(vpcId, ResourceObjectType.Vpc);
|
|
|
|
return destroyVpc(vpc, ctx.getCallingAccount(), ctx.getCallingUserId());
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public boolean destroyVpc(final Vpc vpc, final Account caller, final Long callerUserId) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
s_logger.debug("Destroying vpc " + vpc);
|
|
|
|
// don't allow to delete vpc if it's in use by existing non system
|
|
// networks (system networks are networks of a private gateway of the
|
|
// VPC,
|
|
// and they will get removed as a part of VPC cleanup
|
|
final int networksCount = _ntwkDao.getNonSystemNetworkCountByVpcId(vpc.getId());
|
|
if (networksCount > 0) {
|
|
throw new InvalidParameterValueException("Can't delete VPC " + vpc + " as its used by " + networksCount + " networks");
|
|
}
|
|
|
|
// mark VPC as inactive
|
|
if (vpc.getState() != Vpc.State.Inactive) {
|
|
s_logger.debug("Updating VPC " + vpc + " with state " + Vpc.State.Inactive + " as a part of vpc delete");
|
|
final VpcVO vpcVO = _vpcDao.findById(vpc.getId());
|
|
vpcVO.setState(Vpc.State.Inactive);
|
|
|
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
_vpcDao.update(vpc.getId(), vpcVO);
|
|
|
|
// decrement resource count
|
|
_resourceLimitMgr.decrementResourceCount(vpc.getAccountId(), ResourceType.vpc);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 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(), caller, callerUserId)) {
|
|
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 destroyed succesfully");
|
|
return true;
|
|
} else {
|
|
s_logger.warn("Vpc " + vpc + " failed to destroy");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription = "updating vpc")
|
|
public Vpc updateVpc(final long vpcId, final String vpcName, final String displayText, final String customId, final Boolean displayVpc) {
|
|
CallContext.current().setEventDetails(" Id: " + vpcId);
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
|
|
// Verify input parameters
|
|
final VpcVO vpcToUpdate = _vpcDao.findById(vpcId);
|
|
if (vpcToUpdate == null) {
|
|
throw new InvalidParameterValueException("Unable to find vpc by id " + vpcId);
|
|
}
|
|
|
|
_accountMgr.checkAccess(caller, null, false, vpcToUpdate);
|
|
|
|
final VpcVO vpc = _vpcDao.createForUpdate(vpcId);
|
|
|
|
if (vpcName != null) {
|
|
vpc.setName(vpcName);
|
|
}
|
|
|
|
if (displayText != null) {
|
|
vpc.setDisplayText(displayText);
|
|
}
|
|
|
|
if (customId != null) {
|
|
vpc.setUuid(customId);
|
|
}
|
|
|
|
if (displayVpc != null) {
|
|
vpc.setDisplay(displayVpc);
|
|
}
|
|
|
|
if (_vpcDao.update(vpcId, vpc)) {
|
|
s_logger.debug("Updated VPC id=" + vpcId);
|
|
return _vpcDao.findById(vpcId);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Pair<List<? extends Vpc>, Integer> listVpcs(final Long id, final String vpcName, final String displayText, final List<String> supportedServicesStr, final String cidr,
|
|
final Long vpcOffId, final String state, final String accountName, Long domainId, final String keyword, final Long startIndex, final Long pageSizeVal,
|
|
final Long zoneId, Boolean isRecursive, final Boolean listAll, final Boolean restartRequired, final Map<String, String> tags, final Long projectId,
|
|
final Boolean display) {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
final List<Long> permittedAccounts = new ArrayList<Long>();
|
|
final Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive,
|
|
null);
|
|
_accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
|
domainId = domainIdRecursiveListProject.first();
|
|
isRecursive = domainIdRecursiveListProject.second();
|
|
final ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
|
final Filter searchFilter = new Filter(VpcVO.class, "created", false, null, null);
|
|
|
|
final 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);
|
|
sb.and("cidr", sb.entity().getCidr(), SearchCriteria.Op.EQ);
|
|
sb.and("display", sb.entity().isDisplay(), SearchCriteria.Op.EQ);
|
|
|
|
if (tags != null && !tags.isEmpty()) {
|
|
final SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
|
|
for (int count = 0; count < tags.size(); count++) {
|
|
tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
|
|
tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
|
|
tagSearch.cp();
|
|
}
|
|
tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
|
sb.groupBy(sb.entity().getId());
|
|
sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
// now set the SC criteria...
|
|
final SearchCriteria<VpcVO> sc = sb.create();
|
|
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
|
|
|
if (keyword != null) {
|
|
final 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 (tags != null && !tags.isEmpty()) {
|
|
int count = 0;
|
|
sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.Vpc.toString());
|
|
for (final Map.Entry<String, String> entry : tags.entrySet()) {
|
|
sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), entry.getKey());
|
|
sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), entry.getValue());
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (display != null) {
|
|
sc.setParameters("display", display);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
final List<VpcVO> vpcs = _vpcDao.search(sc, searchFilter);
|
|
|
|
// filter by supported services
|
|
final boolean listBySupportedServices = supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty();
|
|
|
|
if (listBySupportedServices) {
|
|
final List<VpcVO> supportedVpcs = new ArrayList<VpcVO>();
|
|
Service[] supportedServices = null;
|
|
|
|
if (listBySupportedServices) {
|
|
supportedServices = new Service[supportedServicesStr.size()];
|
|
int i = 0;
|
|
for (final String supportedServiceStr : supportedServicesStr) {
|
|
final Service service = Service.getService(supportedServiceStr);
|
|
if (service == null) {
|
|
throw new InvalidParameterValueException("Invalid service specified " + supportedServiceStr);
|
|
} else {
|
|
supportedServices[i] = service;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
for (final VpcVO vpc : vpcs) {
|
|
if (areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), supportedServices)) {
|
|
supportedVpcs.add(vpc);
|
|
}
|
|
}
|
|
|
|
final List<? extends Vpc> wPagination = StringUtils.applyPagination(supportedVpcs, startIndex, pageSizeVal);
|
|
if (wPagination != null) {
|
|
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, supportedVpcs.size());
|
|
return listWPagination;
|
|
}
|
|
return new Pair<List<? extends Vpc>, Integer>(supportedVpcs, supportedVpcs.size());
|
|
} else {
|
|
final List<? extends Vpc> wPagination = StringUtils.applyPagination(vpcs, startIndex, pageSizeVal);
|
|
if (wPagination != null) {
|
|
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, vpcs.size());
|
|
return listWPagination;
|
|
}
|
|
return new Pair<List<? extends Vpc>, Integer>(vpcs, vpcs.size());
|
|
}
|
|
}
|
|
|
|
protected List<Service> getSupportedServices() {
|
|
final 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.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(final long vpcId, final boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
|
|
final CallContext ctx = CallContext.current();
|
|
final Account caller = ctx.getCallingAccount();
|
|
final User callerUser = _accountMgr.getActiveUser(ctx.getCallingUserId());
|
|
|
|
// check if vpc exists
|
|
final Vpc vpc = getActiveVpc(vpcId);
|
|
if (vpc == null) {
|
|
final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified");
|
|
ex.addProxyObject(String.valueOf(vpcId), "VPC");
|
|
throw ex;
|
|
}
|
|
|
|
// permission check
|
|
_accountMgr.checkAccess(caller, null, false, vpc);
|
|
|
|
final DataCenter dc = _entityMgr.findById(DataCenter.class, vpc.getZoneId());
|
|
|
|
final DeployDestination dest = new DeployDestination(dc, null, null, null);
|
|
final 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 (final 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, caller, callerUser.getId())) {
|
|
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(final Vpc vpc, final DeployDestination dest, final ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException,
|
|
InsufficientCapacityException {
|
|
// deploy provider
|
|
boolean success = true;
|
|
final List<Provider> providersToImplement = getVpcProviders(vpc.getId());
|
|
for (final VpcProvider element : getVpcElements()) {
|
|
if (providersToImplement.contains(element.getProvider())) {
|
|
if (element.implementVpc(vpc, dest, context)) {
|
|
s_logger.debug("Vpc " + vpc + " has started successfully");
|
|
} else {
|
|
s_logger.warn("Vpc " + vpc + " failed to start");
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
@Override
|
|
public boolean shutdownVpc(final long vpcId) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
final CallContext ctx = CallContext.current();
|
|
final Account caller = ctx.getCallingAccount();
|
|
|
|
// check if vpc exists
|
|
final Vpc vpc = _vpcDao.findById(vpcId);
|
|
if (vpc == null) {
|
|
throw new InvalidParameterValueException("Unable to find vpc by id " + vpcId);
|
|
}
|
|
|
|
// permission check
|
|
_accountMgr.checkAccess(caller, null, false, vpc);
|
|
|
|
// shutdown provider
|
|
s_logger.debug("Shutting down vpc " + vpc);
|
|
// TODO - shutdown all vpc resources here (ACLs, gateways, etc)
|
|
|
|
boolean success = true;
|
|
final List<Provider> providersToImplement = getVpcProviders(vpc.getId());
|
|
final ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(ctx.getCallingUserId()), caller);
|
|
for (final VpcProvider element : getVpcElements()) {
|
|
if (providersToImplement.contains(element.getProvider())) {
|
|
if (element.shutdownVpc(vpc, context)) {
|
|
s_logger.debug("Vpc " + vpc + " has been shutdown succesfully");
|
|
} else {
|
|
s_logger.warn("Vpc " + vpc + " failed to shutdown");
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public void validateNtwkOffForNtwkInVpc(final Long networkId, final long newNtwkOffId, final String newCidr, final String newNetworkDomain, final Vpc vpc,
|
|
final String gateway, final Account networkOwner, final Long aclId) {
|
|
|
|
final NetworkOffering guestNtwkOff = _entityMgr.findById(NetworkOffering.class, newNtwkOffId);
|
|
|
|
if (guestNtwkOff == null) {
|
|
throw new InvalidParameterValueException("Can't find network offering by id specified");
|
|
}
|
|
|
|
if (networkId == null) {
|
|
// 1) Validate attributes that has to be passed in when create new
|
|
// guest network
|
|
validateNewVpcGuestNetwork(newCidr, gateway, networkOwner, vpc, newNetworkDomain);
|
|
}
|
|
|
|
// 2) validate network offering attributes
|
|
final List<Service> svcs = _ntwkModel.listNetworkOfferingServices(guestNtwkOff.getId());
|
|
validateNtwkOffForVpc(guestNtwkOff, svcs);
|
|
|
|
// 3) Check services/providers against VPC providers
|
|
final List<NetworkOfferingServiceMapVO> networkProviders = _ntwkOffServiceDao.listByNetworkOfferingId(guestNtwkOff.getId());
|
|
|
|
for (final NetworkOfferingServiceMapVO nSvcVO : networkProviders) {
|
|
final String pr = nSvcVO.getProvider();
|
|
final 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);
|
|
}
|
|
}
|
|
|
|
// 4) Only one network in the VPC can support public LB inside the VPC.
|
|
// Internal LB can be supported on multiple VPC tiers
|
|
if (_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.Lb) && guestNtwkOff.getPublicLb()) {
|
|
final List<? extends Network> networks = getVpcNetworks(vpc.getId());
|
|
for (final Network network : networks) {
|
|
if (networkId != null && network.getId() == networkId.longValue()) {
|
|
// skip my own network
|
|
continue;
|
|
} else {
|
|
final NetworkOffering otherOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
|
|
// throw only if networks have different offerings with
|
|
// public lb support
|
|
if (_ntwkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb) && otherOff.getPublicLb() && guestNtwkOff.getId() != otherOff.getId()) {
|
|
throw new InvalidParameterValueException("Public LB service is already supported " + "by network " + network + " in VPC " + vpc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5) When aclId is provided, verify that ACLProvider is supported by
|
|
// network offering
|
|
if (aclId != null && !_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL)) {
|
|
throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service");
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public void validateNtwkOffForVpc(final NetworkOffering guestNtwkOff, final List<Service> supportedSvcs) {
|
|
// 1) in current release, only vpc provider is supported by Vpc offering
|
|
final List<Provider> providers = _ntwkModel.getNtwkOffDistinctProviders(guestNtwkOff.getId());
|
|
for (final Provider provider : providers) {
|
|
if (!supportedProviders.contains(provider)) {
|
|
throw new InvalidParameterValueException("Provider of type " + provider.getName() + " is not supported for network offerings that can be used in VPC");
|
|
}
|
|
}
|
|
|
|
// 2) Only Isolated networks with Source nat service enabled can be
|
|
// added to vpc
|
|
if (!(guestNtwkOff.getGuestType() == GuestType.Isolated && supportedSvcs.contains(Service.SourceNat))) {
|
|
|
|
throw new InvalidParameterValueException("Only network offerings of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName()
|
|
+ " are valid for vpc ");
|
|
}
|
|
|
|
// 3) No redundant router support
|
|
/*
|
|
* TODO This should have never been hardcoded like this in the first
|
|
* place 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) If Netscaler is LB provider make sure it is in dedicated mode
|
|
if (providers.contains(Provider.Netscaler) && !guestNtwkOff.getDedicatedLB()) {
|
|
throw new InvalidParameterValueException("Netscaler only with Dedicated LB can belong to VPC");
|
|
}
|
|
return;
|
|
}
|
|
|
|
@DB
|
|
protected void validateNewVpcGuestNetwork(final String cidr, final String gateway, final Account networkOwner, final Vpc vpc, final String networkDomain) {
|
|
|
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
final Vpc locked = _vpcDao.acquireInLockTable(vpc.getId());
|
|
if (locked == null) {
|
|
throw new CloudRuntimeException("Unable to acquire lock on " + vpc);
|
|
}
|
|
|
|
try {
|
|
// check number of active networks in vpc
|
|
if (_ntwkDao.countVpcNetworks(vpc.getId()) >= _maxNetworks) {
|
|
throw new CloudRuntimeException("Number of networks per VPC can't extend " + _maxNetworks + "; increase it using global config " + Config.VpcMaxNetworks);
|
|
}
|
|
|
|
// 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
|
|
final List<? extends Network> ntwks = _ntwkDao.listByVpc(vpc.getId());
|
|
for (final Network ntwk : ntwks) {
|
|
assert cidr != null : "Why the network cidr is null when it belongs to vpc?";
|
|
|
|
if (NetUtils.isNetworkAWithinNetworkB(ntwk.getCidr(), cidr) || NetUtils.isNetworkAWithinNetworkB(cidr, 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());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public List<VpcProvider> getVpcElements() {
|
|
if (vpcElements == null) {
|
|
vpcElements = new ArrayList<VpcProvider>();
|
|
vpcElements.add((VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.VPCVirtualRouter.getName()));
|
|
vpcElements.add((VpcProvider) _ntwkModel.getElementImplementingProvider(Provider.JuniperContrailVpcRouter.getName()));
|
|
}
|
|
|
|
if (vpcElements == null) {
|
|
throw new CloudRuntimeException("Failed to initialize vpc elements");
|
|
}
|
|
|
|
return vpcElements;
|
|
}
|
|
|
|
@Override
|
|
public List<? extends Vpc> getVpcsForAccount(final long accountId) {
|
|
final List<Vpc> vpcs = new ArrayList<Vpc>();
|
|
vpcs.addAll(_vpcDao.listByAccountId(accountId));
|
|
return vpcs;
|
|
}
|
|
|
|
public boolean cleanupVpcResources(final long vpcId, final Account caller, final long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException {
|
|
s_logger.debug("Cleaning up resources for vpc id=" + vpcId);
|
|
boolean success = true;
|
|
|
|
// 1) Remove VPN connections and VPN gateway
|
|
s_logger.debug("Cleaning up existed site to site VPN connections");
|
|
_s2sVpnMgr.cleanupVpnConnectionByVpc(vpcId);
|
|
s_logger.debug("Cleaning up existed site to site VPN gateways");
|
|
_s2sVpnMgr.cleanupVpnGatewayByVpc(vpcId);
|
|
|
|
// 2) release all ip addresses
|
|
final List<IPAddressVO> ipsToRelease = _ipAddressDao.listByAssociatedVpc(vpcId, null);
|
|
s_logger.debug("Releasing ips for vpc id=" + vpcId + " as a part of vpc cleanup");
|
|
for (final IPAddressVO ipToRelease : ipsToRelease) {
|
|
if (ipToRelease.isPortable()) {
|
|
// portable IP address are associated with owner, until
|
|
// explicitly requested to be disassociated.
|
|
// so as part of VPC clean up just break IP association with VPC
|
|
ipToRelease.setVpcId(null);
|
|
ipToRelease.setAssociatedWithNetworkId(null);
|
|
_ipAddressDao.update(ipToRelease.getId(), ipToRelease);
|
|
s_logger.debug("Portable IP address " + ipToRelease + " is no longer associated with any VPC");
|
|
} else {
|
|
success = success && _ipAddrMgr.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
|
|
}
|
|
|
|
// 3) 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;
|
|
}
|
|
|
|
// 4) Delete private gateways
|
|
final List<PrivateGateway> gateways = getVpcPrivateGateways(vpcId);
|
|
if (gateways != null) {
|
|
for (final PrivateGateway gateway : gateways) {
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//5) Delete ACLs
|
|
final SearchBuilder<NetworkACLVO> searchBuilder = _networkAclDao.createSearchBuilder();
|
|
|
|
searchBuilder.and("vpcId", searchBuilder.entity().getVpcId(), Op.IN);
|
|
final SearchCriteria<NetworkACLVO> searchCriteria = searchBuilder.create();
|
|
searchCriteria.setParameters("vpcId", vpcId, 0);
|
|
|
|
final Filter filter = new Filter(NetworkACLVO.class, "id", false, null, null);
|
|
final Pair<List<NetworkACLVO>, Integer> aclsCountPair = _networkAclDao.searchAndCount(searchCriteria, filter);
|
|
|
|
final List<NetworkACLVO> acls = aclsCountPair.first();
|
|
for (final NetworkACLVO networkAcl : acls) {
|
|
if (networkAcl.getId() != NetworkACL.DEFAULT_ALLOW && networkAcl.getId() != NetworkACL.DEFAULT_DENY) {
|
|
_networkAclMgr.deleteNetworkACL(networkAcl);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_VPC_RESTART, eventDescription = "restarting vpc")
|
|
public boolean restartVpc(final long vpcId, final boolean cleanUp, final boolean makeRedundant) throws ConcurrentOperationException, ResourceUnavailableException,
|
|
InsufficientCapacityException {
|
|
|
|
final Account callerAccount = CallContext.current().getCallingAccount();
|
|
final User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
|
|
final ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount);
|
|
|
|
// Verify input parameters
|
|
Vpc vpc = getActiveVpc(vpcId);
|
|
if (vpc == null) {
|
|
final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified");
|
|
ex.addProxyObject(String.valueOf(vpcId), "VPC");
|
|
throw ex;
|
|
}
|
|
|
|
_accountMgr.checkAccess(callerAccount, null, false, vpc);
|
|
|
|
s_logger.debug("Restarting VPC " + vpc);
|
|
boolean restartRequired = false;
|
|
try {
|
|
boolean forceCleanup = cleanUp;
|
|
if (!vpc.isRedundant() && makeRedundant) {
|
|
final VpcOfferingVO redundantOffering = _vpcOffDao.findByUniqueName(VpcOffering.redundantVPCOfferingName);
|
|
|
|
final VpcVO entity = _vpcDao.findById(vpcId);
|
|
entity.setRedundant(true);
|
|
entity.setVpcOfferingId(redundantOffering.getId());
|
|
|
|
// Change the VPC in order to get it updated after the end of
|
|
// the restart procedure.
|
|
if (_vpcDao.update(vpc.getId(), entity)) {
|
|
vpc = entity;
|
|
}
|
|
|
|
// If the offering and redundant column are changing, force the
|
|
// clean up.
|
|
forceCleanup = true;
|
|
}
|
|
|
|
if (forceCleanup) {
|
|
if (!rollingRestartVpc(vpc, context)) {
|
|
s_logger.warn("Failed to execute a rolling restart as a part of VPC " + vpc + " restart process");
|
|
restartRequired = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
s_logger.debug("Starting VPC " + vpc + " as a part of VPC restart process without cleanup");
|
|
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);
|
|
final VpcVO vo = _vpcDao.findById(vpcId);
|
|
vo.setRestartRequired(restartRequired);
|
|
_vpcDao.update(vpc.getId(), vo);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<PrivateGateway> getVpcPrivateGateways(final long vpcId) {
|
|
final List<VpcGatewayVO> gateways = _vpcGatewayDao.listByVpcIdAndType(vpcId, VpcGateway.Type.Private);
|
|
|
|
if (gateways != null) {
|
|
final List<PrivateGateway> pvtGateway = new ArrayList<PrivateGateway>();
|
|
for (final VpcGatewayVO gateway : gateways) {
|
|
pvtGateway.add(getPrivateGatewayProfile(gateway));
|
|
}
|
|
return pvtGateway;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PrivateGateway getVpcPrivateGateway(final long id) {
|
|
final VpcGateway gateway = _vpcGatewayDao.findById(id);
|
|
|
|
if (gateway == null || gateway.getType() != VpcGateway.Type.Private) {
|
|
return null;
|
|
}
|
|
return getPrivateGatewayProfile(gateway);
|
|
}
|
|
|
|
protected PrivateGateway getPrivateGatewayProfile(final VpcGateway gateway) {
|
|
final Network network = _ntwkModel.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(final long vpcId, Long physicalNetworkId, final String broadcastUri, final String ipAddress, final String gateway,
|
|
final String netmask, final long gatewayOwnerId, final Long networkOfferingId, final Boolean isSourceNat, final Long aclId) throws ResourceAllocationException,
|
|
ConcurrentOperationException, InsufficientCapacityException {
|
|
|
|
// Validate parameters
|
|
final Vpc vpc = getActiveVpc(vpcId);
|
|
if (vpc == null) {
|
|
final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC by id specified");
|
|
ex.addProxyObject(String.valueOf(vpcId), "VPC");
|
|
throw ex;
|
|
}
|
|
|
|
PhysicalNetwork physNet = null;
|
|
// Validate physical network
|
|
if (physicalNetworkId == null) {
|
|
final List<? extends PhysicalNetwork> pNtwks = _ntwkModel.getPhysicalNtwksSupportingTrafficType(vpc.getZoneId(), TrafficType.Guest);
|
|
if (pNtwks.isEmpty() || pNtwks.size() != 1) {
|
|
throw new InvalidParameterValueException("Physical network can't be determined; pass physical network id");
|
|
}
|
|
physNet = pNtwks.get(0);
|
|
physicalNetworkId = physNet.getId();
|
|
}
|
|
|
|
if (physNet == null) {
|
|
physNet = _entityMgr.findById(PhysicalNetwork.class, physicalNetworkId);
|
|
}
|
|
final Long dcId = physNet.getDataCenterId();
|
|
|
|
final Long physicalNetworkIdFinal = physicalNetworkId;
|
|
final PhysicalNetwork physNetFinal = physNet;
|
|
VpcGatewayVO gatewayVO = null;
|
|
try {
|
|
gatewayVO = Transaction.execute(new TransactionCallbackWithException<VpcGatewayVO, Exception>() {
|
|
@Override
|
|
public VpcGatewayVO doInTransaction(final TransactionStatus status) throws ResourceAllocationException, ConcurrentOperationException,
|
|
InsufficientCapacityException {
|
|
s_logger.debug("Creating Private gateway for VPC " + vpc);
|
|
// 1) create private network unless it is existing and
|
|
// lswitch'd
|
|
Network privateNtwk = null;
|
|
if (BroadcastDomainType.getSchemeValue(BroadcastDomainType.fromString(broadcastUri)) == BroadcastDomainType.Lswitch) {
|
|
final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
|
|
privateNtwk = _ntwkDao.getPrivateNetwork(broadcastUri, cidr, gatewayOwnerId, dcId, networkOfferingId);
|
|
// if the dcid is different we get no network so next we
|
|
// try to create it
|
|
}
|
|
if (privateNtwk == null) {
|
|
s_logger.info("creating new network for vpc " + vpc + " using broadcast uri: " + broadcastUri);
|
|
final String networkName = "vpc-" + vpc.getName() + "-privateNetwork";
|
|
privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkIdFinal, broadcastUri, ipAddress, null, gateway, netmask,
|
|
gatewayOwnerId, vpcId, isSourceNat, networkOfferingId);
|
|
} else { // create the nic/ip as createPrivateNetwork
|
|
// doesn''t do that work for us now
|
|
s_logger.info("found and using existing network for vpc " + vpc + ": " + broadcastUri);
|
|
final DataCenterVO dc = _dcDao.lockRow(physNetFinal.getDataCenterId(), true);
|
|
|
|
// add entry to private_ip_address table
|
|
PrivateIpVO privateIp = _privateIpDao.findByIpAndSourceNetworkId(privateNtwk.getId(), ipAddress);
|
|
if (privateIp != null) {
|
|
throw new InvalidParameterValueException("Private ip address " + ipAddress + " already used for private gateway" + " in zone "
|
|
+ _entityMgr.findById(DataCenter.class, dcId).getName());
|
|
}
|
|
|
|
final Long mac = dc.getMacAddress();
|
|
final Long nextMac = mac + 1;
|
|
dc.setMacAddress(nextMac);
|
|
|
|
s_logger.info("creating private ip address for vpc (" + ipAddress + ", " + privateNtwk.getId() + ", " + nextMac + ", " + vpcId + ", " + isSourceNat + ")");
|
|
privateIp = new PrivateIpVO(ipAddress, privateNtwk.getId(), nextMac, vpcId, isSourceNat);
|
|
_privateIpDao.persist(privateIp);
|
|
|
|
_dcDao.update(dc.getId(), dc);
|
|
}
|
|
|
|
long networkAclId = NetworkACL.DEFAULT_DENY;
|
|
if (aclId != null) {
|
|
final NetworkACLVO aclVO = _networkAclDao.findById(aclId);
|
|
if (aclVO == null) {
|
|
throw new InvalidParameterValueException("Invalid network acl id passed ");
|
|
}
|
|
if (aclVO.getVpcId() != vpcId && !(aclId == NetworkACL.DEFAULT_DENY || aclId == NetworkACL.DEFAULT_ALLOW)) {
|
|
throw new InvalidParameterValueException("Private gateway and network acl are not in the same vpc");
|
|
}
|
|
|
|
networkAclId = aclId;
|
|
}
|
|
|
|
{ // experimental block, this is a hack
|
|
// set vpc id in network to null
|
|
// might be needed for all types of broadcast domains
|
|
// the ugly hack is that vpc gateway nets are created as
|
|
// guest network
|
|
// while they are not.
|
|
// A more permanent solution would be to define a type of
|
|
// 'gatewaynetwork'
|
|
// so that handling code is not mixed between the two
|
|
final NetworkVO gatewaynet = _ntwkDao.findById(privateNtwk.getId());
|
|
gatewaynet.setVpcId(null);
|
|
_ntwkDao.persist(gatewaynet);
|
|
}
|
|
|
|
// 2) create gateway entry
|
|
final VpcGatewayVO gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), privateNtwk.getId(), broadcastUri,
|
|
gateway, netmask, vpc.getAccountId(), vpc.getDomainId(), isSourceNat, networkAclId);
|
|
_vpcGatewayDao.persist(gatewayVO);
|
|
|
|
s_logger.debug("Created vpc gateway entry " + gatewayVO);
|
|
|
|
return gatewayVO;
|
|
}
|
|
});
|
|
} catch (final Exception e) {
|
|
ExceptionUtil.rethrowRuntime(e);
|
|
ExceptionUtil.rethrow(e, InsufficientCapacityException.class);
|
|
ExceptionUtil.rethrow(e, ResourceAllocationException.class);
|
|
throw new IllegalStateException(e);
|
|
}
|
|
|
|
CallContext.current().setEventDetails("Private Gateway Id: " + gatewayVO.getId());
|
|
return getVpcPrivateGateway(gatewayVO.getId());
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_CREATE, eventDescription = "Applying VPC private gateway", async = true)
|
|
public PrivateGateway applyVpcPrivateGateway(final long gatewayId, final boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
final VpcGatewayVO vo = _vpcGatewayDao.findById(gatewayId);
|
|
|
|
boolean success = true;
|
|
try {
|
|
final List<Provider> providersToImplement = getVpcProviders(vo.getVpcId());
|
|
|
|
final PrivateGateway gateway = getVpcPrivateGateway(gatewayId);
|
|
for (final VpcProvider provider : getVpcElements()) {
|
|
if (providersToImplement.contains(provider.getProvider())) {
|
|
if (!provider.createPrivateGateway(gateway)) {
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
if (success) {
|
|
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);
|
|
}
|
|
CallContext.current().setEventDetails("Private Gateway Id: " + gatewayId);
|
|
return getVpcPrivateGateway(gatewayId);
|
|
} else {
|
|
s_logger.warn("Private gateway " + gateway + " failed to apply on the backend");
|
|
return null;
|
|
}
|
|
} finally {
|
|
// do cleanup
|
|
if (!success) {
|
|
if (destroyOnFailure) {
|
|
s_logger.debug("Destroying private gateway " + vo + " that failed to start");
|
|
// calling deleting from db because on createprivategateway
|
|
// fail, destroyPrivateGateway is already called
|
|
if (deletePrivateGatewayFromTheDB(getVpcPrivateGateway(gatewayId))) {
|
|
s_logger.warn("Successfully destroyed vpc " + vo + " that failed to start");
|
|
} else {
|
|
s_logger.warn("Failed to destroy vpc " + vo + " that failed to start");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_DELETE, eventDescription = "deleting private gateway")
|
|
@DB
|
|
public boolean deleteVpcPrivateGateway(final long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException {
|
|
final VpcGatewayVO gatewayToBeDeleted = _vpcGatewayDao.findById(gatewayId);
|
|
if (gatewayToBeDeleted == null) {
|
|
s_logger.debug("VPC gateway is already deleted for id=" + gatewayId);
|
|
return true;
|
|
}
|
|
|
|
final VpcGatewayVO gatewayVO = _vpcGatewayDao.acquireInLockTable(gatewayId);
|
|
if (gatewayVO == null || gatewayVO.getType() != VpcGateway.Type.Private) {
|
|
throw new ConcurrentOperationException("Unable to lock gateway " + gatewayId);
|
|
}
|
|
|
|
try {
|
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
// don't allow to remove gateway when there are static
|
|
// routes associated with it
|
|
final 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);
|
|
_vpcGatewayDao.update(gatewayVO.getId(), gatewayVO);
|
|
s_logger.debug("Marked gateway " + gatewayVO + " with state " + VpcGateway.State.Deleting);
|
|
}
|
|
});
|
|
|
|
// 1) delete the gateway on the backend
|
|
final List<Provider> providersToImplement = getVpcProviders(gatewayVO.getVpcId());
|
|
final PrivateGateway gateway = getVpcPrivateGateway(gatewayId);
|
|
for (final VpcProvider provider : getVpcElements()) {
|
|
if (providersToImplement.contains(provider.getProvider())) {
|
|
if (provider.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");
|
|
gatewayVO.setState(VpcGateway.State.Ready);
|
|
_vpcGatewayDao.update(gatewayVO.getId(), gatewayVO);
|
|
s_logger.debug("Marked gateway " + gatewayVO + " with state " + VpcGateway.State.Ready);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2) Delete private gateway from the DB
|
|
return deletePrivateGatewayFromTheDB(gateway);
|
|
|
|
} finally {
|
|
if (gatewayVO != null) {
|
|
_vpcGatewayDao.releaseFromLockTable(gatewayId);
|
|
}
|
|
}
|
|
}
|
|
|
|
@DB
|
|
protected boolean deletePrivateGatewayFromTheDB(final PrivateGateway gateway) {
|
|
// check if there are ips allocted in the network
|
|
final long networkId = gateway.getNetworkId();
|
|
|
|
vpcTxCallable.setGateway(gateway);
|
|
|
|
final ExecutorService txExecutor = Executors.newSingleThreadExecutor();
|
|
final Future<Boolean> futureResult = txExecutor.submit(vpcTxCallable);
|
|
|
|
boolean deleteNetworkFinal;
|
|
try {
|
|
deleteNetworkFinal = futureResult.get();
|
|
if (deleteNetworkFinal) {
|
|
final User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
|
|
final Account owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM);
|
|
final ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner);
|
|
_ntwkMgr.destroyNetwork(networkId, context, false);
|
|
s_logger.debug("Deleted private network id=" + networkId);
|
|
}
|
|
} catch (final InterruptedException e) {
|
|
s_logger.error("deletePrivateGatewayFromTheDB failed to delete network id " + networkId + "due to => ", e);
|
|
} catch (final ExecutionException e) {
|
|
s_logger.error("deletePrivateGatewayFromTheDB failed to delete network id " + networkId + "due to => ", e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public Pair<List<PrivateGateway>, Integer> listPrivateGateway(final ListPrivateGatewaysCmd cmd) {
|
|
final String ipAddress = cmd.getIpAddress();
|
|
final String vlan = cmd.getVlan();
|
|
final Long vpcId = cmd.getVpcId();
|
|
final Long id = cmd.getId();
|
|
Boolean isRecursive = cmd.isRecursive();
|
|
final Boolean listAll = cmd.listAll();
|
|
Long domainId = cmd.getDomainId();
|
|
final String accountName = cmd.getAccountName();
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
final List<Long> permittedAccounts = new ArrayList<Long>();
|
|
final String state = cmd.getState();
|
|
final Long projectId = cmd.getProjectId();
|
|
|
|
final Filter searchFilter = new Filter(VpcGatewayVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal());
|
|
final Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive,
|
|
null);
|
|
_accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
|
domainId = domainIdRecursiveListProject.first();
|
|
isRecursive = domainIdRecursiveListProject.second();
|
|
final ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
|
|
|
final SearchBuilder<VpcGatewayVO> sb = _vpcGatewayDao.createSearchBuilder();
|
|
_accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
|
if (vlan != null) {
|
|
final 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);
|
|
}
|
|
|
|
final SearchCriteria<VpcGatewayVO> 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));
|
|
}
|
|
|
|
final Pair<List<VpcGatewayVO>, Integer> vos = _vpcGatewayDao.searchAndCount(sc, searchFilter);
|
|
final List<PrivateGateway> privateGtws = new ArrayList<PrivateGateway>(vos.first().size());
|
|
for (final VpcGateway vo : vos.first()) {
|
|
privateGtws.add(getPrivateGatewayProfile(vo));
|
|
}
|
|
|
|
return new Pair<List<PrivateGateway>, Integer>(privateGtws, vos.second());
|
|
}
|
|
|
|
@Override
|
|
public StaticRoute getStaticRoute(final long routeId) {
|
|
return _staticRouteDao.findById(routeId);
|
|
}
|
|
|
|
@Override
|
|
public boolean applyStaticRoutesForVpc(final long vpcId) throws ResourceUnavailableException {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
final List<? extends StaticRoute> routes = _staticRouteDao.listByVpcId(vpcId);
|
|
return applyStaticRoutes(routes, caller, true);
|
|
}
|
|
|
|
protected boolean applyStaticRoutes(final List<? extends StaticRoute> routes, final Account caller, final boolean updateRoutesInDB) throws ResourceUnavailableException {
|
|
final boolean success = true;
|
|
final List<StaticRouteProfile> staticRouteProfiles = new ArrayList<StaticRouteProfile>(routes.size());
|
|
final Map<Long, VpcGateway> gatewayMap = new HashMap<Long, VpcGateway>();
|
|
for (final 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 (final 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) {
|
|
final 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(final List<StaticRouteProfile> routes) throws ResourceUnavailableException {
|
|
if (routes.isEmpty()) {
|
|
s_logger.debug("No static routes to apply");
|
|
return true;
|
|
}
|
|
final Vpc vpc = _vpcDao.findById(routes.get(0).getVpcId());
|
|
|
|
s_logger.debug("Applying static routes for vpc " + vpc);
|
|
final String staticNatProvider = _vpcSrvcDao.getProviderForServiceInVpc(vpc.getId(), Service.StaticNat);
|
|
|
|
for (final VpcProvider provider : getVpcElements()) {
|
|
if (!(provider instanceof StaticNatServiceProvider && provider.getName().equalsIgnoreCase(staticNatProvider))) {
|
|
continue;
|
|
}
|
|
|
|
if (provider.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(final long routeId) throws ResourceUnavailableException {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
|
|
final 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 applyStaticRoutesForVpc(route.getVpcId());
|
|
}
|
|
|
|
@DB
|
|
protected boolean revokeStaticRoutesForVpc(final long vpcId, final Account caller) throws ResourceUnavailableException {
|
|
// get all static routes for the vpc
|
|
final List<StaticRouteVO> 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.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
for (final StaticRouteVO route : routes) {
|
|
markStaticRouteForRevoke(route, caller);
|
|
}
|
|
}
|
|
});
|
|
return applyStaticRoutesForVpc(vpcId);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
@ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_CREATE, eventDescription = "creating static route", create = true)
|
|
public StaticRoute createStaticRoute(final long gatewayId, final String cidr) throws NetworkRuleConflictException {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
|
|
// parameters validation
|
|
final 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());
|
|
}
|
|
|
|
final 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.isValidIp4Cidr(cidr)) {
|
|
throw new InvalidParameterValueException("Invalid format for cidr " + cidr);
|
|
}
|
|
|
|
// validate the cidr
|
|
// 1) CIDR should be outside of VPC cidr for guest networks
|
|
if (NetUtils.isNetworksOverlap(vpc.getCidr(), cidr)) {
|
|
throw new InvalidParameterValueException("CIDR should be outside of VPC cidr " + vpc.getCidr());
|
|
}
|
|
|
|
// 2) CIDR should be outside of link-local cidr
|
|
if (NetUtils.isNetworksOverlap(vpc.getCidr(), NetUtils.getLinkLocalCIDR())) {
|
|
throw new InvalidParameterValueException("CIDR should be outside of link local cidr " + NetUtils.getLinkLocalCIDR());
|
|
}
|
|
|
|
// 3) Verify against blacklisted routes
|
|
if (isCidrBlacklisted(cidr, vpc.getZoneId())) {
|
|
throw new InvalidParameterValueException("The static gateway cidr overlaps with one of the blacklisted routes of the zone the VPC belongs to");
|
|
}
|
|
|
|
return Transaction.execute(new TransactionCallbackWithException<StaticRouteVO, NetworkRuleConflictException>() {
|
|
@Override
|
|
public StaticRouteVO doInTransaction(final TransactionStatus status) throws NetworkRuleConflictException {
|
|
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);
|
|
}
|
|
CallContext.current().setEventDetails("Static route Id: " + newRoute.getId());
|
|
|
|
return newRoute;
|
|
}
|
|
});
|
|
}
|
|
|
|
protected boolean isCidrBlacklisted(final String cidr, final long zoneId) {
|
|
final String routesStr = NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId);
|
|
if (routesStr != null && !routesStr.isEmpty()) {
|
|
final String[] cidrBlackList = routesStr.split(",");
|
|
|
|
if (cidrBlackList != null && cidrBlackList.length > 0) {
|
|
for (final String blackListedRoute : cidrBlackList) {
|
|
if (NetUtils.isNetworksOverlap(blackListedRoute, cidr)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Pair<List<? extends StaticRoute>, Integer> listStaticRoutes(final ListStaticRoutesCmd cmd) {
|
|
final Long id = cmd.getId();
|
|
final Long gatewayId = cmd.getGatewayId();
|
|
final Long vpcId = cmd.getVpcId();
|
|
Long domainId = cmd.getDomainId();
|
|
Boolean isRecursive = cmd.isRecursive();
|
|
final Boolean listAll = cmd.listAll();
|
|
final String accountName = cmd.getAccountName();
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
final List<Long> permittedAccounts = new ArrayList<Long>();
|
|
final Map<String, String> tags = cmd.getTags();
|
|
final Long projectId = cmd.getProjectId();
|
|
|
|
final Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive,
|
|
null);
|
|
_accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false);
|
|
domainId = domainIdRecursiveListProject.first();
|
|
isRecursive = domainIdRecursiveListProject.second();
|
|
final ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
|
final Filter searchFilter = new Filter(StaticRouteVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
|
|
|
|
final 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);
|
|
|
|
if (tags != null && !tags.isEmpty()) {
|
|
final SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
|
|
for (int count = 0; count < tags.size(); count++) {
|
|
tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
|
|
tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
|
|
tagSearch.cp();
|
|
}
|
|
tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
|
sb.groupBy(sb.entity().getId());
|
|
sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
|
|
}
|
|
|
|
final 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, gatewayId);
|
|
}
|
|
|
|
if (tags != null && !tags.isEmpty()) {
|
|
int count = 0;
|
|
sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.StaticRoute.toString());
|
|
for (final String key : tags.keySet()) {
|
|
sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
|
|
sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
|
|
count++;
|
|
}
|
|
}
|
|
|
|
final Pair<List<StaticRouteVO>, Integer> result = _staticRouteDao.searchAndCount(sc, searchFilter);
|
|
return new Pair<List<? extends StaticRoute>, Integer>(result.first(), result.second());
|
|
}
|
|
|
|
protected void detectRoutesConflict(final StaticRoute newRoute) throws NetworkRuleConflictException {
|
|
// Multiple private gateways can exist within Vpc. Check for conflicts
|
|
// for all static routes in Vpc
|
|
// and not just the gateway
|
|
final List<? extends StaticRoute> routes = _staticRouteDao.listByVpcIdAndNotRevoked(newRoute.getVpcId());
|
|
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 (final 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(final StaticRouteVO route, final 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 extends ManagedContextRunnable {
|
|
@Override
|
|
protected void runInContext() {
|
|
try {
|
|
final 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;
|
|
}
|
|
|
|
try {
|
|
// Cleanup inactive VPCs
|
|
final List<VpcVO> inactiveVpcs = _vpcDao.listInactiveVpcs();
|
|
if (inactiveVpcs != null) {
|
|
s_logger.info("Found " + inactiveVpcs.size() + " removed VPCs to cleanup");
|
|
for (final VpcVO vpc : inactiveVpcs) {
|
|
s_logger.debug("Cleaning up " + vpc);
|
|
destroyVpc(vpc, _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM);
|
|
}
|
|
}
|
|
} catch (final Exception e) {
|
|
s_logger.error("Exception ", e);
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
} catch (final Exception e) {
|
|
s_logger.error("Exception ", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "associating Ip", async = true)
|
|
public IpAddress associateIPToVpc(final long ipId, final long vpcId) throws ResourceAllocationException, ResourceUnavailableException, InsufficientAddressCapacityException,
|
|
ConcurrentOperationException {
|
|
final Account caller = CallContext.current().getCallingAccount();
|
|
Account owner = null;
|
|
|
|
final IpAddress ipToAssoc = _ntwkModel.getIp(ipId);
|
|
if (ipToAssoc != null) {
|
|
_accountMgr.checkAccess(caller, null, true, ipToAssoc);
|
|
owner = _accountMgr.getAccount(ipToAssoc.getAllocatedToAccountId());
|
|
} else {
|
|
s_logger.debug("Unable to find ip address by id: " + ipId);
|
|
return null;
|
|
}
|
|
|
|
final Vpc vpc = _vpcDao.findById(vpcId);
|
|
if (vpc == null) {
|
|
throw new InvalidParameterValueException("Invalid VPC id provided");
|
|
}
|
|
|
|
// check permissions
|
|
_accountMgr.checkAccess(caller, null, true, owner, vpc);
|
|
|
|
s_logger.debug("Associating ip " + ipToAssoc + " to vpc " + vpc);
|
|
|
|
final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(owner.getId(), vpcId) == null;
|
|
Transaction.execute(new TransactionCallbackNoReturn() {
|
|
@Override
|
|
public void doInTransactionWithoutResult(final TransactionStatus status) {
|
|
final IPAddressVO ip = _ipAddressDao.findById(ipId);
|
|
// update ip address with networkId
|
|
ip.setVpcId(vpcId);
|
|
ip.setSourceNat(isSourceNatFinal);
|
|
|
|
_ipAddressDao.update(ipId, ip);
|
|
|
|
// mark ip as allocated
|
|
_ipAddrMgr.markPublicIpAsAllocated(ip);
|
|
}
|
|
});
|
|
|
|
s_logger.debug("Successfully assigned ip " + ipToAssoc + " to vpc " + vpc);
|
|
|
|
return _ipAddressDao.findById(ipId);
|
|
}
|
|
|
|
@Override
|
|
public void unassignIPFromVpcNetwork(final long ipId, final long networkId) {
|
|
final IPAddressVO ip = _ipAddressDao.findById(ipId);
|
|
if (isIpAllocatedToVpc(ip)) {
|
|
return;
|
|
}
|
|
|
|
if (ip == null || ip.getVpcId() == null) {
|
|
return;
|
|
}
|
|
|
|
s_logger.debug("Releasing VPC ip address " + ip + " from vpc network id=" + networkId);
|
|
|
|
final long vpcId = ip.getVpcId();
|
|
boolean success = false;
|
|
try {
|
|
// unassign ip from the VPC router
|
|
success = _ipAddrMgr.applyIpAssociations(_ntwkModel.getNetwork(networkId), true);
|
|
} catch (final ResourceUnavailableException ex) {
|
|
throw new CloudRuntimeException("Failed to apply ip associations for network id=" + networkId + " as a part of unassigning ip " + ipId + " from vpc", ex);
|
|
}
|
|
|
|
if (success) {
|
|
ip.setAssociatedWithNetworkId(null);
|
|
_ipAddressDao.update(ipId, ip);
|
|
s_logger.debug("IP address " + ip + " is no longer associated with the network inside vpc id=" + vpcId);
|
|
} else {
|
|
throw new CloudRuntimeException("Failed to apply ip associations for network id=" + networkId + " as a part of unassigning ip " + ipId + " from vpc");
|
|
}
|
|
s_logger.debug("Successfully released VPC ip address " + ip + " back to VPC pool ");
|
|
}
|
|
|
|
@Override
|
|
public boolean isIpAllocatedToVpc(final IpAddress ip) {
|
|
return ip != null && ip.getVpcId() != null && (ip.isOneToOneNat() || !_firewallDao.listByIp(ip.getId()).isEmpty());
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public Network createVpcGuestNetwork(final long ntwkOffId, final String name, final String displayText, final String gateway, final String cidr, final String vlanId,
|
|
String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, final Boolean subdomainAccess,
|
|
final long vpcId, final Long aclId, final Account caller, final Boolean isDisplayNetworkEnabled, String externalId) throws ConcurrentOperationException, InsufficientCapacityException,
|
|
ResourceAllocationException {
|
|
|
|
final Vpc vpc = getActiveVpc(vpcId);
|
|
|
|
if (vpc == null) {
|
|
final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find Enabled VPC ");
|
|
ex.addProxyObject(String.valueOf(vpcId), "VPC");
|
|
throw ex;
|
|
}
|
|
_accountMgr.checkAccess(caller, null, false, vpc);
|
|
|
|
if (networkDomain == null) {
|
|
networkDomain = vpc.getNetworkDomain();
|
|
}
|
|
|
|
if (!vpc.isRegionLevelVpc() && vpc.getZoneId() != zoneId) {
|
|
throw new InvalidParameterValueException("New network doesn't belong to vpc zone");
|
|
}
|
|
|
|
// 1) Validate if network can be created for VPC
|
|
validateNtwkOffForNtwkInVpc(null, ntwkOffId, cidr, networkDomain, vpc, gateway, owner, aclId);
|
|
|
|
// 2) Create network
|
|
final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, false, networkDomain, owner, domainId, pNtwk, zoneId, aclType,
|
|
subdomainAccess, vpcId, null, null, isDisplayNetworkEnabled, null, externalId);
|
|
|
|
if (guestNetwork != null) {
|
|
guestNetwork.setNetworkACLId(aclId);
|
|
_ntwkDao.update(guestNetwork.getId(), (NetworkVO) guestNetwork);
|
|
}
|
|
return guestNetwork;
|
|
}
|
|
|
|
protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long vpcId) {
|
|
|
|
final List<IPAddressVO> addrs = listPublicIpsAssignedToVpc(ownerId, true, vpcId);
|
|
|
|
IPAddressVO sourceNatIp = null;
|
|
if (addrs.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
// Account already has ip addresses
|
|
for (final IPAddressVO addr : addrs) {
|
|
if (addr.isSourceNat()) {
|
|
sourceNatIp = addr;
|
|
return sourceNatIp;
|
|
}
|
|
}
|
|
|
|
assert sourceNatIp != null : "How do we get a bunch of ip addresses but none of them are source nat? " + "account=" + ownerId + "; vpcId=" + vpcId;
|
|
}
|
|
|
|
return sourceNatIp;
|
|
}
|
|
|
|
protected List<IPAddressVO> listPublicIpsAssignedToVpc(final long accountId, final Boolean sourceNat, final long vpcId) {
|
|
final SearchCriteria<IPAddressVO> sc = IpAddressSearch.create();
|
|
sc.setParameters("accountId", accountId);
|
|
sc.setParameters("vpcId", vpcId);
|
|
|
|
if (sourceNat != null) {
|
|
sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat);
|
|
}
|
|
sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork);
|
|
|
|
return _ipAddressDao.search(sc, null);
|
|
}
|
|
|
|
@Override
|
|
public PublicIp assignSourceNatIpAddressToVpc(final Account owner, final Vpc vpc) throws InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
final long dcId = vpc.getZoneId();
|
|
|
|
final IPAddressVO sourceNatIp = getExistingSourceNatInVpc(owner.getId(), vpc.getId());
|
|
|
|
PublicIp ipToReturn = null;
|
|
|
|
if (sourceNatIp != null) {
|
|
ipToReturn = PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId()));
|
|
} else {
|
|
ipToReturn = _ipAddrMgr.assignDedicateIpAddress(owner, null, vpc.getId(), dcId, true);
|
|
}
|
|
|
|
return ipToReturn;
|
|
}
|
|
|
|
@Override
|
|
public List<HypervisorType> getSupportedVpcHypervisors() {
|
|
return Collections.unmodifiableList(hTypes);
|
|
}
|
|
|
|
private List<Provider> getVpcProviders(final long vpcId) {
|
|
final List<String> providerNames = _vpcSrvcDao.getDistinctProviders(vpcId);
|
|
final Map<String, Provider> providers = new HashMap<String, Provider>();
|
|
for (final String providerName : providerNames) {
|
|
if (!providers.containsKey(providerName)) {
|
|
providers.put(providerName, Network.Provider.getProvider(providerName));
|
|
}
|
|
}
|
|
|
|
return new ArrayList<Provider>(providers.values());
|
|
}
|
|
|
|
@Inject
|
|
public void setVpcElements(final List<VpcProvider> vpcElements) {
|
|
this.vpcElements = vpcElements;
|
|
}
|
|
|
|
@Override
|
|
@ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_CREATE, eventDescription = "Applying static route", async = true)
|
|
public boolean applyStaticRoute(final long routeId) throws ResourceUnavailableException {
|
|
final StaticRoute route = _staticRouteDao.findById(routeId);
|
|
return applyStaticRoutesForVpc(route.getVpcId());
|
|
}
|
|
|
|
@Override
|
|
public boolean isSrcNatIpRequired(long vpcOfferingId) {
|
|
final Map<Network.Service, Set<Network.Provider>> vpcOffSvcProvidersMap = getVpcOffSvcProvidersMap(vpcOfferingId);
|
|
return vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter);
|
|
}
|
|
|
|
/**
|
|
* rollingRestartVpc performs restart of routers of a VPC by first
|
|
* deploying a new VR and then destroying old VRs in rolling fashion. For
|
|
* non-redundant VPC, it will re-program the new router as final step
|
|
* otherwise deploys a backup router for the VPC.
|
|
* @param vpc vpc to be restarted
|
|
* @param context reservation context
|
|
* @return returns true when the rolling restart succeeds
|
|
* @throws ResourceUnavailableException
|
|
* @throws ConcurrentOperationException
|
|
* @throws InsufficientCapacityException
|
|
*/
|
|
private boolean rollingRestartVpc(final Vpc vpc, final ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException {
|
|
s_logger.debug("Performing rolling restart of routers of VPC " + vpc);
|
|
_ntwkMgr.destroyExpendableRouters(_routerDao.listByVpcId(vpc.getId()), context);
|
|
|
|
final DeployDestination dest = new DeployDestination(_dcDao.findById(vpc.getZoneId()), null, null, null);
|
|
final List<DomainRouterVO> oldRouters = _routerDao.listByVpcId(vpc.getId());
|
|
|
|
// Create a new router
|
|
if (oldRouters.size() > 0) {
|
|
vpc.setRollingRestart(true);
|
|
}
|
|
startVpc(vpc, dest, context);
|
|
if (oldRouters.size() > 0) {
|
|
vpc.setRollingRestart(false);
|
|
}
|
|
|
|
// For redundant vpc wait for 3*advert_int+skew_seconds for VRRP to kick in
|
|
if (vpc.isRedundant() || (oldRouters.size() == 1 && oldRouters.get(0).getIsRedundantRouter())) {
|
|
try {
|
|
Thread.sleep(NetworkOrchestrationService.RVRHandoverTime);
|
|
} catch (final InterruptedException ignored) {
|
|
}
|
|
}
|
|
|
|
// Destroy old routers
|
|
for (final DomainRouterVO oldRouter : oldRouters) {
|
|
_routerService.destroyRouter(oldRouter.getId(), context.getAccount(), context.getCaller().getId());
|
|
}
|
|
|
|
// Re-program VPC VR or add a new backup router for redundant VPC
|
|
if (!startVpc(vpc, dest, context)) {
|
|
s_logger.debug("Failed to re-program VPC router or deploy a new backup router for VPC" + vpc);
|
|
return false;
|
|
}
|
|
|
|
return _ntwkMgr.areRoutersRunning(_routerDao.listByVpcId(vpc.getId()));
|
|
}
|
|
|
|
}
|