From 731e78937fcd66c81c672d77320386552c0bde3f Mon Sep 17 00:00:00 2001 From: alena Date: Tue, 25 Jan 2011 15:30:52 -0800 Subject: [PATCH] Added 2 new api commands for 1-1 nat feauture: enable/disableOneToOneNat. Here is the 1-1 nat api summary: * to enable 1-1 nat for ip/vm use enalbeOneToOneNat api * to open port range (or multiple port ranges) use createIpForwardingRule api. * to delete one port range use deleteIpForwardingRule api. * to disable 1-1 nat use disableOneToOneNat api. --- .../commands/CreateIpForwardingRuleCmd.java | 25 ++-- .../commands/DeleteIpForwardingRuleCmd.java | 8 +- .../api/commands/DisableOneToOneNat.java | 88 +++++++++++++ .../cloud/api/commands/EnableOneToOneNat.java | 86 +++++++++++++ api/src/com/cloud/event/EventTypes.java | 5 + api/src/com/cloud/network/IpAddress.java | 2 + api/src/com/cloud/network/NetworkService.java | 3 + .../com/cloud/network/rules/RulesService.java | 5 + client/tomcatconf/commands.properties.in | 2 + server/src/com/cloud/network/IPAddressVO.java | 15 ++- .../com/cloud/network/NetworkManagerImpl.java | 5 + .../src/com/cloud/network/addr/PublicIp.java | 5 + .../cloud/network/dao/FirewallRulesDao.java | 2 +- .../network/dao/FirewallRulesDaoImpl.java | 6 +- .../cloud/network/dao/IPAddressDaoImpl.java | 1 + .../lb/LoadBalancingRulesManagerImpl.java | 3 +- .../cloud/network/rules/RulesManagerImpl.java | 117 +++++++++++++----- setup/db/create-schema.sql | 1 + 18 files changed, 335 insertions(+), 44 deletions(-) create mode 100644 api/src/com/cloud/api/commands/DisableOneToOneNat.java create mode 100644 api/src/com/cloud/api/commands/EnableOneToOneNat.java diff --git a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java index 7350a3ef052..81d40d01680 100644 --- a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java @@ -28,7 +28,9 @@ import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.FirewallRuleResponse; import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.network.IpAddress; import com.cloud.network.rules.PortForwardingRule; import com.cloud.user.Account; import com.cloud.user.UserContext; @@ -46,9 +48,6 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, required=true, description="the public IP address of the forwarding rule, already associated via associateIp") private String ipAddress; - - @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=true, description="the ID of the virtual machine for the forwarding rule") - private Long virtualMachineId; @Parameter(name=ApiConstants.START_PORT, type=CommandType.INTEGER, required=true, description="the start port for the rule") private Integer startPort; @@ -58,6 +57,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por @Parameter(name=ApiConstants.PROTOCOL, type=CommandType.STRING, required=true, description="the protocol for the rule. Valid values are TCP or UDP.") private String protocol; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -66,10 +66,6 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por public String getIpAddress() { return ipAddress; } - - public long getVirtualMachineId() { - return virtualMachineId; - } public int getStartPort() { return startPort; @@ -113,7 +109,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por public void create() { PortForwardingRule rule; try { - rule = _rulesService.createPortForwardingRule(this, virtualMachineId, true); + rule = _rulesService.createPortForwardingRule(this, getVirtualMachineId(), true); } catch (NetworkRuleConflictException e) { s_logger.info("Unable to create Port Forwarding Rule due to " + e.getMessage()); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); @@ -140,7 +136,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por @Override public String getEventDescription() { - return ("Creating an ipforwarding 1:1 NAT rule for "+ipAddress+" with virtual machine:"+virtualMachineId); + return ("Creating an ipforwarding 1:1 NAT rule for "+ipAddress+" with virtual machine:"+ getVirtualMachineId()); } @Override @@ -225,5 +221,16 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Por public boolean isOneToOneNat() { return true; } + + @Override + public long getVirtualMachineId() { + IpAddress ip = _networkService.getIp(new Ip(ipAddress)); + if (ip == null) { + throw new InvalidParameterValueException("Ip address " + ipAddress + " doesn't exist in the system"); + } else { + return _networkService.getIp(new Ip(ipAddress)).getVmId(); + } + + } } diff --git a/api/src/com/cloud/api/commands/DeleteIpForwardingRuleCmd.java b/api/src/com/cloud/api/commands/DeleteIpForwardingRuleCmd.java index e1d49946b5e..1049a9d7001 100644 --- a/api/src/com/cloud/api/commands/DeleteIpForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/DeleteIpForwardingRuleCmd.java @@ -28,6 +28,7 @@ import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.SuccessResponse; import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.rules.PortForwardingRule; @Implementation(description="Deletes an ip forwarding rule", responseObject=SuccessResponse.class) @@ -79,7 +80,12 @@ public class DeleteIpForwardingRuleCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { if (ownerId == null) { - ownerId = _entityMgr.findById(PortForwardingRule.class, id).getAccountId(); + PortForwardingRule rule = _entityMgr.findById(PortForwardingRule.class, id); + if (rule == null) { + throw new InvalidParameterValueException("Unable to find firewall rule by id: " + id); + } else { + ownerId = rule.getAccountId(); + } } return ownerId; } diff --git a/api/src/com/cloud/api/commands/DisableOneToOneNat.java b/api/src/com/cloud/api/commands/DisableOneToOneNat.java new file mode 100644 index 00000000000..724cc69b8bd --- /dev/null +++ b/api/src/com/cloud/api/commands/DisableOneToOneNat.java @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseAsyncCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; +import com.cloud.utils.net.Ip; + +@Implementation(description="Disables one to one nat rule", responseObject=SuccessResponse.class) +public class DisableOneToOneNat extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeletePortForwardingRuleCmd.class.getName()); + private static final String s_name = "disableonetoonenatresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, required=true, description="the public IP address for which one-to-one nat feature is being disableed") + private String ipAddress; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getIpAddress() { + return ipAddress; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getCommandName() { + return s_name; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_DISABLE_ONE_TO_ONE_NAT; + } + + @Override + public String getEventDescription() { + return ("Disabling one to one nat for ip=" + ipAddress); + } + + @Override + public long getEntityOwnerId() { + return _entityMgr.findById(IpAddress.class, ipAddress).getAccountId(); + } + + @Override + public void execute() throws ResourceUnavailableException { + boolean result = _rulesService.disableOneToOneNat(new Ip(ipAddress)); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to disable oneToOne nat rule"); + } + } +} diff --git a/api/src/com/cloud/api/commands/EnableOneToOneNat.java b/api/src/com/cloud/api/commands/EnableOneToOneNat.java new file mode 100644 index 00000000000..9f92391294f --- /dev/null +++ b/api/src/com/cloud/api/commands/EnableOneToOneNat.java @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.utils.net.Ip; + +@Implementation(description="Enables one to one nat for the ip address", responseObject=SuccessResponse.class) +public class EnableOneToOneNat extends BaseCmd{ + public static final Logger s_logger = Logger.getLogger(CreateIpForwardingRuleCmd.class.getName()); + + private static final String s_name = "enableonetoonenatresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, required=true, description="the public IP address for which one-to-one nat feature is being enabled") + private String ipAddress; + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=true, description="the ID of the virtual machine for enabling one-to-one nat feature") + private Long virtualMachineId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getIpAddress() { + return ipAddress; + } + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + try { + boolean result = _rulesService.enableOneToOneNat(new Ip(ipAddress), virtualMachineId); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to enable one-to-one nat"); + } + } catch (NetworkRuleConflictException ex) { + s_logger.info("Network rule conflict: " + ex.getMessage()); + s_logger.trace("Network Rule Conflict: ", ex); + throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); + } + } + +} diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 22c038bf787..28b4614e405 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -183,4 +183,9 @@ public class EventTypes { //Custom certificates public static final String EVENT_UPLOAD_CUSTOM_CERTIFICATE = "UPLOAD.CUSTOM.CERTIFICATE"; + + //OneToOnenat + public static final String EVENT_ENABLE_ONE_TO_ONE_NAT = "ONETOONENAT.ENABLE"; + public static final String EVENT_DISABLE_ONE_TO_ONE_NAT = "ONETOONENAT.DISABLE"; + } diff --git a/api/src/com/cloud/network/IpAddress.java b/api/src/com/cloud/network/IpAddress.java index e4552298684..1eaeabada82 100644 --- a/api/src/com/cloud/network/IpAddress.java +++ b/api/src/com/cloud/network/IpAddress.java @@ -66,4 +66,6 @@ public interface IpAddress extends ControlledEntity { boolean readyToUse(); Long getAssociatedWithNetworkId(); + + Long getVmId(); } diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index a6a83d83f85..affd873cfae 100644 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -32,6 +32,7 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.offering.NetworkOffering; +import com.cloud.utils.net.Ip; public interface NetworkService { @@ -58,4 +59,6 @@ public interface NetworkService { Network getNetwork(long networkId); + IpAddress getIp(Ip ip); + } diff --git a/api/src/com/cloud/network/rules/RulesService.java b/api/src/com/cloud/network/rules/RulesService.java index cb985a1ac08..f8f39f6aff7 100644 --- a/api/src/com/cloud/network/rules/RulesService.java +++ b/api/src/com/cloud/network/rules/RulesService.java @@ -54,4 +54,9 @@ public interface RulesService { public List listPortForwardingRules(ListPortForwardingRulesCmd cmd); boolean applyPortForwardingRules(Ip ip, Account caller) throws ResourceUnavailableException; + + boolean enableOneToOneNat(Ip ipAddress, long vmId) throws NetworkRuleConflictException; + + boolean disableOneToOneNat(Ip ipAddress); + } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index f0c9608dd6e..2577e8e26fb 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -111,9 +111,11 @@ deletePortForwardingRule=com.cloud.api.commands.DeletePortForwardingRuleCmd;15 #### updatePortForwardingRule=com.cloud.api.commands.UpdatePortForwardingRuleCmd;15 #### NAT commands +enableOneToOneNat=com.cloud.api.commands.EnableOneToOneNat;15 createIpForwardingRule=com.cloud.api.commands.CreateIpForwardingRuleCmd;15 deleteIpForwardingRule=com.cloud.api.commands.DeleteIpForwardingRuleCmd;15 listIpForwardingRules=com.cloud.api.commands.ListIpForwardingRulesCmd;15 +disableOneToOneNat=com.cloud.api.commands.DisableOneToOneNat;15 #### load balancer commands createLoadBalancerRule=com.cloud.api.commands.CreateLoadBalancerRuleCmd;15 diff --git a/server/src/com/cloud/network/IPAddressVO.java b/server/src/com/cloud/network/IPAddressVO.java index eb170e81f9c..ebee43c37e5 100644 --- a/server/src/com/cloud/network/IPAddressVO.java +++ b/server/src/com/cloud/network/IPAddressVO.java @@ -65,6 +65,9 @@ public class IPAddressVO implements IpAddress { @Column(name="one_to_one_nat") private boolean oneToOneNat; + @Column(name="vm_id") + private Long associatedWithVmId; + @Column(name="state") private State state; @@ -121,6 +124,15 @@ public class IPAddressVO implements IpAddress { public void setAssociatedWithNetworkId(Long networkId) { this.associatedWithNetworkId = networkId; } + + @Override + public Long getVmId() { + return associatedWithVmId; + } + + public void setAssociatedWithVmId(Long vmId) { + this.associatedWithVmId = vmId; + } @Override public Long getAllocatedInDomainId() { @@ -193,5 +205,6 @@ public class IPAddressVO implements IpAddress { @Override public String toString() { return new StringBuilder("Ip[").append(address).append("-").append(dataCenterId).append("]").toString(); - } + } + } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index abf7c114066..e28bb9e89a0 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -1979,4 +1979,9 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag assert vos.size() <= 1 : "If we have multiple networks of the same type, then this method should no longer be used."; return vos.size() == 1 ? vos.get(0) : null; } + + @Override + public IpAddress getIp(Ip ip) { + return _ipAddressDao.findById(ip); + } } diff --git a/server/src/com/cloud/network/addr/PublicIp.java b/server/src/com/cloud/network/addr/PublicIp.java index 277c3e97faf..26477bbdc61 100644 --- a/server/src/com/cloud/network/addr/PublicIp.java +++ b/server/src/com/cloud/network/addr/PublicIp.java @@ -79,6 +79,11 @@ public class PublicIp implements PublicIpAddress { return _addr.isOneToOneNat(); } + @Override + public Long getVmId() { + return _addr.getVmId(); + } + @Override public Date getAllocatedTime() { return _addr.getAllocatedTime(); diff --git a/server/src/com/cloud/network/dao/FirewallRulesDao.java b/server/src/com/cloud/network/dao/FirewallRulesDao.java index b5a99cbb4ce..abe9fddb486 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDao.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDao.java @@ -29,7 +29,7 @@ import com.cloud.utils.net.Ip; * Data Access Object for user_ip_address and ip_forwarding tables */ public interface FirewallRulesDao extends GenericDao { - List listByIpAndNotRevoked(Ip ip); + List listByIpAndNotRevoked(Ip ip, Boolean isOneToOneNat); boolean setStateToAdd(FirewallRuleVO rule); diff --git a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java index c1d2afcd907..a63556e042f 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -59,6 +59,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i IpNotRevokedSearch = createSearchBuilder(); IpNotRevokedSearch.and("ip", IpNotRevokedSearch.entity().getSourceIpAddress(), Op.EQ); IpNotRevokedSearch.and("state", IpNotRevokedSearch.entity().getState(), Op.NEQ); + IpNotRevokedSearch.and("oneToOneNat", IpNotRevokedSearch.entity().isOneToOneNat(), Op.EQ); IpNotRevokedSearch.done(); ReleaseSearch = createSearchBuilder(); @@ -91,10 +92,13 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i } @Override - public List listByIpAndNotRevoked(Ip ip) { + public List listByIpAndNotRevoked(Ip ip, Boolean isOneToOneNat) { SearchCriteria sc = IpNotRevokedSearch.create(); sc.setParameters("ip", ip); sc.setParameters("state", State.Revoke); + if (isOneToOneNat != null) { + sc.setParameters("oneToOneNat", isOneToOneNat); + } return listBy(sc); } diff --git a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java index 9ac8ff3bdb2..67ec35bf2d2 100644 --- a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java @@ -152,6 +152,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implements address.setAllocatedTime(null); address.setSourceNat(false); address.setOneToOneNat(false); + address.setAssociatedWithVmId(null); address.setState(State.Free); address.setAssociatedWithNetworkId(null); update(ipAddress, address); diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 6ec8a81ffe5..8681197df7a 100644 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -36,7 +36,6 @@ import com.cloud.api.commands.UpdateLoadBalancerRuleCmd; import com.cloud.dc.dao.VlanDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.EventTypes; -import com.cloud.event.EventVO; import com.cloud.event.UsageEventVO; import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; @@ -399,7 +398,7 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, @Override public boolean removeAllLoadBalanacers(Ip ip) { - List rules = _rulesDao.listByIpAndNotRevoked(ip); + List rules = _rulesDao.listByIpAndNotRevoked(ip, null); if (rules != null) s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); for (FirewallRule rule : rules) { diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 768f789ac48..0d4b80591f0 100644 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -83,7 +83,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { public void detectRulesConflict(FirewallRule newRule, IpAddress ipAddress) throws NetworkRuleConflictException { assert newRule.getSourceIpAddress().equals(ipAddress.getAddress()) : "You passed in an ip address that doesn't match the address in the new rule"; - List rules = _firewallDao.listByIpAndNotRevoked(newRule.getSourceIpAddress()); + List rules = _firewallDao.listByIpAndNotRevoked(newRule.getSourceIpAddress(), null); assert (rules.size() >= 1) : "For network rules, we now always first persist the rule and then check for network conflicts so we should at least have one rule at this point."; for (FirewallRuleVO rule : rules) { @@ -92,9 +92,9 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } if (rule.isOneToOneNat() && !newRule.isOneToOneNat()) { - throw new NetworkRuleConflictException("There is already port forwarding rule specified for the " + newRule.getSourceIpAddress()); + throw new NetworkRuleConflictException("There is 1 to 1 Nat rule specified for the " + newRule.getSourceIpAddress()); } else if (!rule.isOneToOneNat() && newRule.isOneToOneNat()) { - throw new NetworkRuleConflictException("There is already 1 to 1 Nat rule specified for the " + newRule.getSourceIpAddress()); + throw new NetworkRuleConflictException("There is already firewall rule specified for the " + newRule.getSourceIpAddress()); } if (rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) { @@ -133,6 +133,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { throw new InvalidParameterValueException("Invalid user vm: " + userVm.getId()); } + _accountMgr.checkAccess(caller, ipAddress); _accountMgr.checkAccess(caller, userVm); // validate that IP address and userVM belong to the same account @@ -193,13 +194,10 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { long accountId = network.getAccountId(); long domainId = network.getDomainId(); - checkIpAndUserVm(ipAddress, vm, caller); - if (isNat && (ipAddress.isSourceNat())) { + if (isNat && (ipAddress.isSourceNat() || !ipAddress.isOneToOneNat() || ipAddress.getVmId() == null)) { throw new NetworkRuleConflictException("Can't do one to one NAT on ip address: " + ipAddress.getAddress()); } - Transaction txn = Transaction.currentTxn(); - txn.start(); PortForwardingRuleVO newRule = new PortForwardingRuleVO(rule.getXid(), rule.getSourceIpAddress(), @@ -213,12 +211,6 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { accountId, domainId, vmId, isNat); newRule = _forwardingDao.persist(newRule); - - if (isNat && !ipAddress.isOneToOneNat()) { - ipAddress.setOneToOneNat(true); - _ipAddressDao.update(ipAddress.getAddress(), ipAddress); - } - txn.commit(); try { detectRulesConflict(newRule, ipAddress); @@ -230,21 +222,48 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { _usageEventDao.persist(usageEvent); return newRule; } catch (Exception e) { - txn.start(); _forwardingDao.remove(newRule.getId()); - if (isNat) { - ipAddress.setOneToOneNat(false); - _ipAddressDao.update(ipAddress.getAddress(), ipAddress); - } - txn.commit(); if (e instanceof NetworkRuleConflictException) { throw (NetworkRuleConflictException)e; } - throw new CloudRuntimeException("Unable to add rule for " + newRule.getSourceIpAddress(), e); } } + @Override + public boolean enableOneToOneNat(Ip ip, long vmId) throws NetworkRuleConflictException{ + IPAddressVO ipAddress = _ipAddressDao.findById(ip); + Account caller = UserContext.current().getCaller(); + + UserVmVO vm = null; + vm = _vmDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException("Can't enable one-to-one nat for the address " + ipAddress + ", invalid virtual machine id specified (" + vmId + ")."); + } + + checkIpAndUserVm(ipAddress, vm, caller); + + if (ipAddress.isSourceNat()) { + throw new InvalidParameterValueException("Can't enable one to one nat, ip address: " + ip.addr() + " is a sourceNat ip address"); + } + + if (!ipAddress.isOneToOneNat()) { + List rules = _firewallDao.listByIpAndNotRevoked(ip, false); + if (rules != null && !rules.isEmpty()) { + throw new NetworkRuleConflictException("Failed to enable one to one nat for the ip address " + ipAddress.getAddress() + " as it already has firewall rules assigned"); + } + } else { + if (ipAddress.getVmId() != null && ipAddress.getVmId().longValue() != vmId) { + throw new NetworkRuleConflictException("Failed to enable one to one nat for the ip address " + ipAddress.getAddress() + " and vm id=" + vmId + " as it's already assigned to antoher vm"); + } + } + + ipAddress.setOneToOneNat(true); + ipAddress.setAssociatedWithVmId(vmId); + return _ipAddressDao.update(ipAddress.getAddress(), ipAddress); + + } + protected Pair getUserVmGuestIpAddress(UserVm vm) { Ip dstIp = null; List nics = _networkMgr.getNics(vm); @@ -276,14 +295,6 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { rule.setState(State.Revoke); _firewallDao.update(rule.getId(), rule); } - if (rule.isOneToOneNat()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Removing one to one nat so setting the ip back to one to one nat is false: " + rule.getSourceIpAddress()); - } - IPAddressVO ipAddress = _ipAddressDao.findById(rule.getSourceIpAddress()); - ipAddress.setOneToOneNat(false); - _ipAddressDao.update(ipAddress.getAddress(), ipAddress); - } // Save and create the event String ruleName = rule.getPurpose() == Purpose.Firewall ? "Firewall" : (rule.isOneToOneNat() ? "ip forwarding" : "port forwarding"); @@ -339,7 +350,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } public List listFirewallRules(Ip ip) { - return _firewallDao.listByIpAndNotRevoked(ip); + return _firewallDao.listByIpAndNotRevoked(ip, null); } @Override @@ -557,5 +568,53 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { public List listByNetworkId(long networkId) { return _forwardingDao.listByNetworkId(networkId); } + + public boolean isLastOneToOneNatRule(FirewallRule ruleToCheck) { + List rules = _firewallDao.listByIpAndNotRevoked(ruleToCheck.getSourceIpAddress(), false); + if (rules != null && !rules.isEmpty()) { + for (FirewallRuleVO rule : rules) { + if (ruleToCheck.getId() == rule.getId()) { + continue; + } + if (rule.isOneToOneNat()) { + return false; + } + } + } else { + return true; + } + + return true; + } + + @Override + public boolean disableOneToOneNat(Ip ip){ + Account caller = UserContext.current().getCaller(); + + IPAddressVO ipAddress = _ipAddressDao.findById(ip); + checkIpAndUserVm(ipAddress, null, caller); + + if (!ipAddress.isOneToOneNat()) { + throw new InvalidParameterValueException("One to one nat is not enabled for the ip: " + ip.addr()); + } + + List rules = _firewallDao.listByIpAndNotRevoked(ip, true); + if (rules != null) { + for (FirewallRuleVO rule : rules) { + rule.setState(State.Revoke); + _firewallDao.update(rule.getId(), rule); + } + } + + if (applyPortForwardingRules(ip, true)) { + ipAddress.setOneToOneNat(false); + ipAddress.setAssociatedWithVmId(null); + _ipAddressDao.update(ipAddress.getAddress(), ipAddress); + return true; + } else { + s_logger.warn("Failed to disable one to one nat for the ip address " + ip.addr()); + return false; + } + } } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 75a788995ef..777564f2c03 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -682,6 +682,7 @@ CREATE TABLE `cloud`.`user_ip_address` ( `allocated` datetime NULL COMMENT 'Date this ip was allocated to someone', `vlan_db_id` bigint unsigned NOT NULL, `one_to_one_nat` int(1) unsigned NOT NULL default '0', + `vm_id` bigint unsigned COMMENT 'vm id the one_to_one nat ip is assigned to', `state` char(32) NOT NULL default 'Free' COMMENT 'state of the ip address', `mac_address` bigint unsigned NOT NULL COMMENT 'mac address of this ip', `network_id` bigint unsigned COMMENT 'network this public ip address is associated with',