blob: 266564134cbce7a39c7a441a6225a844c4669226 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed 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.onosproject.incubator.net.virtual.impl;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.incubator.net.tunnel.TunnelId;
import org.onosproject.incubator.net.virtual.DefaultVirtualLink;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.TenantId;
import org.onosproject.incubator.net.virtual.VirtualDevice;
import org.onosproject.incubator.net.virtual.VirtualHost;
import org.onosproject.incubator.net.virtual.VirtualLink;
import org.onosproject.incubator.net.virtual.VirtualNetwork;
import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate;
import org.onosproject.incubator.net.virtual.VirtualPort;
import org.onosproject.incubator.net.virtual.VnetService;
import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProvider;
import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderRegistry;
import org.onosproject.incubator.net.virtual.provider.VirtualNetworkProviderService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentEvent;
import org.onosproject.net.intent.IntentListener;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.provider.AbstractListenerProviderRegistry;
import org.onosproject.net.provider.AbstractProviderService;
import org.onosproject.net.topology.PathService;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of the virtual network service.
*/
@Component(immediate = true)
@Service
public class VirtualNetworkManager
extends AbstractListenerProviderRegistry<VirtualNetworkEvent,
VirtualNetworkListener, VirtualNetworkProvider, VirtualNetworkProviderService>
implements VirtualNetworkService, VirtualNetworkAdminService,
VirtualNetworkProviderRegistry {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String TENANT_NULL = "Tenant ID cannot be null";
private static final String NETWORK_NULL = "Network ID cannot be null";
private static final String DEVICE_NULL = "Device ID cannot be null";
private static final String LINK_POINT_NULL = "Link end-point cannot be null";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VirtualNetworkStore store;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
private final InternalVirtualIntentListener intentListener =
new InternalVirtualIntentListener();
private VirtualNetworkStoreDelegate delegate = this::post;
private ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
// TODO: figure out how to coordinate "implementation" of a virtual network in a cluster
/**
* Only used for Junit test methods outside of this package.
*
* @param store virtual network store
*/
public void setStore(VirtualNetworkStore store) {
this.store = store;
}
/**
* Only used for Junit test methods outside of this package.
*
* @param intentService intent service
*/
public void setIntentService(IntentService intentService) {
this.intentService = intentService;
}
@Activate
public void activate() {
store.setDelegate(delegate);
eventDispatcher.addSink(VirtualNetworkEvent.class, listenerRegistry);
intentService.addListener(intentListener);
log.info("Started");
}
@Deactivate
public void deactivate() {
store.unsetDelegate(delegate);
eventDispatcher.removeSink(VirtualNetworkEvent.class);
intentService.removeListener(intentListener);
log.info("Stopped");
}
@Override
public void registerTenantId(TenantId tenantId) {
checkNotNull(tenantId, TENANT_NULL);
store.addTenantId(tenantId);
}
@Override
public void unregisterTenantId(TenantId tenantId) {
checkNotNull(tenantId, TENANT_NULL);
store.removeTenantId(tenantId);
}
@Override
public Set<TenantId> getTenantIds() {
return store.getTenantIds();
}
@Override
public VirtualNetwork createVirtualNetwork(TenantId tenantId) {
checkNotNull(tenantId, TENANT_NULL);
return store.addNetwork(tenantId);
}
@Override
public void removeVirtualNetwork(NetworkId networkId) {
checkNotNull(networkId, NETWORK_NULL);
store.removeNetwork(networkId);
}
@Override
public VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(deviceId, DEVICE_NULL);
return store.addDevice(networkId, deviceId);
}
@Override
public void removeVirtualDevice(NetworkId networkId, DeviceId deviceId) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(deviceId, DEVICE_NULL);
store.removeDevice(networkId, deviceId);
}
@Override
public VirtualHost createVirtualHost(NetworkId networkId, HostId hostId,
MacAddress mac, VlanId vlan,
HostLocation location, Set<IpAddress> ips) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(hostId, DEVICE_NULL);
return store.addHost(networkId, hostId, mac, vlan, location, ips);
}
@Override
public void removeVirtualHost(NetworkId networkId, HostId hostId) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(hostId, DEVICE_NULL);
store.removeHost(networkId, hostId);
}
@Override
public VirtualLink createVirtualLink(NetworkId networkId,
ConnectPoint src, ConnectPoint dst) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(src, LINK_POINT_NULL);
checkNotNull(dst, LINK_POINT_NULL);
ConnectPoint physicalSrc = mapVirtualToPhysicalPort(networkId, src);
checkNotNull(physicalSrc, LINK_POINT_NULL);
ConnectPoint physicalDst = mapVirtualToPhysicalPort(networkId, dst);
checkNotNull(physicalDst, LINK_POINT_NULL);
VirtualNetworkProvider provider = getProvider(DefaultVirtualLink.PID);
Link.State state = Link.State.INACTIVE;
if (provider != null) {
boolean traversable = provider.isTraversable(physicalSrc, physicalDst);
state = traversable ? Link.State.ACTIVE : Link.State.INACTIVE;
}
return store.addLink(networkId, src, dst, state, null);
}
/**
* Maps the virtual connect point to a physical connect point.
*
* @param networkId network identifier
* @param virtualCp virtual connect point
* @return physical connect point
*/
private ConnectPoint mapVirtualToPhysicalPort(NetworkId networkId,
ConnectPoint virtualCp) {
Set<VirtualPort> ports = store.getPorts(networkId, virtualCp.deviceId());
for (VirtualPort port : ports) {
if (port.number().equals(virtualCp.port())) {
return new ConnectPoint(port.realizedBy().deviceId(),
port.realizedBy().port());
}
}
return null;
}
/**
* Maps the physical connect point to a virtual connect point.
*
* @param networkId network identifier
* @param physicalCp physical connect point
* @return virtual connect point
*/
private ConnectPoint mapPhysicalToVirtualToPort(NetworkId networkId,
ConnectPoint physicalCp) {
Set<VirtualPort> ports = store.getPorts(networkId, null);
for (VirtualPort port : ports) {
if (port.realizedBy().deviceId().equals(physicalCp.elementId()) &&
port.realizedBy().port().equals(physicalCp.port())) {
return new ConnectPoint(port.element().id(), port.number());
}
}
return null;
}
@Override
public void removeVirtualLink(NetworkId networkId, ConnectPoint src,
ConnectPoint dst) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(src, LINK_POINT_NULL);
checkNotNull(dst, LINK_POINT_NULL);
store.removeLink(networkId, src, dst);
}
@Override
public VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId,
PortNumber portNumber, ConnectPoint realizedBy) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(deviceId, DEVICE_NULL);
checkNotNull(portNumber, "Port description cannot be null");
return store.addPort(networkId, deviceId, portNumber, realizedBy);
}
@Override
public void bindVirtualPort(NetworkId networkId, DeviceId deviceId,
PortNumber portNumber, ConnectPoint realizedBy) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(deviceId, DEVICE_NULL);
checkNotNull(portNumber, "Port description cannot be null");
checkNotNull(realizedBy, "Physical port description cannot be null");
store.bindPort(networkId, deviceId, portNumber, realizedBy);
}
@Override
public void removeVirtualPort(NetworkId networkId, DeviceId deviceId,
PortNumber portNumber) {
checkNotNull(networkId, NETWORK_NULL);
checkNotNull(deviceId, DEVICE_NULL);
checkNotNull(portNumber, "Port number cannot be null");
store.removePort(networkId, deviceId, portNumber);
}
@Override
public ServiceDirectory getServiceDirectory() {
return serviceDirectory;
}
@Override
public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) {
checkNotNull(tenantId, TENANT_NULL);
return store.getNetworks(tenantId);
}
/**
* Returns the virtual network matching the network identifier.
*
* @param networkId network identifier
* @return virtual network
*/
private VirtualNetwork getVirtualNetwork(NetworkId networkId) {
checkNotNull(networkId, NETWORK_NULL);
return store.getNetwork(networkId);
}
@Override
public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) {
checkNotNull(networkId, NETWORK_NULL);
return store.getDevices(networkId);
}
@Override
public Set<VirtualHost> getVirtualHosts(NetworkId networkId) {
checkNotNull(networkId, NETWORK_NULL);
return store.getHosts(networkId);
}
@Override
public Set<VirtualLink> getVirtualLinks(NetworkId networkId) {
checkNotNull(networkId, NETWORK_NULL);
return store.getLinks(networkId);
}
@Override
public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) {
checkNotNull(networkId, NETWORK_NULL);
return store.getPorts(networkId, deviceId);
}
private final Map<ServiceKey, VnetService> networkServices = Maps.newConcurrentMap();
@Override
public <T> T get(NetworkId networkId, Class<T> serviceClass) {
checkNotNull(networkId, NETWORK_NULL);
ServiceKey serviceKey = networkServiceKey(networkId, serviceClass);
VnetService service = lookup(serviceKey);
if (service == null) {
service = create(serviceKey);
}
return (T) service;
}
/**
* Returns the Vnet service matching the service key.
*
* @param serviceKey service key
* @return vnet service
*/
private VnetService lookup(ServiceKey serviceKey) {
return networkServices.get(serviceKey);
}
/**
* Creates a new service key using the specified network identifier and service class.
*
* @param networkId network identifier
* @param serviceClass service class
* @param <T> type of service
* @return service key
*/
private <T> ServiceKey networkServiceKey(NetworkId networkId, Class<T> serviceClass) {
return new ServiceKey(networkId, serviceClass);
}
/**
* Create a new vnet service instance.
*
* @param serviceKey service key
* @return vnet service
*/
private VnetService create(ServiceKey serviceKey) {
VirtualNetwork network = getVirtualNetwork(serviceKey.networkId());
checkNotNull(network, NETWORK_NULL);
VnetService service;
if (serviceKey.serviceClass.equals(DeviceService.class)) {
service = new VirtualNetworkDeviceManager(this, network.id());
} else if (serviceKey.serviceClass.equals(LinkService.class)) {
service = new VirtualNetworkLinkManager(this, network.id());
} else if (serviceKey.serviceClass.equals(TopologyService.class)) {
service = new VirtualNetworkTopologyManager(this, network.id());
} else if (serviceKey.serviceClass.equals(IntentService.class)) {
service = new VirtualNetworkIntentManager(this, network.id());
} else if (serviceKey.serviceClass.equals(HostService.class)) {
service = new VirtualNetworkHostManager(this, network.id());
} else if (serviceKey.serviceClass.equals(PathService.class)) {
service = new VirtualNetworkPathManager(this, network.id());
} else if (serviceKey.serviceClass.equals(FlowRuleService.class)) {
service = new VirtualNetworkFlowRuleManager(this, network.id());
} else {
return null;
}
networkServices.put(serviceKey, service);
return service;
}
/**
* Service key class.
*/
private class ServiceKey {
final NetworkId networkId;
final Class serviceClass;
/**
* Constructor for service key.
*
* @param networkId network identifier
* @param serviceClass service class
*/
public ServiceKey(NetworkId networkId, Class serviceClass) {
checkNotNull(networkId, NETWORK_NULL);
this.networkId = networkId;
this.serviceClass = serviceClass;
}
/**
* Returns the network identifier.
*
* @return network identifier
*/
public NetworkId networkId() {
return networkId;
}
/**
* Returns the service class.
*
* @return service class
*/
public Class serviceClass() {
return serviceClass;
}
}
/**
* Internal intent event listener.
*/
private class InternalVirtualIntentListener implements IntentListener {
@Override
public void event(IntentEvent event) {
// Ignore intent events that are not relevant.
if (!isRelevant(event)) {
return;
}
VirtualNetworkIntent intent = (VirtualNetworkIntent) event.subject();
switch (event.type()) {
case INSTALL_REQ:
store.addOrUpdateIntent(intent, IntentState.INSTALL_REQ);
break;
case INSTALLED:
store.addOrUpdateIntent(intent, IntentState.INSTALLED);
break;
case WITHDRAW_REQ:
store.addOrUpdateIntent(intent, IntentState.WITHDRAW_REQ);
break;
case WITHDRAWN:
store.addOrUpdateIntent(intent, IntentState.WITHDRAWN);
break;
case FAILED:
store.addOrUpdateIntent(intent, IntentState.FAILED);
break;
case CORRUPT:
store.addOrUpdateIntent(intent, IntentState.CORRUPT);
break;
case PURGED:
store.removeIntent(intent.key());
default:
break;
}
}
@Override
public boolean isRelevant(IntentEvent event) {
if (event.subject() instanceof VirtualNetworkIntent) {
return true;
}
return false;
}
}
@Override
protected VirtualNetworkProviderService
createProviderService(VirtualNetworkProvider provider) {
return new InternalVirtualNetworkProviderService(provider);
}
/**
* Service issued to registered virtual network providers so that they
* can interact with the core.
*/
private class InternalVirtualNetworkProviderService
extends AbstractProviderService<VirtualNetworkProvider>
implements VirtualNetworkProviderService {
/**
* Constructor.
* @param provider virtual network provider
*/
InternalVirtualNetworkProviderService(VirtualNetworkProvider provider) {
super(provider);
}
@Override
public void topologyChanged(Set<Set<ConnectPoint>> clusters) {
Set<TenantId> tenantIds = getTenantIds();
tenantIds.forEach(tenantId -> {
Set<VirtualNetwork> virtualNetworks = getVirtualNetworks(tenantId);
virtualNetworks.forEach(virtualNetwork -> {
Set<VirtualLink> virtualLinks = getVirtualLinks(virtualNetwork.id());
virtualLinks.forEach(virtualLink -> {
if (isVirtualLinkInCluster(virtualNetwork.id(),
virtualLink, clusters)) {
store.updateLink(virtualLink, virtualLink.tunnelId(),
Link.State.ACTIVE);
} else {
store.updateLink(virtualLink, virtualLink.tunnelId(),
Link.State.INACTIVE);
}
});
});
});
}
/**
* Determines if the virtual link (both source and destination connect point)
* is in a cluster.
*
* @param networkId virtual network identifier
* @param virtualLink virtual link
* @param clusters topology clusters
* @return true if the virtual link is in a cluster.
*/
private boolean isVirtualLinkInCluster(NetworkId networkId, VirtualLink virtualLink,
Set<Set<ConnectPoint>> clusters) {
ConnectPoint srcPhysicalCp =
mapVirtualToPhysicalPort(networkId, virtualLink.src());
ConnectPoint dstPhysicalCp =
mapVirtualToPhysicalPort(networkId, virtualLink.dst());
final boolean[] foundSrc = {false};
final boolean[] foundDst = {false};
clusters.forEach(connectPoints -> {
connectPoints.forEach(connectPoint -> {
if (connectPoint.equals(srcPhysicalCp)) {
foundSrc[0] = true;
} else if (connectPoint.equals(dstPhysicalCp)) {
foundDst[0] = true;
}
});
if (foundSrc[0] && foundDst[0]) {
return;
}
});
return foundSrc[0] && foundDst[0];
}
@Override
public void tunnelUp(NetworkId networkId, ConnectPoint src,
ConnectPoint dst, TunnelId tunnelId) {
ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
log.error("Src or dst virtual connection point was not found.");
}
VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
if (virtualLink != null) {
store.updateLink(virtualLink, tunnelId, Link.State.ACTIVE);
}
}
@Override
public void tunnelDown(NetworkId networkId, ConnectPoint src,
ConnectPoint dst, TunnelId tunnelId) {
ConnectPoint srcVirtualCp = mapPhysicalToVirtualToPort(networkId, src);
ConnectPoint dstVirtualCp = mapPhysicalToVirtualToPort(networkId, dst);
if ((srcVirtualCp == null) || (dstVirtualCp == null)) {
log.error("Src or dst virtual connection point was not found.");
}
VirtualLink virtualLink = store.getLink(networkId, srcVirtualCp, dstVirtualCp);
if (virtualLink != null) {
store.updateLink(virtualLink, tunnelId, Link.State.INACTIVE);
}
}
}
}