cloudstack/server/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java

353 lines
13 KiB
Java

// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
package com.cloud.network.ovs;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.manager.Commands;
import com.cloud.configuration.Config;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.deploy.DeployDestination;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.network.Network;
import com.cloud.network.ovs.dao.OvsTunnelNetworkDao;
import com.cloud.network.ovs.dao.OvsTunnelNetworkVO;
import com.cloud.network.ovs.dao.OvsTunnelDao;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Inject;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.NicVO;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao;
@Local(value={OvsTunnelManager.class})
public class OvsTunnelManagerImpl implements OvsTunnelManager {
public static final Logger s_logger = Logger.getLogger(OvsTunnelManagerImpl.class.getName());
String _name;
boolean _isEnabled;
ScheduledExecutorService _executorPool;
ScheduledExecutorService _cleanupExecutor;
@Inject ConfigurationDao _configDao;
@Inject NicDao _nicDao;
@Inject HostDao _hostDao;
@Inject UserVmDao _userVmDao;
@Inject DomainRouterDao _routerDao;
@Inject OvsTunnelNetworkDao _tunnelNetworkDao;
@Inject AgentManager _agentMgr;
@Override
public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException {
_name = name;
_isEnabled = Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key()));
if (_isEnabled) {
_executorPool = Executors.newScheduledThreadPool(10, new NamedThreadFactory("OVS"));
_cleanupExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("OVS-Cleanup"));
}
return true;
}
protected OvsTunnelNetworkVO createTunnelRecord(long from, long to, long networkId) {
OvsTunnelNetworkVO ta = null;
try {
// Use the network id as a Key
// FIXME: networkid is long, key must be integer
// This is a horrible cast, and it must be removed and replace with something better
// before committing into master
ta = new OvsTunnelNetworkVO(from, to, (int)networkId, networkId);
OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1));
if (lock == null) {
s_logger.warn("Cannot lock table ovs_tunnel_account");
return null;
}
_tunnelNetworkDao.persist(ta);
_tunnelNetworkDao.releaseFromLockTable(lock.getId());
} catch (EntityExistsException e) {
s_logger.debug("A record for the tunnel from " + from + " to " + to + " already exists");
}
return ta;
}
private void handleCreateTunnelAnswer(Answer[] answers){
OvsCreateTunnelAnswer r = (OvsCreateTunnelAnswer) answers[0];
String s = String.format(
"(hostIP:%1$s, remoteIP:%2$s, bridge:%3$s, greKey:%4$s, portName:%5$s)",
r.getFromIp(), r.getToIp(), r.getBridge(), r.getKey(), r.getInPortName());
Long from = r.getFrom();
Long to = r.getTo();
long networkId = r.getNetworkId();
OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(from, to, networkId);
if (ta == null) {
throw new CloudRuntimeException(String.format("Unable find tunnelAccount record(from=%1$s, to=%2$s, account=%3$s", from, to, networkId));
}
s_logger.debug("Result:" + r.getResult());
if (!r.getResult()) {
ta.setState("FAILED");
s_logger.warn("Create GRE tunnel failed due to " + r.getDetails() + s);
} else {
ta.setState("SUCCESS");
ta.setPortName(r.getInPortName());
s_logger.warn("Create GRE tunnel " + r.getDetails() + s);
}
_tunnelNetworkDao.update(ta.getId(), ta);
}
@DB
protected void CheckAndCreateTunnel(VirtualMachine instance, Network nw, DeployDestination dest) {
if (!_isEnabled) {
return;
}
s_logger.debug("Creating tunnels with OVS tunnel manager");
if (instance.getType() != VirtualMachine.Type.User
&& instance.getType() != VirtualMachine.Type.DomainRouter) {
s_logger.debug("Will not work if you're not an instance or a virtual router");
return;
}
long hostId = dest.getHost().getId();
// Find active (i.e.: not shut off) VMs with a NIC on the target network
List<UserVmVO> vms = _userVmDao.listByNetworkIdAndStates(nw.getId(), State.Running, State.Starting,
State.Stopping, State.Unknown, State.Migrating);
// Find routers for the network
List<DomainRouterVO> routers = _routerDao.findByNetwork(nw.getId());
List<VMInstanceVO>ins = new ArrayList<VMInstanceVO>();
if (vms != null) {
ins.addAll(vms);
}
if (routers.size() != 0) {
ins.addAll(routers);
}
s_logger.debug("### Virtual Machines:" + vms.size());
s_logger.debug("### Virtual Routers:" + routers.size());
List<Long> toHostIds = new ArrayList<Long>();
List<Long> fromHostIds = new ArrayList<Long>();
for (VMInstanceVO v : ins) {
Long rh = v.getHostId();
if (rh == null || rh.longValue() == hostId) {
continue;
}
//FIXME: Still using 'TunnelAccount name' - but should actually be tunnelNetwork or something like that
OvsTunnelNetworkVO ta = _tunnelNetworkDao.getByFromToNetwork(hostId, rh.longValue(), nw.getId());
// Try and create the tunnel even if a previous attempt failed
if (ta == null || ta.getState().equals("FAILED")) {
s_logger.debug("Attempting to create tunnel from:" + hostId + " to:" + rh.longValue());
if (ta == null) {
this.createTunnelRecord(hostId, rh.longValue(), nw.getId());
}
if (!toHostIds.contains(rh)) {
toHostIds.add(rh);
}
}
ta = _tunnelNetworkDao.getByFromToNetwork(rh.longValue(), hostId, nw.getId());
// Try and create the tunnel even if a previous attempt failed
if (ta == null || ta.getState().equals("FAILED")) {
s_logger.debug("Attempting to create tunnel from:" + rh.longValue() + " to:" + hostId);
if (ta == null) {
this.createTunnelRecord(rh.longValue(), hostId, nw.getId());
}
if (!fromHostIds.contains(rh)) {
fromHostIds.add(rh);
}
}
}
try {
String myIp = dest.getHost().getPrivateIpAddress();
for (Long i : toHostIds) {
HostVO rHost = _hostDao.findById(i);
Commands cmds = new Commands(
new OvsCreateTunnelCommand(rHost.getPrivateIpAddress(), String.valueOf(nw.getId()),
Long.valueOf(hostId), i, nw.getId(), myIp));
s_logger.debug("Ask host " + hostId + " to create gre tunnel to " + i);
Answer[] answers = _agentMgr.send(hostId, cmds);
handleCreateTunnelAnswer(answers);
}
for (Long i : fromHostIds) {
HostVO rHost = _hostDao.findById(i);
Commands cmd2s = new Commands(
new OvsCreateTunnelCommand(myIp, String.valueOf(nw.getId()),
i, Long.valueOf(hostId), nw.getId(), rHost.getPrivateIpAddress()));
s_logger.debug("Ask host " + i + " to create gre tunnel to " + hostId);
Answer[] answers = _agentMgr.send(i, cmd2s);
handleCreateTunnelAnswer(answers);
}
} catch (Exception e) {
s_logger.debug("Ovs Tunnel network created tunnel failed", e);
}
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public String getName() {
return _name;
}
@Override
public boolean isOvsTunnelEnabled() {
return _isEnabled;
}
@Override
public void VmCheckAndCreateTunnel(VirtualMachineProfile<? extends VirtualMachine> vm, Network nw, DeployDestination dest) {
CheckAndCreateTunnel(vm.getVirtualMachine(), nw, dest);
}
private void handleDestroyTunnelAnswer(Answer ans, long from, long to, long network_id) {
if (ans.getResult()) {
OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1));
if (lock == null) {
s_logger.warn(String.format("failed to lock ovs_tunnel_account, remove record of " +
"tunnel(from=%1$s, to=%2$s account=%3$s) failed",
from, to, network_id));
return;
}
_tunnelNetworkDao.removeByFromToNetwork(from, to, network_id);
_tunnelNetworkDao.releaseFromLockTable(lock.getId());
s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) successful",
network_id, from, to));
} else {
s_logger.debug(String.format("Destroy tunnel(account:%1$s, from:%2$s, to:%3$s) failed",
network_id, from, to));
}
}
private void handleDestroyBridgeAnswer(Answer ans, long host_id, long network_id) {
if (ans.getResult()) {
OvsTunnelNetworkVO lock = _tunnelNetworkDao.acquireInLockTable(Long.valueOf(1));
if (lock == null) {
s_logger.warn("failed to lock ovs_tunnel_network, remove record");
return;
}
_tunnelNetworkDao.removeByFromNetwork(host_id, network_id);
_tunnelNetworkDao.releaseFromLockTable(lock.getId());
s_logger.debug(String.format("Destroy bridge for network %1$s successful", network_id));
} else {
s_logger.debug(String.format("Destroy bridge for network %1$s failed", network_id));
}
}
@Override
public void CheckAndDestroyTunnel(VirtualMachine vm, Network nw) {
s_logger.debug("### DESTROYING YOUR TUNNELS!");
if (!_isEnabled) {
return;
}
List<UserVmVO> userVms = _userVmDao.listByAccountIdAndHostId(vm.getAccountId(), vm.getHostId());
s_logger.debug("### User Vms:" + userVms.size());
if (vm.getType() == VirtualMachine.Type.User) {
if (userVms.size() > 1) {
return;
}
List<DomainRouterVO> routers = _routerDao.findByNetwork(nw.getId());
for (DomainRouterVO router : routers) {
if (router.getHostId() == vm.getHostId()) {
s_logger.debug("### Not destroying because on same host as router");
return;
}
}
} else if (vm.getType() == VirtualMachine.Type.DomainRouter && userVms.size() != 0) {
return;
}
s_logger.debug("### I WILL DESTROYYY");
try {
/* Now we are last one on host, destroy the bridge with all
* the tunnels for this network */
Command cmd = new OvsDestroyBridgeCommand(nw.getId());
s_logger.debug("### Destroying bridge for network " + nw.getId() + " on host:" + vm.getHostId());
Answer ans = _agentMgr.send(vm.getHostId(), cmd);
handleDestroyBridgeAnswer(ans, vm.getHostId(), nw.getId());
/* Then ask hosts have peer tunnel with me to destroy them */
List<OvsTunnelNetworkVO> peers = _tunnelNetworkDao.listByToNetwork(vm.getHostId(), nw.getId());
for (OvsTunnelNetworkVO p : peers) {
cmd = new OvsDestroyTunnelCommand(p.getNetworkId(), p.getPortName());
s_logger.debug("### Destroying tunnel to " + vm.getHostId() +
" from " + p.getFrom());
ans = _agentMgr.send(p.getFrom(), cmd);
handleDestroyTunnelAnswer(ans, p.getFrom(), p.getTo(), p.getNetworkId());
}
} catch (Exception e) {
s_logger.warn(String.format("Destroy tunnel(account:%1$s, hostId:%2$s) failed", vm.getAccountId(), vm.getHostId()), e);
}
}
@Override
public void applyDefaultFlow(VirtualMachine instance,
DeployDestination dest) {
if (!_isEnabled) {
return;
}
VirtualMachine.Type vmType = instance.getType();
if (vmType != VirtualMachine.Type.User
&& vmType != VirtualMachine.Type.DomainRouter) {
return;
}
s_logger.debug("### Applying rules for allowing broadcast traffic");
}
}