mirror of https://github.com/apache/cloudstack.git
332 lines
15 KiB
Java
332 lines
15 KiB
Java
// 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.topology;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
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.InsufficientCapacityException;
|
|
import com.cloud.exception.ResourceUnavailableException;
|
|
import com.cloud.host.dao.HostDao;
|
|
import com.cloud.network.Network;
|
|
import com.cloud.network.PublicIpAddress;
|
|
import com.cloud.network.VpnUser;
|
|
import com.cloud.network.lb.LoadBalancingRule;
|
|
import com.cloud.network.router.VirtualRouter;
|
|
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.RuleApplier;
|
|
import com.cloud.network.rules.RuleApplierWrapper;
|
|
import com.cloud.network.rules.StaticNat;
|
|
import com.cloud.network.rules.StaticNatRules;
|
|
import com.cloud.network.rules.VirtualNetworkApplianceFactory;
|
|
import com.cloud.network.rules.VpnRules;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.vm.DomainRouterVO;
|
|
import com.cloud.vm.NicProfile;
|
|
import com.cloud.vm.VirtualMachine.State;
|
|
import com.cloud.vm.VirtualMachineProfile;
|
|
import com.cloud.vm.VirtualMachineProfile.Param;
|
|
|
|
public class BasicNetworkTopology implements NetworkTopology {
|
|
|
|
private static final Logger s_logger = Logger.getLogger(BasicNetworkTopology.class);
|
|
|
|
@Inject
|
|
protected VirtualNetworkApplianceFactory virtualNetworkApplianceFactory;
|
|
|
|
@Inject
|
|
protected DataCenterDao _dcDao;
|
|
|
|
@Inject
|
|
protected HostDao _hostDao;
|
|
|
|
@Override
|
|
public List<DomainRouterVO> findOrDeployVirtualRouterInGuestNetwork(final Network guestNetwork, final DeployDestination dest, final Account owner, final boolean isRedundant,
|
|
final Map<Param, Object> params) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public StringBuilder createGuestBootLoadArgs(final NicProfile guestNic, final String defaultDns1, final String defaultDns2, final DomainRouterVO router) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public String retrieveGuestDhcpRange(final NicProfile guestNic, final Network guestNetwork, final DataCenter dc) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public NicProfile retrieveControlNic(final VirtualMachineProfile profile) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean configDhcpForSubnet(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest,
|
|
final List<DomainRouterVO> routers) throws ResourceUnavailableException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean applyDhcpEntry(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest,
|
|
final List<DomainRouterVO> routers) throws ResourceUnavailableException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean applyUserData(final Network network, final NicProfile nic, final VirtualMachineProfile profile, final DeployDestination dest, final List<DomainRouterVO> routers)
|
|
throws ResourceUnavailableException {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean applyLoadBalancingRules(final Network network, final List<LoadBalancingRule> rules, final List<? extends VirtualRouter> routers)
|
|
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;
|
|
|
|
LoadBalancingRules loadBalancingRules = virtualNetworkApplianceFactory.createLoadBalancingRules(network, rules);
|
|
|
|
return applyRules(network, routers, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(loadBalancingRules));
|
|
}
|
|
|
|
@Override
|
|
public boolean applyFirewallRules(final Network network, final List<? extends FirewallRule> rules, final List<? extends VirtualRouter> routers)
|
|
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;
|
|
|
|
FirewallRules firewallRules = virtualNetworkApplianceFactory.createFirewallRules(network, rules);
|
|
|
|
return applyRules(network, routers, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(firewallRules));
|
|
}
|
|
|
|
@Override
|
|
public boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules, final List<? extends VirtualRouter> routers) 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;
|
|
|
|
StaticNatRules natRules = virtualNetworkApplianceFactory.createStaticNatRules(network, rules);
|
|
|
|
return applyRules(network, routers, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper<RuleApplier>(natRules));
|
|
}
|
|
|
|
@Override
|
|
public boolean associatePublicIP(final Network network, final List<? extends PublicIpAddress> ipAddress, final List<? extends VirtualRouter> routers)
|
|
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;
|
|
|
|
IpAssociationRules ipAddresses = virtualNetworkApplianceFactory.createIpAssociationRules(network, ipAddress);
|
|
|
|
return applyRules(network, routers, 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 VPN RULES");
|
|
|
|
// should become a BasicNetworkVisitor in the end
|
|
AdvancedNetworkVisitor visitor = new AdvancedNetworkVisitor();
|
|
|
|
boolean agentResults = true;
|
|
|
|
for (final DomainRouterVO router : routers) {
|
|
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());
|
|
}
|
|
|
|
VpnRules vpnRules = virtualNetworkApplianceFactory.createVpnRules(network, users);
|
|
|
|
// [FIXME] REMOVE THIS SHIT AND INJECT USING A FACTORY FOR THE
|
|
// VISITORS
|
|
visitor.setApplianceManager(vpnRules.getApplianceManager());
|
|
|
|
// 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(visitor, 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 applyRules(final Network network, final List<? extends VirtualRouter> routers, final String typeString, final boolean isPodLevelException, final Long podId,
|
|
final boolean failWhenDisconnect, final RuleApplierWrapper<RuleApplier> ruleApplierWrapper) throws ResourceUnavailableException {
|
|
|
|
if (routers == null || routers.isEmpty()) {
|
|
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());
|
|
}
|
|
|
|
// should become a BasicNetworkVisitor in the end
|
|
AdvancedNetworkVisitor visitor = new AdvancedNetworkVisitor();
|
|
|
|
RuleApplier ruleApplier = ruleApplierWrapper.getRuleType();
|
|
|
|
// [FIXME] REMOVE THIS SHIT AND INJECT USING A FACTORY FOR THE VISITORS
|
|
visitor.setApplianceManager(ruleApplier.getApplianceManager());
|
|
|
|
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 ";
|
|
for (final VirtualRouter router : routers) {
|
|
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");
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
ruleApplier.accept(visitor, 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()) {
|
|
if (!isZoneBasic && !disconnectedRouters.isEmpty() && disconnectedRouters.get(0).getIsRedundantRouter()) {
|
|
// These disconnected redundant virtual routers are out of sync
|
|
// now, stop them for synchronization
|
|
// [FIXME] handleSingleWorkingRedundantRouter(connectedRouters,
|
|
// disconnectedRouters, msg);
|
|
}
|
|
} else if (!disconnectedRouters.isEmpty()) {
|
|
for (final VirtualRouter router : disconnectedRouters) {
|
|
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;
|
|
}
|
|
} |