alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 34 changed files with 1801 additions and 162 deletions
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-app-metrics</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-metrics-intent</artifactId>
<packaging>bundle</packaging>
<description>ONOS intent metrics application</description>
<dependencies>
<dependency>
<groupId>org.onlab.onos</groupId>
<artifactId>onos-cli</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
</dependencies>
</project>
package org.onlab.onos.metrics.intent;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.LinkedList;
import java.util.List;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.metrics.MetricsComponent;
import org.onlab.metrics.MetricsFeature;
import org.onlab.metrics.MetricsService;
import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentListener;
import org.onlab.onos.net.intent.IntentService;
import org.slf4j.Logger;
/**
* ONOS Intent Metrics Application that collects intent-related metrics.
*/
@Component(immediate = true)
@Service
public class IntentMetrics implements IntentMetricsService,
IntentListener {
private static final Logger log = getLogger(IntentMetrics.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
private LinkedList<IntentEvent> lastEvents = new LinkedList<>();
private static final int LAST_EVENTS_MAX_N = 100;
//
// Metrics
//
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MetricsService metricsService;
//
private static final String COMPONENT_NAME = "Intent";
private static final String FEATURE_SUBMITTED_NAME = "Submitted";
private static final String FEATURE_INSTALLED_NAME = "Installed";
private static final String FEATURE_WITHDRAW_REQUESTED_NAME =
"WithdrawRequested";
private static final String FEATURE_WITHDRAWN_NAME = "Withdrawn";
private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
private static final String METER_RATE_NAME = "Rate";
//
private MetricsComponent metricsComponent;
private MetricsFeature metricsFeatureSubmitted;
private MetricsFeature metricsFeatureInstalled;
private MetricsFeature metricsFeatureWithdrawRequested;
private MetricsFeature metricsFeatureWithdrawn;
//
// Timestamps:
// - Intent Submitted API operation (ms from the Epoch)
// - Intent Installed operation completion (ms from the Epoch)
// - Intent Withdraw Requested API operation (ms from the Epoch)
// - Intent Withdrawn operation completion (ms from the Epoch)
//
private volatile long intentSubmittedTimestampEpochMs = 0;
private volatile long intentInstalledTimestampEpochMs = 0;
private volatile long intentWithdrawRequestedTimestampEpochMs = 0;
private volatile long intentWithdrawnTimestampEpochMs = 0;
//
private Gauge<Long> intentSubmittedTimestampEpochMsGauge;
private Gauge<Long> intentInstalledTimestampEpochMsGauge;
private Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge;
private Gauge<Long> intentWithdrawnTimestampEpochMsGauge;
//
// Rate meters:
// - Rate of the Submitted Intent API operations
// - Rate of the Installed Intent operations
// - Rate of the Withdrawn Requested Intent API operations
// - Rate of the Withdrawn Intent operations
//
private Meter intentSubmittedRateMeter;
private Meter intentInstalledRateMeter;
private Meter intentWithdrawRequestedRateMeter;
private Meter intentWithdrawnRateMeter;
@Activate
protected void activate() {
clear();
registerMetrics();
intentService.addListener(this);
log.info("ONOS Intent Metrics started.");
}
@Deactivate
public void deactivate() {
intentService.removeListener(this);
removeMetrics();
clear();
log.info("ONOS Intent Metrics stopped.");
}
@Override
public List<IntentEvent> getEvents() {
synchronized (lastEvents) {
return ImmutableList.<IntentEvent>copyOf(lastEvents);
}
}
@Override
public Gauge<Long> intentSubmittedTimestampEpochMsGauge() {
return intentSubmittedTimestampEpochMsGauge;
}
@Override
public Gauge<Long> intentInstalledTimestampEpochMsGauge() {
return intentInstalledTimestampEpochMsGauge;
}
@Override
public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge() {
return intentWithdrawRequestedTimestampEpochMsGauge;
}
@Override
public Gauge<Long> intentWithdrawnTimestampEpochMsGauge() {
return intentWithdrawnTimestampEpochMsGauge;
}
@Override
public Meter intentSubmittedRateMeter() {
return intentSubmittedRateMeter;
}
@Override
public Meter intentInstalledRateMeter() {
return intentInstalledRateMeter;
}
@Override
public Meter intentWithdrawRequestedRateMeter() {
return intentWithdrawRequestedRateMeter;
}
@Override
public Meter intentWithdrawnRateMeter() {
return intentWithdrawnRateMeter;
}
@Override
public void event(IntentEvent event) {
synchronized (lastEvents) {
//
// TODO: The processing below is incomplete: we don't have
// an event equivalent of "Withdraw Requested"
//
switch (event.type()) {
case SUBMITTED:
intentSubmittedTimestampEpochMs = System.currentTimeMillis();
intentSubmittedRateMeter.mark(1);
break;
case INSTALLED:
intentInstalledTimestampEpochMs = System.currentTimeMillis();
intentInstalledRateMeter.mark(1);
break;
case FAILED:
// TODO: Just ignore?
break;
/*
case WITHDRAW_REQUESTED:
intentWithdrawRequestedTimestampEpochMs =
System.currentTimeMillis();
intentWithdrawRequestedRateMeter.mark(1);
break;
*/
case WITHDRAWN:
intentWithdrawnTimestampEpochMs = System.currentTimeMillis();
intentWithdrawnRateMeter.mark(1);
break;
default:
break;
}
//
// Keep only the last N events, where N = LAST_EVENTS_MAX_N
//
while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
lastEvents.remove();
}
lastEvents.add(event);
}
log.debug("Intent Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
/**
* Clears the internal state.
*/
private void clear() {
synchronized (lastEvents) {
intentSubmittedTimestampEpochMs = 0;
intentInstalledTimestampEpochMs = 0;
intentWithdrawRequestedTimestampEpochMs = 0;
intentWithdrawnTimestampEpochMs = 0;
lastEvents.clear();
}
}
/**
* Registers the metrics.
*/
private void registerMetrics() {
metricsComponent = metricsService.registerComponent(COMPONENT_NAME);
//
metricsFeatureSubmitted =
metricsComponent.registerFeature(FEATURE_SUBMITTED_NAME);
metricsFeatureInstalled =
metricsComponent.registerFeature(FEATURE_INSTALLED_NAME);
metricsFeatureWithdrawRequested =
metricsComponent.registerFeature(FEATURE_WITHDRAW_REQUESTED_NAME);
metricsFeatureWithdrawn =
metricsComponent.registerFeature(FEATURE_WITHDRAWN_NAME);
//
intentSubmittedTimestampEpochMsGauge =
metricsService.registerMetric(metricsComponent,
metricsFeatureSubmitted,
GAUGE_TIMESTAMP_NAME,
new Gauge<Long>() {
@Override
public Long getValue() {
return intentSubmittedTimestampEpochMs;
}
});
//
intentInstalledTimestampEpochMsGauge =
metricsService.registerMetric(metricsComponent,
metricsFeatureInstalled,
GAUGE_TIMESTAMP_NAME,
new Gauge<Long>() {
@Override
public Long getValue() {
return intentInstalledTimestampEpochMs;
}
});
//
intentWithdrawRequestedTimestampEpochMsGauge =
metricsService.registerMetric(metricsComponent,
metricsFeatureWithdrawRequested,
GAUGE_TIMESTAMP_NAME,
new Gauge<Long>() {
@Override
public Long getValue() {
return intentWithdrawRequestedTimestampEpochMs;
}
});
//
intentWithdrawnTimestampEpochMsGauge =
metricsService.registerMetric(metricsComponent,
metricsFeatureWithdrawn,
GAUGE_TIMESTAMP_NAME,
new Gauge<Long>() {
@Override
public Long getValue() {
return intentWithdrawnTimestampEpochMs;
}
});
//
intentSubmittedRateMeter =
metricsService.createMeter(metricsComponent,
metricsFeatureSubmitted,
METER_RATE_NAME);
//
intentInstalledRateMeter =
metricsService.createMeter(metricsComponent,
metricsFeatureInstalled,
METER_RATE_NAME);
//
intentWithdrawRequestedRateMeter =
metricsService.createMeter(metricsComponent,
metricsFeatureWithdrawRequested,
METER_RATE_NAME);
//
intentWithdrawnRateMeter =
metricsService.createMeter(metricsComponent,
metricsFeatureWithdrawn,
METER_RATE_NAME);
}
/**
* Removes the metrics.
*/
private void removeMetrics() {
metricsService.removeMetric(metricsComponent,
metricsFeatureSubmitted,
GAUGE_TIMESTAMP_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureInstalled,
GAUGE_TIMESTAMP_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureWithdrawRequested,
GAUGE_TIMESTAMP_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureWithdrawn,
GAUGE_TIMESTAMP_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureSubmitted,
METER_RATE_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureInstalled,
METER_RATE_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureWithdrawRequested,
METER_RATE_NAME);
metricsService.removeMetric(metricsComponent,
metricsFeatureWithdrawn,
METER_RATE_NAME);
}
}
package org.onlab.onos.metrics.intent;
import java.util.List;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import org.onlab.onos.net.intent.IntentEvent;
/**
* Service interface exported by IntentMetrics.
*/
public interface IntentMetricsService {
/**
* Gets the last saved intent events.
*
* @return the last saved intent events.
*/
public List<IntentEvent> getEvents();
/**
* Gets the Metrics' Gauge for the intent SUBMITTED event timestamp
* (ms from the epoch).
*
* @return the Metrics' Gauge for the intent SUBMITTED event timestamp
* (ms from the epoch)
*/
public Gauge<Long> intentSubmittedTimestampEpochMsGauge();
/**
* Gets the Metrics' Gauge for the intent INSTALLED event timestamp
* (ms from the epoch).
*
* @return the Metrics' Gauge for the intent INSTALLED event timestamp
* (ms from the epoch)
*/
public Gauge<Long> intentInstalledTimestampEpochMsGauge();
/**
* Gets the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
* timestamp (ms from the epoch).
*
* TODO: This intent event is not implemented yet.
*
* @return the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
* timestamp (ms from the epoch)
*/
public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge();
/**
* Gets the Metrics' Gauge for the intent WITHDRAWN event timestamp
* (ms from the epoch).
*
* @return the Metrics' Gauge for the intent WITHDRAWN event timestamp
* (ms from the epoch)
*/
public Gauge<Long> intentWithdrawnTimestampEpochMsGauge();
/**
* Gets the Metrics' Meter for the submitted intents event rate.
*
* @return the Metrics' Meter for the submitted intents event rate
*/
public Meter intentSubmittedRateMeter();
/**
* Gets the Metrics' Meter for the installed intents event rate.
*
* @return the Metrics' Meter for the installed intent event rate
*/
public Meter intentInstalledRateMeter();
/**
* Gets the Metrics' Meter for the withdraw requested intents event rate.
*
* @return the Metrics' Meter for the withdraw requested intents event rate
*/
public Meter intentWithdrawRequestedRateMeter();
/**
* Gets the Metrics' Meter for the withdraw completed intents event rate.
*
* @return the Metrics' Meter for the withdraw completed intents event rate
*/
public Meter intentWithdrawnRateMeter();
}
package org.onlab.onos.metrics.intent.cli;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.metrics.intent.IntentMetricsService;
import org.onlab.onos.net.intent.IntentEvent;
/**
* Command to show the list of last intent events.
*/
@Command(scope = "onos", name = "intents-events",
description = "Lists the last intent events")
public class IntentEventsListCommand extends AbstractShellCommand {
private static final String FORMAT_EVENT = "Event=%s";
@Override
protected void execute() {
IntentMetricsService service = get(IntentMetricsService.class);
if (outputJson()) {
print("%s", json(service.getEvents()));
} else {
for (IntentEvent event : service.getEvents()) {
print(FORMAT_EVENT, event);
print(""); // Extra empty line for clarity
}
}
}
/**
* Produces a JSON array of intent events.
*
* @param intentEvents the intent events with the data
* @return JSON array with the intent events
*/
private JsonNode json(List<IntentEvent> intentEvents) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (IntentEvent event : intentEvents) {
result.add(json(mapper, event));
}
return result;
}
/**
* Produces JSON object for a intent event.
*
* @param mapper the JSON object mapper to use
* @param intentEvent the intent event with the data
* @return JSON object for the intent event
*/
private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) {
ObjectNode result = mapper.createObjectNode();
result.put("time", intentEvent.time())
.put("type", intentEvent.type().toString())
.put("event", intentEvent.toString());
return result;
}
}
package org.onlab.onos.metrics.intent.cli;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.codahale.metrics.json.MetricsModule;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.metrics.intent.IntentMetricsService;
/**
* Command to show the intent events metrics.
*/
@Command(scope = "onos", name = "intents-events-metrics",
description = "Lists intent events metrics")
public class IntentEventsMetricsCommand extends AbstractShellCommand {
private static final String FORMAT_GAUGE =
"Intent %s Event Timestamp (ms from epoch)=%d";
private static final String FORMAT_METER =
"Intent %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
@Override
protected void execute() {
IntentMetricsService service = get(IntentMetricsService.class);
Gauge<Long> gauge;
Meter meter;
if (outputJson()) {
ObjectMapper mapper = new ObjectMapper()
.registerModule(new MetricsModule(TimeUnit.SECONDS,
TimeUnit.MILLISECONDS,
false));
ObjectNode result = mapper.createObjectNode();
//
gauge = service.intentSubmittedTimestampEpochMsGauge();
result.put("intentSubmittedTimestamp", json(mapper, gauge));
gauge = service.intentInstalledTimestampEpochMsGauge();
result.put("intentInstalledTimestamp", json(mapper, gauge));
gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
result.put("intentWithdrawRequestedTimestamp",
json(mapper, gauge));
gauge = service.intentWithdrawnTimestampEpochMsGauge();
result.put("intentWithdrawnTimestamp", json(mapper, gauge));
//
meter = service.intentSubmittedRateMeter();
result.put("intentSubmittedRate", json(mapper, meter));
meter = service.intentInstalledRateMeter();
result.put("intentInstalledRate", json(mapper, meter));
meter = service.intentWithdrawRequestedRateMeter();
result.put("intentWithdrawRequestedRate", json(mapper, meter));
meter = service.intentWithdrawnRateMeter();
result.put("intentWithdrawnRate", json(mapper, meter));
//
print("%s", result);
} else {
gauge = service.intentSubmittedTimestampEpochMsGauge();
printGauge("Submitted", gauge);
gauge = service.intentInstalledTimestampEpochMsGauge();
printGauge("Installed", gauge);
gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
printGauge("Withdraw Requested", gauge);
gauge = service.intentWithdrawnTimestampEpochMsGauge();
printGauge("Withdrawn", gauge);
//
meter = service.intentSubmittedRateMeter();
printMeter("Submitted", meter);
meter = service.intentInstalledRateMeter();
printMeter("Installed", meter);
meter = service.intentWithdrawRequestedRateMeter();
printMeter("Withdraw Requested", meter);
meter = service.intentWithdrawnRateMeter();
printMeter("Withdrawn", meter);
}
}
/**
* Produces JSON node for an Object.
*
* @param mapper the JSON object mapper to use
* @param object the Object with the data
* @return JSON node for the Object
*/
private JsonNode json(ObjectMapper mapper, Object object) {
//
// NOTE: The API for custom serializers is incomplete,
// hence we have to parse the JSON string to create JsonNode.
//
try {
final String objectJson = mapper.writeValueAsString(object);
JsonNode objectNode = mapper.readTree(objectJson);
return objectNode;
} catch (JsonProcessingException e) {
log.error("Error writing value as JSON string", e);
} catch (IOException e) {
log.error("Error writing value as JSON string", e);
}
return null;
}
/**
* Prints a Gauge.
*
* @param operationStr the string with the intent operation to print
* @param gauge the Gauge to print
*/
private void printGauge(String operationStr, Gauge<Long> gauge) {
print(FORMAT_GAUGE, operationStr, gauge.getValue());
}
/**
* Prints a Meter.
*
* @param operationStr the string with the intent operation to print
* @param meter the Meter to print
*/
private void printMeter(String operationStr, Meter meter) {
TimeUnit rateUnit = TimeUnit.SECONDS;
double rateFactor = rateUnit.toSeconds(1);
print(FORMAT_METER, operationStr, meter.getCount(),
meter.getMeanRate() * rateFactor,
meter.getOneMinuteRate() * rateFactor,
meter.getFiveMinuteRate() * rateFactor,
meter.getFifteenMinuteRate() * rateFactor);
}
}
/**
* ONOS Intent Metrics Application that collects intent-related metrics.
*/
package org.onlab.onos.metrics.intent;
<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.onlab.onos.metrics.intent.cli.IntentEventsListCommand"/>
</command>
<command>
<action class="org.onlab.onos.metrics.intent.cli.IntentEventsMetricsCommand"/>
</command>
</command-bundle>
</blueprint>
......@@ -17,6 +17,7 @@
<description>ONOS metrics applications</description>
<modules>
<module>intent</module>
<module>topology</module>
</modules>
......
......@@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent;
import org.onlab.metrics.MetricsFeature;
import org.onlab.metrics.MetricsService;
import org.onlab.onos.event.Event;
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceListener;
import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.host.HostEvent;
import org.onlab.onos.net.host.HostListener;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.topology.TopologyEvent;
import org.onlab.onos.net.topology.TopologyListener;
import org.onlab.onos.net.topology.TopologyService;
......@@ -28,14 +37,26 @@ import org.slf4j.Logger;
*/
@Component(immediate = true)
@Service
public class TopologyMetrics implements TopologyMetricsService,
TopologyListener {
public class TopologyMetrics implements TopologyMetricsService {
private static final Logger log = getLogger(TopologyMetrics.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
private LinkedList<TopologyEvent> lastEvents = new LinkedList<>();
private static final int LAST_EVENTS_MAX_N = 10;
private LinkedList<Event> lastEvents = new LinkedList<>();
private static final int LAST_EVENTS_MAX_N = 100;
private final DeviceListener deviceListener = new InnerDeviceListener();
private final HostListener hostListener = new InnerHostListener();
private final LinkListener linkListener = new InnerLinkListener();
private final TopologyListener topologyListener =
new InnerTopologyListener();
//
// Metrics
......@@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService,
protected void activate() {
clear();
registerMetrics();
topologyService.addListener(this);
// Register for all topology-related events
deviceService.addListener(deviceListener);
hostService.addListener(hostListener);
linkService.addListener(linkListener);
topologyService.addListener(topologyListener);
log.info("ONOS Topology Metrics started.");
}
@Deactivate
public void deactivate() {
topologyService.removeListener(this);
// De-register from all topology-related events
deviceService.removeListener(deviceListener);
hostService.removeListener(hostListener);
linkService.removeListener(linkListener);
topologyService.removeListener(topologyListener);
removeMetrics();
clear();
log.info("ONOS Topology Metrics stopped.");
}
@Override
public List<TopologyEvent> getEvents() {
public List<Event> getEvents() {
synchronized (lastEvents) {
return ImmutableList.<TopologyEvent>copyOf(lastEvents);
return ImmutableList.<Event>copyOf(lastEvents);
}
}
......@@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService,
return eventRateMeter;
}
@Override
public void event(TopologyEvent event) {
lastEventTimestampEpochMs = System.currentTimeMillis();
//
// NOTE: If we want to count each "reason" as a separate event,
// then we should use 'event.reason().size()' instead of '1' to
// mark the meter below.
//
eventRateMeter.mark(1);
log.debug("Topology Event: time = {} type = {} subject = {}",
event.time(), event.type(), event.subject());
for (Event reason : event.reasons()) {
log.debug("Topology Event Reason: time = {} type = {} subject = {}",
reason.time(), reason.type(), reason.subject());
}
//
// Keep only the last N events, where N = LAST_EVENTS_MAX_N
//
/**
* Records an event.
*
* @param event the event to record
* @param updateEventRateMeter if true, update the Event Rate Meter
*/
private void recordEvent(Event event, boolean updateEventRateMeter) {
synchronized (lastEvents) {
lastEventTimestampEpochMs = System.currentTimeMillis();
if (updateEventRateMeter) {
eventRateMeter.mark(1);
}
//
// Keep only the last N events, where N = LAST_EVENTS_MAX_N
//
while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
lastEvents.remove();
}
......@@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService,
}
/**
* Inner Device Event Listener class.
*/
private class InnerDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
recordEvent(event, true);
log.debug("Device Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
}
/**
* Inner Host Event Listener class.
*/
private class InnerHostListener implements HostListener {
@Override
public void event(HostEvent event) {
recordEvent(event, true);
log.debug("Host Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
}
/**
* Inner Link Event Listener class.
*/
private class InnerLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
recordEvent(event, true);
log.debug("Link Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
}
/**
* Inner Topology Event Listener class.
*/
private class InnerTopologyListener implements TopologyListener {
@Override
public void event(TopologyEvent event) {
//
// NOTE: Don't update the eventRateMeter, because the real
// events are already captured/counted.
//
recordEvent(event, false);
log.debug("Topology Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
for (Event reason : event.reasons()) {
log.debug("Topology Event Reason: time = {} type = {} event = {}",
reason.time(), reason.type(), reason);
}
}
}
/**
* Clears the internal state.
*/
private void clear() {
lastEventTimestampEpochMs = 0;
synchronized (lastEvents) {
lastEventTimestampEpochMs = 0;
lastEvents.clear();
}
}
......
......@@ -4,7 +4,7 @@ import java.util.List;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import org.onlab.onos.net.topology.TopologyEvent;
import org.onlab.onos.event.Event;
/**
* Service interface exported by TopologyMetrics.
......@@ -15,7 +15,7 @@ public interface TopologyMetricsService {
*
* @return the last saved topology events.
*/
public List<TopologyEvent> getEvents();
public List<Event> getEvents();
/**
* Gets the Metrics' Gauge for the last topology event timestamp
......
......@@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent;
description = "Lists the last topology events")
public class TopologyEventsListCommand extends AbstractShellCommand {
private static final String FORMAT_EVENT =
"Topology Event time=%d type=%s subject=%s";
private static final String FORMAT_REASON =
" Reason time=%d type=%s subject=%s";
private static final String FORMAT_EVENT = "Event=%s";
private static final String FORMAT_REASON = " Reason=%s";
@Override
protected void execute() {
......@@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
if (outputJson()) {
print("%s", json(service.getEvents()));
} else {
for (TopologyEvent event : service.getEvents()) {
print(FORMAT_EVENT, event.time(), event.type(),
event.subject());
for (Event reason : event.reasons()) {
print(FORMAT_REASON, reason.time(), reason.type(),
reason.subject());
for (Event event : service.getEvents()) {
print(FORMAT_EVENT, event);
if (event instanceof TopologyEvent) {
TopologyEvent topologyEvent = (TopologyEvent) event;
for (Event reason : topologyEvent.reasons()) {
print(FORMAT_REASON, reason);
}
}
print(""); // Extra empty line for clarity
}
......@@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
/**
* Produces a JSON array of topology events.
*
* @param topologyEvents the topology events with the data
* @param events the topology events with the data
* @return JSON array with the topology events
*/
private JsonNode json(List<TopologyEvent> topologyEvents) {
private JsonNode json(List<Event> events) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (TopologyEvent event : topologyEvents) {
for (Event event : events) {
result.add(json(mapper, event));
}
return result;
......@@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
* @param topologyEvent the topology event with the data
* @return JSON object for the topology event
*/
private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) {
ObjectNode result = mapper.createObjectNode();
ArrayNode reasons = mapper.createArrayNode();
for (Event reason : topologyEvent.reasons()) {
reasons.add(json(mapper, reason));
}
result.put("time", topologyEvent.time())
.put("type", topologyEvent.type().toString())
.put("subject", topologyEvent.subject().toString())
.put("reasons", reasons);
return result;
}
/**
* Produces JSON object for a generic event.
*
* @param event the generic event with the data
* @return JSON object for the generic event
*/
private ObjectNode json(ObjectMapper mapper, Event event) {
ObjectNode result = mapper.createObjectNode();
result.put("time", event.time())
.put("type", event.type().toString())
.put("subject", event.subject().toString());
.put("event", event.toString());
// Add the reasons if a TopologyEvent
if (event instanceof TopologyEvent) {
TopologyEvent topologyEvent = (TopologyEvent) event;
ArrayNode reasons = mapper.createArrayNode();
for (Event reason : topologyEvent.reasons()) {
reasons.add(json(mapper, reason));
}
result.put("reasons", reasons);
}
return result;
}
}
......
......@@ -284,7 +284,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
Link.Type.DIRECT,
Link.Type.OPTICAL,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
......@@ -315,7 +315,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
Link.Type.DIRECT,
Link.Type.OPTICAL,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
......
{
"opticalSwitches": [
"opticalSwitches": [
{
"allowed": true,
"latitude": 37.6,
......@@ -12,7 +12,7 @@
"type": "Roadm"
},
{
{
"allowed": true,
"latitude": 37.3,
"longitude": 121.9,
......@@ -22,9 +22,9 @@
"numRegen": 0
},
"type": "Roadm"
},
},
{
{
"allowed": true,
"latitude": 33.9,
"longitude": 118.4,
......@@ -34,10 +34,10 @@
"numRegen": 2
},
"type": "Roadm"
}
}
],
"opticalLinks": [
"opticalLinks": [
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
......@@ -51,10 +51,38 @@
"port2": 30
},
"type": "wdmLink"
},
{
"allowed": true,
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
"params": {
"distKms": 1000,
"nodeName1": "ROADM3",
"nodeName2": "ROADM1",
"numWaves": 80,
"port1": 30,
"port2": 10
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
"params": {
"distKms": 2000,
"nodeName1": "ROADM2",
"nodeName2": "ROADM3",
"numWaves": 80,
"port1": 20,
"port2": 31
},
"type": "wdmLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
"params": {
......@@ -66,10 +94,9 @@
"port2": 20
},
"type": "wdmLink"
},
},
{
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
......@@ -82,8 +109,21 @@
},
"type": "pktOptLink"
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
"nodeDpid2": "00:00:ff:ff:ff:ff:00:01",
"params": {
"nodeName1": "ROADM1",
"nodeName2": "ROUTER1",
"bandWidth": 100000,
"port1": 11,
"port2": 10
},
"type": "pktOptLink"
},
{
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
......@@ -95,7 +135,20 @@
"port2": 21
},
"type": "pktOptLink"
}
},
{
"allowed": true,
"nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
"nodeDpid2": "00:00:ff:ff:ff:ff:00:02",
"params": {
"nodeName1": "ROADM2",
"nodeName2": "ROUTER2",
"bandWidth": 100000,
"port1": 21,
"port2": 10
},
"type": "pktOptLink"
}
]
}
......
package org.onlab.onos.sdnip;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import org.apache.commons.lang3.tuple.Pair;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
......@@ -36,20 +42,15 @@ import org.onlab.packet.MacAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.googlecode.concurrenttrees.common.KeyValuePair;
import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
/**
* This class processes BGP route update, translates each update into a intent
......@@ -744,6 +745,21 @@ public class Router implements RouteListener {
}
/**
* Gets the pushed route intents.
*
* @return the pushed route intents
*/
public Collection<MultiPointToSinglePointIntent> getPushedRouteIntents() {
List<MultiPointToSinglePointIntent> pushedIntents = new LinkedList<>();
for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
pushedRouteIntents.entrySet()) {
pushedIntents.add(entry.getValue());
}
return pushedIntents;
}
/**
* Listener for host events.
*/
class InternalHostListener implements HostListener {
......
package org.onlab.onos.sdnip;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultHost;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.IntentService;
import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.sdnip.config.BgpPeer;
import org.onlab.onos.sdnip.config.Interface;
import org.onlab.onos.sdnip.config.SdnIpConfigService;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.TestUtils;
import org.onlab.util.TestUtils.TestUtilsException;
import com.google.common.collect.Sets;
/**
* This class tests adding a route, updating a route, deleting a route,
* and adding a route whose next hop is the local BGP speaker.
*/
public class RouterTest {
private SdnIpConfigService sdnIpConfigService;
private InterfaceService interfaceService;
private IntentService intentService;
private HostService hostService;
private Map<IpAddress, BgpPeer> bgpPeers;
private Map<IpAddress, BgpPeer> configuredPeers;
private Set<Interface> interfaces;
private Set<Interface> configuredInterfaces;
private static final ApplicationId APPID = new ApplicationId() {
@Override
public short id() {
return 1;
}
@Override
public String name() {
return "SDNIP";
}
};
private Router router;
@Before
public void setUp() throws Exception {
bgpPeers = setUpBgpPeers();
interfaces = setUpInterfaces();
initRouter();
}
/**
* Initializes Router class.
*/
private void initRouter() {
intentService = createMock(IntentService.class);
hostService = createMock(HostService.class);
interfaceService = createMock(InterfaceService.class);
expect(interfaceService.getInterfaces()).andReturn(
interfaces).anyTimes();
Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>();
ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24"));
Interface expectedInterface =
new Interface(new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber("1")),
ipAddressesOnSw1Eth1,
MacAddress.valueOf("00:00:00:00:00:01"));
ConnectPoint egressPoint = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber(1));
expect(interfaceService.getInterface(egressPoint)).andReturn(
expectedInterface).anyTimes();
Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>();
ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24"));
Interface expectedInterfaceNew =
new Interface(new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber("1")),
ipAddressesOnSw2Eth1,
MacAddress.valueOf("00:00:00:00:00:02"));
ConnectPoint egressPointNew = new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber(1));
expect(interfaceService.getInterface(egressPointNew)).andReturn(
expectedInterfaceNew).anyTimes();
replay(interfaceService);
sdnIpConfigService = createMock(SdnIpConfigService.class);
expect(sdnIpConfigService.getBgpPeers()).andReturn(bgpPeers).anyTimes();
replay(sdnIpConfigService);
router = new Router(APPID, intentService,
hostService, sdnIpConfigService, interfaceService);
}
/**
* Sets up BGP peers in external networks.
*
* @return configured BGP peers as a Map from peer IP address to BgpPeer
*/
private Map<IpAddress, BgpPeer> setUpBgpPeers() {
configuredPeers = new HashMap<>();
String peerSw1Eth1 = "192.168.10.1";
configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
// Two BGP peers are connected to switch 2 port 1.
String peer1Sw2Eth1 = "192.168.20.1";
configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
String peer2Sw2Eth1 = "192.168.20.2";
configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
return configuredPeers;
}
/**
* Sets up logical interfaces, which emulate the configured interfaces
* in SDN-IP application.
*
* @return configured interfaces as a Set
*/
private Set<Interface> setUpInterfaces() {
configuredInterfaces = Sets.newHashSet();
Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>();
ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24"));
configuredInterfaces.add(
new Interface(new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber(1)),
ipAddressesOnSw1Eth1,
MacAddress.valueOf("00:00:00:00:00:01")));
Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>();
ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24"));
configuredInterfaces.add(
new Interface(new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber(1)),
ipAddressesOnSw2Eth1,
MacAddress.valueOf("00:00:00:00:00:02")));
Set<IpPrefix> ipAddressesOnSw3Eth1 = new HashSet<IpPrefix>();
ipAddressesOnSw3Eth1.add(IpPrefix.valueOf("192.168.30.0/24"));
configuredInterfaces.add(
new Interface(new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber(1)),
ipAddressesOnSw3Eth1,
MacAddress.valueOf("00:00:00:00:00:03")));
return configuredInterfaces;
}
/**
* This method tests adding a route entry.
*/
@Test
public void testProcessRouteAdd() throws TestUtilsException {
// Construct a route entry
RouteEntry routeEntry = new RouteEntry(
IpPrefix.valueOf("1.1.1.0/24"),
IpAddress.valueOf("192.168.10.1"));
// Construct a MultiPointToSinglePointIntent intent
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
routeEntry.prefix());
TrafficTreatment.Builder treatmentBuilder =
DefaultTrafficTreatment.builder();
treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber("1")));
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber("1")));
ConnectPoint egressPoint = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber("1"));
MultiPointToSinglePointIntent intent =
new MultiPointToSinglePointIntent(APPID,
selectorBuilder.build(), treatmentBuilder.build(),
ingressPoints, egressPoint);
// Reset host service
reset(hostService);
Set<Host> hosts = new HashSet<Host>(1);
Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>();
ipPrefixes.add(IpPrefix.valueOf("192.168.10.1/32"));
hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
new HostLocation(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber(1), 1),
ipPrefixes));
expect(hostService.getHostsByIp(
IpPrefix.valueOf("192.168.10.1/32"))).andReturn(hosts);
replay(hostService);
// Set up test expectation
reset(intentService);
intentService.submit(intent);
replay(intentService);
// Call the processRouteAdd() method in Router class
router.leaderChanged(true);
TestUtils.setField(router, "isActivatedLeader", true);
router.processRouteAdd(routeEntry);
// Verify
assertEquals(router.getRoutes().size(), 1);
assertTrue(router.getRoutes().contains(routeEntry));
assertEquals(router.getPushedRouteIntents().size(), 1);
assertEquals(router.getPushedRouteIntents().iterator().next(),
intent);
verify(intentService);
}
/**
* This method tests updating a route entry.
*
* @throws TestUtilsException
*/
@Test
public void testRouteUpdate() throws TestUtilsException {
// Firstly add a route
testProcessRouteAdd();
// Construct the existing route entry
RouteEntry routeEntry = new RouteEntry(
IpPrefix.valueOf("1.1.1.0/24"),
IpAddress.valueOf("192.168.10.1"));
// Construct the existing MultiPointToSinglePointIntent intent
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
routeEntry.prefix());
TrafficTreatment.Builder treatmentBuilder =
DefaultTrafficTreatment.builder();
treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
ConnectPoint egressPoint = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber("1"));
Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber("1")));
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber("1")));
MultiPointToSinglePointIntent intent =
new MultiPointToSinglePointIntent(APPID,
selectorBuilder.build(), treatmentBuilder.build(),
ingressPoints, egressPoint);
// Start to construct a new route entry and new intent
RouteEntry routeEntryUpdate = new RouteEntry(
IpPrefix.valueOf("1.1.1.0/24"),
IpAddress.valueOf("192.168.20.1"));
// Construct a new MultiPointToSinglePointIntent intent
TrafficSelector.Builder selectorBuilderNew =
DefaultTrafficSelector.builder();
selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
routeEntryUpdate.prefix());
TrafficTreatment.Builder treatmentBuilderNew =
DefaultTrafficTreatment.builder();
treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
ConnectPoint egressPointNew = new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber("1"));
Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>();
ingressPointsNew.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber("1")));
ingressPointsNew.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber("1")));
MultiPointToSinglePointIntent intentNew =
new MultiPointToSinglePointIntent(APPID,
selectorBuilderNew.build(),
treatmentBuilderNew.build(),
ingressPointsNew, egressPointNew);
// Reset host service
reset(hostService);
Set<Host> hosts = new HashSet<Host>(1);
Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>();
ipPrefixes.add(IpPrefix.valueOf("192.168.20.1/32"));
hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE,
MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
new HostLocation(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber(1), 1),
ipPrefixes));
expect(hostService.getHostsByIp(
IpPrefix.valueOf("192.168.20.1/32"))).andReturn(hosts);
replay(hostService);
// Set up test expectation
reset(intentService);
intentService.withdraw(intent);
intentService.submit(intentNew);
replay(intentService);
// Call the processRouteAdd() method in Router class
router.leaderChanged(true);
TestUtils.setField(router, "isActivatedLeader", true);
router.processRouteAdd(routeEntryUpdate);
// Verify
assertEquals(router.getRoutes().size(), 1);
assertTrue(router.getRoutes().contains(routeEntryUpdate));
assertEquals(router.getPushedRouteIntents().size(), 1);
assertEquals(router.getPushedRouteIntents().iterator().next(),
intentNew);
verify(intentService);
}
/**
* This method tests deleting a route entry.
*/
@Test
public void testProcessRouteDelete() throws TestUtilsException {
// Firstly add a route
testProcessRouteAdd();
// Construct the existing route entry
RouteEntry routeEntry = new RouteEntry(
IpPrefix.valueOf("1.1.1.0/24"),
IpAddress.valueOf("192.168.10.1"));
// Construct the existing MultiPointToSinglePointIntent intent
TrafficSelector.Builder selectorBuilder =
DefaultTrafficSelector.builder();
selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
routeEntry.prefix());
TrafficTreatment.Builder treatmentBuilder =
DefaultTrafficTreatment.builder();
treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
ConnectPoint egressPoint = new ConnectPoint(
DeviceId.deviceId("of:0000000000000001"),
PortNumber.portNumber("1"));
Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000002"),
PortNumber.portNumber("1")));
ingressPoints.add(new ConnectPoint(
DeviceId.deviceId("of:0000000000000003"),
PortNumber.portNumber("1")));
MultiPointToSinglePointIntent intent =
new MultiPointToSinglePointIntent(APPID,
selectorBuilder.build(), treatmentBuilder.build(),
ingressPoints, egressPoint);
// Set up expectation
reset(intentService);
intentService.withdraw(intent);
replay(intentService);
// Call route deleting method in Router class
router.leaderChanged(true);
TestUtils.setField(router, "isActivatedLeader", true);
router.processRouteDelete(routeEntry);
// Verify
assertEquals(router.getRoutes().size(), 0);
assertEquals(router.getPushedRouteIntents().size(), 0);
verify(intentService);
}
/**
* This method tests when the next hop of a route is the local BGP speaker.
*
* @throws TestUtilsException
*/
@Test
public void testLocalRouteAdd() throws TestUtilsException {
// Construct a route entry, the next hop is the local BGP speaker
RouteEntry routeEntry = new RouteEntry(
IpPrefix.valueOf("1.1.1.0/24"), IpAddress.valueOf("0.0.0.0"));
// Reset intentService to check whether the submit method is called
reset(intentService);
replay(intentService);
// Call the processRouteAdd() method in Router class
router.leaderChanged(true);
TestUtils.setField(router, "isActivatedLeader", true);
router.processRouteAdd(routeEntry);
// Verify
assertEquals(router.getRoutes().size(), 1);
assertTrue(router.getRoutes().contains(routeEntry));
assertEquals(router.getPushedRouteIntents().size(), 0);
verify(intentService);
}
}
......@@ -20,8 +20,10 @@ package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyProvider;
import org.onlab.onos.net.topology.TopologyService;
/**
......@@ -35,6 +37,10 @@ public class TopologyCommand extends AbstractShellCommand {
private static final String FMT =
"time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
@Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
required = false, multiValued = false)
private boolean recompute = false;
protected TopologyService service;
protected Topology topology;
......@@ -49,7 +55,10 @@ public class TopologyCommand extends AbstractShellCommand {
@Override
protected void execute() {
init();
if (outputJson()) {
if (recompute) {
get(TopologyProvider.class).triggerRecompute();
} else if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("time", topology.time())
.put("deviceCount", topology.deviceCount())
......
......@@ -25,7 +25,18 @@ public interface Link extends Annotated, Provided, NetworkResource {
/**
* Signifies that this link is an edge, i.e. host link.
*/
EDGE
EDGE,
/**
* Signifies that this link represents a logical link backed by
* some form of a tunnel.
*/
TUNNEL,
/**
* Signifies that this link is realized by optical connection.
*/
OPTICAL
}
/**
......@@ -49,6 +60,4 @@ public interface Link extends Annotated, Provided, NetworkResource {
*/
Type type();
// LinkInfo info(); // Additional link information / decorations
}
......
......@@ -7,4 +7,9 @@ import org.onlab.onos.net.provider.Provider;
*/
public interface TopologyProvider extends Provider {
/**
* Triggers topology recomputation.
*/
void triggerRecompute();
}
......
......@@ -208,7 +208,7 @@ public class LinkManager
LinkEvent event = store.createOrUpdateLink(provider().id(),
linkDescription);
if (event != null) {
log.debug("Link {} detected", linkDescription);
log.info("Link {} detected", linkDescription);
post(event);
}
}
......
......@@ -5,6 +5,7 @@ import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.event.AbstractEventAccumulator;
import org.onlab.onos.event.Event;
import org.onlab.onos.event.EventAccumulator;
......@@ -39,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger;
* new topology snapshots.
*/
@Component(immediate = true)
@Service
public class DefaultTopologyProvider extends AbstractProvider
implements TopologyProvider {
......@@ -89,7 +91,7 @@ public class DefaultTopologyProvider extends AbstractProvider
linkService.addListener(linkListener);
isStarted = true;
triggerTopologyBuild(Collections.<Event>emptyList());
triggerRecompute();
log.info("Started");
}
......@@ -108,6 +110,11 @@ public class DefaultTopologyProvider extends AbstractProvider
log.info("Stopped");
}
@Override
public void triggerRecompute() {
triggerTopologyBuild(Collections.<Event>emptyList());
}
/**
* Triggers assembly of topology data citing the specified events as the
* reason.
......@@ -177,7 +184,11 @@ public class DefaultTopologyProvider extends AbstractProvider
@Override
public void run() {
buildTopology(reasons);
try {
buildTopology(reasons);
} catch (Exception e) {
log.warn("Unable to compute topology due to: {}", e.getMessage());
}
}
}
......
......@@ -195,6 +195,10 @@ public class TopologyManagerTest {
public TestProvider() {
super(PID);
}
@Override
public void triggerRecompute() {
}
}
private static class TestListener implements TopologyListener {
......
......@@ -192,14 +192,6 @@ public class SimpleLinkStore
// Creates and stores the link and returns the appropriate event.
// Guarded by linkDescs value (=locking each Link)
private LinkEvent createLink(LinkKey key, Link newLink) {
if (newLink.providerId().isAncillary()) {
// TODO: revisit ancillary only Link handling
// currently treating ancillary only as down (not visible outside)
return null;
}
links.put(key, newLink);
srcLinks.put(newLink.src().deviceId(), key);
dstLinks.put(newLink.dst().deviceId(), key);
......@@ -209,10 +201,8 @@ public class SimpleLinkStore
// Updates, if necessary the specified link and returns the appropriate event.
// Guarded by linkDescs value (=locking each Link)
private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
if (newLink.providerId().isAncillary()) {
// TODO: revisit ancillary only Link handling
// currently treating ancillary only as down (not visible outside)
return null;
}
......
package org.onlab.onos.store.trivial.impl;
import static org.junit.Assert.*;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.Link.Type.*;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Iterables;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
......@@ -23,17 +11,27 @@ import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkEvent;
import org.onlab.onos.net.link.LinkStore;
import org.onlab.onos.net.link.LinkStoreDelegate;
import org.onlab.onos.net.provider.ProviderId;
import com.google.common.collect.Iterables;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.Link.Type.*;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
/**
* Test of the simple LinkStore implementation.
......@@ -301,7 +299,7 @@ public class SimpleLinkStoreTest {
LinkEvent event = linkStore.createOrUpdateLink(PIDA,
new DefaultLinkDescription(src, dst, INDIRECT, A1));
assertNull("Ancillary only link is ignored", event);
assertNotNull("Ancillary only link is ignored", event);
// add Primary link
LinkEvent event2 = linkStore.createOrUpdateLink(PID,
......@@ -309,7 +307,7 @@ public class SimpleLinkStoreTest {
assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
assertEquals(LINK_ADDED, event2.type());
assertEquals(LINK_UPDATED, event2.type());
// update link type
LinkEvent event3 = linkStore.createOrUpdateLink(PID,
......@@ -375,7 +373,7 @@ public class SimpleLinkStoreTest {
}
@Test
public final void testAncillaryOnlyNotVisible() {
public final void testAncillaryVisible() {
ConnectPoint src = new ConnectPoint(DID1, P1);
ConnectPoint dst = new ConnectPoint(DID2, P2);
......@@ -384,18 +382,8 @@ public class SimpleLinkStoreTest {
new DefaultLinkDescription(src, dst, INDIRECT, A1));
// Ancillary only link should not be visible
assertEquals(0, linkStore.getLinkCount());
assertTrue(Iterables.isEmpty(linkStore.getLinks()));
assertNull(linkStore.getLink(src, dst));
assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
assertEquals(1, linkStore.getLinkCount());
assertNotNull(linkStore.getLink(src, dst));
}
// If Delegates should be called only on remote events,
......
......@@ -199,9 +199,16 @@
<feature name="onos-app-metrics" version="1.0.0"
description="ONOS metrics applications">
<feature>onos-app-metrics-intent</feature>
<feature>onos-app-metrics-topology</feature>
</feature>
<feature name="onos-app-metrics-intent" version="1.0.0"
description="ONOS intent metrics application">
<feature>onos-api</feature>
<bundle>mvn:org.onlab.onos/onos-app-metrics-intent/1.0.0-SNAPSHOT</bundle>
</feature>
<feature name="onos-app-metrics-topology" version="1.0.0"
description="ONOS topology metrics application">
<feature>onos-api</feature>
......
......@@ -88,7 +88,6 @@
<version>18.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
......
......@@ -7,4 +7,4 @@ export OC1="192.168.56.101"
export OCN="192.168.56.103"
export OCI="${OC1}"
export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-rest,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
{
"devices" : [
{
"uri": "of:0000ffffffffff01", "mac": "ffffffffffff01", "type": "ROADM",
"mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
"annotations": { "latitude": 37.6, "longitude": 122.3, "optical.regens": 0 }
},
{
"uri": "of:0000ffffffffff02", "mac": "ffffffffffff02", "type": "ROADM",
"mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
"annotations": { "latitude": 37.3, "longitude": 121.9, "optical.regens": 0 }
},
{
"uri": "of:0000ffffffffff03", "mac": "ffffffffffff03", "type": "ROADM",
"mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
"annotations": { "latitude": 33.9, "longitude": 118.4, "optical.regens": 2 }
},
{
"uri": "of:0000ffffffff0001", "mac": "ffffffffff0003", "type": "SWITCH",
"mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
"annotations": { "latitude": 37.6, "longitude": 122.3 }
},
{
"uri": "of:0000ffffffff0002", "mac": "ffffffffff0002", "type": "SWITCH",
"mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
"annotations": { "latitude": 37.3, "longitude": 121.9 }
}
],
"links" : [
{ "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
{ "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
{ "src": "of:0000ffffffff0001/10", "dst": "of:0000ffffffffff01/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } },
{ "src": "of:0000ffffffff0002/10", "dst": "of:0000ffffffffff02/21", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } }
],
"hosts" : [
{ "mac": "a0:00:00:00:00:11", "vlan": -1, "location": "of:0000ffffffff0001/11", "ip": "1.2.3.4" },
{ "mac": "a0:00:00:00:00:12", "vlan": -1, "location": "of:0000ffffffff0001/12", "ip": "1.2.3.5" },
{ "mac": "a0:00:00:00:00:21", "vlan": -1, "location": "of:0000ffffffff0002/11", "ip": "2.2.3.4" },
{ "mac": "a0:00:00:00:00:22", "vlan": -1, "location": "of:0000ffffffff0002/12", "ip": "2.2.3.5" }
]
}
\ No newline at end of file
......@@ -32,7 +32,7 @@ public final class ChassisId {
* @param value the value to use.
*/
public ChassisId(String value) {
this.value = Long.valueOf(value);
this.value = Long.valueOf(value, 16);
}
/**
......
package org.onlab.nio;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.net.InetAddress;
......@@ -33,7 +34,8 @@ public class IOLoopIntegrationTest {
}
}
// TODO: this test can not pass in some environments, need to be improved
@Ignore
@Test
public void basic() throws Exception {
runTest(MILLION, MESSAGE_LENGTH, TIMEOUT);
......
......@@ -23,12 +23,6 @@
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
......
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.onlab.onos.rest;
import com.fasterxml.jackson.databind.JsonNode;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.device.DefaultDeviceDescription;
import org.onlab.onos.net.device.DeviceDescription;
import org.onlab.onos.net.device.DeviceProvider;
import org.onlab.onos.net.device.DeviceProviderRegistry;
import org.onlab.onos.net.device.DeviceProviderService;
import org.onlab.onos.net.host.DefaultHostDescription;
import org.onlab.onos.net.host.HostProvider;
import org.onlab.onos.net.host.HostProviderRegistry;
import org.onlab.onos.net.host.HostProviderService;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkProvider;
import org.onlab.onos.net.link.LinkProviderRegistry;
import org.onlab.onos.net.link.LinkProviderService;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import java.net.URI;
import java.util.Iterator;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
/**
* Provider of devices and links parsed from a JSON configuration structure.
*/
class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
private static final ProviderId PID =
new ProviderId("cfg", "org.onlab.onos.rest", true);
private final JsonNode cfg;
private final DeviceProviderRegistry deviceProviderRegistry;
private final LinkProviderRegistry linkProviderRegistry;
private final HostProviderRegistry hostProviderRegistry;
/**
* Creates a new configuration provider.
*
* @param cfg JSON configuration
* @param deviceProviderRegistry device provider registry
* @param linkProviderRegistry link provider registry
* @param hostProviderRegistry host provider registry
*/
ConfigProvider(JsonNode cfg,
DeviceProviderRegistry deviceProviderRegistry,
LinkProviderRegistry linkProviderRegistry,
HostProviderRegistry hostProviderRegistry) {
this.cfg = checkNotNull(cfg, "Configuration 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");
}
/**
* Parses the given JSON and provides links as configured.
*/
void parse() {
parseDevices();
parseLinks();
parseHosts();
}
// Parses the given JSON and provides devices.
private void parseDevices() {
try {
DeviceProviderService dps = deviceProviderRegistry.register(this);
JsonNode nodes = cfg.get("devices");
if (nodes != null) {
for (JsonNode node : nodes) {
parseDevice(dps, node);
}
}
} finally {
deviceProviderRegistry.unregister(this);
}
}
// Parses the given node with device data and supplies the device.
private void parseDevice(DeviceProviderService dps, 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"));
SparseAnnotations annotations = annotations(node.get("annotations"));
DeviceDescription desc =
new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
cid, annotations);
dps.deviceConnected(deviceId(uri), desc);
}
// 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);
}
}
}
} finally {
linkProviderRegistry.unregister(this);
}
}
// Parses the given node with link data and supplies the link.
private void parseLink(LinkProviderService lps, 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"));
SparseAnnotations annotations = annotations(node.get("annotations"));
DefaultLinkDescription desc = reverse ?
new DefaultLinkDescription(dst, src, type, annotations) :
new DefaultLinkDescription(src, dst, type, annotations);
lps.linkDetected(desc);
}
// 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);
}
}
} finally {
hostProviderRegistry.unregister(this);
}
}
// Parses the given node with host data and supplies the host.
private void parseHost(HostProviderService hps, JsonNode node) {
MacAddress mac = MacAddress.valueOf(get(node, "mac"));
VlanId vlanId = VlanId.vlanId(node.get("vlan").shortValue());
HostId hostId = HostId.hostId(mac, vlanId);
SparseAnnotations annotations = annotations(node.get("annotations"));
HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
IpPrefix ip = IpPrefix.valueOf(get(node, "ip"));
DefaultHostDescription desc =
new DefaultHostDescription(mac, vlanId, location, ip, annotations);
hps.hostDetected(hostId, desc);
}
// Produces set of annotations from the given JSON node.
private SparseAnnotations annotations(JsonNode node) {
if (node == null) {
return null;
}
DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
Iterator<String> it = node.fieldNames();
while (it.hasNext()) {
String k = it.next();
builder.set(k, node.get(k).asText());
}
return builder.build();
}
// Produces a connection point from the specified uri/port text.
private ConnectPoint connectPoint(String text) {
int i = text.lastIndexOf("/");
return new ConnectPoint(deviceId(text.substring(0, i)),
portNumber(text.substring(i + 1)));
}
// Returns string form of the named property in the given JSON object.
private String get(JsonNode node, String name) {
return node.path(name).asText();
}
@Override
public void triggerProbe(Device device) {
}
@Override
public void roleChanged(Device device, MastershipRole newRole) {
}
@Override
public void triggerProbe(Host host) {
}
@Override
public ProviderId id() {
return PID;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.onlab.onos.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.onlab.onos.net.device.DeviceProviderRegistry;
import org.onlab.onos.net.host.HostProviderRegistry;
import org.onlab.onos.net.link.LinkProviderRegistry;
import org.onlab.rest.BaseResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
/**
* Resource that acts as an ancillary provider for uploading pre-configured
* devices, ports and links.
*/
@Path("config")
public class ConfigResource extends BaseResource {
@POST
@Path("topology")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response topology(InputStream input) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode cfg = mapper.readTree(input);
new ConfigProvider(cfg, get(DeviceProviderRegistry.class),
get(LinkProviderRegistry.class),
get(HostProviderRegistry.class)).parse();
return Response.ok(mapper.createObjectNode().toString()).build();
}
}
{
"devices" : [
{
"uri": "of:00000000000001", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
"serial": "ab321", "mac": "00000000000001", "annotations": {"foo": "bar"},
"ports": []
},
{
"uri": "of:00000000000002", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
"serial": "ab456", "mac": "00000000000002", "annotations": {"foo": "bar"},
"ports": []
}
],
"links" : [
{ "src": "of:00000000000001/1", "dst": "of:00000000000002/1", "type": "OPTICAL" },
{ "src": "of:00000000000002/1", "dst": "of:00000000000001/1", "type": "OPTICAL" }
]
}
\ No newline at end of file
......@@ -44,6 +44,11 @@
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
</dependency>
......@@ -93,6 +98,7 @@
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
org.slf4j,
org.osgi.framework,
javax.ws.rs,javax.ws.rs.core,
com.sun.jersey.api.core,
......@@ -100,6 +106,8 @@
com.sun.jersey.server.impl.container.servlet,
com.fasterxml.jackson.databind,
com.fasterxml.jackson.databind.node,
com.google.common.base.*,
org.onlab.packet.*,
org.onlab.rest.*,
org.onlab.onos.*
</Import-Package>
......