| // 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.rules; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.inject.Inject; |
| |
| import com.cloud.exception.UnsupportedServiceException; |
| import com.cloud.network.element.UserDataServiceProvider; |
| import com.cloud.storage.VMTemplateVO; |
| import com.cloud.storage.dao.VMTemplateDao; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.VirtualMachineProfile; |
| import com.cloud.vm.VirtualMachineProfileImpl; |
| import org.apache.cloudstack.acl.SecurityChecker; |
| import org.apache.cloudstack.api.command.user.firewall.ListPortForwardingRulesCmd; |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; |
| |
| import com.cloud.configuration.ConfigurationManager; |
| import com.cloud.domain.dao.DomainDao; |
| import com.cloud.event.ActionEvent; |
| import com.cloud.event.EventTypes; |
| import com.cloud.event.UsageEventUtils; |
| import com.cloud.event.dao.EventDao; |
| import com.cloud.event.dao.UsageEventDao; |
| import com.cloud.exception.InsufficientAddressCapacityException; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.NetworkRuleConflictException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.network.IpAddress; |
| import com.cloud.network.IpAddressManager; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.Service; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.dao.FirewallRulesCidrsDao; |
| import com.cloud.network.dao.FirewallRulesDao; |
| import com.cloud.network.dao.IPAddressDao; |
| import com.cloud.network.dao.IPAddressVO; |
| import com.cloud.network.dao.LoadBalancerVMMapDao; |
| import com.cloud.network.dao.LoadBalancerVMMapVO; |
| import com.cloud.network.rules.FirewallRule.FirewallRuleType; |
| import com.cloud.network.rules.FirewallRule.Purpose; |
| import com.cloud.network.rules.FirewallRule.State; |
| import com.cloud.network.rules.dao.PortForwardingRulesDao; |
| import com.cloud.network.vpc.VpcManager; |
| import com.cloud.network.vpc.VpcService; |
| import com.cloud.offering.NetworkOffering; |
| 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.DomainManager; |
| import com.cloud.uservm.UserVm; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.Ternary; |
| 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.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.TransactionCallbackNoReturn; |
| import com.cloud.utils.db.TransactionCallbackWithException; |
| import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; |
| import com.cloud.utils.db.TransactionStatus; |
| 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.NicSecondaryIp; |
| import com.cloud.vm.UserVmVO; |
| import com.cloud.vm.VMInstanceVO; |
| import com.cloud.vm.VirtualMachine; |
| import com.cloud.vm.VirtualMachine.Type; |
| import com.cloud.vm.dao.NicDao; |
| import com.cloud.vm.dao.NicSecondaryIpDao; |
| import com.cloud.vm.dao.NicSecondaryIpVO; |
| import com.cloud.vm.dao.UserVmDao; |
| import com.cloud.vm.dao.VMInstanceDao; |
| |
| public class RulesManagerImpl extends ManagerBase implements RulesManager, RulesService { |
| |
| @Inject |
| IpAddressManager _ipAddrMgr; |
| @Inject |
| EntityManager _entityMgr; |
| @Inject |
| PortForwardingRulesDao _portForwardingDao; |
| @Inject |
| FirewallRulesCidrsDao _firewallCidrsDao; |
| @Inject |
| FirewallRulesDao _firewallDao; |
| @Inject |
| IPAddressDao _ipAddressDao; |
| @Inject |
| UserVmDao _vmDao; |
| @Inject |
| VMInstanceDao _vmInstanceDao; |
| @Inject |
| AccountManager _accountMgr; |
| @Inject |
| NetworkOrchestrationService _networkMgr; |
| @Inject |
| NetworkModel _networkModel; |
| @Inject |
| EventDao _eventDao; |
| @Inject |
| UsageEventDao _usageEventDao; |
| @Inject |
| DomainDao _domainDao; |
| @Inject |
| FirewallManager _firewallMgr; |
| @Inject |
| DomainManager _domainMgr; |
| @Inject |
| ConfigurationManager _configMgr; |
| @Inject |
| NicDao _nicDao; |
| @Inject |
| ResourceTagDao _resourceTagDao; |
| @Inject |
| VpcManager _vpcMgr; |
| @Inject |
| NicSecondaryIpDao _nicSecondaryDao; |
| @Inject |
| LoadBalancerVMMapDao _loadBalancerVMMapDao; |
| @Inject |
| VpcService _vpcSvc; |
| @Inject |
| VMTemplateDao _templateDao; |
| |
| protected void checkIpAndUserVm(IpAddress ipAddress, UserVm userVm, Account caller, Boolean ignoreVmState) { |
| if (ipAddress == null || ipAddress.getAllocatedTime() == null || ipAddress.getAllocatedToAccountId() == null) { |
| throw new InvalidParameterValueException("Unable to create ip forwarding rule on address " + ipAddress + ", invalid IP address specified."); |
| } |
| |
| if (userVm == null) { |
| return; |
| } |
| |
| if (userVm.getState() == VirtualMachine.State.Destroyed || userVm.getState() == VirtualMachine.State.Expunging) { |
| if (!ignoreVmState) { |
| throw new InvalidParameterValueException("Invalid user vm: " + userVm.getId()); |
| } |
| } |
| |
| _accountMgr.checkAccess(caller, null, false, ipAddress, userVm); |
| |
| // validate that userVM is in the same availability zone as the IP address |
| if (ipAddress.getDataCenterId() != userVm.getDataCenterId()) { |
| //make an exception for portable IP |
| if (!ipAddress.isPortable()) { |
| throw new InvalidParameterValueException("Unable to create ip forwarding rule, IP address " + ipAddress + |
| " is not in the same availability zone as virtual machine " + userVm.toString()); |
| } |
| } |
| |
| } |
| |
| @Override |
| public void checkRuleAndUserVm(FirewallRule rule, UserVm userVm, Account caller) { |
| if (userVm == null || rule == null) { |
| return; |
| } |
| |
| _accountMgr.checkAccess(caller, null, false, rule, userVm); |
| |
| if (userVm.getState() == VirtualMachine.State.Destroyed || userVm.getState() == VirtualMachine.State.Expunging) { |
| throw new InvalidParameterValueException("Invalid user vm: " + userVm.getId()); |
| } |
| } |
| |
| @Override |
| @DB |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_ADD, eventDescription = "creating forwarding rule", create = true) |
| public PortForwardingRule createPortForwardingRule(final PortForwardingRule rule, final Long vmId, Ip vmIp, final boolean openFirewall, final Boolean forDisplay) |
| throws NetworkRuleConflictException { |
| CallContext ctx = CallContext.current(); |
| final Account caller = ctx.getCallingAccount(); |
| |
| final Long ipAddrId = rule.getSourceIpAddressId(); |
| |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipAddrId); |
| |
| // Validate ip address |
| if (ipAddress == null) { |
| throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + " doesn't exist in the system"); |
| } else if (ipAddress.isOneToOneNat()) { |
| throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + " has static nat enabled"); |
| } |
| |
| final Long networkId = rule.getNetworkId(); |
| Network network = _networkModel.getNetwork(networkId); |
| //associate ip address to network (if needed) |
| boolean performedIpAssoc = false; |
| Nic guestNic; |
| if (ipAddress.getAssociatedWithNetworkId() == null) { |
| boolean assignToVpcNtwk = network.getVpcId() != null && ipAddress.getVpcId() != null && ipAddress.getVpcId().longValue() == network.getVpcId(); |
| if (assignToVpcNtwk) { |
| _networkModel.checkIpForService(ipAddress, Service.PortForwarding, networkId); |
| |
| logger.debug("The ip is not associated with the VPC network id=" + networkId + ", so assigning"); |
| try { |
| ipAddress = _ipAddrMgr.associateIPToGuestNetwork(ipAddrId, networkId, false); |
| performedIpAssoc = true; |
| } catch (Exception ex) { |
| throw new CloudRuntimeException("Failed to associate ip to VPC network as " + "a part of port forwarding rule creation"); |
| } |
| } |
| } else { |
| _networkModel.checkIpForService(ipAddress, Service.PortForwarding, null); |
| } |
| |
| if (ipAddress.getAssociatedWithNetworkId() == null) { |
| throw new InvalidParameterValueException("Ip address " + ipAddress + " is not assigned to the network " + network); |
| } |
| |
| try { |
| _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.PortForwarding, |
| FirewallRuleType.User, networkId, rule.getTrafficType()); |
| |
| final Long accountId = ipAddress.getAllocatedToAccountId(); |
| final Long domainId = ipAddress.getAllocatedInDomainId(); |
| |
| // start port can't be bigger than end port |
| if (rule.getDestinationPortStart() > rule.getDestinationPortEnd()) { |
| throw new InvalidParameterValueException("Start port can't be bigger than end port"); |
| } |
| |
| // check that the port ranges are of equal size |
| if ((rule.getDestinationPortEnd() - rule.getDestinationPortStart()) != (rule.getSourcePortEnd() - rule.getSourcePortStart())) { |
| throw new InvalidParameterValueException("Source port and destination port ranges should be of equal sizes."); |
| } |
| |
| // validate user VM exists |
| UserVm vm = _vmDao.findById(vmId); |
| if (vm == null) { |
| throw new InvalidParameterValueException("Unable to create port forwarding rule on address " + ipAddress + ", invalid virtual machine id specified (" + |
| vmId + ")."); |
| } else if (vm.getState() == VirtualMachine.State.Destroyed || vm.getState() == VirtualMachine.State.Expunging) { |
| throw new InvalidParameterValueException("Invalid user vm: " + vm.getId()); |
| } |
| |
| // Verify that vm has nic in the network |
| Ip dstIp = rule.getDestinationIpAddress(); |
| guestNic = _networkModel.getNicInNetwork(vmId, networkId); |
| if (guestNic == null || guestNic.getIPv4Address() == null) { |
| throw new InvalidParameterValueException("Vm doesn't belong to network associated with ipAddress"); |
| } else { |
| dstIp = new Ip(guestNic.getIPv4Address()); |
| } |
| |
| if (vmIp != null) { |
| //vm ip is passed so it can be primary or secondary ip addreess. |
| if (!dstIp.equals(vmIp)) { |
| //the vm ip is secondary ip to the nic. |
| // is vmIp is secondary ip or not |
| NicSecondaryIp secondaryIp = _nicSecondaryDao.findByIp4AddressAndNicId(vmIp.toString(), guestNic.getId()); |
| if (secondaryIp == null) { |
| throw new InvalidParameterValueException("IP Address is not in the VM nic's network "); |
| } |
| dstIp = vmIp; |
| } |
| } |
| |
| //if start port and end port are passed in, and they are not equal to each other, perform the validation |
| boolean validatePortRange = false; |
| if (rule.getSourcePortStart().intValue() != rule.getSourcePortEnd().intValue() || rule.getDestinationPortStart() != rule.getDestinationPortEnd()) { |
| validatePortRange = true; |
| } |
| |
| if (validatePortRange) { |
| //source start port and source dest port should be the same. The same applies to dest ports |
| if (rule.getSourcePortStart().intValue() != rule.getDestinationPortStart()) { |
| throw new InvalidParameterValueException("Private port start should be equal to public port start"); |
| } |
| |
| if (rule.getSourcePortEnd().intValue() != rule.getDestinationPortEnd()) { |
| throw new InvalidParameterValueException("Private port end should be equal to public port end"); |
| } |
| } |
| |
| final Ip dstIpFinal = dstIp; |
| final IPAddressVO ipAddressFinal = ipAddress; |
| return Transaction.execute(new TransactionCallbackWithException<PortForwardingRuleVO, NetworkRuleConflictException>() { |
| @Override |
| public PortForwardingRuleVO doInTransaction(TransactionStatus status) throws NetworkRuleConflictException { |
| PortForwardingRuleVO newRule = |
| new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), dstIpFinal, |
| rule.getDestinationPortStart(), rule.getDestinationPortEnd(), rule.getProtocol().toLowerCase(), networkId, accountId, domainId, vmId); |
| |
| if (forDisplay != null) { |
| newRule.setDisplay(forDisplay); |
| } |
| newRule = _portForwardingDao.persist(newRule); |
| |
| // create firewallRule for 0.0.0.0/0 cidr |
| if (openFirewall) { |
| _firewallMgr.createRuleForAllCidrs(ipAddrId, caller, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), null, null, |
| newRule.getId(), networkId); |
| } |
| |
| try { |
| _firewallMgr.detectRulesConflict(newRule); |
| if (!_firewallDao.setStateToAdd(newRule)) { |
| throw new CloudRuntimeException("Unable to update the state to add for " + newRule); |
| } |
| CallContext.current().setEventDetails("Rule Id: " + newRule.getId()); |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(), ipAddressFinal.getDataCenterId(), newRule.getId(), null, |
| PortForwardingRule.class.getName(), newRule.getUuid()); |
| return newRule; |
| } catch (Exception e) { |
| if (newRule != null) { |
| // no need to apply the rule as it wasn't programmed on the backend yet |
| _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false); |
| removePFRule(newRule); |
| } |
| |
| if (e instanceof NetworkRuleConflictException) { |
| throw (NetworkRuleConflictException)e; |
| } |
| |
| throw new CloudRuntimeException("Unable to add rule for the ip id=" + ipAddrId, e); |
| } |
| } |
| }); |
| |
| } finally { |
| // release ip address if ipassoc was perfored |
| if (performedIpAssoc) { |
| //if the rule is the last one for the ip address assigned to VPC, unassign it from the network |
| IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); |
| _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); |
| } |
| } |
| } |
| |
| @Override |
| @DB |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_ADD, eventDescription = "creating static nat rule", create = true) |
| public StaticNatRule createStaticNatRule(final StaticNatRule rule, final boolean openFirewall) throws NetworkRuleConflictException { |
| final Account caller = CallContext.current().getCallingAccount(); |
| |
| final Long ipAddrId = rule.getSourceIpAddressId(); |
| |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipAddrId); |
| |
| // Validate ip address |
| if (ipAddress == null) { |
| throw new InvalidParameterValueException("Unable to create static nat rule; ip id=" + ipAddrId + " doesn't exist in the system"); |
| } else if (ipAddress.isSourceNat() || !ipAddress.isOneToOneNat() || ipAddress.getAssociatedWithVmId() == null) { |
| throw new NetworkRuleConflictException("Can't do static nat on ip address: " + ipAddress.getAddress()); |
| } |
| |
| _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.StaticNat, |
| FirewallRuleType.User, null, rule.getTrafficType()); |
| |
| final Long networkId = ipAddress.getAssociatedWithNetworkId(); |
| final Long accountId = ipAddress.getAllocatedToAccountId(); |
| final Long domainId = ipAddress.getAllocatedInDomainId(); |
| |
| _networkModel.checkIpForService(ipAddress, Service.StaticNat, null); |
| |
| Network network = _networkModel.getNetwork(networkId); |
| NetworkOffering off = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); |
| if (off.isElasticIp()) { |
| throw new InvalidParameterValueException("Can't create ip forwarding rules for the network where elasticIP service is enabled"); |
| } |
| |
| //String dstIp = _networkModel.getIpInNetwork(ipAddress.getAssociatedWithVmId(), networkId); |
| final String dstIp = ipAddress.getVmIp(); |
| return Transaction.execute(new TransactionCallbackWithException<StaticNatRule, NetworkRuleConflictException>() { |
| @Override |
| public StaticNatRule doInTransaction(TransactionStatus status) throws NetworkRuleConflictException { |
| |
| FirewallRuleVO newRule = |
| new FirewallRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol().toLowerCase(), |
| networkId, accountId, domainId, rule.getPurpose(), null, null, null, null, null); |
| |
| newRule = _firewallDao.persist(newRule); |
| |
| // create firewallRule for 0.0.0.0/0 cidr |
| if (openFirewall) { |
| _firewallMgr.createRuleForAllCidrs(ipAddrId, caller, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), null, null, |
| newRule.getId(), networkId); |
| } |
| |
| try { |
| _firewallMgr.detectRulesConflict(newRule); |
| if (!_firewallDao.setStateToAdd(newRule)) { |
| throw new CloudRuntimeException("Unable to update the state to add for " + newRule); |
| } |
| CallContext.current().setEventDetails("Rule Id: " + newRule.getId()); |
| UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_RULE_ADD, newRule.getAccountId(), 0, newRule.getId(), null, FirewallRule.class.getName(), |
| newRule.getUuid()); |
| |
| StaticNatRule staticNatRule = new StaticNatRuleImpl(newRule, dstIp); |
| |
| return staticNatRule; |
| } catch (Exception e) { |
| if (newRule != null) { |
| // no need to apply the rule as it wasn't programmed on the backend yet |
| _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false); |
| _firewallMgr.removeRule(newRule); |
| } |
| |
| if (e instanceof NetworkRuleConflictException) { |
| throw (NetworkRuleConflictException)e; |
| } |
| throw new CloudRuntimeException("Unable to add static nat rule for the ip id=" + newRule.getSourceIpAddressId(), e); |
| } |
| } |
| }); |
| |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_ENABLE_STATIC_NAT, eventDescription = "enabling static nat") |
| public boolean enableStaticNat(long ipId, long vmId, long networkId, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException { |
| return enableStaticNat(ipId, vmId, networkId, false, vmGuestIp); |
| } |
| |
| private boolean enableStaticNat(long ipId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) throws NetworkRuleConflictException, |
| ResourceUnavailableException { |
| CallContext ctx = CallContext.current(); |
| Account caller = ctx.getCallingAccount(); |
| CallContext.current().setEventDetails("Ip Id: " + ipId); |
| |
| // Verify input parameters |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipId); |
| if (ipAddress == null) { |
| throw new InvalidParameterValueException("Unable to find ip address by id " + ipId); |
| } |
| |
| // Verify input parameters |
| boolean performedIpAssoc = false; |
| boolean isOneToOneNat = ipAddress.isOneToOneNat(); |
| Long associatedWithVmId = ipAddress.getAssociatedWithVmId(); |
| Nic guestNic; |
| NicSecondaryIpVO nicSecIp = null; |
| String dstIp = null; |
| |
| try { |
| Network network = _networkModel.getNetwork(networkId); |
| if (network == null) { |
| throw new InvalidParameterValueException("Unable to find network by id"); |
| } |
| |
| // Check that vm has a nic in the network |
| guestNic = _networkModel.getNicInNetwork(vmId, networkId); |
| if (guestNic == null) { |
| throw new InvalidParameterValueException("Vm doesn't belong to the network with specified id"); |
| } |
| dstIp = guestNic.getIPv4Address(); |
| |
| if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.StaticNat)) { |
| throw new InvalidParameterValueException("Unable to create static nat rule; StaticNat service is not " + "supported in network with specified id"); |
| } |
| |
| if (!isSystemVm) { |
| UserVmVO vm = _vmDao.findById(vmId); |
| if (vm == null) { |
| throw new InvalidParameterValueException("Can't enable static nat for the address id=" + ipId + ", invalid virtual machine id specified (" + vmId + |
| ")."); |
| } |
| //associate ip address to network (if needed) |
| if (ipAddress.getAssociatedWithNetworkId() == null) { |
| boolean assignToVpcNtwk = network.getVpcId() != null && ipAddress.getVpcId() != null && ipAddress.getVpcId().longValue() == network.getVpcId(); |
| if (assignToVpcNtwk) { |
| _networkModel.checkIpForService(ipAddress, Service.StaticNat, networkId); |
| |
| logger.debug("The ip is not associated with the VPC network id=" + networkId + ", so assigning"); |
| try { |
| ipAddress = _ipAddrMgr.associateIPToGuestNetwork(ipId, networkId, false); |
| performedIpAssoc = true; |
| } catch (Exception ex) { |
| logger.warn("Failed to associate ip id=" + ipId + " to VPC network id=" + networkId + " as " + "a part of enable static nat"); |
| return false; |
| } |
| } else if (ipAddress.isPortable()) { |
| logger.info("Portable IP " + ipAddress.getUuid() + " is not associated with the network yet " + " so associate IP with the network " + |
| networkId); |
| try { |
| // check if StaticNat service is enabled in the network |
| _networkModel.checkIpForService(ipAddress, Service.StaticNat, networkId); |
| |
| // associate portable IP to vpc, if network is part of VPC |
| if (network.getVpcId() != null) { |
| _vpcSvc.associateIPToVpc(ipId, network.getVpcId()); |
| } |
| |
| // associate portable IP with guest network |
| ipAddress = _ipAddrMgr.associatePortableIPToGuestNetwork(ipId, networkId, false); |
| } catch (Exception e) { |
| logger.warn("Failed to associate portable id=" + ipId + " to network id=" + networkId + " as " + "a part of enable static nat"); |
| return false; |
| } |
| } |
| } else if (ipAddress.getAssociatedWithNetworkId() != networkId) { |
| if (ipAddress.isPortable()) { |
| // check if destination network has StaticNat service enabled |
| _networkModel.checkIpForService(ipAddress, Service.StaticNat, networkId); |
| |
| // check if portable IP can be transferred across the networks |
| if (_ipAddrMgr.isPortableIpTransferableFromNetwork(ipId, ipAddress.getAssociatedWithNetworkId())) { |
| try { |
| // transfer the portable IP and refresh IP details |
| _ipAddrMgr.transferPortableIP(ipId, ipAddress.getAssociatedWithNetworkId(), networkId); |
| ipAddress = _ipAddressDao.findById(ipId); |
| } catch (Exception e) { |
| logger.warn("Failed to associate portable id=" + ipId + " to network id=" + networkId + " as " + "a part of enable static nat"); |
| return false; |
| } |
| } else { |
| throw new InvalidParameterValueException("Portable IP: " + ipId + " has associated services " + "in network " + |
| ipAddress.getAssociatedWithNetworkId() + " so can not be transferred to " + " network " + networkId); |
| } |
| } else { |
| throw new InvalidParameterValueException("Invalid network Id=" + networkId + ". IP is associated with" + |
| " a different network than passed network id"); |
| } |
| } else { |
| _networkModel.checkIpForService(ipAddress, Service.StaticNat, null); |
| } |
| |
| if (ipAddress.getAssociatedWithNetworkId() == null) { |
| throw new InvalidParameterValueException("Ip address " + ipAddress + " is not assigned to the network " + network); |
| } |
| |
| // Check permissions |
| if (ipAddress.getSystem()) { |
| // when system is enabling static NAT on system IP's (for EIP) ignore VM state |
| checkIpAndUserVm(ipAddress, vm, caller, true); |
| } else { |
| checkIpAndUserVm(ipAddress, vm, caller, false); |
| } |
| Account vmOwner = _accountMgr.getAccount(vm.getAccountId()); |
| _accountMgr.checkAccess(vmOwner, SecurityChecker.AccessType.UseEntry, false, network); |
| |
| //is static nat is for vm secondary ip |
| //dstIp = guestNic.getIp4Address(); |
| if (vmGuestIp != null) { |
| //dstIp = guestNic.getIp4Address(); |
| |
| if (!dstIp.equals(vmGuestIp)) { |
| //check whether the secondary ip set to the vm or not |
| boolean secondaryIpSet = _networkMgr.isSecondaryIpSetForNic(guestNic.getId()); |
| if (!secondaryIpSet) { |
| throw new InvalidParameterValueException("VM ip " + vmGuestIp + " address not belongs to the vm"); |
| } |
| //check the ip belongs to the vm or not |
| nicSecIp = _nicSecondaryDao.findByIp4AddressAndNicId(vmGuestIp, guestNic.getId()); |
| if (nicSecIp == null) { |
| throw new InvalidParameterValueException("VM ip " + vmGuestIp + " address not belongs to the vm"); |
| } |
| dstIp = nicSecIp.getIp4Address(); |
| // Set public ip column with the vm ip |
| } |
| } |
| |
| // Verify ip address parameter |
| // checking vm id is not sufficient, check for the vm ip |
| isIpReadyForStaticNat(vmId, ipAddress, dstIp, caller, ctx.getCallingUserId()); |
| } |
| |
| ipAddress.setOneToOneNat(true); |
| ipAddress.setAssociatedWithVmId(vmId); |
| |
| ipAddress.setVmIp(dstIp); |
| if (_ipAddressDao.update(ipAddress.getId(), ipAddress)) { |
| // enable static nat on the backend |
| logger.trace("Enabling static nat for ip address " + ipAddress + " and vm id=" + vmId + " on the backend"); |
| if (applyStaticNatForIp(ipId, false, caller, false)) { |
| applyUserDataIfNeeded(vmId, network, guestNic); |
| performedIpAssoc = false; // ignor unassignIPFromVpcNetwork in finally block |
| return true; |
| } else { |
| logger.warn("Failed to enable static nat rule for ip address " + ipId + " on the backend"); |
| ipAddress.setOneToOneNat(isOneToOneNat); |
| ipAddress.setAssociatedWithVmId(associatedWithVmId); |
| ipAddress.setVmIp(null); |
| _ipAddressDao.update(ipAddress.getId(), ipAddress); |
| } |
| } else { |
| logger.warn("Failed to update ip address " + ipAddress + " in the DB as a part of enableStaticNat"); |
| |
| } |
| } finally { |
| if (performedIpAssoc) { |
| //if the rule is the last one for the ip address assigned to VPC, unassign it from the network |
| IpAddress ip = _ipAddressDao.findById(ipAddress.getId()); |
| _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), networkId); |
| } |
| } |
| return false; |
| } |
| |
| protected void applyUserDataIfNeeded(long vmId, Network network, Nic guestNic) throws ResourceUnavailableException { |
| UserDataServiceProvider element = null; |
| try { |
| element = _networkModel.getUserDataUpdateProvider(network); |
| } catch (UnsupportedServiceException ex) { |
| logger.info(String.format("%s is not supported by network %s, skipping.", Service.UserData.getName(), network)); |
| return; |
| } |
| if (element == null) { |
| logger.error("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); |
| } else { |
| UserVmVO vm = _vmDao.findById(vmId); |
| try { |
| VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); |
| NicProfile nicProfile = new NicProfile(guestNic, network, null, null, null, |
| _networkModel.isSecurityGroupSupportedInNetwork(network), |
| _networkModel.getNetworkTag(template.getHypervisorType(), network)); |
| VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); |
| if (!element.saveUserData(network, nicProfile, vmProfile)) { |
| logger.error("Failed to update userdata for vm " + vm + " and nic " + guestNic); |
| } |
| } catch (Exception e) { |
| logger.error("Failed to update userdata for vm " + vm + " and nic " + guestNic + " due to " + e.getMessage(), e); |
| } |
| } |
| } |
| |
| protected void isIpReadyForStaticNat(long vmId, IPAddressVO ipAddress, String vmIp, Account caller, long callerUserId) throws NetworkRuleConflictException, |
| ResourceUnavailableException { |
| if (ipAddress.isSourceNat()) { |
| throw new InvalidParameterValueException("Can't enable static, ip address " + ipAddress + " is a sourceNat ip address"); |
| } |
| |
| if (!ipAddress.isOneToOneNat()) { // Dont allow to enable static nat if PF/LB rules exist for the IP |
| List<FirewallRuleVO> portForwardingRules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipAddress.getId(), Purpose.PortForwarding); |
| if (portForwardingRules != null && !portForwardingRules.isEmpty()) { |
| throw new NetworkRuleConflictException("Failed to enable static nat for the ip address " + ipAddress + " as it already has PortForwarding rules assigned"); |
| } |
| |
| List<FirewallRuleVO> loadBalancingRules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipAddress.getId(), Purpose.LoadBalancing); |
| if (loadBalancingRules != null && !loadBalancingRules.isEmpty()) { |
| throw new NetworkRuleConflictException("Failed to enable static nat for the ip address " + ipAddress + " as it already has LoadBalancing rules assigned"); |
| } |
| } else if (ipAddress.getAssociatedWithVmId() != null && ipAddress.getAssociatedWithVmId().longValue() != vmId) { |
| throw new NetworkRuleConflictException("Failed to enable static for the ip address " + ipAddress + " and vm id=" + vmId + |
| " as it's already assigned to antoher vm"); |
| } |
| |
| //check whether the vm ip is already associated with any public ip address |
| IPAddressVO oldIP = _ipAddressDao.findByAssociatedVmIdAndVmIp(vmId, vmIp); |
| |
| if (oldIP != null) { |
| // If elasticIP functionality is supported in the network, we always have to disable static nat on the old |
| // ip in order to re-enable it on the new one |
| Long networkId = oldIP.getAssociatedWithNetworkId(); |
| VMInstanceVO vm = _vmInstanceDao.findById(vmId); |
| |
| boolean reassignStaticNat = false; |
| if (networkId != null) { |
| Network guestNetwork = _networkModel.getNetwork(networkId); |
| NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, guestNetwork.getNetworkOfferingId()); |
| if (offering.isElasticIp()) { |
| reassignStaticNat = true; |
| } |
| } |
| |
| // If there is public ip address already associated with the vm, throw an exception |
| if (!reassignStaticNat) { |
| throw new InvalidParameterValueException("Failed to enable static nat on the ip " + |
| ipAddress.getAddress()+" with Id " +ipAddress.getUuid()+" as the vm " +vm.getInstanceName() + " with Id " + |
| vm.getUuid() +" is already associated with another public ip " + oldIP.getAddress() +" with id "+ |
| oldIP.getUuid()); |
| } |
| // unassign old static nat rule |
| logger.debug("Disassociating static nat for ip " + oldIP); |
| if (!disableStaticNat(oldIP.getId(), caller, callerUserId, true)) { |
| throw new CloudRuntimeException("Failed to disable old static nat rule for vm "+ vm.getInstanceName() + |
| " with id "+vm.getUuid() +" and public ip " + oldIP); |
| } |
| } |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_DELETE, eventDescription = "revoking forwarding rule", async = true) |
| public boolean revokePortForwardingRule(long ruleId, boolean apply) { |
| CallContext ctx = CallContext.current(); |
| Account caller = ctx.getCallingAccount(); |
| |
| PortForwardingRuleVO rule = _portForwardingDao.findById(ruleId); |
| if (rule == null) { |
| throw new InvalidParameterValueException("Unable to find " + ruleId); |
| } |
| |
| _accountMgr.checkAccess(caller, null, true, rule); |
| |
| if (!revokePortForwardingRuleInternal(ruleId, caller, ctx.getCallingUserId(), apply)) { |
| throw new CloudRuntimeException("Failed to delete port forwarding rule"); |
| } |
| return true; |
| } |
| |
| private boolean revokePortForwardingRuleInternal(long ruleId, Account caller, long userId, boolean apply) { |
| PortForwardingRuleVO rule = _portForwardingDao.findById(ruleId); |
| |
| _firewallMgr.revokeRule(rule, caller, userId, true); |
| |
| boolean success = false; |
| |
| if (apply) { |
| success = applyPortForwardingRules(rule.getSourceIpAddressId(), _ipAddrMgr.RulesContinueOnError.value(), caller); |
| } else { |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_DELETE, eventDescription = "revoking forwarding rule", async = true) |
| public boolean revokeStaticNatRule(long ruleId, boolean apply) { |
| CallContext ctx = CallContext.current(); |
| Account caller = ctx.getCallingAccount(); |
| |
| FirewallRuleVO rule = _firewallDao.findById(ruleId); |
| if (rule == null) { |
| throw new InvalidParameterValueException("Unable to find " + ruleId); |
| } |
| |
| _accountMgr.checkAccess(caller, null, true, rule); |
| |
| if (!revokeStaticNatRuleInternal(ruleId, caller, ctx.getCallingUserId(), apply)) { |
| throw new CloudRuntimeException("Failed to revoke forwarding rule"); |
| } |
| return true; |
| } |
| |
| private boolean revokeStaticNatRuleInternal(long ruleId, Account caller, long userId, boolean apply) { |
| FirewallRuleVO rule = _firewallDao.findById(ruleId); |
| |
| _firewallMgr.revokeRule(rule, caller, userId, true); |
| |
| boolean success = false; |
| |
| if (apply) { |
| success = applyStaticNatRulesForIp(rule.getSourceIpAddressId(), _ipAddrMgr.RulesContinueOnError.value(), caller, true); |
| } else { |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public boolean revokePortForwardingRulesForVm(long vmId) { |
| boolean success = true; |
| UserVmVO vm = _vmDao.findByIdIncludingRemoved(vmId); |
| if (vm == null) { |
| return false; |
| } |
| |
| List<PortForwardingRuleVO> rules = _portForwardingDao.listByVm(vmId); |
| Set<Long> ipsToReprogram = new HashSet<Long>(); |
| |
| if (rules == null || rules.isEmpty()) { |
| logger.debug("No port forwarding rules are found for vm id=" + vmId); |
| return true; |
| } |
| |
| for (PortForwardingRuleVO rule : rules) { |
| // Mark port forwarding rule as Revoked, but don't revoke it yet (apply=false) |
| revokePortForwardingRuleInternal(rule.getId(), _accountMgr.getSystemAccount(), Account.ACCOUNT_ID_SYSTEM, false); |
| ipsToReprogram.add(rule.getSourceIpAddressId()); |
| } |
| |
| // apply rules for all ip addresses |
| for (Long ipId : ipsToReprogram) { |
| logger.debug("Applying port forwarding rules for ip address id=" + ipId + " as a part of vm expunge"); |
| if (!applyPortForwardingRules(ipId, _ipAddrMgr.RulesContinueOnError.value(), _accountMgr.getSystemAccount())) { |
| logger.warn("Failed to apply port forwarding rules for ip id=" + ipId); |
| success = false; |
| } |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public Pair<List<? extends PortForwardingRule>, Integer> listPortForwardingRules(ListPortForwardingRulesCmd cmd) { |
| Long ipId = cmd.getIpAddressId(); |
| Long id = cmd.getId(); |
| Map<String, String> tags = cmd.getTags(); |
| Long networkId = cmd.getNetworkId(); |
| Boolean display = cmd.getDisplay(); |
| |
| Account caller = CallContext.current().getCallingAccount(); |
| List<Long> permittedAccounts = new ArrayList<Long>(); |
| |
| if (ipId != null) { |
| IPAddressVO ipAddressVO = _ipAddressDao.findById(ipId); |
| if (ipAddressVO == null || !ipAddressVO.readyToUse()) { |
| throw new InvalidParameterValueException("Ip address id=" + ipId + " not ready for port forwarding rules yet"); |
| } |
| _accountMgr.checkAccess(caller, null, true, ipAddressVO); |
| } |
| |
| Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null); |
| _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); |
| Long domainId = domainIdRecursiveListProject.first(); |
| Boolean isRecursive = domainIdRecursiveListProject.second(); |
| ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); |
| |
| Filter filter = new Filter(PortForwardingRuleVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal()); |
| SearchBuilder<PortForwardingRuleVO> sb = _portForwardingDao.createSearchBuilder(); |
| _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| sb.and("id", sb.entity().getId(), Op.EQ); |
| sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ); |
| sb.and("purpose", sb.entity().getPurpose(), Op.EQ); |
| sb.and("networkId", sb.entity().getNetworkId(), Op.EQ); |
| sb.and("display", sb.entity().isDisplay(), Op.EQ); |
| |
| if (tags != null && !tags.isEmpty()) { |
| 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); |
| } |
| |
| SearchCriteria<PortForwardingRuleVO> sc = sb.create(); |
| _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| if (id != null) { |
| sc.setParameters("id", id); |
| } |
| |
| if (display != null) { |
| sc.setParameters("display", display); |
| } |
| |
| if (tags != null && !tags.isEmpty()) { |
| int count = 0; |
| sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.PortForwardingRule.toString()); |
| for (String key : tags.keySet()) { |
| sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); |
| sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); |
| count++; |
| } |
| } |
| |
| if (ipId != null) { |
| sc.setParameters("ip", ipId); |
| } |
| |
| if (networkId != null) { |
| sc.setParameters("networkId", networkId); |
| } |
| |
| sc.setParameters("purpose", Purpose.PortForwarding); |
| |
| Pair<List<PortForwardingRuleVO>, Integer> result = _portForwardingDao.searchAndCount(sc, filter); |
| return new Pair<List<? extends PortForwardingRule>, Integer>(result.first(), result.second()); |
| } |
| |
| protected boolean applyPortForwardingRules(long ipId, boolean continueOnError, Account caller) { |
| List<PortForwardingRuleVO> rules = _portForwardingDao.listForApplication(ipId); |
| |
| if (rules.size() == 0) { |
| logger.debug("There are no port forwarding rules to apply for ip id=" + ipId); |
| return true; |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()])); |
| } |
| |
| try { |
| if (!_firewallMgr.applyRules(rules, continueOnError, true)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to apply port forwarding rules for ip due to ", ex); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| protected boolean applyStaticNatRulesForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { |
| List<? extends FirewallRule> rules = _firewallDao.listByIpAndPurpose(sourceIpId, Purpose.StaticNat); |
| List<StaticNatRule> staticNatRules = new ArrayList<StaticNatRule>(); |
| |
| if (rules.size() == 0) { |
| logger.debug("There are no static nat rules to apply for ip id=" + sourceIpId); |
| return true; |
| } |
| |
| for (FirewallRule rule : rules) { |
| staticNatRules.add(buildStaticNatRule(rule, forRevoke)); |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, staticNatRules.toArray(new StaticNatRule[staticNatRules.size()])); |
| } |
| |
| try { |
| if (!_firewallMgr.applyRules(staticNatRules, continueOnError, true)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to apply static nat rules for ip due to ", ex); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyPortForwardingRulesForNetwork(long networkId, boolean continueOnError, Account caller) { |
| List<PortForwardingRuleVO> rules = listByNetworkId(networkId); |
| if (rules.size() == 0) { |
| logger.debug("There are no port forwarding rules to apply for network id=" + networkId); |
| return true; |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, rules.toArray(new PortForwardingRuleVO[rules.size()])); |
| } |
| |
| try { |
| if (!_firewallMgr.applyRules(rules, continueOnError, true)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to apply port forwarding rules for network due to ", ex); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyStaticNatRulesForNetwork(long networkId, boolean continueOnError, Account caller) { |
| List<FirewallRuleVO> rules = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.StaticNat); |
| List<StaticNatRule> staticNatRules = new ArrayList<StaticNatRule>(); |
| |
| if (rules.size() == 0) { |
| logger.debug("There are no static nat rules to apply for network id=" + networkId); |
| return true; |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, rules.toArray(new FirewallRule[rules.size()])); |
| } |
| |
| for (FirewallRuleVO rule : rules) { |
| staticNatRules.add(buildStaticNatRule(rule, false)); |
| } |
| |
| try { |
| if (!_firewallMgr.applyRules(staticNatRules, continueOnError, true)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to apply static nat rules for network due to ", ex); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyStaticNatsForNetwork(long networkId, boolean continueOnError, Account caller) { |
| List<IPAddressVO> ips = _ipAddressDao.listStaticNatPublicIps(networkId); |
| if (ips.isEmpty()) { |
| logger.debug("There are no static nat to apply for network id=" + networkId); |
| return true; |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, ips.toArray(new IPAddressVO[ips.size()])); |
| } |
| |
| List<StaticNat> staticNats = new ArrayList<StaticNat>(); |
| for (IPAddressVO ip : ips) { |
| // Get nic IP4 address |
| //String dstIp = _networkModel.getIpInNetwork(ip.getAssociatedWithVmId(), networkId); |
| StaticNatImpl staticNat = new StaticNatImpl(ip.getAllocatedToAccountId(), ip.getAllocatedInDomainId(), networkId, ip.getId(), ip.getVmIp(), false); |
| staticNats.add(staticNat); |
| } |
| |
| try { |
| if (!_ipAddrMgr.applyStaticNats(staticNats, continueOnError, false)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to create static nat for network due to ", ex); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public Pair<List<? extends FirewallRule>, Integer> searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId, |
| Long projectId, boolean isRecursive, boolean listAll) { |
| Account caller = CallContext.current().getCallingAccount(); |
| List<Long> permittedAccounts = new ArrayList<Long>(); |
| |
| if (ipId != null) { |
| IPAddressVO ipAddressVO = _ipAddressDao.findById(ipId); |
| if (ipAddressVO == null || !ipAddressVO.readyToUse()) { |
| throw new InvalidParameterValueException("Ip address id=" + ipId + " not ready for port forwarding rules yet"); |
| } |
| _accountMgr.checkAccess(caller, null, true, ipAddressVO); |
| } |
| |
| 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(); |
| ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); |
| |
| Filter filter = new Filter(PortForwardingRuleVO.class, "id", false, start, size); |
| SearchBuilder<FirewallRuleVO> sb = _firewallDao.createSearchBuilder(); |
| _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| |
| sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ); |
| sb.and("purpose", sb.entity().getPurpose(), Op.EQ); |
| sb.and("id", sb.entity().getId(), Op.EQ); |
| |
| if (vmId != null) { |
| SearchBuilder<IPAddressVO> ipSearch = _ipAddressDao.createSearchBuilder(); |
| ipSearch.and("associatedWithVmId", ipSearch.entity().getAssociatedWithVmId(), Op.EQ); |
| sb.join("ipSearch", ipSearch, sb.entity().getSourceIpAddressId(), ipSearch.entity().getId(), JoinBuilder.JoinType.INNER); |
| } |
| |
| SearchCriteria<FirewallRuleVO> sc = sb.create(); |
| _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); |
| sc.setParameters("purpose", Purpose.StaticNat); |
| |
| if (id != null) { |
| sc.setParameters("id", id); |
| } |
| |
| if (ipId != null) { |
| sc.setParameters("ip", ipId); |
| } |
| |
| if (vmId != null) { |
| sc.setJoinParameters("ipSearch", "associatedWithVmId", vmId); |
| } |
| |
| Pair<List<FirewallRuleVO>, Integer> result = _firewallDao.searchAndCount(sc, filter); |
| return new Pair<List<? extends FirewallRule>, Integer>(result.first(), result.second()); |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_ADD, eventDescription = "applying port forwarding rule", async = true) |
| public boolean applyPortForwardingRules(long ipId, Account caller) throws ResourceUnavailableException { |
| if (!applyPortForwardingRules(ipId, false, caller)) { |
| throw new CloudRuntimeException("Failed to apply port forwarding rule"); |
| } |
| return true; |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_ADD, eventDescription = "applying static nat rule", async = true) |
| public boolean applyStaticNatRules(long ipId, Account caller) throws ResourceUnavailableException { |
| if (!applyStaticNatRulesForIp(ipId, false, caller, false)) { |
| throw new CloudRuntimeException("Failed to apply static nat rule"); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean revokeAllPFAndStaticNatRulesForIp(long ipId, long userId, Account caller) throws ResourceUnavailableException { |
| List<FirewallRule> rules = new ArrayList<FirewallRule>(); |
| |
| List<PortForwardingRuleVO> pfRules = _portForwardingDao.listByIpAndNotRevoked(ipId); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Releasing " + pfRules.size() + " port forwarding rules for ip id=" + ipId); |
| } |
| |
| for (PortForwardingRuleVO rule : pfRules) { |
| // Mark all PF rules as Revoke, but don't revoke them yet |
| revokePortForwardingRuleInternal(rule.getId(), caller, userId, false); |
| } |
| |
| List<FirewallRuleVO> staticNatRules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.StaticNat); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Releasing " + staticNatRules.size() + " static nat rules for ip id=" + ipId); |
| } |
| |
| for (FirewallRuleVO rule : staticNatRules) { |
| // Mark all static nat rules as Revoke, but don't revoke them yet |
| revokeStaticNatRuleInternal(rule.getId(), caller, userId, false); |
| } |
| |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipId); |
| Long vmId = ipAddress.getAssociatedWithVmId(); |
| Long networkId = ipAddress.getAssociatedWithNetworkId(); |
| |
| boolean success = true; |
| |
| // revoke all port forwarding rules |
| success = success && applyPortForwardingRules(ipId, _ipAddrMgr.RulesContinueOnError.value(), caller); |
| |
| // revoke all static nat rules |
| success = success && applyStaticNatRulesForIp(ipId, _ipAddrMgr.RulesContinueOnError.value(), caller, true); |
| |
| // revoke static nat for the ip address |
| if (vmId != null && networkId != null) { |
| Network guestNetwork = _networkModel.getNetwork(networkId); |
| Nic guestNic = _networkModel.getNicInNetwork(vmId, guestNetwork.getId()); |
| if (applyStaticNatForIp(ipId, false, caller, true)) { |
| if (ipAddress.getState() == IpAddress.State.Releasing) { |
| applyUserDataIfNeeded(vmId, guestNetwork, guestNic); |
| } |
| } else { |
| success = false; |
| } |
| } |
| |
| // Now we check again in case more rules have been inserted. |
| rules.addAll(_portForwardingDao.listByIpAndNotRevoked(ipId)); |
| rules.addAll(_firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.StaticNat)); |
| |
| if (logger.isDebugEnabled() && success) { |
| logger.debug("Successfully released rules for ip id=" + ipId + " and # of rules now = " + rules.size()); |
| } |
| |
| return (rules.size() == 0 && success); |
| } |
| |
| @Override |
| public boolean revokeAllPFStaticNatRulesForNetwork(long networkId, long userId, Account caller) throws ResourceUnavailableException { |
| List<FirewallRule> rules = new ArrayList<FirewallRule>(); |
| |
| List<PortForwardingRuleVO> pfRules = _portForwardingDao.listByNetwork(networkId); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Releasing " + pfRules.size() + " port forwarding rules for network id=" + networkId); |
| } |
| |
| List<FirewallRuleVO> staticNatRules = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.StaticNat); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Releasing " + staticNatRules.size() + " static nat rules for network id=" + networkId); |
| } |
| |
| // Mark all pf rules (Active and non-Active) to be revoked, but don't revoke it yet - pass apply=false |
| for (PortForwardingRuleVO rule : pfRules) { |
| revokePortForwardingRuleInternal(rule.getId(), caller, userId, false); |
| } |
| |
| // Mark all static nat rules (Active and non-Active) to be revoked, but don't revoke it yet - pass apply=false |
| for (FirewallRuleVO rule : staticNatRules) { |
| revokeStaticNatRuleInternal(rule.getId(), caller, userId, false); |
| } |
| |
| boolean success = true; |
| // revoke all PF rules for the network |
| success = success && applyPortForwardingRulesForNetwork(networkId, true, caller); |
| success = success && applyPortForwardingRulesForNetwork(networkId, _ipAddrMgr.RulesContinueOnError.value(), caller); |
| |
| // revoke all static nat rules for the network |
| success = success && applyStaticNatRulesForNetwork(networkId, true, caller); |
| success = success && applyStaticNatRulesForNetwork(networkId, _ipAddrMgr.RulesContinueOnError.value(), caller); |
| |
| // Now we check again in case more rules have been inserted. |
| rules.addAll(_portForwardingDao.listByNetworkAndNotRevoked(networkId)); |
| rules.addAll(_firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.StaticNat)); |
| |
| if (logger.isDebugEnabled()) { |
| logger.debug("Successfully released rules for network id=" + networkId + " and # of rules now = " + rules.size()); |
| } |
| |
| return success && rules.size() == 0; |
| } |
| |
| @Override |
| @DB |
| public FirewallRuleVO[] reservePorts(final IpAddress ip, final String protocol, final FirewallRule.Purpose purpose, final boolean openFirewall, final Account caller, |
| final int... ports) throws NetworkRuleConflictException { |
| final FirewallRuleVO[] rules = new FirewallRuleVO[ports.length]; |
| |
| Transaction.execute(new TransactionCallbackWithExceptionNoReturn<NetworkRuleConflictException>() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) throws NetworkRuleConflictException { |
| for (int i = 0; i < ports.length; i++) { |
| |
| rules[i] = |
| new FirewallRuleVO(null, ip.getId(), ports[i], protocol, ip.getAssociatedWithNetworkId(), ip.getAllocatedToAccountId(), |
| ip.getAllocatedInDomainId(), purpose, null, null, null, null); |
| rules[i] = _firewallDao.persist(rules[i]); |
| |
| if (openFirewall) { |
| _firewallMgr.createRuleForAllCidrs(ip.getId(), caller, ports[i], ports[i], protocol, null, null, rules[i].getId(), |
| ip.getAssociatedWithNetworkId()); |
| } |
| } |
| } |
| }); |
| |
| boolean success = false; |
| try { |
| for (FirewallRuleVO newRule : rules) { |
| _firewallMgr.detectRulesConflict(newRule); |
| } |
| success = true; |
| return rules; |
| } finally { |
| if (!success) { |
| Transaction.execute(new TransactionCallbackNoReturn() { |
| @Override |
| public void doInTransactionWithoutResult(TransactionStatus status) { |
| for (FirewallRuleVO newRule : rules) { |
| _firewallMgr.removeRule(newRule); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| private List<PortForwardingRuleVO> listByNetworkId(long networkId) { |
| return _portForwardingDao.listByNetwork(networkId); |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_DISABLE_STATIC_NAT, eventDescription = "disabling static nat", async = true) |
| public boolean disableStaticNat(long ipId) throws ResourceUnavailableException, NetworkRuleConflictException, InsufficientAddressCapacityException { |
| CallContext ctx = CallContext.current(); |
| Account caller = ctx.getCallingAccount(); |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipId); |
| checkIpAndUserVm(ipAddress, null, caller, false); |
| |
| if (ipAddress.getSystem()) { |
| InvalidParameterValueException ex = new InvalidParameterValueException("Can't disable static nat for system IP address with specified id"); |
| ex.addProxyObject(ipAddress.getUuid(), "ipId"); |
| throw ex; |
| } |
| |
| Long vmId = ipAddress.getAssociatedWithVmId(); |
| if (vmId == null) { |
| InvalidParameterValueException ex = new InvalidParameterValueException("Specified IP address id is not associated with any vm Id"); |
| ex.addProxyObject(ipAddress.getUuid(), "ipId"); |
| throw ex; |
| } |
| |
| // if network has elastic IP functionality supported, we first have to disable static nat on old ip in order to |
| // re-enable it on the new one enable static nat takes care of that |
| Network guestNetwork = _networkModel.getNetwork(ipAddress.getAssociatedWithNetworkId()); |
| NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, guestNetwork.getNetworkOfferingId()); |
| if (offering.isElasticIp()) { |
| if (offering.isAssociatePublicIP()) { |
| getSystemIpAndEnableStaticNatForVm(_vmDao.findById(vmId), true); |
| return true; |
| } |
| } |
| |
| if (disableStaticNat(ipId, caller, ctx.getCallingUserId(), false)) { |
| Nic guestNic = _networkModel.getNicInNetworkIncludingRemoved(vmId, guestNetwork.getId()); |
| applyUserDataIfNeeded(vmId, guestNetwork, guestNic); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean disableStaticNat(long ipId, Account caller, long callerUserId, boolean releaseIpIfElastic) throws ResourceUnavailableException { |
| boolean success = true; |
| |
| IPAddressVO ipAddress = _ipAddressDao.findById(ipId); |
| checkIpAndUserVm(ipAddress, null, caller, false); |
| long networkId = ipAddress.getAssociatedWithNetworkId(); |
| |
| if (!ipAddress.isOneToOneNat()) { |
| InvalidParameterValueException ex = new InvalidParameterValueException("One to one nat is not enabled for the specified ip id"); |
| ex.addProxyObject(ipAddress.getUuid(), "ipId"); |
| throw ex; |
| } |
| |
| ipAddress.setRuleState(IpAddress.State.Releasing); |
| _ipAddressDao.update(ipAddress.getId(), ipAddress); |
| ipAddress = _ipAddressDao.findById(ipId); |
| |
| // Revoke all firewall rules for the ip |
| try { |
| logger.debug("Revoking all " + Purpose.Firewall + "rules as a part of disabling static nat for public IP id=" + ipId); |
| if (!_firewallMgr.revokeFirewallRulesForIp(ipId, callerUserId, caller)) { |
| logger.warn("Unable to revoke all the firewall rules for ip id=" + ipId + " as a part of disable statis nat"); |
| success = false; |
| } |
| } catch (ResourceUnavailableException e) { |
| logger.warn("Unable to revoke all firewall rules for ip id=" + ipId + " as a part of ip release", e); |
| success = false; |
| } |
| |
| if (!revokeAllPFAndStaticNatRulesForIp(ipId, callerUserId, caller)) { |
| logger.warn("Unable to revoke all static nat rules for ip " + ipAddress); |
| success = false; |
| } |
| |
| if (success) { |
| boolean isIpSystem = ipAddress.getSystem(); |
| ipAddress.setOneToOneNat(false); |
| ipAddress.setAssociatedWithVmId(null); |
| ipAddress.setRuleState(null); |
| ipAddress.setVmIp(null); |
| if (isIpSystem && !releaseIpIfElastic) { |
| ipAddress.setSystem(false); |
| } |
| _ipAddressDao.update(ipAddress.getId(), ipAddress); |
| _vpcMgr.unassignIPFromVpcNetwork(ipAddress.getId(), networkId); |
| |
| if (isIpSystem && releaseIpIfElastic && !_ipAddrMgr.handleSystemIpRelease(ipAddress)) { |
| logger.warn("Failed to release system ip address " + ipAddress); |
| success = false; |
| } |
| |
| return true; |
| } else { |
| logger.warn("Failed to disable one to one nat for the ip address id" + ipId); |
| ipAddress = _ipAddressDao.findById(ipId); |
| ipAddress.setRuleState(null); |
| _ipAddressDao.update(ipAddress.getId(), ipAddress); |
| return false; |
| } |
| } |
| |
| @Override |
| public StaticNatRule buildStaticNatRule(FirewallRule rule, boolean forRevoke) { |
| IpAddress ip = _ipAddressDao.findById(rule.getSourceIpAddressId()); |
| FirewallRuleVO ruleVO = _firewallDao.findById(rule.getId()); |
| |
| if (ip == null || !ip.isOneToOneNat() || ip.getAssociatedWithVmId() == null) { |
| InvalidParameterValueException ex = new InvalidParameterValueException("Source ip address of the specified firewall rule id is not static nat enabled"); |
| ex.addProxyObject(ruleVO.getUuid(), "ruleId"); |
| throw ex; |
| } |
| |
| String dstIp = ip.getVmIp(); |
| if (dstIp == null) { |
| InvalidParameterValueException ex = new InvalidParameterValueException("VM ip address of the specified public ip is not set "); |
| ex.addProxyObject(ruleVO.getUuid(), "ruleId"); |
| throw ex; |
| } |
| |
| return new StaticNatRuleImpl(ruleVO, dstIp); |
| } |
| |
| protected boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { |
| IpAddress sourceIp = _ipAddressDao.findById(sourceIpId); |
| |
| List<StaticNat> staticNats = createStaticNatForIp(sourceIp, caller, forRevoke); |
| |
| if (staticNats != null && !staticNats.isEmpty()) { |
| try { |
| if (!_ipAddrMgr.applyStaticNats(staticNats, continueOnError, forRevoke)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to create static nat rule due to ", ex); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyStaticNatForNetwork(long networkId, boolean continueOnError, Account caller, boolean forRevoke) { |
| List<? extends IpAddress> staticNatIps = _ipAddressDao.listStaticNatPublicIps(networkId); |
| |
| List<StaticNat> staticNats = new ArrayList<StaticNat>(); |
| for (IpAddress staticNatIp : staticNatIps) { |
| staticNats.addAll(createStaticNatForIp(staticNatIp, caller, forRevoke)); |
| } |
| |
| if (staticNats != null && !staticNats.isEmpty()) { |
| if (forRevoke) { |
| logger.debug("Found " + staticNats.size() + " static nats to disable for network id " + networkId); |
| } |
| try { |
| if (!_ipAddrMgr.applyStaticNats(staticNats, continueOnError, forRevoke)) { |
| return false; |
| } |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to create static nat rule due to ", ex); |
| return false; |
| } |
| } else { |
| logger.debug("Found 0 static nat rules to apply for network id " + networkId); |
| } |
| |
| return true; |
| } |
| |
| protected List<StaticNat> createStaticNatForIp(IpAddress sourceIp, Account caller, boolean forRevoke) { |
| List<StaticNat> staticNats = new ArrayList<StaticNat>(); |
| if (!sourceIp.isOneToOneNat()) { |
| logger.debug("Source ip id=" + sourceIp + " is not one to one nat"); |
| return staticNats; |
| } |
| |
| Long networkId = sourceIp.getAssociatedWithNetworkId(); |
| if (networkId == null) { |
| throw new CloudRuntimeException("Ip address is not associated with any network"); |
| } |
| |
| VMInstanceVO vm = _vmInstanceDao.findByIdIncludingRemoved(sourceIp.getAssociatedWithVmId()); |
| Network network = _networkModel.getNetwork(networkId); |
| if (network == null) { |
| CloudRuntimeException ex = new CloudRuntimeException("Unable to find an ip address to map to specified vm id"); |
| ex.addProxyObject(vm.getUuid(), "vmId"); |
| throw ex; |
| } |
| |
| if (caller != null) { |
| _accountMgr.checkAccess(caller, null, true, sourceIp); |
| } |
| |
| // create new static nat rule |
| // Get nic IP4 address |
| Nic guestNic = _networkModel.getNicInNetworkIncludingRemoved(vm.getId(), networkId); |
| if (guestNic == null) { |
| throw new InvalidParameterValueException("Vm doesn't belong to the network with specified id"); |
| } |
| |
| String dstIp; |
| |
| dstIp = sourceIp.getVmIp(); |
| if (dstIp == null) { |
| throw new InvalidParameterValueException("Vm ip is not set as dnat ip for this public ip"); |
| } |
| |
| String srcMac = null; |
| try { |
| srcMac = _networkModel.getNextAvailableMacAddressInNetwork(networkId); |
| } catch (InsufficientAddressCapacityException e) { |
| throw new CloudRuntimeException("Insufficient MAC address for static NAT instantiation."); |
| } |
| |
| StaticNatImpl staticNat = new StaticNatImpl(sourceIp.getAllocatedToAccountId(), sourceIp.getAllocatedInDomainId(), networkId, sourceIp.getId(), dstIp, srcMac, forRevoke); |
| staticNats.add(staticNat); |
| return staticNats; |
| } |
| |
| @Override |
| public void getSystemIpAndEnableStaticNatForVm(VirtualMachine vm, boolean getNewIp) throws InsufficientAddressCapacityException { |
| boolean success = true; |
| |
| // enable static nat if eIp capability is supported |
| List<? extends Nic> nics = _nicDao.listByVmId(vm.getId()); |
| for (Nic nic : nics) { |
| Network guestNetwork = _networkModel.getNetwork(nic.getNetworkId()); |
| NetworkOffering offering = _entityMgr.findById(NetworkOffering.class, guestNetwork.getNetworkOfferingId()); |
| if (offering.isElasticIp()) { |
| boolean isSystemVM = (vm.getType() == Type.ConsoleProxy || vm.getType() == Type.SecondaryStorageVm); |
| // for user VM's associate public IP only if offering is marked to associate a public IP by default on start of VM |
| if (!isSystemVM && !offering.isAssociatePublicIP()) { |
| continue; |
| } |
| // check if there is already static nat enabled |
| if (_ipAddressDao.findByAssociatedVmId(vm.getId()) != null && !getNewIp) { |
| logger.debug("Vm " + vm + " already has ip associated with it in guest network " + guestNetwork); |
| continue; |
| } |
| |
| logger.debug("Allocating system ip and enabling static nat for it for the vm " + vm + " in guest network " + guestNetwork); |
| IpAddress ip = _ipAddrMgr.assignSystemIp(guestNetwork.getId(), _accountMgr.getAccount(vm.getAccountId()), false, true); |
| if (ip == null) { |
| throw new CloudRuntimeException("Failed to allocate system ip for vm " + vm + " in guest network " + guestNetwork); |
| } |
| |
| logger.debug("Allocated system ip " + ip + ", now enabling static nat on it for vm " + vm); |
| |
| try { |
| success = enableStaticNat(ip.getId(), vm.getId(), guestNetwork.getId(), isSystemVM, null); |
| } catch (NetworkRuleConflictException ex) { |
| logger.warn("Failed to enable static nat as a part of enabling elasticIp and staticNat for vm " + vm + " in guest network " + guestNetwork + |
| " due to exception ", ex); |
| success = false; |
| } catch (ResourceUnavailableException ex) { |
| logger.warn("Failed to enable static nat as a part of enabling elasticIp and staticNat for vm " + vm + " in guest network " + guestNetwork + |
| " due to exception ", ex); |
| success = false; |
| } |
| |
| if (!success) { |
| logger.warn("Failed to enable static nat on system ip " + ip + " for the vm " + vm + ", releasing the ip..."); |
| _ipAddrMgr.handleSystemIpRelease(ip); |
| throw new CloudRuntimeException("Failed to enable static nat on system ip for the vm " + vm); |
| } else { |
| logger.warn("Successfully enabled static nat on system ip " + ip + " for the vm " + vm); |
| } |
| } |
| } |
| } |
| |
| protected void removePFRule(PortForwardingRuleVO rule) { |
| _portForwardingDao.remove(rule.getId()); |
| } |
| |
| @Override |
| public List<FirewallRuleVO> listAssociatedRulesForGuestNic(Nic nic) { |
| logger.debug("Checking if PF/StaticNat/LoadBalancer rules are configured for nic " + nic.getId()); |
| List<FirewallRuleVO> result = new ArrayList<FirewallRuleVO>(); |
| // add PF rules |
| result.addAll(_portForwardingDao.listByNetworkAndDestIpAddr(nic.getIPv4Address(), nic.getNetworkId())); |
| if(result.size() > 0) { |
| logger.debug("Found " + result.size() + " portforwarding rule configured for the nic in the network " + nic.getNetworkId()); |
| } |
| // add static NAT rules |
| List<FirewallRuleVO> staticNatRules = _firewallDao.listStaticNatByVmId(nic.getInstanceId()); |
| for (FirewallRuleVO rule : staticNatRules) { |
| if (rule.getNetworkId() == nic.getNetworkId()) { |
| result.add(rule); |
| logger.debug("Found rule " + rule.getId() + " " + rule.getPurpose() + " configured"); |
| } |
| } |
| List<? extends IpAddress> staticNatIps = _ipAddressDao.listStaticNatPublicIps(nic.getNetworkId()); |
| for (IpAddress ip : staticNatIps) { |
| if (ip.getVmIp() != null && ip.getVmIp().equals(nic.getIPv4Address())) { |
| VMInstanceVO vm = _vmInstanceDao.findById(nic.getInstanceId()); |
| // generate a static Nat rule on the fly because staticNATrule does not persist into db anymore |
| // FIX ME |
| FirewallRuleVO staticNatRule = |
| new FirewallRuleVO(null, ip.getId(), 0, 65535, NetUtils.ALL_PROTO.toString(), nic.getNetworkId(), vm.getAccountId(), vm.getDomainId(), |
| Purpose.StaticNat, null, null, null, null, null); |
| result.add(staticNatRule); |
| logger.debug("Found rule " + staticNatRule.getId() + " " + staticNatRule.getPurpose() + " configured"); |
| } |
| } |
| // add LB rules |
| List<LoadBalancerVMMapVO> lbMapList = _loadBalancerVMMapDao.listByInstanceId(nic.getInstanceId()); |
| for (LoadBalancerVMMapVO lb : lbMapList) { |
| FirewallRuleVO lbRule = _firewallDao.findById(lb.getLoadBalancerId()); |
| if (lbRule.getNetworkId() == nic.getNetworkId()) { |
| result.add(lbRule); |
| logger.debug("Found rule " + lbRule.getId() + " " + lbRule.getPurpose() + " configured"); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| @ActionEvent(eventType = EventTypes.EVENT_NET_RULE_MODIFY, eventDescription = "updating forwarding rule", async = true) |
| public PortForwardingRule updatePortForwardingRule(long id, Integer privatePort, Integer privateEndPort, Long virtualMachineId, Ip vmGuestIp, String customId, Boolean forDisplay) { |
| Account caller = CallContext.current().getCallingAccount(); |
| PortForwardingRuleVO rule = _portForwardingDao.findById(id); |
| if (rule == null) { |
| throw new InvalidParameterValueException("Unable to find " + id); |
| } |
| _accountMgr.checkAccess(caller, null, true, rule); |
| |
| if (customId != null) { |
| rule.setUuid(customId); |
| } |
| |
| if (forDisplay != null) { |
| rule.setDisplay(forDisplay); |
| } |
| |
| if (privatePort != null && !NetUtils.isValidPort(privatePort)) { |
| throw new InvalidParameterValueException("privatePort is an invalid value: " + privatePort); |
| } |
| if (privateEndPort != null && !NetUtils.isValidPort(privateEndPort)) { |
| throw new InvalidParameterValueException("PrivateEndPort has an invalid value: " + privateEndPort); |
| } |
| |
| if (privatePort != null && privateEndPort != null && ((privateEndPort - privatePort) != (rule.getSourcePortEnd() - rule.getSourcePortStart()))) |
| { |
| throw new InvalidParameterValueException("Unable to update the private port range of port forwarding rule as " + |
| "the provided port range is not consistent with the port range : " + rule.getSourcePortStart() + " to " + rule.getSourcePortEnd()); |
| } |
| |
| //in case of port range |
| if (!rule.getSourcePortStart().equals(rule.getSourcePortEnd())) { |
| if ((privatePort == null || privateEndPort == null) && !(privatePort == null && privateEndPort == null)) { |
| throw new InvalidParameterValueException("Unable to update the private port range of port forwarding rule as " + |
| "the provided port range is not consistent with the port range : " + rule.getSourcePortStart() + " to " + rule.getSourcePortEnd()); |
| } |
| } |
| |
| |
| |
| if (virtualMachineId == null && vmGuestIp != null) { |
| throw new InvalidParameterValueException("vmguestip should be set along with virtualmachineid"); |
| } |
| Ip dstIp = rule.getDestinationIpAddress(); |
| if (virtualMachineId != null) { |
| // Verify that vm has nic in the network |
| Nic guestNic = _networkModel.getNicInNetwork(virtualMachineId, rule.getNetworkId()); |
| if (guestNic == null || guestNic.getIPv4Address() == null) { |
| throw new InvalidParameterValueException("Vm doesn't belong to network associated with ipAddress"); |
| } else { |
| dstIp = new Ip(guestNic.getIPv4Address()); |
| } |
| |
| if (vmGuestIp != null) { |
| //vm ip is passed so it can be primary or secondary ip addreess. |
| if (!dstIp.equals(vmGuestIp)) { |
| //the vm ip is secondary ip to the nic. |
| // is vmIp is secondary ip or not |
| NicSecondaryIp secondaryIp = _nicSecondaryDao.findByIp4AddressAndNicId(vmGuestIp.toString(), guestNic.getId()); |
| if (secondaryIp == null) { |
| throw new InvalidParameterValueException("IP Address is not in the VM nic's network "); |
| } |
| dstIp = vmGuestIp; |
| } |
| } |
| } |
| |
| // revoke old rules at first |
| List<PortForwardingRuleVO> rules = new ArrayList<PortForwardingRuleVO>(); |
| rule.setState(State.Revoke); |
| _portForwardingDao.update(id, rule); |
| rules.add(rule); |
| try { |
| if (!_firewallMgr.applyRules(rules, true, false)) { |
| throw new CloudRuntimeException("Failed to revoke the existing port forwarding rule:" + id); |
| } |
| } catch (ResourceUnavailableException ex) { |
| throw new CloudRuntimeException("Failed to revoke the existing port forwarding rule:" + id + " due to ", ex); |
| } |
| |
| rule = _portForwardingDao.findById(id); |
| rule.setState(State.Add); |
| if (privatePort != null) { |
| rule.setDestinationPortStart(privatePort.intValue()); |
| rule.setDestinationPortEnd((privateEndPort == null) ? privatePort.intValue() : privateEndPort.intValue()); |
| } else if (privateEndPort != null) { |
| rule.setDestinationPortStart(privateEndPort.intValue()); |
| rule.setDestinationPortEnd(privateEndPort); |
| } |
| |
| if (virtualMachineId != null) { |
| rule.setVirtualMachineId(virtualMachineId); |
| rule.setDestinationIpAddress(dstIp); |
| } |
| _portForwardingDao.update(id, rule); |
| |
| //apply new rules |
| if (!applyPortForwardingRules(rule.getSourceIpAddressId(), false, caller)) { |
| throw new CloudRuntimeException("Failed to apply the new port forwarding rule:" + id); |
| } |
| |
| return _portForwardingDao.findById(id); |
| } |
| } |