From 40ac2f5cdff7fdb105a389dc446b081b7ce2c39d Mon Sep 17 00:00:00 2001 From: Murali reddy Date: Thu, 19 Jan 2012 20:19:55 +0530 Subject: [PATCH] bug 12826: enable NetScaler in basic zone for load balancing and static NAT status 12826: resolved fixed --- .../network/resource/NetscalerResource.java | 78 +++++++- .../network/element/NetscalerElement.java | 186 +++++++++++++++--- 2 files changed, 233 insertions(+), 31 deletions(-) diff --git a/core/src/com/cloud/network/resource/NetscalerResource.java b/core/src/com/cloud/network/resource/NetscalerResource.java index e26d25a33c4..c6430c5b094 100644 --- a/core/src/com/cloud/network/resource/NetscalerResource.java +++ b/core/src/com/cloud/network/resource/NetscalerResource.java @@ -39,8 +39,11 @@ import com.cloud.agent.api.StartupExternalLoadBalancerCommand; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO; import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO; import com.cloud.host.Host; @@ -322,7 +325,7 @@ public class NetscalerResource implements ServerResource { @Override public Answer executeRequest(Command cmd) { return executeRequest(cmd, _numRetries); - } + } private Answer executeRequest(Command cmd, int numRetries) { if (cmd instanceof ReadyCommand) { @@ -339,6 +342,8 @@ public class NetscalerResource implements ServerResource { return execute((CreateLoadBalancerApplianceCommand) cmd, numRetries); } else if (cmd instanceof DestroyLoadBalancerApplianceCommand) { return execute((DestroyLoadBalancerApplianceCommand) cmd, numRetries); + } else if (cmd instanceof SetStaticNatRulesCommand) { + return execute((SetStaticNatRulesCommand) cmd, numRetries); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -763,6 +768,73 @@ public class NetscalerResource implements ServerResource { } } + private synchronized Answer execute(SetStaticNatRulesCommand cmd, int numRetries) { + + if (_isSdx) { + return Answer.createUnsupportedCommandAnswer(cmd); + } + + String[] results = new String[cmd.getRules().length]; + int i = 0; + boolean endResult = true; + + try { + for (StaticNatRuleTO rule : cmd.getRules()) { + String srcIp = rule.getSrcIp(); + String dstIP = rule.getDstIp(); + String iNatRuleName = generateInatRuleName(srcIp, dstIP); + inat iNatRule = null; + + if (!rule.revoked()) { + try { + iNatRule = inat.get(_netscalerService, iNatRuleName); + } catch (nitro_exception e) { + if (e.getErrorCode() != NitroError.NS_RESOURCE_NOT_EXISTS) { + throw e; + } + } + + if (iNatRule == null) { + iNatRule = new inat(); + iNatRule.set_name(iNatRuleName); + iNatRule.set_publicip(srcIp); + iNatRule.set_privateip(dstIP); + iNatRule.set_usnip("ON"); + iNatRule.set_usip("OFF"); + try { + apiCallResult = inat.add(_netscalerService, iNatRule); + } catch (nitro_exception e) { + if (e.getErrorCode() != NitroError.NS_RESOURCE_EXISTS) { + throw e; + } + } + s_logger.debug("Created Inat rule on the Netscaler device " + _ip + " to enable static NAT from " + srcIp + " to " + dstIP); + } + } else { + try { + inat.delete(_netscalerService, iNatRuleName); + } catch (nitro_exception e) { + if (e.getErrorCode() != NitroError.NS_RESOURCE_NOT_EXISTS) { + throw e; + } + } + s_logger.debug("Deleted Inat rule on the Netscaler device " + _ip + " to remove static NAT from " + srcIp + " to " + dstIP); + } + + results[i++] = "Static nat rule from " + srcIp + " to " + dstIP + " successfully " + (rule.revoked() ? " revoked.":" created."); + } + } catch (Exception e) { + if (shouldRetry(numRetries)) { + return retry(cmd, numRetries); + } + results[i++] = "Configuring static nat rule failed due to " + e.getMessage(); + endResult = false; + return new SetStaticNatRulesAnswer(cmd, results, endResult); + } + + return new SetStaticNatRulesAnswer(cmd, results, endResult); + } + private synchronized Answer execute(ExternalNetworkResourceUsageCommand cmd, int numRetries) { try { if (!_isSdx) { @@ -1190,6 +1262,10 @@ public class NetscalerResource implements ServerResource { return false; } + private String generateInatRuleName(String srcIp, String dstIP) { + return genObjectName("Cloud-Inat", srcIp); + } + private String generateNSVirtualServerName(String srcIp, long srcPort, String protocol) { return genObjectName("Cloud-VirtualServer", protocol, srcIp, srcPort); } diff --git a/server/src/com/cloud/network/element/NetscalerElement.java b/server/src/com/cloud/network/element/NetscalerElement.java index 0e5f2e03b28..8ca9bf36eb6 100644 --- a/server/src/com/cloud/network/element/NetscalerElement.java +++ b/server/src/com/cloud/network/element/NetscalerElement.java @@ -31,6 +31,15 @@ import javax.ejb.Local; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.AgentManager.OnError; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.agent.manager.Commands; import com.cloud.api.ApiConstants; import com.cloud.api.commands.AddNetscalerLoadBalancerCmd; import com.cloud.api.commands.ConfigureNetscalerLoadBalancerCmd; @@ -40,6 +49,7 @@ import com.cloud.api.commands.ListNetscalerLoadBalancersCmd; import com.cloud.api.response.NetscalerLoadBalancerResponse; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeployDestination; @@ -55,6 +65,7 @@ import com.cloud.host.dao.HostDetailsDao; import com.cloud.network.ExternalLoadBalancerDeviceManager; import com.cloud.network.ExternalLoadBalancerDeviceManagerImpl; import com.cloud.network.ExternalLoadBalancerDeviceVO; +import com.cloud.network.IpAddress; import com.cloud.network.ExternalLoadBalancerDeviceVO.LBDeviceState; import com.cloud.network.ExternalNetworkDeviceManager.NetworkDevice; import com.cloud.network.Network; @@ -74,9 +85,11 @@ import com.cloud.network.dao.NetworkExternalLoadBalancerDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.resource.NetscalerResource; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LbStickinessMethod; +import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType; import com.cloud.network.rules.StaticNat; import com.cloud.offering.NetworkOffering; @@ -110,24 +123,29 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl @Inject NetworkDao _networkDao; @Inject HostDetailsDao _detailsDao; - private boolean canHandle(Network config) { - DataCenter zone = _dcDao.findById(config.getDataCenterId()); - boolean handleInAdvanceZone = (zone.getNetworkType() == NetworkType.Advanced && config.getGuestType() == Network.GuestType.Isolated && config.getTrafficType() == TrafficType.Guest); - boolean handleInBasicZone = (zone.getNetworkType() == NetworkType.Basic && config.getGuestType() == Network.GuestType.Shared && config.getTrafficType() == TrafficType.Guest); - + private boolean canHandle(Network config, Service service) { + DataCenter zone = _dcDao.findById(config.getDataCenterId()); + boolean handleInAdvanceZone = (zone.getNetworkType() == NetworkType.Advanced && config.getGuestType() == Network.GuestType.Isolated && config.getTrafficType() == TrafficType.Guest); + boolean handleInBasicZone = (zone.getNetworkType() == NetworkType.Basic && config.getGuestType() == Network.GuestType.Shared && config.getTrafficType() == TrafficType.Guest); + if (!(handleInAdvanceZone || handleInBasicZone)) { s_logger.trace("Not handling network with Type " + config.getGuestType() + " and traffic type " + config.getTrafficType() + " in zone of type " + zone.getNetworkType()); return false; } return (_networkManager.isProviderForNetwork(getProvider(), config.getId()) && - _ntwkSrvcDao.canProviderSupportServiceInNetwork(config.getId(), Service.Lb, Network.Provider.Netscaler)); + _ntwkSrvcDao.canProviderSupportServiceInNetwork(config.getId(), service, Network.Provider.Netscaler)); + } + + private boolean isBasicZoneNetwok(Network config) { + DataCenter zone = _dcDao.findById(config.getDataCenterId()); + return (zone.getNetworkType() == NetworkType.Basic && config.getGuestType() == Network.GuestType.Shared && config.getTrafficType() == TrafficType.Guest); } @Override public boolean implement(Network guestConfig, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientNetworkCapacityException { - if (!canHandle(guestConfig)) { + if (!canHandle(guestConfig, Service.Lb)) { return false; } @@ -145,13 +163,13 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl } @Override - public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { return true; } @Override public boolean shutdown(Network guestConfig, ReservationContext context, boolean cleanup) throws ResourceUnavailableException, ConcurrentOperationException { - if (!canHandle(guestConfig)) { + if (!canHandle(guestConfig, Service.Lb)) { return false; } @@ -170,18 +188,22 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl @Override public boolean applyLBRules(Network config, List rules) throws ResourceUnavailableException { - if (!canHandle(config)) { + if (!canHandle(config, Service.Lb)) { return false; } - - return applyLoadBalancerRules(config, rules); + + if (isBasicZoneNetwok(config)) { + return applyElasticLoadBalancerRules(config, rules); + } else { + return applyLoadBalancerRules(config, rules); + } } @Override public Map> getCapabilities() { - Map> capabilities = new HashMap>(); - - // Set capabilities for LB service + Map> capabilities = new HashMap>(); + + // Set capabilities for LB service Map lbCapabilities = new HashMap(); // Specifies that the RoundRobin and Leastconn algorithms are supported for load balancing rules @@ -493,7 +515,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl @Override public boolean applyIps(Network network, List ipAddress, Set service) throws ResourceUnavailableException { - // return true, as IP will be associated as part of LB rule configuration + // return true, as IP will be associated as part of LB rule configuration return false; } @@ -502,19 +524,123 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl return this; } - @Override - public boolean applyFWRules(Network network, - List rules) - throws ResourceUnavailableException { - // TODO - Murali, your code should go here - return true; - } + @Override + public boolean applyFWRules(Network network, List rules) throws ResourceUnavailableException { + return true; + } - @Override - public boolean applyStaticNats(Network config, - List rules) - throws ResourceUnavailableException { - // TODO - Murali, your code should go here - return true; - } + public boolean applyElasticLoadBalancerRules(Network network, List rules) throws ResourceUnavailableException { + + List loadBalancingRules = new ArrayList(); + for (FirewallRule rule : rules) { + if (rule.getPurpose().equals(Purpose.LoadBalancing)) { + loadBalancingRules.add((LoadBalancingRule) rule); + } + } + + if (loadBalancingRules == null || loadBalancingRules.isEmpty()) { + return true; + } + + String errMsg = null; + ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network); + if (lbDeviceVO == null) { + try { + lbDeviceVO = allocateLoadBalancerForNetwork(network); + } catch (Exception e) { + errMsg = "Could not allocate a NetSclaer load balancer for configuring elastic load balancer rules due to " + e.getMessage(); + s_logger.error(errMsg); + throw new ResourceUnavailableException(errMsg, this.getClass(), 0); + } + } + + if (!isNetscalerDevice(lbDeviceVO.getDeviceName())) { + errMsg = "There are no NetScaler load balancer assigned for this network. So NetScaler element can not be handle elastic load balancer rules."; + s_logger.error(errMsg); + throw new ResourceUnavailableException(errMsg, this.getClass(), 0); + } + + List loadBalancersToApply = new ArrayList(); + for (int i = 0; i < loadBalancingRules.size(); i++) { + LoadBalancingRule rule = loadBalancingRules.get(i); + boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke)); + String protocol = rule.getProtocol(); + String algorithm = rule.getAlgorithm(); + String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); + int srcPort = rule.getSourcePortStart(); + List destinations = rule.getDestinations(); + + if (destinations != null && !destinations.isEmpty()) { + LoadBalancerTO loadBalancer = new LoadBalancerTO(srcIp, srcPort, protocol, algorithm, revoked, false, destinations, rule.getStickinessPolicies()); + loadBalancersToApply.add(loadBalancer); + } + } + + if (loadBalancersToApply.size() > 0) { + int numLoadBalancersForCommand = loadBalancersToApply.size(); + LoadBalancerTO[] loadBalancersForCommand = loadBalancersToApply.toArray(new LoadBalancerTO[numLoadBalancersForCommand]); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(loadBalancersForCommand); + + HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId()); + Answer answer = _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "Unable to apply elastic load balancer rules to the external load balancer appliance in zone " + network.getDataCenterId() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); + } + } + + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + + if (!canHandle(config, Service.StaticNat)) { + return false; + } + + String errMsg; + ExternalLoadBalancerDeviceVO lbDevice = getExternalLoadBalancerForNetwork(config); + if (lbDevice == null) { + try { + lbDevice = allocateLoadBalancerForNetwork(config); + } catch (Exception e) { + errMsg = "Could not allocate a NetSclaer load balancer for configuring static NAT rules due to" + e.getMessage(); + s_logger.error(errMsg); + throw new ResourceUnavailableException(errMsg, this.getClass(), 0); + } + } + + if (!isNetscalerDevice(lbDevice.getDeviceName())) { + errMsg = "There are no NetScaler load balancer assigned for this network. So NetScaler element will not be handling the static nat rules."; + s_logger.error(errMsg); + throw new ResourceUnavailableException(errMsg, this.getClass(), 0); + } + + SetStaticNatRulesAnswer answer = null; + try { + List rulesTO = null; + if (rules != null) { + rulesTO = new ArrayList(); + for (StaticNat rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + StaticNatRuleTO ruleTO = new StaticNatRuleTO(0, sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(), false); + rulesTO.add(ruleTO); + } + } + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO); + answer = (SetStaticNatRulesAnswer )_agentMgr.send(lbDevice.getHostId(), cmd); + if (answer == null) { + return false; + } else { + return answer.getResult(); + } + } catch (Exception e) { + s_logger.error("Failed to configure StaticNat rule due to " + e.getMessage()); + return false; + } + } } \ No newline at end of file