Praseed Balakrishnan

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

Showing 138 changed files with 4475 additions and 510 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;
}
}
......
......@@ -285,7 +285,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
DefaultLinkDescription linkDescription =
new DefaultLinkDescription(srcPoint,
snkPoint,
Link.Type.DIRECT,
Link.Type.OPTICAL,
extendedAttributes);
linkProviderService.linkDetected(linkDescription);
......@@ -316,7 +316,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": 21
},
"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": 11
},
"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"
}
]
}
......
......@@ -4,19 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set;
import org.apache.commons.lang.NotImplementedException;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.sdnip.config.Interface;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import com.google.common.collect.Sets;
/**
* Provides IntefaceService using PortAddresses data from the HostService.
* Provides InterfaceService using PortAddresses data from the HostService.
*/
public class HostToInterfaceAdaptor implements InterfaceService {
......@@ -52,8 +50,17 @@ public class HostToInterfaceAdaptor implements InterfaceService {
@Override
public Interface getMatchingInterface(IpAddress ipAddress) {
// TODO implement
throw new NotImplementedException("getMatchingInteface is not yet implemented");
checkNotNull(ipAddress);
for (PortAddresses portAddresses : hostService.getAddressBindings()) {
for (IpPrefix p : portAddresses.ips()) {
if (p.contains(ipAddress)) {
return new Interface(portAddresses);
}
}
}
return null;
}
}
......
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 {
......
......@@ -64,6 +64,7 @@ public class SdnIp implements SdnIpService {
bgpSessionManager.startUp(2000); // TODO
// TODO need to disable link discovery on external ports
}
@Deactivate
......
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.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.host.PortAddresses;
import org.onlab.onos.sdnip.config.Interface;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Unit tests for the HostToInterfaceAdaptor class.
*/
public class HostToInterfaceAdaptorTest {
private HostService hostService;
private HostToInterfaceAdaptor adaptor;
private Set<PortAddresses> portAddresses;
private Map<ConnectPoint, Interface> interfaces;
private static final ConnectPoint CP1 = new ConnectPoint(
DeviceId.deviceId("of:1"), PortNumber.portNumber(1));
private static final ConnectPoint CP2 = new ConnectPoint(
DeviceId.deviceId("of:1"), PortNumber.portNumber(2));
private static final ConnectPoint CP3 = new ConnectPoint(
DeviceId.deviceId("of:2"), PortNumber.portNumber(1));
private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint(
DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1));
private static final PortAddresses DEFAULT_PA = new PortAddresses(
NON_EXISTENT_CP, null, null);
@Before
public void setUp() throws Exception {
hostService = createMock(HostService.class);
portAddresses = Sets.newHashSet();
interfaces = Maps.newHashMap();
createPortAddressesAndInterface(CP1,
Sets.newHashSet(IpPrefix.valueOf("192.168.1.1/24")),
MacAddress.valueOf("00:00:00:00:00:01"));
// Two addresses in the same subnet
createPortAddressesAndInterface(CP2,
Sets.newHashSet(IpPrefix.valueOf("192.168.2.1/24"),
IpPrefix.valueOf("192.168.2.2/24")),
MacAddress.valueOf("00:00:00:00:00:02"));
// Two addresses in different subnets
createPortAddressesAndInterface(CP3,
Sets.newHashSet(IpPrefix.valueOf("192.168.3.1/24"),
IpPrefix.valueOf("192.168.4.1/24")),
MacAddress.valueOf("00:00:00:00:00:03"));
expect(hostService.getAddressBindings()).andReturn(portAddresses).anyTimes();
replay(hostService);
adaptor = new HostToInterfaceAdaptor(hostService);
}
/**
* Creates both a PortAddresses and an Interface for the given inputs and
* places them in the correct global data stores.
*
* @param cp the connect point
* @param ips the set of IP addresses
* @param mac the MAC address
*/
private void createPortAddressesAndInterface(
ConnectPoint cp, Set<IpPrefix> ips, MacAddress mac) {
PortAddresses pa = new PortAddresses(cp, ips, mac);
portAddresses.add(pa);
expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes();
Interface intf = new Interface(cp, ips, mac);
interfaces.put(cp, intf);
}
/**
* Tests {@link HostToInterfaceAdaptor#getInterfaces()}.
* Verifies that the set of interfaces returned matches what is expected
* based on the input PortAddresses data.
*/
@Test
public void testGetInterfaces() {
Set<Interface> adaptorIntfs = adaptor.getInterfaces();
assertEquals(3, adaptorIntfs.size());
assertTrue(adaptorIntfs.contains(this.interfaces.get(CP1)));
assertTrue(adaptorIntfs.contains(this.interfaces.get(CP2)));
assertTrue(adaptorIntfs.contains(this.interfaces.get(CP3)));
}
/**
* Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)}.
* Verifies that the correct interface is returned for a given connect
* point.
*/
@Test
public void testGetInterface() {
assertEquals(this.interfaces.get(CP1), adaptor.getInterface(CP1));
assertEquals(this.interfaces.get(CP2), adaptor.getInterface(CP2));
assertEquals(this.interfaces.get(CP3), adaptor.getInterface(CP3));
// Try and get an interface for a connect point with no addresses
reset(hostService);
expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP))
.andReturn(DEFAULT_PA).anyTimes();
replay(hostService);
assertNull(adaptor.getInterface(NON_EXISTENT_CP));
}
/**
* Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)} in the
* case that the input connect point is null.
* Verifies that a NullPointerException is thrown.
*/
@Test(expected = NullPointerException.class)
public void testGetInterfaceNull() {
adaptor.getInterface(null);
}
/**
* Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)}.
* Verifies that the correct interface is returned based on the given IP
* address.
*/
@Test
public void testGetMatchingInterface() {
assertEquals(this.interfaces.get(CP1),
adaptor.getMatchingInterface(IpAddress.valueOf("192.168.1.100")));
assertEquals(this.interfaces.get(CP2),
adaptor.getMatchingInterface(IpAddress.valueOf("192.168.2.100")));
assertEquals(this.interfaces.get(CP3),
adaptor.getMatchingInterface(IpAddress.valueOf("192.168.3.100")));
assertEquals(this.interfaces.get(CP3),
adaptor.getMatchingInterface(IpAddress.valueOf("192.168.4.100")));
// Try and match an address we don't have subnet configured for
assertNull(adaptor.getMatchingInterface(IpAddress.valueOf("1.1.1.1")));
}
/**
* Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)} in the
* case that the input IP address is null.
* Verifies that a NullPointerException is thrown.
*/
@Test(expected = NullPointerException.class)
public void testGetMatchingInterfaceNull() {
adaptor.getMatchingInterface(null);
}
}
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);
}
}
......@@ -18,10 +18,13 @@
*/
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Option;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.CoreService;
import org.onlab.onos.net.Annotations;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceNotFoundException;
......@@ -76,6 +79,34 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport {
}
/**
* Produces a string image of the specified key/value annotations.
*
* @param annotations key/value annotations
* @return string image with ", k1=v1, k2=v2, ..." pairs
*/
public static String annotations(Annotations annotations) {
StringBuilder sb = new StringBuilder();
for (String key : annotations.keys()) {
sb.append(", ").append(key).append('=').append(annotations.value(key));
}
return sb.toString();
}
/**
* Produces a JSON object from the specified key/value annotations.
*
* @param annotations key/value annotations
* @return JSON object
*/
public static ObjectNode annotations(ObjectMapper mapper, Annotations annotations) {
ObjectNode result = mapper.createObjectNode();
for (String key : annotations.keys()) {
result.put(key, annotations.value(key));
}
return result;
}
/**
* Executes this command.
*/
protected abstract void execute();
......
......@@ -43,7 +43,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
description = "Lists all ports or all ports of a device")
public class DevicePortsListCommand extends DevicesListCommand {
private static final String FMT = " port=%s, state=%s";
private static final String FMT = " port=%s, state=%s%s";
@Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports",
required = false, multiValued = false)
......@@ -112,7 +112,8 @@ public class DevicePortsListCommand extends DevicesListCommand {
if (isIncluded(port)) {
ports.add(mapper.createObjectNode()
.put("port", port.number().toString())
.put("isEnabled", port.isEnabled()));
.put("isEnabled", port.isEnabled())
.set("annotations", annotations(mapper, port.annotations())));
}
}
return result.put("device", device.id().toString()).set("ports", ports);
......@@ -131,7 +132,8 @@ public class DevicePortsListCommand extends DevicesListCommand {
Collections.sort(ports, Comparators.PORT_COMPARATOR);
for (Port port : ports) {
if (isIncluded(port)) {
print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled");
print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled",
annotations(port.annotations()));
}
}
}
......
......@@ -41,7 +41,7 @@ import static com.google.common.collect.Lists.newArrayList;
public class DevicesListCommand extends AbstractShellCommand {
private static final String FMT =
"id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s";
"id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s%s";
@Override
protected void execute() {
......@@ -89,7 +89,8 @@ public class DevicesListCommand extends AbstractShellCommand {
.put("mfr", device.manufacturer())
.put("hw", device.hwVersion())
.put("sw", device.swVersion())
.put("serial", device.serialNumber());
.put("serial", device.serialNumber())
.set("annotations", annotations(mapper, device.annotations()));
}
return result;
}
......@@ -117,7 +118,7 @@ public class DevicesListCommand extends AbstractShellCommand {
print(FMT, device.id(), service.isAvailable(device.id()),
service.getRole(device.id()), device.type(),
device.manufacturer(), device.hwVersion(), device.swVersion(),
device.serialNumber());
device.serialNumber(), annotations(device.annotations()));
}
}
......
/*
* 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.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.statistic.Load;
import org.onlab.onos.net.statistic.StatisticService;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
/**
* Fetches statistics.
*/
@Command(scope = "onos", name = "get-stats",
description = "Fetches stats for a connection point")
public class GetStatistics extends AbstractShellCommand {
@Argument(index = 0, name = "connectPoint",
description = "Device/Port Description",
required = true, multiValued = false)
String connectPoint = null;
@Override
protected void execute() {
StatisticService service = get(StatisticService.class);
DeviceId ingressDeviceId = deviceId(getDeviceId(connectPoint));
PortNumber ingressPortNumber = portNumber(getPortNumber(connectPoint));
ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
Load load = service.load(cp);
print("Load on %s -> %s", cp, load);
}
/**
* Extracts the port number portion of the ConnectPoint.
*
* @param deviceString string representing the device/port
* @return port number as a string, empty string if the port is not found
*/
private String getPortNumber(String deviceString) {
int slash = deviceString.indexOf('/');
if (slash <= 0) {
return "";
}
return deviceString.substring(slash + 1, deviceString.length());
}
/**
* Extracts the device ID portion of the ConnectPoint.
*
* @param deviceString string representing the device/port
* @return device ID string
*/
private String getDeviceId(String deviceString) {
int slash = deviceString.indexOf('/');
if (slash <= 0) {
return "";
}
return deviceString.substring(0, slash);
}
}
......@@ -42,7 +42,7 @@ import static com.google.common.collect.Lists.newArrayList;
public class HostsListCommand extends AbstractShellCommand {
private static final String FMT =
"id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s";
"id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s%s";
@Override
protected void execute() {
......@@ -80,6 +80,7 @@ public class HostsListCommand extends AbstractShellCommand {
.put("vlan", host.vlan().toString());
result.set("location", loc);
result.set("ips", ips);
result.set("annotations", annotations(mapper, host.annotations()));
return result;
}
......@@ -105,7 +106,8 @@ public class HostsListCommand extends AbstractShellCommand {
print(FMT, host.id(), host.mac(),
host.location().deviceId(),
host.location().port(),
host.vlan(), host.ipAddresses());
host.vlan(), host.ipAddresses(),
annotations(host.annotations()));
}
}
}
......
......@@ -38,7 +38,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
description = "Lists all infrastructure links")
public class LinksListCommand extends AbstractShellCommand {
private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s";
private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s%s";
private static final String COMPACT = "%s/%s-%s/%s";
@Argument(index = 0, name = "uri", description = "Device ID",
......@@ -85,6 +85,7 @@ public class LinksListCommand extends AbstractShellCommand {
ObjectNode result = mapper.createObjectNode();
result.set("src", json(mapper, link.src()));
result.set("dst", json(mapper, link.dst()));
result.set("annotations", annotations(mapper, link.annotations()));
return result;
}
......@@ -109,7 +110,8 @@ public class LinksListCommand extends AbstractShellCommand {
*/
public static String linkString(Link link) {
return String.format(FMT, link.src().deviceId(), link.src().port(),
link.dst().deviceId(), link.dst().port(), link.type());
link.dst().deviceId(), link.dst().port(), link.type(),
annotations(link.annotations()));
}
/**
......
......@@ -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())
......
......@@ -119,6 +119,12 @@
</optional-completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.GetStatistics"/>
<completers>
<ref component-id="connectPointCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/>
<completers>
<ref component-id="connectPointCompleter"/>
......
package org.onlab.onos.mastership;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.cluster.RoleInfo;
import org.onlab.onos.event.AbstractEvent;
import org.onlab.onos.net.DeviceId;
......@@ -56,19 +55,6 @@ public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceI
}
/**
* Returns the NodeID of the node associated with the event.
* For MASTER_CHANGED this is the newly elected master, and for
* BACKUPS_CHANGED, this is the node that was newly added, removed, or
* whose position was changed in the list.
*
* @return node ID as a subject
*/
//XXX to-be removed - or keep for convenience?
public NodeId node() {
return roleInfo.master();
}
/**
* Returns the current role state for the subject.
*
* @return RoleInfo associated with Device ID subject
......
......@@ -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
}
......
......@@ -4,6 +4,8 @@ import org.onlab.onos.event.AbstractEvent;
import org.onlab.onos.net.Device;
import org.onlab.onos.net.Port;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* Describes infrastructure device event.
*/
......@@ -109,4 +111,12 @@ public class DeviceEvent extends AbstractEvent<DeviceEvent.Type, Device> {
return port;
}
@Override
public String toString() {
if (port == null) {
return super.toString();
}
return toStringHelper(this).add("time", time()).add("type", type())
.add("subject", subject()).add("port", port).toString();
}
}
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/*
* 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.net.flow;
import static com.google.common.base.Preconditions.checkNotNull;
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/*
* 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.net.flow;
import java.util.Objects;
......
/*
* 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.net.flow;
import java.util.List;
......
/*
* 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.net.flow;
/**
* An interface of the class which is assigned to BatchOperation.
*/
public interface BatchOperationTarget {
}
/*
* 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.net.flow;
import java.util.List;
......
/*
* 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.net.flow;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.net.flow;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.net.flow;
import java.util.HashMap;
......
/*
* 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.net.flow;
import java.util.LinkedList;
......
/*
* 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.net.flow;
......
/*
* 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.net.flow;
import com.google.common.base.Objects;
......
/*
* 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.net.flow;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.intent.BatchOperationTarget;
/**
* Represents a generalized match &amp; action pair to be applied to
......
/*
* 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.net.flow;
import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
import org.onlab.onos.net.intent.BatchOperationEntry;
public class FlowRuleBatchEntry
......
/*
* 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.net.flow;
import java.util.Collection;
import org.onlab.onos.net.intent.BatchOperation;
public class FlowRuleBatchOperation
extends BatchOperation<FlowRuleBatchEntry> {
......
/*
* 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.net.flow;
import org.onlab.onos.event.AbstractEvent;
......
/*
* 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.net.flow;
import org.onlab.onos.event.EventListener;
......
/*
* 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.net.flow;
import java.util.concurrent.Future;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.provider.Provider;
/**
......
/*
* 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.net.flow;
import org.onlab.onos.net.provider.ProviderRegistry;
......
/*
* 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.net.flow;
import org.onlab.onos.net.DeviceId;
......
/*
* 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.net.flow;
import java.util.concurrent.Future;
......
/*
* 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.net.flow;
import org.onlab.onos.ApplicationId;
......
/*
* 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.net.flow;
import org.onlab.onos.store.StoreDelegate;
......
/*
* 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.net.flow;
......
/*
* 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.net.flow;
import java.util.Set;
......
/*
* 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.net.flow;
import java.util.List;
......
/*
* 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.net.flow;
import org.onlab.onos.net.PortNumber;
......
/*
* 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.net.flow.criteria;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.net.flow.criteria;
......
/*
* 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.
*/
/**
* Traffic selection criteria model.
*/
......
/*
* 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.net.flow.instructions;
/**
......
/*
* 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.net.flow.instructions;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.net.flow.instructions;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.net.flow.instructions;
import static com.google.common.base.MoreObjects.toStringHelper;
......
/*
* 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.
*/
/**
* Traffic treatment model.
*/
......
/*
* 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.
*/
/**
* Flow rule model &amp; related services API definitions.
*/
......
package org.onlab.onos.net.intent;
//TODO is this the right package?
/**
* An interface of the class which is assigned to BatchOperation.
*/
public interface BatchOperationTarget {
}
/*
* 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.net.intent;
import com.google.common.collect.ImmutableSet;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.net.intent;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.net.NetworkResource;
import org.onlab.onos.net.flow.BatchOperationTarget;
import java.util.Collection;
import java.util.Objects;
......
package org.onlab.onos.net.intent;
/**
* A list of intent operations.
*/
public class IntentBatchOperation extends
BatchOperation<BatchOperationEntry<IntentBatchOperation.Operator, ?>> {
/**
* The intent operators.
*/
public enum Operator {
ADD,
REMOVE,
}
/**
* Adds an add-intent operation.
*
* @param intent the intent to be added
* @return the IntentBatchOperation object if succeeded, null otherwise
*/
public IntentBatchOperation addAddIntentOperation(Intent intent) {
return (null == super.addOperation(
new BatchOperationEntry<Operator, Intent>(Operator.ADD, intent)))
? null : this;
}
/**
* Adds a remove-intent operation.
*
* @param id the ID of intent to be removed
* @return the IntentBatchOperation object if succeeded, null otherwise
*/
public IntentBatchOperation addRemoveIntentOperation(IntentId id) {
return (null == super.addOperation(
new BatchOperationEntry<Operator, IntentId>(Operator.REMOVE, id)))
? null : this;
}
}
/*
* 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.net.intent;
import java.util.List;
......
/*
* 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.net.intent;
import org.onlab.onos.event.AbstractEvent;
......
/*
* 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.net.intent;
/**
......
/*
* 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.net.intent;
import java.util.Map;
......
/*
* 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.net.intent;
import org.onlab.onos.net.flow.BatchOperationTarget;
/**
* Intent identifier suitable as an external key.
* <p/>
......
/*
* 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.net.intent;
import java.util.List;
......
/*
* 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.net.intent;
import org.onlab.onos.event.EventListener;
......
/*
* 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.net.intent;
/**
* Abstraction of an intent-related operation, e.g. add, remove, replace.
*/
public class IntentOperation {
private final Type type;
private final IntentId intentId;
private final Intent intent;
/**
* Operation type.
*/
enum Type {
/**
* Indicates that an intent should be added.
*/
SUBMIT,
/**
* Indicates that an intent should be removed.
*/
WITHDRAW,
/**
* Indicates that an intent should be replaced with another.
*/
REPLACE
}
/**
* Creates an intent operation.
*
* @param type operation type
* @param intentId identifier of the intent subject to the operation
* @param intent intent subject
*/
IntentOperation(Type type, IntentId intentId, Intent intent) {
this.type = type;
this.intentId = intentId;
this.intent = intent;
}
/**
* Returns the type of the operation.
*
* @return operation type
*/
public Type type() {
return type;
}
/**
* Returns the identifier of the intent to which this operation applies.
*
* @return intent identifier
*/
public IntentId intentId() {
return intentId;
}
/**
* Returns the intent to which this operation applied. For remove,
* this can be null.
*
* @return intent that is the subject of the operation; null for remove
*/
public Intent intent() {
return intent;
}
}
/*
* 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.net.intent;
import com.google.common.collect.ImmutableList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.onos.net.intent.IntentOperation.Type.REPLACE;
import static org.onlab.onos.net.intent.IntentOperation.Type.SUBMIT;
import static org.onlab.onos.net.intent.IntentOperation.Type.WITHDRAW;
/**
* Abstraction of a batch of intent submit/withdraw operations.
* Batch of intent submit/withdraw/replace operations.
*/
public interface IntentOperations {
public final class IntentOperations {
private final List<IntentOperation> operations;
/**
* Creates a batch of intent operations using the supplied list.
*
* @param operations list of intent operations
*/
private IntentOperations(List<IntentOperation> operations) {
this.operations = operations;
}
/**
* List of operations that need to be executed as a unit.
*
* @return list of intent operations
*/
public List<IntentOperation> operations() {
return operations;
}
/**
* Returns a builder for intent operation batches.
*
* @return intent operations builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder for batches of intent operations.
*/
public static final class Builder {
private final ImmutableList.Builder<IntentOperation> builder = ImmutableList.builder();
// Public construction is forbidden.
private Builder() {
}
/**
* Adds an intent submit operation.
*
* @param intent intent to be submitted
* @return self
*/
public Builder addSubmitOperation(Intent intent) {
checkNotNull(intent, "Intent cannot be null");
builder.add(new IntentOperation(SUBMIT, intent.id(), intent));
return this;
}
/**
* Adds an intent submit operation.
*
* @param oldIntentId intent to be replaced
* @param newIntent replacement intent
* @return self
*/
public Builder addReplaceOperation(IntentId oldIntentId, Intent newIntent) {
checkNotNull(oldIntentId, "Intent ID cannot be null");
checkNotNull(newIntent, "Intent cannot be null");
builder.add(new IntentOperation(REPLACE, oldIntentId, newIntent));
return this;
}
/**
* Adds an intent submit operation.
*
* @param intentId identifier of the intent to be withdrawn
* @return self
*/
public Builder addWithdrawOperation(IntentId intentId) {
checkNotNull(intentId, "Intent ID cannot be null");
builder.add(new IntentOperation(WITHDRAW, intentId, null));
return this;
}
// TODO: elaborate once the revised BatchOperation scheme is in place
/**
* Builds a batch of intent operations.
*
* @return immutable batch of intent operations
*/
public IntentOperations build() {
return new IntentOperations(builder.build());
}
}
}
......
/*
* 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.net.intent;
import java.util.List;
import java.util.concurrent.Future;
/**
* Service for application submitting or withdrawing their intents.
......@@ -28,6 +47,14 @@ public interface IntentService {
void withdraw(Intent intent);
/**
* Replaces the specified intent with a new one.
*
* @param oldIntentId identifier of the old intent being replaced
* @param newIntent new intent replacing the old one
*/
void replace(IntentId oldIntentId, Intent newIntent);
/**
* Submits a batch of submit &amp; withdraw operations. Such a batch is
* assumed to be processed together.
* <p/>
......@@ -36,7 +63,7 @@ public interface IntentService {
*
* @param operations batch of intent operations
*/
void execute(IntentOperations operations);
Future<IntentOperations> execute(IntentOperations operations);
/**
* Returns an iterable of intents currently in the system.
......
/*
* 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.net.intent;
/**
......
/*
* 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.net.intent;
import org.onlab.onos.store.Store;
......
/*
* 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.net.intent;
import org.onlab.onos.store.StoreDelegate;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.net.intent;
import com.google.common.base.MoreObjects;
......
/*
* 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.
*/
/**
* Set of abstractions for conveying high-level intents for treatment of
* selected network traffic by allowing applications to express the
......
package org.onlab.onos.net.statistic;
import com.google.common.base.MoreObjects;
import org.onlab.onos.net.flow.FlowRuleProvider;
/**
* Implementation of a load.
*/
public class DefaultLoad implements Load {
private final boolean isValid;
private final long current;
private final long previous;
private final long time;
/**
* Creates an invalid load.
*/
public DefaultLoad() {
this.isValid = false;
this.time = System.currentTimeMillis();
this.current = -1;
this.previous = -1;
}
/**
* Creates a load value from the parameters.
* @param current the current value
* @param previous the previous value
*/
public DefaultLoad(long current, long previous) {
this.current = current;
this.previous = previous;
this.time = System.currentTimeMillis();
this.isValid = true;
}
@Override
public long rate() {
return (current - previous) / FlowRuleProvider.POLL_INTERVAL;
}
@Override
public long latest() {
return current;
}
@Override
public boolean isValid() {
return isValid;
}
@Override
public long time() {
return time;
}
@Override
public String toString() {
return MoreObjects.toStringHelper("Load").add("rate", rate())
.add("latest", latest()).toString();
}
}
......@@ -6,15 +6,27 @@ package org.onlab.onos.net.statistic;
public interface Load {
/**
* Obtain the current observed rate on a link.
* Obtain the current observed rate (in bytes/s) on a link.
* @return long value
*/
long rate();
/**
* Obtain the latest counter viewed on that link.
* Obtain the latest bytes counter viewed on that link.
* @return long value
*/
long latest();
/**
* Indicates whether this load was built on valid values.
* @return boolean
*/
boolean isValid();
/**
* Returns when this value was seen.
* @return epoch time
*/
long time();
}
......
......@@ -7,4 +7,9 @@ import org.onlab.onos.net.provider.Provider;
*/
public interface TopologyProvider extends Provider {
/**
* Triggers topology recomputation.
*/
void triggerRecompute();
}
......
package org.onlab.onos.store.cluster.messaging;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.onlab.onos.cluster.NodeId;
public interface ClusterMessageResponse {
public interface ClusterMessageResponse extends Future<byte[]> {
public NodeId sender();
public byte[] get(long timeout, TimeUnit timeunit) throws TimeoutException;
public byte[] get(long timeout) throws InterruptedException;
// TODO InterruptedException, ExecutionException removed from original
// Future declaration. Revisit if we ever need those.
@Override
public byte[] get(long timeout, TimeUnit unit) throws TimeoutException;
}
......
......@@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Fake implementation of the intent service to assist in developing tests of
......@@ -171,11 +172,17 @@ public class FakeIntentManager implements TestableIntentService {
}
@Override
public void execute(IntentOperations operations) {
public void replace(IntentId oldIntentId, Intent newIntent) {
// TODO: implement later
}
@Override
public Future<IntentOperations> execute(IntentOperations operations) {
// TODO: implement later
return null;
}
@Override
public Set<Intent> getIntents() {
return Collections.unmodifiableSet(new HashSet<>(intents.values()));
}
......
......@@ -227,10 +227,14 @@ implements MastershipService, MastershipAdminService {
if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
return true;
}
//else {
// else {
//FIXME: break tie for equal-sized clusters, by number of
// connected switches, then masters, then nodeId hash
// }
// problem is, how do we get at channel info cleanly here?
// Also, what's the time hit for a distributed store look-up
// versus channel re-negotiation? bet on the latter being worse.
// }
return false;
}
......
......@@ -108,6 +108,9 @@ public class FlowRuleManager
if (local) {
// TODO: aggregate all local rules and push down once?
applyFlowRulesToProviders(f);
eventDispatcher.post(
new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, f));
}
}
}
......@@ -136,6 +139,8 @@ public class FlowRuleManager
if (local) {
// TODO: aggregate all local rules and push down once?
removeFlowRulesFromProviders(f);
eventDispatcher.post(
new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, f));
}
}
}
......
......@@ -126,7 +126,13 @@ public class IntentManager
// FIXME: implement this method
@Override
public void execute(IntentOperations operations) {
public void replace(IntentId oldIntentId, Intent newIntent) {
throw new UnsupportedOperationException("execute() is not implemented yet");
}
// FIXME: implement this method
@Override
public Future<IntentOperations> execute(IntentOperations operations) {
throw new UnsupportedOperationException("execute() is not implemented yet");
}
......
......@@ -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);
}
}
......
......@@ -68,7 +68,9 @@ implements PacketService, PacketProviderRegistry {
checkNotNull(packet, "Packet cannot be null");
final Device device = deviceService.getDevice(packet.sendThrough());
final PacketProvider packetProvider = getProvider(device.providerId());
packetProvider.emit(packet);
if (packetProvider != null) {
packetProvider.emit(packet);
}
}
@Override
......
......@@ -10,14 +10,17 @@ import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.FlowRuleEvent;
import org.onlab.onos.net.flow.FlowRuleListener;
import org.onlab.onos.net.flow.FlowRuleService;
import org.onlab.onos.net.statistic.DefaultLoad;
import org.onlab.onos.net.statistic.Load;
import org.onlab.onos.net.statistic.StatisticService;
import org.onlab.onos.net.statistic.StatisticStore;
import org.slf4j.Logger;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -54,27 +57,91 @@ public class StatisticManager implements StatisticService {
@Override
public Load load(Link link) {
return null;
return load(link.src());
}
@Override
public Load load(ConnectPoint connectPoint) {
return null;
return loadInternal(connectPoint);
}
@Override
public Link max(Path path) {
return null;
if (path.links().isEmpty()) {
return null;
}
Load maxLoad = new DefaultLoad();
Link maxLink = null;
for (Link link : path.links()) {
Load load = loadInternal(link.src());
if (load.rate() > maxLoad.rate()) {
maxLoad = load;
maxLink = link;
}
}
return maxLink;
}
@Override
public Link min(Path path) {
return null;
if (path.links().isEmpty()) {
return null;
}
Load minLoad = new DefaultLoad();
Link minLink = null;
for (Link link : path.links()) {
Load load = loadInternal(link.src());
if (load.rate() < minLoad.rate()) {
minLoad = load;
minLink = link;
}
}
return minLink;
}
@Override
public FlowRule highestHitter(ConnectPoint connectPoint) {
return null;
Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint);
if (hitters.isEmpty()) {
return null;
}
FlowEntry max = hitters.iterator().next();
for (FlowEntry entry : hitters) {
if (entry.bytes() > max.bytes()) {
max = entry;
}
}
return max;
}
private Load loadInternal(ConnectPoint connectPoint) {
Set<FlowEntry> current;
Set<FlowEntry> previous;
synchronized (statisticStore) {
current = statisticStore.getCurrentStatistic(connectPoint);
previous = statisticStore.getPreviousStatistic(connectPoint);
}
if (current == null || previous == null) {
return new DefaultLoad();
}
long currentAggregate = aggregate(current);
long previousAggregate = aggregate(previous);
return new DefaultLoad(currentAggregate, previousAggregate);
}
/**
* Aggregates a set of values.
* @param values the values to aggregate
* @return a long value
*/
private long aggregate(Set<FlowEntry> values) {
long sum = 0;
for (FlowEntry f : values) {
sum += f.bytes();
}
return sum;
}
/**
......@@ -84,22 +151,29 @@ public class StatisticManager implements StatisticService {
@Override
public void event(FlowRuleEvent event) {
// FlowRule rule = event.subject();
// switch (event.type()) {
// case RULE_ADDED:
// case RULE_UPDATED:
// if (rule instanceof FlowEntry) {
// statisticStore.addOrUpdateStatistic((FlowEntry) rule);
// }
// break;
// case RULE_ADD_REQUESTED:
// statisticStore.prepareForStatistics(rule);
// break;
// case RULE_REMOVE_REQUESTED:
// case RULE_REMOVED:
// statisticStore.removeFromStatistics(rule);
// break;
// }
FlowRule rule = event.subject();
switch (event.type()) {
case RULE_ADDED:
case RULE_UPDATED:
if (rule instanceof FlowEntry) {
statisticStore.addOrUpdateStatistic((FlowEntry) rule);
} else {
log.warn("IT AIN'T A FLOWENTRY");
}
break;
case RULE_ADD_REQUESTED:
log.info("Preparing for stats");
statisticStore.prepareForStatistics(rule);
break;
case RULE_REMOVE_REQUESTED:
log.info("Removing stats");
statisticStore.removeFromStatistics(rule);
break;
case RULE_REMOVED:
break;
default:
log.warn("Unknown flow rule event {}", 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());
}
}
}
......
package org.onlab.onos.net.flow.impl;
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
......@@ -45,7 +50,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.flow.BatchOperation;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
......@@ -164,7 +169,8 @@ public class FlowRuleManagerTest {
assertEquals("2 rules should exist", 2, flowCount());
providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
validateEvents(RULE_ADDED, RULE_ADDED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
RULE_ADDED, RULE_ADDED);
addFlowRule(1);
assertEquals("should still be 2 rules", 2, flowCount());
......@@ -218,11 +224,12 @@ public class FlowRuleManagerTest {
FlowEntry fe2 = new DefaultFlowEntry(f2);
FlowEntry fe3 = new DefaultFlowEntry(f3);
providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3));
validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
RULE_ADDED, RULE_ADDED, RULE_ADDED);
mgr.removeFlowRules(f1, f2);
//removing from north, so no events generated
validateEvents();
validateEvents(RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED);
assertEquals("3 rule should exist", 3, flowCount());
assertTrue("Entries should be pending remove.",
validateState(ImmutableMap.of(
......@@ -244,7 +251,8 @@ public class FlowRuleManagerTest {
service.removeFlowRules(f1);
fe1.setState(FlowEntryState.REMOVED);
providerService.flowRemoved(fe1);
validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED,
RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED);
providerService.flowRemoved(fe1);
validateEvents();
......@@ -253,7 +261,7 @@ public class FlowRuleManagerTest {
FlowEntry fe3 = new DefaultFlowEntry(f3);
service.applyFlowRules(f3);
providerService.pushFlowMetrics(DID, Collections.singletonList(fe3));
validateEvents(RULE_ADDED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADDED);
providerService.flowRemoved(fe3);
validateEvents();
......@@ -282,7 +290,8 @@ public class FlowRuleManagerTest {
f2, FlowEntryState.ADDED,
f3, FlowEntryState.PENDING_ADD)));
validateEvents(RULE_ADDED, RULE_ADDED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
RULE_ADDED, RULE_ADDED);
}
@Test
......@@ -302,7 +311,7 @@ public class FlowRuleManagerTest {
providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3));
validateEvents(RULE_ADDED, RULE_ADDED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, RULE_ADDED);
}
......@@ -327,7 +336,8 @@ public class FlowRuleManagerTest {
providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED);
validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED);
}
......
......@@ -195,6 +195,10 @@ public class TopologyManagerTest {
public TestProvider() {
super(PID);
}
@Override
public void triggerRecompute() {
}
}
private static class TestListener implements TopologyListener {
......
......@@ -4,9 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -181,10 +181,13 @@ public class ClusterCommunicationManager
}
}
private static final class InternalClusterMessageResponse implements ClusterMessageResponse {
private static final class InternalClusterMessageResponse
implements ClusterMessageResponse {
private final NodeId sender;
private final Response responseFuture;
private volatile boolean isCancelled = false;
private volatile boolean isDone = false;
public InternalClusterMessageResponse(NodeId sender, Response responseFuture) {
this.sender = sender;
......@@ -198,12 +201,39 @@ public class ClusterCommunicationManager
@Override
public byte[] get(long timeout, TimeUnit timeunit)
throws TimeoutException {
return responseFuture.get(timeout, timeunit);
final byte[] result = responseFuture.get(timeout, timeunit);
isDone = true;
return result;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (isDone()) {
return false;
}
// doing nothing for now
// when onlab.netty Response support cancel, call them.
isCancelled = true;
return true;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public boolean isDone() {
return this.isDone || isCancelled();
}
@Override
public byte[] get(long timeout) throws InterruptedException {
return responseFuture.get();
public byte[] get() throws InterruptedException, ExecutionException {
// TODO: consider forbidding this call and force the use of timed get
// to enforce handling of remote peer failure scenario
final byte[] result = responseFuture.get();
isDone = true;
return result;
}
}
}
......
......@@ -290,12 +290,17 @@ public class GossipDeviceStore
private DeviceEvent updateDevice(ProviderId providerId,
Device oldDevice,
Device newDevice, Timestamp newTimestamp) {
// We allow only certain attributes to trigger update
if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
!Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
!AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
boolean propertiesChanged =
!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
!Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
boolean annotationsChanged =
!AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
// Primary providers can respond to all changes, but ancillary ones
// should respond only to annotation changes.
if ((providerId.isAncillary() && annotationsChanged) ||
(!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
if (!replaced) {
verify(replaced,
......
......@@ -86,7 +86,7 @@ public class ReplicaInfoManager implements ReplicaInfoService {
final List<NodeId> standbyList = Collections.<NodeId>emptyList();
eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED,
event.subject(),
new ReplicaInfo(event.node(), standbyList)));
new ReplicaInfo(event.roleInfo().master(), standbyList)));
}
}
......
package org.onlab.onos.store.statistic.impl;
import static org.onlab.onos.store.statistic.impl.StatisticStoreMessageSubjects.*;
import static org.slf4j.LoggerFactory.getLogger;
import com.google.common.collect.Sets;
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.onos.cluster.ClusterService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowRule;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions;
import org.onlab.onos.net.statistic.StatisticStore;
import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
import org.onlab.onos.store.cluster.messaging.ClusterMessage;
import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
import org.onlab.onos.store.cluster.messaging.ClusterMessageResponse;
import org.onlab.onos.store.flow.ReplicaInfo;
import org.onlab.onos.store.flow.ReplicaInfoService;
import org.onlab.onos.store.serializers.KryoNamespaces;
import org.onlab.onos.store.serializers.KryoSerializer;
import org.onlab.util.KryoNamespace;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Maintains statistics using RPC calls to collect stats from remote instances
* on demand.
*/
@Component(immediate = true)
@Service
public class DistributedStatisticStore implements StatisticStore {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ReplicaInfoService replicaInfoManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ClusterCommunicationService clusterCommunicator;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ClusterService clusterService;
private Map<ConnectPoint, InternalStatisticRepresentation> representations =
new ConcurrentHashMap<>();
private Map<ConnectPoint, Set<FlowEntry>> previous =
new ConcurrentHashMap<>();
private Map<ConnectPoint, Set<FlowEntry>> current =
new ConcurrentHashMap<>();
protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
@Override
protected void setupKryoPool() {
serializerPool = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
// register this store specific classes here
.build()
.populate(1);
}
};;
private static final long STATISTIC_STORE_TIMEOUT_MILLIS = 3000;
@Activate
public void activate() {
clusterCommunicator.addSubscriber(GET_CURRENT, new ClusterMessageHandler() {
@Override
public void handle(ClusterMessage message) {
ConnectPoint cp = SERIALIZER.decode(message.payload());
try {
message.respond(SERIALIZER.encode(getCurrentStatisticInternal(cp)));
} catch (IOException e) {
log.error("Failed to respond back", e);
}
}
});
clusterCommunicator.addSubscriber(GET_PREVIOUS, new ClusterMessageHandler() {
@Override
public void handle(ClusterMessage message) {
ConnectPoint cp = SERIALIZER.decode(message.payload());
try {
message.respond(SERIALIZER.encode(getPreviousStatisticInternal(cp)));
} catch (IOException e) {
log.error("Failed to respond back", e);
}
}
});
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public void prepareForStatistics(FlowRule rule) {
ConnectPoint cp = buildConnectPoint(rule);
if (cp == null) {
return;
}
InternalStatisticRepresentation rep;
synchronized (representations) {
rep = getOrCreateRepresentation(cp);
}
rep.prepare();
}
@Override
public synchronized void removeFromStatistics(FlowRule rule) {
ConnectPoint cp = buildConnectPoint(rule);
if (cp == null) {
return;
}
InternalStatisticRepresentation rep = representations.get(cp);
if (rep != null) {
rep.remove(rule);
}
Set<FlowEntry> values = current.get(cp);
if (values != null) {
values.remove(rule);
}
values = previous.get(cp);
if (values != null) {
values.remove(rule);
}
}
@Override
public void addOrUpdateStatistic(FlowEntry rule) {
ConnectPoint cp = buildConnectPoint(rule);
if (cp == null) {
return;
}
InternalStatisticRepresentation rep = representations.get(cp);
if (rep != null && rep.submit(rule)) {
updatePublishedStats(cp, rep.get());
}
}
private synchronized void updatePublishedStats(ConnectPoint cp,
Set<FlowEntry> flowEntries) {
Set<FlowEntry> curr = current.get(cp);
if (curr == null) {
curr = new HashSet<>();
}
previous.put(cp, curr);
current.put(cp, flowEntries);
}
@Override
public Set<FlowEntry> getCurrentStatistic(ConnectPoint connectPoint) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(connectPoint.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
return getCurrentStatisticInternal(connectPoint);
} else {
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
GET_CURRENT,
SERIALIZER.encode(connectPoint));
try {
ClusterMessageResponse response =
clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
return SERIALIZER.decode(response.get(STATISTIC_STORE_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS));
} catch (IOException | TimeoutException e) {
// FIXME: throw a StatsStoreException
throw new RuntimeException(e);
}
}
}
private synchronized Set<FlowEntry> getCurrentStatisticInternal(ConnectPoint connectPoint) {
return current.get(connectPoint);
}
@Override
public Set<FlowEntry> getPreviousStatistic(ConnectPoint connectPoint) {
ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(connectPoint.deviceId());
if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
return getPreviousStatisticInternal(connectPoint);
} else {
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
GET_PREVIOUS,
SERIALIZER.encode(connectPoint));
try {
ClusterMessageResponse response =
clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
return SERIALIZER.decode(response.get(STATISTIC_STORE_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS));
} catch (IOException | TimeoutException e) {
// FIXME: throw a StatsStoreException
throw new RuntimeException(e);
}
}
}
private synchronized Set<FlowEntry> getPreviousStatisticInternal(ConnectPoint connectPoint) {
return previous.get(connectPoint);
}
private InternalStatisticRepresentation getOrCreateRepresentation(ConnectPoint cp) {
if (representations.containsKey(cp)) {
return representations.get(cp);
} else {
InternalStatisticRepresentation rep = new InternalStatisticRepresentation();
representations.put(cp, rep);
return rep;
}
}
private ConnectPoint buildConnectPoint(FlowRule rule) {
PortNumber port = getOutput(rule);
if (port == null) {
log.warn("Rule {} has no output.", rule);
return null;
}
ConnectPoint cp = new ConnectPoint(rule.deviceId(), port);
return cp;
}
private PortNumber getOutput(FlowRule rule) {
for (Instruction i : rule.treatment().instructions()) {
if (i.type() == Instruction.Type.OUTPUT) {
Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
return out.port();
}
if (i.type() == Instruction.Type.DROP) {
return PortNumber.P0;
}
}
return null;
}
private class InternalStatisticRepresentation {
private final AtomicInteger counter = new AtomicInteger(0);
private final Set<FlowEntry> rules = new HashSet<>();
public void prepare() {
counter.incrementAndGet();
}
public synchronized void remove(FlowRule rule) {
rules.remove(rule);
counter.decrementAndGet();
}
public synchronized boolean submit(FlowEntry rule) {
if (rules.contains(rule)) {
rules.remove(rule);
}
rules.add(rule);
if (counter.get() == 0) {
return true;
} else {
return counter.decrementAndGet() == 0;
}
}
public synchronized Set<FlowEntry> get() {
counter.set(rules.size());
return Sets.newHashSet(rules);
}
}
}
package org.onlab.onos.store.statistic.impl;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
/**
* MessageSubjects used by DistributedStatisticStore peer-peer communication.
*/
public final class StatisticStoreMessageSubjects {
private StatisticStoreMessageSubjects() {}
public static final MessageSubject GET_CURRENT =
new MessageSubject("peer-return-current");
public static final MessageSubject GET_PREVIOUS =
new MessageSubject("peer-return-previous");
}
/**
* Implementation of the statistic store.
*/
package org.onlab.onos.store.statistic.impl;
\ No newline at end of file
......@@ -91,23 +91,14 @@ implements MastershipStore {
@Override
public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
NodeId current = getNode(MASTER, deviceId);
if (current == null) {
if (isRole(STANDBY, nodeId, deviceId)) {
//was previously standby, or set to standby from master
return MastershipRole.STANDBY;
} else {
return MastershipRole.NONE;
}
} else {
if (current.equals(nodeId)) {
//*should* be in unusable, not always
return MastershipRole.MASTER;
} else {
//may be in backups or unusable from earlier retirement
return MastershipRole.STANDBY;
}
final RoleValue roleInfo = getRoleValue(deviceId);
if (roleInfo.contains(MASTER, nodeId)) {
return MASTER;
}
if (roleInfo.contains(STANDBY, nodeId)) {
return STANDBY;
}
return NONE;
}
@Override
......@@ -124,10 +115,11 @@ implements MastershipStore {
roleMap.put(deviceId, rv);
return null;
case STANDBY:
case NONE:
NodeId current = rv.get(MASTER);
if (current != null) {
//backup and replace current master
rv.reassign(nodeId, NONE, STANDBY);
rv.reassign(current, NONE, STANDBY);
rv.replace(current, nodeId, MASTER);
} else {
//no master before so just add.
......@@ -137,12 +129,6 @@ implements MastershipStore {
roleMap.put(deviceId, rv);
updateTerm(deviceId);
return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo());
case NONE:
rv.add(MASTER, nodeId);
rv.reassign(nodeId, STANDBY, NONE);
roleMap.put(deviceId, rv);
updateTerm(deviceId);
return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo());
default:
log.warn("unknown Mastership Role {}", role);
return null;
......@@ -193,21 +179,28 @@ implements MastershipStore {
switch (role) {
case MASTER:
rv.reassign(local, STANDBY, NONE);
terms.putIfAbsent(deviceId, INIT);
roleMap.put(deviceId, rv);
break;
case STANDBY:
rv.reassign(local, NONE, STANDBY);
roleMap.put(deviceId, rv);
terms.putIfAbsent(deviceId, INIT);
break;
case NONE:
//claim mastership
rv.add(MASTER, local);
rv.reassign(local, STANDBY, NONE);
//either we're the first standby, or first to device.
//for latter, claim mastership.
if (rv.get(MASTER) == null) {
rv.add(MASTER, local);
rv.reassign(local, STANDBY, NONE);
updateTerm(deviceId);
role = MastershipRole.MASTER;
} else {
rv.add(STANDBY, local);
rv.reassign(local, NONE, STANDBY);
role = MastershipRole.STANDBY;
}
roleMap.put(deviceId, rv);
updateTerm(deviceId);
role = MastershipRole.MASTER;
break;
default:
log.warn("unknown Mastership Role {}", role);
......@@ -315,7 +308,10 @@ implements MastershipStore {
RoleValue value = roleMap.get(deviceId);
if (value == null) {
value = new RoleValue();
roleMap.put(deviceId, value);
RoleValue concurrentlyAdded = roleMap.putIfAbsent(deviceId, value);
if (concurrentlyAdded != null) {
return concurrentlyAdded;
}
}
return value;
}
......@@ -329,16 +325,6 @@ implements MastershipStore {
return null;
}
//check if node is a certain role given a device
private boolean isRole(
MastershipRole role, NodeId nodeId, DeviceId deviceId) {
RoleValue value = roleMap.get(deviceId);
if (value != null) {
return value.contains(role, nodeId);
}
return false;
}
//adds or updates term information.
private void updateTerm(DeviceId deviceId) {
terms.lock(deviceId);
......
......@@ -97,6 +97,7 @@ public class DistributedMastershipStoreTest {
assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
testStore.put(DID1, N1, true, false, true);
assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
testStore.put(DID1, N2, false, true, false);
assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
}
......@@ -155,6 +156,7 @@ public class DistributedMastershipStoreTest {
//switch over to N2
assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
//orphan switch - should be rare case
......@@ -182,14 +184,9 @@ public class DistributedMastershipStoreTest {
assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
assertEquals("wrong master", N2, dms.getMaster(DID1));
//STANDBY - nothing here, either
assertNull("wrong event:", dms.relinquishRole(N1, DID1));
assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1));
//all nodes "give up" on device, which goes back to NONE.
assertNull("wrong event:", dms.relinquishRole(N2, DID1));
assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
assertEquals("wrong number of retired nodes", 2,
dms.roleMap.get(DID1).nodesOfRole(NONE).size());
......@@ -201,6 +198,10 @@ public class DistributedMastershipStoreTest {
assertEquals("wrong number of backup nodes", 1,
dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
//If STANDBY, should drop to NONE
assertNull("wrong event:", dms.relinquishRole(N1, DID1));
assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
//NONE - nothing happens
assertNull("wrong event:", dms.relinquishRole(N1, DID2));
assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
......@@ -218,7 +219,7 @@ public class DistributedMastershipStoreTest {
public void notify(MastershipEvent event) {
assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
assertEquals("wrong subject", DID1, event.subject());
assertEquals("wrong subject", N1, event.node());
assertEquals("wrong subject", N1, event.roleInfo().master());
addLatch.countDown();
}
};
......
......@@ -4,6 +4,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.DefaultControllerNode;
......@@ -26,9 +27,11 @@ import org.onlab.onos.net.Port;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DefaultDeviceDescription;
import org.onlab.onos.net.device.DefaultPortDescription;
import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.DefaultFlowRule;
import org.onlab.onos.net.flow.DefaultTrafficSelector;
import org.onlab.onos.net.flow.DefaultTrafficTreatment;
import org.onlab.onos.net.flow.FlowEntry;
import org.onlab.onos.net.flow.FlowId;
import org.onlab.onos.net.flow.criteria.Criteria;
import org.onlab.onos.net.flow.criteria.Criterion;
......@@ -75,6 +78,7 @@ public final class KryoNamespaces {
ArrayList.class,
Arrays.asList().getClass(),
HashMap.class,
HashSet.class,
//
//
ControllerNode.State.class,
......@@ -94,6 +98,8 @@ public final class KryoNamespaces {
HostDescription.class,
DefaultHostDescription.class,
DefaultFlowRule.class,
DefaultFlowEntry.class,
FlowEntry.FlowEntryState.class,
FlowId.class,
DefaultTrafficSelector.class,
Criteria.PortCriterion.class,
......
......@@ -70,15 +70,16 @@ public class SimpleDeviceStore
public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
// collection of Description given from various providers
// Collection of Description given from various providers
private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
deviceDescs = Maps.newConcurrentMap();
deviceDescs = Maps.newConcurrentMap();
// cache of Device and Ports generated by compositing descriptions from providers
// Cache of Device and Ports generated by compositing descriptions from providers
private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
devicePorts = Maps.newConcurrentMap();
// available(=UP) devices
// Available (=UP) devices
private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
......@@ -113,19 +114,17 @@ public class SimpleDeviceStore
@Override
public DeviceEvent createOrUpdateDevice(ProviderId providerId,
DeviceId deviceId,
DeviceDescription deviceDescription) {
DeviceId deviceId,
DeviceDescription deviceDescription) {
Map<ProviderId, DeviceDescriptions> providerDescs
= getOrCreateDeviceDescriptions(deviceId);
= getOrCreateDeviceDescriptions(deviceId);
synchronized (providerDescs) {
// locking per device
DeviceDescriptions descs
= getOrCreateProviderDeviceDescriptions(providerDescs,
providerId,
deviceDescription);
= getOrCreateProviderDeviceDescriptions(providerDescs,
providerId,
deviceDescription);
Device oldDevice = devices.get(deviceId);
// update description
......@@ -145,12 +144,11 @@ public class SimpleDeviceStore
// Creates the device and returns the appropriate event if necessary.
// Guarded by deviceDescs value (=Device lock)
private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
// update composed device cache
Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
verify(oldDevice == null,
"Unexpected Device in cache. PID:%s [old=%s, new=%s]",
providerId, oldDevice, newDevice);
"Unexpected Device in cache. PID:%s [old=%s, new=%s]",
providerId, oldDevice, newDevice);
if (!providerId.isAncillary()) {
availableDevices.add(newDevice.id());
......@@ -162,17 +160,24 @@ public class SimpleDeviceStore
// Updates the device and returns the appropriate event if necessary.
// Guarded by deviceDescs value (=Device lock)
private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
// We allow only certain attributes to trigger update
if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
!Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
!AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) {
boolean propertiesChanged =
!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
!Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
boolean annotationsChanged =
!AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
// Primary providers can respond to all changes, but ancillary ones
// should respond only to annotation changes.
if ((providerId.isAncillary() && annotationsChanged) ||
(!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
if (!replaced) {
// FIXME: Is the enclosing if required here?
verify(replaced,
"Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
providerId, oldDevice, devices.get(newDevice.id())
"Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
providerId, oldDevice, devices.get(newDevice.id())
, newDevice);
}
if (!providerId.isAncillary()) {
......@@ -193,7 +198,7 @@ public class SimpleDeviceStore
@Override
public DeviceEvent markOffline(DeviceId deviceId) {
Map<ProviderId, DeviceDescriptions> providerDescs
= getOrCreateDeviceDescriptions(deviceId);
= getOrCreateDeviceDescriptions(deviceId);
// locking device
synchronized (providerDescs) {
......@@ -212,9 +217,8 @@ public class SimpleDeviceStore
@Override
public List<DeviceEvent> updatePorts(ProviderId providerId,
DeviceId deviceId,
List<PortDescription> portDescriptions) {
DeviceId deviceId,
List<PortDescription> portDescriptions) {
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
......@@ -226,8 +230,8 @@ public class SimpleDeviceStore
DeviceDescriptions descs = descsMap.get(providerId);
// every provider must provide DeviceDescription.
checkArgument(descs != null,
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
Map<PortNumber, Port> ports = getPortMap(deviceId);
......@@ -247,8 +251,8 @@ public class SimpleDeviceStore
newPort = composePort(device, number, descsMap);
events.add(oldPort == null ?
createPort(device, newPort, ports) :
updatePort(device, oldPort, newPort, ports));
createPort(device, newPort, ports) :
updatePort(device, oldPort, newPort, ports));
}
events.addAll(pruneOldPorts(device, ports, processed));
......@@ -272,7 +276,7 @@ public class SimpleDeviceStore
Port newPort,
Map<PortNumber, Port> ports) {
if (oldPort.isEnabled() != newPort.isEnabled() ||
!AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
!AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) {
ports.put(oldPort.number(), newPort);
return new DeviceEvent(PORT_UPDATED, device, newPort);
......@@ -303,7 +307,7 @@ public class SimpleDeviceStore
// exist, it creates and registers a new one.
private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
return createIfAbsentUnchecked(devicePorts, deviceId,
NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
}
private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
......@@ -325,9 +329,8 @@ public class SimpleDeviceStore
// Guarded by deviceDescs value (=Device lock)
private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
Map<ProviderId, DeviceDescriptions> device,
ProviderId providerId, DeviceDescription deltaDesc) {
Map<ProviderId, DeviceDescriptions> device,
ProviderId providerId, DeviceDescription deltaDesc) {
synchronized (device) {
DeviceDescriptions r = device.get(providerId);
if (r == null) {
......@@ -340,7 +343,7 @@ public class SimpleDeviceStore
@Override
public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
PortDescription portDescription) {
PortDescription portDescription) {
Device device = devices.get(deviceId);
checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
......@@ -351,8 +354,8 @@ public class SimpleDeviceStore
DeviceDescriptions descs = descsMap.get(providerId);
// assuming all providers must give DeviceDescription first
checkArgument(descs != null,
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
"Device description for Device ID %s from Provider %s was not found",
deviceId, providerId);
ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
final PortNumber number = portDescription.portNumber();
......@@ -404,19 +407,19 @@ public class SimpleDeviceStore
availableDevices.remove(deviceId);
descs.clear();
return device == null ? null :
new DeviceEvent(DEVICE_REMOVED, device, null);
new DeviceEvent(DEVICE_REMOVED, device, null);
}
}
/**
* Returns a Device, merging description given from multiple Providers.
*
* @param deviceId device identifier
* @param deviceId device identifier
* @param providerDescs Collection of Descriptions from multiple providers
* @return Device instance
*/
private Device composeDevice(DeviceId deviceId,
Map<ProviderId, DeviceDescriptions> providerDescs) {
Map<ProviderId, DeviceDescriptions> providerDescs) {
checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
......@@ -447,21 +450,21 @@ public class SimpleDeviceStore
annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
}
return new DefaultDevice(primary, deviceId , type, manufacturer,
hwVersion, swVersion, serialNumber,
chassisId, annotations);
return new DefaultDevice(primary, deviceId, type, manufacturer,
hwVersion, swVersion, serialNumber,
chassisId, annotations);
}
/**
* Returns a Port, merging description given from multiple Providers.
*
* @param device device the port is on
* @param number port number
* @param device device the port is on
* @param number port number
* @param descsMap Collection of Descriptions from multiple providers
* @return Port instance
*/
private Port composePort(Device device, PortNumber number,
Map<ProviderId, DeviceDescriptions> descsMap) {
Map<ProviderId, DeviceDescriptions> descsMap) {
ProviderId primary = pickPrimaryPID(descsMap);
DeviceDescriptions primDescs = descsMap.get(primary);
......
......@@ -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>
......
......@@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.OFHello;
import org.projectfloodlight.openflow.protocol.OFHelloElem;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
......@@ -661,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
* However, we could be more forgiving
* @param h the channel handler that received the message
* @param m the message
* @throws SwitchStateException
* @throws SwitchStateExeption we always through the execption
* @throws SwitchStateException we always throw the exception
*/
// needs to be protected because enum members are acutally subclasses
// needs to be protected because enum members are actually subclasses
protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
throws SwitchStateException {
String msg = getSwitchStateMessage(h, m,
......@@ -1025,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
// all state for the original switch (with the same dpid),
// which we obviously don't want.
log.info("{}:removal called", getSwitchInfoString());
sw.removeConnectedSwitch();
if (sw != null) {
sw.removeConnectedSwitch();
}
} else {
// A duplicate was disconnected on this ChannelHandler,
// this is the same switch reconnecting, but the original state was
......
......@@ -207,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController {
+ "value for dpid: {}", dpid);
return false;
} else {
log.error("Added switch {}", dpid);
log.info("Added switch {}", dpid);
connectedSwitches.put(dpid, sw);
for (OpenFlowSwitchListener l : ofSwitchListener) {
l.switchAdded(dpid);
......
......@@ -1188,7 +1188,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
.setHardTimeout(0)
.setXid(getNextTransactionId())
.build();
sendMsg(tableMissEntry);
write(tableMissEntry);
}
private void sendBarrier(boolean finalBarrier) {
......@@ -1200,7 +1201,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
.buildBarrierRequest()
.setXid(xid)
.build();
sendMsg(br);
write(br);
}
@Override
......@@ -1210,7 +1212,7 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
@Override
public void write(OFMessage msg) {
this.channel.write(msg);
this.channel.write(Collections.singletonList(msg));
}
......
......@@ -88,7 +88,6 @@
<version>18.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
......@@ -610,7 +609,8 @@
</plugin>
</plugins>
</reporting>
<prerequisites>
<maven>3.0.0</maven>
</prerequisites>
</project>
......
......@@ -16,6 +16,7 @@
package org.onlab.onos.provider.lldp.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.nio.ByteBuffer;
......@@ -95,11 +96,13 @@ public class LinkDiscovery implements TimerTask {
*/
public LinkDiscovery(Device device, PacketService pktService,
MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) {
this.device = device;
this.probeRate = 3000;
this.linkProvider = providerService;
this.pktService = pktService;
this.mastershipService = masterService;
this.mastershipService = checkNotNull(masterService, "WTF!");
this.slowPorts = Collections.synchronizedSet(new HashSet<Long>());
this.fastPorts = Collections.synchronizedSet(new HashSet<Long>());
this.portProbeCount = new HashMap<>();
......@@ -344,6 +347,12 @@ public class LinkDiscovery implements TimerTask {
}
private void sendProbes(Long portNumber) {
if (device == null) {
log.warn("CRAZY SHIT");
}
if (mastershipService == null) {
log.warn("INSANE");
}
if (device.type() != Device.Type.ROADM &&
mastershipService.getLocalRole(this.device.id()) ==
MastershipRole.MASTER) {
......
......@@ -31,7 +31,7 @@ import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
import org.onlab.onos.net.flow.FlowRuleProvider;
import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
import org.onlab.onos.net.flow.FlowRuleProviderService;
import org.onlab.onos.net.intent.BatchOperation;
import org.onlab.onos.net.flow.BatchOperation;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.TopologyService;
......@@ -103,6 +103,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
private final Map<Long, InstallationFuture> pendingFMs =
new ConcurrentHashMap<Long, InstallationFuture>();
private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
/**
* Creates an OpenFlow host provider.
*/
......@@ -115,6 +117,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
providerService = providerRegistry.register(this);
controller.addListener(listener);
controller.addEventListener(listener);
for (OpenFlowSwitch sw : controller.getSwitches()) {
FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL);
fsc.start();
collectors.put(new Dpid(sw.getId()), fsc);
}
log.info("Started");
}
......@@ -213,7 +223,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
private class InternalFlowProvider
implements OpenFlowSwitchListener, OpenFlowEventListener {
private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
private final Multimap<DeviceId, FlowEntry> completeEntries =
ArrayListMultimap.create();
......
......@@ -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
......@@ -16,58 +16,95 @@ from functools import partial
import time
from sys import argv
from time import sleep
from sets import Set
class ONOS( Controller ):
#def __init__( self, name, command='/opt/onos/bin/onos-service', **kwargs ):
# Controller.__init__( self, name, command=command, inNamespace=True, **kwargs )
#def __init__( self, name, inNamespace=False, command='controller',
# cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1",
# port=6633, protocol='tcp', **params ):
#self.command = command
#self.cargs = cargs
#self.cdir = cdir
#self.ip = ip
#self.port = port
#self.protocol = protocol
#Node.__init__( self, name, inNamespace=inNamespace,
# ip=ip, **params )
#self.checkListening()
ONOS_DIR = '/opt/onos/'
KARAF_DIR = ONOS_DIR + 'apache-karaf-3.0.1/'
reactive = True
"TODO"
onosDir = '/opt/onos/'
def __init__( self, name, onosDir=onosDir,
reactive=True, features=[ 'onos-app-tvue' ],
**kwargs ):
'''TODO'''
Controller.__init__( self, name, **kwargs )
# the following have been done for us:
#self.ip = ip ('127.0.0.1')
#self.port = port (6633)
#self.protocol = protocol ('tcp')
#self.checkListening()
self.onosDir = onosDir
self.karafDir = onosDir + 'apache-karaf-3.0.1/'
self.instanceDir = self.karafDir
# add default modules
# TODO: consider an ordered set
self.features = Set([ 'webconsole',
'onos-api',
'onos-cli',
'onos-openflow' ])
self.features.update( features )
# add reactive forwarding modules
if reactive:
self.features.update( ['onos-app-fwd',
'onos-app-proxyarp',
'onos-app-mobility' ] )
# add the distributed core if we are in a namespace with no trivial core
if self.inNamespace and 'onos-core-trivial' not in self.features:
self.features.add( 'onos-core' )
# if there is no core, add the trivial one
if 'onos-core' not in self.features:
self.features.add( 'onos-core-trivial' )
print self.features
def start( self ):
# switch to the non-root user because karaf gets upset otherwise
# TODO we should look into why....
self.sendCmd( 'sudo su - %s' % self.findUser() )
self.waiting = False
if self.inNamespace:
self.cmd( self.KARAF_DIR + 'bin/instance create %s' % self.name )
src = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg'
dst = self.KARAF_DIR + 'instances/%s/etc/org.apache.karaf.features.cfg' % self.name
self.cmd( 'cp %s %s' % (src, dst) )
self.updateProperties( dst )
self.cmd( self.KARAF_DIR + 'bin/instance start %s' % self.name )
instanceOpts = ( '-furl mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features '
'-s 8101' )
self.userCmd( self.karafDir + 'bin/instance create %s %s' % ( instanceOpts, self.name ) )
self.instanceDir = self.karafDir + 'instances/%s/' % self.name
else:
# we are running in the root namespace, so let's use the root instance
self.cmd( 'rm -rf '+ self.KARAF_DIR + 'data/' )
filename = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg'
self.updateProperties( filename )
self.cmd( self.KARAF_DIR + 'bin/start' )
# clean up the data directory
#self.userCmd( 'rm -rf '+ self.karafDir + 'data/' )
pass
self.userCmd( 'rm -rf '+ self.instanceDir + 'data/' )
# Update etc/org.apache.karaf.features.cfg
self.updateFeatures()
# TODO 2. Update etc/hazelcast.xml : interface lines
#cp etc/hazelcast.xml instances/c1/etc/
self.updateHazelcast()
# TODO 3. Update etc/system.properties : onos.ip
# TODO 4. Update config/cluster.json : with all nodes
# start onos
self.userCmd( self.instanceDir + 'bin/start' )
#TODO we should wait for startup...
def stop( self ):
if self.inNamespace:
self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance stop %s' % self.name )
self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance destroy %s' % self.name )
else:
self.cmd( self.ONOS_DIR + 'apache-karaf-3.0.1/bin/stop' )
self.userCmd( self.instanceDir + 'bin/stop' )
#if self.inNamespace:
# self.userCmd( self.karafDir + 'bin/instance destroy %s' % self.name )
self.terminate()
def updateProperties( self, filename ):
def updateHazelcast( self ):
readfile = self.karafDir + 'etc/hazelcast.xml'
writefile = self.instanceDir + 'etc/hazelcast.xml'
with open( readfile, 'r' ) as r:
with open( writefile, 'w' ) as w:
for line in r.readlines():
if '<interface>' in line:
line = '<interface>' + '192.168.123.*' + '</interface>\n'
w.write( line )
def updateFeatures( self ):
filename = self.instanceDir + 'etc/org.apache.karaf.features.cfg'
with open( filename, 'r+' ) as f:
lines = f.readlines()
f.seek(0)
......@@ -75,17 +112,25 @@ class ONOS( Controller ):
for line in lines:
#print '?', line,
if 'featuresBoot=' in line:
line = line.rstrip()
#print ord(line[-1]), ord(line[-2]), ord(line[-3])
if self.reactive:
line += ',onos-app-fwd'
line += '\n'
# parse the features from the line
features = line.rstrip().split('=')[1].split(',')
# add the features to our features set
self.features.update( features )
# generate the new features line
line = 'featuresBoot=' + ','.join( self.features ) + '\n'
#print '!', line,
f.write( line )
@classmethod
def isAvailable( self ):
return quietRun( 'ls /opt/onos' )
return quietRun( 'ls %s' % self.onosDir )
def userCmd( self, cmd ):
# switch to the non-root user because karaf gets upset otherwise
# because the .m2repo is not stored with root
cmd = 'sudo -u %s %s' % ( self.findUser(), cmd )
return self.cmd( cmd )
@staticmethod
def findUser():
......@@ -111,7 +156,7 @@ class ControlNetwork( Topo ):
# Connect everything to a single switch
cs0 = self.addSwitch( 'cs0' )
# Add hosts which will serve as data network controllers
for i in range( 0, n ):
for i in range( 1, n+1 ):
c = self.addHost( 'c%s' % i, cls=dataController,
inNamespace=True )
self.addLink( c, cs0 )
......@@ -122,7 +167,7 @@ class ControlNetwork( Topo ):
class ONOSCluster( Controller ):
# TODO
n = 4
n = 3
def start( self ):
ctopo = ControlNetwork( n=self.n, dataController=ONOS )
......@@ -137,6 +182,9 @@ class ONOSCluster( Controller ):
host.start()
def stop( self ):
for host in self.cnet.hosts:
if isinstance( host, Controller ):
host.stop()
self.cnet.stop()
def clist( self ):
......@@ -158,10 +206,11 @@ switches = { 'ovso': OVSSwitchONOS }
if __name__ == '__main__':
# Simple test for ONOS() controller class
setLogLevel( 'info' )
setLogLevel( 'info' ) #TODO info
size = 2 if len( argv ) != 2 else int( argv[ 1 ] )
net = Mininet( topo=LinearTopo( size ),
controller=partial( ONOSCluster, n=4 ),
#controller=ONOS,
controller=partial( ONOSCluster, n=3 ), #TODO
switch=OVSSwitchONOS )
net.start()
#waitConnected( net.switches )
......
......@@ -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,13 +23,6 @@
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
<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
/*
Geometry library - based on work by Mike Bostock.
*/
(function() {
if (typeof geo == 'undefined') {
geo = {};
}
var tolerance = 1e-10;
function eq(a, b) {
return (Math.abs(a - b) < tolerance);
}
function gt(a, b) {
return (a - b > -tolerance);
}
function lt(a, b) {
return gt(b, a);
}
geo.eq = eq;
geo.gt = gt;
geo.lt = lt;
geo.LineSegment = function(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
// Ax + By = C
this.a = y2 - y1;
this.b = x1 - x2;
this.c = x1 * this.a + y1 * this.b;
if (eq(this.a, 0) && eq(this.b, 0)) {
throw new Error(
'Cannot construct a LineSegment with two equal endpoints.');
}
};
geo.LineSegment.prototype.intersect = function(that) {
var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
(this.y1 - this.y2) * (that.x1 - that.x2);
if (eq(d, 0)) {
// The two lines are parallel or very close.
return {
x : NaN,
y : NaN
};
}
var t1 = this.x1 * this.y2 - this.y1 * this.x2,
t2 = that.x1 * that.y2 - that.y1 * that.x2,
x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));
return {
x : x,
y : y,
in1 : in1,
in2 : in2
};
};
geo.LineSegment.prototype.x = function(y) {
// x = (C - By) / a;
if (this.a) {
return (this.c - this.b * y) / this.a;
} else {
// a == 0 -> horizontal line
return NaN;
}
};
geo.LineSegment.prototype.y = function(x) {
// y = (C - Ax) / b;
if (this.b) {
return (this.c - this.a * x) / this.b;
} else {
// b == 0 -> vertical line
return NaN;
}
};
geo.LineSegment.prototype.length = function() {
return Math.sqrt(
(this.y2 - this.y1) * (this.y2 - this.y1) +
(this.x2 - this.x1) * (this.x2 - this.x1));
};
geo.LineSegment.prototype.offset = function(x, y) {
return new geo.LineSegment(
this.x1 + x, this.y1 + y,
this.x2 + x, this.y2 + y);
};
})();
......@@ -14,6 +14,7 @@
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="onos.css">
<script src="geometry.js"></script>
<script src="onosui.js"></script>
</head>
......
......@@ -10,11 +10,16 @@
var api = onos.api;
var config = {
layering: false,
jsonUrl: 'network.json',
iconUrl: {
pkt: 'pkt.png',
opt: 'opt.png'
},
mastHeight: 32,
force: {
linkDistance: 150,
linkStrength: 0.9,
linkDistance: 240,
linkStrength: 0.8,
charge: -400,
ticksWithoutCollisions: 50,
marginLR: 20,
......@@ -26,8 +31,9 @@
}
},
labels: {
padLR: 3,
padTB: 2,
imgPad: 22,
padLR: 8,
padTB: 6,
marginLR: 3,
marginTB: 2
},
......@@ -53,7 +59,7 @@
d3.json(config.jsonUrl, function (err, data) {
if (err) {
alert('Oops! Error reading JSON...\n\n' +
'URL: ' + jsonUrl + '\n\n' +
'URL: ' + config.jsonUrl + '\n\n' +
'Error: ' + err.message);
return;
}
......@@ -152,8 +158,8 @@
.attr('width', view.width)
.attr('height', view.height)
.append('g')
.attr('transform', config.force.translate());
// .attr('id', 'zoomable')
.attr('transform', config.force.translate())
// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
// function zoomRedraw() {
......@@ -247,7 +253,7 @@
}
});
// TODO: add drag, mouseover, mouseout behaviors
network.node = network.svg.selectAll('.node')
.data(network.force.nodes(), function(d) {return d.id})
.enter().append('g')
......@@ -279,23 +285,26 @@
}
});
// TODO: augment stroke and fill functions
network.nodeRect = network.node.append('rect')
// TODO: css for node rects
.attr('rx', 5)
.attr('ry', 5)
// .attr('stroke', function(d) { return '#000'})
// .attr('fill', function(d) { return '#ddf'})
.attr('width', 60)
.attr('height', 24);
.attr('width', 126)
.attr('height', 40);
network.node.each(function(d) {
var node = d3.select(this),
rect = node.select('rect');
var text = node.append('text')
.text(d.id)
.attr('dx', '1em')
.attr('dy', '2.1em');
rect = node.select('rect'),
img = node.append('svg:image')
.attr('x', -16)
.attr('y', -16)
.attr('width', 32)
.attr('height', 32)
.attr('xlink:href', iconUrl(d)),
text = node.append('text')
.text(d.id)
.attr('dy', '1.1em'),
dummy;
});
// this function is scheduled to happen soon after the given thread ends
......@@ -308,12 +317,64 @@
first = true;
// NOTE: probably unnecessary code if we only have one line.
text.each(function() {
var box = this.getBBox();
if (first || box.x < bounds.x1) {
bounds.x1 = box.x;
}
if (first || box.y < bounds.y1) {
bounds.y1 = box.y;
}
if (first || box.x + box.width < bounds.x2) {
bounds.x2 = box.x + box.width;
}
if (first || box.y + box.height < bounds.y2) {
bounds.y2 = box.y + box.height;
}
first = false;
}).attr('text-anchor', 'middle');
var lab = config.labels,
oldWidth = bounds.x2 - bounds.x1;
bounds.x1 -= oldWidth / 2;
bounds.x2 -= oldWidth / 2;
bounds.x1 -= (lab.padLR + lab.imgPad);
bounds.y1 -= lab.padTB;
bounds.x2 += lab.padLR;
bounds.y2 += lab.padTB;
node.select('rect')
.attr('x', bounds.x1)
.attr('y', bounds.y1)
.attr('width', bounds.x2 - bounds.x1)
.attr('height', bounds.y2 - bounds.y1);
node.select('image')
.attr('x', bounds.x1);
d.extent = {
left: bounds.x1 - lab.marginLR,
right: bounds.x2 + lab.marginLR,
top: bounds.y1 - lab.marginTB,
bottom: bounds.y2 + lab.marginTB
};
d.edge = {
left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2),
right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2),
top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1),
bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
};
// ====
});
network.numTicks = 0;
network.preventCollisions = false;
network.force.start();
for (var i = 0; i < config.ticksWithoutCollisions; i++) {
for (var i = 0; i < config.force.ticksWithoutCollisions; i++) {
network.force.tick();
}
network.preventCollisions = true;
......@@ -322,22 +383,78 @@
}
function iconUrl(d) {
return config.iconUrl[d.type];
}
function translate(x, y) {
return 'translate(' + x + ',' + y + ')';
}
function preventCollisions() {
var quadtree = d3.geom.quadtree(network.nodes);
network.nodes.forEach(function(n) {
var nx1 = n.x + n.extent.left,
nx2 = n.x + n.extent.right,
ny1 = n.y + n.extent.top,
ny2 = n.y + n.extent.bottom;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && quad.point !== n) {
// check if the rectangles intersect
var p = quad.point,
px1 = p.x + p.extent.left,
px2 = p.x + p.extent.right,
py1 = p.y + p.extent.top,
py2 = p.y + p.extent.bottom,
ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2);
if (ix) {
var xa1 = nx2 - px1, // shift n left , p right
xa2 = px2 - nx1, // shift n right, p left
ya1 = ny2 - py1, // shift n up , p down
ya2 = py2 - ny1, // shift n down , p up
adj = Math.min(xa1, xa2, ya1, ya2);
if (adj == xa1) {
n.x -= adj / 2;
p.x += adj / 2;
} else if (adj == xa2) {
n.x += adj / 2;
p.x -= adj / 2;
} else if (adj == ya1) {
n.y -= adj / 2;
p.y += adj / 2;
} else if (adj == ya2) {
n.y += adj / 2;
p.y -= adj / 2;
}
}
return ix;
}
});
});
}
function tick(e) {
network.numTicks++;
// adjust the y-coord of each node, based on y-pos constraints
// network.nodes.forEach(function (n) {
// var z = e.alpha * n.constraint.weight;
// if (!isNaN(n.constraint.y)) {
// n.y = (n.constraint.y * z + n.y * (1 - z));
// }
// });
if (config.layering) {
// adjust the y-coord of each node, based on y-pos constraints
network.nodes.forEach(function (n) {
var z = e.alpha * n.constraint.weight;
if (!isNaN(n.constraint.y)) {
n.y = (n.constraint.y * z + n.y * (1 - z));
}
});
}
if (network.preventCollisions) {
preventCollisions();
}
// TODO: use intersection technique for source end of link also
network.link
.attr('x1', function(d) {
return d.source.x;
......@@ -345,11 +462,24 @@
.attr('y1', function(d) {
return d.source.y;
})
.attr('x2', function(d) {
return d.target.x;
})
.attr('y2', function(d) {
return d.target.y;
.each(function(d) {
var x = d.target.x,
y = d.target.y,
line = new geo.LineSegment(d.source.x, d.source.y, x, y);
for (var e in d.target.edge) {
var ix = line.intersect(d.target.edge[e].offset(x,y));
if (ix.in1 && ix.in2) {
x = ix.x;
y = ix.y;
break;
}
}
d3.select(this)
.attr('x2', x)
.attr('y2', y);
});
network.node
......
......@@ -7,50 +7,50 @@
},
"nodes": [
{
"id": "switch-1",
"id": "sample1",
"type": "opt",
"status": "good"
},
{
"id": "switch-2",
"id": "00:00:00:00:00:00:00:02",
"type": "opt",
"status": "good"
},
{
"id": "switch-3",
"id": "00:00:00:00:00:00:00:03",
"type": "opt",
"status": "good"
},
{
"id": "switch-4",
"id": "00:00:00:00:00:00:00:04",
"type": "opt",
"status": "good"
},
{
"id": "switch-11",
"id": "00:00:00:00:00:00:00:11",
"type": "pkt",
"status": "good"
},
{
"id": "switch-12",
"id": "00:00:00:00:00:00:00:12",
"type": "pkt",
"status": "good"
},
{
"id": "switch-13",
"id": "00:00:00:00:00:00:00:13",
"type": "pkt",
"status": "good"
}
],
"links": [
{ "src": "switch-1", "dst": "switch-2" },
{ "src": "switch-1", "dst": "switch-3" },
{ "src": "switch-1", "dst": "switch-4" },
{ "src": "switch-2", "dst": "switch-3" },
{ "src": "switch-2", "dst": "switch-4" },
{ "src": "switch-3", "dst": "switch-4" },
{ "src": "switch-13", "dst": "switch-3" },
{ "src": "switch-12", "dst": "switch-2" },
{ "src": "switch-11", "dst": "switch-1" }
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:02" },
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:03", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:13", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "00:00:00:00:00:00:00:12", "dst": "00:00:00:00:00:00:00:02" },
{ "src": "00:00:00:00:00:00:00:11", "dst": "sample1" }
]
}
......
......@@ -85,12 +85,14 @@ svg .node.selected rect {
svg .link.inactive,
svg .node.inactive rect,
svg .node.inactive text {
svg .node.inactive text,
svg .node.inactive image {
opacity: .2;
}
svg .node.inactive.selected rect,
svg .node.inactive.selected text {
svg .node.inactive.selected text,
svg .node.inactive.selected image {
opacity: .6;
}
......
......@@ -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>
......