From 8eee8f342eeb72418055defc4d1eb51ae155b592 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Fri, 27 Jul 2012 16:18:52 -0700 Subject: [PATCH] S2S VPN: CS-15650: Add connection status update to s2s vpn --- .../api/CheckS2SVpnConnectionsAnswer.java | 55 ++++++++++++ .../api/CheckS2SVpnConnectionsCommand.java | 23 +++++ .../VirtualRoutingResource.java | 19 +++++ .../config/opt/cloud/bin/checks2svpn.sh | 8 +- .../vmware/resource/VmwareResource.java | 31 +++++++ .../xen/resource/CitrixResourceBase.java | 16 ++++ .../VirtualNetworkApplianceManagerImpl.java | 85 ++++++++++++++++++- .../network/vpn/Site2SiteVpnManager.java | 6 ++ .../network/vpn/Site2SiteVpnManagerImpl.java | 13 +++ 9 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 api/src/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java create mode 100644 api/src/com/cloud/agent/api/CheckS2SVpnConnectionsCommand.java diff --git a/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java b/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java new file mode 100644 index 00000000000..b9e9a6638ab --- /dev/null +++ b/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java @@ -0,0 +1,55 @@ +package com.cloud.agent.api; + +import java.util.HashMap; +import java.util.Map; + +public class CheckS2SVpnConnectionsAnswer extends Answer { + Map ipToConnected; + Map ipToDetail; + String details; + + protected CheckS2SVpnConnectionsAnswer() { + ipToConnected = new HashMap(); + ipToDetail = new HashMap(); + } + + public CheckS2SVpnConnectionsAnswer(CheckS2SVpnConnectionsCommand cmd, boolean result, String details) { + super(cmd, result, details); + ipToConnected = new HashMap(); + ipToDetail = new HashMap(); + this.details = details; + if (result) { + parseDetails(details); + } + } + + protected void parseDetails(String details) { + String[] lines = details.split("&"); + for (String line : lines) { + String[] words = line.split(":"); + if (words.length != 3) { + //Not something we can parse + return; + } + String ip = words[0]; + boolean connected = words[1].equals("0"); + String detail = words[2]; + ipToConnected.put(ip, connected); + ipToDetail.put(ip, detail); + } + } + + public boolean isConnected(String ip) { + if (this.getResult()) { + return ipToConnected.get(ip); + } + return false; + } + + public String getDetail(String ip) { + if (this.getResult()) { + return ipToDetail.get(ip); + } + return null; + } +} diff --git a/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsCommand.java b/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsCommand.java new file mode 100644 index 00000000000..9401b47be65 --- /dev/null +++ b/api/src/com/cloud/agent/api/CheckS2SVpnConnectionsCommand.java @@ -0,0 +1,23 @@ +package com.cloud.agent.api; + +import java.util.List; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class CheckS2SVpnConnectionsCommand extends NetworkElementCommand { + List vpnIps; + + @Override + public boolean executeInSequence() { + return true; + } + + public CheckS2SVpnConnectionsCommand(List vpnIps) { + super(); + this.vpnIps = vpnIps; + } + + public List getVpnIps() { + return vpnIps; + } +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index fe6c45c303a..4081ff619b9 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -42,6 +42,8 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.BumpUpPriorityCommand; import com.cloud.agent.api.CheckRouterAnswer; import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; +import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; @@ -139,6 +141,8 @@ public class VirtualRoutingResource implements Manager { return execute((GetDomRVersionCmd)cmd); } else if (cmd instanceof Site2SiteVpnCfgCommand) { return execute((Site2SiteVpnCfgCommand)cmd); + } else if (cmd instanceof CheckS2SVpnConnectionsCommand) { + return execute((CheckS2SVpnConnectionsCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); @@ -500,6 +504,21 @@ public class VirtualRoutingResource implements Manager { return null; } + private CheckS2SVpnConnectionsAnswer execute(CheckS2SVpnConnectionsCommand cmd) { + final String routerIP = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + + String args = ""; + for (String ip : cmd.getVpnIps()) { + args += " " + ip; + } + + final String result = routerProxy("checkbatchs2svpn.sh", routerIP, args); + if (result == null || result.isEmpty()) { + return new CheckS2SVpnConnectionsAnswer(cmd, false, "CheckS2SVpnConneciontsCommand failed"); + } + return new CheckS2SVpnConnectionsAnswer(cmd, true, result); + } + protected Answer execute(CheckRouterCommand cmd) { final String routerPrivateIPAddress = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); diff --git a/patches/systemvm/debian/config/opt/cloud/bin/checks2svpn.sh b/patches/systemvm/debian/config/opt/cloud/bin/checks2svpn.sh index 3198824bc1b..e6bf9e52d31 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/checks2svpn.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/checks2svpn.sh @@ -12,20 +12,20 @@ cat /tmp/vpn-$1.status | grep "ISAKMP SA established" > /dev/null isakmpok=$? if [ $isakmpok -ne 0 ] then - echo "ISAKMP SA not found" + echo -n "ISAKMP SA not found" echo "Site-to-site VPN have not connected" exit 12 fi -echo "ISAKMP SA found" +echo -n "ISAKMP SA found;" cat /tmp/vpn-$1.status | grep "IPsec SA established" > /dev/null ipsecok=$? if [ $ipsecok -ne 0 ] then - echo "IPsec SA not found" + echo -n "IPsec SA not found;" echo "Site-to-site VPN have not connected" exit 11 fi -echo "IPsec SA found" +echo -n "IPsec SA found;" echo "Site-to-site VPN have connected" exit 0 diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index bb4198ffff2..7c486d8c8c8 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -53,6 +53,8 @@ import com.cloud.agent.api.CheckOnHostAnswer; import com.cloud.agent.api.CheckOnHostCommand; import com.cloud.agent.api.CheckRouterAnswer; import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; +import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.Command; @@ -1020,6 +1022,35 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return new Answer(cmd); } + protected CheckS2SVpnConnectionsAnswer execute(CheckS2SVpnConnectionsCommand cmd) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Executing resource CheckS2SVpnConnectionsCommand: " + _gson.toJson(cmd)); + s_logger.debug("Run command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + ", /opt/cloud/bin/checkbatchs2svpn.sh "); + } + + Pair result; + try { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + String controlIp = getRouterSshControlIp(cmd); + result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/checkbatchs2svpn.sh "); + + if (!result.first()) { + s_logger.error("check site-to-site vpn connections command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + " failed, message: " + result.second()); + + return new CheckS2SVpnConnectionsAnswer(cmd, false, result.second()); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("check site-to-site vpn connections command on domain router " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + " completed"); + } + } catch (Throwable e) { + String msg = "CheckS2SVpnConnectionsCommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new CheckS2SVpnConnectionsAnswer(cmd, false, "CheckS2SVpnConneciontsCommand failed"); + } + return new CheckS2SVpnConnectionsAnswer(cmd, true, result.second()); + } protected Answer execute(CheckRouterCommand cmd) { if (s_logger.isDebugEnabled()) { s_logger.debug("Executing resource CheckRouterCommand: " + _gson.toJson(cmd)); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index ef6a88847bd..8f3ca0b6426 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -66,6 +66,8 @@ import com.cloud.agent.api.CheckOnHostAnswer; import com.cloud.agent.api.CheckOnHostCommand; import com.cloud.agent.api.CheckRouterAnswer; import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; +import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CleanupNetworkRulesCmd; @@ -549,6 +551,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((SetStaticRouteCommand) cmd); } else if (clazz == Site2SiteVpnCfgCommand.class) { return execute((Site2SiteVpnCfgCommand) cmd); + } else if (clazz == CheckS2SVpnConnectionsCommand.class) { + return execute((CheckS2SVpnConnectionsCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -1400,6 +1404,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new Answer(cmd); } + private CheckS2SVpnConnectionsAnswer execute(CheckS2SVpnConnectionsCommand cmd) { + Connection conn = getConnection(); + String args = "checkbatchs2svpn.sh " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + for (String ip : cmd.getVpnIps()) { + args += " " + ip; + } + String result = callHostPlugin(conn, "vmops", "routerProxy", "args", args); + if (result == null || result.isEmpty()) { + return new CheckS2SVpnConnectionsAnswer(cmd, false, "CheckS2SVpnConneciontsCommand failed"); + } + return new CheckS2SVpnConnectionsAnswer(cmd, true, result); + } private CheckRouterAnswer execute(CheckRouterCommand cmd) { Connection conn = getConnection(); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index c812824efc2..da6cc7cded0 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -47,6 +47,8 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.BumpUpPriorityCommand; import com.cloud.agent.api.CheckRouterAnswer; import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; +import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; @@ -132,6 +134,11 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.PublicIpAddress; import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.Site2SiteCustomerGateway; +import com.cloud.network.Site2SiteCustomerGatewayVO; +import com.cloud.network.Site2SiteVpnConnection; +import com.cloud.network.Site2SiteVpnConnectionVO; +import com.cloud.network.Site2SiteVpnGatewayVO; import com.cloud.network.SshKeysDistriMonitor; import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.VirtualRouterProvider; @@ -165,6 +172,7 @@ import com.cloud.network.rules.StaticNat; import com.cloud.network.rules.StaticNatImpl; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.dao.PortForwardingRulesDao; +import com.cloud.network.vpn.Site2SiteVpnManager; import com.cloud.offering.ServiceOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -318,6 +326,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian Site2SiteVpnGatewayDao _s2sVpnGatewayDao; @Inject Site2SiteVpnConnectionDao _s2sVpnConnectionDao; + @Inject + Site2SiteVpnManager _s2sVpnMgr; int _routerRamSize; int _routerCpuMHz; @@ -901,7 +911,79 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } } - + protected void updateSite2SiteVpnConnectionState(List routers) { + for (DomainRouterVO router : routers) { + List conns = _s2sVpnMgr.getConnectionsForRouter(router); + if (conns == null || conns.isEmpty()) { + continue; + } + if (router.getState() != State.Running) { + for (Site2SiteVpnConnectionVO conn : conns) { + conn.setState(Site2SiteVpnConnection.State.Disconnected); + _s2sVpnConnectionDao.persist(conn); + } + continue; + } + List ipList = new ArrayList(); + for (Site2SiteVpnConnectionVO conn : conns) { + if (conn.getState() != Site2SiteVpnConnection.State.Connected && + conn.getState() != Site2SiteVpnConnection.State.Disconnected) { + continue; + } + Site2SiteCustomerGateway gw = _s2sCustomerGatewayDao.findById(conn.getCustomerGatewayId()); + ipList.add(gw.getGatewayIp()); + } + String privateIP = router.getPrivateIpAddress(); + HostVO host = _hostDao.findById(router.getHostId()); + if (host == null || host.getStatus() != Status.Up) { + continue; + } else if (host.getManagementServerId() != ManagementServerNode.getManagementServerId()) { + /* Only cover hosts managed by this management server */ + continue; + } else if (privateIP != null) { + final CheckS2SVpnConnectionsCommand command = new CheckS2SVpnConnectionsCommand(ipList); + command.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); + command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + command.setWait(30); + final Answer origAnswer = _agentMgr.easySend(router.getHostId(), command); + CheckS2SVpnConnectionsAnswer answer = null; + if (origAnswer instanceof CheckS2SVpnConnectionsAnswer) { + answer = (CheckS2SVpnConnectionsAnswer)origAnswer; + } else { + s_logger.warn("Unable to update router " + router.getHostName() + "'s VPN connection status"); + continue; + } + if (!answer.getResult()) { + s_logger.warn("Unable to update router " + router.getHostName() + "'s VPN connection status"); + continue; + } + for (Site2SiteVpnConnectionVO conn : conns) { + if (conn.getState() != Site2SiteVpnConnection.State.Connected && + conn.getState() != Site2SiteVpnConnection.State.Disconnected) { + continue; + } + Site2SiteVpnConnection.State oldState = conn.getState(); + Site2SiteCustomerGateway gw = _s2sCustomerGatewayDao.findById(conn.getCustomerGatewayId()); + if (answer.isConnected(gw.getGatewayIp())) { + conn.setState(Site2SiteVpnConnection.State.Connected); + } else { + conn.setState(Site2SiteVpnConnection.State.Disconnected); + } + _s2sVpnConnectionDao.persist(conn); + if (oldState != conn.getState()) { + String title = "Site-to-site Vpn Connection to " + gw.getName() + + " just switch from " + oldState + " to " + conn.getState(); + String context = "Site-to-site Vpn Connection to " + gw.getName() + " on router " + router.getHostName() + + "(id: " + router.getId() + ") " + " just switch from " + oldState + " to " + conn.getState(); + s_logger.info(context); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_DOMAIN_ROUTER, + router.getDataCenterIdToDeployIn(), router.getPodIdToDeployIn(), title, context); + } + } + } + } + } + protected void updateRoutersRedundantState(List routers) { boolean updated = false; for (DomainRouterVO router : routers) { @@ -1094,6 +1176,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.debug("Found " + routers.size() + " routers. "); updateRoutersRedundantState(routers); + updateSite2SiteVpnConnectionState(routers); /* FIXME assumed the a pair of redundant routers managed by same mgmt server, * then the update above can get the latest status */ diff --git a/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java b/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java index a44c3615ad4..567b76549df 100644 --- a/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java +++ b/server/src/com/cloud/network/vpn/Site2SiteVpnManager.java @@ -1,7 +1,13 @@ package com.cloud.network.vpn; +import java.util.List; + +import com.cloud.network.Site2SiteVpnConnectionVO; +import com.cloud.vm.DomainRouterVO; + public interface Site2SiteVpnManager extends Site2SiteVpnService { boolean cleanupVpnConnectionByVpc(long vpcId); boolean cleanupVpnGatewayByVpc(long vpcId); void markDisconnectVpnConnByVpc(long vpcId); + List getConnectionsForRouter(DomainRouterVO router); } diff --git a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java index 0abedef7ebc..0647de5c637 100644 --- a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java @@ -60,6 +60,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; +import com.cloud.vm.DomainRouterVO; @Local(value = { Site2SiteVpnManager.class, Site2SiteVpnService.class } ) public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { @@ -591,4 +592,16 @@ public class Site2SiteVpnManagerImpl implements Site2SiteVpnManager, Manager { } } } + + @Override + public List getConnectionsForRouter(DomainRouterVO router) { + List conns = new ArrayList(); + // One router for one VPC + Long vpcId = router.getVpcId(); + if (router.getVpcId() == null) { + return conns; + } + conns.addAll(_vpnConnectionDao.listByVpcId(vpcId)); + return conns; + } }