/** * Copyright (C) 2011 Cloud.com, Inc. All rights reserved. */ package com.cloud.network; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.api.commands.AddExternalFirewallCmd; import com.cloud.api.commands.DeleteExternalFirewallCmd; import com.cloud.api.commands.ListExternalFirewallsCmd; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Vlan; import com.cloud.dc.VlanVO; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.InlineLoadBalancerNicMapDao; import com.cloud.network.resource.JuniperSrxResource; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.StaticNatRuleImpl; import com.cloud.offering.NetworkOffering; import com.cloud.server.api.response.ExternalFirewallResponse; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.utils.component.Inject; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.UrlUtil; import com.cloud.vm.Nic.ReservationStrategy; import com.cloud.vm.Nic.State; import com.cloud.vm.NicVO; import com.cloud.vm.dao.NicDao; @Local(value = { ExternalFirewallManager.class }) public class JuniperSrxManagerImpl extends ExternalNetworkManagerImpl implements ExternalFirewallManager { @Inject NetworkManager _networkMgr; @Inject NicDao _nicDao; @Inject AccountDao _accountDao; @Inject DataCenterDao _dcDao; @Inject FirewallRulesDao _firewallRulesDao; @Inject UserStatisticsDao _userStatsDao; @Inject VlanDao _vlanDao; @Inject ConfigurationManager _configMgr; @Inject AccountManager _accountMgr; private static final org.apache.log4j.Logger s_logger = Logger.getLogger(JuniperSrxManagerImpl.class); @Override public Host addExternalFirewall(AddExternalFirewallCmd cmd) { long zoneId = cmd.getZoneId(); DataCenterVO zone = _dcDao.findById(zoneId); String zoneName; if (zone == null) { throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); } else { zoneName = zone.getName(); } List externalFirewallsInZone = _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); if (externalFirewallsInZone.size() != 0) { throw new InvalidParameterValueException("Already added an external firewall in zone: " + zoneName); } URI uri; try { uri = new URI(cmd.getUrl()); } catch (Exception e) { s_logger.debug(e); throw new InvalidParameterValueException(e.getMessage()); } String ipAddress = uri.getHost(); String username = cmd.getUsername(); String password = cmd.getPassword(); Map params = new HashMap(); UrlUtil.parseQueryParameters(uri.getQuery(), true, params); String publicInterface = params.get("publicinterface"); String usageInterface = params.get("usageinterface"); String privateInterface = params.get("privateinterface"); String publicZone = params.get("publiczone"); String privateZone = params.get("privatezone"); String numRetries = params.get("numretries"); String timeout = params.get("timeout"); if (publicInterface == null) { throw new InvalidParameterValueException("Please specify a public interface."); } if (usageInterface != null) { if (!usageInterface.contains(".")) { usageInterface += ".0"; } } if (privateInterface != null) { if (privateInterface.contains(".")) { throw new InvalidParameterValueException("The private interface name must not have a unit identifier."); } } else { throw new InvalidParameterValueException("Please specify a private interface."); } if (publicZone == null) { publicZone = "untrust"; } if (privateZone == null) { privateZone = "trust"; } if (numRetries == null) { numRetries = "1"; } if (timeout == null) { timeout = "300"; } JuniperSrxResource resource = new JuniperSrxResource(); String guid = getExternalNetworkResourceGuid(zoneId, ExternalNetworkResourceName.JuniperSrx, ipAddress); Map hostDetails = new HashMap(); hostDetails.put("zoneId", String.valueOf(zoneId)); hostDetails.put("ip", ipAddress); hostDetails.put("username", username); hostDetails.put("password", password); hostDetails.put("publicInterface", publicInterface); hostDetails.put("privateInterface", privateInterface); hostDetails.put("publicZone", publicZone); hostDetails.put("privateZone", privateZone); hostDetails.put("numRetries", numRetries); hostDetails.put("timeout", timeout); hostDetails.put("guid", guid); hostDetails.put("name", guid); if (usageInterface != null) { hostDetails.put("usageInterface", usageInterface); } try { resource.configure(guid, hostDetails); } catch (ConfigurationException e) { throw new CloudRuntimeException(e.getMessage()); } Host externalFirewall = _agentMgr.addHost(zoneId, resource, Host.Type.ExternalFirewall, hostDetails); if (externalFirewall != null) { zone.setFirewallProvider(Network.Provider.JuniperSRX.getName()); zone.setUserDataProvider(Network.Provider.DhcpServer.getName()); zone.setVpnProvider(null); if (zone.getGatewayProvider() == null || !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { zone.setGatewayProvider(Network.Provider.JuniperSRX.getName()); } if (zone.getDnsProvider() == null || !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { zone.setDnsProvider(Network.Provider.DhcpServer.getName()); } if (zone.getDhcpProvider() == null || !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { zone.setDhcpProvider(Network.Provider.DhcpServer.getName()); } if (zone.getLoadBalancerProvider() == null || !zone.getLoadBalancerProvider().equals(Network.Provider.F5BigIp.getName())) { zone.setLoadBalancerProvider(Network.Provider.None.getName()); } _dcDao.update(zone.getId(), zone); return externalFirewall; } else { return null; } } @Override public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd) { long hostId = cmd.getId(); User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); HostVO externalFirewall = _hostDao.findById(hostId); if (externalFirewall == null) { throw new InvalidParameterValueException("Could not find an external firewall with ID: " + hostId); } try { if (_agentMgr.maintain(hostId) && _agentMgr.deleteHost(hostId, false, false, caller)) { DataCenterVO zone = _dcDao.findById(externalFirewall.getDataCenterId()); zone.setFirewallProvider(Network.Provider.VirtualRouter.getName()); zone.setUserDataProvider(Network.Provider.VirtualRouter.getName()); zone.setVpnProvider(Network.Provider.VirtualRouter.getName()); if (zone.getGatewayProvider() != null && !zone.getGatewayProvider().equals(Network.Provider.ExternalGateWay)) { zone.setGatewayProvider(Network.Provider.VirtualRouter.getName()); } if (zone.getDnsProvider() != null && !zone.getDnsProvider().equals(Network.Provider.ExternalDhcpServer)) { zone.setDnsProvider(Network.Provider.VirtualRouter.getName()); } if (zone.getDhcpProvider() != null && !zone.getDhcpProvider().equals(Network.Provider.ExternalDhcpServer)) { zone.setDhcpProvider(Network.Provider.VirtualRouter.getName()); } if (zone.getLoadBalancerProvider() != null && zone.getLoadBalancerProvider().equals(Network.Provider.None)) { if (zone.getNetworkType().equals(NetworkType.Advanced)) { zone.setLoadBalancerProvider(Network.Provider.VirtualRouter.getName()); } else if (zone.getNetworkType().equals(NetworkType.Basic)) { zone.setLoadBalancerProvider(null); } } return _dcDao.update(zone.getId(), zone); } else { return false; } } catch (AgentUnavailableException e) { s_logger.debug(e); return false; } } @Override public List listExternalFirewalls(ListExternalFirewallsCmd cmd) { long zoneId = cmd.getZoneId(); return _hostDao.listByTypeDataCenter(Host.Type.ExternalFirewall, zoneId); } @Override public ExternalFirewallResponse getApiResponse(Host externalFirewall) { Map fwDetails = _detailsDao.findDetails(externalFirewall.getId()); ExternalFirewallResponse response = new ExternalFirewallResponse(); response.setId(externalFirewall.getId()); response.setIpAddress(externalFirewall.getPrivateIpAddress()); response.setUsername(fwDetails.get("username")); response.setPublicInterface(fwDetails.get("publicInterface")); response.setUsageInterface(fwDetails.get("usageInterface")); response.setPrivateInterface(fwDetails.get("privateInterface")); response.setPublicZone(fwDetails.get("publicZone")); response.setPrivateZone(fwDetails.get("privateZone")); response.setNumRetries(fwDetails.get("numRetries")); response.setTimeout(fwDetails.get("timeout")); return response; } @Override public boolean manageGuestNetwork(boolean add, Network network, NetworkOffering offering) throws ResourceUnavailableException { if (network.getTrafficType() != TrafficType.Guest) { s_logger.trace("JuniperSrxManager can only add/remove guest networks."); return false; } // Find the external firewall in this zone long zoneId = network.getDataCenterId(); DataCenterVO zone = _dcDao.findById(zoneId); HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); if (externalFirewall == null) { return false; } Account account = _accountDao.findByIdIncludingRemoved(network.getAccountId()); boolean sharedSourceNat = offering.isSharedSourceNatService(); IPAddressVO sourceNatIp = null; if (!sharedSourceNat) { // Get the source NAT IP address for this network List sourceNatIps = _networkMgr.listPublicIpAddressesInVirtualNetwork(network.getAccountId(), zoneId, true, null); if (sourceNatIps.size() != 1) { String errorMsg = "JuniperSrxManager was unable to find the source NAT IP address for account " + account.getAccountName(); s_logger.error(errorMsg); return true; } else { sourceNatIp = sourceNatIps.get(0); } } // Send a command to the external firewall to implement or shutdown the guest network long guestVlanTag = Long.parseLong(network.getBroadcastUri().getHost()); String guestVlanGateway = network.getGateway(); String guestVlanCidr = network.getCidr(); String sourceNatIpAddress = sourceNatIp.getAddress().addr(); VlanVO publicVlan = _vlanDao.findById(sourceNatIp.getVlanId()); String publicVlanTag = publicVlan.getVlanTag(); // Get network rate Integer networkRate = _networkMgr.getNetworkRate(network.getId(), null); IpAddressTO ip = new IpAddressTO(account.getId(), sourceNatIpAddress, add, false, !sharedSourceNat, publicVlanTag, null, null, null, null, networkRate, sourceNatIp.isOneToOneNat()); IpAddressTO[] ips = new IpAddressTO[1]; ips[0] = ip; IpAssocCommand cmd = new IpAssocCommand(ips); cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, guestVlanGateway); cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, guestVlanCidr); cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, String.valueOf(guestVlanTag)); Answer answer = _agentMgr.easySend(externalFirewall.getId(), cmd); if (answer == null || !answer.getResult()) { String action = add ? "implement" : "shutdown"; String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; String msg = "JuniperSrxManager was unable to " + action + " the guest network on the external firewall in zone " + zone.getName() + " due to " + answerDetails; s_logger.error(msg); throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); } List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(network.getId()); if (add && (!reservedIpAddressesForGuestNetwork.contains(network.getGateway()))) { // Insert a new NIC for this guest network to reserve the gateway address savePlaceholderNic(network, network.getGateway()); } // Delete any mappings used for inline external load balancers in this network List nicsInNetwork = _nicDao.listByNetworkId(network.getId()); for (NicVO nic : nicsInNetwork) { InlineLoadBalancerNicMapVO mapping = _inlineLoadBalancerNicMapDao.findByNicId(nic.getId()); if (mapping != null) { _nicDao.expunge(mapping.getNicId()); _inlineLoadBalancerNicMapDao.expunge(mapping.getId()); } } String action = add ? "implemented" : "shut down"; s_logger.debug("JuniperSrxManager has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); return true; } public void applyStaticNatRuleForInlineLBRule(DataCenterVO zone, Network network, HostVO externalFirewall, boolean revoked, String publicIp, String privateIp) throws ResourceUnavailableException { List staticNatRules = new ArrayList(); IPAddressVO ipVO = _ipAddressDao.listByDcIdIpAddress(zone.getId(), publicIp).get(0); VlanVO vlan = _vlanDao.findById(ipVO.getVlanId()); FirewallRuleVO fwRule = new FirewallRuleVO(null, ipVO.getId(), -1, -1, "any", network.getId(), network.getAccountId(), network.getDomainId(), Purpose.StaticNat, null, null, null, null); FirewallRule.State state = !revoked ? FirewallRule.State.Add : FirewallRule.State.Revoke; fwRule.setState(state); StaticNatRule rule = new StaticNatRuleImpl(fwRule, privateIp); StaticNatRuleTO ruleTO = new StaticNatRuleTO(rule, vlan.getVlanTag(), publicIp, privateIp); staticNatRules.add(ruleTO); applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); } @Override public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { // Find the external firewall in this zone long zoneId = network.getDataCenterId(); DataCenterVO zone = _dcDao.findById(zoneId); HostVO externalFirewall = getExternalNetworkAppliance(zoneId, Host.Type.ExternalFirewall); if (externalFirewall == null) { return false; } if (network.getState() == Network.State.Allocated) { s_logger.debug("JuniperSrxManager was asked to apply firewall rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); return true; } List staticNatRules = new ArrayList(); List portForwardingRules = new ArrayList(); for (FirewallRule rule : rules) { IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); if (rule.getPurpose() == Purpose.StaticNat) { StaticNatRule staticNatRule = (StaticNatRule) rule; StaticNatRuleTO ruleTO = new StaticNatRuleTO(staticNatRule, vlan.getVlanTag(), sourceIp.getAddress().addr(), staticNatRule.getDestIpAddress()); staticNatRules.add(ruleTO); } else if (rule.getPurpose() == Purpose.PortForwarding) { PortForwardingRuleTO ruleTO = new PortForwardingRuleTO((PortForwardingRule) rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); portForwardingRules.add(ruleTO); } } // Apply static nat rules applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); // apply port forwarding rules applyPortForwardingRules(portForwardingRules, zone, externalFirewall.getId()); return true; } protected void applyStaticNatRules(List staticNatRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { if (!staticNatRules.isEmpty()) { SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(staticNatRules); Answer answer = _agentMgr.easySend(externalFirewallId, cmd); if (answer == null || !answer.getResult()) { String details = (answer != null) ? answer.getDetails() : "details unavailable"; String msg = "JuniperSrxManager was unable to apply static nat rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; s_logger.error(msg); throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); } } } protected void applyPortForwardingRules(List portForwardingRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { if (!portForwardingRules.isEmpty()) { SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(portForwardingRules); Answer answer = _agentMgr.easySend(externalFirewallId, cmd); if (answer == null || !answer.getResult()) { String details = (answer != null) ? answer.getDetails() : "details unavailable"; String msg = "JuniperSrxManager was unable to apply port forwarding rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; s_logger.error(msg); throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); } } } @Override public boolean applyIps(Network network, List ipAddresses) throws ResourceUnavailableException { return true; } }