mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
1926 lines
88 KiB
Java
1926 lines
88 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;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
|
import org.apache.cloudstack.context.CallContext;
|
|
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
|
import org.apache.cloudstack.framework.config.Configurable;
|
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|
import org.apache.cloudstack.region.PortableIp;
|
|
import org.apache.cloudstack.region.PortableIpDao;
|
|
import org.apache.cloudstack.region.PortableIpVO;
|
|
import org.apache.cloudstack.region.Region;
|
|
|
|
import com.cloud.agent.AgentManager;
|
|
import com.cloud.alert.AlertManager;
|
|
import com.cloud.api.ApiDBUtils;
|
|
import com.cloud.configuration.ConfigurationManager;
|
|
import com.cloud.configuration.Resource.ResourceType;
|
|
import com.cloud.dc.AccountVlanMapVO;
|
|
import com.cloud.dc.DataCenter;
|
|
import com.cloud.dc.DataCenter.NetworkType;
|
|
import com.cloud.dc.Pod;
|
|
import com.cloud.dc.PodVlanMapVO;
|
|
import com.cloud.dc.Vlan;
|
|
import com.cloud.dc.Vlan.VlanType;
|
|
import com.cloud.dc.VlanVO;
|
|
import com.cloud.dc.dao.AccountVlanMapDao;
|
|
import com.cloud.dc.dao.DataCenterDao;
|
|
import com.cloud.dc.dao.DataCenterVnetDao;
|
|
import com.cloud.dc.dao.PodVlanMapDao;
|
|
import com.cloud.dc.dao.VlanDao;
|
|
import com.cloud.deploy.DeployDestination;
|
|
import com.cloud.domain.Domain;
|
|
import com.cloud.domain.dao.DomainDao;
|
|
import com.cloud.event.ActionEventUtils;
|
|
import com.cloud.event.EventTypes;
|
|
import com.cloud.event.UsageEventUtils;
|
|
import com.cloud.event.dao.UsageEventDao;
|
|
import com.cloud.exception.AccountLimitException;
|
|
import com.cloud.exception.ConcurrentOperationException;
|
|
import com.cloud.exception.InsufficientAddressCapacityException;
|
|
import com.cloud.exception.InsufficientCapacityException;
|
|
import com.cloud.exception.InsufficientVirtualNetworkCapcityException;
|
|
import com.cloud.exception.InvalidParameterValueException;
|
|
import com.cloud.exception.PermissionDeniedException;
|
|
import com.cloud.exception.ResourceAllocationException;
|
|
import com.cloud.exception.ResourceUnavailableException;
|
|
import com.cloud.host.dao.HostDao;
|
|
import com.cloud.network.IpAddress.State;
|
|
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.Networks.AddressFormat;
|
|
import com.cloud.network.Networks.BroadcastDomainType;
|
|
import com.cloud.network.Networks.IsolationType;
|
|
import com.cloud.network.Networks.TrafficType;
|
|
import com.cloud.network.addr.PublicIp;
|
|
import com.cloud.network.dao.AccountGuestVlanMapDao;
|
|
import com.cloud.network.dao.FirewallRulesDao;
|
|
import com.cloud.network.dao.IPAddressDao;
|
|
import com.cloud.network.dao.IPAddressVO;
|
|
import com.cloud.network.dao.LoadBalancerDao;
|
|
import com.cloud.network.dao.NetworkAccountDao;
|
|
import com.cloud.network.dao.NetworkDao;
|
|
import com.cloud.network.dao.NetworkDomainDao;
|
|
import com.cloud.network.dao.NetworkServiceMapDao;
|
|
import com.cloud.network.dao.PhysicalNetworkDao;
|
|
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
|
|
import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
|
|
import com.cloud.network.dao.UserIpv6AddressDao;
|
|
import com.cloud.network.element.IpDeployer;
|
|
import com.cloud.network.element.IpDeployingRequester;
|
|
import com.cloud.network.element.NetworkElement;
|
|
import com.cloud.network.element.StaticNatServiceProvider;
|
|
import com.cloud.network.guru.NetworkGuru;
|
|
import com.cloud.network.lb.LoadBalancingRulesManager;
|
|
import com.cloud.network.rules.FirewallManager;
|
|
import com.cloud.network.rules.FirewallRule;
|
|
import com.cloud.network.rules.FirewallRule.Purpose;
|
|
import com.cloud.network.rules.FirewallRuleVO;
|
|
import com.cloud.network.rules.RulesManager;
|
|
import com.cloud.network.rules.StaticNat;
|
|
import com.cloud.network.rules.dao.PortForwardingRulesDao;
|
|
import com.cloud.network.vpc.NetworkACLManager;
|
|
import com.cloud.network.vpc.VpcManager;
|
|
import com.cloud.network.vpc.dao.PrivateIpDao;
|
|
import com.cloud.network.vpn.RemoteAccessVpnService;
|
|
import com.cloud.offering.NetworkOffering;
|
|
import com.cloud.offering.NetworkOffering.Availability;
|
|
import com.cloud.offerings.NetworkOfferingVO;
|
|
import com.cloud.offerings.dao.NetworkOfferingDao;
|
|
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
|
|
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
|
|
import com.cloud.org.Grouping;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.user.AccountManager;
|
|
import com.cloud.user.ResourceLimitService;
|
|
import com.cloud.user.User;
|
|
import com.cloud.user.UserVO;
|
|
import com.cloud.user.dao.AccountDao;
|
|
import com.cloud.user.dao.UserDao;
|
|
import com.cloud.utils.Journal;
|
|
import com.cloud.utils.Pair;
|
|
import com.cloud.utils.component.ManagerBase;
|
|
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.JoinType;
|
|
import com.cloud.utils.db.SearchBuilder;
|
|
import com.cloud.utils.db.SearchCriteria;
|
|
import com.cloud.utils.db.SearchCriteria.Op;
|
|
import com.cloud.utils.db.Transaction;
|
|
import com.cloud.utils.exception.CloudRuntimeException;
|
|
import com.cloud.utils.net.Ip;
|
|
import com.cloud.utils.net.NetUtils;
|
|
import com.cloud.vm.Nic;
|
|
import com.cloud.vm.NicProfile;
|
|
import com.cloud.vm.ReservationContext;
|
|
import com.cloud.vm.ReservationContextImpl;
|
|
import com.cloud.vm.VirtualMachine;
|
|
import com.cloud.vm.VirtualMachineProfile;
|
|
import com.cloud.vm.dao.NicDao;
|
|
import com.cloud.vm.dao.NicIpAliasDao;
|
|
import com.cloud.vm.dao.NicSecondaryIpDao;
|
|
import com.cloud.vm.dao.UserVmDao;
|
|
import com.cloud.vm.dao.VMInstanceDao;
|
|
|
|
public class IpAddressManagerImpl extends ManagerBase implements IpAddressManager, Configurable {
|
|
private static final Logger s_logger = Logger.getLogger(IpAddressManagerImpl.class);
|
|
|
|
@Inject
|
|
NetworkOrchestrationService _networkMgr = null;
|
|
@Inject
|
|
EntityManager _entityMgr = null;
|
|
@Inject
|
|
DataCenterDao _dcDao = null;
|
|
@Inject
|
|
VlanDao _vlanDao = null;
|
|
@Inject
|
|
IPAddressDao _ipAddressDao = null;
|
|
@Inject
|
|
AccountDao _accountDao = null;
|
|
@Inject
|
|
DomainDao _domainDao = null;
|
|
@Inject
|
|
UserDao _userDao = null;
|
|
@Inject
|
|
ConfigurationDao _configDao;
|
|
@Inject
|
|
UserVmDao _userVmDao = null;
|
|
@Inject
|
|
AlertManager _alertMgr;
|
|
@Inject
|
|
AccountManager _accountMgr;
|
|
@Inject
|
|
ConfigurationManager _configMgr;
|
|
@Inject
|
|
AccountVlanMapDao _accountVlanMapDao;
|
|
@Inject
|
|
NetworkOfferingDao _networkOfferingDao = null;
|
|
@Inject
|
|
NetworkDao _networksDao = null;
|
|
@Inject
|
|
NicDao _nicDao = null;
|
|
@Inject
|
|
RulesManager _rulesMgr;
|
|
@Inject
|
|
LoadBalancingRulesManager _lbMgr;
|
|
@Inject
|
|
RemoteAccessVpnService _vpnMgr;
|
|
@Inject
|
|
PodVlanMapDao _podVlanMapDao;
|
|
@Inject
|
|
NetworkOfferingDetailsDao _ntwkOffDetailsDao;
|
|
@Inject
|
|
AccountGuestVlanMapDao _accountGuestVlanMapDao;
|
|
@Inject
|
|
DataCenterVnetDao _datacenterVnetDao;
|
|
@Inject
|
|
NetworkAccountDao _networkAccountDao;
|
|
@Inject
|
|
protected NicIpAliasDao _nicIpAliasDao;
|
|
@Inject
|
|
protected IPAddressDao _publicIpAddressDao;
|
|
@Inject
|
|
NetworkDomainDao _networkDomainDao;
|
|
@Inject
|
|
VMInstanceDao _vmDao;
|
|
@Inject
|
|
FirewallManager _firewallMgr;
|
|
@Inject
|
|
FirewallRulesDao _firewallDao;
|
|
@Inject
|
|
ResourceLimitService _resourceLimitMgr;
|
|
|
|
@Inject
|
|
NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
|
|
@Inject
|
|
PhysicalNetworkDao _physicalNetworkDao;
|
|
@Inject
|
|
PhysicalNetworkServiceProviderDao _pNSPDao;
|
|
@Inject
|
|
PortForwardingRulesDao _portForwardingRulesDao;
|
|
@Inject
|
|
LoadBalancerDao _lbDao;
|
|
@Inject
|
|
PhysicalNetworkTrafficTypeDao _pNTrafficTypeDao;
|
|
@Inject
|
|
AgentManager _agentMgr;
|
|
@Inject
|
|
HostDao _hostDao;
|
|
@Inject
|
|
NetworkServiceMapDao _ntwkSrvcDao;
|
|
@Inject
|
|
StorageNetworkManager _stnwMgr;
|
|
@Inject
|
|
VpcManager _vpcMgr;
|
|
@Inject
|
|
PrivateIpDao _privateIpDao;
|
|
@Inject
|
|
NetworkACLManager _networkACLMgr;
|
|
@Inject
|
|
UsageEventDao _usageEventDao;
|
|
@Inject
|
|
NetworkModel _networkModel;
|
|
@Inject
|
|
NicSecondaryIpDao _nicSecondaryIpDao;
|
|
@Inject
|
|
UserIpv6AddressDao _ipv6Dao;
|
|
@Inject
|
|
Ipv6AddressManager _ipv6Mgr;
|
|
@Inject
|
|
PortableIpDao _portableIpDao;
|
|
SearchBuilder<IPAddressVO> AssignIpAddressSearch;
|
|
SearchBuilder<IPAddressVO> AssignIpAddressFromPodVlanSearch;
|
|
|
|
@Override
|
|
public boolean configure(String name, Map<String, Object> params) {
|
|
// populate providers
|
|
Map<Network.Service, Set<Network.Provider>> defaultSharedNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
Set<Network.Provider> defaultProviders = new HashSet<Network.Provider>();
|
|
|
|
defaultProviders.add(Network.Provider.VirtualRouter);
|
|
defaultSharedNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
|
|
defaultSharedNetworkOfferingProviders.put(Service.Dns, defaultProviders);
|
|
defaultSharedNetworkOfferingProviders.put(Service.UserData, defaultProviders);
|
|
|
|
Map<Network.Service, Set<Network.Provider>> defaultIsolatedNetworkOfferingProviders = defaultSharedNetworkOfferingProviders;
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Dns, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.UserData, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Firewall, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Gateway, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Lb, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.StaticNat, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders);
|
|
defaultIsolatedNetworkOfferingProviders.put(Service.Vpn, defaultProviders);
|
|
|
|
Map<Network.Service, Set<Network.Provider>> defaultSharedSGEnabledNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
|
|
defaultSharedSGEnabledNetworkOfferingProviders.put(Service.Dns, defaultProviders);
|
|
defaultSharedSGEnabledNetworkOfferingProviders.put(Service.UserData, defaultProviders);
|
|
Set<Provider> sgProviders = new HashSet<Provider>();
|
|
sgProviders.add(Provider.SecurityGroupProvider);
|
|
defaultSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, sgProviders);
|
|
|
|
Map<Network.Service, Set<Network.Provider>> defaultIsolatedSourceNatEnabledNetworkOfferingProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
defaultProviders.clear();
|
|
defaultProviders.add(Network.Provider.VirtualRouter);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Dhcp, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Dns, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.UserData, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Firewall, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Gateway, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Lb, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.SourceNat, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.StaticNat, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.PortForwarding, defaultProviders);
|
|
defaultIsolatedSourceNatEnabledNetworkOfferingProviders.put(Service.Vpn, defaultProviders);
|
|
|
|
Map<Network.Service, Set<Network.Provider>> defaultVPCOffProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
defaultProviders.clear();
|
|
defaultProviders.add(Network.Provider.VirtualRouter);
|
|
defaultVPCOffProviders.put(Service.Dhcp, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.Dns, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.UserData, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.NetworkACL, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.Gateway, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.Lb, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.SourceNat, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.StaticNat, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.PortForwarding, defaultProviders);
|
|
defaultVPCOffProviders.put(Service.Vpn, defaultProviders);
|
|
|
|
|
|
|
|
//#8 - network offering with internal lb service
|
|
Map<Network.Service, Set<Network.Provider>> internalLbOffProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
Set<Network.Provider> defaultVpcProvider = new HashSet<Network.Provider>();
|
|
defaultVpcProvider.add(Network.Provider.VPCVirtualRouter);
|
|
|
|
Set<Network.Provider> defaultInternalLbProvider = new HashSet<Network.Provider>();
|
|
defaultInternalLbProvider.add(Network.Provider.InternalLbVm);
|
|
|
|
internalLbOffProviders.put(Service.Dhcp, defaultVpcProvider);
|
|
internalLbOffProviders.put(Service.Dns, defaultVpcProvider);
|
|
internalLbOffProviders.put(Service.UserData, defaultVpcProvider);
|
|
internalLbOffProviders.put(Service.NetworkACL, defaultVpcProvider);
|
|
internalLbOffProviders.put(Service.Gateway, defaultVpcProvider);
|
|
internalLbOffProviders.put(Service.Lb, defaultInternalLbProvider);
|
|
internalLbOffProviders.put(Service.SourceNat, defaultVpcProvider);
|
|
|
|
|
|
|
|
Map<Network.Service, Set<Network.Provider>> netscalerServiceProviders = new HashMap<Network.Service, Set<Network.Provider>>();
|
|
Set<Network.Provider> vrProvider = new HashSet<Network.Provider>();
|
|
vrProvider.add(Provider.VirtualRouter);
|
|
Set<Network.Provider> sgProvider = new HashSet<Network.Provider>();
|
|
sgProvider.add(Provider.SecurityGroupProvider);
|
|
Set<Network.Provider> nsProvider = new HashSet<Network.Provider>();
|
|
nsProvider.add(Provider.Netscaler);
|
|
netscalerServiceProviders.put(Service.Dhcp, vrProvider);
|
|
netscalerServiceProviders.put(Service.Dns, vrProvider);
|
|
netscalerServiceProviders.put(Service.UserData, vrProvider);
|
|
netscalerServiceProviders.put(Service.SecurityGroup, sgProvider);
|
|
netscalerServiceProviders.put(Service.StaticNat, nsProvider);
|
|
netscalerServiceProviders.put(Service.Lb, nsProvider);
|
|
|
|
Map<Service, Map<Capability, String>> serviceCapabilityMap = new HashMap<Service, Map<Capability, String>>();
|
|
Map<Capability, String> elb = new HashMap<Capability, String>();
|
|
elb.put(Capability.ElasticLb, "true");
|
|
Map<Capability, String> eip = new HashMap<Capability, String>();
|
|
eip.put(Capability.ElasticIp, "true");
|
|
serviceCapabilityMap.put(Service.Lb, elb);
|
|
serviceCapabilityMap.put(Service.StaticNat, eip);
|
|
|
|
|
|
AssignIpAddressSearch = _ipAddressDao.createSearchBuilder();
|
|
AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ);
|
|
AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL);
|
|
AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN);
|
|
SearchBuilder<VlanVO> vlanSearch = _vlanDao.createSearchBuilder();
|
|
vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ);
|
|
vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ);
|
|
AssignIpAddressSearch.join("vlan", vlanSearch, vlanSearch.entity().getId(), AssignIpAddressSearch.entity().getVlanId(), JoinType.INNER);
|
|
AssignIpAddressSearch.done();
|
|
|
|
AssignIpAddressFromPodVlanSearch = _ipAddressDao.createSearchBuilder();
|
|
AssignIpAddressFromPodVlanSearch.and("dc", AssignIpAddressFromPodVlanSearch.entity().getDataCenterId(), Op.EQ);
|
|
AssignIpAddressFromPodVlanSearch.and("allocated", AssignIpAddressFromPodVlanSearch.entity().getAllocatedTime(), Op.NULL);
|
|
SearchBuilder<VlanVO> podVlanSearch = _vlanDao.createSearchBuilder();
|
|
podVlanSearch.and("type", podVlanSearch.entity().getVlanType(), Op.EQ);
|
|
podVlanSearch.and("networkId", podVlanSearch.entity().getNetworkId(), Op.EQ);
|
|
SearchBuilder<PodVlanMapVO> podVlanMapSB = _podVlanMapDao.createSearchBuilder();
|
|
podVlanMapSB.and("podId", podVlanMapSB.entity().getPodId(), Op.EQ);
|
|
AssignIpAddressFromPodVlanSearch.join("podVlanMapSB",
|
|
podVlanMapSB,
|
|
podVlanMapSB.entity().getVlanDbId(),
|
|
AssignIpAddressFromPodVlanSearch.entity().getVlanId(),
|
|
JoinType.INNER);
|
|
AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER);
|
|
AssignIpAddressFromPodVlanSearch.done();
|
|
|
|
Network.State.getStateMachine().registerListener(new NetworkStateListener(_usageEventDao, _networksDao));
|
|
|
|
s_logger.info("Network Manager is configured.");
|
|
|
|
return true;
|
|
}
|
|
|
|
private IpAddress allocateIP(Account ipOwner, boolean isSystem, long zoneId) throws ResourceAllocationException, InsufficientAddressCapacityException,
|
|
ConcurrentOperationException {
|
|
Account caller = CallContext.current().getCallingAccount();
|
|
long callerUserId = CallContext.current().getCallingUserId();
|
|
// check permissions
|
|
_accountMgr.checkAccess(caller, null, false, ipOwner);
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
|
|
|
|
return allocateIp(ipOwner, isSystem, caller, callerUserId, zone);
|
|
}
|
|
|
|
// An IP association is required in below cases
|
|
// 1.there is at least one public IP associated with the network on which first rule (PF/static NAT/LB) is being applied.
|
|
// 2.last rule (PF/static NAT/LB) on the public IP has been revoked. So the public IP should not be associated with any provider
|
|
boolean checkIfIpAssocRequired(Network network, boolean postApplyRules, List<PublicIp> publicIps) {
|
|
for (PublicIp ip : publicIps) {
|
|
if (ip.isSourceNat()) {
|
|
continue;
|
|
} else if (ip.isOneToOneNat()) {
|
|
continue;
|
|
} else {
|
|
Long totalCount = null;
|
|
Long revokeCount = null;
|
|
Long activeCount = null;
|
|
Long addCount = null;
|
|
|
|
totalCount = _firewallDao.countRulesByIpId(ip.getId());
|
|
if (postApplyRules) {
|
|
revokeCount = _firewallDao.countRulesByIpIdAndState(ip.getId(), FirewallRule.State.Revoke);
|
|
} else {
|
|
activeCount = _firewallDao.countRulesByIpIdAndState(ip.getId(), FirewallRule.State.Active);
|
|
addCount = _firewallDao.countRulesByIpIdAndState(ip.getId(), FirewallRule.State.Add);
|
|
}
|
|
|
|
if (totalCount == null || totalCount.longValue() == 0L) {
|
|
continue;
|
|
}
|
|
|
|
if (postApplyRules) {
|
|
|
|
if (revokeCount != null && revokeCount.longValue() == totalCount.longValue()) {
|
|
s_logger.trace("All rules are in Revoke state, have to dis-assiciate IP from the backend");
|
|
return true;
|
|
}
|
|
} else {
|
|
if (activeCount != null && activeCount > 0) {
|
|
continue;
|
|
} else if (addCount != null && addCount.longValue() == totalCount.longValue()) {
|
|
s_logger.trace("All rules are in Add state, have to assiciate IP with the backend");
|
|
return true;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// there are no IP's corresponding to this network that need to be associated with provider
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean applyRules(List<? extends FirewallRule> rules, FirewallRule.Purpose purpose, NetworkRuleApplier applier, boolean continueOnError)
|
|
throws ResourceUnavailableException {
|
|
if (rules == null || rules.size() == 0) {
|
|
s_logger.debug("There are no rules to forward to the network elements");
|
|
return true;
|
|
}
|
|
|
|
boolean success = true;
|
|
Network network = _networksDao.findById(rules.get(0).getNetworkId());
|
|
FirewallRuleVO.TrafficType trafficType = rules.get(0).getTrafficType();
|
|
List<PublicIp> publicIps = new ArrayList<PublicIp>();
|
|
|
|
if (!(rules.get(0).getPurpose() == FirewallRule.Purpose.Firewall && trafficType == FirewallRule.TrafficType.Egress)) {
|
|
// get the list of public ip's owned by the network
|
|
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
|
|
if (userIps != null && !userIps.isEmpty()) {
|
|
for (IPAddressVO userIp : userIps) {
|
|
PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
|
|
publicIps.add(publicIp);
|
|
}
|
|
}
|
|
}
|
|
// rules can not programmed unless IP is associated with network service provider, so run IP assoication for
|
|
// the network so as to ensure IP is associated before applying rules (in add state)
|
|
if (checkIfIpAssocRequired(network, false, publicIps)) {
|
|
applyIpAssociations(network, false, continueOnError, publicIps);
|
|
}
|
|
|
|
try {
|
|
applier.applyRules(network, purpose, rules);
|
|
} catch (ResourceUnavailableException e) {
|
|
if (!continueOnError) {
|
|
throw e;
|
|
}
|
|
s_logger.warn("Problems with applying " + purpose + " rules but pushing on", e);
|
|
success = false;
|
|
}
|
|
|
|
// if there are no active rules associated with a public IP, then public IP need not be associated with a provider.
|
|
// This IPAssoc ensures, public IP is dis-associated after last active rule is revoked.
|
|
if (checkIfIpAssocRequired(network, true, publicIps)) {
|
|
applyIpAssociations(network, true, continueOnError, publicIps);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
protected boolean cleanupIpResources(long ipId, long userId, Account caller) {
|
|
boolean success = true;
|
|
|
|
// Revoke all firewall rules for the ip
|
|
try {
|
|
s_logger.debug("Revoking all " + Purpose.Firewall + "rules as a part of public IP id=" + ipId + " release...");
|
|
if (!_firewallMgr.revokeFirewallRulesForIp(ipId, userId, caller)) {
|
|
s_logger.warn("Unable to revoke all the firewall rules for ip id=" + ipId + " as a part of ip release");
|
|
success = false;
|
|
}
|
|
} catch (ResourceUnavailableException e) {
|
|
s_logger.warn("Unable to revoke all firewall rules for ip id=" + ipId + " as a part of ip release", e);
|
|
success = false;
|
|
}
|
|
|
|
// Revoke all PF/Static nat rules for the ip
|
|
try {
|
|
s_logger.debug("Revoking all " + Purpose.PortForwarding + "/" + Purpose.StaticNat + " rules as a part of public IP id=" + ipId + " release...");
|
|
if (!_rulesMgr.revokeAllPFAndStaticNatRulesForIp(ipId, userId, caller)) {
|
|
s_logger.warn("Unable to revoke all the port forwarding rules for ip id=" + ipId + " as a part of ip release");
|
|
success = false;
|
|
}
|
|
} catch (ResourceUnavailableException e) {
|
|
s_logger.warn("Unable to revoke all the port forwarding rules for ip id=" + ipId + " as a part of ip release", e);
|
|
success = false;
|
|
}
|
|
|
|
s_logger.debug("Revoking all " + Purpose.LoadBalancing + " rules as a part of public IP id=" + ipId + " release...");
|
|
if (!_lbMgr.removeAllLoadBalanacersForIp(ipId, caller, userId)) {
|
|
s_logger.warn("Unable to revoke all the load balancer rules for ip id=" + ipId + " as a part of ip release");
|
|
success = false;
|
|
}
|
|
|
|
// remote access vpn can be enabled only for static nat ip, so this part should never be executed under normal
|
|
// conditions
|
|
// only when ip address failed to be cleaned up as a part of account destroy and was marked as Releasing, this part of
|
|
// the code would be triggered
|
|
s_logger.debug("Cleaning up remote access vpns as a part of public IP id=" + ipId + " release...");
|
|
try {
|
|
_vpnMgr.destroyRemoteAccessVpnForIp(ipId, caller);
|
|
} catch (ResourceUnavailableException e) {
|
|
s_logger.warn("Unable to destroy remote access vpn for ip id=" + ipId + " as a part of ip release", e);
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public boolean disassociatePublicIpAddress(long addrId, long userId, Account caller) {
|
|
|
|
boolean success = true;
|
|
// Cleanup all ip address resources - PF/LB/Static nat rules
|
|
if (!cleanupIpResources(addrId, userId, caller)) {
|
|
success = false;
|
|
s_logger.warn("Failed to release resources for ip address id=" + addrId);
|
|
}
|
|
|
|
IPAddressVO ip = markIpAsUnavailable(addrId);
|
|
|
|
assert (ip != null) : "Unable to mark the ip address id=" + addrId + " as unavailable.";
|
|
if (ip == null) {
|
|
return true;
|
|
}
|
|
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Releasing ip id=" + addrId + "; sourceNat = " + ip.isSourceNat());
|
|
}
|
|
|
|
if (ip.getAssociatedWithNetworkId() != null) {
|
|
Network network = _networksDao.findById(ip.getAssociatedWithNetworkId());
|
|
try {
|
|
if (!applyIpAssociations(network, true)) {
|
|
s_logger.warn("Unable to apply ip address associations for " + network);
|
|
success = false;
|
|
}
|
|
} catch (ResourceUnavailableException e) {
|
|
throw new CloudRuntimeException("We should never get to here because we used true when applyIpAssociations", e);
|
|
}
|
|
} else {
|
|
if (ip.getState() == IpAddress.State.Releasing) {
|
|
_ipAddressDao.unassignIpAddress(ip.getId());
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
if (ip.isPortable()) {
|
|
releasePortableIpAddress(addrId);
|
|
}
|
|
s_logger.debug("Released a public ip id=" + addrId);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public boolean releasePortableIpAddress(long addrId) {
|
|
Transaction txn = Transaction.currentTxn();
|
|
GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange");
|
|
|
|
txn.start();
|
|
try {
|
|
portableIpLock.lock(5);
|
|
IPAddressVO ip = _ipAddressDao.findById(addrId);
|
|
|
|
// unassign portable IP
|
|
PortableIpVO portableIp = _portableIpDao.findByIpAddress(ip.getAddress().addr());
|
|
_portableIpDao.unassignIpAddress(portableIp.getId());
|
|
|
|
// removed the provisioned vlan
|
|
VlanVO vlan = _vlanDao.findById(ip.getVlanId());
|
|
_vlanDao.remove(vlan.getId());
|
|
|
|
// remove the provisioned public ip address
|
|
_ipAddressDao.remove(ip.getId());
|
|
|
|
txn.commit();
|
|
return true;
|
|
} finally {
|
|
portableIpLock.releaseRef();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem)
|
|
throws InsufficientAddressCapacityException {
|
|
return fetchNewPublicIp(dcId, podId, null, owner, type, networkId, false, true, requestedIp, isSystem, null);
|
|
}
|
|
|
|
@Override
|
|
public PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List<Long> vlanDbIds, Long networkId, String requestedIp, boolean isSystem)
|
|
throws InsufficientAddressCapacityException {
|
|
return fetchNewPublicIp(dcId, podId, vlanDbIds, owner, type, networkId, false, true, requestedIp, isSystem, null);
|
|
}
|
|
|
|
@DB
|
|
public PublicIp fetchNewPublicIp(long dcId, Long podId, List<Long> vlanDbIds, Account owner, VlanType vlanUse, Long guestNetworkId, boolean sourceNat, boolean assign,
|
|
String requestedIp, boolean isSystem, Long vpcId) throws InsufficientAddressCapacityException {
|
|
StringBuilder errorMessage = new StringBuilder("Unable to get ip adress in ");
|
|
boolean fetchFromDedicatedRange = false;
|
|
List<Long> dedicatedVlanDbIds = new ArrayList<Long>();
|
|
List<Long> nonDedicatedVlanDbIds = new ArrayList<Long>();
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
SearchCriteria<IPAddressVO> sc = null;
|
|
if (podId != null) {
|
|
sc = AssignIpAddressFromPodVlanSearch.create();
|
|
sc.setJoinParameters("podVlanMapSB", "podId", podId);
|
|
errorMessage.append(" pod id=" + podId);
|
|
} else {
|
|
sc = AssignIpAddressSearch.create();
|
|
errorMessage.append(" zone id=" + dcId);
|
|
}
|
|
|
|
// If owner has dedicated Public IP ranges, fetch IP from the dedicated range
|
|
// Otherwise fetch IP from the system pool
|
|
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByAccount(owner.getId());
|
|
for (AccountVlanMapVO map : maps) {
|
|
if (vlanDbIds == null || vlanDbIds.contains(map.getVlanDbId()))
|
|
dedicatedVlanDbIds.add(map.getVlanDbId());
|
|
}
|
|
List<VlanVO> nonDedicatedVlans = _vlanDao.listZoneWideNonDedicatedVlans(dcId);
|
|
for (VlanVO nonDedicatedVlan : nonDedicatedVlans) {
|
|
if (vlanDbIds == null || vlanDbIds.contains(nonDedicatedVlan.getId()))
|
|
nonDedicatedVlanDbIds.add(nonDedicatedVlan.getId());
|
|
}
|
|
if (dedicatedVlanDbIds != null && !dedicatedVlanDbIds.isEmpty()) {
|
|
fetchFromDedicatedRange = true;
|
|
sc.setParameters("vlanId", dedicatedVlanDbIds.toArray());
|
|
errorMessage.append(", vlanId id=" + dedicatedVlanDbIds.toArray());
|
|
} else if (nonDedicatedVlanDbIds != null && !nonDedicatedVlanDbIds.isEmpty()) {
|
|
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
|
|
errorMessage.append(", vlanId id=" + nonDedicatedVlanDbIds.toArray());
|
|
} else {
|
|
if (podId != null) {
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
|
|
ex.addProxyObject(ApiDBUtils.findPodById(podId).getUuid());
|
|
throw ex;
|
|
}
|
|
s_logger.warn(errorMessage.toString());
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", DataCenter.class, dcId);
|
|
ex.addProxyObject(ApiDBUtils.findZoneById(dcId).getUuid());
|
|
throw ex;
|
|
}
|
|
|
|
sc.setParameters("dc", dcId);
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, dcId);
|
|
|
|
// for direct network take ip addresses only from the vlans belonging to the network
|
|
if (vlanUse == VlanType.DirectAttached) {
|
|
sc.setJoinParameters("vlan", "networkId", guestNetworkId);
|
|
errorMessage.append(", network id=" + guestNetworkId);
|
|
}
|
|
sc.setJoinParameters("vlan", "type", vlanUse);
|
|
|
|
if (requestedIp != null) {
|
|
sc.addAnd("address", SearchCriteria.Op.EQ, requestedIp);
|
|
errorMessage.append(": requested ip " + requestedIp + " is not available");
|
|
}
|
|
|
|
Filter filter = new Filter(IPAddressVO.class, "vlanId", true, 0l, 1l);
|
|
|
|
List<IPAddressVO> addrs = _ipAddressDao.lockRows(sc, filter, true);
|
|
|
|
// If all the dedicated IPs of the owner are in use fetch an IP from the system pool
|
|
if (addrs.size() == 0 && fetchFromDedicatedRange) {
|
|
// Verify if account is allowed to acquire IPs from the system
|
|
boolean useSystemIps = UseSystemPublicIps.valueIn(owner.getId());
|
|
if (useSystemIps && nonDedicatedVlanDbIds != null && !nonDedicatedVlanDbIds.isEmpty()) {
|
|
fetchFromDedicatedRange = false;
|
|
sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray());
|
|
errorMessage.append(", vlanId id=" + nonDedicatedVlanDbIds.toArray());
|
|
addrs = _ipAddressDao.lockRows(sc, filter, true);
|
|
}
|
|
}
|
|
|
|
if (addrs.size() == 0) {
|
|
if (podId != null) {
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId);
|
|
// for now, we hardcode the table names, but we should ideally do a lookup for the tablename from the VO object.
|
|
ex.addProxyObject(ApiDBUtils.findPodById(podId).getUuid());
|
|
throw ex;
|
|
}
|
|
s_logger.warn(errorMessage.toString());
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", DataCenter.class, dcId);
|
|
ex.addProxyObject(ApiDBUtils.findZoneById(dcId).getUuid());
|
|
throw ex;
|
|
}
|
|
|
|
assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size();
|
|
|
|
if (!fetchFromDedicatedRange) {
|
|
// Check that the maximum number of public IPs for the given accountId will not be exceeded
|
|
try {
|
|
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.public_ip);
|
|
} catch (ResourceAllocationException ex) {
|
|
s_logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + owner);
|
|
throw new AccountLimitException("Maximum number of public IP addresses for account: " + owner.getAccountName() + " has been exceeded.");
|
|
}
|
|
}
|
|
|
|
IPAddressVO addr = addrs.get(0);
|
|
addr.setSourceNat(sourceNat);
|
|
addr.setAllocatedTime(new Date());
|
|
addr.setAllocatedInDomainId(owner.getDomainId());
|
|
addr.setAllocatedToAccountId(owner.getId());
|
|
addr.setSystem(isSystem);
|
|
|
|
if (assign) {
|
|
markPublicIpAsAllocated(addr);
|
|
} else {
|
|
addr.setState(IpAddress.State.Allocating);
|
|
}
|
|
addr.setState(assign ? IpAddress.State.Allocated : IpAddress.State.Allocating);
|
|
|
|
if (vlanUse != VlanType.DirectAttached) {
|
|
addr.setAssociatedWithNetworkId(guestNetworkId);
|
|
addr.setVpcId(vpcId);
|
|
}
|
|
|
|
_ipAddressDao.update(addr.getId(), addr);
|
|
|
|
txn.commit();
|
|
|
|
if (vlanUse == VlanType.VirtualNetwork) {
|
|
_firewallMgr.addSystemFirewallRules(addr, owner);
|
|
}
|
|
|
|
return PublicIp.createFromAddrAndVlan(addr, _vlanDao.findById(addr.getVlanId()));
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public void markPublicIpAsAllocated(IPAddressVO addr) {
|
|
|
|
assert (addr.getState() == IpAddress.State.Allocating || addr.getState() == IpAddress.State.Free) : "Unable to transition from state " + addr.getState() + " to " +
|
|
IpAddress.State.Allocated;
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
|
|
Account owner = _accountMgr.getAccount(addr.getAllocatedToAccountId());
|
|
|
|
txn.start();
|
|
addr.setState(IpAddress.State.Allocated);
|
|
_ipAddressDao.update(addr.getId(), addr);
|
|
|
|
// Save usage event
|
|
if (owner.getAccountId() != Account.ACCOUNT_ID_SYSTEM) {
|
|
VlanVO vlan = _vlanDao.findById(addr.getVlanId());
|
|
|
|
String guestType = vlan.getVlanType().toString();
|
|
|
|
if (!isIpDedicated(addr)) {
|
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN,
|
|
owner.getId(),
|
|
addr.getDataCenterId(),
|
|
addr.getId(),
|
|
addr.getAddress().toString(),
|
|
addr.isSourceNat(),
|
|
guestType,
|
|
addr.getSystem(),
|
|
addr.getClass().getName(),
|
|
addr.getUuid());
|
|
}
|
|
// don't increment resource count for direct and dedicated ip addresses
|
|
if (addr.getAssociatedWithNetworkId() != null && !isIpDedicated(addr)) {
|
|
_resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.public_ip);
|
|
}
|
|
}
|
|
|
|
txn.commit();
|
|
}
|
|
|
|
private boolean isIpDedicated(IPAddressVO addr) {
|
|
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByVlan(addr.getVlanId());
|
|
if (maps != null && !maps.isEmpty())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public PublicIp assignSourceNatIpAddressToGuestNetwork(Account owner, Network guestNetwork) throws InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
assert (guestNetwork.getTrafficType() != null) : "You're asking for a source nat but your network "
|
|
+ "can't participate in source nat. What do you have to say for yourself?";
|
|
long dcId = guestNetwork.getDataCenterId();
|
|
|
|
IPAddressVO sourceNatIp = getExistingSourceNatInNetwork(owner.getId(), guestNetwork.getId());
|
|
|
|
PublicIp ipToReturn = null;
|
|
if (sourceNatIp != null) {
|
|
ipToReturn = PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId()));
|
|
} else {
|
|
ipToReturn = assignDedicateIpAddress(owner, guestNetwork.getId(), null, dcId, true);
|
|
}
|
|
|
|
return ipToReturn;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public PublicIp assignDedicateIpAddress(Account owner, Long guestNtwkId, Long vpcId, long dcId, boolean isSourceNat) throws ConcurrentOperationException,
|
|
InsufficientAddressCapacityException {
|
|
|
|
long ownerId = owner.getId();
|
|
|
|
PublicIp ip = null;
|
|
Transaction txn = Transaction.currentTxn();
|
|
try {
|
|
txn.start();
|
|
|
|
owner = _accountDao.acquireInLockTable(ownerId);
|
|
|
|
if (owner == null) {
|
|
// this ownerId comes from owner or type Account. See the class "AccountVO" and the annotations in that class
|
|
// to get the table name and field name that is queried to fill this ownerid.
|
|
ConcurrentOperationException ex = new ConcurrentOperationException("Unable to lock account");
|
|
throw ex;
|
|
}
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("lock account " + ownerId + " is acquired");
|
|
}
|
|
|
|
ip = fetchNewPublicIp(dcId, null, null, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, false, null, false, vpcId);
|
|
IPAddressVO publicIp = ip.ip();
|
|
|
|
markPublicIpAsAllocated(publicIp);
|
|
_ipAddressDao.update(publicIp.getId(), publicIp);
|
|
|
|
txn.commit();
|
|
return ip;
|
|
} finally {
|
|
if (owner != null) {
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Releasing lock account " + ownerId);
|
|
}
|
|
|
|
_accountDao.releaseFromLockTable(ownerId);
|
|
}
|
|
if (ip == null) {
|
|
txn.rollback();
|
|
s_logger.error("Unable to get source nat ip address for account " + ownerId);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean applyIpAssociations(Network network, boolean continueOnError) throws ResourceUnavailableException {
|
|
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
|
|
boolean success = true;
|
|
|
|
// CloudStack will take a lazy approach to associate an acquired public IP to a network service provider as
|
|
// it will not know what service an acquired IP will be used for. An IP is actually associated with a provider when first
|
|
// rule is applied. Similarly when last rule on the acquired IP is revoked, IP is not associated with any provider
|
|
// but still be associated with the account. At this point just mark IP as allocated or released.
|
|
for (IPAddressVO addr : userIps) {
|
|
if (addr.getState() == IpAddress.State.Allocating) {
|
|
addr.setAssociatedWithNetworkId(network.getId());
|
|
markPublicIpAsAllocated(addr);
|
|
} else if (addr.getState() == IpAddress.State.Releasing) {
|
|
// Cleanup all the resources for ip address if there are any, and only then un-assign ip in the system
|
|
if (cleanupIpResources(addr.getId(), Account.ACCOUNT_ID_SYSTEM, _accountMgr.getSystemAccount())) {
|
|
_ipAddressDao.unassignIpAddress(addr.getId());
|
|
} else {
|
|
success = false;
|
|
s_logger.warn("Failed to release resources for ip address id=" + addr.getId());
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// CloudStack will take a lazy approach to associate an acquired public IP to a network service provider as
|
|
// it will not know what a acquired IP will be used for. An IP is actually associated with a provider when first
|
|
// rule is applied. Similarly when last rule on the acquired IP is revoked, IP is not associated with any provider
|
|
// but still be associated with the account. Its up to caller of this function to decide when to invoke IPAssociation
|
|
@Override
|
|
public boolean applyIpAssociations(Network network, boolean postApplyRules, boolean continueOnError, List<? extends PublicIpAddress> publicIps)
|
|
throws ResourceUnavailableException {
|
|
boolean success = true;
|
|
|
|
Map<PublicIpAddress, Set<Service>> ipToServices = _networkModel.getIpToServices(publicIps, postApplyRules, true);
|
|
Map<Provider, ArrayList<PublicIpAddress>> providerToIpList = _networkModel.getProviderToIpList(network, ipToServices);
|
|
|
|
for (Provider provider : providerToIpList.keySet()) {
|
|
try {
|
|
ArrayList<PublicIpAddress> ips = providerToIpList.get(provider);
|
|
if (ips == null || ips.isEmpty()) {
|
|
continue;
|
|
}
|
|
IpDeployer deployer = null;
|
|
NetworkElement element = _networkModel.getElementImplementingProvider(provider.getName());
|
|
if (!(element instanceof IpDeployingRequester)) {
|
|
throw new CloudRuntimeException("Element " + element + " is not a IpDeployingRequester!");
|
|
}
|
|
deployer = ((IpDeployingRequester)element).getIpDeployer(network);
|
|
if (deployer == null) {
|
|
throw new CloudRuntimeException("Fail to get ip deployer for element: " + element);
|
|
}
|
|
Set<Service> services = new HashSet<Service>();
|
|
for (PublicIpAddress ip : ips) {
|
|
if (!ipToServices.containsKey(ip)) {
|
|
continue;
|
|
}
|
|
services.addAll(ipToServices.get(ip));
|
|
}
|
|
deployer.applyIps(network, ips, services);
|
|
} catch (ResourceUnavailableException e) {
|
|
success = false;
|
|
if (!continueOnError) {
|
|
throw e;
|
|
} else {
|
|
s_logger.debug("Resource is not available: " + provider.getName(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerUserId, DataCenter zone) throws ConcurrentOperationException,
|
|
ResourceAllocationException, InsufficientAddressCapacityException {
|
|
|
|
VlanType vlanType = VlanType.VirtualNetwork;
|
|
boolean assign = false;
|
|
|
|
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
|
|
// zone is of type DataCenter. See DataCenterVO.java.
|
|
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation, " + "Zone is currently disabled");
|
|
ex.addProxyObject(zone.getUuid(), "zoneId");
|
|
throw ex;
|
|
}
|
|
|
|
PublicIp ip = null;
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
Account accountToLock = null;
|
|
try {
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Associate IP address called by the user " + callerUserId + " account " + ipOwner.getId());
|
|
}
|
|
accountToLock = _accountDao.acquireInLockTable(ipOwner.getId());
|
|
if (accountToLock == null) {
|
|
s_logger.warn("Unable to lock account: " + ipOwner.getId());
|
|
throw new ConcurrentOperationException("Unable to acquire account lock");
|
|
}
|
|
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Associate IP address lock acquired");
|
|
}
|
|
|
|
txn.start();
|
|
|
|
ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, isSystem, null);
|
|
|
|
if (ip == null) {
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zone.getId());
|
|
ex.addProxyObject(ApiDBUtils.findZoneById(zone.getId()).getUuid());
|
|
throw ex;
|
|
}
|
|
CallContext.current().setEventDetails("Ip Id: " + ip.getId());
|
|
Ip ipAddress = ip.getAddress();
|
|
|
|
s_logger.debug("Got " + ipAddress + " to assign for account " + ipOwner.getId() + " in zone " + zone.getId());
|
|
|
|
txn.commit();
|
|
} finally {
|
|
if (accountToLock != null) {
|
|
if (s_logger.isDebugEnabled()) {
|
|
s_logger.debug("Releasing lock account " + ipOwner);
|
|
}
|
|
_accountDao.releaseFromLockTable(ipOwner.getId());
|
|
s_logger.debug("Associate IP address lock released");
|
|
}
|
|
}
|
|
return ip;
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) throws ConcurrentOperationException, ResourceAllocationException,
|
|
InsufficientAddressCapacityException {
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange");
|
|
PortableIpVO allocatedPortableIp;
|
|
IPAddressVO ipaddr;
|
|
|
|
try {
|
|
portableIpLock.lock(5);
|
|
|
|
txn.start();
|
|
|
|
List<PortableIpVO> portableIpVOs = _portableIpDao.listByRegionIdAndState(1, PortableIp.State.Free);
|
|
if (portableIpVOs == null || portableIpVOs.isEmpty()) {
|
|
InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available portable IP addresses", Region.class, new Long(1));
|
|
throw ex;
|
|
}
|
|
|
|
// allocate first portable IP to the user
|
|
allocatedPortableIp = portableIpVOs.get(0);
|
|
allocatedPortableIp.setAllocatedTime(new Date());
|
|
allocatedPortableIp.setAllocatedToAccountId(ipOwner.getAccountId());
|
|
allocatedPortableIp.setAllocatedInDomainId(ipOwner.getDomainId());
|
|
allocatedPortableIp.setState(PortableIp.State.Allocated);
|
|
_portableIpDao.update(allocatedPortableIp.getId(), allocatedPortableIp);
|
|
|
|
// To make portable IP available as a zone level resource we need to emulate portable IP's (which are
|
|
// provisioned at region level) as public IP provisioned in a zone. user_ip_address and vlan combo give the
|
|
// identity of a public IP in zone. Create entry for portable ip in these tables.
|
|
|
|
// provision portable IP range VLAN into the zone
|
|
long physicalNetworkId = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(dcId, TrafficType.Public).getId();
|
|
Network network = _networkModel.getSystemNetworkByZoneAndTrafficType(dcId, TrafficType.Public);
|
|
String range = allocatedPortableIp.getAddress() + "-" + allocatedPortableIp.getAddress();
|
|
VlanVO vlan = new VlanVO(VlanType.VirtualNetwork,
|
|
allocatedPortableIp.getVlan(),
|
|
allocatedPortableIp.getGateway(),
|
|
allocatedPortableIp.getNetmask(),
|
|
dcId,
|
|
range,
|
|
network.getId(),
|
|
physicalNetworkId,
|
|
null,
|
|
null,
|
|
null);
|
|
vlan = _vlanDao.persist(vlan);
|
|
|
|
// provision the portable IP in to user_ip_address table
|
|
ipaddr = new IPAddressVO(new Ip(allocatedPortableIp.getAddress()), dcId, networkId, vpcID, physicalNetworkId, network.getId(), vlan.getId(), true);
|
|
ipaddr.setState(State.Allocated);
|
|
ipaddr.setAllocatedTime(new Date());
|
|
ipaddr.setAllocatedInDomainId(ipOwner.getDomainId());
|
|
ipaddr.setAllocatedToAccountId(ipOwner.getId());
|
|
ipaddr = _ipAddressDao.persist(ipaddr);
|
|
|
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_PORTABLE_IP_ASSIGN,
|
|
ipaddr.getId(),
|
|
ipaddr.getDataCenterId(),
|
|
ipaddr.getId(),
|
|
ipaddr.getAddress().toString(),
|
|
ipaddr.isSourceNat(),
|
|
null,
|
|
ipaddr.getSystem(),
|
|
ipaddr.getClass().getName(),
|
|
ipaddr.getUuid());
|
|
|
|
txn.commit();
|
|
|
|
} finally {
|
|
portableIpLock.unlock();
|
|
}
|
|
|
|
return ipaddr;
|
|
}
|
|
|
|
protected IPAddressVO getExistingSourceNatInNetwork(long ownerId, Long networkId) {
|
|
|
|
List<? extends IpAddress> addrs = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, networkId, true);
|
|
|
|
IPAddressVO sourceNatIp = null;
|
|
if (addrs.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
// Account already has ip addresses
|
|
for (IpAddress addr : addrs) {
|
|
if (addr.isSourceNat()) {
|
|
sourceNatIp = _ipAddressDao.findById(addr.getId());
|
|
return sourceNatIp;
|
|
}
|
|
}
|
|
|
|
assert (sourceNatIp != null) : "How do we get a bunch of ip addresses but none of them are source nat? " + "account=" + ownerId + "; networkId=" + networkId;
|
|
}
|
|
|
|
return sourceNatIp;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public IPAddressVO associateIPToGuestNetwork(long ipId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException,
|
|
InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
Account caller = CallContext.current().getCallingAccount();
|
|
Account owner = null;
|
|
|
|
IPAddressVO ipToAssoc = _ipAddressDao.findById(ipId);
|
|
if (ipToAssoc != null) {
|
|
Network network = _networksDao.findById(networkId);
|
|
if (network == null) {
|
|
throw new InvalidParameterValueException("Invalid network id is given");
|
|
}
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
|
|
if (zone.getNetworkType() == NetworkType.Advanced) {
|
|
if (network.getGuestType() == Network.GuestType.Shared) {
|
|
if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) {
|
|
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), AccessType.UseNetwork, false, network);
|
|
} else {
|
|
throw new InvalidParameterValueException("IP can be associated with guest network of 'shared' type only if "
|
|
+ "network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network");
|
|
}
|
|
}
|
|
} else {
|
|
_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;
|
|
}
|
|
|
|
if (ipToAssoc.getAssociatedWithNetworkId() != null) {
|
|
s_logger.debug("IP " + ipToAssoc + " is already assocaited with network id" + networkId);
|
|
return ipToAssoc;
|
|
}
|
|
|
|
Network network = _networksDao.findById(networkId);
|
|
if (network != null) {
|
|
_accountMgr.checkAccess(owner, AccessType.UseNetwork, false, network);
|
|
} else {
|
|
s_logger.debug("Unable to find ip address by id: " + ipId);
|
|
return null;
|
|
}
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
|
|
|
|
// allow associating IP addresses to guest network only
|
|
if (network.getTrafficType() != TrafficType.Guest) {
|
|
throw new InvalidParameterValueException("Ip address can be associated to the network with trafficType " + TrafficType.Guest);
|
|
}
|
|
|
|
// Check that network belongs to IP owner - skip this check
|
|
// - if zone is basic zone as there is just one guest network,
|
|
// - if shared network in Advanced zone
|
|
// - and it belongs to the system
|
|
if (network.getAccountId() != owner.getId()) {
|
|
if (zone.getNetworkType() != NetworkType.Basic && !(zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Shared)) {
|
|
throw new InvalidParameterValueException("The owner of the network is not the same as owner of the IP");
|
|
}
|
|
}
|
|
|
|
// In Advance zone only allow to do IP assoc
|
|
// - for Isolated networks with source nat service enabled
|
|
// - for shared networks with source nat service enabled
|
|
if (zone.getNetworkType() == NetworkType.Advanced && !(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat))) {
|
|
throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced + " ip address can be associated only to the network of guest type " +
|
|
GuestType.Isolated + " with the " + Service.SourceNat.getName() + " enabled");
|
|
}
|
|
|
|
NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
|
|
boolean sharedSourceNat = offering.getSharedSourceNat();
|
|
boolean isSourceNat = false;
|
|
if (!sharedSourceNat) {
|
|
if (getExistingSourceNatInNetwork(owner.getId(), networkId) == null) {
|
|
if (network.getGuestType() == GuestType.Isolated && network.getVpcId() == null && !ipToAssoc.isPortable()) {
|
|
isSourceNat = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
s_logger.debug("Associating ip " + ipToAssoc + " to network " + network);
|
|
|
|
IPAddressVO ip = _ipAddressDao.findById(ipId);
|
|
//update ip address with networkId
|
|
ip.setAssociatedWithNetworkId(networkId);
|
|
ip.setSourceNat(isSourceNat);
|
|
_ipAddressDao.update(ipId, ip);
|
|
|
|
boolean success = false;
|
|
try {
|
|
success = applyIpAssociations(network, false);
|
|
if (success) {
|
|
s_logger.debug("Successfully associated ip address " + ip.getAddress().addr() + " to network " + network);
|
|
} else {
|
|
s_logger.warn("Failed to associate ip address " + ip.getAddress().addr() + " to network " + network);
|
|
}
|
|
return ip;
|
|
} finally {
|
|
if (!success && releaseOnFailure) {
|
|
if (ip != null) {
|
|
try {
|
|
s_logger.warn("Failed to associate ip address, so releasing ip from the database " + ip);
|
|
_ipAddressDao.markAsUnavailable(ip.getId());
|
|
if (!applyIpAssociations(network, true)) {
|
|
// if fail to apply ip assciations again, unassign ip address without updating resource
|
|
// count and generating usage event as there is no need to keep it in the db
|
|
_ipAddressDao.unassignIpAddress(ip.getId());
|
|
}
|
|
} catch (Exception e) {
|
|
s_logger.warn("Unable to disassociate ip address for recovery", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected boolean isSharedNetworkOfferingWithServices(long networkOfferingId) {
|
|
NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId);
|
|
if ((networkOffering.getGuestType() == Network.GuestType.Shared) &&
|
|
(_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) ||
|
|
_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) ||
|
|
_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) ||
|
|
_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId,
|
|
Service.Lb))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException,
|
|
InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
return associateIPToGuestNetwork(ipAddrId, networkId, releaseOnFailure);
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public IPAddressVO disassociatePortableIPToGuestNetwork(long ipId, long networkId) throws ResourceAllocationException, ResourceUnavailableException,
|
|
InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
|
|
Account caller = CallContext.current().getCallingAccount();
|
|
Account owner = null;
|
|
|
|
Network network = _networksDao.findById(networkId);
|
|
if (network == null) {
|
|
throw new InvalidParameterValueException("Invalid network id is given");
|
|
}
|
|
|
|
IPAddressVO ipToAssoc = _ipAddressDao.findById(ipId);
|
|
if (ipToAssoc != null) {
|
|
|
|
if (ipToAssoc.getAssociatedWithNetworkId() == null) {
|
|
throw new InvalidParameterValueException("IP " + ipToAssoc + " is not associated with any network");
|
|
}
|
|
|
|
if (ipToAssoc.getAssociatedWithNetworkId() != network.getId()) {
|
|
throw new InvalidParameterValueException("IP " + ipToAssoc + " is not associated with network id" + networkId);
|
|
}
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
|
|
if (zone.getNetworkType() == NetworkType.Advanced) {
|
|
if (network.getGuestType() == Network.GuestType.Shared) {
|
|
assert (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()));
|
|
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), AccessType.UseNetwork, false, network);
|
|
}
|
|
} else {
|
|
_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;
|
|
}
|
|
|
|
DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId());
|
|
|
|
// Check that network belongs to IP owner - skip this check
|
|
// - if zone is basic zone as there is just one guest network,
|
|
// - if shared network in Advanced zone
|
|
// - and it belongs to the system
|
|
if (network.getAccountId() != owner.getId()) {
|
|
if (zone.getNetworkType() != NetworkType.Basic && !(zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Shared)) {
|
|
throw new InvalidParameterValueException("The owner of the network is not the same as owner of the IP");
|
|
}
|
|
}
|
|
|
|
// Check if IP has any services (rules) associated in the network
|
|
List<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
|
|
PublicIp publicIp = PublicIp.createFromAddrAndVlan(ipToAssoc, _vlanDao.findById(ipToAssoc.getVlanId()));
|
|
ipList.add(publicIp);
|
|
Map<PublicIpAddress, Set<Service>> ipToServices = _networkModel.getIpToServices(ipList, false, true);
|
|
if (ipToServices != null & !ipToServices.isEmpty()) {
|
|
Set<Service> services = ipToServices.get(publicIp);
|
|
if (services != null && !services.isEmpty()) {
|
|
throw new InvalidParameterValueException("IP " + ipToAssoc + " has services and rules associated in the network " + networkId);
|
|
}
|
|
}
|
|
|
|
IPAddressVO ip = _ipAddressDao.findById(ipId);
|
|
ip.setAssociatedWithNetworkId(null);
|
|
_ipAddressDao.update(ipId, ip);
|
|
|
|
try {
|
|
boolean success = applyIpAssociations(network, false);
|
|
if (success) {
|
|
s_logger.debug("Successfully associated ip address " + ip.getAddress().addr() + " to network " + network);
|
|
} else {
|
|
s_logger.warn("Failed to associate ip address " + ip.getAddress().addr() + " to network " + network);
|
|
}
|
|
return ip;
|
|
} finally {
|
|
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isPortableIpTransferableFromNetwork(long ipAddrId, long networkId) {
|
|
Network network = _networksDao.findById(networkId);
|
|
if (network == null) {
|
|
throw new InvalidParameterValueException("Invalid network id is given");
|
|
}
|
|
|
|
IPAddressVO ip = _ipAddressDao.findById(ipAddrId);
|
|
if (ip == null) {
|
|
throw new InvalidParameterValueException("Invalid network id is given");
|
|
}
|
|
|
|
// Check if IP has any services (rules) associated in the network
|
|
List<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
|
|
PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId()));
|
|
ipList.add(publicIp);
|
|
Map<PublicIpAddress, Set<Service>> ipToServices = _networkModel.getIpToServices(ipList, false, true);
|
|
if (ipToServices != null & !ipToServices.isEmpty()) {
|
|
Set<Service> ipServices = ipToServices.get(publicIp);
|
|
if (ipServices != null && !ipServices.isEmpty()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public void transferPortableIP(long ipAddrId, long currentNetworkId, long newNetworkId) throws ResourceAllocationException, ResourceUnavailableException,
|
|
InsufficientAddressCapacityException, ConcurrentOperationException {
|
|
|
|
Network srcNetwork = _networksDao.findById(currentNetworkId);
|
|
if (srcNetwork == null) {
|
|
throw new InvalidParameterValueException("Invalid source network id " + currentNetworkId + " is given");
|
|
}
|
|
|
|
Network dstNetwork = _networksDao.findById(newNetworkId);
|
|
if (dstNetwork == null) {
|
|
throw new InvalidParameterValueException("Invalid source network id " + newNetworkId + " is given");
|
|
}
|
|
|
|
IPAddressVO ip = _ipAddressDao.findById(ipAddrId);
|
|
if (ip == null) {
|
|
throw new InvalidParameterValueException("Invalid portable ip address id is given");
|
|
}
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
|
|
assert (isPortableIpTransferableFromNetwork(ipAddrId, currentNetworkId));
|
|
|
|
// disassociate portable IP with current network/VPC network
|
|
if (srcNetwork.getVpcId() != null) {
|
|
_vpcMgr.unassignIPFromVpcNetwork(ipAddrId, currentNetworkId);
|
|
} else {
|
|
disassociatePortableIPToGuestNetwork(ipAddrId, currentNetworkId);
|
|
}
|
|
|
|
// If portable IP need to be transferred across the zones, then mark the entry corresponding to portable ip
|
|
// in user_ip_address and vlan tables so as to emulate portable IP as provisioned in destination data center
|
|
if (srcNetwork.getDataCenterId() != dstNetwork.getDataCenterId()) {
|
|
txn.start();
|
|
|
|
long physicalNetworkId = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(dstNetwork.getDataCenterId(), TrafficType.Public).getId();
|
|
long publicNetworkId = _networkModel.getSystemNetworkByZoneAndTrafficType(dstNetwork.getDataCenterId(), TrafficType.Public).getId();
|
|
|
|
ip.setDataCenterId(dstNetwork.getDataCenterId());
|
|
ip.setPhysicalNetworkId(physicalNetworkId);
|
|
ip.setSourceNetworkId(publicNetworkId);
|
|
_ipAddressDao.update(ipAddrId, ip);
|
|
|
|
VlanVO vlan = _vlanDao.findById(ip.getVlanId());
|
|
vlan.setPhysicalNetworkId(physicalNetworkId);
|
|
vlan.setNetworkId(publicNetworkId);
|
|
vlan.setDataCenterId(dstNetwork.getDataCenterId());
|
|
_vlanDao.update(ip.getVlanId(), vlan);
|
|
|
|
txn.commit();
|
|
}
|
|
|
|
// associate portable IP with new network/VPC network
|
|
associatePortableIPToGuestNetwork(ipAddrId, newNetworkId, false);
|
|
|
|
txn.start();
|
|
|
|
if (dstNetwork.getVpcId() != null) {
|
|
ip.setVpcId(dstNetwork.getVpcId());
|
|
} else {
|
|
ip.setVpcId(null);
|
|
}
|
|
|
|
_ipAddressDao.update(ipAddrId, ip);
|
|
|
|
txn.commit();
|
|
|
|
// trigger an action event for the transfer of portable IP across the networks, so that external entities
|
|
// monitoring for this event can initiate the route advertisement for the availability of IP from the zoe
|
|
ActionEventUtils.onActionEvent(User.UID_SYSTEM,
|
|
Account.ACCOUNT_ID_SYSTEM,
|
|
Domain.ROOT_DOMAIN,
|
|
EventTypes.EVENT_PORTABLE_IP_TRANSFER,
|
|
"Portable IP associated is transferred from network " + currentNetworkId + " to " + newNetworkId);
|
|
}
|
|
|
|
protected List<? extends Network> getIsolatedNetworksWithSourceNATOwnedByAccountInZone(long zoneId, Account owner) {
|
|
|
|
return _networksDao.listSourceNATEnabledNetworks(owner.getId(), zoneId, Network.GuestType.Isolated);
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public boolean associateIpAddressListToAccount(long userId, long accountId, long zoneId, Long vlanId, Network guestNetwork) throws InsufficientCapacityException,
|
|
ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException {
|
|
Account owner = _accountMgr.getActiveAccountById(accountId);
|
|
boolean createNetwork = false;
|
|
|
|
if (guestNetwork != null && guestNetwork.getTrafficType() != TrafficType.Guest) {
|
|
throw new InvalidParameterValueException("Network " + guestNetwork + " is not of a type " + TrafficType.Guest);
|
|
}
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
|
|
if (guestNetwork == null) {
|
|
List<? extends Network> networks = getIsolatedNetworksWithSourceNATOwnedByAccountInZone(zoneId, owner);
|
|
if (networks.size() == 0) {
|
|
createNetwork = true;
|
|
} else if (networks.size() == 1) {
|
|
guestNetwork = networks.get(0);
|
|
} else {
|
|
throw new InvalidParameterValueException("Error, more than 1 Guest Isolated Networks with SourceNAT "
|
|
+ "service enabled found for this account, cannot assosiate the IP range, please provide the network ID");
|
|
}
|
|
}
|
|
|
|
// create new Virtual network (Isolated with SourceNAT) for the user if it doesn't exist
|
|
List<NetworkOfferingVO> requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false);
|
|
if (requiredOfferings.size() < 1) {
|
|
throw new CloudRuntimeException("Unable to find network offering with availability=" + Availability.Required +
|
|
" to automatically create the network as part of createVlanIpRange");
|
|
}
|
|
if (createNetwork) {
|
|
if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) {
|
|
long physicalNetworkId = _networkModel.findPhysicalNetworkId(zoneId, requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType());
|
|
// Validate physical network
|
|
PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId);
|
|
if (physicalNetwork == null) {
|
|
throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + requiredOfferings.get(0).getTags());
|
|
}
|
|
|
|
s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() +
|
|
" as a part of createVlanIpRange process");
|
|
guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(),
|
|
owner.getAccountName() + "-network",
|
|
owner.getAccountName() + "-network",
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
owner,
|
|
null,
|
|
physicalNetwork,
|
|
zoneId,
|
|
ACLType.Account,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
true,
|
|
null);
|
|
if (guestNetwork == null) {
|
|
s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId);
|
|
throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " +
|
|
"service enabled as a part of createVlanIpRange, for the account " + accountId + "in zone " + zoneId);
|
|
}
|
|
} else {
|
|
throw new CloudRuntimeException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled);
|
|
}
|
|
}
|
|
|
|
// Check if there is a source nat ip address for this account; if not - we have to allocate one
|
|
boolean allocateSourceNat = false;
|
|
List<IPAddressVO> sourceNat = _ipAddressDao.listByAssociatedNetwork(guestNetwork.getId(), true);
|
|
if (sourceNat.isEmpty()) {
|
|
allocateSourceNat = true;
|
|
}
|
|
|
|
// update all ips with a network id, mark them as allocated and update resourceCount/usage
|
|
List<IPAddressVO> ips = _ipAddressDao.listByVlanId(vlanId);
|
|
boolean isSourceNatAllocated = false;
|
|
for (IPAddressVO addr : ips) {
|
|
if (addr.getState() != State.Allocated) {
|
|
if (!isSourceNatAllocated && allocateSourceNat) {
|
|
addr.setSourceNat(true);
|
|
isSourceNatAllocated = true;
|
|
} else {
|
|
addr.setSourceNat(false);
|
|
}
|
|
addr.setAssociatedWithNetworkId(guestNetwork.getId());
|
|
addr.setVpcId(guestNetwork.getVpcId());
|
|
addr.setAllocatedTime(new Date());
|
|
addr.setAllocatedInDomainId(owner.getDomainId());
|
|
addr.setAllocatedToAccountId(owner.getId());
|
|
addr.setSystem(false);
|
|
addr.setState(IpAddress.State.Allocating);
|
|
markPublicIpAsAllocated(addr);
|
|
}
|
|
}
|
|
|
|
txn.commit();
|
|
|
|
// if the network offering has persistent set to true, implement the network
|
|
if (createNetwork && requiredOfferings.get(0).getIsPersistent()) {
|
|
DataCenter zone = _dcDao.findById(zoneId);
|
|
DeployDestination dest = new DeployDestination(zone, null, null, null);
|
|
Account callerAccount = CallContext.current().getCallingAccount();
|
|
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
|
|
Journal journal = new Journal.LogJournal("Implementing " + guestNetwork, s_logger);
|
|
ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), journal, callerUser, callerAccount);
|
|
s_logger.debug("Implementing network " + guestNetwork + " as a part of network provision for persistent network");
|
|
try {
|
|
Pair<? extends NetworkGuru, ? extends Network> implementedNetwork = _networkMgr.implementNetwork(guestNetwork.getId(), dest, context);
|
|
if (implementedNetwork.first() == null) {
|
|
s_logger.warn("Failed to implement the network " + guestNetwork);
|
|
}
|
|
guestNetwork = implementedNetwork.second();
|
|
} catch (Exception ex) {
|
|
s_logger.warn("Failed to implement network " + guestNetwork + " elements and resources as a part of" + " network provision due to ", ex);
|
|
CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id)"
|
|
+ " elements and resources as a part of network provision for persistent network");
|
|
e.addProxyObject(guestNetwork.getUuid(), "networkId");
|
|
throw e;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@DB
|
|
@Override
|
|
public IPAddressVO markIpAsUnavailable(long addrId) {
|
|
Transaction txn = Transaction.currentTxn();
|
|
|
|
IPAddressVO ip = _ipAddressDao.findById(addrId);
|
|
|
|
if (ip.getAllocatedToAccountId() == null && ip.getAllocatedTime() == null) {
|
|
s_logger.trace("Ip address id=" + addrId + " is already released");
|
|
return ip;
|
|
}
|
|
|
|
if (ip.getState() != State.Releasing) {
|
|
txn.start();
|
|
|
|
// don't decrement resource count for direct and dedicated ips
|
|
if (ip.getAssociatedWithNetworkId() != null && !isIpDedicated(ip)) {
|
|
_resourceLimitMgr.decrementResourceCount(_ipAddressDao.findById(addrId).getAllocatedToAccountId(), ResourceType.public_ip);
|
|
}
|
|
|
|
// Save usage event
|
|
if (ip.getAllocatedToAccountId() != null && ip.getAllocatedToAccountId() != Account.ACCOUNT_ID_SYSTEM) {
|
|
VlanVO vlan = _vlanDao.findById(ip.getVlanId());
|
|
|
|
String guestType = vlan.getVlanType().toString();
|
|
if (!isIpDedicated(ip)) {
|
|
String eventType = ip.isPortable() ? EventTypes.EVENT_PORTABLE_IP_RELEASE : EventTypes.EVENT_NET_IP_RELEASE;
|
|
UsageEventUtils.publishUsageEvent(eventType,
|
|
ip.getAllocatedToAccountId(),
|
|
ip.getDataCenterId(),
|
|
addrId,
|
|
ip.getAddress().addr(),
|
|
ip.isSourceNat(),
|
|
guestType,
|
|
ip.getSystem(),
|
|
ip.getClass().getName(),
|
|
ip.getUuid());
|
|
}
|
|
}
|
|
|
|
ip = _ipAddressDao.markAsUnavailable(addrId);
|
|
|
|
txn.commit();
|
|
}
|
|
|
|
return ip;
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public String acquireGuestIpAddress(Network network, String requestedIp) {
|
|
if (requestedIp != null && requestedIp.equals(network.getGateway())) {
|
|
s_logger.warn("Requested ip address " + requestedIp + " is used as a gateway address in network " + network);
|
|
return null;
|
|
}
|
|
|
|
Set<Long> availableIps = _networkModel.getAvailableIps(network, requestedIp);
|
|
|
|
if (availableIps == null || availableIps.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
Long[] array = availableIps.toArray(new Long[availableIps.size()]);
|
|
|
|
if (requestedIp != null) {
|
|
// check that requested ip has the same cidr
|
|
String[] cidr = network.getCidr().split("/");
|
|
boolean isSameCidr = NetUtils.sameSubnetCIDR(requestedIp, NetUtils.long2Ip(array[0]), Integer.parseInt(cidr[1]));
|
|
if (!isSameCidr) {
|
|
s_logger.warn("Requested ip address " + requestedIp + " doesn't belong to the network " + network + " cidr");
|
|
return null;
|
|
} else {
|
|
return requestedIp;
|
|
}
|
|
}
|
|
|
|
String result;
|
|
do {
|
|
result = NetUtils.long2Ip(array[_rand.nextInt(array.length)]);
|
|
} while (result.split("\\.")[3].equals("1"));
|
|
return result;
|
|
}
|
|
|
|
Random _rand = new Random(System.currentTimeMillis());
|
|
|
|
@Override
|
|
public boolean applyStaticNats(List<? extends StaticNat> staticNats, boolean continueOnError, boolean forRevoke) throws ResourceUnavailableException {
|
|
Network network = _networksDao.findById(staticNats.get(0).getNetworkId());
|
|
boolean success = true;
|
|
|
|
if (staticNats == null || staticNats.size() == 0) {
|
|
s_logger.debug("There are no static nat rules for the network elements");
|
|
return true;
|
|
}
|
|
|
|
// get the list of public ip's owned by the network
|
|
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
|
|
List<PublicIp> publicIps = new ArrayList<PublicIp>();
|
|
if (userIps != null && !userIps.isEmpty()) {
|
|
for (IPAddressVO userIp : userIps) {
|
|
PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
|
|
publicIps.add(publicIp);
|
|
}
|
|
}
|
|
|
|
// static NAT rules can not programmed unless IP is associated with source NAT service provider, so run IP
|
|
// association for the network so as to ensure IP is associated before applying rules
|
|
if (checkStaticNatIPAssocRequired(network, false, forRevoke, publicIps)) {
|
|
applyIpAssociations(network, false, continueOnError, publicIps);
|
|
}
|
|
|
|
// get provider
|
|
StaticNatServiceProvider element = _networkMgr.getStaticNatProviderForNetwork(network);
|
|
try {
|
|
success = element.applyStaticNats(network, staticNats);
|
|
} catch (ResourceUnavailableException e) {
|
|
if (!continueOnError) {
|
|
throw e;
|
|
}
|
|
s_logger.warn("Problems with " + element.getName() + " but pushing on", e);
|
|
success = false;
|
|
}
|
|
|
|
// For revoked static nat IP, set the vm_id to null, indicate it should be revoked
|
|
for (StaticNat staticNat : staticNats) {
|
|
if (staticNat.isForRevoke()) {
|
|
for (PublicIp publicIp : publicIps) {
|
|
if (publicIp.getId() == staticNat.getSourceIpAddressId()) {
|
|
publicIps.remove(publicIp);
|
|
IPAddressVO ip = _ipAddressDao.findByIdIncludingRemoved(staticNat.getSourceIpAddressId());
|
|
// ip can't be null, otherwise something wrong happened
|
|
ip.setAssociatedWithVmId(null);
|
|
publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId()));
|
|
publicIps.add(publicIp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the static NAT rules configured on public IP is revoked then, dis-associate IP with static NAT service provider
|
|
if (checkStaticNatIPAssocRequired(network, true, forRevoke, publicIps)) {
|
|
applyIpAssociations(network, true, continueOnError, publicIps);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// checks if there are any public IP assigned to network, that are marked for one-to-one NAT that
|
|
// needs to be associated/dis-associated with static-nat provider
|
|
boolean checkStaticNatIPAssocRequired(Network network, boolean postApplyRules, boolean forRevoke, List<PublicIp> publicIps) {
|
|
for (PublicIp ip : publicIps) {
|
|
if (ip.isOneToOneNat()) {
|
|
Long activeFwCount = null;
|
|
activeFwCount = _firewallDao.countRulesByIpIdAndState(ip.getId(), FirewallRule.State.Active);
|
|
|
|
if (!postApplyRules && !forRevoke) {
|
|
if (activeFwCount > 0) {
|
|
continue;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else if (postApplyRules && forRevoke) {
|
|
return true;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public IpAddress assignSystemIp(long networkId, Account owner, boolean forElasticLb, boolean forElasticIp) throws InsufficientAddressCapacityException {
|
|
Network guestNetwork = _networksDao.findById(networkId);
|
|
NetworkOffering off = _entityMgr.findById(NetworkOffering.class, guestNetwork.getNetworkOfferingId());
|
|
IpAddress ip = null;
|
|
if ((off.getElasticLb() && forElasticLb) || (off.getElasticIp() && forElasticIp)) {
|
|
|
|
try {
|
|
s_logger.debug("Allocating system IP address for load balancer rule...");
|
|
// allocate ip
|
|
ip = allocateIP(owner, true, guestNetwork.getDataCenterId());
|
|
// apply ip associations
|
|
ip = associateIPToGuestNetwork(ip.getId(), networkId, true);
|
|
;
|
|
} catch (ResourceAllocationException ex) {
|
|
throw new CloudRuntimeException("Failed to allocate system ip due to ", ex);
|
|
} catch (ConcurrentOperationException ex) {
|
|
throw new CloudRuntimeException("Failed to allocate system lb ip due to ", ex);
|
|
} catch (ResourceUnavailableException ex) {
|
|
throw new CloudRuntimeException("Failed to allocate system lb ip due to ", ex);
|
|
}
|
|
|
|
if (ip == null) {
|
|
throw new CloudRuntimeException("Failed to allocate system ip");
|
|
}
|
|
}
|
|
|
|
return ip;
|
|
}
|
|
|
|
@Override
|
|
public boolean handleSystemIpRelease(IpAddress ip) {
|
|
boolean success = true;
|
|
Long networkId = ip.getAssociatedWithNetworkId();
|
|
if (networkId != null) {
|
|
if (ip.getSystem()) {
|
|
CallContext ctx = CallContext.current();
|
|
if (!disassociatePublicIpAddress(ip.getId(), ctx.getCallingUserId(), ctx.getCallingAccount())) {
|
|
s_logger.warn("Unable to release system ip address id=" + ip.getId());
|
|
success = false;
|
|
} else {
|
|
s_logger.warn("Successfully released system ip address id=" + ip.getId());
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
@Override
|
|
@DB
|
|
public void allocateDirectIp(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Network network, String requestedIpv4, String requestedIpv6)
|
|
throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException {
|
|
//This method allocates direct ip for the Shared network in Advance zones
|
|
boolean ipv4 = false;
|
|
|
|
Transaction txn = Transaction.currentTxn();
|
|
txn.start();
|
|
|
|
if (network.getGateway() != null) {
|
|
if (nic.getIp4Address() == null) {
|
|
ipv4 = true;
|
|
PublicIp ip = null;
|
|
|
|
//Get ip address from the placeholder and don't allocate a new one
|
|
if (requestedIpv4 != null && vm.getType() == VirtualMachine.Type.DomainRouter) {
|
|
Nic placeholderNic = _networkModel.getPlaceholderNicForRouter(network, null);
|
|
if (placeholderNic != null) {
|
|
IPAddressVO userIp = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), placeholderNic.getIp4Address());
|
|
ip = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
|
|
s_logger.debug("Nic got an ip address " + placeholderNic.getIp4Address() + " stored in placeholder nic for the network " + network);
|
|
}
|
|
}
|
|
|
|
if (ip == null) {
|
|
ip = assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.DirectAttached, network.getId(), requestedIpv4, false);
|
|
}
|
|
|
|
nic.setIp4Address(ip.getAddress().toString());
|
|
nic.setGateway(ip.getGateway());
|
|
nic.setNetmask(ip.getNetmask());
|
|
nic.setIsolationUri(IsolationType.Vlan.toUri(ip.getVlanTag()));
|
|
//nic.setBroadcastType(BroadcastDomainType.Vlan);
|
|
//nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag()));
|
|
nic.setBroadcastType(network.getBroadcastDomainType());
|
|
nic.setBroadcastUri(network.getBroadcastUri());
|
|
nic.setFormat(AddressFormat.Ip4);
|
|
nic.setReservationId(String.valueOf(ip.getVlanTag()));
|
|
nic.setMacAddress(ip.getMacAddress());
|
|
}
|
|
nic.setDns1(dc.getDns1());
|
|
nic.setDns2(dc.getDns2());
|
|
}
|
|
|
|
//FIXME - get ipv6 address from the placeholder if it's stored there
|
|
if (network.getIp6Gateway() != null) {
|
|
if (nic.getIp6Address() == null) {
|
|
UserIpv6Address ip = _ipv6Mgr.assignDirectIp6Address(dc.getId(), vm.getOwner(), network.getId(), requestedIpv6);
|
|
Vlan vlan = _vlanDao.findById(ip.getVlanId());
|
|
nic.setIp6Address(ip.getAddress().toString());
|
|
nic.setIp6Gateway(vlan.getIp6Gateway());
|
|
nic.setIp6Cidr(vlan.getIp6Cidr());
|
|
if (ipv4) {
|
|
nic.setFormat(AddressFormat.DualStack);
|
|
} else {
|
|
nic.setIsolationUri(IsolationType.Vlan.toUri(vlan.getVlanTag()));
|
|
nic.setBroadcastType(BroadcastDomainType.Vlan);
|
|
nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan.getVlanTag()));
|
|
nic.setFormat(AddressFormat.Ip6);
|
|
nic.setReservationId(String.valueOf(vlan.getVlanTag()));
|
|
nic.setMacAddress(ip.getMacAddress());
|
|
}
|
|
}
|
|
nic.setIp6Dns1(dc.getIp6Dns1());
|
|
nic.setIp6Dns2(dc.getIp6Dns2());
|
|
}
|
|
|
|
txn.commit();
|
|
}
|
|
|
|
@Override
|
|
public int getRuleCountForIp(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state) {
|
|
List<FirewallRuleVO> rules = _firewallDao.listByIpAndPurposeWithState(addressId, purpose, state);
|
|
if (rules == null) {
|
|
return 0;
|
|
}
|
|
return rules.size();
|
|
}
|
|
|
|
@Override
|
|
public String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod, Account owner, String requestedIp) throws InsufficientAddressCapacityException {
|
|
PublicIp ip = assignPublicIpAddress(dc.getId(), null, owner, VlanType.DirectAttached, networkId, requestedIp, false);
|
|
if (ip == null) {
|
|
s_logger.debug("There is no free public ip address");
|
|
return null;
|
|
}
|
|
Ip ipAddr = ip.getAddress();
|
|
return ipAddr.addr();
|
|
}
|
|
|
|
@Override
|
|
public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) throws InsufficientAddressCapacityException {
|
|
String ipaddr = null;
|
|
Account caller = CallContext.current().getCallingAccount();
|
|
// check permissions
|
|
Network network = _networksDao.findById(networkId);
|
|
|
|
_accountMgr.checkAccess(caller, null, false, network);
|
|
|
|
ipaddr = acquireGuestIpAddress(network, requestedIp);
|
|
return ipaddr;
|
|
}
|
|
|
|
@Override
|
|
public String getConfigComponentName() {
|
|
return IpAddressManager.class.getSimpleName();
|
|
}
|
|
|
|
@Override
|
|
public ConfigKey<?>[] getConfigKeys() {
|
|
return new ConfigKey<?>[] {UseSystemPublicIps};
|
|
}
|
|
}
|