Do not update network if one of the router's state is unknown

Added checks to prevent netwrok update when router state is unknown or when
the new offering removes a service that is in use.

Added a new param forced to the updateNetwork API. The network will
undergo a forced update when this param is set to true.

CLOUDSTACK-8751 Clean network config like firewall rules etc, when network services are removed during network update.
This commit is contained in:
Bharat Kumar 2015-11-26 15:30:06 +05:30 committed by Bharat Kumar
parent f416332994
commit ba9dcba16d
13 changed files with 186 additions and 14 deletions

View File

@ -77,7 +77,7 @@ public interface NetworkService {
IpAddress getIp(long id); IpAddress getIp(long id);
Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, Long networkOfferingId, 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 updateInSequence); Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID, boolean updateInSequence, boolean forced);
PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed, List<String> isolationMethods, String broadcastDomainRange, Long domainId, PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed, List<String> isolationMethods, String broadcastDomainRange, Long domainId,
List<String> tags, String name); List<String> tags, String name);

View File

@ -33,7 +33,7 @@ public interface RemoteAccessVpnService {
RemoteAccessVpn createRemoteAccessVpn(long vpnServerAddressId, String ipRange, boolean openFirewall, Boolean forDisplay) throws NetworkRuleConflictException; 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; RemoteAccessVpn startRemoteAccessVpn(long vpnServerAddressId, boolean openFirewall) throws ResourceUnavailableException;

View File

@ -49,7 +49,7 @@ public class UpdateNetworkCmdByAdmin extends UpdateNetworkCmd {
} }
Network result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount, Network result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount,
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence()); callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence(),getForced());
if (result != null) { if (result != null) {

View File

@ -83,6 +83,9 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd {
description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin})
private Boolean displayNetwork; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -129,6 +132,12 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd {
return updateInSequence; return updateInSequence;
} }
public boolean getForced(){
if(forced==null){
return false;
}
return forced;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -159,7 +168,7 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd {
Network result = Network result =
_networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount, callerUser, getNetworkDomain(), getNetworkOfferingId(), _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount, callerUser, getNetworkDomain(), getNetworkOfferingId(),
getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence()); getChangeCidr(), getGuestVmCidr(), getDisplayNetwork(), getCustomId(), getUpdateInSequence(), getForced());
if (result != null) { if (result != null) {
NetworkResponse response = _responseGenerator.createNetworkResponse(ResponseView.Restricted, result); NetworkResponse response = _responseGenerator.createNetworkResponse(ResponseView.Restricted, result);

View File

@ -93,7 +93,7 @@ public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd {
@Override @Override
public void execute() throws ResourceUnavailableException { 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"); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete remote access vpn");
} }
} }

View File

@ -227,6 +227,10 @@ public interface NetworkOrchestrationService {
boolean canUpdateInSequence(Network network); boolean canUpdateInSequence(Network network);
List<String> getServicesNotSupportedInNewOffering(Network network, long newNetworkOfferingId);
void cleanupConfigForServicesInNetwork(List<String> services, Network network);
void configureUpdateInSequence(Network network); void configureUpdateInSequence(Network network);
int getResourceCount(Network network); int getResourceCount(Network network);

View File

@ -39,6 +39,9 @@ import javax.naming.ConfigurationException;
import com.cloud.network.Networks; import com.cloud.network.Networks;
import com.cloud.network.dao.NetworkDetailsDao; 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.network.element.RedundantResource;
import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.DomainRouterDao;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -271,6 +274,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
VMNetworkMapDao _vmNetworkMapDao; VMNetworkMapDao _vmNetworkMapDao;
@Inject @Inject
DomainRouterDao _rotuerDao; DomainRouterDao _rotuerDao;
@Inject
RemoteAccessVpnDao _remoteAccessVpnDao;
@Inject
VpnUserDao _vpnUserDao;
List<NetworkGuru> networkGurus; List<NetworkGuru> networkGurus;
@ -1283,6 +1290,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
@Override @Override
public boolean canUpdateInSequence(Network network){ public boolean canUpdateInSequence(Network network){
List<Provider> providers = getNetworkProviders(network.getId()); List<Provider> providers = getNetworkProviders(network.getId());
//check if the there are no service provider other than virtualrouter. //check if the there are no service provider other than virtualrouter.
for(Provider provider :providers){ for(Provider provider :providers){
if(provider!=Provider.VirtualRouter) if(provider!=Provider.VirtualRouter)
@ -1291,6 +1299,111 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return true; return true;
} }
@Override
public List<String> getServicesNotSupportedInNewOffering(Network network,long newNetworkOfferingId){
NetworkOffering offering =_networkOfferingDao.findById(newNetworkOfferingId);
List<String> services=_ntwkOfferingSrvcDao.listServicesForNetworkOffering(offering.getId());
List<NetworkServiceMapVO> serviceMap= _ntwkSrvcDao.getServicesInNetwork(network.getId());
List<String> 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<String> 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<IPAddressVO> 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<IPAddressVO> 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 @Override
public void configureUpdateInSequence(Network network) { public void configureUpdateInSequence(Network network) {
List<Provider> providers = getNetworkProviders(network.getId()); List<Provider> providers = getNetworkProviders(network.getId());

View File

@ -562,7 +562,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
// the code would be triggered // the code would be triggered
s_logger.debug("Cleaning up remote access vpns as a part of public IP id=" + ipId + " release..."); s_logger.debug("Cleaning up remote access vpns as a part of public IP id=" + ipId + " release...");
try { try {
_vpnMgr.destroyRemoteAccessVpnForIp(ipId, caller); _vpnMgr.destroyRemoteAccessVpnForIp(ipId, caller,false);
} catch (ResourceUnavailableException e) { } catch (ResourceUnavailableException e) {
s_logger.warn("Unable to destroy remote access vpn for ip id=" + ipId + " as a part of ip release", e); s_logger.warn("Unable to destroy remote access vpn for ip id=" + ipId + " as a part of ip release", e);
success = false; success = false;

View File

@ -39,6 +39,7 @@ import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import com.cloud.network.router.VirtualRouter;
import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiConstants;
@ -2002,7 +2003,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
@DB @DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_UPDATE, eventDescription = "updating network", async = true) @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, 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, boolean updateInSequence) { final Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String customId, boolean updateInSequence, boolean forced) {
boolean restartNetwork = false; boolean restartNetwork = false;
// verify input parameters // verify input parameters
@ -2248,14 +2249,39 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount); 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 // 1) Shutdown all the elements and cleanup all the rules. Don't allow to shutdown network in intermediate
// states - Shutdown and Implementing // states - Shutdown and Implementing
List<DomainRouterVO> routers=null;
int resourceCount=1; int resourceCount=1;
if(updateInSequence && restartNetwork && _networkOfferingDao.findById(network.getNetworkOfferingId()).getRedundantRouter() && networkOfferingId!=null && _networkOfferingDao.findById(networkOfferingId).getRedundantRouter() && network.getVpcId()==null) { if(updateInSequence && restartNetwork && _networkOfferingDao.findById(network.getNetworkOfferingId()).getRedundantRouter()
&& (networkOfferingId==null || _networkOfferingDao.findById(networkOfferingId).getRedundantRouter()) && network.getVpcId()==null) {
_networkMgr.canUpdateInSequence(network); _networkMgr.canUpdateInSequence(network);
NetworkDetailVO networkDetail =new NetworkDetailVO(network.getId(),Network.updatingInSequence,"true",true); NetworkDetailVO networkDetail =new NetworkDetailVO(network.getId(),Network.updatingInSequence,"true",true);
_networkDetailsDao.persist(networkDetail); _networkDetailsDao.persist(networkDetail);
_networkMgr.configureUpdateInSequence(network); _networkMgr.configureUpdateInSequence(network);
resourceCount=_networkMgr.getResourceCount(network); resourceCount=_networkMgr.getResourceCount(network);
//check if routers are in correct state before proceeding with the update
List<DomainRouterVO> 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<String > 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());
} }
boolean validStateToShutdown = (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated); boolean validStateToShutdown = (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated);

View File

@ -696,6 +696,16 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian
return _routerDao.listByVpcId(vpcId); return _routerDao.listByVpcId(vpcId);
} }
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override @Override
public boolean startRemoteAccessVpn(final RemoteAccessVpn vpn, final VirtualRouter router) throws ResourceUnavailableException { public boolean startRemoteAccessVpn(final RemoteAccessVpn vpn, final VirtualRouter router) throws ResourceUnavailableException {
if (router.getState() != State.Running) { if (router.getState() != State.Running) {

View File

@ -281,7 +281,7 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
@Override @Override
@DB @DB
@ActionEvent(eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, eventDescription = "removing remote access vpn", async = true) @ActionEvent(eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, eventDescription = "removing remote access vpn", async = true)
public boolean destroyRemoteAccessVpnForIp(long ipId, Account caller) throws ResourceUnavailableException { public boolean destroyRemoteAccessVpnForIp(long ipId, Account caller, final boolean forceCleanup) throws ResourceUnavailableException {
final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipId); final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipId);
if (vpn == null) { if (vpn == null) {
s_logger.debug("there are no Remote access vpns for public ip address id=" + ipId); s_logger.debug("there are no Remote access vpns for public ip address id=" + ipId);
@ -309,7 +309,7 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
RemoteAccessVpn.State.Running); RemoteAccessVpn.State.Running);
success = false; success = false;
} finally { } finally {
if (success) { if (success|| forceCleanup) {
//Cleanup corresponding ports //Cleanup corresponding ports
final List<? extends FirewallRule> vpnFwRules = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn); final List<? extends FirewallRule> vpnFwRules = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn);
@ -339,7 +339,7 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc
success = _firewallMgr.applyIngressFirewallRules(ipId, caller); success = _firewallMgr.applyIngressFirewallRules(ipId, caller);
} }
if (success) { if (success|| forceCleanup) {
try { try {
Transaction.execute(new TransactionCallbackNoReturn() { Transaction.execute(new TransactionCallbackNoReturn() {
@Override @Override

View File

@ -786,7 +786,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
try { try {
for (RemoteAccessVpnVO vpn : remoteAccessVpns) { for (RemoteAccessVpnVO vpn : remoteAccessVpns) {
_remoteAccessVpnMgr.destroyRemoteAccessVpnForIp(vpn.getServerAddressId(), caller); _remoteAccessVpnMgr.destroyRemoteAccessVpnForIp(vpn.getServerAddressId(), caller, false);
} }
} catch (ResourceUnavailableException ex) { } catch (ResourceUnavailableException ex) {
s_logger.warn("Failed to cleanup remote access vpn resources as a part of account id=" + accountId + " cleanup due to Exception: ", ex); s_logger.warn("Failed to cleanup remote access vpn resources as a part of account id=" + accountId + " cleanup due to Exception: ", ex);

View File

@ -247,7 +247,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
*/ */
@Override @Override
public Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, 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,boolean updateInSequence) { Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork, String newUUID,boolean updateInSequence, boolean forced) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@ -846,6 +846,16 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
return false; return false;
} }
@Override
public List<String> getServicesNotSupportedInNewOffering(Network network, long newNetworkOfferingId) {
return null;
}
@Override
public void cleanupConfigForServicesInNetwork(List<String> services, Network network) {
return;
}
@Override @Override
public void configureUpdateInSequence(Network network) { public void configureUpdateInSequence(Network network) {
return; return;