diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 7621404f120..d50fc02d4be 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -122,6 +122,7 @@ import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; +import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.FirewallServiceProvider; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.element.NetworkElement; @@ -1424,6 +1425,26 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } } + protected void prepareElement(NetworkElement element, NetworkVO network, NicProfile profile, VirtualMachineProfile vmProfile, + DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { + element.prepare(network, profile, vmProfile, dest, context); + if (vmProfile.getType() == Type.User && element.getProvider() != null) { + if (areServicesSupportedInNetwork(network.getId(), Service.Dhcp) && + isProviderSupportedInNetwork(network.getId(), Service.Dhcp, element.getProvider()) && + (element instanceof DhcpServiceProvider)) { + DhcpServiceProvider sp = (DhcpServiceProvider)element; + sp.addDhcpEntry(network, profile, vmProfile, dest, context); + } + if (areServicesSupportedInNetwork(network.getId(), Service.UserData) && + isProviderSupportedInNetwork(network.getId(), Service.UserData, element.getProvider()) && + (element instanceof UserDataServiceProvider)) { + UserDataServiceProvider sp = (UserDataServiceProvider)element; + sp.addPasswordAndUserdata(network, profile, vmProfile, dest, context); + } + } + } + @DB protected void updateNic(NicVO nic, long networkId, int count) { Transaction txn = Transaction.currentTxn(); @@ -1502,7 +1523,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to prepare for " + nic); } - element.prepare(network, profile, vmProfile, dest, context); + prepareElement(element, network, profile, vmProfile, dest, context); } profile.setSecurityGroupEnabled(isSecurityGroupSupportedInNetwork(network)); diff --git a/server/src/com/cloud/network/element/RedundantVirtualRouterElement.java b/server/src/com/cloud/network/element/RedundantVirtualRouterElement.java index f3d8afa8a9d..d168c874158 100644 --- a/server/src/com/cloud/network/element/RedundantVirtualRouterElement.java +++ b/server/src/com/cloud/network/element/RedundantVirtualRouterElement.java @@ -82,8 +82,7 @@ public class RedundantVirtualRouterElement extends VirtualRouterElement implemen if ((routers == null) || (routers.size() == 0)) { throw new ResourceUnavailableException("Can't find at least one running router!", this.getClass(), 0); } - List rets = _routerMgr.addVirtualMachineIntoNetwork(network, nic, uservm, dest, context, routers); - return (rets != null) && (!rets.isEmpty()); + return true; } @Override diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 229076119cc..b12faef4cab 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -151,18 +151,7 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl if ((routers == null) || (routers.size() == 0)) { throw new ResourceUnavailableException("Can't find at least one running router!", this.getClass(), 0); } - - //for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when network.dns.basiczone.updates is set to "all" - Long podId = dest.getPod().getId(); - DataCenter dc = dest.getDataCenter(); - boolean isPodBased = (dc.getNetworkType() == NetworkType.Basic || _networkMgr.isSecurityGroupSupportedInNetwork(network)) && network.getTrafficType() == TrafficType.Guest; - if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { - List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.DHCP_USERDATA); - routers.addAll(allRunningRoutersOutsideThePod); - } - - List rets = _routerMgr.addVirtualMachineIntoNetwork(network, nic, uservm, dest, context, routers); - return (rets != null) && (!rets.isEmpty()); + return true; } @Override @@ -488,14 +477,96 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl @Override public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { - // TODO Auto-generated method stub + if (canHandle(network, Service.Dhcp)) { + if (vm.getType() != VirtualMachine.Type.User) { + return false; + } + + @SuppressWarnings("unchecked") + VirtualMachineProfile uservm = (VirtualMachineProfile)vm; + + boolean publicNetwork = false; + if (_networkMgr.isProviderSupportedInNetwork(network.getId(), Service.SourceNat, getProvider())) { + publicNetwork = true; + } + boolean isPodBased = (dest.getDataCenter().getNetworkType() == NetworkType.Basic || _networkMgr.isSecurityGroupSupportedInNetwork(network)) && + network.getTrafficType() == TrafficType.Guest; + + List routers; + + if (publicNetwork) { + routers = _routerDao.listByNetworkAndRole(network.getId(), Role.DHCP_FIREWALL_LB_PASSWD_USERDATA); + } else { + Long podId = dest.getPod().getId(); + if (isPodBased) { + routers = _routerDao.listByNetworkAndPodAndRole(network.getId(), podId, Role.DHCP_USERDATA); + } else { + routers = _routerDao.listByNetworkAndRole(network.getId(), Role.DHCP_USERDATA); + } + } + + //for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when network.dns.basiczone.updates is set to "all" + Long podId = dest.getPod().getId(); + if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { + List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.DHCP_USERDATA); + routers.addAll(allRunningRoutersOutsideThePod); + } + + if ((routers == null) || (routers.size() == 0)) { + throw new ResourceUnavailableException("Can't find at least one router!", this.getClass(), 0); + } + + List rets = _routerMgr.applyDhcpEntry(network, nic, uservm, dest, context, routers); + return (rets != null) && (!rets.isEmpty()); + } return false; } @Override public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { - // TODO Auto-generated method stub + if (canHandle(network, Service.UserData)) { + if (vm.getType() != VirtualMachine.Type.User) { + return false; + } + + @SuppressWarnings("unchecked") + VirtualMachineProfile uservm = (VirtualMachineProfile)vm; + + boolean publicNetwork = false; + if (_networkMgr.isProviderSupportedInNetwork(network.getId(), Service.SourceNat, getProvider())) { + publicNetwork = true; + } + boolean isPodBased = (dest.getDataCenter().getNetworkType() == NetworkType.Basic || _networkMgr.isSecurityGroupSupportedInNetwork(network)) && + network.getTrafficType() == TrafficType.Guest; + + List routers; + + if (publicNetwork) { + routers = _routerDao.listByNetworkAndRole(network.getId(), Role.DHCP_FIREWALL_LB_PASSWD_USERDATA); + } else { + Long podId = dest.getPod().getId(); + if (isPodBased) { + routers = _routerDao.listByNetworkAndPodAndRole(network.getId(), podId, Role.DHCP_USERDATA); + } else { + routers = _routerDao.listByNetworkAndRole(network.getId(), Role.DHCP_USERDATA); + } + } + + //for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when network.dns.basiczone.updates is set to "all" + Long podId = dest.getPod().getId(); + if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { + List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running, Role.DHCP_USERDATA); + routers.addAll(allRunningRoutersOutsideThePod); + } + + if ((routers == null) || (routers.size() == 0)) { + throw new ResourceUnavailableException("Can't find at least one router!", this.getClass(), 0); + } + + List rets = _routerMgr.applyUserData(network, nic, uservm, dest, context, routers); + return (rets != null) && (!rets.isEmpty()); + } return false; } } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index 1647f6fe509..315318dc72f 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -73,7 +73,8 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA //List deployDhcp(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException; - List addVirtualMachineIntoNetwork(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; + List applyDhcpEntry(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; + List applyUserData(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; boolean startRemoteAccessVpn(Network network, RemoteAccessVpn vpn, List routers) throws ResourceUnavailableException; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 9f9d8bf63e9..5181db20c91 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1966,7 +1966,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } @Override - public List addVirtualMachineIntoNetwork(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, List routers) + public List applyDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { List rets = new ArrayList(routers.size()); @@ -1981,7 +1981,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian List disconnectedRouters = new ArrayList(); for (DomainRouterVO router : routers) { - boolean sendPasswordAndVmData = true; boolean sendDnsDhcpData = true; if (router.getState() != State.Running) { @@ -2005,7 +2004,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (isZoneBasic) { podId = dest.getPod().getId(); if (router.getPodIdToDeployIn().longValue() != podId.longValue()) { - sendPasswordAndVmData = false; if (_dnsBasicZoneUpdates.equalsIgnoreCase("pod")) { sendDnsDhcpData = false; } @@ -2034,6 +2032,99 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmds.addCommand("dhcp", dhcpCommand); } + if (cmds.size() > 0) { + boolean podLevelException = false; + //for user vm in Basic zone we should try to re-deploy vm in a diff pod if it fails to deploy in original pod; so throwing exception with Pod scope + if (isZoneBasic && podId != null && profile.getVirtualMachine().getType() == VirtualMachine.Type.User && network.getTrafficType() == TrafficType.Guest && network.getGuestType() == Network.GuestType.Shared) { + podLevelException = true; + } + try { + _agentMgr.send(router.getHostId(), cmds); + } catch (AgentUnavailableException e){ + s_logger.warn("Unable to reach the agent " + router.getHostId(), e); + disconnectedRouters.add(router); + continue; + } catch (OperationTimedoutException e) { + s_logger.warn("Connection timeout on host " + router.getHostId(), e); + disconnectedRouters.add(router); + continue; + } + connectedRouters.add(router); + + Answer answer = cmds.getAnswer("dhcp"); + if (!answer.getResult()) { + s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getHostName() + " due to " + answer.getDetails()); + if (podLevelException) { + throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), Pod.class, podId); + } + throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn()); + } + } + + rets.add(router); + } + + String msg = "Unable to apply dhcp entry for new VM into network on disconnected router "; + if (!connectedRouters.isEmpty()) { + // These disconnected ones are out of sync now, stop them for synchronization + handleSingleWorkingRedundantRouter(connectedRouters, disconnectedRouters, msg); + } else if (!disconnectedRouters.isEmpty()) { + for (VirtualRouter router : disconnectedRouters) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(msg + router.getInstanceName() + "(" + router.getId() + ")"); + } + } + throw new ResourceUnavailableException(msg, VirtualRouter.class, disconnectedRouters.get(0).getId()); + } + + return rets; + } + + @Override + public List applyUserData(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context, List routers) + throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + + List rets = new ArrayList(routers.size()); + _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); + + DataCenter dc = dest.getDataCenter(); + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); + boolean isZoneBasic = (dc.getNetworkType() == NetworkType.Basic); + + List connectedRouters = new ArrayList(); + List disconnectedRouters = new ArrayList(); + + for (DomainRouterVO router : routers) { + boolean sendPasswordAndVmData = true; + + if (router.getState() != State.Running) { + s_logger.warn("Unable to add virtual machine " + profile.getVirtualMachine() + " to the router " + router + " as the router is not in Running state"); + continue; + } + + if (router.isStopPending()) { + if (_hostDao.findById(router.getHostId()).getStatus() == Status.Up) { + throw new ResourceUnavailableException("Unable to process due to the stop pending router " + router.getInstanceName() + " haven't been stopped after it's host coming back!", + VirtualRouter.class, router.getId()); + } + s_logger.warn("Unable to add virtual machine " + profile.getVirtualMachine() + " to the router " + router + " as the router is to be stopped"); + continue; + } + + //for basic zone: + //1) send vm data/password information only to the dhcp in the same pod + //2) send dhcp/dns information to all routers in the cloudstack only when _dnsBasicZoneUpdates is set to "all" value + Long podId = null; + if (isZoneBasic) { + podId = dest.getPod().getId(); + if (router.getPodIdToDeployIn().longValue() != podId.longValue()) { + sendPasswordAndVmData = false; + } + } + + Commands cmds = new Commands(OnError.Stop); + if (sendPasswordAndVmData) { String password = (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword); String userData = profile.getVirtualMachine().getUserData(); @@ -2078,16 +2169,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } connectedRouters.add(router); - Answer answer = cmds.getAnswer("dhcp"); - if (!answer.getResult()) { - s_logger.error("Unable to set dhcp entry for " + profile + " on domR: " + router.getHostName() + " due to " + answer.getDetails()); - if (podLevelException) { - throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), Pod.class, podId); - } - throw new ResourceUnavailableException("Unable to set dhcp entry for " + profile + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn()); - } - - answer = cmds.getAnswer("password"); + Answer answer = cmds.getAnswer("password"); if (answer != null && !answer.getResult()) { s_logger.error("Unable to set password for " + profile + " due to " + answer.getDetails()); if (podLevelException) { @@ -2109,7 +2191,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian rets.add(router); } - String msg = "Unable to add new VM into network on disconnected router "; + String msg = "Unable to apply userdata for new VM into network on disconnected router "; if (!connectedRouters.isEmpty()) { // These disconnected ones are out of sync now, stop them for synchronization handleSingleWorkingRedundantRouter(connectedRouters, disconnectedRouters, msg);