Brian Stanke
Committed by Gerrit Code Review

ONOS-2184 - Initial implementation of Virtual network Intent service.

Change-Id: I03103b4eca797cd32480fbd0e3b4cf0385b50ef2
Showing 24 changed files with 1543 additions and 7 deletions
......@@ -2,6 +2,7 @@ COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:org.apache.karaf.shell.console',
'//incubator/api:onos-incubator-api',
'//incubator/net:onos-incubator-net',
'//utils/rest:onlab-rest',
'//core/common:onos-core-common',
]
......
/*
* 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.cli.net.vnet;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.net.ConnectivityIntentCommand;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import java.util.List;
/**
* Installs virtual network intents.
*/
@Command(scope = "onos", name = "add-vnet-intent",
description = "Installs virtual network connectivity intent")
public class VirtualNetworkIntentCreateCommand extends ConnectivityIntentCommand {
@Argument(index = 0, name = "networkId", description = "Network ID",
required = true, multiValued = false)
Long networkId = null;
@Argument(index = 1, name = "ingressDevice",
description = "Ingress Device/Port Description",
required = true, multiValued = false)
String ingressDeviceString = null;
@Argument(index = 2, name = "egressDevice",
description = "Egress Device/Port Description",
required = true, multiValued = false)
String egressDeviceString = null;
@Override
protected void execute() {
VirtualNetworkService service = get(VirtualNetworkService.class);
IntentService virtualNetworkIntentService = service.get(NetworkId.networkId(networkId), IntentService.class);
ConnectPoint ingress = ConnectPoint.deviceConnectPoint(ingressDeviceString);
ConnectPoint egress = ConnectPoint.deviceConnectPoint(egressDeviceString);
TrafficSelector selector = buildTrafficSelector();
TrafficTreatment treatment = buildTrafficTreatment();
List<Constraint> constraints = buildConstraints();
Intent intent = VirtualNetworkIntent.builder()
.networkId(NetworkId.networkId(networkId))
.appId(appId())
.key(key())
.selector(selector)
.treatment(treatment)
.ingressPoint(ingress)
.egressPoint(egress)
.constraints(constraints)
.priority(priority())
.build();
virtualNetworkIntentService.submit(intent);
print("Virtual intent submitted:\n%s", intent.toString());
}
}
/*
* 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.cli.net.vnet;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkService;
import org.onosproject.net.intent.Intent;
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.intent.Key;
import java.math.BigInteger;
import java.util.EnumSet;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.net.intent.IntentState.FAILED;
import static org.onosproject.net.intent.IntentState.WITHDRAWN;
/**
* Removes a virtual network intent.
*/
@Command(scope = "onos", name = "remove-vnet-intent",
description = "Removes the virtual network intent")
public class VirtualNetworkIntentRemoveCommand extends AbstractShellCommand {
@Argument(index = 0, name = "networkId", description = "Network ID",
required = true, multiValued = false)
Long networkId = null;
@Argument(index = 1, name = "app",
description = "Application ID",
required = false, multiValued = false)
String applicationIdString = null;
@Argument(index = 2, name = "key",
description = "Intent Key",
required = false, multiValued = false)
String keyString = null;
@Option(name = "-p", aliases = "--purge",
description = "Purge the intent from the store after removal",
required = false, multiValued = false)
private boolean purgeAfterRemove = false;
@Option(name = "-s", aliases = "--sync",
description = "Waits for the removal before returning",
required = false, multiValued = false)
private boolean sync = false;
private static final EnumSet<IntentState> CAN_PURGE = EnumSet.of(WITHDRAWN, FAILED);
@Override
protected void execute() {
VirtualNetworkService service = get(VirtualNetworkService.class);
IntentService intentService = service.get(NetworkId.networkId(networkId), IntentService.class);
CoreService coreService = get(CoreService.class);
if (purgeAfterRemove || sync) {
print("Using \"sync\" to remove/purge intents - this may take a while...");
print("Check \"summary\" to see remove/purge progress.");
}
ApplicationId appId = appId();
if (!isNullOrEmpty(applicationIdString)) {
appId = coreService.getAppId(applicationIdString);
if (appId == null) {
print("Cannot find application Id %s", applicationIdString);
return;
}
}
if (isNullOrEmpty(keyString)) {
for (Intent intent : intentService.getIntents()) {
if (intent.appId().equals(appId)) {
removeIntent(intentService, intent);
}
}
} else {
final Key key;
if (keyString.startsWith("0x")) {
// The intent uses a LongKey
keyString = keyString.replaceFirst("0x", "");
key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
} else {
// The intent uses a StringKey
key = Key.of(keyString, appId);
}
Intent intent = intentService.getIntent(key);
if (intent != null) {
removeIntent(intentService, intent);
} else {
print("Intent not found!");
}
}
}
/**
* Removes the intent using the specified intentService.
*
* @param intentService intent service
* @param intent intent
*/
private void removeIntent(IntentService intentService, Intent intent) {
IntentListener listener = null;
Key key = intent.key();
final CountDownLatch withdrawLatch, purgeLatch;
if (purgeAfterRemove || sync) {
// set up latch and listener to track uninstall progress
withdrawLatch = new CountDownLatch(1);
purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
listener = (IntentEvent event) -> {
if (Objects.equals(event.subject().key(), key)) {
if (event.type() == IntentEvent.Type.WITHDRAWN ||
event.type() == IntentEvent.Type.FAILED) {
withdrawLatch.countDown();
} else if (purgeAfterRemove &&
event.type() == IntentEvent.Type.PURGED) {
purgeLatch.countDown();
}
}
};
intentService.addListener(listener);
} else {
purgeLatch = null;
withdrawLatch = null;
}
// request the withdraw
intentService.withdraw(intent);
if (purgeAfterRemove || sync) {
try { // wait for withdraw event
withdrawLatch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
print("Timed out waiting for intent {} withdraw", key);
}
if (purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
intentService.purge(intent);
if (sync) { // wait for purge event
/* TODO
Technically, the event comes before map.remove() is called.
If we depend on sync and purge working together, we will
need to address this.
*/
try {
purgeLatch.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
print("Timed out waiting for intent {} purge", key);
}
}
}
}
if (listener != null) {
// clean up the listener
intentService.removeListener(listener);
}
}
}
......@@ -683,6 +683,12 @@
<command>
<action class="org.onosproject.cli.net.vnet.VirtualHostRemoveCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.vnet.VirtualNetworkIntentCreateCommand"/>
</command>
<command>
<action class="org.onosproject.cli.net.vnet.VirtualNetworkIntentRemoveCommand"/>
</command>
</command-bundle>
<bean id="reviewAppNameCompleter" class="org.onosproject.cli.security.ReviewApplicationNameCompleter"/>
......
......@@ -155,7 +155,6 @@ public class FakeIntentManager implements TestableIntentService {
@Override
public void withdraw(Intent intent) {
intents.remove(intent.key());
executeWithdraw(intent);
}
......@@ -167,6 +166,7 @@ public class FakeIntentManager implements TestableIntentService {
intents.remove(intent.key());
installables.remove(intent.key());
intentStates.remove(intent.key());
dispatch(new IntentEvent(IntentEvent.Type.PURGED, intent));
}
}
......
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//incubator/api:onos-incubator-api',
'//utils/rest:onlab-rest',
'//incubator/net:onos-incubator-net',
'//incubator/store:onos-incubator-store',
]
TEST_DEPS = [
'//lib:TEST_REST',
'//lib:TEST_ADAPTERS',
'//core/common:onos-core-common',
'//core/store/dist:onos-core-dist',
......
......@@ -93,6 +93,14 @@
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-api</artifactId>
</dependency>
......@@ -110,6 +118,17 @@
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-store</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-net</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
......
/*
* 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.EncapsulationType;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
import org.onosproject.net.intent.constraint.EncapsulationConstraint;
import org.onosproject.net.intent.impl.IntentCompilationException;
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()) {
path.get().links().forEach(link -> {
Intent physicalIntent = createPtPtIntent(intent, link);
intents.add(physicalIntent);
// store the virtual intent to physical intent tunnelId mapping
store.addTunnelId(intent, TunnelId.valueOf(physicalIntent.key().toString()));
});
} 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 identifer, 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 from the virtual network intent and virtual link.
*
* @param intent virtual network intent
* @param link virtual link
* @return point to point intent
*/
private Intent createPtPtIntent(VirtualNetworkIntent intent, Link link) {
ConnectPoint ingressPoint = mapVirtualToPhysicalPort(intent.networkId(), link.src());
ConnectPoint egressPoint = mapVirtualToPhysicalPort(intent.networkId(), link.dst());
Key intentKey = encodeKey(intent.networkId(), intent.appId(), ingressPoint, egressPoint);
List<Constraint> constraints = new ArrayList<>();
constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
// 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(constraints)
.build();
log.debug("Submitting physical intent: " + physicalIntent);
intentService.submit(physicalIntent);
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().element().id(), port.realizedBy().number());
}
}
return null;
}
}
/*
* 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.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.osgi.TestServiceDirectory;
import org.onlab.rest.BaseResource;
import org.onosproject.TestApplicationId;
import org.onosproject.common.event.impl.TestEventDispatcher;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.IdGenerator;
import org.onosproject.incubator.net.virtual.TenantId;
import org.onosproject.incubator.net.virtual.VirtualDevice;
import org.onosproject.incubator.net.virtual.VirtualLink;
import org.onosproject.incubator.net.virtual.VirtualNetwork;
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.impl.VirtualNetworkManager;
import org.onosproject.incubator.store.virtual.impl.DistributedVirtualNetworkStore;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultPort;
import org.onosproject.net.EncapsulationType;
import org.onosproject.net.Link;
import org.onosproject.net.NetTestTools;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.TestDeviceParams;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.FakeIntentManager;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MockIdGenerator;
import org.onosproject.net.intent.TestableIntentService;
import org.onosproject.net.intent.constraint.EncapsulationConstraint;
import org.onosproject.store.service.TestStorageService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.assertEquals;
import static org.onlab.junit.TestUtils.TestUtilsException;
import static org.onlab.junit.TestUtils.setField;
/**
* Junit tests for virtual network intent compiler.
*/
public class VirtualNetworkIntentCompilerTest extends TestDeviceParams {
private CoreService coreService;
private TestableIntentService intentService = new FakeIntentManager();
private IntentExtensionService intentExtensionService;
private final IdGenerator idGenerator = new MockIdGenerator();
private VirtualNetworkIntentCompiler compiler;
private VirtualNetworkManager manager;
private DistributedVirtualNetworkStore virtualNetworkManagerStore;
private ServiceDirectory testDirectory;
private final String tenantIdValue1 = "TENANT_ID1";
private static final ApplicationId APP_ID =
new TestApplicationId("test");
private ConnectPoint cp1;
private ConnectPoint cp2;
private ConnectPoint cp3;
private ConnectPoint cp4;
private ConnectPoint cp5;
private ConnectPoint cp6;
private VirtualLink link1;
private VirtualLink link2;
private VirtualLink link3;
private VirtualLink link4;
private VirtualLink link5;
private VirtualLink link6;
@Before
public void setUp() throws TestUtilsException {
virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
coreService = new TestCoreService();
Intent.unbindIdGenerator(idGenerator);
Intent.bindIdGenerator(idGenerator);
virtualNetworkManagerStore.setCoreService(coreService);
setField(coreService, "coreService", new TestCoreService());
setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
virtualNetworkManagerStore.activate();
manager = new VirtualNetworkManager();
manager.setStore(virtualNetworkManagerStore);
manager.setIntentService(intentService);
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
// Register a compiler and an installer both setup for success.
intentExtensionService = intentService;
testDirectory = new TestServiceDirectory()
.add(VirtualNetworkService.class, manager)
.add(VirtualNetworkStore.class, virtualNetworkManagerStore)
.add(IntentService.class, intentService);
BaseResource.setServiceDirectory(testDirectory);
compiler = new VirtualNetworkIntentCompiler();
compiler.manager = manager;
compiler.intentService = intentService;
compiler.store = virtualNetworkManagerStore;
compiler.intentManager = intentExtensionService;
compiler.serviceDirectory = testDirectory;
}
@After
public void tearDown() {
Intent.unbindIdGenerator(idGenerator);
manager.deactivate();
}
/**
* Method to create the virtual network for further testing.
*
* @return virtual network
*/
private VirtualNetwork setupVirtualNetworkTopology() {
manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
VirtualDevice virtualDevice1 =
manager.createVirtualDevice(virtualNetwork.id(), DID1);
VirtualDevice virtualDevice2 =
manager.createVirtualDevice(virtualNetwork.id(), DID2);
VirtualDevice virtualDevice3 =
manager.createVirtualDevice(virtualNetwork.id(), DID3);
VirtualDevice virtualDevice4 =
manager.createVirtualDevice(virtualNetwork.id(), DID4);
Port port1 = new DefaultPort(virtualDevice1, PortNumber.portNumber(1), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(), port1.number(), port1);
cp1 = new ConnectPoint(virtualDevice1.id(), port1.number());
Port port2 = new DefaultPort(virtualDevice1, PortNumber.portNumber(2), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice1.id(), port2.number(), port2);
cp2 = new ConnectPoint(virtualDevice1.id(), port2.number());
Port port3 = new DefaultPort(virtualDevice2, PortNumber.portNumber(3), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(), port3.number(), port3);
cp3 = new ConnectPoint(virtualDevice2.id(), port3.number());
Port port4 = new DefaultPort(virtualDevice2, PortNumber.portNumber(4), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice2.id(), port4.number(), port4);
cp4 = new ConnectPoint(virtualDevice2.id(), port4.number());
Port port5 = new DefaultPort(virtualDevice3, PortNumber.portNumber(5), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(), port5.number(), port5);
cp5 = new ConnectPoint(virtualDevice3.id(), port5.number());
Port port6 = new DefaultPort(virtualDevice3, PortNumber.portNumber(6), true);
manager.createVirtualPort(virtualNetwork.id(), virtualDevice3.id(), port6.number(), port6);
cp6 = new ConnectPoint(virtualDevice3.id(), port6.number());
link1 = manager.createVirtualLink(virtualNetwork.id(), cp1, cp3);
virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
link2 = manager.createVirtualLink(virtualNetwork.id(), cp3, cp1);
virtualNetworkManagerStore.updateLink(link2, link2.tunnelId(), Link.State.ACTIVE);
link3 = manager.createVirtualLink(virtualNetwork.id(), cp4, cp5);
virtualNetworkManagerStore.updateLink(link3, link3.tunnelId(), Link.State.ACTIVE);
link4 = manager.createVirtualLink(virtualNetwork.id(), cp5, cp4);
virtualNetworkManagerStore.updateLink(link4, link4.tunnelId(), Link.State.ACTIVE);
return virtualNetwork;
}
@Test
public void testCompiler() {
compiler.activate();
VirtualNetwork virtualNetwork = setupVirtualNetworkTopology();
Key intentKey = Key.of("test", APP_ID);
List<Constraint> constraints = new ArrayList<>();
constraints.add(new EncapsulationConstraint(EncapsulationType.VLAN));
VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
.networkId(virtualNetwork.id())
.key(intentKey)
.appId(APP_ID)
.ingressPoint(cp1)
.egressPoint(cp5)
.constraints(constraints)
.build();
List<Intent> compiled = compiler.compile(virtualIntent, Collections.emptyList());
assertEquals("The virtual intents size is not as expected.", 2, compiled.size());
compiler.deactivate();
}
/**
* Core service test class.
*/
private class TestCoreService extends CoreServiceAdapter {
@Override
public IdGenerator getIdGenerator(String topic) {
return new IdGenerator() {
private AtomicLong counter = new AtomicLong(0);
@Override
public long getNewId() {
return counter.getAndIncrement();
}
};
}
}
}
......@@ -9,6 +9,7 @@ COMPILE_DEPS = [
'//lib:netty-handler',
'//lib:netty-transport-native-epoll',
'//lib:commons-math3',
'//incubator/api:onos-incubator-api',
]
TEST_DEPS = [
......
......@@ -27,6 +27,8 @@ import org.onlab.util.KryoNamespace;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentEvent;
......@@ -97,6 +99,8 @@ public class GossipIntentStore
KryoNamespace.Builder intentSerializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
.register(IntentData.class)
.register(VirtualNetworkIntent.class)
.register(NetworkId.class)
.register(MultiValuedTimestamp.class);
currentMap = storageService.<Key, IntentData>eventuallyConsistentMapBuilder()
......
/*
* 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.incubator.net.virtual;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.ConnectivityIntent;
import org.onosproject.net.intent.Constraint;
import org.onosproject.net.intent.Key;
import java.util.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of VirtualNetworkIntent connectivity.
*/
@Beta
public final class VirtualNetworkIntent extends ConnectivityIntent {
private final NetworkId networkId;
private final ConnectPoint ingressPoint;
private final ConnectPoint egressPoint;
private static final String NETWORK_ID_NULL = "Network ID cannot be null";
/**
* Returns a new point to point intent builder. The application id,
* ingress point and egress point are required fields. If they are
* not set by calls to the appropriate methods, an exception will
* be thrown.
*
* @return point to point builder
*/
public static VirtualNetworkIntent.Builder builder() {
return new VirtualNetworkIntent.Builder();
}
/**
* Builder of a point to point intent.
*/
public static final class Builder extends ConnectivityIntent.Builder {
NetworkId networkId;
ConnectPoint ingressPoint;
ConnectPoint egressPoint;
/**
* Builder constructor.
*/
private Builder() {
// Hide constructor
}
@Override
public VirtualNetworkIntent.Builder appId(ApplicationId appId) {
return (VirtualNetworkIntent.Builder) super.appId(appId);
}
@Override
public VirtualNetworkIntent.Builder key(Key key) {
return (VirtualNetworkIntent.Builder) super.key(key);
}
@Override
public VirtualNetworkIntent.Builder selector(TrafficSelector selector) {
return (VirtualNetworkIntent.Builder) super.selector(selector);
}
@Override
public VirtualNetworkIntent.Builder treatment(TrafficTreatment treatment) {
return (VirtualNetworkIntent.Builder) super.treatment(treatment);
}
@Override
public VirtualNetworkIntent.Builder constraints(List<Constraint> constraints) {
return (VirtualNetworkIntent.Builder) super.constraints(constraints);
}
@Override
public VirtualNetworkIntent.Builder priority(int priority) {
return (VirtualNetworkIntent.Builder) super.priority(priority);
}
/**
* Sets the virtual network of the virtual network intent.
*
* @param networkId virtual network identifier
* @return this builder
*/
public VirtualNetworkIntent.Builder networkId(NetworkId networkId) {
this.networkId = networkId;
return this;
}
/**
* Sets the ingress point of the virtual network intent that will be built.
*
* @param ingressPoint ingress connect point
* @return this builder
*/
public VirtualNetworkIntent.Builder ingressPoint(ConnectPoint ingressPoint) {
this.ingressPoint = ingressPoint;
return this;
}
/**
* Sets the egress point of the virtual network intent that will be built.
*
* @param egressPoint egress connect point
* @return this builder
*/
public VirtualNetworkIntent.Builder egressPoint(ConnectPoint egressPoint) {
this.egressPoint = egressPoint;
return this;
}
/**
* Builds a virtual network intent from the accumulated parameters.
*
* @return virtual network intent
*/
public VirtualNetworkIntent build() {
return new VirtualNetworkIntent(
networkId,
appId,
key,
selector,
treatment,
ingressPoint,
egressPoint,
constraints,
priority
);
}
}
/**
* Creates a new point-to-point intent with the supplied ingress/egress
* ports and constraints.
*
* @param networkId virtual network identifier
* @param appId application identifier
* @param key key of the intent
* @param selector traffic selector
* @param treatment treatment
* @param ingressPoint ingress port
* @param egressPoint egress port
* @param constraints optional list of constraints
* @param priority priority to use for flows generated by this intent
* @throws NullPointerException if {@code ingressPoint} or
* {@code egressPoints} or {@code appId} is null.
*/
private VirtualNetworkIntent(NetworkId networkId,
ApplicationId appId,
Key key,
TrafficSelector selector,
TrafficTreatment treatment,
ConnectPoint ingressPoint,
ConnectPoint egressPoint,
List<Constraint> constraints,
int priority) {
super(appId, key, Collections.emptyList(), selector, treatment, constraints,
priority);
checkNotNull(networkId, NETWORK_ID_NULL);
checkArgument(!ingressPoint.equals(egressPoint),
"ingress and egress should be different (ingress: %s, egress: %s)", ingressPoint, egressPoint);
this.networkId = networkId;
this.ingressPoint = checkNotNull(ingressPoint);
this.egressPoint = checkNotNull(egressPoint);
}
/**
* Constructor for serializer.
*/
protected VirtualNetworkIntent() {
super();
this.networkId = null;
this.ingressPoint = null;
this.egressPoint = null;
}
/**
* Returns the virtual network identifier.
*
* @return network identifier
*/
public NetworkId networkId() {
return networkId;
}
/**
* Returns the port on which the ingress traffic should be connected to
* the egress.
*
* @return ingress port
*/
public ConnectPoint ingressPoint() {
return ingressPoint;
}
/**
* Returns the port on which the traffic should egress.
*
* @return egress port
*/
public ConnectPoint egressPoint() {
return egressPoint;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("networkId", networkId)
.add("id", id())
.add("key", key())
.add("appId", appId())
.add("priority", priority())
.add("resources", resources())
.add("selector", selector())
.add("treatment", treatment())
.add("ingress", ingressPoint)
.add("egress", egressPoint)
.add("constraints", constraints())
.toString();
}
}
......@@ -26,6 +26,10 @@ import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.onosproject.store.Store;
import java.util.Set;
......@@ -223,4 +227,73 @@ public interface VirtualNetworkStore
*/
Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId);
/**
* Add or update the intent to the store.
*
* @param intent virtual intent
* @param state intent state
*/
void addOrUpdateIntent(Intent intent, IntentState state);
/**
* Remove the virtual intent from the store.
*
* @param intentKey intent key
* @return intent data
*/
IntentData removeIntent(Key intentKey);
/**
* Adds the intent to tunnel identifier mapping to the store.
*
* @param intent intent
* @param tunnelId tunnel identifier
*/
void addTunnelId(Intent intent, TunnelId tunnelId);
/**
* Return the set of tunnel identifiers store against the intent.
*
* @param intent intent
* @return set of tunnel identifiers
*/
Set<TunnelId> getTunnelIds(Intent intent);
/**
* Removes the intent to tunnel identifier mapping from the store.
*
* @param intent intent
* @param tunnelId tunnel identifier
*/
void removeTunnelId(Intent intent, TunnelId tunnelId);
/**
* Return all intents.
*
* @return set of intents
*/
Set<Intent> getIntents();
/**
* Return the intent for the specified intent key.
*
* @param key intent key
* @return intent
*/
Intent getIntent(Key key);
/**
* Return the set of intent data.
*
* @return set of intent data
*/
Set<IntentData> getIntentData();
/**
* Return the intent data matching the intent key.
*
* @param key intent key
* @return intent data
*/
IntentData getIntentData(Key key);
}
......
......@@ -2,13 +2,15 @@ COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//core/common:onos-core-common',
'//incubator/api:onos-incubator-api',
'//incubator/store:onos-incubator-store',
'//utils/rest:onlab-rest',
]
TEST_DEPS = [
'//lib:TEST_REST',
'//lib:TEST_ADAPTERS',
'//core/api:onos-api-tests',
'//core/common:onos-core-common-tests',
'//incubator/store:onos-incubator-store',
'//core/store/serializers:onos-core-serializers',
'//lib:concurrent-trees',
]
......
......@@ -95,6 +95,14 @@
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
/*
* 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.incubator.net.virtual.impl;
import com.google.common.collect.Iterators;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.event.AbstractListenerManager;
import org.onosproject.incubator.net.virtual.VirtualNetwork;
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.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentEvent;
import org.onosproject.net.intent.IntentListener;
import org.onosproject.net.intent.IntentPartitionService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static com.google.common.base.Preconditions.*;
/**
* intent service implementation built on the virtual network service.
*/
public class VirtualNetworkIntentService extends AbstractListenerManager<IntentEvent, IntentListener>
implements IntentService, VnetService {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String NETWORK_NULL = "Network cannot be null";
private static final String NETWORK_ID_NULL = "Network ID cannot be null";
private static final String DEVICE_NULL = "Device cannot be null";
private static final String INTENT_NULL = "Intent cannot be null";
private static final String KEY_NULL = "Key cannot be null";
private static final String APP_ID_NULL = "Intent app identifier cannot be null";
private static final String INTENT_KEY_NULL = "Intent key cannot be null";
private static final String CP_NULL = "Connect Point cannot be null";
protected IntentService intentService;
protected VirtualNetworkStore store;
protected IntentPartitionService partitionService;
private final VirtualNetwork network;
private final VirtualNetworkService manager;
/**
* Creates a new VirtualNetworkIntentService object.
*
* @param virtualNetworkManager virtual network manager service
* @param network virtual network
* @param serviceDirectory service directory
*/
public VirtualNetworkIntentService(VirtualNetworkService virtualNetworkManager, VirtualNetwork network,
ServiceDirectory serviceDirectory) {
checkNotNull(network, NETWORK_NULL);
this.network = network;
this.manager = virtualNetworkManager;
this.store = serviceDirectory.get(VirtualNetworkStore.class);
this.intentService = serviceDirectory.get(IntentService.class);
this.partitionService = serviceDirectory.get(IntentPartitionService.class);
}
@Override
public void submit(Intent intent) {
checkNotNull(intent, INTENT_NULL);
checkState(intent instanceof VirtualNetworkIntent, "Only VirtualNetworkIntent is supported.");
checkArgument(validateIntent((VirtualNetworkIntent) intent), "Invalid Intent");
intentService.submit(intent);
}
/**
* Returns true if the virtual network intent is valid.
*
* @param intent virtual network intent
* @return true if intent is valid
*/
private boolean validateIntent(VirtualNetworkIntent intent) {
checkNotNull(intent, INTENT_NULL);
checkNotNull(intent.networkId(), NETWORK_ID_NULL);
checkNotNull(intent.appId(), APP_ID_NULL);
checkNotNull(intent.key(), INTENT_KEY_NULL);
ConnectPoint ingressPoint = intent.ingressPoint();
ConnectPoint egressPoint = intent.egressPoint();
return (validateConnectPoint(ingressPoint) && validateConnectPoint(egressPoint));
}
/**
* Returns true if the connect point is valid.
*
* @param connectPoint connect point
* @return true if connect point is valid
*/
private boolean validateConnectPoint(ConnectPoint connectPoint) {
checkNotNull(connectPoint, CP_NULL);
Port port = getPort(connectPoint.deviceId(), connectPoint.port());
return port == null ? false : true;
}
/**
* Returns the virtual port for the given device identifier and port number.
*
* @param deviceId virtual device identifier
* @param portNumber virtual port number
* @return virtual port
*/
private Port getPort(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId, DEVICE_NULL);
Optional<VirtualPort> foundPort = manager.getVirtualPorts(this.network.id(), deviceId)
.stream()
.filter(port -> port.number().equals(portNumber))
.findFirst();
if (foundPort.isPresent()) {
return foundPort.get();
}
return null;
}
@Override
public void withdraw(Intent intent) {
checkNotNull(intent, INTENT_NULL);
// Withdraws the physical intents created due to the virtual intents.
store.getTunnelIds(intent)
.forEach(tunnelId -> {
Key intentKey = Key.of(tunnelId.id(), intent.appId());
Intent physicalIntent = intentService.getIntent(intentKey);
checkNotNull(physicalIntent, INTENT_NULL);
// Withdraw the physical intent(s)
log.info("Withdrawing pt-pt intent: " + physicalIntent);
intentService.withdraw(physicalIntent);
});
// Now withdraw the virtual intent
log.info("Withdrawing virtual intent: " + intent);
intentService.withdraw(intent);
}
@Override
public void purge(Intent intent) {
checkNotNull(intent, INTENT_NULL);
// Purges the physical intents created for each tunnelId.
store.getTunnelIds(intent)
.forEach(tunnelId -> {
Key intentKey = Key.of(tunnelId.id(), intent.appId());
Intent physicalIntent = intentService.getIntent(intentKey);
checkNotNull(physicalIntent, INTENT_NULL);
// Purge the physical intent(s)
intentService.purge(physicalIntent);
store.removeTunnelId(intent, tunnelId);
});
// Now purge the virtual intent
intentService.purge(intent);
}
@Override
public Intent getIntent(Key key) {
checkNotNull(key, KEY_NULL);
return store.getIntent(key);
}
@Override
public Iterable<Intent> getIntents() {
return store.getIntents();
}
@Override
public Iterable<IntentData> getIntentData() {
return store.getIntentData();
}
@Override
public long getIntentCount() {
return Iterators.size(getIntents().iterator());
}
@Override
public IntentState getIntentState(Key intentKey) {
checkNotNull(intentKey, KEY_NULL);
return store.getIntentData(intentKey).state();
}
@Override
public List<Intent> getInstallableIntents(Key intentKey) {
List<Intent> intents = new ArrayList<>();
getIntentData().forEach(intentData -> {
if (intentData.intent().key().equals(intentKey)) {
intents.addAll(intentData.installables());
}
});
return intents;
}
@Override
public boolean isLocal(Key intentKey) {
checkNotNull(intentKey, INTENT_KEY_NULL);
Intent intent = getIntent(intentKey);
checkNotNull(intent, INTENT_NULL);
return partitionService.isMine(intentKey);
}
@Override
public Iterable<Intent> getPending() {
return null;
}
@Override
public VirtualNetwork network() {
return network;
}
}
......@@ -38,6 +38,8 @@ import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.TestDeviceParams;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.intent.FakeIntentManager;
import org.onosproject.net.intent.TestableIntentService;
import org.onosproject.store.service.TestStorageService;
import java.util.Iterator;
......@@ -54,6 +56,7 @@ public class VirtualNetworkDeviceServiceTest extends TestDeviceParams {
private VirtualNetworkManager manager;
private DistributedVirtualNetworkStore virtualNetworkManagerStore;
private CoreService coreService;
private TestableIntentService intentService = new FakeIntentManager();
@Before
public void setUp() throws Exception {
......@@ -67,6 +70,7 @@ public class VirtualNetworkDeviceServiceTest extends TestDeviceParams {
manager = new VirtualNetworkManager();
manager.store = virtualNetworkManagerStore;
manager.intentService = intentService;
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
}
......
......@@ -35,6 +35,8 @@ import org.onosproject.net.Link;
import org.onosproject.net.NetTestTools;
import org.onosproject.net.PortNumber;
import org.onosproject.net.TestDeviceParams;
import org.onosproject.net.intent.FakeIntentManager;
import org.onosproject.net.intent.TestableIntentService;
import org.onosproject.net.link.LinkService;
import org.onosproject.store.service.TestStorageService;
......@@ -54,6 +56,7 @@ public class VirtualNetworkLinkServiceTest extends TestDeviceParams {
private VirtualNetworkManager manager;
private DistributedVirtualNetworkStore virtualNetworkManagerStore;
private CoreService coreService;
private TestableIntentService intentService = new FakeIntentManager();
@Before
public void setUp() throws Exception {
......@@ -67,6 +70,7 @@ public class VirtualNetworkLinkServiceTest extends TestDeviceParams {
manager = new VirtualNetworkManager();
manager.store = virtualNetworkManagerStore;
manager.intentService = intentService;
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
}
......@@ -103,12 +107,12 @@ public class VirtualNetworkLinkServiceTest extends TestDeviceParams {
Iterator<Link> it = linkService.getLinks().iterator();
assertEquals("The link set size did not match.", 2, Iterators.size(it));
// test the getActiveLinks() method where no links are ACTIVE
// test the getActiveLinks() method where all links are ACTIVE
Iterator<Link> it2 = linkService.getActiveLinks().iterator();
assertEquals("The link set size did not match.", 0, Iterators.size(it2));
assertEquals("The link set size did not match.", 2, Iterators.size(it2));
// test the getActiveLinks() method where one link is ACTIVE
virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.ACTIVE);
virtualNetworkManagerStore.updateLink(link1, link1.tunnelId(), Link.State.INACTIVE);
Iterator<Link> it3 = linkService.getActiveLinks().iterator();
assertEquals("The link set size did not match.", 1, Iterators.size(it3));
......
......@@ -27,6 +27,7 @@ import org.onosproject.core.CoreService;
import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.IdGenerator;
import org.onosproject.event.Event;
import org.onosproject.incubator.net.tunnel.TunnelId;
import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork;
import org.onosproject.incubator.net.virtual.NetworkId;
import org.onosproject.incubator.net.virtual.TenantId;
......@@ -35,6 +36,7 @@ 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.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.VirtualPort;
......@@ -46,6 +48,12 @@ import org.onosproject.net.NetTestTools;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.TestDeviceParams;
import org.onosproject.net.intent.FakeIntentManager;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MockIdGenerator;
import org.onosproject.net.intent.TestableIntentService;
import org.onosproject.store.service.TestStorageService;
import java.util.Collection;
......@@ -54,6 +62,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.*;
import static org.onosproject.net.NetTestTools.APP_ID;
/**
* Junit tests for VirtualNetworkManager.
......@@ -67,10 +76,13 @@ public class VirtualNetworkManagerTest extends TestDeviceParams {
private DistributedVirtualNetworkStore virtualNetworkManagerStore;
private CoreService coreService;
private TestListener listener = new TestListener();
private TestableIntentService intentService = new FakeIntentManager();
private IdGenerator idGenerator = new MockIdGenerator();
@Before
public void setUp() throws Exception {
virtualNetworkManagerStore = new DistributedVirtualNetworkStore();
Intent.bindIdGenerator(idGenerator);
coreService = new TestCoreService();
virtualNetworkManagerStore.setCoreService(coreService);
......@@ -81,6 +93,7 @@ public class VirtualNetworkManagerTest extends TestDeviceParams {
manager = new VirtualNetworkManager();
manager.store = virtualNetworkManagerStore;
manager.addListener(listener);
manager.intentService = intentService;
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
virtualNetworkManagerService = manager;
......@@ -92,6 +105,7 @@ public class VirtualNetworkManagerTest extends TestDeviceParams {
manager.removeListener(listener);
manager.deactivate();
NetTestTools.injectEventDispatcher(manager, null);
Intent.unbindIdGenerator(idGenerator);
}
/**
......@@ -377,6 +391,95 @@ public class VirtualNetworkManagerTest extends TestDeviceParams {
}
/**
* Tests the addOrUpdateIntent() method in the store with a null intent.
*/
@Test(expected = NullPointerException.class)
public void testAddOrUpdateNullIntent() {
manager.store.addOrUpdateIntent(null, null);
}
/**
* Tests the removeIntent() method in the store with a null intent key.
*/
@Test(expected = NullPointerException.class)
public void testRemoveNullIntentKey() {
manager.store.removeIntent(null);
}
/**
* Tests the addOrUpdateIntent(), getIntents(), getIntent(), removeIntent() methods with the store.
*/
@Test
public void testAddOrUpdateIntent() {
manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
ConnectPoint cp1 = new ConnectPoint(DID1, P1);
ConnectPoint cp2 = new ConnectPoint(DID2, P1);
VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
.networkId(virtualNetwork.id())
.key(Key.of("Test", APP_ID))
.appId(APP_ID)
.ingressPoint(cp1)
.egressPoint(cp2)
.build();
// Add the intent to the store.
manager.store.addOrUpdateIntent(virtualIntent, IntentState.INSTALL_REQ);
assertEquals("The intent size should match.", 1, manager.store.getIntents().size());
assertNotNull("The intent should not be null.", manager.store.getIntent(virtualIntent.key()));
// remove the intent from the store.
manager.store.removeIntent(virtualIntent.key());
assertTrue("The intents should be empty.", manager.store.getIntents().isEmpty());
assertNull("The intent should be null.", manager.store.getIntent(virtualIntent.key()));
}
/**
* Tests the addTunnelId() method in the store with a null intent.
*/
@Test(expected = NullPointerException.class)
public void testAddTunnelIdNullIntent() {
manager.store.addTunnelId(null, null);
}
/**
* Tests the removeTunnelId() method in the store with a null intent.
*/
@Test(expected = NullPointerException.class)
public void testRemoveTunnelIdNullIntent() {
manager.store.removeTunnelId(null, null);
}
/**
* Tests the addTunnelId, getTunnelIds(), removeTunnelId() methods with the store.
*/
@Test
public void testAddTunnelId() {
manager.registerTenantId(TenantId.tenantId(tenantIdValue1));
VirtualNetwork virtualNetwork = manager.createVirtualNetwork(TenantId.tenantId(tenantIdValue1));
ConnectPoint cp1 = new ConnectPoint(DID1, P1);
ConnectPoint cp2 = new ConnectPoint(DID2, P1);
VirtualNetworkIntent virtualIntent = VirtualNetworkIntent.builder()
.networkId(virtualNetwork.id())
.key(Key.of("Test", APP_ID))
.appId(APP_ID)
.ingressPoint(cp1)
.egressPoint(cp2)
.build();
TunnelId tunnelId = TunnelId.valueOf("virtual tunnel");
// Add the intent to tunnelID mapping to the store.
manager.store.addTunnelId(virtualIntent, tunnelId);
assertEquals("The tunnels size should match.", 1, manager.store.getTunnelIds(virtualIntent).size());
// Remove the intent to tunnelID mapping from the store.
manager.store.removeTunnelId(virtualIntent, tunnelId);
assertTrue("The tunnels should be empty.", manager.store.getTunnelIds(virtualIntent).isEmpty());
}
/**
* Method to validate that the actual versus expected virtual network events were
* received correctly.
*
......
......@@ -38,6 +38,8 @@ import org.onosproject.net.NetTestTools;
import org.onosproject.net.Path;
import org.onosproject.net.PortNumber;
import org.onosproject.net.TestDeviceParams;
import org.onosproject.net.intent.FakeIntentManager;
import org.onosproject.net.intent.TestableIntentService;
import org.onosproject.net.topology.LinkWeight;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyCluster;
......@@ -62,6 +64,7 @@ public class VirtualNetworkTopologyServiceTest extends TestDeviceParams {
private VirtualNetworkManager manager;
private DistributedVirtualNetworkStore virtualNetworkManagerStore;
private CoreService coreService;
private TestableIntentService intentService = new FakeIntentManager();
@Before
public void setUp() throws Exception {
......@@ -75,6 +78,7 @@ public class VirtualNetworkTopologyServiceTest extends TestDeviceParams {
manager = new VirtualNetworkManager();
manager.store = virtualNetworkManagerStore;
manager.intentService = intentService;
NetTestTools.injectEventDispatcher(manager, new TestEventDispatcher());
manager.activate();
}
......
......@@ -42,6 +42,7 @@ 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.VirtualNetworkEvent;
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.VirtualNetworkStoreDelegate;
......@@ -54,6 +55,10 @@ import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentData;
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
......@@ -64,6 +69,7 @@ import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.SetEvent;
import org.onosproject.store.service.SetEventListener;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
import org.slf4j.Logger;
import java.util.HashSet;
......@@ -134,6 +140,14 @@ public class DistributedVirtualNetworkStore
private ConsistentMap<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetConsistentMap;
private Map<NetworkId, Set<VirtualPort>> networkIdVirtualPortSetMap;
// Track intent key to intent data
private ConsistentMap<Key, IntentData> intentKeyIntentDataConsistentMap;
private Map<Key, IntentData> intentKeyIntentDataMap;
// Track intent ID to TunnelIds
private ConsistentMap<Key, Set<TunnelId>> intentKeyTunnelIdSetConsistentMap;
private Map<Key, Set<TunnelId>> intentKeyTunnelIdSetMap;
private static final Serializer SERIALIZER = Serializer
.using(new KryoNamespace.Builder().register(KryoNamespaces.API)
.register(TenantId.class)
......@@ -150,8 +164,11 @@ public class DistributedVirtualNetworkStore
.register(DefaultVirtualPort.class)
.register(Device.class)
.register(TunnelId.class)
.register(IntentData.class)
.register(VirtualNetworkIntent.class)
.register(WallClockTimestamp.class)
.nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
.build("VirtualNetworkStore"));
.build());
/**
* Distributed network store service activate method.
......@@ -220,11 +237,25 @@ public class DistributedVirtualNetworkStore
networkIdVirtualPortSetConsistentMap = storageService.<NetworkId, Set<VirtualPort>>consistentMapBuilder()
.withSerializer(SERIALIZER)
.withName("onos-networkId-virtualportss")
.withName("onos-networkId-virtualports")
.withRelaxedReadConsistency()
.build();
networkIdVirtualPortSetMap = networkIdVirtualPortSetConsistentMap.asJavaMap();
intentKeyTunnelIdSetConsistentMap = storageService.<Key, Set<TunnelId>>consistentMapBuilder()
.withSerializer(SERIALIZER)
.withName("onos-intentKey-tunnelIds")
.withRelaxedReadConsistency()
.build();
intentKeyTunnelIdSetMap = intentKeyTunnelIdSetConsistentMap.asJavaMap();
intentKeyIntentDataConsistentMap = storageService.<Key, IntentData>consistentMapBuilder()
.withSerializer(SERIALIZER)
.withName("onos-intentKey-intentData")
.withRelaxedReadConsistency()
.build();
intentKeyIntentDataMap = intentKeyIntentDataConsistentMap.asJavaMap();
log.info("Started");
}
......@@ -607,6 +638,85 @@ public class DistributedVirtualNetworkStore
return ImmutableSet.copyOf(portSet);
}
@Override
public synchronized void addOrUpdateIntent(Intent intent, IntentState state) {
checkNotNull(intent, "Intent cannot be null");
IntentData intentData = removeIntent(intent.key());
if (intentData == null) {
intentData = new IntentData(intent, state, new WallClockTimestamp(System.currentTimeMillis()));
} else {
intentData = new IntentData(intent, state, intentData.version());
}
intentKeyIntentDataMap.put(intent.key(), intentData);
}
@Override
public IntentData removeIntent(Key intentKey) {
checkNotNull(intentKey, "Intent key cannot be null");
return intentKeyIntentDataMap.remove(intentKey);
}
@Override
public void addTunnelId(Intent intent, TunnelId tunnelId) {
// Add the tunnelId to the intent key set map
Set<TunnelId> tunnelIdSet = intentKeyTunnelIdSetMap.remove(intent.key());
if (tunnelIdSet == null) {
tunnelIdSet = new HashSet<>();
}
tunnelIdSet.add(tunnelId);
intentKeyTunnelIdSetMap.put(intent.key(), tunnelIdSet);
}
@Override
public Set<TunnelId> getTunnelIds(Intent intent) {
Set<TunnelId> tunnelIdSet = intentKeyTunnelIdSetMap.get(intent.key());
return tunnelIdSet == null ? new HashSet<TunnelId>() : ImmutableSet.copyOf(tunnelIdSet);
}
@Override
public void removeTunnelId(Intent intent, TunnelId tunnelId) {
Set<TunnelId> tunnelIdSet = new HashSet<>();
intentKeyTunnelIdSetMap.get(intent.key()).forEach(tunnelId1 -> {
if (tunnelId1.equals(tunnelId)) {
tunnelIdSet.add(tunnelId);
}
});
if (!tunnelIdSet.isEmpty()) {
intentKeyTunnelIdSetMap.compute(intent.key(), (key, existingTunnelIds) -> {
if (existingTunnelIds == null || existingTunnelIds.isEmpty()) {
return new HashSet<>();
} else {
return new HashSet<>(Sets.difference(existingTunnelIds, tunnelIdSet));
}
});
}
}
@Override
public Set<Intent> getIntents() {
Set<Intent> intents = new HashSet<>();
intentKeyIntentDataMap.values().forEach(intentData -> intents.add(intentData.intent()));
return ImmutableSet.copyOf(intents);
}
@Override
public Intent getIntent(Key key) {
IntentData intentData = intentKeyIntentDataMap.get(key);
return intentData == null ? null : intentData.intent();
}
@Override
public Set<IntentData> getIntentData() {
return ImmutableSet.copyOf(intentKeyIntentDataMap.values());
}
@Override
public IntentData getIntentData(Key key) {
IntentData intentData = intentKeyIntentDataMap.get(key);
return intentData == null ? null : new IntentData(intentData);
}
/**
* Listener class to map listener set events to the virtual network events.
*/
......
......@@ -142,6 +142,12 @@
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-incubator-net</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-common</artifactId>
<version>${project.version}</version>
</dependency>
......