blob: 2222b50c82aaad80e89b01cd47fbeb53b96a685d [file] [log] [blame]
/*
* Copyright 2016-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.net.intent.impl.compiler;
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.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.tunnel.TunnelId;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
import org.onosproject.incubator.net.virtual.VirtualPort;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompilationException;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
/**
* An intent compiler for {@link org.onosproject.incubator.net.virtual.VirtualNetworkIntent}.
*/
@Component(immediate = true)
public class VirtualNetworkIntentCompiler
extends ConnectivityIntentCompiler<VirtualNetworkIntent> {
private final Logger log = getLogger(getClass());
private static final String NETWORK_ID = "networkId=";
protected static final String KEY_FORMAT = "{" + NETWORK_ID + "%s, src=%s, dst=%s}";
protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VirtualNetworkService manager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VirtualNetworkStore store;
@Activate
public void activate() {
intentManager.registerCompiler(VirtualNetworkIntent.class, this);
}
@Deactivate
public void deactivate() {
intentManager.unregisterCompiler(VirtualNetworkIntent.class);
}
@Override
public List<Intent> compile(VirtualNetworkIntent intent, List<Intent> installable) {
log.debug("Compiling intent: " + intent);
List<Intent> intents = new ArrayList<>();
Optional<Path> path = getPaths(intent).stream()
.findFirst();
if (path != null && path.isPresent()) {
List<Link> links = path.get().links();
// First create an intent between the intent ingress CP and the first link source CP,
// only if the two CPs are not the same.
Link firstLink = links.get(0);
if (!intent.ingressPoint().equals(firstLink.src())) {
intents.add(createPtPtIntent(intent, intent.ingressPoint(), firstLink.src()));
}
// Next create an intent between the intent egress CP and the last link destination CP,
// only if the two CPs are not the same.
Link lastLink = links.get(links.size() - 1);
if (!intent.egressPoint().equals(lastLink.dst())) {
intents.add(createPtPtIntent(intent, lastLink.dst(), intent.egressPoint()));
}
// Now loop through all of the virtual links in the path and create an intent.
// An intent is also created connecting two virtual links.
final int[] index = {0};
links.forEach(link -> {
intents.add(createPtPtIntent(intent, link.src(), link.dst()));
if (index[0] > 0) {
Link previousLink = links.get(index[0] - 1);
intents.add(createPtPtIntent(intent, previousLink.dst(), link.src()));
}
index[0]++;
});
} else {
throw new IntentCompilationException("Unable to find a path for intent " + intent);
}
return intents;
}
/**
* Returns the paths for the virtual network intent.
*
* @param intent virtual network intent
* @return set of paths
*/
private Set<Path> getPaths(VirtualNetworkIntent intent) {
TopologyService topologyService = manager.get(intent.networkId(), TopologyService.class);
if (topologyService == null) {
throw new IntentCompilationException("topologyService is null");
}
return topologyService.getPaths(topologyService.currentTopology(),
intent.ingressPoint().deviceId(), intent.egressPoint().deviceId());
}
/**
* Encodes the key using the network identifier, application identifier, source and destination
* connect points.
*
* @param networkId virtual network identifier
* @param applicationId application identifier
* @param src source connect point
* @param dst destination connect point
* @return encoded key
*/
private static Key encodeKey(NetworkId networkId, ApplicationId applicationId, ConnectPoint src, ConnectPoint dst) {
String key = String.format(KEY_FORMAT, networkId, src, dst);
return Key.of(key, applicationId);
}
/**
* Creates a point-to-point intent using the virtual network intent between the source and destination
* connect point.
*
* @param intent virtual network intent
* @param src source connect point
* @param dst destination connect point
* @return point to point intent
*/
private Intent createPtPtIntent(VirtualNetworkIntent intent, ConnectPoint src, ConnectPoint dst) {
ConnectPoint ingressPoint = mapVirtualToPhysicalPort(intent.networkId(), src);
ConnectPoint egressPoint = mapVirtualToPhysicalPort(intent.networkId(), dst);
Key intentKey = encodeKey(intent.networkId(), intent.appId(), ingressPoint, egressPoint);
// TODO Currently there can only be one intent between the ingress and egress across
// all virtual networks. We may want to support multiple intents between the same src/dst pairs.
PointToPointIntent physicalIntent = PointToPointIntent.builder()
.key(intentKey)
.appId(intent.appId())
.ingressPoint(ingressPoint)
.egressPoint(egressPoint)
.constraints(intent.constraints())
.selector(intent.selector())
.treatment(intent.treatment())
.resourceGroup(intent.resourceGroup())
.build();
log.debug("Submitting physical intent: " + physicalIntent);
intentService.submit(physicalIntent);
// Store the physical intent against this virtual intent.
store.addTunnelId(intent, TunnelId.valueOf(physicalIntent.key().toString()));
return physicalIntent;
}
/**
* Maps the virtual connect point to a physical connect point.
*
* @param networkId virtual network identifier
* @param virtualCp virtual connect point
* @return physical connect point
*/
private ConnectPoint mapVirtualToPhysicalPort(NetworkId networkId, ConnectPoint virtualCp) {
Set<VirtualPort> ports = manager.getVirtualPorts(networkId, virtualCp.deviceId());
for (VirtualPort port : ports) {
if (port.element().id().equals(virtualCp.elementId()) &&
port.number().equals(virtualCp.port())) {
return new ConnectPoint(port.realizedBy().deviceId(), port.realizedBy().port());
}
}
return null;
}
}