Merge branch 'optical-integration'
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java
index 38366b6..cfc88b8 100644
--- a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetrics.java
@@ -5,8 +5,6 @@
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;
@@ -14,8 +12,7 @@
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.EventMetric;
import org.onlab.metrics.MetricsService;
import org.onlab.onos.net.intent.IntentEvent;
import org.onlab.onos.net.intent.IntentListener;
@@ -33,56 +30,32 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MetricsService metricsService;
+
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;
+ // Event metrics:
+ // - Intent Submitted API operation
+ // - Intent Installed operation completion
+ // - Intent Withdraw Requested API operation
+ // - Intent Withdrawn operation completion
//
- // 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;
+ private EventMetric intentSubmittedEventMetric;
+ private EventMetric intentInstalledEventMetric;
+ private EventMetric intentWithdrawRequestedEventMetric;
+ private EventMetric intentWithdrawnEventMetric;
@Activate
protected void activate() {
@@ -108,43 +81,23 @@
}
@Override
- public Gauge<Long> intentSubmittedTimestampEpochMsGauge() {
- return intentSubmittedTimestampEpochMsGauge;
+ public EventMetric intentSubmittedEventMetric() {
+ return intentSubmittedEventMetric;
}
@Override
- public Gauge<Long> intentInstalledTimestampEpochMsGauge() {
- return intentInstalledTimestampEpochMsGauge;
+ public EventMetric intentInstalledEventMetric() {
+ return intentInstalledEventMetric;
}
@Override
- public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge() {
- return intentWithdrawRequestedTimestampEpochMsGauge;
+ public EventMetric intentWithdrawRequestedEventMetric() {
+ return intentWithdrawRequestedEventMetric;
}
@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;
+ public EventMetric intentWithdrawnEventMetric() {
+ return intentWithdrawnEventMetric;
}
@Override
@@ -156,26 +109,21 @@
//
switch (event.type()) {
case SUBMITTED:
- intentSubmittedTimestampEpochMs = System.currentTimeMillis();
- intentSubmittedRateMeter.mark(1);
+ intentSubmittedEventMetric.eventReceived();
break;
case INSTALLED:
- intentInstalledTimestampEpochMs = System.currentTimeMillis();
- intentInstalledRateMeter.mark(1);
+ intentInstalledEventMetric.eventReceived();
break;
case FAILED:
// TODO: Just ignore?
break;
/*
case WITHDRAW_REQUESTED:
- intentWithdrawRequestedTimestampEpochMs =
- System.currentTimeMillis();
- intentWithdrawRequestedRateMeter.mark(1);
+ intentWithdrawRequestedEventMetric.eventReceived();
break;
*/
case WITHDRAWN:
- intentWithdrawnTimestampEpochMs = System.currentTimeMillis();
- intentWithdrawnRateMeter.mark(1);
+ intentWithdrawnEventMetric.eventReceived();
break;
default:
break;
@@ -199,10 +147,6 @@
*/
private void clear() {
synchronized (lastEvents) {
- intentSubmittedTimestampEpochMs = 0;
- intentInstalledTimestampEpochMs = 0;
- intentWithdrawRequestedTimestampEpochMs = 0;
- intentWithdrawnTimestampEpochMs = 0;
lastEvents.clear();
}
}
@@ -211,109 +155,32 @@
* 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);
+ intentSubmittedEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_SUBMITTED_NAME);
+ intentInstalledEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_INSTALLED_NAME);
+ intentWithdrawRequestedEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_WITHDRAW_REQUESTED_NAME);
+ intentWithdrawnEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_WITHDRAWN_NAME);
+
+ intentSubmittedEventMetric.registerMetrics();
+ intentInstalledEventMetric.registerMetrics();
+ intentWithdrawRequestedEventMetric.registerMetrics();
+ intentWithdrawnEventMetric.registerMetrics();
}
/**
* 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);
+ intentSubmittedEventMetric.removeMetrics();
+ intentInstalledEventMetric.removeMetrics();
+ intentWithdrawRequestedEventMetric.removeMetrics();
+ intentWithdrawnEventMetric.removeMetrics();
}
}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
index 4acd00f..73df4bc 100644
--- a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
@@ -1,9 +1,7 @@
package org.onlab.onos.metrics.intent;
import java.util.List;
-
-import com.codahale.metrics.Gauge;
-import com.codahale.metrics.Meter;
+import org.onlab.metrics.EventMetric;
import org.onlab.onos.net.intent.IntentEvent;
/**
@@ -18,68 +16,32 @@
public List<IntentEvent> getEvents();
/**
- * Gets the Metrics' Gauge for the intent SUBMITTED event timestamp
- * (ms from the epoch).
+ * Gets the Event Metric for the intent SUBMITTED events.
*
- * @return the Metrics' Gauge for the intent SUBMITTED event timestamp
- * (ms from the epoch)
+ * @return the Event Metric for the intent SUBMITTED events.
*/
- public Gauge<Long> intentSubmittedTimestampEpochMsGauge();
+ public EventMetric intentSubmittedEventMetric();
/**
- * Gets the Metrics' Gauge for the intent INSTALLED event timestamp
- * (ms from the epoch).
+ * Gets the Event Metric for the intent INSTALLED events.
*
- * @return the Metrics' Gauge for the intent INSTALLED event timestamp
- * (ms from the epoch)
+ * @return the Event Metric for the intent INSTALLED events.
*/
- public Gauge<Long> intentInstalledTimestampEpochMsGauge();
+ public EventMetric intentInstalledEventMetric();
/**
- * Gets the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
- * timestamp (ms from the epoch).
+ * Gets the Event Metric for the intent WITHDRAW_REQUESTED events.
*
* TODO: This intent event is not implemented yet.
*
- * @return the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
- * timestamp (ms from the epoch)
+ * @return the Event Metric for the intent WITHDRAW_REQUESTED events.
*/
- public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge();
+ public EventMetric intentWithdrawRequestedEventMetric();
/**
- * Gets the Metrics' Gauge for the intent WITHDRAWN event timestamp
- * (ms from the epoch).
+ * Gets the Event Metric for the intent WITHDRAWN events.
*
- * @return the Metrics' Gauge for the intent WITHDRAWN event timestamp
- * (ms from the epoch)
+ * @return the Event Metric for the intent WITHDRAWN events.
*/
- 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();
+ public EventMetric intentWithdrawnEventMetric();
}
diff --git a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
index 204cfd6..6f8d013 100644
--- a/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
+++ b/apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
@@ -11,6 +11,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
+import org.onlab.metrics.EventMetric;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.metrics.intent.IntentMetricsService;
@@ -29,8 +30,6 @@
@Override
protected void execute() {
IntentMetricsService service = get(IntentMetricsService.class);
- Gauge<Long> gauge;
- Meter meter;
if (outputJson()) {
ObjectMapper mapper = new ObjectMapper()
@@ -38,49 +37,49 @@
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));
- //
+ result = json(mapper, result, "intentSubmitted",
+ service.intentSubmittedEventMetric());
+ result = json(mapper, result, "intentInstalled",
+ service.intentInstalledEventMetric());
+ result = json(mapper, result, "intentWithdrawRequested",
+ service.intentWithdrawRequestedEventMetric());
+ result = json(mapper, result, "intentWithdrawn",
+ service.intentWithdrawnEventMetric());
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);
+ printEventMetric("Submitted",
+ service.intentSubmittedEventMetric());
+ printEventMetric("Installed",
+ service.intentInstalledEventMetric());
+ printEventMetric("Withdraw Requested",
+ service.intentWithdrawRequestedEventMetric());
+ printEventMetric("Withdrawn",
+ service.intentWithdrawnEventMetric());
}
}
/**
+ * Produces JSON node for an Event Metric.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param objectNode the JSON object node to use
+ * @param propertyPrefix the property prefix to use
+ * @param eventMetric the Event Metric with the data
+ * @return JSON object node for the Event Metric
+ */
+ private ObjectNode json(ObjectMapper mapper, ObjectNode objectNode,
+ String propertyPrefix, EventMetric eventMetric) {
+ String gaugeName = propertyPrefix + "Timestamp";
+ String meterName = propertyPrefix + "Rate";
+ Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+ Meter meter = eventMetric.eventRateMeter();
+
+ objectNode.put(gaugeName, json(mapper, gauge));
+ objectNode.put(meterName, json(mapper, meter));
+ return objectNode;
+ }
+
+ /**
* Produces JSON node for an Object.
*
* @param mapper the JSON object mapper to use
@@ -94,8 +93,8 @@
//
try {
final String objectJson = mapper.writeValueAsString(object);
- JsonNode objectNode = mapper.readTree(objectJson);
- return objectNode;
+ JsonNode jsonNode = mapper.readTree(objectJson);
+ return jsonNode;
} catch (JsonProcessingException e) {
log.error("Error writing value as JSON string", e);
} catch (IOException e) {
@@ -105,28 +104,26 @@
}
/**
- * Prints a Gauge.
+ * Prints an Event Metric.
*
* @param operationStr the string with the intent operation to print
- * @param gauge the Gauge to print
+ * @param eventMetric the Event Metric to print
*/
- private void printGauge(String operationStr, Gauge<Long> gauge) {
- print(FORMAT_GAUGE, operationStr, gauge.getValue());
- }
+ private void printEventMetric(String operationStr,
+ EventMetric eventMetric) {
+ Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+ Meter meter = eventMetric.eventRateMeter();
+ TimeUnit rateUnit = TimeUnit.SECONDS;
+ double rateFactor = rateUnit.toSeconds(1);
- /**
- * 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);
+ // Print the Gauge
+ print(FORMAT_GAUGE, operationStr, gauge.getValue());
+
+ // Print the Meter
+ print(FORMAT_METER, operationStr, meter.getCount(),
+ meter.getMeanRate() * rateFactor,
+ meter.getOneMinuteRate() * rateFactor,
+ meter.getFiveMinuteRate() * rateFactor,
+ meter.getFifteenMinuteRate() * rateFactor);
}
}
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
index 32cf0cf..814e178 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetrics.java
@@ -5,8 +5,6 @@
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;
@@ -14,8 +12,7 @@
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.EventMetric;
import org.onlab.metrics.MetricsService;
import org.onlab.onos.event.Event;
import org.onlab.onos.net.device.DeviceEvent;
@@ -48,6 +45,8 @@
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MetricsService metricsService;
private LinkedList<Event> lastEvents = new LinkedList<>();
private static final int LAST_EVENTS_MAX_N = 100;
@@ -61,22 +60,22 @@
//
// Metrics
//
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected MetricsService metricsService;
- //
private static final String COMPONENT_NAME = "Topology";
- private static final String FEATURE_NAME = "EventNotification";
- private static final String GAUGE_NAME = "LastEventTimestamp.EpochMs";
- private static final String METER_NAME = "EventRate";
+ private static final String FEATURE_DEVICE_NAME = "DeviceEvent";
+ private static final String FEATURE_HOST_NAME = "HostEvent";
+ private static final String FEATURE_LINK_NAME = "LinkEvent";
+ private static final String FEATURE_GRAPH_NAME = "GraphEvent";
//
- private MetricsComponent metricsComponent;
- private MetricsFeature metricsFeatureEventNotification;
+ // Event metrics:
+ // - Device events
+ // - Host events
+ // - Link events
+ // - Topology Graph events
//
- // Timestamp of the last Topology event (ms from the Epoch)
- private volatile long lastEventTimestampEpochMs = 0;
- private Gauge<Long> lastEventTimestampEpochMsGauge;
- // Rate of the Topology events published to the Topology listeners
- private Meter eventRateMeter;
+ private EventMetric topologyDeviceEventMetric;
+ private EventMetric topologyHostEventMetric;
+ private EventMetric topologyLinkEventMetric;
+ private EventMetric topologyGraphEventMetric;
@Activate
protected void activate() {
@@ -113,27 +112,34 @@
}
@Override
- public Gauge<Long> lastEventTimestampEpochMsGauge() {
- return lastEventTimestampEpochMsGauge;
+ public EventMetric topologyDeviceEventMetric() {
+ return topologyDeviceEventMetric;
}
@Override
- public Meter eventRateMeter() {
- return eventRateMeter;
+ public EventMetric topologyHostEventMetric() {
+ return topologyHostEventMetric;
+ }
+
+ @Override
+ public EventMetric topologyLinkEventMetric() {
+ return topologyLinkEventMetric;
+ }
+
+ @Override
+ public EventMetric topologyGraphEventMetric() {
+ return topologyGraphEventMetric;
}
/**
* Records an event.
*
* @param event the event to record
- * @param updateEventRateMeter if true, update the Event Rate Meter
+ * @param eventMetric the Event Metric to use
*/
- private void recordEvent(Event event, boolean updateEventRateMeter) {
+ private void recordEvent(Event event, EventMetric eventMetric) {
synchronized (lastEvents) {
- lastEventTimestampEpochMs = System.currentTimeMillis();
- if (updateEventRateMeter) {
- eventRateMeter.mark(1);
- }
+ eventMetric.eventReceived();
//
// Keep only the last N events, where N = LAST_EVENTS_MAX_N
@@ -151,7 +157,7 @@
private class InnerDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
- recordEvent(event, true);
+ recordEvent(event, topologyDeviceEventMetric);
log.debug("Device Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
@@ -163,7 +169,7 @@
private class InnerHostListener implements HostListener {
@Override
public void event(HostEvent event) {
- recordEvent(event, true);
+ recordEvent(event, topologyHostEventMetric);
log.debug("Host Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
@@ -175,7 +181,7 @@
private class InnerLinkListener implements LinkListener {
@Override
public void event(LinkEvent event) {
- recordEvent(event, true);
+ recordEvent(event, topologyLinkEventMetric);
log.debug("Link Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
}
@@ -187,11 +193,7 @@
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);
+ recordEvent(event, topologyGraphEventMetric);
log.debug("Topology Event: time = {} type = {} event = {}",
event.time(), event.type(), event);
for (Event reason : event.reasons()) {
@@ -206,7 +208,6 @@
*/
private void clear() {
synchronized (lastEvents) {
- lastEventTimestampEpochMs = 0;
lastEvents.clear();
}
}
@@ -215,35 +216,32 @@
* Registers the metrics.
*/
private void registerMetrics() {
- metricsComponent = metricsService.registerComponent(COMPONENT_NAME);
- metricsFeatureEventNotification =
- metricsComponent.registerFeature(FEATURE_NAME);
- lastEventTimestampEpochMsGauge =
- metricsService.registerMetric(metricsComponent,
- metricsFeatureEventNotification,
- GAUGE_NAME,
- new Gauge<Long>() {
- @Override
- public Long getValue() {
- return lastEventTimestampEpochMs;
- }
- });
- eventRateMeter =
- metricsService.createMeter(metricsComponent,
- metricsFeatureEventNotification,
- METER_NAME);
+ topologyDeviceEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_DEVICE_NAME);
+ topologyHostEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_HOST_NAME);
+ topologyLinkEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_LINK_NAME);
+ topologyGraphEventMetric =
+ new EventMetric(metricsService, COMPONENT_NAME,
+ FEATURE_GRAPH_NAME);
+ topologyDeviceEventMetric.registerMetrics();
+ topologyHostEventMetric.registerMetrics();
+ topologyLinkEventMetric.registerMetrics();
+ topologyGraphEventMetric.registerMetrics();
}
/**
* Removes the metrics.
*/
private void removeMetrics() {
- metricsService.removeMetric(metricsComponent,
- metricsFeatureEventNotification,
- GAUGE_NAME);
- metricsService.removeMetric(metricsComponent,
- metricsFeatureEventNotification,
- METER_NAME);
+ topologyDeviceEventMetric.removeMetrics();
+ topologyHostEventMetric.removeMetrics();
+ topologyLinkEventMetric.removeMetrics();
+ topologyGraphEventMetric.removeMetrics();
}
}
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
index aeb2e32..9203d9b 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/TopologyMetricsService.java
@@ -1,9 +1,7 @@
package org.onlab.onos.metrics.topology;
import java.util.List;
-
-import com.codahale.metrics.Gauge;
-import com.codahale.metrics.Meter;
+import org.onlab.metrics.EventMetric;
import org.onlab.onos.event.Event;
/**
@@ -18,18 +16,30 @@
public List<Event> getEvents();
/**
- * Gets the Metrics' Gauge for the last topology event timestamp
- * (ms from the epoch).
+ * Gets the Event Metric for the Device Events.
*
- * @return the Metrics' Gauge for the last topology event timestamp
- * (ms from the epoch)
+ * @return the Event Metric for the Device Events.
*/
- public Gauge<Long> lastEventTimestampEpochMsGauge();
+ public EventMetric topologyDeviceEventMetric();
/**
- * Gets the Metrics' Meter for the topology events rate.
+ * Gets the Event Metric for the Host Events.
*
- * @return the Metrics' Meter for the topology events rate
+ * @return the Event Metric for the Host Events.
*/
- public Meter eventRateMeter();
+ public EventMetric topologyHostEventMetric();
+
+ /**
+ * Gets the Event Metric for the Link Events.
+ *
+ * @return the Event Metric for the Link Events.
+ */
+ public EventMetric topologyLinkEventMetric();
+
+ /**
+ * Gets the Event Metric for the Topology Graph Events.
+ *
+ * @return the Event Metric for the Topology Graph Events.
+ */
+ public EventMetric topologyGraphEventMetric();
}
diff --git a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
index 54d3a95..b7e0401 100644
--- a/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
+++ b/apps/metrics/topology/src/main/java/org/onlab/onos/metrics/topology/cli/TopologyEventsMetricsCommand.java
@@ -11,6 +11,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
+import org.onlab.metrics.EventMetric;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.metrics.topology.TopologyMetricsService;
@@ -22,15 +23,13 @@
public class TopologyEventsMetricsCommand extends AbstractShellCommand {
private static final String FORMAT_GAUGE =
- "Last Topology Event Timestamp (ms from epoch)=%d";
+ "Topology %s Event Timestamp (ms from epoch)=%d";
private static final String FORMAT_METER =
- "Topology Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
+ "Topology %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
@Override
protected void execute() {
TopologyMetricsService service = get(TopologyMetricsService.class);
- Gauge<Long> gauge = service.lastEventTimestampEpochMsGauge();
- Meter meter = service.eventRateMeter();
if (outputJson()) {
ObjectMapper mapper = new ObjectMapper()
@@ -38,32 +37,89 @@
TimeUnit.MILLISECONDS,
false));
ObjectNode result = mapper.createObjectNode();
- try {
- //
- // NOTE: The API for custom serializers is incomplete,
- // hence we have to parse the JSON string to create JsonNode.
- //
- final String gaugeJson = mapper.writeValueAsString(gauge);
- final String meterJson = mapper.writeValueAsString(meter);
- JsonNode gaugeNode = mapper.readTree(gaugeJson);
- JsonNode meterNode = mapper.readTree(meterJson);
- result.put("lastTopologyEventTimestamp", gaugeNode);
- result.put("topologyEventRate", meterNode);
- } catch (JsonProcessingException e) {
- log.error("Error writing value as JSON string", e);
- } catch (IOException e) {
- log.error("Error writing value as JSON string", e);
- }
+ result = json(mapper, result, "topologyDeviceEvent",
+ service.topologyDeviceEventMetric());
+ result = json(mapper, result, "topologyHostEvent",
+ service.topologyHostEventMetric());
+ result = json(mapper, result, "topologyLinkEvent",
+ service.topologyLinkEventMetric());
+ result = json(mapper, result, "topologyGraphEvent",
+ service.topologyGraphEventMetric());
print("%s", result);
} else {
- TimeUnit rateUnit = TimeUnit.SECONDS;
- double rateFactor = rateUnit.toSeconds(1);
- print(FORMAT_GAUGE, gauge.getValue());
- print(FORMAT_METER, meter.getCount(),
- meter.getMeanRate() * rateFactor,
- meter.getOneMinuteRate() * rateFactor,
- meter.getFiveMinuteRate() * rateFactor,
- meter.getFifteenMinuteRate() * rateFactor);
+ printEventMetric("Device", service.topologyDeviceEventMetric());
+ printEventMetric("Host", service.topologyHostEventMetric());
+ printEventMetric("Link", service.topologyLinkEventMetric());
+ printEventMetric("Graph", service.topologyGraphEventMetric());
}
}
+
+ /**
+ * Produces JSON node for an Event Metric.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param objectNode the JSON object node to use
+ * @param propertyPrefix the property prefix to use
+ * @param eventMetric the Event Metric with the data
+ * @return JSON object node for the Event Metric
+ */
+ private ObjectNode json(ObjectMapper mapper, ObjectNode objectNode,
+ String propertyPrefix, EventMetric eventMetric) {
+ String gaugeName = propertyPrefix + "Timestamp";
+ String meterName = propertyPrefix + "Rate";
+ Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+ Meter meter = eventMetric.eventRateMeter();
+
+ objectNode.put(gaugeName, json(mapper, gauge));
+ objectNode.put(meterName, json(mapper, meter));
+ return objectNode;
+ }
+
+ /**
+ * 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 jsonNode = mapper.readTree(objectJson);
+ return jsonNode;
+ } 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 an Event Metric.
+ *
+ * @param operationStr the string with the intent operation to print
+ * @param eventMetric the Event Metric to print
+ */
+ private void printEventMetric(String operationStr,
+ EventMetric eventMetric) {
+ Gauge<Long> gauge = eventMetric.lastEventTimestampGauge();
+ Meter meter = eventMetric.eventRateMeter();
+ TimeUnit rateUnit = TimeUnit.SECONDS;
+ double rateFactor = rateUnit.toSeconds(1);
+
+ // Print the Gauge
+ print(FORMAT_GAUGE, operationStr, gauge.getValue());
+
+ // Print the Meter
+ print(FORMAT_METER, operationStr, meter.getCount(),
+ meter.getMeanRate() * rateFactor,
+ meter.getOneMinuteRate() * rateFactor,
+ meter.getFiveMinuteRate() * rateFactor,
+ meter.getFifteenMinuteRate() * rateFactor);
+ }
}
diff --git a/core/api/src/main/java/org/onlab/onos/codec/CodecService.java b/core/api/src/main/java/org/onlab/onos/codec/CodecService.java
new file mode 100644
index 0000000..c6dbda2
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/codec/CodecService.java
@@ -0,0 +1,58 @@
+/*
+ * 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.codec;
+
+import java.util.Set;
+
+/**
+ * Service for registering and retrieving JSON codecs for various entities.
+ */
+public interface CodecService {
+
+ /**
+ * Returns the set of classes with currently registered codecs.
+ *
+ * @return set of entity classes
+ */
+ Set<Class<?>> getCodecs();
+
+ /**
+ * Returns the JSON codec for the specified entity class.
+ *
+ * @param entityClass entity class
+ * @return JSON codec; null if no codec available for the class
+ */
+ JsonCodec getCodec(Class<?> entityClass);
+
+ /**
+ * Registers the specified JSON codec for the given entity class.
+ *
+ * @param entityClass entity class
+ * @param codec JSON codec
+ */
+ void registerCodec(Class<?> entityClass, JsonCodec codec);
+
+ /**
+ * Unregisters the JSON codec for the specified entity class.
+ *
+ * @param entityClass entity class
+ */
+ void unregisterCodec(Class<?> entityClass);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/codec/JsonCodec.java b/core/api/src/main/java/org/onlab/onos/codec/JsonCodec.java
new file mode 100644
index 0000000..4338cde
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/codec/JsonCodec.java
@@ -0,0 +1,88 @@
+/*
+ * 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.codec;
+
+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 java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Abstraction of a codec capable for encoding/decoding arbitrary objects to/from JSON.
+ */
+public abstract class JsonCodec<T> {
+
+ /**
+ * Encodes the specified entity into JSON.
+ *
+ * @param entity entity to encode
+ * @param mapper object mapper
+ * @return JSON node
+ * @throws java.lang.UnsupportedOperationException if the codec does not
+ * support encode operations
+ */
+ public abstract ObjectNode encode(T entity, ObjectMapper mapper);
+
+ /**
+ * Decodes the specified entity from JSON.
+ *
+ * @param json JSON to decode
+ * @return decoded entity
+ * @throws java.lang.UnsupportedOperationException if the codec does not
+ * support decode operations
+ */
+ public abstract T decode(ObjectNode json);
+
+ /**
+ * Encodes the collection of the specified entities.
+ *
+ * @param entities collection of entities to encode
+ * @param mapper object mapper
+ * @return JSON array
+ * @throws java.lang.UnsupportedOperationException if the codec does not
+ * support encode operations
+ */
+ public ArrayNode encode(Iterable<T> entities, ObjectMapper mapper) {
+ ArrayNode result = mapper.createArrayNode();
+ for (T entity : entities) {
+ result.add(encode(entity, mapper));
+ }
+ return result;
+ }
+
+ /**
+ * Decodes the specified JSON array into a collection of entities.
+ *
+ * @param json JSON array to decode
+ * @return collection of decoded entities
+ * @throws java.lang.UnsupportedOperationException if the codec does not
+ * support decode operations
+ */
+ public List<T> decode(ArrayNode json) {
+ List<T> result = new ArrayList<>();
+ for (JsonNode node : json) {
+ result.add(decode((ObjectNode) node));
+ }
+ return result;
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/codec/package-info.java b/core/api/src/main/java/org/onlab/onos/codec/package-info.java
new file mode 100644
index 0000000..33d6d8c
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/codec/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Base JSON codec abstraction and a service for tracking various JSON codecs.
+ */
+package org.onlab.onos.codec;
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
index 4ba3366..d0d1820 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchEvent.java
@@ -30,7 +30,7 @@
* @param request batch operation request.
* @return event.
*/
- public static FlowRuleBatchEvent create(FlowRuleBatchRequest request) {
+ public static FlowRuleBatchEvent requested(FlowRuleBatchRequest request) {
FlowRuleBatchEvent event = new FlowRuleBatchEvent(Type.BATCH_OPERATION_REQUESTED, request, null);
return event;
}
@@ -41,7 +41,7 @@
* @param result completed batch operation result.
* @return event.
*/
- public static FlowRuleBatchEvent create(FlowRuleBatchRequest request, CompletedBatchOperation result) {
+ public static FlowRuleBatchEvent completed(FlowRuleBatchRequest request, CompletedBatchOperation result) {
FlowRuleBatchEvent event = new FlowRuleBatchEvent(Type.BATCH_OPERATION_COMPLETED, request, result);
return event;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
index 0414fcb..34e3d31 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/FlowRuleBatchRequest.java
@@ -9,10 +9,12 @@
public class FlowRuleBatchRequest {
+ private final int batchId;
private final List<FlowEntry> toAdd;
private final List<FlowEntry> toRemove;
- public FlowRuleBatchRequest(List<FlowEntry> toAdd, List<FlowEntry> toRemove) {
+ public FlowRuleBatchRequest(int batchId, List<FlowEntry> toAdd, List<FlowEntry> toRemove) {
+ this.batchId = batchId;
this.toAdd = Collections.unmodifiableList(toAdd);
this.toRemove = Collections.unmodifiableList(toRemove);
}
@@ -35,4 +37,8 @@
}
return new FlowRuleBatchOperation(entries);
}
+
+ public int batchId() {
+ return batchId;
+ }
}
diff --git a/core/api/src/test/java/org/onlab/onos/codec/JsonCodecTest.java b/core/api/src/test/java/org/onlab/onos/codec/JsonCodecTest.java
new file mode 100644
index 0000000..f3a10e3
--- /dev/null
+++ b/core/api/src/test/java/org/onlab/onos/codec/JsonCodecTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.codec;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test of the base JSON codec abstraction.
+ */
+public class JsonCodecTest {
+
+ private static class Foo {
+ final String name;
+
+ Foo(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final Foo other = (Foo) obj;
+ return Objects.equals(this.name, other.name);
+ }
+ }
+
+ private static class FooCodec extends JsonCodec<Foo> {
+ @Override
+ public ObjectNode encode(Foo entity, ObjectMapper mapper) {
+ return mapper.createObjectNode().put("name", entity.name);
+ }
+
+ @Override
+ public Foo decode(ObjectNode json) {
+ return new Foo(json.get("name").asText());
+ }
+ }
+
+ @Test
+ public void encode() {
+ Foo f1 = new Foo("foo");
+ Foo f2 = new Foo("bar");
+ FooCodec codec = new FooCodec();
+ ImmutableList<Foo> entities = ImmutableList.of(f1, f2);
+ ArrayNode json = codec.encode(entities, new ObjectMapper());
+ List<Foo> foos = codec.decode(json);
+ assertEquals("incorrect encode/decode", entities, foos);
+ }
+
+}
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 3ef9fc8..60ab307 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -2,12 +2,14 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
+import static org.onlab.util.Tools.namedThreads;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -74,6 +76,9 @@
private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
+ private final ExecutorService futureListeners =
+ Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleStore store;
@@ -92,6 +97,8 @@
@Deactivate
public void deactivate() {
+ futureListeners.shutdownNow();
+
store.unsetDelegate(delegate);
eventDispatcher.removeSink(FlowRuleEvent.class);
log.info("Stopped");
@@ -398,9 +405,10 @@
result.addListener(new Runnable() {
@Override
public void run() {
- store.batchOperationComplete(FlowRuleBatchEvent.create(request, Futures.getUnchecked(result)));
+ store.batchOperationComplete(FlowRuleBatchEvent.completed(request,
+ Futures.getUnchecked(result)));
}
- }, Executors.newCachedThreadPool());
+ }, futureListeners);
break;
case BATCH_OPERATION_COMPLETED:
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
index 4db23ce..b2f679c 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/ClusterCommunicationManager.java
@@ -103,7 +103,7 @@
final ControllerNode localNode = clusterService.getLocalNode();
for (NodeId nodeId : nodes) {
if (!nodeId.equals(localNode.id())) {
- ok = unicast(message, nodeId) && ok;
+ ok = unicastUnchecked(message, nodeId) && ok;
}
}
return ok;
@@ -124,6 +124,14 @@
}
}
+ private boolean unicastUnchecked(ClusterMessage message, NodeId toNodeId) throws IOException {
+ try {
+ return unicast(message, toNodeId);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
@Override
public ClusterMessageResponse sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException {
ControllerNode node = clusterService.getNode(toNodeId);
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
index 85f928a..e5aa3e8 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/DistributedFlowRuleStore.java
@@ -3,15 +3,20 @@
import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onlab.onos.store.flow.impl.FlowStoreMessageSubjects.*;
+import static org.onlab.util.Tools.namedThreads;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.List;
import org.apache.felix.scr.annotations.Activate;
@@ -22,7 +27,9 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.ApplicationId;
import org.onlab.onos.cluster.ClusterService;
+import org.onlab.onos.net.Device;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.device.DeviceService;
import org.onlab.onos.net.flow.CompletedBatchOperation;
import org.onlab.onos.net.flow.DefaultFlowEntry;
import org.onlab.onos.net.flow.FlowEntry;
@@ -52,8 +59,12 @@
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
/**
* Manages inventory of flow rules using a distributed state management protocol.
@@ -74,13 +85,26 @@
ArrayListMultimap.<Short, FlowRule>create();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- private ReplicaInfoService replicaInfoManager;
+ protected ReplicaInfoService replicaInfoManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- private ClusterCommunicationService clusterCommunicator;
+ protected ClusterCommunicationService clusterCommunicator;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- private ClusterService clusterService;
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ private final AtomicInteger localBatchIdGen = new AtomicInteger();
+
+
+ // FIXME switch to expiraing map/Cache?
+ private Map<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures = Maps.newConcurrentMap();
+
+ private final ExecutorService futureListeners =
+ Executors.newCachedThreadPool(namedThreads("flowstore-peer-responders"));
+
protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
@Override
@@ -97,36 +121,26 @@
@Activate
public void activate() {
- clusterCommunicator.addSubscriber(STORE_FLOW_RULE, new ClusterMessageHandler() {
+ clusterCommunicator.addSubscriber(APPLY_BATCH_FLOWS, new ClusterMessageHandler() {
@Override
- public void handle(ClusterMessage message) {
- FlowRule rule = SERIALIZER.decode(message.payload());
- log.info("received add request for {}", rule);
- storeFlowRule(rule);
- // FIXME what to respond.
- try {
- message.respond(SERIALIZER.encode("ACK"));
- } catch (IOException e) {
- log.error("Failed to respond back", e);
- }
- }
- });
+ public void handle(final ClusterMessage message) {
+ FlowRuleBatchOperation operation = SERIALIZER.decode(message.payload());
+ log.info("received batch request {}", operation);
+ final ListenableFuture<CompletedBatchOperation> f = storeBatchInternal(operation);
- clusterCommunicator.addSubscriber(DELETE_FLOW_RULE, new ClusterMessageHandler() {
+ f.addListener(new Runnable() {
- @Override
- public void handle(ClusterMessage message) {
- FlowRule rule = SERIALIZER.decode(message.payload());
- log.info("received delete request for {}", rule);
- deleteFlowRule(rule);
- // FIXME what to respond.
- try {
- message.respond(SERIALIZER.encode("ACK"));
- } catch (IOException e) {
- log.error("Failed to respond back", e);
- }
-
+ @Override
+ public void run() {
+ CompletedBatchOperation result = Futures.getUnchecked(f);
+ try {
+ message.respond(SERIALIZER.encode(result));
+ } catch (IOException e) {
+ log.error("Failed to respond back", e);
+ }
+ }
+ }, futureListeners);
}
});
@@ -159,7 +173,13 @@
// make it device specific.
@Override
public int getFlowRuleCount() {
- return flowEntries.size();
+ // implementing in-efficient operation for debugging purpose.
+ int sum = 0;
+ for (Device device : deviceService.getDevices()) {
+ final DeviceId did = device.id();
+ sum += Iterables.size(getFlowEntries(did));
+ }
+ return sum;
}
@Override
@@ -218,6 +238,7 @@
storeBatch(new FlowRuleBatchOperation(Arrays.asList(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule))));
}
+ @Override
public Future<CompletedBatchOperation> storeBatch(FlowRuleBatchOperation operation) {
if (operation.getOperations().isEmpty()) {
return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowEntry>emptySet()));
@@ -236,7 +257,7 @@
ClusterMessage message = new ClusterMessage(
clusterService.getLocalNode().id(),
- FlowStoreMessageSubjects.STORE_FLOW_RULE,
+ APPLY_BATCH_FLOWS,
SERIALIZER.encode(operation));
try {
@@ -250,7 +271,7 @@
return null;
}
- private Future<CompletedBatchOperation> storeBatchInternal(FlowRuleBatchOperation operation) {
+ private ListenableFuture<CompletedBatchOperation> storeBatchInternal(FlowRuleBatchOperation operation) {
List<FlowEntry> toRemove = new ArrayList<>();
List<FlowEntry> toAdd = new ArrayList<>();
// TODO: backup changes to hazelcast map
@@ -261,8 +282,8 @@
StoredFlowEntry entry = getFlowEntryInternal(flowRule);
if (entry != null) {
entry.setState(FlowEntryState.PENDING_REMOVE);
+ toRemove.add(entry);
}
- toRemove.add(entry);
} else if (op.equals(FlowRuleOperation.ADD)) {
StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule);
DeviceId deviceId = flowRule.deviceId();
@@ -276,9 +297,13 @@
if (toAdd.isEmpty() && toRemove.isEmpty()) {
return Futures.immediateFuture(new CompletedBatchOperation(true, Collections.<FlowEntry>emptySet()));
}
- notifyDelegate(FlowRuleBatchEvent.create(new FlowRuleBatchRequest(toAdd, toRemove)));
- // TODO: imlpement this.
- return Futures.immediateFailedFuture(new RuntimeException("Implement this."));
+
+ SettableFuture<CompletedBatchOperation> r = SettableFuture.create();
+ final int batchId = localBatchIdGen.incrementAndGet();
+
+ pendingFutures.put(batchId, r);
+ notifyDelegate(FlowRuleBatchEvent.requested(new FlowRuleBatchRequest(batchId, toAdd, toRemove)));
+ return r;
}
@Override
@@ -293,18 +318,9 @@
return addOrUpdateFlowRuleInternal(rule);
}
- ClusterMessage message = new ClusterMessage(
- clusterService.getLocalNode().id(),
- FlowStoreMessageSubjects.ADD_OR_UPDATE_FLOW_RULE,
- SERIALIZER.encode(rule));
-
- try {
- ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
- return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
- } catch (IOException | TimeoutException e) {
- // FIXME: throw a FlowStoreException
- throw new RuntimeException(e);
- }
+ log.error("Tried to update FlowRule {} state,"
+ + " while the Node was not the master.", rule);
+ return null;
}
private synchronized FlowRuleEvent addOrUpdateFlowRuleInternal(FlowEntry rule) {
@@ -338,18 +354,9 @@
return removeFlowRuleInternal(rule);
}
- ClusterMessage message = new ClusterMessage(
- clusterService.getLocalNode().id(),
- FlowStoreMessageSubjects.REMOVE_FLOW_RULE,
- SERIALIZER.encode(rule));
-
- try {
- ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
- return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
- } catch (IOException | TimeoutException e) {
- // FIXME: throw a FlowStoreException
- throw new RuntimeException(e);
- }
+ log.error("Tried to remove FlowRule {},"
+ + " while the Node was not the master.", rule);
+ return null;
}
private synchronized FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
@@ -364,6 +371,11 @@
@Override
public void batchOperationComplete(FlowRuleBatchEvent event) {
+ SettableFuture<CompletedBatchOperation> future
+ = pendingFutures.get(event.subject().batchId());
+ if (future != null) {
+ future.set(event.result());
+ }
notifyDelegate(event);
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/FlowStoreMessageSubjects.java b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/FlowStoreMessageSubjects.java
index ca833b8..ef68b55 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/FlowStoreMessageSubjects.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/flow/impl/FlowStoreMessageSubjects.java
@@ -7,10 +7,10 @@
*/
public final class FlowStoreMessageSubjects {
private FlowStoreMessageSubjects() {}
- public static final MessageSubject STORE_FLOW_RULE = new MessageSubject("peer-forward-store-flow-rule");
- public static final MessageSubject DELETE_FLOW_RULE = new MessageSubject("peer-forward-delete-flow-rule");
- public static final MessageSubject ADD_OR_UPDATE_FLOW_RULE =
- new MessageSubject("peer-forward-add-or-update-flow-rule");
- public static final MessageSubject REMOVE_FLOW_RULE = new MessageSubject("peer-forward-remove-flow-rule");
- public static final MessageSubject GET_FLOW_ENTRY = new MessageSubject("peer-forward-get-flow-entry");
+
+ public static final MessageSubject APPLY_BATCH_FLOWS
+ = new MessageSubject("peer-forward-apply-batch");
+
+ public static final MessageSubject GET_FLOW_ENTRY
+ = new MessageSubject("peer-forward-get-flow-entry");
}
diff --git a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
index 316a3b4..f9352fe 100644
--- a/core/store/hz/cluster/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
+++ b/core/store/hz/cluster/src/main/java/org/onlab/onos/store/mastership/impl/DistributedMastershipStore.java
@@ -58,7 +58,6 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
- @SuppressWarnings({ "unchecked", "rawtypes" })
@Override
@Activate
public void activate() {
@@ -76,9 +75,9 @@
}
};
- roleMap = new SMap(theInstance.getMap("nodeRoles"), this.serializer);
+ roleMap = new SMap<>(theInstance.<byte[], byte[]>getMap("nodeRoles"), this.serializer);
roleMap.addEntryListener((new RemoteMasterShipEventHandler()), true);
- terms = new SMap(theInstance.getMap("terms"), this.serializer);
+ terms = new SMap<>(theInstance.<byte[], byte[]>getMap("terms"), this.serializer);
clusterSize = theInstance.getAtomicLong("clustersize");
log.info("Started");
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
index 7fddb01..0e9e19c 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
@@ -5,6 +5,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.DefaultControllerNode;
@@ -27,12 +28,15 @@
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.CompletedBatchOperation;
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.FlowRuleBatchEntry;
+import org.onlab.onos.net.flow.FlowRuleBatchOperation;
import org.onlab.onos.net.flow.StoredFlowEntry;
import org.onlab.onos.net.flow.criteria.Criteria;
import org.onlab.onos.net.flow.criteria.Criterion;
@@ -80,6 +84,7 @@
Arrays.asList().getClass(),
HashMap.class,
HashSet.class,
+ LinkedList.class,
//
//
ControllerNode.State.class,
@@ -118,7 +123,11 @@
DefaultTrafficTreatment.class,
Instructions.DropInstruction.class,
Instructions.OutputInstruction.class,
- RoleInfo.class
+ RoleInfo.class,
+ FlowRuleBatchOperation.class,
+ CompletedBatchOperation.class,
+ FlowRuleBatchEntry.class,
+ FlowRuleBatchEntry.FlowRuleOperation.class
)
.register(URI.class, new URISerializer())
.register(NodeId.class, new NodeIdSerializer())
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
index bbfc263..82ea4e2 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleFlowRuleStore.java
@@ -1,19 +1,8 @@
package org.onlab.onos.store.trivial.impl;
-import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Future;
-
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.util.concurrent.Futures;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -40,9 +29,19 @@
import org.onlab.util.NewConcurrentHashMap;
import org.slf4j.Logger;
-import com.google.common.base.Function;
-import com.google.common.collect.FluentIterable;
-import com.google.common.util.concurrent.Futures;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Future;
+
+import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
+import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages inventory of flow rules using trivial in-memory implementation.
@@ -132,15 +131,15 @@
public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
// flatten and make iterator unmodifiable
return FluentIterable.from(getFlowTable(deviceId).values())
- .transformAndConcat(
- new Function<List<StoredFlowEntry>, Iterable<? extends FlowEntry>>() {
+ .transformAndConcat(
+ new Function<List<StoredFlowEntry>, Iterable<? extends FlowEntry>>() {
- @Override
- public Iterable<? extends FlowEntry> apply(
- List<StoredFlowEntry> input) {
- return Collections.unmodifiableList(input);
- }
- });
+ @Override
+ public Iterable<? extends FlowEntry> apply(
+ List<StoredFlowEntry> input) {
+ return Collections.unmodifiableList(input);
+ }
+ });
}
@Override
@@ -176,10 +175,10 @@
}
// new flow rule added
existing.add(f);
- notifyDelegate(FlowRuleBatchEvent.create(
- new FlowRuleBatchRequest(
- Arrays.<FlowEntry>asList(f),
- Collections.<FlowEntry>emptyList())));
+ notifyDelegate(FlowRuleBatchEvent.requested(
+ new FlowRuleBatchRequest(1, /* FIXME generate something */
+ Arrays.<FlowEntry>asList(f),
+ Collections.<FlowEntry>emptyList())));
}
}
@@ -194,10 +193,10 @@
synchronized (entry) {
entry.setState(FlowEntryState.PENDING_REMOVE);
// TODO: Should we notify only if it's "remote" event?
- notifyDelegate(FlowRuleBatchEvent.create(
- new FlowRuleBatchRequest(
- Collections.<FlowEntry>emptyList(),
- Arrays.<FlowEntry>asList(entry))));
+ notifyDelegate(FlowRuleBatchEvent.requested(
+ new FlowRuleBatchRequest(1, /* FIXME generate something */
+ Collections.<FlowEntry>emptyList(),
+ Arrays.<FlowEntry>asList(entry))));
}
}
}
diff --git a/docs/external.xml b/docs/external.xml
new file mode 100644
index 0000000..959b473
--- /dev/null
+++ b/docs/external.xml
@@ -0,0 +1,59 @@
+<?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>
+
+ <prerequisites>
+ <maven>3.0.0</maven>
+ </prerequisites>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-docs-external</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS Java API documentation</description>
+
+ <modules>
+ <module>..</module>
+ </modules>
+
+ <url>http://onlab.us/</url>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.10.1</version>
+ <configuration>
+ <show>package</show>
+ <excludePackageNames>org.onlab.thirdparty:*.impl:*.impl.*:org.onlab.onos.provider.*:org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli*:org.onlab.onos.tvue:org.onlab.onos.foo:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.optical:org.onlab.onos.config:org.onlab.onos.calendar:org.onlab.onos.sdnip*:org.onlab.onos.metrics</excludePackageNames>
+ <docfilessubdirs>true</docfilessubdirs>
+ <doctitle>ONOS Java API</doctitle>
+ <groups>
+ <group>
+ <title>Network Model & Services</title>
+ <packages>
+ org.onlab.onos:org.onlab.onos.*
+ </packages>
+ </group>
+ <group>
+ <title>Utilities</title>
+ <packages>
+ org.onlab.*
+ </packages>
+ </group>
+ </groups>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/docs/pom.xml b/docs/pom.xml
new file mode 100644
index 0000000..3a38f13
--- /dev/null
+++ b/docs/pom.xml
@@ -0,0 +1,85 @@
+<?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>
+
+ <prerequisites>
+ <maven>3.0.0</maven>
+ </prerequisites>
+
+ <parent>
+ <groupId>org.onlab.onos</groupId>
+ <artifactId>onos</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-docs</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS Java API documentation</description>
+
+ <modules>
+ <module>..</module>
+ </modules>
+
+ <url>http://onlab.us/</url>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.10.1</version>
+ <configuration>
+ <show>package</show>
+ <docfilessubdirs>true</docfilessubdirs>
+ <doctitle>ONOS Java API</doctitle>
+ <groups>
+ <group>
+ <title>Network Model & Services</title>
+ <packages>
+ org.onlab.onos:org.onlab.onos.*
+ </packages>
+ </group>
+ <group>
+ <title>Core Subsystems</title>
+ <packages>
+ org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl:org.onlab.onos.json:org.onlab.onos.json.*:org.onlab.onos.provider.host.impl:org.onlab.onos.provider.lldp.impl:org.onlab.onos.net.statistic.impl
+ </packages>
+ </group>
+ <group>
+ <title>OpenFlow Providers & Controller
+ </title>
+ <packages>
+ org.onlab.onos.provider.of.*:org.onlab.onos.openflow.*
+ </packages>
+ </group>
+ <group>
+ <title>Utilities</title>
+ <packages>
+ org.onlab.*
+ </packages>
+ </group>
+ <group>
+ <title>GUI, REST & Command-Line</title>
+ <packages>
+ org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli:org.onlab.onos.gui.*:org.onlab.onos.rest.*:org.onlab.onos.cli.*
+ </packages>
+ </group>
+ <group>
+ <title>Sample Applications</title>
+ <packages>
+ org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar:org.onlab.onos.sdnip:org.onlab.onos.sdnip.*:org.onlab.onos.optical:org.onlab.onos.optical.*:org.onlab.onos.metrics.*:org.onlab.onos.config
+ </packages>
+ </group>
+ </groups>
+ <excludePackageNames>org.onlab.thirdparty
+ </excludePackageNames>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/src/main/javadoc/doc-files/onos-subsystem.png b/docs/src/main/javadoc/doc-files/onos-subsystem.png
similarity index 100%
rename from src/main/javadoc/doc-files/onos-subsystem.png
rename to docs/src/main/javadoc/doc-files/onos-subsystem.png
Binary files differ
diff --git a/src/main/javadoc/doc-files/onos-tiers.png b/docs/src/main/javadoc/doc-files/onos-tiers.png
similarity index 100%
rename from src/main/javadoc/doc-files/onos-tiers.png
rename to docs/src/main/javadoc/doc-files/onos-tiers.png
Binary files differ
diff --git a/src/main/javadoc/overview.html b/docs/src/main/javadoc/overview.html
similarity index 100%
rename from src/main/javadoc/overview.html
rename to docs/src/main/javadoc/overview.html
diff --git a/features/features.xml b/features/features.xml
index e363094..be9110b 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -35,7 +35,7 @@
<bundle>mvn:io.netty/netty-transport-native-epoll/4.0.23.Final</bundle>
<bundle>mvn:commons-pool/commons-pool/1.6</bundle>
- <bundle>mvn:com.hazelcast/hazelcast/3.3</bundle>
+ <bundle>mvn:com.hazelcast/hazelcast/3.3.2</bundle>
<bundle>mvn:io.dropwizard.metrics/metrics-core/3.1.0</bundle>
<bundle>mvn:io.dropwizard.metrics/metrics-json/3.1.0</bundle>
<bundle>mvn:com.eclipsesource.minimal-json/minimal-json/0.9.1</bundle>
diff --git a/pom.xml b/pom.xml
index 8aa5ce4..ce02451 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,6 +4,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.0.0</maven>
+ </prerequisites>
+
<groupId>org.onlab.onos</groupId>
<artifactId>onos</artifactId>
<packaging>pom</packaging>
@@ -28,7 +32,6 @@
<licenses>
<license>
- <!-- TODO: Is this really our license scheme? -->
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
@@ -192,7 +195,7 @@
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
- <version>3.3</version>
+ <version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.eclipsesource.minimal-json</groupId>
@@ -528,59 +531,6 @@
</execution>
</executions>
</plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.10.1</version>
- <configuration>
- <show>package</show>
- <docfilessubdirs>true</docfilessubdirs>
- <doctitle>ONOS Java API</doctitle>
- <groups>
- <group>
- <title>Network Model & Services</title>
- <packages>
- org.onlab.onos:org.onlab.onos.*
- </packages>
- </group>
- <group>
- <title>Core Subsystems</title>
- <packages>
- org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl:org.onlab.onos.json:org.onlab.onos.json.*:org.onlab.onos.provider.host.impl:org.onlab.onos.provider.lldp.impl:org.onlab.onos.net.statistic.impl
- </packages>
- </group>
- <group>
- <title>OpenFlow Providers & Controller
- </title>
- <packages>
- org.onlab.onos.provider.of.*:org.onlab.onos.openflow.*
- </packages>
- </group>
- <group>
- <title>Utilities</title>
- <packages>
- org.onlab.*
- </packages>
- </group>
- <group>
- <title>GUI, REST & Command-Line</title>
- <packages>
- org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli:org.onlab.onos.gui.*:org.onlab.onos.rest.*:org.onlab.onos.cli.*
- </packages>
- </group>
- <group>
- <title>Sample Applications</title>
- <packages>
- org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar:org.onlab.onos.sdnip:org.onlab.onos.sdnip.*:org.onlab.onos.optical:org.onlab.onos.optical.*:org.onlab.onos.metrics.*
- </packages>
- </group>
- </groups>
- <excludePackageNames>org.onlab.thirdparty
- </excludePackageNames>
- </configuration>
- </plugin>
-
</plugins>
</build>
@@ -607,10 +557,6 @@
</rulesets>
</configuration>
</plugin>
-
</plugins>
</reporting>
- <prerequisites>
- <maven>3.0.0</maven>
- </prerequisites>
</project>
diff --git a/tools/build/onos-build b/tools/build/onos-build
index cf2ebe4..2f31eeb 100755
--- a/tools/build/onos-build
+++ b/tools/build/onos-build
@@ -6,5 +6,4 @@
[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
. $ONOS_ROOT/tools/build/envDefaults
-cd $ONOS_ROOT
-mvn clean install && mvn javadoc:aggregate
+cd $ONOS_ROOT && mvn clean install && cd docs && mvn javadoc:aggregate
diff --git a/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java b/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java
new file mode 100644
index 0000000..464a123
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/metrics/EventMetric.java
@@ -0,0 +1,103 @@
+package org.onlab.metrics;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Meter;
+
+/**
+ * Metric measurements for events.
+ */
+public class EventMetric {
+ private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
+ private static final String METER_RATE_NAME = "Rate";
+
+ private final MetricsService metricsService;
+ private final String componentName;
+ private final String featureName;
+
+ private MetricsComponent metricsComponent;
+ private MetricsFeature metricsFeature;
+
+ private volatile long lastEventTimestampEpochMs = 0;
+ private Gauge<Long> lastEventTimestampGauge;
+ private Meter eventRateMeter;
+
+ /**
+ * Constructor.
+ *
+ * @param metricsService the Metrics Service to use for Metrics
+ * registration and deregistration
+ * @param componentName the Metrics Component Name to use for Metrics
+ * registration and deregistration
+ * @param featureName the Metrics Feature Name to use for Metrics
+ * registration and deregistration
+ */
+ public EventMetric(MetricsService metricsService, String componentName,
+ String featureName) {
+ this.metricsService = metricsService;
+ this.componentName = componentName;
+ this.featureName = featureName;
+ }
+
+ /**
+ * Registers the metrics.
+ */
+ public void registerMetrics() {
+ metricsComponent = metricsService.registerComponent(componentName);
+ metricsFeature = metricsComponent.registerFeature(featureName);
+
+ lastEventTimestampEpochMs = 0;
+ lastEventTimestampGauge =
+ metricsService.registerMetric(metricsComponent,
+ metricsFeature,
+ GAUGE_TIMESTAMP_NAME,
+ new Gauge<Long>() {
+ @Override
+ public Long getValue() {
+ return lastEventTimestampEpochMs;
+ }
+ });
+
+ eventRateMeter = metricsService.createMeter(metricsComponent,
+ metricsFeature,
+ METER_RATE_NAME);
+ }
+
+ /**
+ * Removes the metrics.
+ */
+ public void removeMetrics() {
+ lastEventTimestampEpochMs = 0;
+ metricsService.removeMetric(metricsComponent,
+ metricsFeature,
+ GAUGE_TIMESTAMP_NAME);
+ metricsService.removeMetric(metricsComponent,
+ metricsFeature,
+ METER_RATE_NAME);
+ }
+
+ /**
+ * Updates the metric measurements for a single event.
+ */
+ public void eventReceived() {
+ lastEventTimestampEpochMs = System.currentTimeMillis();
+ eventRateMeter.mark(1);
+ }
+
+ /**
+ * Gets the last event timestamp Gauge (ms from the Epoch).
+ *
+ * @return the last event timestamp Gauge (ms from the Epoch)
+ */
+ public Gauge<Long> lastEventTimestampGauge() {
+ return lastEventTimestampGauge;
+ }
+
+ /**
+ * Gets the event rate meter.
+ *
+ * @return the event rate meter
+ */
+ public Meter eventRateMeter() {
+ return eventRateMeter;
+ }
+}
diff --git a/web/gui/src/main/webapp/network.js b/web/gui/src/main/webapp/network.js
index 80d11b7..ef190e7 100644
--- a/web/gui/src/main/webapp/network.js
+++ b/web/gui/src/main/webapp/network.js
@@ -10,17 +10,33 @@
var api = onos.api;
var config = {
- layering: false,
+ options: {
+ layering: false,
+ collisionPrevention: true
+ },
jsonUrl: 'network.json',
iconUrl: {
- pkt: 'pkt.png',
- opt: 'opt.png'
+ logo: 'img/onos-logo.tiff',
+ device: 'img/device.png',
+ host: 'img/host.png',
+ pkt: 'img/pkt.png',
+ opt: 'img/opt.png'
},
mastHeight: 32,
force: {
- linkDistance: 240,
- linkStrength: 0.8,
- charge: -400,
+ note: 'node.class or link.class is used to differentiate',
+ linkDistance: {
+ infra: 240,
+ host: 100
+ },
+ linkStrength: {
+ infra: 1.0,
+ host: 0.4
+ },
+ charge: {
+ device: -800,
+ host: -400
+ },
ticksWithoutCollisions: 50,
marginLR: 20,
marginTB: 20,
@@ -37,12 +53,21 @@
marginLR: 3,
marginTB: 2
},
+ icons: {
+ w: 32,
+ h: 32,
+ xoff: -12,
+ yoff: -10
+ },
constraints: {
ypos: {
- pkt: 0.3,
- opt: 0.7
+ host: 0.15,
+ switch: 0.3,
+ roadm: 0.7
}
- }
+ },
+ hostLinkWidth: 1.0,
+ mouseOutTimerDelayMs: 120
},
view = {},
network = {},
@@ -104,14 +129,23 @@
var nw = network.forceWidth,
nh = network.forceHeight;
- network.data.nodes.forEach(function(n) {
+ function yPosConstraintForNode(n) {
+ return config.constraints.ypos[n.type || 'host'];
+ }
+
+ // Note that both 'devices' and 'hosts' get mapped into the nodes array
+
+ // first, the devices...
+ network.data.devices.forEach(function(n) {
var ypc = yPosConstraintForNode(n),
ix = Math.random() * 0.6 * nw + 0.2 * nw,
iy = ypc * nh,
node = {
id: n.id,
+ labels: n.labels,
+ class: 'device',
+ icon: 'device',
type: n.type,
- status: n.status,
x: ix,
y: iy,
constraint: {
@@ -123,21 +157,61 @@
network.nodes.push(node);
});
- function yPosConstraintForNode(n) {
- return config.constraints.ypos[n.type] || 0.5;
- }
+ // then, the hosts...
+ network.data.hosts.forEach(function(n) {
+ var ypc = yPosConstraintForNode(n),
+ ix = Math.random() * 0.6 * nw + 0.2 * nw,
+ iy = ypc * nh,
+ node = {
+ id: n.id,
+ labels: n.labels,
+ class: 'host',
+ icon: 'host',
+ type: n.type,
+ x: ix,
+ y: iy,
+ constraint: {
+ weight: 0.7,
+ y: iy
+ }
+ };
+ network.lookup[n.id] = node;
+ network.nodes.push(node);
+ });
+ // now, process the explicit links...
network.data.links.forEach(function(n) {
var src = network.lookup[n.src],
dst = network.lookup[n.dst],
id = src.id + "~" + dst.id;
var link = {
+ class: 'infra',
id: id,
+ type: n.type,
+ width: n.linkWidth,
source: src,
target: dst,
- strength: config.force.linkStrength
+ strength: config.force.linkStrength.infra
+ };
+ network.links.push(link);
+ });
+
+ // finally, infer host links...
+ network.data.hosts.forEach(function(n) {
+ var src = network.lookup[n.id],
+ dst = network.lookup[n.cp.device],
+ id = src.id + "~" + dst.id;
+
+ var link = {
+ class: 'host',
+ id: id,
+ type: 'hostLink',
+ width: config.hostLinkWidth,
+ source: src,
+ target: dst,
+ strength: config.force.linkStrength.host
};
network.links.push(link);
});
@@ -145,13 +219,15 @@
function createLayout() {
+ var cfg = config.force;
+
network.force = d3.layout.force()
+ .size([network.forceWidth, network.forceHeight])
.nodes(network.nodes)
.links(network.links)
- .linkStrength(function(d) { return d.strength; })
- .size([network.forceWidth, network.forceHeight])
- .linkDistance(config.force.linkDistance)
- .charge(config.force.charge)
+ .linkStrength(function(d) { return cfg.linkStrength[d.class]; })
+ .linkDistance(function(d) { return cfg.linkDistance[d.class]; })
+ .charge(function(d) { return cfg.charge[d.class]; })
.on('tick', tick);
network.svg = d3.select('#view').append('svg')
@@ -205,9 +281,10 @@
network.link = network.svg.append('g').selectAll('.link')
.data(network.force.links(), function(d) {return d.id})
.enter().append('line')
- .attr('class', 'link');
+ .attr('class', function(d) {return 'link ' + d.class});
- // TODO: drag behavior
+
+ // == define node drag behavior...
network.draggedThreshold = d3.scale.linear()
.domain([0, 0.1])
.range([5, 20])
@@ -258,7 +335,11 @@
.data(network.force.nodes(), function(d) {return d.id})
.enter().append('g')
.attr('class', function(d) {
- return 'node ' + d.type;
+ var cls = 'node ' + d.class;
+ if (d.type) {
+ cls += ' ' + d.type;
+ }
+ return cls;
})
.attr('transform', function(d) {
return translate(d.x, d.y);
@@ -281,29 +362,32 @@
}
network.mouseoutTimeout = setTimeout(function() {
highlightObject(null);
- }, 160);
+ }, config.mouseOutTimerDelayMs);
}
});
network.nodeRect = network.node.append('rect')
.attr('rx', 5)
- .attr('ry', 5)
- .attr('width', 126)
- .attr('height', 40);
+ .attr('ry', 5);
+ // note that width/height are adjusted to fit the label text
network.node.each(function(d) {
var node = d3.select(this),
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)),
+ icon = iconUrl(d),
text = node.append('text')
.text(d.id)
- .attr('dy', '1.1em'),
- dummy;
+ .attr('dy', '1.1em');
+
+ if (icon) {
+ var cfg = config.icons;
+ node.append('svg:image')
+ .attr('width', cfg.w)
+ .attr('height', cfg.h)
+ .attr('xlink:href', icon);
+ // note, icon relative positioning (x,y) is done after we have
+ // adjusted the bounds of the rectangle...
+ }
});
@@ -352,7 +436,8 @@
.attr('height', bounds.y2 - bounds.y1);
node.select('image')
- .attr('x', bounds.x1);
+ .attr('x', bounds.x1 + config.icons.xoff)
+ .attr('y', bounds.y1 + config.icons.yoff);
d.extent = {
left: bounds.x1 - lab.marginLR,
@@ -384,7 +469,7 @@
}
function iconUrl(d) {
- return config.iconUrl[d.type];
+ return config.iconUrl[d.icon];
}
function translate(x, y) {
@@ -440,7 +525,7 @@
function tick(e) {
network.numTicks++;
- if (config.layering) {
+ if (config.options.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;
@@ -450,7 +535,7 @@
});
}
- if (network.preventCollisions) {
+ if (config.options.collisionPrevention && network.preventCollisions) {
preventCollisions();
}
diff --git a/web/gui/src/main/webapp/network.json b/web/gui/src/main/webapp/network.json
index b4f1b6e..c43e0ef 100644
--- a/web/gui/src/main/webapp/network.json
+++ b/web/gui/src/main/webapp/network.json
@@ -1,56 +1,163 @@
{
- "id": "network-v1",
"meta": {
- "__comment_1__": "This is sample data for developing the ONOS UI",
- "foo": "bar",
- "zoo": "goo"
+ "comments": [
+ "This is sample data for developing the ONOS UI (network view)",
+ " in a standalone mode (no server required).",
+ " Eventually, we will wire this up to live data",
+ " from the server, via a websocket.",
+ "",
+ "Note that this is just a first-draft of the data --",
+ " additional fields will be added when they are needed."
+ ],
+ "otherMetaData": "can go here..."
},
- "nodes": [
+ "devices": [
{
- "id": "sample1",
- "type": "opt",
- "status": "good"
+ "id": "of:0000000000000001",
+ "labels": ["00:00:00:00:00:00:00:01", "of/::01", "opt-1"],
+ "type": "roadm"
},
{
- "id": "00:00:00:00:00:00:00:02",
- "type": "opt",
- "status": "good"
+ "id": "of:0000000000000002",
+ "labels": ["00:00:00:00:00:00:00:02", "of/::02", "opt-2"],
+ "type": "roadm"
},
{
- "id": "00:00:00:00:00:00:00:03",
- "type": "opt",
- "status": "good"
+ "id": "of:0000000000000003",
+ "labels": ["00:00:00:00:00:00:00:03", "of/::03", "opt-3"],
+ "type": "roadm"
},
{
- "id": "00:00:00:00:00:00:00:04",
- "type": "opt",
- "status": "good"
+ "id": "of:0000000000000004",
+ "labels": ["00:00:00:00:00:00:00:04", "of/::04", "opt-4"],
+ "type": "roadm"
},
{
- "id": "00:00:00:00:00:00:00:11",
- "type": "pkt",
- "status": "good"
+ "id": "of:0000000000000011",
+ "labels": ["00:00:00:00:00:00:00:11", "of/::11", "pkt-11"],
+ "type": "switch"
},
{
- "id": "00:00:00:00:00:00:00:12",
- "type": "pkt",
- "status": "good"
+ "id": "of:0000000000000012",
+ "labels": ["00:00:00:00:00:00:00:12", "of/::12", "pkt-12"],
+ "type": "switch"
},
{
- "id": "00:00:00:00:00:00:00:13",
- "type": "pkt",
- "status": "good"
+ "id": "of:0000000000000013",
+ "labels": ["00:00:00:00:00:00:00:13", "of/::13", "pkt-13"],
+ "type": "switch"
}
],
+ "linkNotes": [
+ "even though we have 'directionality' (src/dst), each link is",
+ " considered to be bi-directional. Note that links between hosts",
+ " and edge switches are inferred from host information, and not",
+ " explicitly represented here."
+ ],
"links": [
- { "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" }
+ {
+ "src": "of:0000000000000001",
+ "dst": "of:0000000000000002",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000001",
+ "dst": "of:0000000000000003",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000001",
+ "dst": "of:0000000000000004",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000002",
+ "dst": "of:0000000000000003",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000002",
+ "dst": "of:0000000000000004",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000003",
+ "dst": "of:0000000000000004",
+ "type": "optical",
+ "linkWidth": 1.5
+ },
+ {
+ "src": "of:0000000000000013",
+ "dst": "of:0000000000000003",
+ "type": "direct",
+ "linkWidth": 1.0
+ },
+ {
+ "src": "of:0000000000000012",
+ "dst": "of:0000000000000002",
+ "type": "direct",
+ "linkWidth": 1.0
+ },
+ {
+ "src": "of:0000000000000011",
+ "dst": "of:0000000000000001",
+ "type": "direct",
+ "linkWidth": 1.0
+ }
+ ],
+ "hosts": [
+ {
+ "id": "00:60:d3:00:11:01/7",
+ "labels": ["00:60:d3:00:11:01/7", "Host-11-A"],
+ "cp" : {
+ "device": "of:0000000000000011",
+ "port": 6
+ }
+ },
+ {
+ "id": "00:60:d3:00:11:02/7",
+ "labels": ["00:60:d3:00:11:02/7", "Host-11-B"],
+ "cp" : {
+ "device": "of:0000000000000011",
+ "port": 8
+ }
+ },
+ {
+ "id": "00:60:d3:00:12:01/4",
+ "labels": ["00:60:d3:00:12:01/4", "Host-12-C"],
+ "cp" : {
+ "device": "of:0000000000000012",
+ "port": 12
+ }
+ },
+ {
+ "id": "00:60:d3:00:12:02/4",
+ "labels": ["00:60:d3:00:12:02/4", "Host-12-D"],
+ "cp" : {
+ "device": "of:0000000000000012",
+ "port": 13
+ }
+ },
+ {
+ "id": "00:60:d3:00:13:01/19",
+ "labels": ["00:60:d3:00:13:01/19", "Host-13-E"],
+ "cp" : {
+ "device": "of:0000000000000013",
+ "port": 7
+ }
+ },
+ {
+ "id": "00:60:d3:00:13:02/19",
+ "labels": ["00:60:d3:00:13:02/19", "Host-13-F"],
+ "cp" : {
+ "device": "of:0000000000000013",
+ "port": 8
+ }
+ }
]
}
diff --git a/web/gui/src/main/webapp/onos.css b/web/gui/src/main/webapp/onos.css
index 340ae79..b2df75c 100644
--- a/web/gui/src/main/webapp/onos.css
+++ b/web/gui/src/main/webapp/onos.css
@@ -38,7 +38,7 @@
* Network Graph elements ======================================
*/
-.link {
+svg .link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
@@ -56,7 +56,7 @@
stroke-width: 1.5px;
}
-.node rect {
+svg .node rect {
stroke-width: 1.5px;
transition: opacity 250ms;
@@ -64,13 +64,15 @@
-moz-transition: opacity 250ms;
}
-/*differentiate between packet and optical nodes*/
-svg .node.pkt rect {
- fill: #77a;
+svg .node.device.roadm rect {
+ fill: #229;
+}
+svg .node.device.switch rect {
+ fill: #55f;
}
-svg .node.opt rect {
- fill: #7a7;
+svg .node.host rect {
+ fill: #787;
}
svg .node text {
@@ -121,15 +123,13 @@
#frame {
width: 100%;
height: 100%;
- background-color: #cdf;
+ background-color: #fff;
}
#mast {
height: 32px;
- background-color: #abe;
+ padding: 6px;
+ background-color: #ccc;
vertical-align: baseline;
}
-#main {
- background-color: #99c;
-}
diff --git a/web/gui/src/main/webapp/opt.png b/web/gui/src/main/webapp/opt.png
deleted file mode 100644
index 2f2c88e..0000000
--- a/web/gui/src/main/webapp/opt.png
+++ /dev/null
Binary files differ
diff --git a/web/gui/src/main/webapp/pkt.png b/web/gui/src/main/webapp/pkt.png
deleted file mode 100644
index 1b1d5a3..0000000
--- a/web/gui/src/main/webapp/pkt.png
+++ /dev/null
Binary files differ