Thomas Vachuska

Consolidating null providers and making them fully configurable and integrated w…

…ith the ConfigProvider to allow arbitrary topologies.

Change-Id: I899e27a9771af4013a3ce6da7f683a4927ffb438
Showing 40 changed files with 2452 additions and 91 deletions
/*
* Copyright 2015 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;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import java.util.List;
import java.util.SortedSet;
/**
* Abstraction of a completer with preset choices.
*/
public abstract class AbstractChoicesCompleter extends AbstractCompleter {
protected abstract List<String> choices();
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
StringsCompleter delegate = new StringsCompleter();
SortedSet<String> strings = delegate.getStrings();
choices().forEach(strings::add);
return delegate.complete(buffer, cursor, candidates);
}
}
......@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.cli.net;
package org.onosproject.cli;
import org.apache.felix.service.command.CommandSession;
import org.apache.karaf.shell.console.CommandSessionHolder;
......
/*
* Copyright 2015 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;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* Start/stop command completer.
*/
public class StartStopCompleter extends AbstractChoicesCompleter {
public static final String START = "start";
public static final String STOP = "stop";
@Override
public List<String> choices() {
return ImmutableList.of(START, STOP);
}
}
/*
* Copyright 2015 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;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* Up/down command completer.
*/
public class UpDownCompleter extends AbstractChoicesCompleter {
public static final String UP = "up";
public static final String DOWN = "down";
@Override
public List<String> choices() {
return ImmutableList.of(UP, DOWN);
}
}
......@@ -15,30 +15,20 @@
*/
package org.onosproject.cli.app;
import org.apache.karaf.shell.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import com.google.common.collect.ImmutableList;
import org.onosproject.cli.AbstractChoicesCompleter;
import java.util.List;
import java.util.SortedSet;
import static org.onosproject.cli.app.ApplicationCommand.*;
/**
* Application name completer.
* Application command completer.
*/
public class ApplicationCommandCompleter implements Completer {
public class ApplicationCommandCompleter extends AbstractChoicesCompleter {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
SortedSet<String> strings = delegate.getStrings();
strings.add(INSTALL);
strings.add(UNINSTALL);
strings.add(ACTIVATE);
strings.add(DEACTIVATE);
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
public List<String> choices() {
return ImmutableList.of(INSTALL, UNINSTALL, ACTIVATE, DEACTIVATE);
}
}
......
......@@ -19,7 +19,7 @@ import org.apache.karaf.shell.console.completer.ArgumentCompleter;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.app.ApplicationService;
import org.onosproject.app.ApplicationState;
import org.onosproject.cli.net.AbstractCompleter;
import org.onosproject.cli.AbstractCompleter;
import org.onosproject.core.Application;
import java.util.Iterator;
......
......@@ -19,7 +19,7 @@ import org.apache.karaf.shell.console.completer.ArgumentCompleter;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cfg.ConfigProperty;
import org.onosproject.cli.net.AbstractCompleter;
import org.onosproject.cli.AbstractCompleter;
import java.util.List;
import java.util.Set;
......
/*
* Copyright 2015 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;
import org.apache.karaf.shell.console.completer.ArgumentCompleter;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.cli.AbstractCompleter;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.link.LinkService;
import java.util.List;
import java.util.SortedSet;
import static org.onosproject.cli.net.AddPointToPointIntentCommand.getDeviceId;
import static org.onosproject.cli.net.AddPointToPointIntentCommand.getPortNumber;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
/**
* Link end-point completer.
*/
public class LinkDstCompleter extends AbstractCompleter {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
// Fetch our service and feed it's offerings to the string completer
LinkService service = AbstractShellCommand.get(LinkService.class);
// Link source the previous argument.
ArgumentCompleter.ArgumentList list = getArgumentList();
String srcArg = list.getArguments()[list.getCursorArgumentIndex() - 1];
// Generate the device ID/port number identifiers
SortedSet<String> strings = delegate.getStrings();
try {
ConnectPoint src = new ConnectPoint(deviceId(getDeviceId(srcArg)),
portNumber(getPortNumber(srcArg)));
service.getEgressLinks(src)
.forEach(link -> strings.add(link.dst().elementId().toString() +
"/" + link.dst().port()));
} catch (NumberFormatException e) {
System.err.println("Invalid connect-point");
}
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
}
}
/*
* Copyright 2015 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;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.cli.AbstractCompleter;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.link.LinkService;
import java.util.List;
import java.util.SortedSet;
/**
* Link end-point completer.
*/
public class LinkSrcCompleter extends AbstractCompleter {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
// Fetch our service and feed it's offerings to the string completer
LinkService service = AbstractShellCommand.get(LinkService.class);
// Generate the device ID/port number identifiers
SortedSet<String> strings = delegate.getStrings();
service.getLinks()
.forEach(link -> strings.add(link.src().elementId().toString() +
"/" + link.src().port()));
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
}
}
......@@ -332,4 +332,7 @@
<bean id="ipProtocolCompleter" class="org.onosproject.cli.net.IpProtocolCompleter"/>
<bean id="driverNameCompleter" class="org.onosproject.cli.net.DriverNameCompleter"/>
<bean id="startStopCompleter" class="org.onosproject.cli.StartStopCompleter"/>
<bean id="upDownCompleter" class="org.onosproject.cli.UpDownCompleter"/>
</blueprint>
......
......@@ -20,7 +20,7 @@ import org.onosproject.net.DeviceId;
/**
* Service for administering the inventory of infrastructure devices.
*/
public interface DeviceAdminService {
public interface DeviceAdminService extends DeviceService {
/**
* Removes the device with the specified identifier.
......
......@@ -21,7 +21,7 @@ import org.onosproject.net.HostId;
/**
* Service for administering the inventory of end-station hosts.
*/
public interface HostAdminService {
public interface HostAdminService extends HostService {
/**
* Removes the end-station host with the specified identifier.
......
......@@ -21,7 +21,7 @@ import org.onosproject.net.DeviceId;
/**
* Service for administering the inventory of infrastructure links.
*/
public interface LinkAdminService {
public interface LinkAdminService extends LinkService {
/**
* Removes all infrastructure links leading to and from the
......
......@@ -705,7 +705,8 @@ public class DistributedGroupStore
remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
}
} else {
if (deviceAuditStatus.get(deviceId)) {
Boolean audited = deviceAuditStatus.get(deviceId);
if (audited != null && audited) {
log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
+ "status for device {}", deviceId);
deviceAuditStatus.put(deviceId, false);
......@@ -717,8 +718,8 @@ public class DistributedGroupStore
@Override
public boolean deviceInitialAuditStatus(DeviceId deviceId) {
synchronized (deviceAuditStatus) {
return (deviceAuditStatus.get(deviceId) != null)
? deviceAuditStatus.get(deviceId) : false;
Boolean audited = deviceAuditStatus.get(deviceId);
return audited != null && audited;
}
}
......
......@@ -79,7 +79,7 @@
<group>
<title>Null Providers</title>
<packages>
org.onosproject.provider.nil.*
org.onosproject.provider.nil:org.onosproject.provider.nil.*
</packages>
</group>
<group>
......
......@@ -131,13 +131,7 @@
<feature name="onos-null" version="@FEATURE-VERSION"
description="ONOS Null providers">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-null-provider-device/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-null-provider-link/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-null-provider-host/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-null-provider-packet/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-null-provider-flow/@ONOS-VERSION</bundle>
<bundle>mvn:org.onosproject/onos-null-provider/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-openflow" version="@FEATURE-VERSION"
......
......@@ -26,20 +26,25 @@
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-null-providers</artifactId>
<packaging>pom</packaging>
<artifactId>onos-null-provider</artifactId>
<packaging>bundle</packaging>
<description>ONOS null protocol adapters</description>
<modules>
<module>device</module>
<module>link</module>
<module>host</module>
<module>packet</module>
<module>flow</module>
</modules>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
......
/*
* Copyright 2015 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.provider.nil;
/**
* Linear topology with hosts on every device.
*/
public class CentipedeTopologySimulator extends LinearTopologySimulator {
/**
* Creates simulated hosts.
*/
protected void createHosts() {
deviceIds.forEach(id -> createHosts(id, infrastructurePorts));
}
}
/*
* Copyright 2015 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.provider.nil;
/**
* Topology simulator which operates on topology configured via the REST API
* config service.
*/
public class ConfiguredTopologySimulator extends TopologySimulator {
@Override
protected void createDevices() {
deviceService.getDevices()
.forEach(device -> deviceProviderService
.deviceConnected(device.id(), description(device)));
}
@Override
protected void createLinks() {
linkService.getLinks()
.forEach(link -> linkProviderService
.linkDetected(description(link)));
}
@Override
protected void createHosts() {
hostService.getHosts()
.forEach(host -> hostProviderService
.hostDetected(host.id(), description(host)));
}
}
/*
* Copyright 2015 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.provider.nil;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Linear topology simulator.
*/
public class LinearTopologySimulator extends TopologySimulator {
@Override
protected void processTopoShape(String shape) {
super.processTopoShape(shape);
deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
}
@Override
public void setUpTopology() {
checkArgument(deviceCount > 1, "There must be at least 2 devices");
prepareForDeviceEvents(deviceCount);
createDevices();
waitForDeviceEvents();
createLinks();
createHosts();
}
@Override
protected void createLinks() {
for (int i = 0, n = deviceCount - 1; i < n; i++) {
createLink(i, i + 1);
}
}
@Override
protected void createHosts() {
createHosts(deviceIds.get(0), infrastructurePorts);
createHosts(deviceIds.get(deviceCount - 1), infrastructurePorts);
}
}
/*
* Copyright 2015 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.provider.nil;
/**
* Full mesh topology with hosts at each device.
*/
public class MeshTopologySimulator extends TopologySimulator {
@Override
protected void processTopoShape(String shape) {
super.processTopoShape(shape);
// FIXME: implement this
}
@Override
public void setUpTopology() {
// FIXME: implement this
// checkArgument(FIXME, "There must be at least ...");
super.setUpTopology();
}
@Override
protected void createLinks() {
}
@Override
protected void createHosts() {
}
}
/*
* Copyright 2015 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.provider.nil;
import com.google.common.collect.Sets;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.util.Timer;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.CompletedBatchOperation;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleBatchEntry;
import org.onosproject.net.flow.FlowRuleBatchOperation;
import org.onosproject.net.flow.FlowRuleProvider;
import org.onosproject.net.flow.FlowRuleProviderService;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Null provider to accept any flow and report them.
*/
class NullFlowRuleProvider extends NullProviders.AbstractNullProvider
implements FlowRuleProvider {
private final Logger log = getLogger(getClass());
private ConcurrentMap<DeviceId, Set<FlowEntry>> flowTable = new ConcurrentHashMap<>();
private FlowRuleProviderService providerService;
private HashedWheelTimer timer = Timer.getTimer();
private Timeout timeout;
/**
* Starts the flow rule provider simulation.
*
* @param providerService flow rule provider service
*/
void start(FlowRuleProviderService providerService) {
this.providerService = providerService;
timeout = timer.newTimeout(new StatisticTask(), 5, TimeUnit.SECONDS);
}
/**
* Stops the flow rule provider simulation.
*/
void stop() {
timeout.cancel();
}
@Override
public void applyFlowRule(FlowRule... flowRules) {
// FIXME: invoke executeBatch
}
@Override
public void removeFlowRule(FlowRule... flowRules) {
// FIXME: invoke executeBatch
}
@Override
public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
throw new UnsupportedOperationException("Cannot remove by appId from null provider");
}
@Override
public void executeBatch(FlowRuleBatchOperation batch) {
// TODO: consider checking mastership
Set<FlowEntry> entries =
flowTable.getOrDefault(batch.deviceId(),
Sets.newConcurrentHashSet());
for (FlowRuleBatchEntry fbe : batch.getOperations()) {
switch (fbe.operator()) {
case ADD:
entries.add(new DefaultFlowEntry(fbe.target()));
break;
case REMOVE:
entries.remove(new DefaultFlowEntry(fbe.target()));
break;
case MODIFY:
FlowEntry entry = new DefaultFlowEntry(fbe.target());
entries.remove(entry);
entries.add(entry);
break;
default:
log.error("Unknown flow operation: {}", fbe);
}
}
flowTable.put(batch.deviceId(), entries);
CompletedBatchOperation op =
new CompletedBatchOperation(true, Collections.emptySet(),
batch.deviceId());
providerService.batchOperationCompleted(batch.id(), op);
}
// Periodically reports flow rule statistics.
private class StatisticTask implements TimerTask {
@Override
public void run(Timeout to) throws Exception {
for (DeviceId devId : flowTable.keySet()) {
Set<FlowEntry> entries =
flowTable.getOrDefault(devId, Collections.emptySet());
providerService.pushFlowMetrics(devId, entries);
}
timeout = timer.newTimeout(to.getTask(), 5, TimeUnit.SECONDS);
}
}
}
/*
* Copyright 2015 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.provider.nil;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.util.Timer;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.DefaultPacketContext;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketProvider;
import org.onosproject.net.packet.PacketProviderService;
import org.slf4j.Logger;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.collect.ImmutableList.copyOf;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.onosproject.net.MastershipRole.MASTER;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which generates simulated packets and acts as a sink for outbound
* packets. To be used for benchmarking only.
*/
class NullPacketProvider extends NullProviders.AbstractNullProvider
implements PacketProvider {
private static final int INITIAL_DELAY = 5;
private final Logger log = getLogger(getClass());
// Arbitrary host src/dst
private static final int SRC_HOST = 2;
private static final int DST_HOST = 5;
// Time between event firing, in milliseconds
private int delay;
// TODO: use host service to pick legitimate hosts connected to devices
private HostService hostService;
private PacketProviderService providerService;
private List<Device> devices;
private int currentDevice = 0;
private HashedWheelTimer timer = Timer.getTimer();
private Timeout timeout;
/**
* Starts the packet generation process.
*
* @param packetRate packets per second
* @param hostService host service
* @param deviceService device service
* @param providerService packet provider service
*/
void start(int packetRate, HostService hostService,
DeviceAdminService deviceService,
PacketProviderService providerService) {
this.hostService = hostService;
this.providerService = providerService;
this.devices = copyOf(deviceService.getDevices()).stream()
.filter(d -> deviceService.getRole(d.id()) == MASTER)
.collect(Collectors.toList());
adjustRate(packetRate);
timeout = timer.newTimeout(new PacketDriverTask(), INITIAL_DELAY, SECONDS);
}
/**
* Adjusts packet rate.
*
* @param packetRate new packet rate
*/
void adjustRate(int packetRate) {
delay = 1000 / packetRate;
log.info("Settings: packetRate={}, delay={}", packetRate, delay);
}
/**
* Stops the packet generation process.
*/
void stop() {
if (timeout != null) {
timeout.cancel();
}
}
@Override
public void emit(OutboundPacket packet) {
// We don't have a network to emit to. Keep a counter here, maybe?
}
/**
* Generates packet events at a given rate.
*/
private class PacketDriverTask implements TimerTask {
// Filler echo request
ICMP icmp;
Ethernet eth;
public PacketDriverTask() {
icmp = new ICMP();
icmp.setIcmpType((byte) 8).setIcmpCode((byte) 0).setChecksum((short) 0);
eth = new Ethernet();
eth.setEtherType(Ethernet.TYPE_IPV4);
eth.setPayload(icmp);
}
@Override
public void run(Timeout to) {
if (!devices.isEmpty()) {
sendEvent(devices.get(currentDevice));
currentDevice = (currentDevice + 1) % devices.size();
timeout = timer.newTimeout(to.getTask(), delay, TimeUnit.MILLISECONDS);
}
}
private void sendEvent(Device device) {
// Make it look like things came from ports attached to hosts
eth.setSourceMACAddress("00:00:10:00:00:0" + SRC_HOST)
.setDestinationMACAddress("00:00:10:00:00:0" + DST_HOST);
InboundPacket inPkt = new DefaultInboundPacket(
new ConnectPoint(device.id(), PortNumber.portNumber(SRC_HOST)),
eth, ByteBuffer.wrap(eth.serialize()));
providerService.processPacket(new NullPacketContext(inPkt, null));
}
}
// Minimal PacketContext to make core and applications happy.
private final class NullPacketContext extends DefaultPacketContext {
private NullPacketContext(InboundPacket inPkt, OutboundPacket outPkt) {
super(System.currentTimeMillis(), inPkt, outPkt, false);
}
@Override
public void send() {
// We don't send anything out.
}
}
}
/*
* Copyright 2015 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.provider.nil;
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.Modified;
import org.apache.felix.scr.annotations.Property;
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.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipAdminService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.flow.FlowRuleProviderRegistry;
import org.onosproject.net.flow.FlowRuleProviderService;
import org.onosproject.net.host.HostProvider;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.packet.PacketProviderRegistry;
import org.onosproject.net.packet.PacketProviderService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Dictionary;
import java.util.Properties;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onlab.util.Tools.delay;
import static org.onlab.util.Tools.get;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.MastershipRole.MASTER;
import static org.onosproject.net.MastershipRole.NONE;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider of a fake network environment, i.e. devices, links, hosts, etc.
* To be used for benchmarking only.
*/
@Component(immediate = true)
@Service(value = NullProviders.class)
public class NullProviders {
private static final Logger log = getLogger(NullProviders.class);
static final String SCHEME = "null";
static final String PROVIDER_ID = "org.onosproject.provider.nil";
private static final String FORMAT =
"Settings: enabled={}, topoShape={}, deviceCount={}, " +
"hostCount={}, packetRate={}, mutationRate={}";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipAdminService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceAdminService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceProviderRegistry deviceProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry hostProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkProviderRegistry linkProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleProviderRegistry flowRuleProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketProviderRegistry packetProviderRegistry;
private final NullDeviceProvider deviceProvider = new NullDeviceProvider();
private final NullLinkProvider linkProvider = new NullLinkProvider();
private final NullHostProvider hostProvider = new NullHostProvider();
private final NullFlowRuleProvider flowRuleProvider = new NullFlowRuleProvider();
private final NullPacketProvider packetProvider = new NullPacketProvider();
private final TopologyMutationDriver topologyMutationDriver = new TopologyMutationDriver();
private DeviceProviderService deviceProviderService;
private HostProviderService hostProviderService;
private LinkProviderService linkProviderService;
private FlowRuleProviderService flowRuleProviderService;
private PacketProviderService packetProviderService;
private TopologySimulator simulator;
@Property(name = "enabled", boolValue = false,
label = "Enables or disables the provider")
private boolean enabled = false;
private static final String DEFAULT_TOPO_SHAPE = "configured";
@Property(name = "topoShape", value = DEFAULT_TOPO_SHAPE,
label = "Topology shape: configured, linear, reroute, tree, spineleaf, mesh")
private String topoShape = DEFAULT_TOPO_SHAPE;
private static final int DEFAULT_DEVICE_COUNT = 10;
@Property(name = "deviceCount", intValue = DEFAULT_DEVICE_COUNT,
label = "Number of devices to generate")
private int deviceCount = DEFAULT_DEVICE_COUNT;
private static final int DEFAULT_HOST_COUNT = 5;
@Property(name = "hostCount", intValue = DEFAULT_HOST_COUNT,
label = "Number of host to generate per device")
private int hostCount = DEFAULT_HOST_COUNT;
private static final int DEFAULT_PACKET_RATE = 5;
@Property(name = "packetRate", intValue = DEFAULT_PACKET_RATE,
label = "Packet-in/s rate; 0 for no packets")
private int packetRate = DEFAULT_PACKET_RATE;
private static final double DEFAULT_MUTATION_RATE = 0;
@Property(name = "mutationRate", doubleValue = DEFAULT_MUTATION_RATE,
label = "Link event/s topology mutation rate; 0 for no mutations")
private double mutationRate = DEFAULT_MUTATION_RATE;
private static final String DEFAULT_MASTERSHIP = "random";
@Property(name = "mastership", value = DEFAULT_MASTERSHIP,
label = "Mastership given as 'random' or 'node1=dpid,dpid/node2=dpid,...'")
private String mastership = DEFAULT_MASTERSHIP;
@Activate
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
deviceProviderService = deviceProviderRegistry.register(deviceProvider);
hostProviderService = hostProviderRegistry.register(hostProvider);
linkProviderService = linkProviderRegistry.register(linkProvider);
flowRuleProviderService = flowRuleProviderRegistry.register(flowRuleProvider);
packetProviderService = packetProviderRegistry.register(packetProvider);
log.info("Started");
}
@Deactivate
public void deactivate(ComponentContext context) {
cfgService.unregisterProperties(getClass(), false);
tearDown();
deviceProviderRegistry.unregister(deviceProvider);
hostProviderRegistry.unregister(hostProvider);
linkProviderRegistry.unregister(linkProvider);
flowRuleProviderRegistry.unregister(flowRuleProvider);
packetProviderRegistry.unregister(packetProvider);
deviceProviderService = null;
hostProviderService = null;
linkProviderService = null;
flowRuleProviderService = null;
packetProviderService = null;
log.info("Stopped");
}
@Modified
public void modified(ComponentContext context) {
Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
boolean newEnabled;
int newDeviceCount, newHostCount, newPacketRate;
double newMutationRate;
String newTopoShape, newMastership;
try {
String s = get(properties, "enabled");
newEnabled = isNullOrEmpty(s) ? enabled : Boolean.parseBoolean(s.trim());
newTopoShape = get(properties, "topoShape");
newMastership = get(properties, "mastership");
s = get(properties, "deviceCount");
newDeviceCount = isNullOrEmpty(s) ? deviceCount : Integer.parseInt(s.trim());
s = get(properties, "hostCount");
newHostCount = isNullOrEmpty(s) ? hostCount : Integer.parseInt(s.trim());
s = get(properties, "packetRate");
newPacketRate = isNullOrEmpty(s) ? packetRate : Integer.parseInt(s.trim());
s = get(properties, "mutationRate");
newMutationRate = isNullOrEmpty(s) ? mutationRate : Double.parseDouble(s.trim());
} catch (NumberFormatException e) {
log.warn(e.getMessage());
newEnabled = enabled;
newTopoShape = topoShape;
newDeviceCount = deviceCount;
newHostCount = hostCount;
newPacketRate = packetRate;
newMutationRate = mutationRate;
newMastership = mastership;
}
// Any change in the following parameters implies hard restart
if (newEnabled != enabled || !newTopoShape.equals(topoShape) ||
newDeviceCount != deviceCount || newHostCount != hostCount) {
enabled = newEnabled;
topoShape = newTopoShape;
deviceCount = newDeviceCount;
hostCount = newHostCount;
packetRate = newPacketRate;
mutationRate = newMutationRate;
restartSimulation();
}
// Any change in the following parameters implies just a rate change
if (newPacketRate != packetRate || newMutationRate != mutationRate) {
packetRate = newPacketRate;
mutationRate = newMutationRate;
adjustRates();
}
// Any change in mastership implies just reassignments.
if (!newMastership.equals(mastership)) {
mastership = newMastership;
reassignMastership();
}
log.info(FORMAT, enabled, topoShape, deviceCount, hostCount,
packetRate, mutationRate);
}
/**
* Severs the link between the specified end-points in both directions.
*
* @param one link endpoint
* @param two link endpoint
*/
public void severLink(ConnectPoint one, ConnectPoint two) {
if (enabled) {
topologyMutationDriver.severLink(one, two);
}
}
/**
* Severs the link between the specified end-points in both directions.
*
* @param one link endpoint
* @param two link endpoint
*/
public void repairLink(ConnectPoint one, ConnectPoint two) {
if (enabled) {
topologyMutationDriver.repairLink(one, two);
}
}
// Resets simulation based on the current configuration parameters.
private void restartSimulation() {
tearDown();
if (enabled) {
setUp();
}
}
// Sets up the topology simulation and all providers.
private void setUp() {
simulator = selectSimulator(topoShape);
simulator.init(topoShape, deviceCount, hostCount,
new DefaultServiceDirectory(),
deviceProviderService, hostProviderService,
linkProviderService);
simulator.setUpTopology();
flowRuleProvider.start(flowRuleProviderService);
packetProvider.start(packetRate, hostService, deviceService,
packetProviderService);
topologyMutationDriver.start(mutationRate, linkService, deviceService,
linkProviderService);
}
// Selects the simulator based on the specified name.
private TopologySimulator selectSimulator(String topoShape) {
if (topoShape.matches("linear([,].*|$)")) {
return new LinearTopologySimulator();
} else if (topoShape.matches("centipede([,].*|$)")) {
return new CentipedeTopologySimulator();
} else if (topoShape.matches("reroute([,].*|$)")) {
return new RerouteTopologySimulator();
} else if (topoShape.matches("tree([,].*|$)")) {
return new TreeTopologySimulator();
} else if (topoShape.matches("spineleaf([,].*|$)")) {
return new SpineLeafTopologySimulator();
} else if (topoShape.matches("mesh([,].*|$)")) {
return new MeshTopologySimulator();
} else {
return new ConfiguredTopologySimulator();
}
}
// Shuts down the topology simulator and all providers.
private void tearDown() {
if (simulator != null) {
topologyMutationDriver.stop();
packetProvider.stop();
flowRuleProvider.stop();
delay(500);
rejectMastership();
simulator.tearDownTopology();
simulator = null;
}
}
// Changes packet and mutation rates.
private void adjustRates() {
packetProvider.adjustRate(packetRate);
topologyMutationDriver.adjustRate(mutationRate);
}
// Re-assigns mastership roles.
private void reassignMastership() {
if (mastership.equals(DEFAULT_MASTERSHIP)) {
mastershipService.balanceRoles();
} else {
NodeId localNode = clusterService.getLocalNode().id();
rejectMastership();
String[] nodeSpecs = mastership.split("/");
for (int i = 0; i < nodeSpecs.length; i++) {
String[] specs = nodeSpecs[i].split("=");
if (specs[0].equals(localNode.toString())) {
String[] ids = specs[1].split(",");
for (String id : ids) {
mastershipService.setRole(localNode, deviceId(id), MASTER);
}
break;
}
}
}
}
// Rejects mastership of all devices.
private void rejectMastership() {
NodeId localNode = clusterService.getLocalNode().id();
deviceService.getDevices()
.forEach(device -> mastershipService.setRole(localNode, device.id(),
NONE));
}
// Null provider base class.
abstract static class AbstractNullProvider extends AbstractProvider {
protected AbstractNullProvider() {
super(new ProviderId(SCHEME, PROVIDER_ID));
}
}
// Device provider facade.
private class NullDeviceProvider extends AbstractNullProvider implements DeviceProvider {
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
deviceProviderService.receivedRoleReply(deviceId, newRole, newRole);
}
@Override
public boolean isReachable(DeviceId deviceId) {
return topoShape.equals("configured") || deviceService.isAvailable(deviceId);
}
@Override
public void triggerProbe(DeviceId deviceId) {
}
}
// Host provider facade.
private class NullHostProvider extends AbstractNullProvider implements HostProvider {
@Override
public void triggerProbe(Host host) {
}
}
// Host provider facade.
private class NullLinkProvider extends AbstractNullProvider implements LinkProvider {
}
}
/*
* Copyright 2015 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.provider.nil;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Re-routable linear topology simulator with an alternate path in the middle.
*/
public class RerouteTopologySimulator extends LinearTopologySimulator {
@Override
protected void processTopoShape(String shape) {
super.processTopoShape(shape);
infrastructurePorts = 3;
deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
}
@Override
public void setUpTopology() {
checkArgument(deviceCount > 2, "There must be at least 3 devices");
super.setUpTopology();
}
@Override
protected void createLinks() {
for (int i = 0, n = deviceCount - 2; i < n; i++) {
createLink(i, i + 1);
}
int middle = (deviceCount - 1) / 2;
int alternate = deviceCount - 1;
createLink(middle - 1, alternate);
createLink(middle, alternate);
}
@Override
protected void createHosts() {
createHosts(deviceIds.get(0), infrastructurePorts);
createHosts(deviceIds.get(deviceCount - 2), infrastructurePorts);
}
}
/*
* Copyright 2015 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.provider.nil;
/**
* Spine-leaf topology with hosts at the leaf devices.
*/
public class SpineLeafTopologySimulator extends TopologySimulator {
@Override
protected void processTopoShape(String shape) {
super.processTopoShape(shape);
// FIXME: implement this
}
@Override
public void setUpTopology() {
// checkArgument(FIXME, "There must be at least one spine tier");
super.setUpTopology();
}
@Override
protected void createLinks() {
}
@Override
protected void createHosts() {
}
}
/*
* Copyright 2015 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.provider.nil;
import com.google.common.collect.Lists;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.link.LinkService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.delay;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.MastershipRole.MASTER;
import static org.onosproject.provider.nil.TopologySimulator.description;
/**
* Drives topology mutations at a specified rate of events per second.
*/
class TopologyMutationDriver implements Runnable {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final int WAIT_DELAY = 2_000;
private static final int MAX_DOWN_LINKS = 5;
private final Random random = new Random();
private volatile boolean stopped = true;
private double mutationRate;
private int millis, nanos;
private LinkService linkService;
private DeviceService deviceService;
private LinkProviderService linkProviderService;
private List<LinkDescription> activeLinks;
private List<LinkDescription> inactiveLinks;
private final ExecutorService executor =
newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator"));
/**
* Starts the mutation process.
*
* @param mutationRate link events per second
* @param linkService link service
* @param deviceService device service
* @param linkProviderService link provider service
*/
void start(double mutationRate,
LinkService linkService, DeviceService deviceService,
LinkProviderService linkProviderService) {
stopped = false;
this.linkService = linkService;
this.deviceService = deviceService;
this.linkProviderService = linkProviderService;
activeLinks = reduceLinks();
inactiveLinks = Lists.newArrayList();
adjustRate(mutationRate);
executor.submit(this);
}
/**
* Adjusts the topology mutation rate.
*
* @param mutationRate new topology mutation rate
*/
void adjustRate(double mutationRate) {
this.mutationRate = mutationRate;
if (mutationRate > 0) {
this.millis = (int) (1_000 / mutationRate / 2);
this.nanos = (int) (1_000_000 / mutationRate / 2) % 1_000_000;
} else {
this.millis = 0;
this.nanos = 0;
}
log.info("Settings: millis={}, nanos={}", millis, nanos);
}
/**
* Stops the mutation process.
*/
void stop() {
stopped = true;
}
/**
* Severs the link between the specified end-points in both directions.
*
* @param one link endpoint
* @param two link endpoint
*/
void severLink(ConnectPoint one, ConnectPoint two) {
LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
linkProviderService.linkVanished(link);
linkProviderService.linkVanished(reverse(link));
}
/**
* Repairs the link between the specified end-points in both directions.
*
* @param one link endpoint
* @param two link endpoint
*/
void repairLink(ConnectPoint one, ConnectPoint two) {
LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
linkProviderService.linkDetected(link);
linkProviderService.linkDetected(reverse(link));
}
@Override
public void run() {
delay(WAIT_DELAY);
while (!stopped) {
if (mutationRate > 0 && inactiveLinks.isEmpty()) {
primeInactiveLinks();
} else if (mutationRate <= 0 && !inactiveLinks.isEmpty()) {
repairInactiveLinks();
} else if (inactiveLinks.isEmpty()) {
delay(WAIT_DELAY);
} else {
activeLinks.add(repairLink());
pause();
inactiveLinks.add(severLink());
pause();
}
}
}
// Primes the inactive links with a few random links.
private void primeInactiveLinks() {
for (int i = 0, n = Math.min(MAX_DOWN_LINKS, activeLinks.size()); i < n; i++) {
inactiveLinks.add(severLink());
}
}
// Repairs all inactive links.
private void repairInactiveLinks() {
while (!inactiveLinks.isEmpty()) {
repairLink();
}
}
// Picks a random active link and severs it.
private LinkDescription severLink() {
LinkDescription link = getRandomLink(activeLinks);
linkProviderService.linkVanished(link);
linkProviderService.linkVanished(reverse(link));
return link;
}
// Picks a random inactive link and repairs it.
private LinkDescription repairLink() {
LinkDescription link = getRandomLink(inactiveLinks);
linkProviderService.linkDetected(link);
linkProviderService.linkDetected(reverse(link));
return link;
}
// Produces a reverse of the specified link.
private LinkDescription reverse(LinkDescription link) {
return new DefaultLinkDescription(link.dst(), link.src(), link.type());
}
// Returns a random link from the specified list of links.
private LinkDescription getRandomLink(List<LinkDescription> links) {
return links.remove(random.nextInt(links.size()));
}
// Reduces the given list of links to just a single link in each original pair.
private List<LinkDescription> reduceLinks() {
List<LinkDescription> links = Lists.newArrayList();
linkService.getLinks().forEach(link -> links.add(description(link)));
return links.stream()
.filter(this::isOurLink)
.filter(this::isRightDirection)
.collect(Collectors.toList());
}
// Returns true if the specified link is ours.
private boolean isOurLink(LinkDescription linkDescription) {
return deviceService.getRole(linkDescription.src().deviceId()) == MASTER;
}
// Returns true if the link source is greater than the link destination.
private boolean isRightDirection(LinkDescription link) {
return link.src().deviceId().toString().compareTo(link.dst().deviceId().toString()) > 0;
}
// Pauses the current thread for the pre-computed time of millis & nanos.
private void pause() {
delay(millis, nanos);
}
}
/*
* Copyright 2015 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.provider.nil;
import com.google.common.collect.Lists;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostProviderService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.link.LinkService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.onlab.util.Tools.toHex;
import static org.onosproject.net.HostId.hostId;
import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.onosproject.provider.nil.NullProviders.SCHEME;
/**
* Abstraction of a provider capable to simulate some network topology.
*/
public abstract class TopologySimulator {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected String[] topoShape;
protected int deviceCount;
protected int hostCount;
protected ServiceDirectory directory;
protected NodeId localNode;
protected ClusterService clusterService;
protected MastershipService mastershipService;
protected DeviceAdminService deviceService;
protected HostService hostService;
protected LinkService linkService;
protected DeviceProviderService deviceProviderService;
protected HostProviderService hostProviderService;
protected LinkProviderService linkProviderService;
protected int maxWaitSeconds = 1;
protected int infrastructurePorts = 2;
protected CountDownLatch deviceLatch;
protected final List<DeviceId> deviceIds = Lists.newArrayList();
private DeviceListener deviceEventCounter = new DeviceEventCounter();
/**
* Initializes a new topology simulator with access to the specified service
* directory and various provider services.
*
* @param topoShape topology shape specifier
* @param deviceCount number of devices in the topology
* @param hostCount number of hosts per device
* @param directory service directory
* @param deviceProviderService device provider service
* @param hostProviderService host provider service
* @param linkProviderService link provider service
*/
protected void init(String topoShape, int deviceCount, int hostCount,
ServiceDirectory directory,
DeviceProviderService deviceProviderService,
HostProviderService hostProviderService,
LinkProviderService linkProviderService) {
this.deviceCount = deviceCount;
this.hostCount = hostCount;
this.directory = directory;
this.clusterService = directory.get(ClusterService.class);
this.mastershipService = directory.get(MastershipService.class);
this.deviceService = directory.get(DeviceAdminService.class);
this.hostService = directory.get(HostService.class);
this.linkService = directory.get(LinkService.class);
this.deviceProviderService = deviceProviderService;
this.hostProviderService = hostProviderService;
this.linkProviderService = linkProviderService;
localNode = clusterService.getLocalNode().id();
processTopoShape(topoShape);
}
/**
* Processes the topology shape specifier.
*
* @param shape topology shape specifier
*/
protected void processTopoShape(String shape) {
this.topoShape = shape.split(",");
}
/**
* Sets up network topology simulation.
*/
public void setUpTopology() {
prepareForDeviceEvents(deviceCount);
createDevices();
waitForDeviceEvents();
createLinks();
createHosts();
}
/**
* Creates simulated devices.
*/
protected void createDevices() {
for (int i = 0; i < deviceCount; i++) {
createDevice(i + 1);
}
}
/**
* Creates simulated links.
*/
protected abstract void createLinks();
/**
* Creates simulated hosts.
*/
protected abstract void createHosts();
/**
* Creates simulated device.
*
* @param i index of the device id in the list.
*/
protected void createDevice(int i) {
DeviceId id = DeviceId.deviceId(SCHEME + ":" + toHex(i));
DeviceDescription desc =
new DefaultDeviceDescription(id.uri(), Device.Type.SWITCH,
"ON.Lab", "0.1", "0.1", "1234",
new ChassisId(i));
deviceProviderService.deviceConnected(id, desc);
deviceProviderService.updatePorts(id, buildPorts(hostCount + infrastructurePorts));
deviceIds.add(id);
}
/**
* Creates simulated link between two devices.
*
* @param i index of one simulated device
* @param j index of another simulated device
*/
protected void createLink(int i, int j) {
ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(1));
ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(2));
linkProviderService.linkDetected(new DefaultLinkDescription(one, two, DIRECT));
linkProviderService.linkDetected(new DefaultLinkDescription(two, one, DIRECT));
}
/**
* Creates simularted hosts for the specified device.
*
* @param deviceId device identifier
* @param portOffset port offset where to start attaching hosts
*/
protected void createHosts(DeviceId deviceId, int portOffset) {
String s = deviceId.toString();
byte dByte = Byte.parseByte(s.substring(s.length() - 1), 16);
// TODO: this limits the simulation to 256 devices & 256 hosts/device.
byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
for (int i = 0; i < hostCount; i++) {
int port = portOffset + i + 1;
macBytes[5] = (byte) (i + 1);
ipBytes[3] = (byte) (i + 1);
HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
hostProviderService.hostDetected(id, description(id, ip, deviceId, port));
}
}
/**
* Prepares to count device added/available/removed events.
*
* @param count number of events to count
*/
protected void prepareForDeviceEvents(int count) {
deviceLatch = new CountDownLatch(count);
deviceService.addListener(deviceEventCounter);
}
/**
* Waits for all expected device added/available/removed events.
*/
protected void waitForDeviceEvents() {
try {
deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Device events did not arrive in time");
}
deviceService.removeListener(deviceEventCounter);
}
/**
* Tears down network topology simulation.
*/
public void tearDownTopology() {
removeHosts();
removeLinks();
removeDevices();
}
/**
* Removes any hosts previously advertised by this provider.
*/
protected void removeHosts() {
hostService.getHosts()
.forEach(host -> hostProviderService.hostVanished(host.id()));
}
/**
* Removes any links previously advertised by this provider.
*/
protected void removeLinks() {
linkService.getLinks()
.forEach(link -> linkProviderService.linkVanished(description(link)));
}
/**
* Removes any devices previously advertised by this provider.
*/
protected void removeDevices() {
prepareForDeviceEvents(deviceService.getDeviceCount());
deviceService.getDevices()
.forEach(device -> deviceService.removeDevice(device.id()));
waitForDeviceEvents();
}
/**
* Produces a device description from the given device.
*
* @param device device to copy
* @return device description
*/
static DeviceDescription description(Device device) {
return new DefaultDeviceDescription(device.id().uri(), device.type(),
device.manufacturer(),
device.hwVersion(), device.swVersion(),
device.serialNumber(), device.chassisId());
}
/**
* Produces a link description from the given link.
*
* @param link link to copy
* @return link description
*/
static DefaultLinkDescription description(Link link) {
return new DefaultLinkDescription(link.src(), link.dst(), link.type());
}
/**
* Produces a host description from the given host.
*
* @param host host to copy
* @return host description
*/
static DefaultHostDescription description(Host host) {
return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
host.ipAddresses());
}
/**
* Generates a host description from the given id and location information.
*
* @param hostId host identifier
* @param ip host IP
* @param deviceId edge device
* @param port edge port
* @return host description
*/
static HostDescription description(HostId hostId, IpAddress ip,
DeviceId deviceId, int port) {
HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip);
}
/**
* Generates a list of a configured number of ports.
*
* @param portCount number of ports
* @return list of ports
*/
protected List<PortDescription> buildPorts(int portCount) {
List<PortDescription> ports = Lists.newArrayList();
for (int i = 0; i < portCount; i++) {
ports.add(new DefaultPortDescription(PortNumber.portNumber(i), true,
Port.Type.COPPER, 0));
}
return ports;
}
// Counts down number of device added/available/removed events.
private class DeviceEventCounter implements DeviceListener {
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
type == DEVICE_AVAILABILITY_CHANGED) {
deviceLatch.countDown();
}
}
}
}
\ No newline at end of file
/*
* Copyright 2015 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.provider.nil;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Tree topology with hosts at the leaf devices.
*/
public class TreeTopologySimulator extends TopologySimulator {
private int[] tierMultiplier;
private int[] tierDeviceCount;
private int[] tierOffset;
@Override
protected void processTopoShape(String shape) {
super.processTopoShape(shape);
tierOffset = new int[topoShape.length];
tierMultiplier = new int[topoShape.length];
tierDeviceCount = new int[topoShape.length];
deviceCount = 1;
tierOffset[0] = 0;
tierMultiplier[0] = 1;
tierDeviceCount[0] = deviceCount;
for (int i = 1; i < topoShape.length; i++) {
tierOffset[i] = deviceCount;
tierMultiplier[i] = Integer.parseInt(topoShape[i]);
tierDeviceCount[i] = tierDeviceCount[i - 1] * tierMultiplier[i];
deviceCount = deviceCount + tierDeviceCount[i];
}
}
@Override
public void setUpTopology() {
checkArgument(tierDeviceCount.length > 0, "There must be at least one tree tier");
super.setUpTopology();
}
@Override
protected void createLinks() {
for (int t = 1; t < tierOffset.length; t++) {
int child = tierOffset[t];
for (int parent = tierOffset[t - 1]; parent < tierOffset[t]; parent++) {
for (int i = 0; i < tierMultiplier[t]; i++) {
createLink(parent, child);
child++;
}
}
}
}
@Override
protected void createHosts() {
for (int i = tierOffset[tierOffset.length - 1]; i < deviceCount; i++) {
createHosts(deviceIds.get(i), hostCount);
}
}
}
/*
* Copyright 2015 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.provider.nil.cli;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.provider.nil.NullProviders;
import static org.onosproject.cli.StartStopCompleter.START;
/**
* Starts or stops topology simulation.
*/
@Command(scope = "onos", name = "null-simulation",
description = "Starts or stops topology simulation")
public class NullControlCommand extends AbstractShellCommand {
@Argument(index = 0, name = "cmd", description = "Control command: start/stop",
required = true, multiValued = false)
String cmd = null;
@Argument(index = 1, name = "topoShape",
description = "Topology shape: e.g. configured, linear, reroute, centipede, tree, spineleaf, mesh",
required = false, multiValued = false)
String topoShape = null;
@Override
protected void execute() {
ComponentConfigService service = get(ComponentConfigService.class);
if (topoShape != null) {
service.setProperty(NullProviders.class.getName(), "topoShape", topoShape);
}
service.setProperty(NullProviders.class.getName(), "enabled",
cmd.equals(START) ? "true" : "false");
}
}
/*
* Copyright 2015 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.provider.nil.cli;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.provider.nil.NullProviders;
import static org.onosproject.cli.UpDownCompleter.DOWN;
import static org.onosproject.cli.UpDownCompleter.UP;
import static org.onosproject.cli.net.AddPointToPointIntentCommand.getDeviceId;
import static org.onosproject.cli.net.AddPointToPointIntentCommand.getPortNumber;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
/**
* Servers or repairs a simulated link.
*/
@Command(scope = "onos", name = "null-link",
description = "Severs or repairs a simulated link")
public class NullLinkCommand extends AbstractShellCommand {
@Argument(index = 0, name = "one", description = "One link end-point as device/port",
required = true, multiValued = false)
String one = null;
@Argument(index = 1, name = "two", description = "Another link end-point as device/port",
required = true, multiValued = false)
String two = null;
@Argument(index = 2, name = "cmd", description = "up/down",
required = true, multiValued = false)
String cmd = null;
@Override
protected void execute() {
NullProviders service = get(NullProviders.class);
try {
DeviceId oneId = deviceId(getDeviceId(one));
PortNumber onePort = portNumber(getPortNumber(one));
ConnectPoint onePoint = new ConnectPoint(oneId, onePort);
DeviceId twoId = deviceId(getDeviceId(two));
PortNumber twoPort = portNumber(getPortNumber(two));
ConnectPoint twoPoint = new ConnectPoint(twoId, twoPort);
if (cmd.equals(UP)) {
service.repairLink(onePoint, twoPoint);
} else if (cmd.equals(DOWN)) {
service.severLink(onePoint, twoPoint);
} else {
error("Illegal command %s; must be up or down", cmd);
}
} catch (NumberFormatException e) {
error("Invalid port number specified", e);
}
}
}
/*
* Copyright 2015 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.provider.nil.cli;
import com.google.common.collect.ImmutableList;
import org.onosproject.cli.AbstractChoicesCompleter;
import java.util.List;
/**
* Topology shape completer.
*/
public class TopologyShapeCompleter extends AbstractChoicesCompleter {
@Override
public List<String> choices() {
return ImmutableList.of("configured", "linear", "reroute", "centipede",
"tree", "spineleaf", "mesh");
}
}
/*
* Copyright 2015 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.
*/
/**
* Null provider CLI commands and completers.
*/
package org.onosproject.provider.nil.cli;
/*
* Copyright 2015 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.
*/
/**
* Set of null south-bound providers which permit simulating a network
* topology using fake devices, links, hosts, etc.
*/
package org.onosproject.provider.nil;
<!--
~ Copyright 2015 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.
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
<action class="org.onosproject.provider.nil.cli.NullControlCommand"/>
<completers>
<ref component-id="startStopCompleter"/>
<ref component-id="topoShapeCompleter"/>
<null/>
</completers>
</command>
<command>
<action class="org.onosproject.provider.nil.cli.NullLinkCommand"/>
<completers>
<ref component-id="linkSrcCompleter"/>
<ref component-id="linkDstCompleter"/>
<ref component-id="upDownCompleter"/>
<null/>
</completers>
</command>
</command-bundle>
<bean id="startStopCompleter" class="org.onosproject.cli.StartStopCompleter"/>
<bean id="upDownCompleter" class="org.onosproject.cli.UpDownCompleter"/>
<bean id="topoShapeCompleter" class="org.onosproject.provider.nil.cli.TopologyShapeCompleter"/>
<bean id="linkSrcCompleter" class="org.onosproject.cli.net.LinkSrcCompleter"/>
<bean id="linkDstCompleter" class="org.onosproject.cli.net.LinkDstCompleter"/>
</blueprint>
{
"devices": [
{ "alias": "s1", "uri": "null:0000000000000001", "mac": "000000000001", "annotations": { "name": "CMBR", "latitude": 42.373730, "longitude": -71.109734 }, "type": "SWITCH" },
{ "alias": "s2", "uri": "null:0000000000000002", "mac": "000000000002", "annotations": { "name": "CHCG", "latitude": 41.877461, "longitude": -87.642892 }, "type": "SWITCH" },
{ "alias": "s3", "uri": "null:0000000000000003", "mac": "000000000003", "annotations": { "name": "CLEV", "latitude": 41.498928, "longitude": -81.695217 }, "type": "SWITCH" },
{ "alias": "s4", "uri": "null:0000000000000004", "mac": "000000000004", "annotations": { "name": "RLGH", "latitude": 35.780150, "longitude": -78.644026 }, "type": "SWITCH" },
{ "alias": "s5", "uri": "null:0000000000000005", "mac": "000000000005", "annotations": { "name": "ATLN", "latitude": 33.749017, "longitude": -84.394168 }, "type": "SWITCH" },
{ "alias": "s6", "uri": "null:0000000000000006", "mac": "000000000006", "annotations": { "name": "PHLA", "latitude": 39.952906, "longitude": -75.172278 }, "type": "SWITCH" },
{ "alias": "s7", "uri": "null:0000000000000007", "mac": "000000000007", "annotations": { "name": "WASH", "latitude": 38.906696, "longitude": -77.035509 }, "type": "SWITCH" },
{ "alias": "s8", "uri": "null:0000000000000008", "mac": "000000000008", "annotations": { "name": "NSVL", "latitude": 36.166410, "longitude": -86.787305 }, "type": "SWITCH" },
{ "alias": "s9", "uri": "null:0000000000000009", "mac": "000000000009", "annotations": { "name": "STLS", "latitude": 38.626418, "longitude": -90.198143 }, "type": "SWITCH" },
{ "alias": "s10", "uri": "null:000000000000000a", "mac": "00000000000a", "annotations": { "name": "NWOR", "latitude": 29.951475, "longitude": -90.078434 }, "type": "SWITCH" },
{ "alias": "s11", "uri": "null:000000000000000b", "mac": "00000000000b", "annotations": { "name": "HSTN", "latitude": 29.763249, "longitude": -95.368332 }, "type": "SWITCH" },
{ "alias": "s12", "uri": "null:000000000000000c", "mac": "00000000000c", "annotations": { "name": "SNAN", "latitude": 29.424331, "longitude": -98.491745 }, "type": "SWITCH" },
{ "alias": "s13", "uri": "null:000000000000000d", "mac": "00000000000d", "annotations": { "name": "DLLS", "latitude": 32.777665, "longitude": -96.802064 }, "type": "SWITCH" },
{ "alias": "s14", "uri": "null:000000000000000e", "mac": "00000000000e", "annotations": { "name": "ORLD", "latitude": 28.538641, "longitude": -81.381110 }, "type": "SWITCH" },
{ "alias": "s15", "uri": "null:000000000000000f", "mac": "00000000000f", "annotations": { "name": "DNVR", "latitude": 39.736623, "longitude": -104.984887 }, "type": "SWITCH" },
{ "alias": "s16", "uri": "null:0000000000000010", "mac": "000000000010", "annotations": { "name": "KSCY", "latitude": 39.100725, "longitude": -94.581228 }, "type": "SWITCH" },
{ "alias": "s17", "uri": "null:0000000000000011", "mac": "000000000011", "annotations": { "name": "SNFN", "latitude": 37.779751, "longitude": -122.409791 }, "type": "SWITCH" },
{ "alias": "s18", "uri": "null:0000000000000012", "mac": "000000000012", "annotations": { "name": "SCRM", "latitude": 38.581001, "longitude": -121.497844 }, "type": "SWITCH" },
{ "alias": "s19", "uri": "null:0000000000000013", "mac": "000000000013", "annotations": { "name": "PTLD", "latitude": 45.523317, "longitude": -122.677768 }, "type": "SWITCH" },
{ "alias": "s20", "uri": "null:0000000000000014", "mac": "000000000014", "annotations": { "name": "STTL", "latitude": 47.607326, "longitude": -122.331786 }, "type": "SWITCH" },
{ "alias": "s21", "uri": "null:0000000000000015", "mac": "000000000015", "annotations": { "name": "SLKC", "latitude": 40.759577, "longitude": -111.895079 }, "type": "SWITCH" },
{ "alias": "s22", "uri": "null:0000000000000016", "mac": "000000000016", "annotations": { "name": "LA03", "latitude": 34.056346, "longitude": -118.235951 }, "type": "SWITCH" },
{ "alias": "s23", "uri": "null:0000000000000017", "mac": "000000000017", "annotations": { "name": "SNDG", "latitude": 32.714564, "longitude": -117.153528 }, "type": "SWITCH" },
{ "alias": "s24", "uri": "null:0000000000000018", "mac": "000000000018", "annotations": { "name": "PHNX", "latitude": 33.448289, "longitude": -112.076299 }, "type": "SWITCH" },
{ "alias": "s25", "uri": "null:0000000000000019", "mac": "000000000019", "annotations": { "name": "NY54", "latitude": 40.728270, "longitude": -73.994483 }, "type": "SWITCH" }
],
"hosts": [
{ "alias": "h1", "mac": "00:00:00:00:00:01", "vlan": -1, "location": "null:0000000000000001/1", "ip": "10.0.0.1", "annotations": { "name": "CMBR", "latitude": 43.355715, "longitude": -69.528243 } },
{ "alias": "h2", "mac": "00:00:00:00:00:02", "vlan": -1, "location": "null:0000000000000002/1", "ip": "10.0.0.2", "annotations": { "name": "CHCG", "latitude": 43.632679, "longitude": -88.772526 } },
{ "alias": "h3", "mac": "00:00:00:00:00:03", "vlan": -1, "location": "null:0000000000000003/1", "ip": "10.0.0.3", "annotations": { "name": "CLEV", "latitude": 42.756945, "longitude": -79.831317 } },
{ "alias": "h4", "mac": "00:00:00:00:00:04", "vlan": -1, "location": "null:0000000000000004/1", "ip": "10.0.0.4", "annotations": { "name": "RLGH", "latitude": 36.972249, "longitude": -76.667163 } },
{ "alias": "h5", "mac": "00:00:00:00:00:05", "vlan": -1, "location": "null:0000000000000005/1", "ip": "10.0.0.5", "annotations": { "name": "ATLN", "latitude": 35.427493, "longitude": -83.885831 } },
{ "alias": "h6", "mac": "00:00:00:00:00:06", "vlan": -1, "location": "null:0000000000000006/1", "ip": "10.0.0.6", "annotations": { "name": "PHLA", "latitude": 39.208113, "longitude": -73.421341 } },
{ "alias": "h7", "mac": "00:00:00:00:00:07", "vlan": -1, "location": "null:0000000000000007/1", "ip": "10.0.0.7", "annotations": { "name": "WASH", "latitude": 40.133860, "longitude": -79.238299 } },
{ "alias": "h8", "mac": "00:00:00:00:00:08", "vlan": -1, "location": "null:0000000000000008/1", "ip": "10.0.0.8", "annotations": { "name": "NSVL", "latitude": 37.407589, "longitude": -84.415068 } },
{ "alias": "h9", "mac": "00:00:00:00:00:09", "vlan": -1, "location": "null:0000000000000009/1", "ip": "10.0.0.9", "annotations": { "name": "STLS", "latitude": 40.066810, "longitude": -90.932405 } },
{ "alias": "h10", "mac": "00:00:00:00:00:0a", "vlan": -1, "location": "null:000000000000000a/1", "ip": "10.0.0.10", "annotations": { "name": "NWOR", "latitude": 31.470982, "longitude": -88.779353 } },
{ "alias": "h11", "mac": "00:00:00:00:00:0b", "vlan": -1, "location": "null:000000000000000b/1", "ip": "10.0.0.11", "annotations": { "name": "HSTN", "latitude": 31.136858, "longitude": -94.351656 } },
{ "alias": "h12", "mac": "00:00:00:00:00:0c", "vlan": -1, "location": "null:000000000000000c/1", "ip": "10.0.0.12", "annotations": { "name": "SNAN", "latitude": 28.040975, "longitude": -99.169527 } },
{ "alias": "h13", "mac": "00:00:00:00:00:0d", "vlan": -1, "location": "null:000000000000000d/1", "ip": "10.0.0.13", "annotations": { "name": "DLLS", "latitude": 31.899825, "longitude": -99.287263 } },
{ "alias": "h14", "mac": "00:00:00:00:00:0e", "vlan": -1, "location": "null:000000000000000e/1", "ip": "10.0.0.14", "annotations": { "name": "ORLD", "latitude": 26.670509, "longitude": -81.291920 } },
{ "alias": "h15", "mac": "00:00:00:00:00:0f", "vlan": -1, "location": "null:000000000000000f/1", "ip": "10.0.0.15", "annotations": { "name": "DNVR", "latitude": 40.888148, "longitude": -103.459878 } },
{ "alias": "h16", "mac": "00:00:00:00:00:10", "vlan": -1, "location": "null:0000000000000010/1", "ip": "10.0.0.16", "annotations": { "name": "KSCY", "latitude": 40.545088, "longitude": -93.734002 } },
{ "alias": "h17", "mac": "00:00:00:00:00:11", "vlan": -1, "location": "null:0000000000000011/1", "ip": "10.0.0.17", "annotations": { "name": "SNFN", "latitude": 39.081743, "longitude": -124.330172 } },
{ "alias": "h18", "mac": "00:00:00:00:00:12", "vlan": -1, "location": "null:0000000000000012/1", "ip": "10.0.0.18", "annotations": { "name": "SCRM", "latitude": 40.107468, "longitude": -120.424689 } },
{ "alias": "h19", "mac": "00:00:00:00:00:13", "vlan": -1, "location": "null:0000000000000013/1", "ip": "10.0.0.19", "annotations": { "name": "PTLD", "latitude": 44.383051, "longitude": -124.767594 } },
{ "alias": "h20", "mac": "00:00:00:00:00:14", "vlan": -1, "location": "null:0000000000000014/1", "ip": "10.0.0.20", "annotations": { "name": "STTL", "latitude": 48.832627, "longitude": -120.298441 } },
{ "alias": "h21", "mac": "00:00:00:00:00:15", "vlan": -1, "location": "null:0000000000000015/1", "ip": "10.0.0.21", "annotations": { "name": "SLKC", "latitude": 42.301734, "longitude": -111.217297 } },
{ "alias": "h22", "mac": "00:00:00:00:00:16", "vlan": -1, "location": "null:0000000000000016/1", "ip": "10.0.0.22", "annotations": { "name": "LA03", "latitude": 33.224634, "longitude": -121.532943 } },
{ "alias": "h23", "mac": "00:00:00:00:00:17", "vlan": -1, "location": "null:0000000000000017/1", "ip": "10.0.0.23", "annotations": { "name": "SNDG", "latitude": 31.834607, "longitude": -118.847982 } },
{ "alias": "h24", "mac": "00:00:00:00:00:18", "vlan": -1, "location": "null:0000000000000018/1", "ip": "10.0.0.24", "annotations": { "name": "PHNX", "latitude": 34.662290, "longitude": -110.946662 } },
{ "alias": "h25", "mac": "00:00:00:00:00:19", "vlan": -1, "location": "null:0000000000000019/1", "ip": "10.0.0.25", "annotations": { "name": "NY54", "latitude": 42.395459, "longitude": -75.293563 } }
],
"links": [
{ "src": "null:0000000000000019/2", "dst": "null:0000000000000001/2", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000019/3", "dst": "null:0000000000000002/10", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000019/4", "dst": "null:0000000000000006/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000019/5", "dst": "null:0000000000000007/2", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000001/2", "dst": "null:0000000000000006/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/2", "dst": "null:0000000000000003/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/3", "dst": "null:0000000000000006/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/4", "dst": "null:0000000000000009/2", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/5", "dst": "null:000000000000000f/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/6", "dst": "null:0000000000000010/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/7", "dst": "null:0000000000000011/7", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/8", "dst": "null:0000000000000014/2", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000002/9", "dst": "null:0000000000000015/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000003/2", "dst": "null:0000000000000008/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000003/3", "dst": "null:0000000000000009/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000003/4", "dst": "null:0000000000000006/6", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000004/2", "dst": "null:0000000000000005/7", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000004/3", "dst": "null:0000000000000007/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000005/2", "dst": "null:0000000000000007/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000005/3", "dst": "null:0000000000000008/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000005/4", "dst": "null:0000000000000009/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000005/5", "dst": "null:000000000000000d/6", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000005/6", "dst": "null:000000000000000e/2", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000006/2", "dst": "null:0000000000000007/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000008/2", "dst": "null:0000000000000009/6", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000008/3", "dst": "null:000000000000000d/7", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000009/2", "dst": "null:000000000000000d/8", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000009/3", "dst": "null:0000000000000010/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000009/4", "dst": "null:0000000000000016/5", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000a/2", "dst": "null:000000000000000b/5", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000a/3", "dst": "null:000000000000000d/9", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000a/4", "dst": "null:000000000000000e/3", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000b/2", "dst": "null:000000000000000c/4", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000b/3", "dst": "null:000000000000000d/10", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000b/4", "dst": "null:000000000000000e/4", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000c/2", "dst": "null:0000000000000018/2", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000c/3", "dst": "null:000000000000000d/6", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000d/2", "dst": "null:000000000000000f/5", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000d/3", "dst": "null:0000000000000010/5", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000d/4", "dst": "null:0000000000000011/8", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000d/5", "dst": "null:0000000000000016/6", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000f/2", "dst": "null:0000000000000010/6", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000f/3", "dst": "null:0000000000000011/9", "annotations": { "durable": "true" }},
{ "src": "null:000000000000000f/4", "dst": "null:0000000000000015/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000010/2", "dst": "null:0000000000000011/10", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000011/2", "dst": "null:0000000000000012/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000011/3", "dst": "null:0000000000000013/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000011/4", "dst": "null:0000000000000014/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000011/5", "dst": "null:0000000000000015/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000011/6", "dst": "null:0000000000000016/7", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000012/2", "dst": "null:0000000000000015/5", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000013/2", "dst": "null:0000000000000014/4", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000015/2", "dst": "null:0000000000000016/8", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000016/2", "dst": "null:0000000000000017/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000016/3", "dst": "null:0000000000000018/3", "annotations": { "durable": "true" }},
{ "src": "null:0000000000000017/2", "dst": "null:0000000000000018/5", "annotations": { "durable": "true" }}
]
}
......@@ -192,6 +192,20 @@ public abstract class Tools {
}
/**
* Suspends the current thread for a specified number of millis and nanos.
*
* @param ms number of millis
* @param nanos number of nanos
*/
public static void delay(int ms, int nanos) {
try {
Thread.sleep(ms, nanos);
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted", e);
}
}
/**
* Slurps the contents of a file into a list of strings, one per line.
*
* @param path file path
......
......@@ -16,6 +16,11 @@
package org.onosproject.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
......@@ -30,9 +35,12 @@ import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostProvider;
......@@ -43,10 +51,8 @@ import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.util.ArrayList;
......@@ -54,37 +60,60 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
/**
* Provider of devices and links parsed from a JSON configuration structure.
*/
class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
private final Logger log = LoggerFactory.getLogger(getClass());
private static final ProviderId PID =
new ProviderId("cfg", "org.onosproject.rest", true);
private static final String UNKNOWN = "unknown";
private CountDownLatch deviceLatch;
private final JsonNode cfg;
private final DeviceService deviceService;
private final DeviceProviderRegistry deviceProviderRegistry;
private final LinkProviderRegistry linkProviderRegistry;
private final HostProviderRegistry hostProviderRegistry;
private DeviceProviderService deviceProviderService;
private LinkProviderService linkProviderService;
private HostProviderService hostProviderService;
private DeviceListener deviceEventCounter = new DeviceEventCounter();
private List<ConnectPoint> connectPoints = Lists.newArrayList();
/**
* Creates a new configuration provider.
*
* @param cfg JSON configuration
* @param deviceService device service
* @param deviceProviderRegistry device provider registry
* @param linkProviderRegistry link provider registry
* @param hostProviderRegistry host provider registry
*/
ConfigProvider(JsonNode cfg,
DeviceService deviceService,
DeviceProviderRegistry deviceProviderRegistry,
LinkProviderRegistry linkProviderRegistry,
HostProviderRegistry hostProviderRegistry) {
this.cfg = checkNotNull(cfg, "Configuration cannot be null");
this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
......@@ -94,56 +123,74 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
* Parses the given JSON and provides links as configured.
*/
void parse() {
parseDevices();
parseLinks();
parseHosts();
try {
register();
parseDevices();
parseLinks();
parseHosts();
addMissingPorts();
} finally {
unregister();
}
}
private void register() {
deviceProviderService = deviceProviderRegistry.register(this);
linkProviderService = linkProviderRegistry.register(this);
hostProviderService = hostProviderRegistry.register(this);
}
private void unregister() {
deviceProviderRegistry.unregister(this);
linkProviderRegistry.unregister(this);
hostProviderRegistry.unregister(this);
}
// Parses the given JSON and provides devices.
private void parseDevices() {
try {
DeviceProviderService dps = deviceProviderRegistry.register(this);
JsonNode nodes = cfg.get("devices");
if (nodes != null) {
prepareForDeviceEvents(nodes.size());
for (JsonNode node : nodes) {
parseDevice(dps, node);
parseDevice(node);
}
}
} finally {
deviceProviderRegistry.unregister(this);
waitForDeviceEvents();
}
}
// Parses the given node with device data and supplies the device.
private void parseDevice(DeviceProviderService dps, JsonNode node) {
private void parseDevice(JsonNode node) {
URI uri = URI.create(get(node, "uri"));
Device.Type type = Device.Type.valueOf(get(node, "type"));
String mfr = get(node, "mfr");
String hw = get(node, "hw");
String sw = get(node, "sw");
String serial = get(node, "serial");
ChassisId cid = new ChassisId(get(node, "mac"));
Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
String mfr = get(node, "mfr", UNKNOWN);
String hw = get(node, "hw", UNKNOWN);
String sw = get(node, "sw", UNKNOWN);
String serial = get(node, "serial", UNKNOWN);
ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
SparseAnnotations annotations = annotations(node.get("annotations"));
DeviceDescription desc =
new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
cid, annotations);
DeviceId deviceId = deviceId(uri);
dps.deviceConnected(deviceId, desc);
deviceProviderService.deviceConnected(deviceId, desc);
JsonNode ports = node.get("ports");
if (ports != null) {
parsePorts(dps, deviceId, ports);
parsePorts(deviceId, ports);
}
}
// Parses the given node with list of device ports.
private void parsePorts(DeviceProviderService dps, DeviceId deviceId, JsonNode nodes) {
private void parsePorts(DeviceId deviceId, JsonNode nodes) {
List<PortDescription> ports = new ArrayList<>();
for (JsonNode node : nodes) {
ports.add(parsePort(node));
}
dps.updatePorts(deviceId, ports);
deviceProviderService.updatePorts(deviceId, ports);
}
// Parses the given node with port information.
......@@ -156,43 +203,41 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
// Parses the given JSON and provides links as configured.
private void parseLinks() {
try {
LinkProviderService lps = linkProviderRegistry.register(this);
JsonNode nodes = cfg.get("links");
if (nodes != null) {
for (JsonNode node : nodes) {
parseLink(lps, node, false);
if (!node.has("halfplex")) {
parseLink(lps, node, true);
}
JsonNode nodes = cfg.get("links");
if (nodes != null) {
for (JsonNode node : nodes) {
parseLink(node, false);
if (!node.has("halfplex")) {
parseLink(node, true);
}
}
} finally {
linkProviderRegistry.unregister(this);
}
}
// Parses the given node with link data and supplies the link.
private void parseLink(LinkProviderService lps, JsonNode node, boolean reverse) {
private void parseLink(JsonNode node, boolean reverse) {
ConnectPoint src = connectPoint(get(node, "src"));
ConnectPoint dst = connectPoint(get(node, "dst"));
Link.Type type = Link.Type.valueOf(get(node, "type"));
Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
SparseAnnotations annotations = annotations(node.get("annotations"));
DefaultLinkDescription desc = reverse ?
new DefaultLinkDescription(dst, src, type, annotations) :
new DefaultLinkDescription(src, dst, type, annotations);
lps.linkDetected(desc);
linkProviderService.linkDetected(desc);
connectPoints.add(src);
connectPoints.add(dst);
}
// Parses the given JSON and provides hosts as configured.
private void parseHosts() {
try {
HostProviderService hps = hostProviderRegistry.register(this);
JsonNode nodes = cfg.get("hosts");
if (nodes != null) {
for (JsonNode node : nodes) {
parseHost(hps, node);
parseHost(node);
parseHost(node); // FIXME: hack to make sure host positions take
}
}
} finally {
......@@ -201,14 +246,14 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
}
// Parses the given node with host data and supplies the host.
private void parseHost(HostProviderService hps, JsonNode node) {
private void parseHost(JsonNode node) {
MacAddress mac = MacAddress.valueOf(get(node, "mac"));
VlanId vlanId = VlanId.vlanId(node.get("vlan").shortValue());
VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
HostId hostId = HostId.hostId(mac, vlanId);
SparseAnnotations annotations = annotations(node.get("annotations"));
HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
String[] ipStrings = get(node, "ip").split(",");
String[] ipStrings = get(node, "ip", "").split(",");
Set<IpAddress> ips = new HashSet<>();
for (String ip : ipStrings) {
ips.add(IpAddress.valueOf(ip.trim()));
......@@ -216,9 +261,45 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
DefaultHostDescription desc =
new DefaultHostDescription(mac, vlanId, location, ips, annotations);
hps.hostDetected(hostId, desc);
hostProviderService.hostDetected(hostId, desc);
connectPoints.add(location);
}
// Adds any missing device ports for configured links and host locations.
private void addMissingPorts() {
deviceService.getDevices().forEach(this::addMissingPorts);
}
// Adds any missing device ports.
private void addMissingPorts(Device device) {
List<Port> ports = deviceService.getPorts(device.id());
Set<ConnectPoint> existing = ports.stream()
.map(p -> new ConnectPoint(device.id(), p.number()))
.collect(Collectors.toSet());
Set<ConnectPoint> missing = connectPoints.stream()
.filter(cp -> !existing.contains(cp))
.collect(Collectors.toSet());
if (!missing.isEmpty()) {
List<PortDescription> newPorts = Lists.newArrayList();
ports.forEach(p -> newPorts.add(description(p)));
missing.forEach(cp -> newPorts.add(description(cp)));
deviceProviderService.updatePorts(device.id(), newPorts);
}
}
// Creates a port description from the specified port.
private PortDescription description(Port p) {
return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
}
// Creates a port description from the specified connection point.
private PortDescription description(ConnectPoint cp) {
return new DefaultPortDescription(cp.port(), true);
}
// Produces set of annotations from the given JSON node.
private SparseAnnotations annotations(JsonNode node) {
if (node == null) {
......@@ -246,12 +327,18 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
return node.path(name).asText();
}
@Override
public void triggerProbe(DeviceId deviceId) {
// Returns string form of the named property in the given JSON object.
private String get(JsonNode node, String name, String defaultValue) {
return node.path(name).asText(defaultValue);
}
@Override
public void roleChanged(DeviceId device, MastershipRole newRole) {
deviceProviderService.receivedRoleReply(device, newRole, newRole);
}
@Override
public void triggerProbe(DeviceId deviceId) {
}
@Override
......@@ -265,6 +352,40 @@ class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
@Override
public boolean isReachable(DeviceId device) {
return false;
return true;
}
/**
* Prepares to count device added/available/removed events.
*
* @param count number of events to count
*/
protected void prepareForDeviceEvents(int count) {
deviceLatch = new CountDownLatch(count);
deviceService.addListener(deviceEventCounter);
}
/**
* Waits for all expected device added/available/removed events.
*/
protected void waitForDeviceEvents() {
try {
deviceLatch.await(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Device events did not arrive in time");
}
deviceService.removeListener(deviceEventCounter);
}
// Counts down number of device added/available/removed events.
private class DeviceEventCounter implements DeviceListener {
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
deviceLatch.countDown();
}
}
}
}
......
......@@ -18,6 +18,7 @@ package org.onosproject.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.host.HostProviderRegistry;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onlab.rest.BaseResource;
......@@ -40,9 +41,9 @@ import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
* devices, ports and links.
*/
@Path("config")
public class ConfigResource extends BaseResource {
public class ConfigWebResource extends BaseResource {
private static Logger log = LoggerFactory.getLogger(ConfigResource.class);
private static Logger log = LoggerFactory.getLogger(ConfigWebResource.class);
@POST
@Path("topology")
......@@ -52,7 +53,8 @@ public class ConfigResource extends BaseResource {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode cfg = mapper.readTree(input);
new ConfigProvider(cfg, get(DeviceProviderRegistry.class),
new ConfigProvider(cfg, get(DeviceService.class),
get(DeviceProviderRegistry.class),
get(LinkProviderRegistry.class),
get(HostProviderRegistry.class)).parse();
return Response.ok().build();
......
......@@ -72,7 +72,7 @@
org.onosproject.rest.IntentsWebResource,
org.onosproject.rest.FlowsWebResource,
org.onosproject.rest.TopologyWebResource,
org.onosproject.rest.ConfigResource,
org.onosproject.rest.ConfigWebResource,
org.onosproject.rest.PathsWebResource
</param-value>
</init-param>
......