Prevent network disruption on adding a new VPC tier
diff --git a/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java
index 5c3ee3f..cd04db8 100644
--- a/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java
+++ b/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java
@@ -29,7 +29,6 @@
/**
* @param router
* @param network
- * @param isRedundant
* @param params TODO
* @return
* @throws ConcurrentOperationException
@@ -42,11 +41,30 @@
/**
* @param router
* @param network
- * @param isRedundant
* @return
* @throws ConcurrentOperationException
* @throws ResourceUnavailableException
*/
boolean removeVpcRouterFromGuestNetwork(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException;
+
+ /**
+ * @param router
+ * @param network
+ * @return
+ * @throws ConcurrentOperationException
+ * @throws ResourceUnavailableException
+ */
+ boolean stopKeepAlivedOnRouter(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException;
+
+
+ /**
+ * @param router
+ * @param network
+ * @return
+ * @throws ConcurrentOperationException
+ * @throws ResourceUnavailableException
+ */
+ boolean startKeepAlivedOnRouter(VirtualRouter router, Network network) throws ConcurrentOperationException, ResourceUnavailableException;
+
}
diff --git a/core/src/main/java/com/cloud/agent/api/ManageServiceCommand.java b/core/src/main/java/com/cloud/agent/api/ManageServiceCommand.java
new file mode 100644
index 0000000..d058a32
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/ManageServiceCommand.java
@@ -0,0 +1,49 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.agent.api;
+
+import com.cloud.agent.api.routing.NetworkElementCommand;
+
+public class ManageServiceCommand extends NetworkElementCommand {
+
+ String serviceName;
+ String action;
+
+ @Override
+ public boolean executeInSequence() {
+ return true;
+ }
+
+ protected ManageServiceCommand() {
+ }
+
+ public ManageServiceCommand(String serviceName, String action) {
+ this.serviceName = serviceName;
+ this.action = action;
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public String getAction() {
+ return action;
+ }
+}
diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java
index ebe5e9a..e435c83 100644
--- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java
+++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java
@@ -81,4 +81,5 @@
public static final String VR_UPDATE_INTERFACE_CONFIG = "update_interface_config.sh";
public static final String ROUTER_FILESYSTEM_WRITABLE_CHECK = "filesystem_writable_check.py";
+ public static final String MANAGE_SERVICE = "manage_service.sh";
}
diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
index 4492947..1d2ca67 100644
--- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
+++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
@@ -34,6 +34,7 @@
import javax.naming.ConfigurationException;
+import com.cloud.agent.api.ManageServiceCommand;
import com.cloud.agent.api.routing.UpdateNetworkCommand;
import com.cloud.agent.api.to.IpAddressTO;
import com.cloud.network.router.VirtualRouter;
@@ -143,6 +144,10 @@
return execute((UpdateNetworkCommand) cmd);
}
+ if (cmd instanceof ManageServiceCommand) {
+ return execute((ManageServiceCommand) cmd);
+ }
+
if (_vrAggregateCommandsSet.containsKey(routerName)) {
_vrAggregateCommandsSet.get(routerName).add(cmd);
aggregated = true;
@@ -270,6 +275,17 @@
return new Answer(cmd, new CloudRuntimeException("Failed to update interface mtu"));
}
+ private Answer execute(ManageServiceCommand cmd) {
+ String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
+ String args = cmd.getAction() + " " + cmd.getServiceName();
+ ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.MANAGE_SERVICE, args);
+ if (result.isSuccess()) {
+ return new Answer(cmd, true, "Keepalived stopped successfully. Details: " + result.getDetails());
+ } else {
+ return new Answer(cmd, false, "Failed to stop keepalived. Detail: " + result.getDetails());
+ }
+ }
+
private ExecutionResult applyConfigToVR(String routerAccessIp, ConfigItem c) {
return applyConfigToVR(routerAccessIp, c, VRScripts.VR_SCRIPT_EXEC_TIMEOUT);
}
diff --git a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
index d740f80..86b4af6 100644
--- a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
+++ b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java
@@ -223,24 +223,57 @@
return true;
}
- protected void configureGuestNetwork(final Network network, final List<DomainRouterVO> routers )
- throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+ protected boolean configureGuestNetworkForRouter(final Network network,
+ final DomainRouterVO router) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
+ if (!_networkMdl.isVmPartOfNetwork(router.getId(), network.getId())) {
+ final Map<VirtualMachineProfile.Param, Object> paramsForRouter = new HashMap<VirtualMachineProfile.Param, Object>(1);
+ if (network.getState() == State.Setup) {
+ paramsForRouter.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true);
+ }
+ if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, paramsForRouter)) {
+ s_logger.error("Failed to add VPC router " + router + " to guest network " + network);
+ return false;
+ } else {
+ s_logger.debug("Successfully added VPC router " + router + " to guest network " + network);
+ return true;
+ }
+ }
+ return true;
+ }
+ protected void configureGuestNetwork(final Network network, final List<DomainRouterVO> routers)
+ throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
s_logger.info("Adding VPC routers to Guest Network: " + routers.size() + " to be added!");
- for (final DomainRouterVO router : routers) {
+ List<DomainRouterVO> backupRouters = new ArrayList<>();
+ List<DomainRouterVO> remainingRouters = new ArrayList<>();
+ for (DomainRouterVO router : routers) {
if (!_networkMdl.isVmPartOfNetwork(router.getId(), network.getId())) {
- final Map<VirtualMachineProfile.Param, Object> paramsForRouter = new HashMap<VirtualMachineProfile.Param, Object>(1);
- if (network.getState() == State.Setup) {
- paramsForRouter.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true);
- }
- if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, paramsForRouter)) {
- s_logger.error("Failed to add VPC router " + router + " to guest network " + network);
+ if (router.getRedundantState().equals(DomainRouterVO.RedundantState.BACKUP)) {
+ backupRouters.add(router);
} else {
- s_logger.debug("Successfully added VPC router " + router + " to guest network " + network);
+ remainingRouters.add(router);
}
}
}
+
+ for (final DomainRouterVO router : backupRouters) {
+ if (network.getState() != State.Setup) {
+ if (!_vpcRouterMgr.stopKeepAlivedOnRouter(router, network)) {
+ s_logger.error("Failed to stop keepalived on VPC router " + router + " to guest network " + network);
+ } else {
+ s_logger.debug("Successfully stopped keepalived on VPC router " + router + " to guest network " + network);
+ }
+ }
+ }
+ for (final DomainRouterVO router : remainingRouters) {
+ configureGuestNetworkForRouter(network, router);
+ }
+ for (final DomainRouterVO router : backupRouters) {
+ if (!configureGuestNetworkForRouter(network, router) && !_vpcRouterMgr.startKeepAlivedOnRouter(router, network)) {
+ s_logger.error("Failed to start keepalived on VPC router " + router + " to guest network " + network);
+ }
+ }
}
@Override
@@ -285,30 +318,7 @@
@Override
public boolean shutdown(final Network network, final ReservationContext context, final boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException {
- final Long vpcId = network.getVpcId();
- if (vpcId == null) {
- s_logger.debug("Network " + network + " doesn't belong to any vpc, so skipping unplug nic part");
- return true;
- }
-
- boolean success = true;
- final List<? extends VirtualRouter> routers = _routerDao.listByVpcId(vpcId);
- for (final VirtualRouter router : routers) {
- // 1) Check if router is already a part of the network
- if (!_networkMdl.isVmPartOfNetwork(router.getId(), network.getId())) {
- s_logger.debug("Router " + router + " is not a part the network " + network);
- continue;
- }
- // 2) Call unplugNics in the network service
- success = success && _vpcRouterMgr.removeVpcRouterFromGuestNetwork(router, network);
- if (!success) {
- s_logger.warn("Failed to unplug nic in network " + network + " for virtual router " + router);
- } else {
- s_logger.debug("Successfully unplugged nic in network " + network + " for virtual router " + router);
- }
- }
-
- return success;
+ return destroy(network, context);
}
@Override
diff --git a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
index 1c1dc56..04af18e 100644
--- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
@@ -27,6 +27,8 @@
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import com.cloud.agent.api.ManageServiceCommand;
+import com.cloud.agent.api.routing.NetworkElementCommand;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -226,6 +228,53 @@
return result;
}
+ @Override
+ public boolean stopKeepAlivedOnRouter(VirtualRouter router,
+ Network network) throws ConcurrentOperationException, ResourceUnavailableException {
+ return manageKeepalivedServiceOnRouter(router, network, "stop");
+ }
+
+ @Override
+ public boolean startKeepAlivedOnRouter(VirtualRouter router,
+ Network network) throws ConcurrentOperationException, ResourceUnavailableException {
+ return manageKeepalivedServiceOnRouter(router, network, "start");
+ }
+
+ private boolean manageKeepalivedServiceOnRouter(VirtualRouter router,
+ Network network, String action) throws ConcurrentOperationException, ResourceUnavailableException {
+ if (network.getTrafficType() != TrafficType.Guest) {
+ s_logger.warn("Network " + network + " is not of type " + TrafficType.Guest);
+ return false;
+ }
+ boolean result = true;
+ try {
+ if (router.getState() == State.Running) {
+ final ManageServiceCommand stopCommand = new ManageServiceCommand("keepalived", action);
+ stopCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
+
+ final Commands cmds = new Commands(Command.OnError.Stop);
+ cmds.addCommand("manageKeepalived", stopCommand);
+ _nwHelper.sendCommandsToRouter(router, cmds);
+
+ final Answer setupAnswer = cmds.getAnswer("manageKeepalived");
+ if (!(setupAnswer != null && setupAnswer.getResult())) {
+ s_logger.warn("Unable to " + action + " keepalived on router " + router);
+ result = false;
+ }
+ } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) {
+ s_logger.debug("Router " + router.getInstanceName() + " is in " + router.getState() + ", so not sending command to the backend");
+ } else {
+ String message = "Unable to " + action + " keepalived on virtual router [" + router + "] is not in the right state " + router.getState();
+ s_logger.warn(message);
+ throw new ResourceUnavailableException(message, DataCenter.class, router.getDataCenterId());
+ }
+ } catch (final Exception ex) {
+ s_logger.warn("Failed to " + action + " keepalived on router " + router + " to network " + network + " due to ", ex);
+ result = false;
+ }
+ return result;
+ }
+
protected boolean setupVpcGuestNetwork(final Network network, final VirtualRouter router, final boolean add, final NicProfile guestNic) throws ConcurrentOperationException,
ResourceUnavailableException {
diff --git a/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
index 3949fa8..27aff3a 100644
--- a/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
+++ b/server/src/test/java/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java
@@ -204,6 +204,20 @@
return false;
}
+ @Override
+ public boolean stopKeepAlivedOnRouter(VirtualRouter router,
+ Network network) throws ConcurrentOperationException, ResourceUnavailableException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean startKeepAlivedOnRouter(VirtualRouter router,
+ Network network) throws ConcurrentOperationException, ResourceUnavailableException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
/* (non-Javadoc)
* @see com.cloud.network.router.VpcVirtualNetworkApplianceManager#destroyPrivateGateway(com.cloud.network.vpc.PrivateGateway, com.cloud.network.router.VirtualRouter)
*/