| // 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 org.apache.cloudstack.network.topology; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.inject.Inject; |
| |
| import org.apache.log4j.Logger; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.beans.factory.annotation.Qualifier; |
| import org.springframework.stereotype.Component; |
| |
| import com.cloud.dc.DataCenter; |
| import com.cloud.dc.DataCenter.NetworkType; |
| import com.cloud.dc.Pod; |
| import com.cloud.dc.dao.DataCenterDao; |
| import com.cloud.deploy.DeployDestination; |
| import com.cloud.exception.AgentUnavailableException; |
| import com.cloud.exception.ConcurrentOperationException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.host.Status; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.network.Network; |
| import com.cloud.network.Networks.TrafficType; |
| import com.cloud.network.PublicIpAddress; |
| import com.cloud.network.RemoteAccessVpn; |
| import com.cloud.network.VpnUser; |
| import com.cloud.network.lb.LoadBalancingRule; |
| import com.cloud.network.router.NetworkHelper; |
| import com.cloud.network.router.VirtualRouter; |
| import com.cloud.network.rules.BasicVpnRules; |
| import com.cloud.network.rules.DhcpEntryRules; |
| import com.cloud.network.rules.DhcpSubNetRules; |
| import com.cloud.network.rules.FirewallRule; |
| import com.cloud.network.rules.FirewallRules; |
| import com.cloud.network.rules.IpAssociationRules; |
| import com.cloud.network.rules.LoadBalancingRules; |
| import com.cloud.network.rules.PasswordToRouterRules; |
| import com.cloud.network.rules.RuleApplier; |
| import com.cloud.network.rules.RuleApplierWrapper; |
| import com.cloud.network.rules.SshKeyToRouterRules; |
| import com.cloud.network.rules.StaticNat; |
| import com.cloud.network.rules.StaticNatRules; |
| import com.cloud.network.rules.UserdataPwdRules; |
| import com.cloud.network.rules.UserdataToRouterRules; |
| import com.cloud.network.vpc.NetworkACLItem; |
| import com.cloud.network.vpc.PrivateGateway; |
| import com.cloud.network.vpc.StaticRouteProfile; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.vm.DomainRouterVO; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.VirtualMachine; |
| import com.cloud.vm.VirtualMachine.State; |
| import com.cloud.vm.VirtualMachineProfile; |
| |
| @Component |
| public class BasicNetworkTopology implements NetworkTopology { |
| |
| private static final Logger s_logger = Logger.getLogger(BasicNetworkTopology.class); |
| |
| @Autowired |
| @Qualifier("basicNetworkVisitor") |
| protected BasicNetworkVisitor _basicVisitor; |
| |
| @Inject |
| protected DataCenterDao _dcDao; |
| |
| @Inject |
| protected HostDao _hostDao; |
| |
| @Autowired |
| @Qualifier("networkHelper") |
| protected NetworkHelper _networkHelper; |
| |
| @Override |
| public NetworkTopologyVisitor getVisitor() { |
| return _basicVisitor; |
| } |
| |
| @Override |
| public boolean setupPrivateGateway(final PrivateGateway gateway, final VirtualRouter router) throws ConcurrentOperationException, ResourceUnavailableException { |
| throw new CloudRuntimeException("setupPrivateGateway not implemented in Basic Network Topology."); |
| } |
| |
| @Override |
| public String[] applyVpnUsers(final RemoteAccessVpn vpn, final List<? extends VpnUser> users, final VirtualRouter router) throws ResourceUnavailableException { |
| throw new CloudRuntimeException("applyVpnUsers not implemented in Basic Network Topology."); |
| } |
| |
| @Override |
| public boolean applyStaticRoutes(final List<StaticRouteProfile> staticRoutes, final List<DomainRouterVO> routers) throws ResourceUnavailableException { |
| throw new CloudRuntimeException("applyStaticRoutes not implemented in Basic Network Topology."); |
| } |
| |
| @Override |
| public boolean applyNetworkACLs(final Network network, final List<? extends NetworkACLItem> rules, final VirtualRouter router, final boolean isPrivateGateway) |
| throws ResourceUnavailableException { |
| throw new CloudRuntimeException("applyNetworkACLs not implemented in Basic Network Topology."); |
| } |
| |
| @Override |
| public boolean setupDhcpForPvlan(final boolean add, final DomainRouterVO router, final Long hostId, final NicProfile nic) throws ResourceUnavailableException { |
| throw new CloudRuntimeException("setupDhcpForPvlan not implemented in Basic Network Topology."); |
| } |
| |
| @Override |
| public boolean configDhcpForSubnet(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest, |
| final List<DomainRouterVO> routers) throws ResourceUnavailableException { |
| |
| s_logger.debug("CONFIG DHCP FOR SUBNETS RULES"); |
| |
| // Assuming we have only one router per network For Now. |
| final DomainRouterVO router = routers.get(0); |
| if (router.getState() != State.Running) { |
| s_logger.warn("Failed to configure dhcp: router not in running state"); |
| throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); |
| } |
| |
| final DhcpSubNetRules subNetRules = new DhcpSubNetRules(network, nic, profile); |
| |
| return subNetRules.accept(_basicVisitor, router); |
| } |
| |
| @Override |
| public boolean applyDhcpEntry(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest, |
| final DomainRouterVO router) throws ResourceUnavailableException { |
| |
| s_logger.debug("APPLYING DHCP ENTRY RULES"); |
| |
| final String typeString = "dhcp entry"; |
| final Long podId = dest.getPod().getId(); |
| boolean isPodLevelException = 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 (podId != null && profile.getVirtualMachine().getType() == VirtualMachine.Type.User && network.getTrafficType() == TrafficType.Guest |
| && network.getGuestType() == Network.GuestType.Shared) { |
| isPodLevelException = true; |
| } |
| |
| final boolean failWhenDisconnect = false; |
| |
| final DhcpEntryRules dhcpRules = new DhcpEntryRules(network, nic, profile, dest); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(dhcpRules)); |
| } |
| |
| @Override |
| public boolean applyUserData(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest, final DomainRouterVO router) |
| throws ResourceUnavailableException { |
| |
| s_logger.debug("APPLYING USERDATA RULES"); |
| |
| final String typeString = "userdata and password entry"; |
| final Long podId = dest.getPod().getId(); |
| boolean isPodLevelException = false; |
| |
| if (podId != null && profile.getVirtualMachine().getType() == VirtualMachine.Type.User && network.getTrafficType() == TrafficType.Guest |
| && network.getGuestType() == Network.GuestType.Shared) { |
| isPodLevelException = true; |
| } |
| |
| final boolean failWhenDisconnect = false; |
| |
| final UserdataPwdRules pwdRules = new UserdataPwdRules(network, nic, profile, dest); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(pwdRules)); |
| } |
| |
| @Override |
| public boolean applyLoadBalancingRules(final Network network, final List<LoadBalancingRule> rules, final VirtualRouter router) |
| throws ResourceUnavailableException { |
| |
| if (rules == null || rules.isEmpty()) { |
| s_logger.debug("No lb rules to be applied for network " + network.getId()); |
| return true; |
| } |
| |
| s_logger.debug("APPLYING LOAD BALANCING RULES"); |
| |
| final String typeString = "loadbalancing rules"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final LoadBalancingRules loadBalancingRules = new LoadBalancingRules(network, rules); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(loadBalancingRules)); |
| } |
| |
| @Override |
| public boolean applyFirewallRules(final Network network, final List<? extends FirewallRule> rules, final VirtualRouter router) |
| throws ResourceUnavailableException { |
| if (rules == null || rules.isEmpty()) { |
| s_logger.debug("No firewall rules to be applied for network " + network.getId()); |
| return true; |
| } |
| |
| s_logger.debug("APPLYING FIREWALL RULES"); |
| |
| final String typeString = "firewall rules"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final FirewallRules firewallRules = new FirewallRules(network, rules); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(firewallRules)); |
| } |
| |
| @Override |
| public boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules, final VirtualRouter router) throws ResourceUnavailableException { |
| if (rules == null || rules.isEmpty()) { |
| s_logger.debug("No static nat rules to be applied for network " + network.getId()); |
| return true; |
| } |
| |
| s_logger.debug("APPLYING STATIC NAT RULES"); |
| |
| final String typeString = "static nat rules"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final StaticNatRules natRules = new StaticNatRules(network, rules); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(natRules)); |
| } |
| |
| @Override |
| public boolean associatePublicIP(final Network network, final List<? extends PublicIpAddress> ipAddress, final VirtualRouter router) |
| throws ResourceUnavailableException { |
| if (ipAddress == null || ipAddress.isEmpty()) { |
| s_logger.debug("No ip association rules to be applied for network " + network.getId()); |
| return true; |
| } |
| |
| s_logger.debug("APPLYING IP RULES"); |
| |
| final String typeString = "ip association"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final IpAssociationRules ipAddresses = new IpAssociationRules(network, ipAddress); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(ipAddresses)); |
| } |
| |
| @Override |
| public String[] applyVpnUsers(final Network network, final List<? extends VpnUser> users, final List<DomainRouterVO> routers) throws ResourceUnavailableException { |
| if (routers == null || routers.isEmpty()) { |
| s_logger.warn("Failed to add/remove VPN users: no router found for account and zone"); |
| throw new ResourceUnavailableException("Unable to assign ip addresses, domR doesn't exist for network " + network.getId(), DataCenter.class, network.getDataCenterId()); |
| } |
| |
| s_logger.debug("APPLYING BASIC VPN RULES"); |
| |
| final BasicVpnRules vpnRules = new BasicVpnRules(network, users); |
| boolean agentResults = true; |
| |
| for (final DomainRouterVO router : routers) { |
| if(router.getState() == State.Stopped || router.getState() == State.Stopping){ |
| s_logger.info("The router " + router.getInstanceName()+ " is in the " + router.getState() + " state. So not applying the VPN rules. Will be applied once the router gets restarted."); |
| continue; |
| } |
| else if (router.getState() != State.Running) { |
| s_logger.warn("Failed to add/remove VPN users: router not in running state"); |
| throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, |
| network.getDataCenterId()); |
| } |
| |
| // Currently we receive just one answer from the agent. In the |
| // future we have to parse individual answers and set |
| // results accordingly |
| final boolean agentResult = vpnRules.accept(_basicVisitor, router); |
| agentResults = agentResults && agentResult; |
| } |
| |
| final String[] result = new String[users.size()]; |
| for (int i = 0; i < result.length; i++) { |
| if (agentResults) { |
| result[i] = null; |
| } else { |
| result[i] = String.valueOf(agentResults); |
| } |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public boolean savePasswordToRouter(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final VirtualRouter router) |
| throws ResourceUnavailableException { |
| |
| s_logger.debug("SAVE PASSWORD TO ROUTE RULES"); |
| |
| final String typeString = "save password entry"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final PasswordToRouterRules routerRules = new PasswordToRouterRules(network, nic, profile); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(routerRules)); |
| } |
| |
| @Override |
| public boolean saveSSHPublicKeyToRouter(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final VirtualRouter router, |
| final String sshPublicKey) throws ResourceUnavailableException { |
| s_logger.debug("SAVE SSH PUB KEY TO ROUTE RULES"); |
| |
| final String typeString = "save SSHkey entry"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final SshKeyToRouterRules keyToRouterRules = new SshKeyToRouterRules(network, nic, profile, sshPublicKey); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(keyToRouterRules)); |
| } |
| |
| @Override |
| public boolean saveUserDataToRouter(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final VirtualRouter router) |
| throws ResourceUnavailableException { |
| s_logger.debug("SAVE USERDATA TO ROUTE RULES"); |
| |
| final String typeString = "save userdata entry"; |
| final boolean isPodLevelException = false; |
| final boolean failWhenDisconnect = false; |
| final Long podId = null; |
| |
| final UserdataToRouterRules userdataToRouterRules = new UserdataToRouterRules(network, nic, profile); |
| |
| return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(userdataToRouterRules)); |
| } |
| |
| @Override |
| public boolean applyRules(final Network network, final VirtualRouter router, final String typeString, final boolean isPodLevelException, final Long podId, |
| final boolean failWhenDisconnect, final RuleApplierWrapper<RuleApplier> ruleApplierWrapper) throws ResourceUnavailableException { |
| |
| if (router == null) { |
| s_logger.warn("Unable to apply " + typeString + ", virtual router doesn't exist in the network " + network.getId()); |
| throw new ResourceUnavailableException("Unable to apply " + typeString, DataCenter.class, network.getDataCenterId()); |
| } |
| |
| final RuleApplier ruleApplier = ruleApplierWrapper.getRuleType(); |
| |
| final DataCenter dc = _dcDao.findById(network.getDataCenterId()); |
| final boolean isZoneBasic = dc.getNetworkType() == NetworkType.Basic; |
| |
| // isPodLevelException and podId is only used for basic zone |
| assert !(!isZoneBasic && isPodLevelException || isZoneBasic && isPodLevelException && podId == null); |
| |
| final List<VirtualRouter> connectedRouters = new ArrayList<VirtualRouter>(); |
| final List<VirtualRouter> disconnectedRouters = new ArrayList<VirtualRouter>(); |
| boolean result = true; |
| final String msg = "Unable to apply " + typeString + " on disconnected router "; |
| if (router.getState() == State.Running) { |
| s_logger.debug("Applying " + typeString + " in network " + network); |
| |
| if (router.isStopPending()) { |
| if (_hostDao.findById(router.getHostId()).getState() == 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!", DataCenter.class, router.getDataCenterId()); |
| } |
| s_logger.debug("Router " + router.getInstanceName() + " is stop pending, so not sending apply " + typeString + " commands to the backend"); |
| return false; |
| } |
| |
| try { |
| result = ruleApplier.accept(getVisitor(), router); |
| connectedRouters.add(router); |
| } catch (final AgentUnavailableException e) { |
| s_logger.warn(msg + router.getInstanceName(), e); |
| disconnectedRouters.add(router); |
| } |
| |
| // If rules fail to apply on one domR and not due to |
| // disconnection, no need to proceed with the rest |
| if (!result) { |
| if (isZoneBasic && isPodLevelException) { |
| throw new ResourceUnavailableException("Unable to apply " + typeString + " on router ", Pod.class, podId); |
| } |
| throw new ResourceUnavailableException("Unable to apply " + typeString + " on router ", DataCenter.class, router.getDataCenterId()); |
| } |
| |
| } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { |
| s_logger.debug("Router " + router.getInstanceName() + " is in " + router.getState() + ", so not sending apply " + typeString + " commands to the backend"); |
| } else { |
| s_logger.warn("Unable to apply " + typeString + ", virtual router is not in the right state " + router.getState()); |
| if (isZoneBasic && isPodLevelException) { |
| throw new ResourceUnavailableException("Unable to apply " + typeString + ", virtual router is not in the right state", Pod.class, podId); |
| } |
| throw new ResourceUnavailableException("Unable to apply " + typeString + ", virtual router is not in the right state", DataCenter.class, router.getDataCenterId()); |
| } |
| |
| if (!connectedRouters.isEmpty()) { |
| // Shouldn't we include this check inside the method? |
| if (!isZoneBasic && !disconnectedRouters.isEmpty()) { |
| // These disconnected redundant virtual routers are out of sync |
| // now, stop them for synchronization |
| for (final VirtualRouter virtualRouter : disconnectedRouters) { |
| // If we have at least 1 disconnected redundant router, callhandleSingleWorkingRedundantRouter(). |
| if (virtualRouter.getIsRedundantRouter()) { |
| _networkHelper.handleSingleWorkingRedundantRouter(connectedRouters, disconnectedRouters, msg); |
| break; |
| } |
| } |
| } |
| } else if (!disconnectedRouters.isEmpty()) { |
| if (s_logger.isDebugEnabled()) { |
| s_logger.debug(msg + router.getInstanceName() + "(" + router.getId() + ")"); |
| } |
| if (isZoneBasic && isPodLevelException) { |
| throw new ResourceUnavailableException(msg, Pod.class, podId); |
| } |
| throw new ResourceUnavailableException(msg, DataCenter.class, disconnectedRouters.get(0).getDataCenterId()); |
| } |
| |
| result = true; |
| if (failWhenDisconnect) { |
| result = !connectedRouters.isEmpty(); |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile profile, VirtualRouter virtualRouter) throws ResourceUnavailableException { |
| s_logger.debug("REMOVING DHCP ENTRY RULE"); |
| |
| final String typeString = "dhcp entry"; |
| final Long podId = profile.getVirtualMachine().getPodIdToDeployIn(); |
| boolean isPodLevelException = false; |
| |
| if (podId != null && profile.getVirtualMachine().getType() == VirtualMachine.Type.User && network.getTrafficType() == TrafficType.Guest |
| && network.getGuestType() == Network.GuestType.Shared) { |
| isPodLevelException = true; |
| } |
| |
| final boolean failWhenDisconnect = false; |
| |
| final DhcpEntryRules dhcpRules = new DhcpEntryRules(network, nic, profile, null); |
| dhcpRules.setRemove(true); |
| |
| return applyRules(network, virtualRouter, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(dhcpRules)); |
| } |
| } |