Copilot commented on code in PR #13766:
URL: https://github.com/apache/cloudstack/pull/13766#discussion_r3701578546


##########
plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java:
##########
@@ -196,6 +282,173 @@ public boolean deleteFirewallRules(Network network, 
List<NsxNetworkRule> netRule
         return result.getResult();
     }
 
+    public NsxVpnGatewayResult createVpnGateway(Vpc vpc, String 
localEndpointIp) {
+        CreateNsxVpnGatewayCommand createNsxVpnGatewayCommand = new 
CreateNsxVpnGatewayCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName(), localEndpointIp);
+        NsxAnswer result = 
nsxControllerUtils.sendNsxCommandForResult(createNsxVpnGatewayCommand, 
vpc.getZoneId());
+        return new NsxVpnGatewayResult(result.getResult(), 
result.isEndpointMayBeInUse());
+    }
+
+    public boolean deleteVpnGateway(Vpc vpc) {
+        DeleteNsxVpnGatewayCommand deleteNsxVpnGatewayCommand = new 
DeleteNsxVpnGatewayCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName());
+        NsxAnswer result = 
nsxControllerUtils.sendNsxCommand(deleteNsxVpnGatewayCommand, vpc.getZoneId());
+        return result.getResult();
+    }
+
+    public boolean createVpnConnection(Vpc vpc, String connectionUuid, String 
peerAddress, String psk,
+                                       String ikePolicy, String espPolicy, 
Long ikeLifetime, Long espLifetime,
+                                       boolean dpdEnabled, String ikeVersion, 
boolean passive, List<String> peerCidrs,
+                                       String vtiLocalIp, String vtiPeerIp, 
int vtiPrefixLength, String localEndpointIp) {
+        CreateNsxVpnConnectionCommand createNsxVpnConnectionCommand = new 
CreateNsxVpnConnectionCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName(), connectionUuid, peerAddress, psk,
+                ikePolicy, espPolicy, ikeLifetime, espLifetime, dpdEnabled, 
ikeVersion, passive, peerCidrs,
+                vtiLocalIp, vtiPeerIp, vtiPrefixLength, vpc.getCidr(), 
localEndpointIp);
+        NsxAnswer result = 
nsxControllerUtils.sendNsxCommand(createNsxVpnConnectionCommand, 
vpc.getZoneId());
+        return result.getResult();
+    }
+
+    public boolean deleteVpnConnection(Vpc vpc, String connectionUuid) {
+        DeleteNsxVpnConnectionCommand deleteNsxVpnConnectionCommand = new 
DeleteNsxVpnConnectionCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName(), connectionUuid);
+        NsxAnswer result = 
nsxControllerUtils.sendNsxCommand(deleteNsxVpnConnectionCommand, 
vpc.getZoneId());
+        return result.getResult();
+    }
+
+    public boolean updateVpnConnectionState(Vpc vpc, String connectionUuid, 
boolean enabled) {
+        UpdateNsxVpnConnectionStateCommand command = new 
UpdateNsxVpnConnectionStateCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName(), connectionUuid, enabled);
+        NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, 
vpc.getZoneId());
+        return result.getResult();
+    }
+
+    public String getVpnConnectionStatus(Vpc vpc, String connectionUuid) {
+        GetNsxVpnSessionStatusCommand getNsxVpnSessionStatusCommand = new 
GetNsxVpnSessionStatusCommand(vpc.getDomainId(),
+                vpc.getAccountId(), vpc.getZoneId(), vpc.getId(), 
vpc.getName(), connectionUuid);
+        NsxAnswer result = 
nsxControllerUtils.sendNsxCommand(getNsxVpnSessionStatusCommand, 
vpc.getZoneId());
+        return result.getDetails();
+    }
+
+    /**
+     * Every management server runs this poller over VPN connections whose 
gateway has persisted
+     * NSX ownership; duplicate polling in a multi-server setup is tolerated, 
as state transitions
+     * are serialized by the row lock in transitionVpnConnectionState
+     */
+    protected class VpnStatusPollTask extends ManagedContextRunnable {
+        @Override
+        protected void runInContext() {
+            try {
+                Set<Long> polledConnectionIds = new HashSet<>();
+                List<Site2SiteVpnConnectionVO> connections = 
site2SiteVpnConnectionDao.listAll();
+                for (Site2SiteVpnConnectionVO connection : connections) {

Review Comment:
   VpnStatusPollTask loads all Site2SiteVpnConnection rows 
(site2SiteVpnConnectionDao.listAll()) every poll interval, then filters in 
memory. On large installations this makes the poll interval proportional to 
total connections (including non-NSX/terminal states). Consider adding a DAO 
query that pre-filters to the polled states and (ideally) NSX-owned gateways, 
so each poll touches only relevant rows.



##########
plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java:
##########
@@ -16,49 +16,135 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
+import javax.naming.ConfigurationException;
 
 import org.apache.cloudstack.NsxAnswer;
 import 
org.apache.cloudstack.agent.api.CreateNsxDistributedFirewallRulesCommand;
 import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand;
 import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
+import org.apache.cloudstack.agent.api.CreateNsxVpnConnectionCommand;
+import org.apache.cloudstack.agent.api.CreateNsxVpnGatewayCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand;
 import 
org.apache.cloudstack.agent.api.DeleteNsxDistributedFirewallRulesCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand;
+import org.apache.cloudstack.agent.api.DeleteNsxVpnConnectionCommand;
+import org.apache.cloudstack.agent.api.DeleteNsxVpnGatewayCommand;
+import org.apache.cloudstack.agent.api.GetNsxVpnSessionStatusCommand;
+import org.apache.cloudstack.agent.api.UpdateNsxVpnConnectionStateCommand;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.cloudstack.managed.context.ManagedContextRunnable;
 import org.apache.cloudstack.resource.NsxNetworkRule;
+import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao;
 import org.apache.cloudstack.utils.NsxControllerUtils;
 import org.apache.cloudstack.utils.NsxHelper;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import com.cloud.alert.AlertManager;
 import com.cloud.network.IpAddress;
 import com.cloud.network.Network;
 import com.cloud.network.SDNProviderNetworkRule;
+import com.cloud.network.Site2SiteVpnConnection;
 import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.dao.Site2SiteVpnConnectionDao;
+import com.cloud.network.dao.Site2SiteVpnConnectionVO;
+import com.cloud.network.dao.Site2SiteVpnGatewayDao;
+import com.cloud.network.dao.Site2SiteVpnGatewayVO;
 import com.cloud.network.nsx.NsxService;
+import com.cloud.network.nsx.NsxVpnGatewayResult;
 import com.cloud.network.vpc.Vpc;
 import com.cloud.network.vpc.VpcVO;
 import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.concurrency.NamedThreadFactory;
 import com.cloud.utils.exception.CloudRuntimeException;
 
-public class NsxServiceImpl implements NsxService, Configurable {
+public class NsxServiceImpl extends ManagerBase implements NsxService, 
Configurable {
+
+    public static final ConfigKey<Integer> NSX_VPN_STATUS_POLL_INTERVAL = new 
ConfigKey<>("Advanced", Integer.class,
+            "nsx.vpn.status.poll.interval", "60",
+            "Interval (in seconds) between two NSX Site-to-Site VPN connection 
status polls; requires a management server restart",
+            false, ConfigKey.Scope.Global);
+
+    protected static final String VPN_SESSION_STATUS_UP = "UP";
+    protected static final String VPN_SESSION_STATUS_DOWN = "DOWN";
+    protected static final String VPN_SESSION_STATUS_DEGRADED = "DEGRADED";
+    protected static final String VPN_SESSION_STATUS_NOT_FOUND = "NOT_FOUND";
+    protected static final int VPN_STATUS_POLL_FAILURE_THRESHOLD = 3;
+    protected static final int VPN_STATUS_POLL_MIN_INTERVAL = 10;
+    protected static final int VPN_STATUS_POLL_DEFAULT_INTERVAL = 60;
+
+    private static final List<Site2SiteVpnConnection.State> VPN_POLLED_STATES 
= List.of(
+            Site2SiteVpnConnection.State.Pending, 
Site2SiteVpnConnection.State.Connecting, Site2SiteVpnConnection.State.Connected,
+            Site2SiteVpnConnection.State.Disconnected);
+
     @Inject
     NsxControllerUtils nsxControllerUtils;
     @Inject
     VpcDao vpcDao;
+    @Inject
+    Site2SiteVpnConnectionDao site2SiteVpnConnectionDao;
+    @Inject
+    Site2SiteVpnGatewayDao site2SiteVpnGatewayDao;
+    @Inject
+    UserIpAddressDetailsDao userIpAddressDetailsDao;
+    @Inject
+    AlertManager alertManager;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
+    private ScheduledExecutorService vpnStatusPollExecutor;
+    private final Map<Long, Integer> vpnStatusPollFailures = new 
ConcurrentHashMap<>();
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws 
ConfigurationException {
+        super.configure(name, params);
+        vpnStatusPollExecutor = Executors.newSingleThreadScheduledExecutor(new 
NamedThreadFactory("Nsx-Vpn-Status-Poll"));
+        return true;
+    }
+
+    @Override
+    public boolean start() {
+        super.start();
+        if (vpnStatusPollExecutor == null) {
+            throw new IllegalStateException("NSX VPN status poller was not 
configured");
+        }
+        Integer configuredInterval = NSX_VPN_STATUS_POLL_INTERVAL.value();
+        int pollInterval = Objects.isNull(configuredInterval) ? 
VPN_STATUS_POLL_DEFAULT_INTERVAL : configuredInterval;
+        if (pollInterval < VPN_STATUS_POLL_MIN_INTERVAL) {
+            logger.warn("The configured value {} of {} is below the minimum of 
{} seconds, using the default of {} seconds",
+                    configuredInterval, NSX_VPN_STATUS_POLL_INTERVAL.key(), 
VPN_STATUS_POLL_MIN_INTERVAL, VPN_STATUS_POLL_DEFAULT_INTERVAL);
+            pollInterval = VPN_STATUS_POLL_DEFAULT_INTERVAL;
+        }
+        vpnStatusPollExecutor.scheduleWithFixedDelay(new VpnStatusPollTask(), 
pollInterval, pollInterval, TimeUnit.SECONDS);
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+        if (Objects.nonNull(vpnStatusPollExecutor)) {
+            vpnStatusPollExecutor.shutdownNow();
+        }
+        return super.stop();
+    }

Review Comment:
   NsxServiceImpl.stop() shuts down vpnStatusPollExecutor but start() never 
recreates it. If this manager is stopped and then started again in the same JVM 
(e.g., component restart), scheduleWithFixedDelay will run on a shut-down 
executor (RejectedExecutionException) or fail the configured check, leaving VPN 
status polling disabled.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to