diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 7cc5441603a..81447d6457c 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -41,6 +41,8 @@ public interface Network extends ControlledEntity, StateObject, I Shared, Isolated } + public String updatingInSequence ="updatingInSequence"; + public static class Service { private static List supportedServices = new ArrayList(); diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index c1b68eb3a46..7a8a9498780 100644 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -77,7 +77,7 @@ public interface NetworkService { IpAddress getIp(long id); Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, Long networkOfferingId, - Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID); + Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID, boolean updateInSequence, boolean forced); PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed, List isolationMethods, String broadcastDomainRange, Long domainId, List tags, String name); diff --git a/api/src/com/cloud/network/element/RedundantResource.java b/api/src/com/cloud/network/element/RedundantResource.java new file mode 100644 index 00000000000..39b6b97d73a --- /dev/null +++ b/api/src/com/cloud/network/element/RedundantResource.java @@ -0,0 +1,27 @@ +// 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.network.element; + +import com.cloud.network.Network; + +/** + * Created by bharat on 11/08/15. + */ +public interface RedundantResource { + public void configureResource(Network network); + public int getResourceCount(Network network); +} diff --git a/api/src/com/cloud/network/router/VirtualRouter.java b/api/src/com/cloud/network/router/VirtualRouter.java index 0114a962146..060ef0fa141 100644 --- a/api/src/com/cloud/network/router/VirtualRouter.java +++ b/api/src/com/cloud/network/router/VirtualRouter.java @@ -26,6 +26,10 @@ public interface VirtualRouter extends VirtualMachine { VIRTUAL_ROUTER, LB, INTERNAL_LB_VM } + public enum UpdateState { + UPDATE_NEEDED, UPDATE_IN_PROGRESS, UPDATE_COMPLETE, UPDATE_FAILED + } + Role getRole(); boolean getIsRedundantRouter(); diff --git a/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java b/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java index decf8c43733..d089b852449 100644 --- a/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java +++ b/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java @@ -33,7 +33,7 @@ public interface RemoteAccessVpnService { RemoteAccessVpn createRemoteAccessVpn(long vpnServerAddressId, String ipRange, boolean openFirewall, Boolean forDisplay) throws NetworkRuleConflictException; - boolean destroyRemoteAccessVpnForIp(long ipId, Account caller) throws ResourceUnavailableException; + boolean destroyRemoteAccessVpnForIp(long ipId, Account caller, boolean forceCleanup) throws ResourceUnavailableException; RemoteAccessVpn startRemoteAccessVpn(long vpnServerAddressId, boolean openFirewall) throws ResourceUnavailableException; diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 1d0b4a3756a..0e838490eb0 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -272,6 +272,7 @@ public class ApiConstants { public static final String USERNAME = "username"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; + public static final String Update_IN_SEQUENCE ="updateinsequence"; public static final String VALUE = "value"; public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java b/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java index 269f43ec959..388348c592c 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/UpdateNetworkCmdByAdmin.java @@ -49,7 +49,7 @@ public class UpdateNetworkCmdByAdmin extends UpdateNetworkCmd { } Network result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount, - callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId()); + callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence(),getForced()); if (result != null) { diff --git a/api/src/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java index 921e74b67b2..c313f369b0e 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java @@ -75,11 +75,17 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.GUEST_VM_CIDR, type = CommandType.STRING, description = "CIDR for guest VMs, CloudStack allocates IPs to guest VMs only from this CIDR") private String guestVmCidr; + @Parameter(name =ApiConstants.Update_IN_SEQUENCE, type=CommandType.BOOLEAN, description = "if true, we will update the routers one after the other. applicable only for redundant router based networks using virtual router as provider") + private Boolean updateInSequence; + @Parameter(name = ApiConstants.DISPLAY_NETWORK, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayNetwork; + @Parameter(name= ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "Setting this to true will cause a forced network update,", authorized = {RoleType.Admin}) + private Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -119,6 +125,19 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd { return displayNetwork; } + public Boolean getUpdateInSequence(){ + if(updateInSequence ==null) + return false; + else + return updateInSequence; + } + + public boolean getForced(){ + if(forced==null){ + return false; + } + return forced; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -149,7 +168,7 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd { Network result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount, callerUser, getNetworkDomain(), getNetworkOfferingId(), - getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId()); + getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence(), getForced()); if (result != null) { NetworkResponse response = _responseGenerator.createNetworkResponse(ResponseView.Restricted, result); diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java index 37b7b5aaa3e..12ab531375f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteRemoteAccessVpnCmd.java @@ -93,7 +93,7 @@ public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd { @Override public void execute() throws ResourceUnavailableException { - if (! _ravService.destroyRemoteAccessVpnForIp(publicIpId, CallContext.current().getCallingAccount())) { + if (! _ravService.destroyRemoteAccessVpnForIp(publicIpId, CallContext.current().getCallingAccount(), false)) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete remote access vpn"); } } diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index b71aa96d5cc..89bec1783f6 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -224,4 +224,14 @@ public interface NetworkOrchestrationService { boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType); void prepareAllNicsForMigration(VirtualMachineProfile vm, DeployDestination dest); + + boolean canUpdateInSequence(Network network); + + List getServicesNotSupportedInNewOffering(Network network, long newNetworkOfferingId); + + void cleanupConfigForServicesInNetwork(List services, Network network); + + void configureUpdateInSequence(Network network); + + int getResourceCount(Network network); } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 66185c60978..5a89dac7810 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -36,6 +36,15 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.Networks; + +import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.RemoteAccessVpnVO; +import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.element.RedundantResource; +import com.cloud.vm.dao.DomainRouterDao; +import org.apache.log4j.Logger; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO; @@ -50,7 +59,6 @@ import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.region.PortableIpDao; -import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -107,7 +115,6 @@ import com.cloud.network.NetworkMigrationResponder; import com.cloud.network.NetworkModel; import com.cloud.network.NetworkProfile; import com.cloud.network.NetworkStateListener; -import com.cloud.network.Networks; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; @@ -265,9 +272,16 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra MessageBus _messageBus; @Inject VMNetworkMapDao _vmNetworkMapDao; + @Inject + DomainRouterDao _rotuerDao; + @Inject + RemoteAccessVpnDao _remoteAccessVpnDao; + @Inject + VpnUserDao _vpnUserDao; List networkGurus; + public List getNetworkGurus() { return networkGurus; } @@ -350,6 +364,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra PortableIpDao _portableIpDao; @Inject ConfigDepot _configDepot; + @Inject + NetworkDetailsDao _networkDetailsDao; protected StateMachine2 _stateMachine; ScheduledExecutorService _executor; @@ -1271,6 +1287,152 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return true; } + @Override + public boolean canUpdateInSequence(Network network){ + List providers = getNetworkProviders(network.getId()); + + //check if the there are no service provider other than virtualrouter. + for(Provider provider :providers){ + if(provider!=Provider.VirtualRouter) + throw new UnsupportedOperationException("Cannot update the network resources in sequence when providers other than virtualrouter are used"); + } + return true; + } + + @Override + public List getServicesNotSupportedInNewOffering(Network network,long newNetworkOfferingId){ + NetworkOffering offering =_networkOfferingDao.findById(newNetworkOfferingId); + List services=_ntwkOfferingSrvcDao.listServicesForNetworkOffering(offering.getId()); + List serviceMap= _ntwkSrvcDao.getServicesInNetwork(network.getId()); + List servicesNotInNewOffering=new ArrayList<>(); + for(NetworkServiceMapVO serviceVO :serviceMap){ + boolean inlist=false; + for(String service: services){ + if(serviceVO.getService().equalsIgnoreCase(service)){ + inlist=true; + break; + } + } + if(!inlist){ + //ignore Gateway service as this has no effect on the + //behaviour of network. + if(!serviceVO.getService().equalsIgnoreCase(Service.Gateway.getName())) + servicesNotInNewOffering.add(serviceVO.getService()); + } + } + return servicesNotInNewOffering; + } + + @Override + public void cleanupConfigForServicesInNetwork(List services, final Network network){ + long networkId=network.getId(); + Account caller=_accountDao.findById(Account.ACCOUNT_ID_SYSTEM); + long userId=User.UID_SYSTEM; + //remove all PF/Static Nat rules for the network + s_logger.info("Services:"+services+" are no longer supported in network:"+network.getUuid()+ + " after applying new network offering:"+network.getNetworkOfferingId()+" removing the related configuration"); + if(services.contains(Service.StaticNat.getName())|| services.contains(Service.PortForwarding.getName())) { + try { + if (_rulesMgr.revokeAllPFStaticNatRulesForNetwork(networkId, userId, caller)) { + s_logger.debug("Successfully cleaned up portForwarding/staticNat rules for network id=" + networkId); + } else { + s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup"); + } + if(services.contains(Service.StaticNat.getName())){ + //removing static nat configured on ips. + //optimizing the db operations using transaction. + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List ips = _ipAddressDao.listStaticNatPublicIps(network.getId()); + for (IPAddressVO ip : ips) { + ip.setOneToOneNat(false); + ip.setAssociatedWithVmId(null); + ip.setVmIp(null); + _ipAddressDao.update(ip.getId(),ip); + } + } + }); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to release portForwarding/StaticNat rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex); + } + } + if(services.contains(Service.SourceNat.getName())){ + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List ips = _ipAddressDao.listByAssociatedNetwork(network.getId(),true); + //removing static nat configured on ips. + for (IPAddressVO ip : ips) { + ip.setSourceNat(false); + _ipAddressDao.update(ip.getId(),ip); + } + } + }); + } + if(services.contains(Service.Lb.getName())){ + //remove all LB rules for the network + if (_lbMgr.removeAllLoadBalanacersForNetwork(networkId, caller, userId)) { + s_logger.debug("Successfully cleaned up load balancing rules for network id=" + networkId); + } else { + s_logger.warn("Failed to cleanup LB rules as a part of network id=" + networkId + " cleanup"); + } + } + + if(services.contains(Service.Firewall.getName())){ + //revoke all firewall rules for the network + try { + if (_firewallMgr.revokeAllFirewallRulesForNetwork(networkId, userId, caller)) { + s_logger.debug("Successfully cleaned up firewallRules rules for network id=" + networkId); + } else { + s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup"); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to cleanup Firewall rules as a part of network id=" + networkId + " cleanup due to resourceUnavailable ", ex); + } + } + + //do not remove vpn service for vpc networks. + if(services.contains(Service.Vpn.getName()) && network.getVpcId()==null){ + RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByAccountAndNetwork(network.getAccountId(),networkId); + try { + _vpnMgr.destroyRemoteAccessVpnForIp(vpn.getServerAddressId(), caller, true); + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to cleanup remote access vpn resources of network:"+network.getUuid() + " due to Exception: ", ex); + } + } + } + + @Override + public void configureUpdateInSequence(Network network) { + List providers = getNetworkProviders(network.getId()); + for (NetworkElement element : networkElements) { + if (providers.contains(element.getProvider())) { + if (element instanceof RedundantResource) { + ((RedundantResource) element).configureResource(network); + } + } + } + } + + @Override + public int getResourceCount(Network network){ + List providers = getNetworkProviders(network.getId()); + int resourceCount=0; + for (NetworkElement element : networkElements) { + if (providers.contains(element.getProvider())) { + //currently only one element implements the redundant resource interface + if (element instanceof RedundantResource) { + resourceCount= ((RedundantResource) element).getResourceCount(network); + break; + } + } + } + return resourceCount; + } + + @DB protected void updateNic(final NicVO nic, final long networkId, final int count) { Transaction.execute(new TransactionCallbackNoReturn() { diff --git a/engine/schema/src/com/cloud/vm/DomainRouterVO.java b/engine/schema/src/com/cloud/vm/DomainRouterVO.java index 2596d24a5fb..2a7aa49b6ed 100644 --- a/engine/schema/src/com/cloud/vm/DomainRouterVO.java +++ b/engine/schema/src/com/cloud/vm/DomainRouterVO.java @@ -69,6 +69,11 @@ public class DomainRouterVO extends VMInstanceVO implements VirtualRouter { @Column(name = "vpc_id") private Long vpcId; + + @Column(name= "update_state") + @Enumerated(EnumType.STRING) + private UpdateState updateState; + public DomainRouterVO(final long id, final long serviceOfferingId, final long elementId, final String name, final long templateId, final HypervisorType hypervisorType, final long guestOSId, final long domainId, final long accountId, final long userId, final boolean isRedundantRouter, final RedundantState redundantState, final boolean haEnabled, final boolean stopPending, final Long vpcId) { @@ -193,4 +198,13 @@ public class DomainRouterVO extends VMInstanceVO implements VirtualRouter { return vpcId; } + public UpdateState getUpdateState() { + return updateState; + } + + public void setUpdateState(UpdateState updateState) { + this.updateState = updateState; + } + + } diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index e65adb60f7d..8a2c3fd7b1f 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -562,7 +562,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage // the code would be triggered s_logger.debug("Cleaning up remote access vpns as a part of public IP id=" + ipId + " release..."); try { - _vpnMgr.destroyRemoteAccessVpnForIp(ipId, caller); + _vpnMgr.destroyRemoteAccessVpnForIp(ipId, caller,false); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to destroy remote access vpn for ip id=" + ipId + " as a part of ip release", e); success = false; diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index b6dac872f30..cade54f088c 100644 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -39,6 +39,7 @@ import java.util.UUID; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.router.VirtualRouter; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ApiConstants; @@ -108,6 +109,8 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDetailVO; +import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkDomainVO; import com.cloud.network.dao.NetworkServiceMapDao; @@ -178,6 +181,7 @@ import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionUtil; import com.cloud.utils.net.NetUtils; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.NicVO; @@ -187,6 +191,7 @@ import com.cloud.vm.SecondaryStorageVmVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.NicSecondaryIpVO; @@ -324,6 +329,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Inject MessageBus _messageBus; + @Inject + DomainRouterDao _routerDao; + + @Inject + NetworkDetailsDao _networkDetailsDao; + int _cidrLimit; boolean _allowSubdomainNetworkAccess; @@ -1992,8 +2003,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @DB @ActionEvent(eventType = EventTypes.EVENT_NETWORK_UPDATE, eventDescription = "updating network", async = true) public Network updateGuestNetwork(final long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, - final Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String customId) { - + final Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String customId, boolean updateInSequence, boolean forced) { boolean restartNetwork = false; // verify input parameters @@ -2239,130 +2249,178 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount); // 1) Shutdown all the elements and cleanup all the rules. Don't allow to shutdown network in intermediate // states - Shutdown and Implementing - boolean validStateToShutdown = (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated); - if (restartNetwork) { - if (validStateToShutdown) { - if (!changeCidr) { - s_logger.debug("Shutting down elements and resources for network id=" + networkId + " as a part of network update"); + int resourceCount=1; + if(updateInSequence && restartNetwork && _networkOfferingDao.findById(network.getNetworkOfferingId()).getRedundantRouter() + && (networkOfferingId==null || _networkOfferingDao.findById(networkOfferingId).getRedundantRouter()) && network.getVpcId()==null) { + _networkMgr.canUpdateInSequence(network); + NetworkDetailVO networkDetail =new NetworkDetailVO(network.getId(),Network.updatingInSequence,"true",true); + _networkDetailsDao.persist(networkDetail); + _networkMgr.configureUpdateInSequence(network); + resourceCount=_networkMgr.getResourceCount(network); + //check if routers are in correct state before proceeding with the update + List routers=_routerDao.listByNetworkAndRole(networkId, VirtualRouter.Role.VIRTUAL_ROUTER); + for(DomainRouterVO router :routers){ + if(router.getRedundantState()== VirtualRouter.RedundantState.UNKNOWN){ + if(!forced){ + throw new CloudRuntimeException("Domain router: "+router.getInstanceName()+" is in unknown state, Cannot update network. set parameter forced to true for forcing an update"); + } + } + } + } + List servicesNotInNewOffering = null; + if(networkOfferingId != null) + servicesNotInNewOffering = _networkMgr.getServicesNotSupportedInNewOffering(network,networkOfferingId); + if(!forced && servicesNotInNewOffering != null && !servicesNotInNewOffering.isEmpty()){ + NetworkOfferingVO newOffering = _networkOfferingDao.findById(networkOfferingId); + throw new CloudRuntimeException("The new offering:"+newOffering.getUniqueName() + +" will remove the following services "+servicesNotInNewOffering +"along with all the related configuration currently in use. will not proceed with the network update." + + "set forced parameter to true for forcing an update."); + } + try{ + if(servicesNotInNewOffering!=null && !servicesNotInNewOffering.isEmpty()){ + _networkMgr.cleanupConfigForServicesInNetwork(servicesNotInNewOffering,network); + } + }catch (Throwable e){ + s_logger.debug("failed to cleanup config related to unused services error:"+e.getMessage()); + } - if (!_networkMgr.shutdownNetworkElementsAndResources(context, true, network)) { - s_logger.warn("Failed to shutdown the network elements and resources as a part of network restart: " + network); - CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network of specified id"); + boolean validStateToShutdown = (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated); + try { + + do { + if (restartNetwork) { + if (validStateToShutdown) { + if (!changeCidr) { + s_logger.debug("Shutting down elements and resources for network id=" + networkId + " as a part of network update"); + + if (!_networkMgr.shutdownNetworkElementsAndResources(context, true, network)) { + s_logger.warn("Failed to shutdown the network elements and resources as a part of network restart: " + network); + CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network of specified id"); + ex.addProxyObject(network.getUuid(), "networkId"); + throw ex; + } + } else { + // We need to shutdown the network, since we want to re-implement the network. + s_logger.debug("Shutting down network id=" + networkId + " as a part of network update"); + + //check if network has reservation + if (NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr())) { + s_logger.warn("Existing IP reservation will become ineffective for the network with id = " + networkId + + " You need to reapply reservation after network reimplementation."); + //set cidr to the newtork cidr + network.setCidr(network.getNetworkCidr()); + //set networkCidr to null to bring network back to no IP reservation state + network.setNetworkCidr(null); + } + + if (!_networkMgr.shutdownNetwork(network.getId(), context, true)) { + s_logger.warn("Failed to shutdown the network as a part of update to network with specified id"); + CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network as a part of update of specified network id"); + ex.addProxyObject(network.getUuid(), "networkId"); + throw ex; + } + } + } else { + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to shutdown the network elements and resources as a part of update to network with specified id; network is in wrong state: " + network.getState()); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } + } + + // 2) Only after all the elements and rules are shutdown properly, update the network VO + // get updated network + Network.State networkState = _networksDao.findById(networkId).getState(); + boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated); + if (restartNetwork && !validStateToImplement) { + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to implement the network elements and resources as a part of update to network with specified id; network is in wrong state: " + networkState); + ex.addProxyObject(network.getUuid(), "networkId"); + throw ex; + } + + if (networkOfferingId != null) { + if (networkOfferingChanged) { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + network.setNetworkOfferingId(networkOfferingId); + _networksDao.update(networkId, network, newSvcProviders); + // get all nics using this network + // log remove usage events for old offering + // log assign usage events for new offering + List nics = _nicDao.listByNetworkId(networkId); + for (NicVO nic : nics) { + long vmId = nic.getInstanceId(); + VMInstanceVO vm = _vmDao.findById(vmId); + if (vm == null) { + s_logger.error("Vm for nic " + nic.getId() + " not found with Vm Id:" + vmId); + continue; + } + long isDefault = (nic.isDefaultNic()) ? 1 : 0; + String nicIdString = Long.toString(nic.getId()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, + oldNetworkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, + networkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); + } + } + }); + } else { + network.setNetworkOfferingId(networkOfferingId); + _networksDao.update(networkId, network, + _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())); + } } else { - // We need to shutdown the network, since we want to re-implement the network. - s_logger.debug("Shutting down network id=" + networkId + " as a part of network update"); + _networksDao.update(networkId, network); + } - //check if network has reservation - if (NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr())) { - s_logger.warn("Existing IP reservation will become ineffective for the network with id = " + networkId - + " You need to reapply reservation after network reimplementation."); - //set cidr to the newtork cidr - network.setCidr(network.getNetworkCidr()); - //set networkCidr to null to bring network back to no IP reservation state - network.setNetworkCidr(null); - } - - if (!_networkMgr.shutdownNetwork(network.getId(), context, true)) { - s_logger.warn("Failed to shutdown the network as a part of update to network with specified id"); - CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network as a part of update of specified network id"); - ex.addProxyObject(network.getUuid(), "networkId"); - throw ex; + // 3) Implement the elements and rules again + if (restartNetwork) { + if (network.getState() != Network.State.Allocated) { + DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null); + s_logger.debug("Implementing the network " + network + " elements and resources as a part of network update"); + try { + if (!changeCidr) { + _networkMgr.implementNetworkElementsAndResources(dest, context, network, _networkOfferingDao.findById(network.getNetworkOfferingId())); + } else { + _networkMgr.implementNetwork(network.getId(), dest, context); + } + } catch (Exception ex) { + s_logger.warn("Failed to implement network " + network + " elements and resources as a part of network update due to ", ex); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of network update"); + e.addProxyObject(network.getUuid(), "networkId"); + throw e; + } } } - } else { - CloudRuntimeException ex = new CloudRuntimeException( - "Failed to shutdown the network elements and resources as a part of update to network with specified id; network is in wrong state: " + network.getState()); - ex.addProxyObject(network.getUuid(), "networkId"); - throw ex; - } - } - // 2) Only after all the elements and rules are shutdown properly, update the network VO - // get updated network - Network.State networkState = _networksDao.findById(networkId).getState(); - boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated); - if (restartNetwork && !validStateToImplement) { - CloudRuntimeException ex = new CloudRuntimeException( - "Failed to implement the network elements and resources as a part of update to network with specified id; network is in wrong state: " + networkState); - ex.addProxyObject(network.getUuid(), "networkId"); - throw ex; - } - - if (networkOfferingId != null) { - if (networkOfferingChanged) { - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - network.setNetworkOfferingId(networkOfferingId); - _networksDao.update(networkId, network, newSvcProviders); - // get all nics using this network - // log remove usage events for old offering - // log assign usage events for new offering - List nics = _nicDao.listByNetworkId(networkId); - for (NicVO nic : nics) { - long vmId = nic.getInstanceId(); - VMInstanceVO vm = _vmDao.findById(vmId); - if (vm == null) { - s_logger.error("Vm for nic " + nic.getId() + " not found with Vm Id:" + vmId); - continue; + // 4) if network has been upgraded from a non persistent ntwk offering to a persistent ntwk offering, + // implement the network if its not already + if (networkOfferingChanged && !oldNtwkOff.getIsPersistent() && networkOffering.getIsPersistent()) { + if (network.getState() == Network.State.Allocated) { + try { + DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null); + _networkMgr.implementNetwork(network.getId(), dest, context); + } catch (Exception ex) { + s_logger.warn("Failed to implement network " + network + " elements and resources as a part o" + "f network update due to ", ex); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified" + " id) elements and resources as a part of network update"); + e.addProxyObject(network.getUuid(), "networkId"); + throw e; + } } - long isDefault = (nic.isDefaultNic()) ? 1 : 0; - String nicIdString = Long.toString(nic.getId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, - oldNetworkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, - networkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplay()); } - } - }); - } else { - network.setNetworkOfferingId(networkOfferingId); - _networksDao.update(networkId, network, - _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())); - } - } else { - _networksDao.update(networkId, network); - } - - // 3) Implement the elements and rules again - if (restartNetwork) { - if (network.getState() != Network.State.Allocated) { - DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null); - s_logger.debug("Implementing the network " + network + " elements and resources as a part of network update"); - try { - if (!changeCidr) { - _networkMgr.implementNetworkElementsAndResources(dest, context, network, _networkOfferingDao.findById(network.getNetworkOfferingId())); - } else { - _networkMgr.implementNetwork(network.getId(), dest, context); - } - } catch (Exception ex) { - s_logger.warn("Failed to implement network " + network + " elements and resources as a part of network update due to ", ex); - CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of network update"); - e.addProxyObject(network.getUuid(), "networkId"); - throw e; + resourceCount--; + } while(updateInSequence && resourceCount>0); + }catch (Exception exception){ + throw new CloudRuntimeException("failed to update network "+network.getUuid()+"due to "+exception.getMessage()); + }finally { + if(updateInSequence){ + if( _networkDetailsDao.findDetail(networkId,Network.updatingInSequence)!=null){ + _networkDetailsDao.removeDetail(networkId,Network.updatingInSequence); } } } - - // 4) if network has been upgraded from a non persistent ntwk offering to a persistent ntwk offering, - // implement the network if its not already - if (networkOfferingChanged && !oldNtwkOff.getIsPersistent() && networkOffering.getIsPersistent()) { - if (network.getState() == Network.State.Allocated) { - try { - DeployDestination dest = new DeployDestination(_dcDao.findById(network.getDataCenterId()), null, null, null); - _networkMgr.implementNetwork(network.getId(), dest, context); - } catch (Exception ex) { - s_logger.warn("Failed to implement network " + network + " elements and resources as a part o" + "f network update due to ", ex); - CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified" + " id) elements and resources as a part of network update"); - e.addProxyObject(network.getUuid(), "networkId"); - throw e; - } - } - } - return getNetwork(network.getId()); } diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index d802188e4c4..03d19588af3 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -24,6 +24,9 @@ import java.util.Set; import javax.inject.Inject; +import com.cloud.network.dao.NetworkDetailVO; +import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; @@ -109,7 +112,7 @@ import org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder; public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, -NetworkMigrationResponder, AggregatedCommandExecutor { +NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource { private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class); public static final AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounterType("cpu"); public static final AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounterType("memory"); @@ -159,6 +162,9 @@ NetworkMigrationResponder, AggregatedCommandExecutor { @Inject NetworkTopologyContext networkTopologyContext; + @Inject + NetworkDetailsDao _networkDetailsDao; + @Inject protected RouterDeploymentDefinitionBuilder routerDeploymentDefinitionBuilder; @@ -262,7 +268,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor { public boolean applyFWRules(final Network network, final List rules) throws ResourceUnavailableException { boolean result = true; if (canHandle(network, Service.Firewall)) { - final List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + final List routers = getRouters(network); if (routers == null || routers.isEmpty()) { s_logger.debug("Virtual router elemnt doesn't need to apply firewall rules on the backend; virtual " + "router doesn't exist in the network " + network.getId()); return true; @@ -407,7 +413,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor { return false; } - final List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + final List routers = getRouters(network); if (routers == null || routers.isEmpty()) { s_logger.debug("Virtual router elemnt doesn't need to apply lb rules on the backend; virtual " + "router doesn't exist in the network " + network.getId()); return true; @@ -498,7 +504,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor { } boolean result = true; if (canHandle) { - final List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + final List routers = getRouters(network); if (routers == null || routers.isEmpty()) { s_logger.debug("Virtual router elemnt doesn't need to associate ip addresses on the backend; virtual " + "router doesn't exist in the network " + network.getId()); return true; @@ -657,7 +663,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor { public boolean applyStaticNats(final Network network, final List rules) throws ResourceUnavailableException { boolean result = true; if (canHandle(network, Service.StaticNat)) { - final List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + final List routers = getRouters(network); if (routers == null || routers.isEmpty()) { s_logger.debug("Virtual router elemnt doesn't need to apply static nat on the backend; virtual " + "router doesn't exist in the network " + network.getId()); return true; @@ -673,6 +679,46 @@ NetworkMigrationResponder, AggregatedCommandExecutor { return result; } + public List getRouters(Network network){ + List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + if (routers !=null && routers.isEmpty()) { + return null; + } + NetworkDetailVO updateInSequence=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence); + if(network.isRedundant() && updateInSequence!=null && "true".equalsIgnoreCase(updateInSequence.getValue())){ + List masterRouters=new ArrayList(); + int noOfrouters=routers.size(); + while (noOfrouters>0){ + DomainRouterVO router = routers.get(0); + if(router.getUpdateState()== VirtualRouter.UpdateState.UPDATE_IN_PROGRESS){ + ArrayList routerList = new ArrayList(); + routerList.add(router); + return routerList; + } + if(router.getUpdateState()== VirtualRouter.UpdateState.UPDATE_COMPLETE) { + routers.remove(router); + noOfrouters--; + continue; + } + if(router.getRedundantState()!=VirtualRouter.RedundantState.BACKUP) { + masterRouters.add(router); + routers.remove(router); + } + noOfrouters--; + } + if(routers.size()==0 && masterRouters.size()==0){ + return null; + } + if(routers.size()==0 && masterRouters.size()!=0){ + routers=masterRouters; + } + routers=routers.subList(0,1); + routers.get(0).setUpdateState(VirtualRouter.UpdateState.UPDATE_IN_PROGRESS); + _routerDao.persist(routers.get(0)); + } + return routers; + } + @Override public boolean shutdown(final Network network, final ReservationContext context, final boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { final List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); @@ -1031,7 +1077,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor { List routers; if (publicNetwork) { - routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + routers = getRouters(network); } else { if (isPodBased && dest.getPod() != null) { final Long podId = dest.getPod().getId(); @@ -1228,7 +1274,27 @@ NetworkMigrationResponder, AggregatedCommandExecutor { throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId()); } - return _routerMgr.completeAggregatedExecution(network, routers); + NetworkDetailVO networkDetail=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence); + boolean updateInSequence= "true".equalsIgnoreCase((networkDetail!=null ? networkDetail.getValue() : null)); + if(updateInSequence){ + DomainRouterVO router=routers.get(0); + router.setUpdateState(VirtualRouter.UpdateState.UPDATE_COMPLETE); + _routerDao.persist(router); + } + boolean result=false; + try{ + result=_routerMgr.completeAggregatedExecution(network, routers); + } finally { + if(!result && updateInSequence) { + //fail the network update. even if one router fails we fail the network update. + List routerList = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER); + for (DomainRouterVO router : routerList) { + router.setUpdateState(VirtualRouter.UpdateState.UPDATE_FAILED); + _routerDao.persist(router); + } + } + } + return result; } @Override @@ -1237,4 +1303,22 @@ NetworkMigrationResponder, AggregatedCommandExecutor { // lets not waste another command return true; } + + @Override + public void configureResource(Network network) { + NetworkDetailVO networkDetail=_networkDetailsDao.findDetail(network.getId(), Network.updatingInSequence); + if(networkDetail==null || !"true".equalsIgnoreCase(networkDetail.getValue())) + throw new CloudRuntimeException("failed to configure the resource, network update is not in progress."); + Listrouters = _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER); + for(DomainRouterVO router : routers){ + router.setUpdateState(VirtualRouter.UpdateState.UPDATE_NEEDED); + _routerDao.persist(router); + } + } + + @Override + public int getResourceCount(Network network) { + return _routerDao.listByNetworkAndRole(network.getId(), VirtualRouter.Role.VIRTUAL_ROUTER).size(); + } + } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 3332393f652..b6c8cf091ae 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2215,6 +2215,11 @@ Configurable, StateListener params) { @@ -410,7 +414,13 @@ public class RouterDeploymentDefinition { // Don't start the router as we are holding the network lock that // needs to be released at the end of router allocation final DomainRouterVO router = nwHelper.deployRouter(this, false); - + //check if the network update is in progress. + //if update is in progress add the update_pending flag to DomainRouterVO. + NetworkDetailVO detail =networkDetailsDao.findDetail(guestNetwork.getId(),Network.updatingInSequence); + if("true".equalsIgnoreCase(detail!=null ? detail.getValue() : null)) { + router.setUpdateState(VirtualRouter.UpdateState.UPDATE_IN_PROGRESS); + routerDao.persist(router); + } if (router != null) { routerDao.addRouterToGuestNetwork(router, guestNetwork); //Fix according to changes by Sheng Yang in commit ID cb4513379996b262ae378daf00c6388c6b7313cf diff --git a/server/src/org/cloud/network/router/deployment/RouterDeploymentDefinitionBuilder.java b/server/src/org/cloud/network/router/deployment/RouterDeploymentDefinitionBuilder.java index 3ba4fad77de..3765537a148 100644 --- a/server/src/org/cloud/network/router/deployment/RouterDeploymentDefinitionBuilder.java +++ b/server/src/org/cloud/network/router/deployment/RouterDeploymentDefinitionBuilder.java @@ -22,6 +22,7 @@ import java.util.Map; import javax.inject.Inject; +import com.cloud.network.dao.NetworkDetailsDao; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -96,6 +97,8 @@ public class RouterDeploymentDefinitionBuilder { private VpcManager vpcMgr; @Inject private VlanDao vlanDao; + @Inject + private NetworkDetailsDao networkDetailsDao; @Autowired @Qualifier("networkHelper") @@ -133,6 +136,7 @@ public class RouterDeploymentDefinitionBuilder { routerDeploymentDefinition.ipv6Dao = ipv6Dao; routerDeploymentDefinition.ipAddressDao = ipAddressDao; routerDeploymentDefinition.serviceOfferingId = offeringId; + routerDeploymentDefinition.networkDetailsDao = networkDetailsDao; routerDeploymentDefinition.nwHelper = nwHelper; diff --git a/server/test/com/cloud/network/element/VirtualRouterElementTest.java b/server/test/com/cloud/network/element/VirtualRouterElementTest.java index 659277824ea..4fbc28ee4cb 100644 --- a/server/test/com/cloud/network/element/VirtualRouterElementTest.java +++ b/server/test/com/cloud/network/element/VirtualRouterElementTest.java @@ -19,6 +19,7 @@ package com.cloud.network.element; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @@ -26,6 +27,11 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.network.dao.NetworkDetailVO; +import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.router.VirtualRouter; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.cloud.network.router.deployment.RouterDeploymentDefinitionBuilder; @@ -35,6 +41,8 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import com.cloud.cluster.dao.ManagementServerHostDao; @@ -111,6 +119,7 @@ import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class VirtualRouterElementTest { @@ -127,6 +136,7 @@ public class VirtualRouterElementTest { @Mock private ManagementServerHostDao _msHostDao; @Mock private NetworkDao _networkDao; @Mock private NetworkOfferingDao _networkOfferingDao; + @Mock private NetworkDetailsDao _networkDetailsDao; @Mock private NicDao _nicDao; @Mock private NicIpAliasDao _nicIpAliasDao; @Mock private OpRouterMonitorServiceDao _opRouterMonitorServiceDao; @@ -225,6 +235,62 @@ public class VirtualRouterElementTest { } + @Test + public void testGetRouters1(){ + Network networkUpdateInprogress=new NetworkVO(1l,null,null,null,1l,1l,1l,1l,"d","d","d",null,1l,1l,null,true,null,true); + mockDAOs((NetworkVO)networkUpdateInprogress,testOffering); + //getRoutes should always return the router that is updating. + List routers=virtualRouterElement.getRouters(networkUpdateInprogress); + assertTrue(routers.size()==1); + assertTrue(routers.get(0).getUpdateState()== VirtualRouter.UpdateState.UPDATE_IN_PROGRESS); + } + + @Test + public void testGetRouters2(){ + Network networkUpdateInprogress=new NetworkVO(2l,null,null,null,1l,1l,1l,1l,"d","d","d",null,1l,1l,null,true,null,true); + mockDAOs((NetworkVO)networkUpdateInprogress,testOffering); + //alwyas return backup routers first when both master and backup need update. + List routers=virtualRouterElement.getRouters(networkUpdateInprogress); + assertTrue(routers.size()==1); + assertTrue(routers.get(0).getRedundantState()==RedundantState.BACKUP && routers.get(0).getUpdateState()==VirtualRouter.UpdateState.UPDATE_IN_PROGRESS); + } + + @Test + public void testGetRouters3(){ + Network network=new NetworkVO(3l,null,null,null,1l,1l,1l,1l,"d","d","d",null,1l,1l,null,true,null,true); + mockDAOs((NetworkVO)network,testOffering); + //alwyas return backup routers first when both master and backup need update. + List routers=virtualRouterElement.getRouters(network); + assertTrue(routers.size()==4); + } + + @Test + public void getResourceCountTest(){ + Network network=new NetworkVO(3l,null,null,null,1l,1l,1l,1l,"d","d","d",null,1l,1l,null,true,null,true); + mockDAOs((NetworkVO)network,testOffering); + int routers=virtualRouterElement.getResourceCount(network); + assertTrue(routers==4); + } + + @Test + public void completeAggregationCommandTest1() throws AgentUnavailableException,ResourceUnavailableException { + virtualRouterElement._routerMgr = Mockito.mock(VpcVirtualNetworkApplianceManagerImpl.class); + virtualRouterElement.routerDeploymentDefinitionBuilder = routerDeploymentDefinitionBuilder; + Network network = new NetworkVO(6l, null, null, null, 1l, 1l, 1l, 1l, "d", "d", "d", null, 1l, 1l, null, true, null, true); + when(virtualRouterElement._routerMgr.completeAggregatedExecution(any(Network.class), anyList())).thenReturn(true); + mockDAOs((NetworkVO) network, testOffering); + when(virtualRouterElement._routerDao.persist(any(DomainRouterVO.class))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + Object args[] = invocationOnMock.getArguments(); + DomainRouterVO router = (DomainRouterVO) args[0]; + if (router.getUpdateState() != VirtualRouter.UpdateState.UPDATE_COMPLETE) { + throw new CloudRuntimeException("TestFailed: completeAggregationCommandTest1 failed"); + } else return null; + } + }); + virtualRouterElement.completeAggregatedExecution(network, testDestination); + } /** * @param networks * @param offerings @@ -293,11 +359,94 @@ public class VirtualRouterElementTest { /* haEnabled */ false, /* stopPending */ false, /* vpcId */ null); - + final DomainRouterVO routerNeedUpdateBackup = new DomainRouterVO(/* id */ 2L, + /* serviceOfferingId */ 1L, + /* elementId */ 0L, + "name", + /* templateId */0L, + HypervisorType.XenServer, + /* guestOSId */ 0L, + /* domainId */ 0L, + /* accountId */ 1L, + /* userId */ 1L, + /* isRedundantRouter */ false, + RedundantState.BACKUP, + /* haEnabled */ false, + /* stopPending */ false, + /* vpcId */ null); + routerNeedUpdateBackup.setUpdateState(VirtualRouter.UpdateState.UPDATE_NEEDED); + final DomainRouterVO routerNeedUpdateMaster = new DomainRouterVO(/* id */ 3L, + /* serviceOfferingId */ 1L, + /* elementId */ 0L, + "name", + /* templateId */0L, + HypervisorType.XenServer, + /* guestOSId */ 0L, + /* domainId */ 0L, + /* accountId */ 1L, + /* userId */ 1L, + /* isRedundantRouter */ false, + RedundantState.MASTER, + /* haEnabled */ false, + /* stopPending */ false, + /* vpcId */ null); + routerNeedUpdateMaster.setUpdateState(VirtualRouter.UpdateState.UPDATE_NEEDED); + final DomainRouterVO routerUpdateComplete = new DomainRouterVO(/* id */ 4L, + /* serviceOfferingId */ 1L, + /* elementId */ 0L, + "name", + /* templateId */0L, + HypervisorType.XenServer, + /* guestOSId */ 0L, + /* domainId */ 0L, + /* accountId */ 1L, + /* userId */ 1L, + /* isRedundantRouter */ false, + RedundantState.UNKNOWN, + /* haEnabled */ false, + /* stopPending */ false, + /* vpcId */ null); + routerUpdateComplete.setUpdateState(VirtualRouter.UpdateState.UPDATE_COMPLETE); + final DomainRouterVO routerUpdateInProgress = new DomainRouterVO(/* id */ 5L, + /* serviceOfferingId */ 1L, + /* elementId */ 0L, + "name", + /* templateId */0L, + HypervisorType.XenServer, + /* guestOSId */ 0L, + /* domainId */ 0L, + /* accountId */ 1L, + /* userId */ 1L, + /* isRedundantRouter */ false, + RedundantState.UNKNOWN, + /* haEnabled */ false, + /* stopPending */ false, + /* vpcId */ null); + routerUpdateInProgress.setUpdateState(VirtualRouter.UpdateState.UPDATE_IN_PROGRESS); + List routerList1=new ArrayList<>(); + routerList1.add(routerUpdateComplete); + routerList1.add(routerNeedUpdateBackup); + routerList1.add(routerNeedUpdateMaster); + routerList1.add(routerUpdateInProgress); + List routerList2=new ArrayList<>(); + routerList2.add(routerUpdateComplete); + routerList2.add(routerNeedUpdateBackup); + routerList2.add(routerNeedUpdateMaster); + List routerList3=new ArrayList<>(); + routerList3.add(routerUpdateComplete); + routerList3.add(routerUpdateInProgress); when(_routerDao.getNextInSequence(Long.class, "id")).thenReturn(1L); when(_templateDao.findRoutingTemplate(HypervisorType.XenServer, "SystemVM Template (XenServer)")).thenReturn(new VMTemplateVO()); when(_routerDao.persist(any(DomainRouterVO.class))).thenReturn(router); when(_routerDao.findById(router.getId())).thenReturn(router); + when(_routerDao.listByNetworkAndRole(1l, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(routerList1); + when(_routerDao.listByNetworkAndRole(2l, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(routerList2); + when(_routerDao.listByNetworkAndRole(3l, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(routerList1); + when(_routerDao.listByNetworkAndRole(6l, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(routerList3); + when(_networkDetailsDao.findDetail(1l, Network.updatingInSequence)).thenReturn(new NetworkDetailVO(1l,Network.updatingInSequence,"true",true)); + when(_networkDetailsDao.findDetail(2l, Network.updatingInSequence)).thenReturn(new NetworkDetailVO(2l,Network.updatingInSequence,"true",true)); + when(_networkDetailsDao.findDetail(6l, Network.updatingInSequence)).thenReturn(new NetworkDetailVO(2l,Network.updatingInSequence,"true",true)); + when(_routerDao.persist(any(DomainRouterVO.class))).thenReturn(router); } } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index a5d8a1ab584..6d2348f97d8 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -247,7 +247,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches */ @Override public Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, - Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID) { + Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID,boolean updateInSequence, boolean forced) { // TODO Auto-generated method stub return null; } @@ -841,6 +841,31 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches return; } + @Override + public boolean canUpdateInSequence(Network network) { + return false; + } + + @Override + public List getServicesNotSupportedInNewOffering(Network network, long newNetworkOfferingId) { + return null; + } + + @Override + public void cleanupConfigForServicesInNetwork(List services, Network network) { + return; + } + + @Override + public void configureUpdateInSequence(Network network) { + return; + } + + @Override + public int getResourceCount(Network network) { + return 0; + } + @Override public void prepareNicForMigration(VirtualMachineProfile vm, DeployDestination dest) { // TODO Auto-generated method stub diff --git a/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTest.java b/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTest.java index eff16c16b8d..6195387dbe2 100644 --- a/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTest.java +++ b/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTest.java @@ -714,7 +714,7 @@ public class RouterDeploymentDefinitionTest extends RouterDeploymentDefinitionTe final DomainRouterVO routerVO2 = mock(DomainRouterVO.class); when(mockNetworkHelper.deployRouter(deploymentUT, false)) .thenReturn(routerVO1).thenReturn(routerVO2); - + when(networkDetailsDao.findById(anyLong())).thenReturn(null); // Execute deploymentUT.deployAllVirtualRouters(); diff --git a/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTestBase.java b/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTestBase.java index 4225083ca2b..626c2d7acc6 100644 --- a/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTestBase.java +++ b/server/test/org/cloud/network/router/deployment/RouterDeploymentDefinitionTestBase.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.cloud.network.dao.NetworkDetailsDao; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -79,6 +80,8 @@ public class RouterDeploymentDefinitionTestBase { @Mock protected NetworkHelper mockNetworkHelper; @Mock + protected NetworkDetailsDao networkDetailsDao; + @Mock protected VpcNetworkHelperImpl vpcNwHelper; @Mock protected VMInstanceDao mockVmDao; diff --git a/setup/db/db/schema-452to460.sql b/setup/db/db/schema-452to460.sql index dfb629f95a0..3b380f397e6 100644 --- a/setup/db/db/schema-452to460.sql +++ b/setup/db/db/schema-452to460.sql @@ -420,3 +420,5 @@ INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervis INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'KVM', 'default', 'CentOS 7', 246, utc_timestamp(), 0); UPDATE `cloud`.`hypervisor_capabilities` SET `max_data_volumes_limit` = '32' WHERE `hypervisor_capabilities`.`hypervisor_type` = 'KVM'; + + diff --git a/setup/db/db/schema-4910to41000.sql b/setup/db/db/schema-4910to41000.sql index f18dbf3f49a..8ea67e196bd 100644 --- a/setup/db/db/schema-4910to41000.sql +++ b/setup/db/db/schema-4910to41000.sql @@ -18,3 +18,5 @@ --; -- Schema upgrade from 4.9.1.0 to 4.10.0.0; --; + +ALTER TABLE `cloud`.`domain_router` ADD COLUMN update_state varchar(64) DEFAULT NULL; \ No newline at end of file diff --git a/test/integration/component/maint/test_redundant_router.py b/test/integration/component/maint/test_redundant_router.py index 98b0bae1ba0..cad1d341d55 100644 --- a/test/integration/component/maint/test_redundant_router.py +++ b/test/integration/component/maint/test_redundant_router.py @@ -29,6 +29,7 @@ from marvin.lib.common import (get_domain, get_zone, get_process_status) import time +import multiprocessing # Import Local Modules from marvin.cloudstackTestCase import cloudstackTestCase @@ -872,9 +873,16 @@ class TestRvRRedundancy(cloudstackTestCase): cls.testdata["nw_off_isolated_RVR"], conservemode=True ) + cls.network_offering_for_update=NetworkOffering.create( + cls.api_client, + cls.testdata["nw_off_isolated_RVR"], + conservemode=True + ) + cls._cleanup.append(cls.network_offering_for_update) cls._cleanup.append(cls.network_offering) # Enable Network offering cls.network_offering.update(cls.api_client, state='Enabled') + cls.network_offering_for_update.update(cls.api_client, state='Enabled') return @classmethod @@ -1511,3 +1519,149 @@ class TestRvRRedundancy(cloudstackTestCase): "Redundant state of the router should be BACKUP but is %s" % routers[0].redundantstate) return + + def updateNetwork(self, conn): + try: + self.network.update( + self.api_client, + networkofferingid=self.network_offering_for_update.id, + updateinsequence=True, + forced=True, + changecidr=False + ) + except Exception as e: + conn.send("Failed to update network: %s due to %s"%(self.network.name, e)) + conn.send("update Network Complete") + return + + + + def get_master_and_backupRouter(self): + retry = 4 + master_router = backup_router=None + while retry > 0: + routers = Router.list( + self.apiclient, + networkid=self.network.id, + listall=True + ) + retry = retry-1 + if not (routers[0].redundantstate == 'MASTER' or routers[1].redundantstate == 'MASTER'): + continue; + if routers[0].redundantstate == 'MASTER': + master_router = routers[0] + backup_router = routers[1] + break + else: + master_router = routers[1] + backup_router = routers[0] + break + return master_router, backup_router + + + def chek_for_new_backupRouter(self,old_backup_router): + master_router, backup_router = self.get_master_and_backupRouter() + retry = 4 + self.info("Checking if new router is getting created.") + self.info("old_backup_router:"+old_backup_router.name+" new_backup_router:"+backup_router.name) + while old_backup_router.name == backup_router.name: + self.debug("waiting for new router old router:"+backup_router.name) + retry = retry-1 + if retry == 0: + break; + time.sleep(self.testdata["sleep"]) + master_router, backup_router = self.get_master_and_backupRouter() + if retry == 0: + self.fail("New router creation taking too long, timed out") + + def wait_untill_router_stabilises(self): + retry=4 + while retry > 0: + routers = Router.list( + self.apiclient, + networkid=self.network.id, + listall=True + ) + retry = retry-1 + self.info("waiting untill state of the routers is stable") + if routers[0].redundantstate != 'UNKNOWN' and routers[1].redundantstate != 'UNKNOWN': + return + elif retry==0: + self.fail("timedout while waiting for routers to stabilise") + return + time.sleep(self.testdata["sleep"]) + + @attr(tags=["bharat"]) + def test_06_updateVRs_in_sequence(self): + """Test update network and check if VRs are updated in sequence + """ + + # Steps to validate + # update network to a new offering + # check if the master router is running while backup is starting. + # check if the backup is running while master is starting. + # check if both the routers are running after the update is complete. + + #clean up the network to make sure it is in proper state. + self.network.restart(self.apiclient,cleanup=True) + time.sleep(self.testdata["sleep"]) + self.wait_untill_router_stabilises() + old_master_router, old_backup_router = self.get_master_and_backupRouter() + self.info("old_master_router:"+old_master_router.name+" old_backup_router"+old_backup_router.name) + #chek if the network is in correct state + self.assertEqual(old_master_router.state, "Running", "The master router is not running, network is not in a correct state to start the test") + self.assertEqual(old_backup_router.state, "Running", "The backup router is not running, network is not in a correct state to start the test") + + worker, monitor = multiprocessing.Pipe() + worker_process = multiprocessing.Process(target=self.updateNetwork, args=(worker,)) + worker_process.start() + if not worker_process.is_alive(): + message = monitor.recv() + if "Complete" not in message: + self.fail(message) + + self.info("Network update Started, the old backup router will get destroyed and a new router will be created") + + self.chek_for_new_backupRouter(old_backup_router) + master_router, new_backup_router=self.get_master_and_backupRouter() + #the state of the master router should be running. while backup is being updated + self.assertEqual(master_router.state, "Running", "State of the master router is not running") + self.assertEqual(master_router.redundantstate, 'MASTER', "Redundant state of the master router should be MASTER, but it is %s"%master_router.redundantstate) + self.info("Old backup router:"+old_backup_router.name+" is destroyed and new router:"+new_backup_router.name+" got created") + + #wait for the new backup to become master. + retry = 4 + while new_backup_router.name != master_router.name: + retry = retry-1 + if retry == 0: + break + time.sleep(self.testdata["sleep"]) + self.info("wating for backup router to become master router name:"+new_backup_router.name) + master_router, backup_router = self.get_master_and_backupRouter() + if retry == 0: + self.fail("timed out while waiting for new backup router to change state to MASTER.") + + #new backup router has become master. + self.info("newly created router:"+new_backup_router.name+" has changed state to Master") + self.info("old master router:"+old_master_router.name+"is destroyed") + #old master will get destroyed and a new backup will be created. + #wait until new backup changes state from unknown to backup + master_router, backup_router = self.get_master_and_backupRouter() + retry = 4 + while backup_router.redundantstate != 'BACKUP': + retry = retry-1 + self.info("waiting for router:"+backup_router.name+" to change state to Backup") + if retry == 0: + break + time.sleep(self.testdata["sleep"]) + master_router, backup_router = self.get_master_and_backupRouter() + self.assertEqual(master_router.state, "Running", "State of the master router is not running") + self.assertEqual(master_router.redundantstate, 'MASTER', "Redundant state of the master router should be MASTER, but it is %s"%master_router.redundantstate) + if retry == 0: + self.fail("timed out while waiting for new backup rotuer to change state to MASTER.") + + #the network update is complete.finally both the router should be running. + new_master_router, new_backup_router=self.get_master_and_backupRouter() + self.assertEqual(new_master_router.state, "Running", "State of the master router:"+new_master_router.name+" is not running") + self.assertEqual(new_backup_router.state, "Running", "State of the backup router:"+new_backup_router.name+" is not running") + worker_process.join()