alshabib

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

Showing 34 changed files with 1801 additions and 162 deletions
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onos-app-metrics</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-app-metrics-intent</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS intent metrics application</description>
18 +
19 + <dependencies>
20 + <dependency>
21 + <groupId>org.onlab.onos</groupId>
22 + <artifactId>onos-cli</artifactId>
23 + <version>${project.version}</version>
24 + </dependency>
25 +
26 + <dependency>
27 + <groupId>org.apache.karaf.shell</groupId>
28 + <artifactId>org.apache.karaf.shell.console</artifactId>
29 + </dependency>
30 + </dependencies>
31 +
32 +</project>
1 +package org.onlab.onos.metrics.intent;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import java.util.LinkedList;
6 +import java.util.List;
7 +
8 +import com.codahale.metrics.Gauge;
9 +import com.codahale.metrics.Meter;
10 +import com.google.common.collect.ImmutableList;
11 +import org.apache.felix.scr.annotations.Activate;
12 +import org.apache.felix.scr.annotations.Component;
13 +import org.apache.felix.scr.annotations.Deactivate;
14 +import org.apache.felix.scr.annotations.Reference;
15 +import org.apache.felix.scr.annotations.ReferenceCardinality;
16 +import org.apache.felix.scr.annotations.Service;
17 +import org.onlab.metrics.MetricsComponent;
18 +import org.onlab.metrics.MetricsFeature;
19 +import org.onlab.metrics.MetricsService;
20 +import org.onlab.onos.net.intent.IntentEvent;
21 +import org.onlab.onos.net.intent.IntentListener;
22 +import org.onlab.onos.net.intent.IntentService;
23 +import org.slf4j.Logger;
24 +
25 +/**
26 + * ONOS Intent Metrics Application that collects intent-related metrics.
27 + */
28 +@Component(immediate = true)
29 +@Service
30 +public class IntentMetrics implements IntentMetricsService,
31 + IntentListener {
32 + private static final Logger log = getLogger(IntentMetrics.class);
33 +
34 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
35 + protected IntentService intentService;
36 + private LinkedList<IntentEvent> lastEvents = new LinkedList<>();
37 + private static final int LAST_EVENTS_MAX_N = 100;
38 +
39 + //
40 + // Metrics
41 + //
42 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
43 + protected MetricsService metricsService;
44 + //
45 + private static final String COMPONENT_NAME = "Intent";
46 + private static final String FEATURE_SUBMITTED_NAME = "Submitted";
47 + private static final String FEATURE_INSTALLED_NAME = "Installed";
48 + private static final String FEATURE_WITHDRAW_REQUESTED_NAME =
49 + "WithdrawRequested";
50 + private static final String FEATURE_WITHDRAWN_NAME = "Withdrawn";
51 + private static final String GAUGE_TIMESTAMP_NAME = "Timestamp.EpochMs";
52 + private static final String METER_RATE_NAME = "Rate";
53 + //
54 + private MetricsComponent metricsComponent;
55 + private MetricsFeature metricsFeatureSubmitted;
56 + private MetricsFeature metricsFeatureInstalled;
57 + private MetricsFeature metricsFeatureWithdrawRequested;
58 + private MetricsFeature metricsFeatureWithdrawn;
59 + //
60 + // Timestamps:
61 + // - Intent Submitted API operation (ms from the Epoch)
62 + // - Intent Installed operation completion (ms from the Epoch)
63 + // - Intent Withdraw Requested API operation (ms from the Epoch)
64 + // - Intent Withdrawn operation completion (ms from the Epoch)
65 + //
66 + private volatile long intentSubmittedTimestampEpochMs = 0;
67 + private volatile long intentInstalledTimestampEpochMs = 0;
68 + private volatile long intentWithdrawRequestedTimestampEpochMs = 0;
69 + private volatile long intentWithdrawnTimestampEpochMs = 0;
70 + //
71 + private Gauge<Long> intentSubmittedTimestampEpochMsGauge;
72 + private Gauge<Long> intentInstalledTimestampEpochMsGauge;
73 + private Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge;
74 + private Gauge<Long> intentWithdrawnTimestampEpochMsGauge;
75 + //
76 + // Rate meters:
77 + // - Rate of the Submitted Intent API operations
78 + // - Rate of the Installed Intent operations
79 + // - Rate of the Withdrawn Requested Intent API operations
80 + // - Rate of the Withdrawn Intent operations
81 + //
82 + private Meter intentSubmittedRateMeter;
83 + private Meter intentInstalledRateMeter;
84 + private Meter intentWithdrawRequestedRateMeter;
85 + private Meter intentWithdrawnRateMeter;
86 +
87 + @Activate
88 + protected void activate() {
89 + clear();
90 + registerMetrics();
91 + intentService.addListener(this);
92 + log.info("ONOS Intent Metrics started.");
93 + }
94 +
95 + @Deactivate
96 + public void deactivate() {
97 + intentService.removeListener(this);
98 + removeMetrics();
99 + clear();
100 + log.info("ONOS Intent Metrics stopped.");
101 + }
102 +
103 + @Override
104 + public List<IntentEvent> getEvents() {
105 + synchronized (lastEvents) {
106 + return ImmutableList.<IntentEvent>copyOf(lastEvents);
107 + }
108 + }
109 +
110 + @Override
111 + public Gauge<Long> intentSubmittedTimestampEpochMsGauge() {
112 + return intentSubmittedTimestampEpochMsGauge;
113 + }
114 +
115 + @Override
116 + public Gauge<Long> intentInstalledTimestampEpochMsGauge() {
117 + return intentInstalledTimestampEpochMsGauge;
118 + }
119 +
120 + @Override
121 + public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge() {
122 + return intentWithdrawRequestedTimestampEpochMsGauge;
123 + }
124 +
125 + @Override
126 + public Gauge<Long> intentWithdrawnTimestampEpochMsGauge() {
127 + return intentWithdrawnTimestampEpochMsGauge;
128 + }
129 +
130 + @Override
131 + public Meter intentSubmittedRateMeter() {
132 + return intentSubmittedRateMeter;
133 + }
134 +
135 + @Override
136 + public Meter intentInstalledRateMeter() {
137 + return intentInstalledRateMeter;
138 + }
139 +
140 + @Override
141 + public Meter intentWithdrawRequestedRateMeter() {
142 + return intentWithdrawRequestedRateMeter;
143 + }
144 +
145 + @Override
146 + public Meter intentWithdrawnRateMeter() {
147 + return intentWithdrawnRateMeter;
148 + }
149 +
150 + @Override
151 + public void event(IntentEvent event) {
152 + synchronized (lastEvents) {
153 + //
154 + // TODO: The processing below is incomplete: we don't have
155 + // an event equivalent of "Withdraw Requested"
156 + //
157 + switch (event.type()) {
158 + case SUBMITTED:
159 + intentSubmittedTimestampEpochMs = System.currentTimeMillis();
160 + intentSubmittedRateMeter.mark(1);
161 + break;
162 + case INSTALLED:
163 + intentInstalledTimestampEpochMs = System.currentTimeMillis();
164 + intentInstalledRateMeter.mark(1);
165 + break;
166 + case FAILED:
167 + // TODO: Just ignore?
168 + break;
169 + /*
170 + case WITHDRAW_REQUESTED:
171 + intentWithdrawRequestedTimestampEpochMs =
172 + System.currentTimeMillis();
173 + intentWithdrawRequestedRateMeter.mark(1);
174 + break;
175 + */
176 + case WITHDRAWN:
177 + intentWithdrawnTimestampEpochMs = System.currentTimeMillis();
178 + intentWithdrawnRateMeter.mark(1);
179 + break;
180 + default:
181 + break;
182 + }
183 +
184 + //
185 + // Keep only the last N events, where N = LAST_EVENTS_MAX_N
186 + //
187 + while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
188 + lastEvents.remove();
189 + }
190 + lastEvents.add(event);
191 + }
192 +
193 + log.debug("Intent Event: time = {} type = {} event = {}",
194 + event.time(), event.type(), event);
195 + }
196 +
197 + /**
198 + * Clears the internal state.
199 + */
200 + private void clear() {
201 + synchronized (lastEvents) {
202 + intentSubmittedTimestampEpochMs = 0;
203 + intentInstalledTimestampEpochMs = 0;
204 + intentWithdrawRequestedTimestampEpochMs = 0;
205 + intentWithdrawnTimestampEpochMs = 0;
206 + lastEvents.clear();
207 + }
208 + }
209 +
210 + /**
211 + * Registers the metrics.
212 + */
213 + private void registerMetrics() {
214 + metricsComponent = metricsService.registerComponent(COMPONENT_NAME);
215 + //
216 + metricsFeatureSubmitted =
217 + metricsComponent.registerFeature(FEATURE_SUBMITTED_NAME);
218 + metricsFeatureInstalled =
219 + metricsComponent.registerFeature(FEATURE_INSTALLED_NAME);
220 + metricsFeatureWithdrawRequested =
221 + metricsComponent.registerFeature(FEATURE_WITHDRAW_REQUESTED_NAME);
222 + metricsFeatureWithdrawn =
223 + metricsComponent.registerFeature(FEATURE_WITHDRAWN_NAME);
224 + //
225 + intentSubmittedTimestampEpochMsGauge =
226 + metricsService.registerMetric(metricsComponent,
227 + metricsFeatureSubmitted,
228 + GAUGE_TIMESTAMP_NAME,
229 + new Gauge<Long>() {
230 + @Override
231 + public Long getValue() {
232 + return intentSubmittedTimestampEpochMs;
233 + }
234 + });
235 + //
236 + intentInstalledTimestampEpochMsGauge =
237 + metricsService.registerMetric(metricsComponent,
238 + metricsFeatureInstalled,
239 + GAUGE_TIMESTAMP_NAME,
240 + new Gauge<Long>() {
241 + @Override
242 + public Long getValue() {
243 + return intentInstalledTimestampEpochMs;
244 + }
245 + });
246 + //
247 + intentWithdrawRequestedTimestampEpochMsGauge =
248 + metricsService.registerMetric(metricsComponent,
249 + metricsFeatureWithdrawRequested,
250 + GAUGE_TIMESTAMP_NAME,
251 + new Gauge<Long>() {
252 + @Override
253 + public Long getValue() {
254 + return intentWithdrawRequestedTimestampEpochMs;
255 + }
256 + });
257 + //
258 + intentWithdrawnTimestampEpochMsGauge =
259 + metricsService.registerMetric(metricsComponent,
260 + metricsFeatureWithdrawn,
261 + GAUGE_TIMESTAMP_NAME,
262 + new Gauge<Long>() {
263 + @Override
264 + public Long getValue() {
265 + return intentWithdrawnTimestampEpochMs;
266 + }
267 + });
268 + //
269 + intentSubmittedRateMeter =
270 + metricsService.createMeter(metricsComponent,
271 + metricsFeatureSubmitted,
272 + METER_RATE_NAME);
273 + //
274 + intentInstalledRateMeter =
275 + metricsService.createMeter(metricsComponent,
276 + metricsFeatureInstalled,
277 + METER_RATE_NAME);
278 + //
279 + intentWithdrawRequestedRateMeter =
280 + metricsService.createMeter(metricsComponent,
281 + metricsFeatureWithdrawRequested,
282 + METER_RATE_NAME);
283 + //
284 + intentWithdrawnRateMeter =
285 + metricsService.createMeter(metricsComponent,
286 + metricsFeatureWithdrawn,
287 + METER_RATE_NAME);
288 + }
289 +
290 + /**
291 + * Removes the metrics.
292 + */
293 + private void removeMetrics() {
294 + metricsService.removeMetric(metricsComponent,
295 + metricsFeatureSubmitted,
296 + GAUGE_TIMESTAMP_NAME);
297 + metricsService.removeMetric(metricsComponent,
298 + metricsFeatureInstalled,
299 + GAUGE_TIMESTAMP_NAME);
300 + metricsService.removeMetric(metricsComponent,
301 + metricsFeatureWithdrawRequested,
302 + GAUGE_TIMESTAMP_NAME);
303 + metricsService.removeMetric(metricsComponent,
304 + metricsFeatureWithdrawn,
305 + GAUGE_TIMESTAMP_NAME);
306 + metricsService.removeMetric(metricsComponent,
307 + metricsFeatureSubmitted,
308 + METER_RATE_NAME);
309 + metricsService.removeMetric(metricsComponent,
310 + metricsFeatureInstalled,
311 + METER_RATE_NAME);
312 + metricsService.removeMetric(metricsComponent,
313 + metricsFeatureWithdrawRequested,
314 + METER_RATE_NAME);
315 + metricsService.removeMetric(metricsComponent,
316 + metricsFeatureWithdrawn,
317 + METER_RATE_NAME);
318 + }
319 +}
1 +package org.onlab.onos.metrics.intent;
2 +
3 +import java.util.List;
4 +
5 +import com.codahale.metrics.Gauge;
6 +import com.codahale.metrics.Meter;
7 +import org.onlab.onos.net.intent.IntentEvent;
8 +
9 +/**
10 + * Service interface exported by IntentMetrics.
11 + */
12 +public interface IntentMetricsService {
13 + /**
14 + * Gets the last saved intent events.
15 + *
16 + * @return the last saved intent events.
17 + */
18 + public List<IntentEvent> getEvents();
19 +
20 + /**
21 + * Gets the Metrics' Gauge for the intent SUBMITTED event timestamp
22 + * (ms from the epoch).
23 + *
24 + * @return the Metrics' Gauge for the intent SUBMITTED event timestamp
25 + * (ms from the epoch)
26 + */
27 + public Gauge<Long> intentSubmittedTimestampEpochMsGauge();
28 +
29 + /**
30 + * Gets the Metrics' Gauge for the intent INSTALLED event timestamp
31 + * (ms from the epoch).
32 + *
33 + * @return the Metrics' Gauge for the intent INSTALLED event timestamp
34 + * (ms from the epoch)
35 + */
36 + public Gauge<Long> intentInstalledTimestampEpochMsGauge();
37 +
38 + /**
39 + * Gets the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
40 + * timestamp (ms from the epoch).
41 + *
42 + * TODO: This intent event is not implemented yet.
43 + *
44 + * @return the Metrics' Gauge for the intent WITHDRAW_REQUESTED event
45 + * timestamp (ms from the epoch)
46 + */
47 + public Gauge<Long> intentWithdrawRequestedTimestampEpochMsGauge();
48 +
49 + /**
50 + * Gets the Metrics' Gauge for the intent WITHDRAWN event timestamp
51 + * (ms from the epoch).
52 + *
53 + * @return the Metrics' Gauge for the intent WITHDRAWN event timestamp
54 + * (ms from the epoch)
55 + */
56 + public Gauge<Long> intentWithdrawnTimestampEpochMsGauge();
57 +
58 + /**
59 + * Gets the Metrics' Meter for the submitted intents event rate.
60 + *
61 + * @return the Metrics' Meter for the submitted intents event rate
62 + */
63 + public Meter intentSubmittedRateMeter();
64 +
65 + /**
66 + * Gets the Metrics' Meter for the installed intents event rate.
67 + *
68 + * @return the Metrics' Meter for the installed intent event rate
69 + */
70 + public Meter intentInstalledRateMeter();
71 +
72 + /**
73 + * Gets the Metrics' Meter for the withdraw requested intents event rate.
74 + *
75 + * @return the Metrics' Meter for the withdraw requested intents event rate
76 + */
77 + public Meter intentWithdrawRequestedRateMeter();
78 +
79 + /**
80 + * Gets the Metrics' Meter for the withdraw completed intents event rate.
81 + *
82 + * @return the Metrics' Meter for the withdraw completed intents event rate
83 + */
84 + public Meter intentWithdrawnRateMeter();
85 +}
1 +package org.onlab.onos.metrics.intent.cli;
2 +
3 +import java.util.List;
4 +
5 +import com.fasterxml.jackson.databind.JsonNode;
6 +import com.fasterxml.jackson.databind.ObjectMapper;
7 +import com.fasterxml.jackson.databind.node.ArrayNode;
8 +import com.fasterxml.jackson.databind.node.ObjectNode;
9 +import org.apache.karaf.shell.commands.Command;
10 +import org.onlab.onos.cli.AbstractShellCommand;
11 +import org.onlab.onos.metrics.intent.IntentMetricsService;
12 +import org.onlab.onos.net.intent.IntentEvent;
13 +
14 +/**
15 + * Command to show the list of last intent events.
16 + */
17 +@Command(scope = "onos", name = "intents-events",
18 + description = "Lists the last intent events")
19 +public class IntentEventsListCommand extends AbstractShellCommand {
20 +
21 + private static final String FORMAT_EVENT = "Event=%s";
22 +
23 + @Override
24 + protected void execute() {
25 + IntentMetricsService service = get(IntentMetricsService.class);
26 +
27 + if (outputJson()) {
28 + print("%s", json(service.getEvents()));
29 + } else {
30 + for (IntentEvent event : service.getEvents()) {
31 + print(FORMAT_EVENT, event);
32 + print(""); // Extra empty line for clarity
33 + }
34 + }
35 + }
36 +
37 + /**
38 + * Produces a JSON array of intent events.
39 + *
40 + * @param intentEvents the intent events with the data
41 + * @return JSON array with the intent events
42 + */
43 + private JsonNode json(List<IntentEvent> intentEvents) {
44 + ObjectMapper mapper = new ObjectMapper();
45 + ArrayNode result = mapper.createArrayNode();
46 +
47 + for (IntentEvent event : intentEvents) {
48 + result.add(json(mapper, event));
49 + }
50 + return result;
51 + }
52 +
53 + /**
54 + * Produces JSON object for a intent event.
55 + *
56 + * @param mapper the JSON object mapper to use
57 + * @param intentEvent the intent event with the data
58 + * @return JSON object for the intent event
59 + */
60 + private ObjectNode json(ObjectMapper mapper, IntentEvent intentEvent) {
61 + ObjectNode result = mapper.createObjectNode();
62 +
63 + result.put("time", intentEvent.time())
64 + .put("type", intentEvent.type().toString())
65 + .put("event", intentEvent.toString());
66 + return result;
67 + }
68 +}
1 +package org.onlab.onos.metrics.intent.cli;
2 +
3 +import java.io.IOException;
4 +import java.util.concurrent.TimeUnit;
5 +
6 +import com.codahale.metrics.Gauge;
7 +import com.codahale.metrics.Meter;
8 +import com.codahale.metrics.json.MetricsModule;
9 +import com.fasterxml.jackson.core.JsonProcessingException;
10 +import com.fasterxml.jackson.databind.JsonNode;
11 +import com.fasterxml.jackson.databind.ObjectMapper;
12 +import com.fasterxml.jackson.databind.node.ObjectNode;
13 +import org.apache.karaf.shell.commands.Command;
14 +import org.onlab.onos.cli.AbstractShellCommand;
15 +import org.onlab.onos.metrics.intent.IntentMetricsService;
16 +
17 +/**
18 + * Command to show the intent events metrics.
19 + */
20 +@Command(scope = "onos", name = "intents-events-metrics",
21 + description = "Lists intent events metrics")
22 +public class IntentEventsMetricsCommand extends AbstractShellCommand {
23 +
24 + private static final String FORMAT_GAUGE =
25 + "Intent %s Event Timestamp (ms from epoch)=%d";
26 + private static final String FORMAT_METER =
27 + "Intent %s Events count=%d rate(events/sec) mean=%f m1=%f m5=%f m15=%f";
28 +
29 + @Override
30 + protected void execute() {
31 + IntentMetricsService service = get(IntentMetricsService.class);
32 + Gauge<Long> gauge;
33 + Meter meter;
34 +
35 + if (outputJson()) {
36 + ObjectMapper mapper = new ObjectMapper()
37 + .registerModule(new MetricsModule(TimeUnit.SECONDS,
38 + TimeUnit.MILLISECONDS,
39 + false));
40 + ObjectNode result = mapper.createObjectNode();
41 + //
42 + gauge = service.intentSubmittedTimestampEpochMsGauge();
43 + result.put("intentSubmittedTimestamp", json(mapper, gauge));
44 + gauge = service.intentInstalledTimestampEpochMsGauge();
45 + result.put("intentInstalledTimestamp", json(mapper, gauge));
46 + gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
47 + result.put("intentWithdrawRequestedTimestamp",
48 + json(mapper, gauge));
49 + gauge = service.intentWithdrawnTimestampEpochMsGauge();
50 + result.put("intentWithdrawnTimestamp", json(mapper, gauge));
51 + //
52 + meter = service.intentSubmittedRateMeter();
53 + result.put("intentSubmittedRate", json(mapper, meter));
54 + meter = service.intentInstalledRateMeter();
55 + result.put("intentInstalledRate", json(mapper, meter));
56 + meter = service.intentWithdrawRequestedRateMeter();
57 + result.put("intentWithdrawRequestedRate", json(mapper, meter));
58 + meter = service.intentWithdrawnRateMeter();
59 + result.put("intentWithdrawnRate", json(mapper, meter));
60 + //
61 + print("%s", result);
62 + } else {
63 + gauge = service.intentSubmittedTimestampEpochMsGauge();
64 + printGauge("Submitted", gauge);
65 + gauge = service.intentInstalledTimestampEpochMsGauge();
66 + printGauge("Installed", gauge);
67 + gauge = service.intentWithdrawRequestedTimestampEpochMsGauge();
68 + printGauge("Withdraw Requested", gauge);
69 + gauge = service.intentWithdrawnTimestampEpochMsGauge();
70 + printGauge("Withdrawn", gauge);
71 + //
72 + meter = service.intentSubmittedRateMeter();
73 + printMeter("Submitted", meter);
74 + meter = service.intentInstalledRateMeter();
75 + printMeter("Installed", meter);
76 + meter = service.intentWithdrawRequestedRateMeter();
77 + printMeter("Withdraw Requested", meter);
78 + meter = service.intentWithdrawnRateMeter();
79 + printMeter("Withdrawn", meter);
80 + }
81 + }
82 +
83 + /**
84 + * Produces JSON node for an Object.
85 + *
86 + * @param mapper the JSON object mapper to use
87 + * @param object the Object with the data
88 + * @return JSON node for the Object
89 + */
90 + private JsonNode json(ObjectMapper mapper, Object object) {
91 + //
92 + // NOTE: The API for custom serializers is incomplete,
93 + // hence we have to parse the JSON string to create JsonNode.
94 + //
95 + try {
96 + final String objectJson = mapper.writeValueAsString(object);
97 + JsonNode objectNode = mapper.readTree(objectJson);
98 + return objectNode;
99 + } catch (JsonProcessingException e) {
100 + log.error("Error writing value as JSON string", e);
101 + } catch (IOException e) {
102 + log.error("Error writing value as JSON string", e);
103 + }
104 + return null;
105 + }
106 +
107 + /**
108 + * Prints a Gauge.
109 + *
110 + * @param operationStr the string with the intent operation to print
111 + * @param gauge the Gauge to print
112 + */
113 + private void printGauge(String operationStr, Gauge<Long> gauge) {
114 + print(FORMAT_GAUGE, operationStr, gauge.getValue());
115 + }
116 +
117 + /**
118 + * Prints a Meter.
119 + *
120 + * @param operationStr the string with the intent operation to print
121 + * @param meter the Meter to print
122 + */
123 + private void printMeter(String operationStr, Meter meter) {
124 + TimeUnit rateUnit = TimeUnit.SECONDS;
125 + double rateFactor = rateUnit.toSeconds(1);
126 + print(FORMAT_METER, operationStr, meter.getCount(),
127 + meter.getMeanRate() * rateFactor,
128 + meter.getOneMinuteRate() * rateFactor,
129 + meter.getFiveMinuteRate() * rateFactor,
130 + meter.getFifteenMinuteRate() * rateFactor);
131 + }
132 +}
1 +/**
2 + * ONOS Intent Metrics Application that collects intent-related metrics.
3 + */
4 +package org.onlab.onos.metrics.intent;
1 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
2 +
3 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
4 + <command>
5 + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsListCommand"/>
6 + </command>
7 + <command>
8 + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsMetricsCommand"/>
9 + </command>
10 + </command-bundle>
11 +
12 +</blueprint>
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 <description>ONOS metrics applications</description> 17 <description>ONOS metrics applications</description>
18 18
19 <modules> 19 <modules>
20 + <module>intent</module>
20 <module>topology</module> 21 <module>topology</module>
21 </modules> 22 </modules>
22 23
......
...@@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent; ...@@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent;
18 import org.onlab.metrics.MetricsFeature; 18 import org.onlab.metrics.MetricsFeature;
19 import org.onlab.metrics.MetricsService; 19 import org.onlab.metrics.MetricsService;
20 import org.onlab.onos.event.Event; 20 import org.onlab.onos.event.Event;
21 +import org.onlab.onos.net.device.DeviceEvent;
22 +import org.onlab.onos.net.device.DeviceListener;
23 +import org.onlab.onos.net.device.DeviceService;
24 +import org.onlab.onos.net.host.HostEvent;
25 +import org.onlab.onos.net.host.HostListener;
26 +import org.onlab.onos.net.host.HostService;
27 +import org.onlab.onos.net.link.LinkEvent;
28 +import org.onlab.onos.net.link.LinkListener;
29 +import org.onlab.onos.net.link.LinkService;
21 import org.onlab.onos.net.topology.TopologyEvent; 30 import org.onlab.onos.net.topology.TopologyEvent;
22 import org.onlab.onos.net.topology.TopologyListener; 31 import org.onlab.onos.net.topology.TopologyListener;
23 import org.onlab.onos.net.topology.TopologyService; 32 import org.onlab.onos.net.topology.TopologyService;
...@@ -28,14 +37,26 @@ import org.slf4j.Logger; ...@@ -28,14 +37,26 @@ import org.slf4j.Logger;
28 */ 37 */
29 @Component(immediate = true) 38 @Component(immediate = true)
30 @Service 39 @Service
31 -public class TopologyMetrics implements TopologyMetricsService, 40 +public class TopologyMetrics implements TopologyMetricsService {
32 - TopologyListener {
33 private static final Logger log = getLogger(TopologyMetrics.class); 41 private static final Logger log = getLogger(TopologyMetrics.class);
34 42
35 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 43 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
44 + protected DeviceService deviceService;
45 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
46 + protected HostService hostService;
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected LinkService linkService;
49 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
36 protected TopologyService topologyService; 50 protected TopologyService topologyService;
37 - private LinkedList<TopologyEvent> lastEvents = new LinkedList<>(); 51 +
38 - private static final int LAST_EVENTS_MAX_N = 10; 52 + private LinkedList<Event> lastEvents = new LinkedList<>();
53 + private static final int LAST_EVENTS_MAX_N = 100;
54 +
55 + private final DeviceListener deviceListener = new InnerDeviceListener();
56 + private final HostListener hostListener = new InnerHostListener();
57 + private final LinkListener linkListener = new InnerLinkListener();
58 + private final TopologyListener topologyListener =
59 + new InnerTopologyListener();
39 60
40 // 61 //
41 // Metrics 62 // Metrics
...@@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService,
61 protected void activate() { 82 protected void activate() {
62 clear(); 83 clear();
63 registerMetrics(); 84 registerMetrics();
64 - topologyService.addListener(this); 85 +
86 + // Register for all topology-related events
87 + deviceService.addListener(deviceListener);
88 + hostService.addListener(hostListener);
89 + linkService.addListener(linkListener);
90 + topologyService.addListener(topologyListener);
91 +
65 log.info("ONOS Topology Metrics started."); 92 log.info("ONOS Topology Metrics started.");
66 } 93 }
67 94
68 @Deactivate 95 @Deactivate
69 public void deactivate() { 96 public void deactivate() {
70 - topologyService.removeListener(this); 97 + // De-register from all topology-related events
98 + deviceService.removeListener(deviceListener);
99 + hostService.removeListener(hostListener);
100 + linkService.removeListener(linkListener);
101 + topologyService.removeListener(topologyListener);
102 +
71 removeMetrics(); 103 removeMetrics();
72 clear(); 104 clear();
73 log.info("ONOS Topology Metrics stopped."); 105 log.info("ONOS Topology Metrics stopped.");
74 } 106 }
75 107
76 @Override 108 @Override
77 - public List<TopologyEvent> getEvents() { 109 + public List<Event> getEvents() {
78 synchronized (lastEvents) { 110 synchronized (lastEvents) {
79 - return ImmutableList.<TopologyEvent>copyOf(lastEvents); 111 + return ImmutableList.<Event>copyOf(lastEvents);
80 } 112 }
81 } 113 }
82 114
...@@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService,
90 return eventRateMeter; 122 return eventRateMeter;
91 } 123 }
92 124
93 - @Override 125 + /**
94 - public void event(TopologyEvent event) { 126 + * Records an event.
95 - lastEventTimestampEpochMs = System.currentTimeMillis(); 127 + *
96 - // 128 + * @param event the event to record
97 - // NOTE: If we want to count each "reason" as a separate event, 129 + * @param updateEventRateMeter if true, update the Event Rate Meter
98 - // then we should use 'event.reason().size()' instead of '1' to 130 + */
99 - // mark the meter below. 131 + private void recordEvent(Event event, boolean updateEventRateMeter) {
100 - //
101 - eventRateMeter.mark(1);
102 -
103 - log.debug("Topology Event: time = {} type = {} subject = {}",
104 - event.time(), event.type(), event.subject());
105 - for (Event reason : event.reasons()) {
106 - log.debug("Topology Event Reason: time = {} type = {} subject = {}",
107 - reason.time(), reason.type(), reason.subject());
108 - }
109 -
110 - //
111 - // Keep only the last N events, where N = LAST_EVENTS_MAX_N
112 - //
113 synchronized (lastEvents) { 132 synchronized (lastEvents) {
133 + lastEventTimestampEpochMs = System.currentTimeMillis();
134 + if (updateEventRateMeter) {
135 + eventRateMeter.mark(1);
136 + }
137 +
138 + //
139 + // Keep only the last N events, where N = LAST_EVENTS_MAX_N
140 + //
114 while (lastEvents.size() >= LAST_EVENTS_MAX_N) { 141 while (lastEvents.size() >= LAST_EVENTS_MAX_N) {
115 lastEvents.remove(); 142 lastEvents.remove();
116 } 143 }
...@@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, ...@@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService,
119 } 146 }
120 147
121 /** 148 /**
149 + * Inner Device Event Listener class.
150 + */
151 + private class InnerDeviceListener implements DeviceListener {
152 + @Override
153 + public void event(DeviceEvent event) {
154 + recordEvent(event, true);
155 + log.debug("Device Event: time = {} type = {} event = {}",
156 + event.time(), event.type(), event);
157 + }
158 + }
159 +
160 + /**
161 + * Inner Host Event Listener class.
162 + */
163 + private class InnerHostListener implements HostListener {
164 + @Override
165 + public void event(HostEvent event) {
166 + recordEvent(event, true);
167 + log.debug("Host Event: time = {} type = {} event = {}",
168 + event.time(), event.type(), event);
169 + }
170 + }
171 +
172 + /**
173 + * Inner Link Event Listener class.
174 + */
175 + private class InnerLinkListener implements LinkListener {
176 + @Override
177 + public void event(LinkEvent event) {
178 + recordEvent(event, true);
179 + log.debug("Link Event: time = {} type = {} event = {}",
180 + event.time(), event.type(), event);
181 + }
182 + }
183 +
184 + /**
185 + * Inner Topology Event Listener class.
186 + */
187 + private class InnerTopologyListener implements TopologyListener {
188 + @Override
189 + public void event(TopologyEvent event) {
190 + //
191 + // NOTE: Don't update the eventRateMeter, because the real
192 + // events are already captured/counted.
193 + //
194 + recordEvent(event, false);
195 + log.debug("Topology Event: time = {} type = {} event = {}",
196 + event.time(), event.type(), event);
197 + for (Event reason : event.reasons()) {
198 + log.debug("Topology Event Reason: time = {} type = {} event = {}",
199 + reason.time(), reason.type(), reason);
200 + }
201 + }
202 + }
203 +
204 + /**
122 * Clears the internal state. 205 * Clears the internal state.
123 */ 206 */
124 private void clear() { 207 private void clear() {
125 - lastEventTimestampEpochMs = 0;
126 synchronized (lastEvents) { 208 synchronized (lastEvents) {
209 + lastEventTimestampEpochMs = 0;
127 lastEvents.clear(); 210 lastEvents.clear();
128 } 211 }
129 } 212 }
......
...@@ -4,7 +4,7 @@ import java.util.List; ...@@ -4,7 +4,7 @@ import java.util.List;
4 4
5 import com.codahale.metrics.Gauge; 5 import com.codahale.metrics.Gauge;
6 import com.codahale.metrics.Meter; 6 import com.codahale.metrics.Meter;
7 -import org.onlab.onos.net.topology.TopologyEvent; 7 +import org.onlab.onos.event.Event;
8 8
9 /** 9 /**
10 * Service interface exported by TopologyMetrics. 10 * Service interface exported by TopologyMetrics.
...@@ -15,7 +15,7 @@ public interface TopologyMetricsService { ...@@ -15,7 +15,7 @@ public interface TopologyMetricsService {
15 * 15 *
16 * @return the last saved topology events. 16 * @return the last saved topology events.
17 */ 17 */
18 - public List<TopologyEvent> getEvents(); 18 + public List<Event> getEvents();
19 19
20 /** 20 /**
21 * Gets the Metrics' Gauge for the last topology event timestamp 21 * Gets the Metrics' Gauge for the last topology event timestamp
......
...@@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; ...@@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent;
19 description = "Lists the last topology events") 19 description = "Lists the last topology events")
20 public class TopologyEventsListCommand extends AbstractShellCommand { 20 public class TopologyEventsListCommand extends AbstractShellCommand {
21 21
22 - private static final String FORMAT_EVENT = 22 + private static final String FORMAT_EVENT = "Event=%s";
23 - "Topology Event time=%d type=%s subject=%s"; 23 + private static final String FORMAT_REASON = " Reason=%s";
24 - private static final String FORMAT_REASON =
25 - " Reason time=%d type=%s subject=%s";
26 24
27 @Override 25 @Override
28 protected void execute() { 26 protected void execute() {
...@@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
31 if (outputJson()) { 29 if (outputJson()) {
32 print("%s", json(service.getEvents())); 30 print("%s", json(service.getEvents()));
33 } else { 31 } else {
34 - for (TopologyEvent event : service.getEvents()) { 32 + for (Event event : service.getEvents()) {
35 - print(FORMAT_EVENT, event.time(), event.type(), 33 + print(FORMAT_EVENT, event);
36 - event.subject()); 34 + if (event instanceof TopologyEvent) {
37 - for (Event reason : event.reasons()) { 35 + TopologyEvent topologyEvent = (TopologyEvent) event;
38 - print(FORMAT_REASON, reason.time(), reason.type(), 36 + for (Event reason : topologyEvent.reasons()) {
39 - reason.subject()); 37 + print(FORMAT_REASON, reason);
38 + }
40 } 39 }
41 print(""); // Extra empty line for clarity 40 print(""); // Extra empty line for clarity
42 } 41 }
...@@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
46 /** 45 /**
47 * Produces a JSON array of topology events. 46 * Produces a JSON array of topology events.
48 * 47 *
49 - * @param topologyEvents the topology events with the data 48 + * @param events the topology events with the data
50 * @return JSON array with the topology events 49 * @return JSON array with the topology events
51 */ 50 */
52 - private JsonNode json(List<TopologyEvent> topologyEvents) { 51 + private JsonNode json(List<Event> events) {
53 ObjectMapper mapper = new ObjectMapper(); 52 ObjectMapper mapper = new ObjectMapper();
54 ArrayNode result = mapper.createArrayNode(); 53 ArrayNode result = mapper.createArrayNode();
55 54
56 - for (TopologyEvent event : topologyEvents) { 55 + for (Event event : events) {
57 result.add(json(mapper, event)); 56 result.add(json(mapper, event));
58 } 57 }
59 return result; 58 return result;
...@@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { ...@@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand {
66 * @param topologyEvent the topology event with the data 65 * @param topologyEvent the topology event with the data
67 * @return JSON object for the topology event 66 * @return JSON object for the topology event
68 */ 67 */
69 - private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) {
70 - ObjectNode result = mapper.createObjectNode();
71 - ArrayNode reasons = mapper.createArrayNode();
72 -
73 - for (Event reason : topologyEvent.reasons()) {
74 - reasons.add(json(mapper, reason));
75 - }
76 - result.put("time", topologyEvent.time())
77 - .put("type", topologyEvent.type().toString())
78 - .put("subject", topologyEvent.subject().toString())
79 - .put("reasons", reasons);
80 - return result;
81 - }
82 -
83 - /**
84 - * Produces JSON object for a generic event.
85 - *
86 - * @param event the generic event with the data
87 - * @return JSON object for the generic event
88 - */
89 private ObjectNode json(ObjectMapper mapper, Event event) { 68 private ObjectNode json(ObjectMapper mapper, Event event) {
90 ObjectNode result = mapper.createObjectNode(); 69 ObjectNode result = mapper.createObjectNode();
91 70
92 result.put("time", event.time()) 71 result.put("time", event.time())
93 .put("type", event.type().toString()) 72 .put("type", event.type().toString())
94 - .put("subject", event.subject().toString()); 73 + .put("event", event.toString());
74 +
75 + // Add the reasons if a TopologyEvent
76 + if (event instanceof TopologyEvent) {
77 + TopologyEvent topologyEvent = (TopologyEvent) event;
78 + ArrayNode reasons = mapper.createArrayNode();
79 + for (Event reason : topologyEvent.reasons()) {
80 + reasons.add(json(mapper, reason));
81 + }
82 + result.put("reasons", reasons);
83 + }
84 +
95 return result; 85 return result;
96 } 86 }
97 } 87 }
......
...@@ -284,7 +284,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro ...@@ -284,7 +284,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
284 DefaultLinkDescription linkDescription = 284 DefaultLinkDescription linkDescription =
285 new DefaultLinkDescription(srcPoint, 285 new DefaultLinkDescription(srcPoint,
286 snkPoint, 286 snkPoint,
287 - Link.Type.DIRECT, 287 + Link.Type.OPTICAL,
288 extendedAttributes); 288 extendedAttributes);
289 289
290 linkProviderService.linkDetected(linkDescription); 290 linkProviderService.linkDetected(linkDescription);
...@@ -315,7 +315,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro ...@@ -315,7 +315,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
315 DefaultLinkDescription linkDescription = 315 DefaultLinkDescription linkDescription =
316 new DefaultLinkDescription(srcPoint, 316 new DefaultLinkDescription(srcPoint,
317 snkPoint, 317 snkPoint,
318 - Link.Type.DIRECT, 318 + Link.Type.OPTICAL,
319 extendedAttributes); 319 extendedAttributes);
320 320
321 linkProviderService.linkDetected(linkDescription); 321 linkProviderService.linkDetected(linkDescription);
......
1 { 1 {
2 - "opticalSwitches": [ 2 + "opticalSwitches": [
3 { 3 {
4 "allowed": true, 4 "allowed": true,
5 "latitude": 37.6, 5 "latitude": 37.6,
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
12 "type": "Roadm" 12 "type": "Roadm"
13 }, 13 },
14 14
15 - { 15 + {
16 "allowed": true, 16 "allowed": true,
17 "latitude": 37.3, 17 "latitude": 37.3,
18 "longitude": 121.9, 18 "longitude": 121.9,
...@@ -22,9 +22,9 @@ ...@@ -22,9 +22,9 @@
22 "numRegen": 0 22 "numRegen": 0
23 }, 23 },
24 "type": "Roadm" 24 "type": "Roadm"
25 - }, 25 + },
26 26
27 - { 27 + {
28 "allowed": true, 28 "allowed": true,
29 "latitude": 33.9, 29 "latitude": 33.9,
30 "longitude": 118.4, 30 "longitude": 118.4,
...@@ -34,10 +34,10 @@ ...@@ -34,10 +34,10 @@
34 "numRegen": 2 34 "numRegen": 2
35 }, 35 },
36 "type": "Roadm" 36 "type": "Roadm"
37 - } 37 + }
38 ], 38 ],
39 39
40 - "opticalLinks": [ 40 + "opticalLinks": [
41 { 41 {
42 "allowed": true, 42 "allowed": true,
43 "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01", 43 "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
...@@ -51,10 +51,38 @@ ...@@ -51,10 +51,38 @@
51 "port2": 30 51 "port2": 30
52 }, 52 },
53 "type": "wdmLink" 53 "type": "wdmLink"
54 - }, 54 + },
55 - 55 + {
56 - { 56 + "allowed": true,
57 - "allowed": true, 57 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
58 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
59 + "params": {
60 + "distKms": 1000,
61 + "nodeName1": "ROADM3",
62 + "nodeName2": "ROADM1",
63 + "numWaves": 80,
64 + "port1": 30,
65 + "port2": 10
66 + },
67 + "type": "wdmLink"
68 + },
69 +
70 + {
71 + "allowed": true,
72 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
73 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
74 + "params": {
75 + "distKms": 2000,
76 + "nodeName1": "ROADM2",
77 + "nodeName2": "ROADM3",
78 + "numWaves": 80,
79 + "port1": 20,
80 + "port2": 31
81 + },
82 + "type": "wdmLink"
83 + },
84 + {
85 + "allowed": true,
58 "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", 86 "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
59 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", 87 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
60 "params": { 88 "params": {
...@@ -66,10 +94,9 @@ ...@@ -66,10 +94,9 @@
66 "port2": 20 94 "port2": 20
67 }, 95 },
68 "type": "wdmLink" 96 "type": "wdmLink"
69 - }, 97 + },
70 98
71 - 99 + {
72 - {
73 "allowed": true, 100 "allowed": true,
74 "nodeDpid1": "00:00:ff:ff:ff:ff:00:01", 101 "nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
75 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01", 102 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
...@@ -82,8 +109,21 @@ ...@@ -82,8 +109,21 @@
82 }, 109 },
83 "type": "pktOptLink" 110 "type": "pktOptLink"
84 }, 111 },
112 + {
113 + "allowed": true,
114 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
115 + "nodeDpid2": "00:00:ff:ff:ff:ff:00:01",
116 + "params": {
117 + "nodeName1": "ROADM1",
118 + "nodeName2": "ROUTER1",
119 + "bandWidth": 100000,
120 + "port1": 11,
121 + "port2": 10
122 + },
123 + "type": "pktOptLink"
124 + },
85 125
86 - { 126 + {
87 "allowed": true, 127 "allowed": true,
88 "nodeDpid1": "00:00:ff:ff:ff:ff:00:02", 128 "nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
89 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", 129 "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
...@@ -95,7 +135,20 @@ ...@@ -95,7 +135,20 @@
95 "port2": 21 135 "port2": 21
96 }, 136 },
97 "type": "pktOptLink" 137 "type": "pktOptLink"
98 - } 138 + },
139 + {
140 + "allowed": true,
141 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
142 + "nodeDpid2": "00:00:ff:ff:ff:ff:00:02",
143 + "params": {
144 + "nodeName1": "ROADM2",
145 + "nodeName2": "ROUTER2",
146 + "bandWidth": 100000,
147 + "port1": 21,
148 + "port2": 10
149 + },
150 + "type": "pktOptLink"
151 + }
99 152
100 ] 153 ]
101 } 154 }
......
1 package org.onlab.onos.sdnip; 1 package org.onlab.onos.sdnip;
2 2
3 -import com.google.common.base.Objects; 3 +import java.util.Collection;
4 -import com.google.common.collect.HashMultimap; 4 +import java.util.HashMap;
5 -import com.google.common.collect.Multimaps; 5 +import java.util.HashSet;
6 -import com.google.common.collect.SetMultimap; 6 +import java.util.Iterator;
7 -import com.google.common.util.concurrent.ThreadFactoryBuilder; 7 +import java.util.LinkedList;
8 -import com.googlecode.concurrenttrees.common.KeyValuePair; 8 +import java.util.List;
9 -import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory; 9 +import java.util.Map;
10 -import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; 10 +import java.util.Set;
11 -import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; 11 +import java.util.concurrent.BlockingQueue;
12 +import java.util.concurrent.ConcurrentHashMap;
13 +import java.util.concurrent.ExecutorService;
14 +import java.util.concurrent.Executors;
15 +import java.util.concurrent.LinkedBlockingQueue;
16 +import java.util.concurrent.Semaphore;
17 +
12 import org.apache.commons.lang3.tuple.Pair; 18 import org.apache.commons.lang3.tuple.Pair;
13 import org.onlab.onos.ApplicationId; 19 import org.onlab.onos.ApplicationId;
14 import org.onlab.onos.net.ConnectPoint; 20 import org.onlab.onos.net.ConnectPoint;
...@@ -36,20 +42,15 @@ import org.onlab.packet.MacAddress; ...@@ -36,20 +42,15 @@ import org.onlab.packet.MacAddress;
36 import org.slf4j.Logger; 42 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory; 43 import org.slf4j.LoggerFactory;
38 44
39 -import java.util.Collection; 45 +import com.google.common.base.Objects;
40 -import java.util.HashMap; 46 +import com.google.common.collect.HashMultimap;
41 -import java.util.HashSet; 47 +import com.google.common.collect.Multimaps;
42 -import java.util.Iterator; 48 +import com.google.common.collect.SetMultimap;
43 -import java.util.LinkedList; 49 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
44 -import java.util.List; 50 +import com.googlecode.concurrenttrees.common.KeyValuePair;
45 -import java.util.Map; 51 +import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
46 -import java.util.Set; 52 +import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
47 -import java.util.concurrent.BlockingQueue; 53 +import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
48 -import java.util.concurrent.ConcurrentHashMap;
49 -import java.util.concurrent.ExecutorService;
50 -import java.util.concurrent.Executors;
51 -import java.util.concurrent.LinkedBlockingQueue;
52 -import java.util.concurrent.Semaphore;
53 54
54 /** 55 /**
55 * This class processes BGP route update, translates each update into a intent 56 * This class processes BGP route update, translates each update into a intent
...@@ -744,6 +745,21 @@ public class Router implements RouteListener { ...@@ -744,6 +745,21 @@ public class Router implements RouteListener {
744 } 745 }
745 746
746 /** 747 /**
748 + * Gets the pushed route intents.
749 + *
750 + * @return the pushed route intents
751 + */
752 + public Collection<MultiPointToSinglePointIntent> getPushedRouteIntents() {
753 + List<MultiPointToSinglePointIntent> pushedIntents = new LinkedList<>();
754 +
755 + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
756 + pushedRouteIntents.entrySet()) {
757 + pushedIntents.add(entry.getValue());
758 + }
759 + return pushedIntents;
760 + }
761 +
762 + /**
747 * Listener for host events. 763 * Listener for host events.
748 */ 764 */
749 class InternalHostListener implements HostListener { 765 class InternalHostListener implements HostListener {
......
1 +package org.onlab.onos.sdnip;
2 +
3 +import static org.easymock.EasyMock.createMock;
4 +import static org.easymock.EasyMock.expect;
5 +import static org.easymock.EasyMock.replay;
6 +import static org.easymock.EasyMock.reset;
7 +import static org.easymock.EasyMock.verify;
8 +import static org.junit.Assert.assertEquals;
9 +import static org.junit.Assert.assertTrue;
10 +
11 +import java.util.HashMap;
12 +import java.util.HashSet;
13 +import java.util.Map;
14 +import java.util.Set;
15 +
16 +import org.junit.Before;
17 +import org.junit.Test;
18 +import org.onlab.onos.ApplicationId;
19 +import org.onlab.onos.net.ConnectPoint;
20 +import org.onlab.onos.net.DefaultHost;
21 +import org.onlab.onos.net.DeviceId;
22 +import org.onlab.onos.net.Host;
23 +import org.onlab.onos.net.HostId;
24 +import org.onlab.onos.net.HostLocation;
25 +import org.onlab.onos.net.PortNumber;
26 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
27 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
28 +import org.onlab.onos.net.flow.TrafficSelector;
29 +import org.onlab.onos.net.flow.TrafficTreatment;
30 +import org.onlab.onos.net.host.HostService;
31 +import org.onlab.onos.net.intent.IntentService;
32 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
33 +import org.onlab.onos.net.provider.ProviderId;
34 +import org.onlab.onos.sdnip.config.BgpPeer;
35 +import org.onlab.onos.sdnip.config.Interface;
36 +import org.onlab.onos.sdnip.config.SdnIpConfigService;
37 +import org.onlab.packet.Ethernet;
38 +import org.onlab.packet.IpAddress;
39 +import org.onlab.packet.IpPrefix;
40 +import org.onlab.packet.MacAddress;
41 +import org.onlab.packet.VlanId;
42 +import org.onlab.util.TestUtils;
43 +import org.onlab.util.TestUtils.TestUtilsException;
44 +
45 +import com.google.common.collect.Sets;
46 +
47 +/**
48 + * This class tests adding a route, updating a route, deleting a route,
49 + * and adding a route whose next hop is the local BGP speaker.
50 + */
51 +public class RouterTest {
52 +
53 + private SdnIpConfigService sdnIpConfigService;
54 + private InterfaceService interfaceService;
55 + private IntentService intentService;
56 + private HostService hostService;
57 +
58 + private Map<IpAddress, BgpPeer> bgpPeers;
59 + private Map<IpAddress, BgpPeer> configuredPeers;
60 + private Set<Interface> interfaces;
61 + private Set<Interface> configuredInterfaces;
62 +
63 + private static final ApplicationId APPID = new ApplicationId() {
64 + @Override
65 + public short id() {
66 + return 1;
67 + }
68 +
69 + @Override
70 + public String name() {
71 + return "SDNIP";
72 + }
73 + };
74 +
75 + private Router router;
76 +
77 + @Before
78 + public void setUp() throws Exception {
79 + bgpPeers = setUpBgpPeers();
80 + interfaces = setUpInterfaces();
81 + initRouter();
82 + }
83 +
84 + /**
85 + * Initializes Router class.
86 + */
87 + private void initRouter() {
88 +
89 + intentService = createMock(IntentService.class);
90 + hostService = createMock(HostService.class);
91 +
92 + interfaceService = createMock(InterfaceService.class);
93 + expect(interfaceService.getInterfaces()).andReturn(
94 + interfaces).anyTimes();
95 +
96 + Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>();
97 + ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24"));
98 + Interface expectedInterface =
99 + new Interface(new ConnectPoint(
100 + DeviceId.deviceId("of:0000000000000001"),
101 + PortNumber.portNumber("1")),
102 + ipAddressesOnSw1Eth1,
103 + MacAddress.valueOf("00:00:00:00:00:01"));
104 + ConnectPoint egressPoint = new ConnectPoint(
105 + DeviceId.deviceId("of:0000000000000001"),
106 + PortNumber.portNumber(1));
107 + expect(interfaceService.getInterface(egressPoint)).andReturn(
108 + expectedInterface).anyTimes();
109 +
110 + Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>();
111 + ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24"));
112 + Interface expectedInterfaceNew =
113 + new Interface(new ConnectPoint(
114 + DeviceId.deviceId("of:0000000000000002"),
115 + PortNumber.portNumber("1")),
116 + ipAddressesOnSw2Eth1,
117 + MacAddress.valueOf("00:00:00:00:00:02"));
118 + ConnectPoint egressPointNew = new ConnectPoint(
119 + DeviceId.deviceId("of:0000000000000002"),
120 + PortNumber.portNumber(1));
121 + expect(interfaceService.getInterface(egressPointNew)).andReturn(
122 + expectedInterfaceNew).anyTimes();
123 + replay(interfaceService);
124 +
125 + sdnIpConfigService = createMock(SdnIpConfigService.class);
126 + expect(sdnIpConfigService.getBgpPeers()).andReturn(bgpPeers).anyTimes();
127 + replay(sdnIpConfigService);
128 +
129 + router = new Router(APPID, intentService,
130 + hostService, sdnIpConfigService, interfaceService);
131 + }
132 +
133 + /**
134 + * Sets up BGP peers in external networks.
135 + *
136 + * @return configured BGP peers as a Map from peer IP address to BgpPeer
137 + */
138 + private Map<IpAddress, BgpPeer> setUpBgpPeers() {
139 +
140 + configuredPeers = new HashMap<>();
141 +
142 + String peerSw1Eth1 = "192.168.10.1";
143 + configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
144 + new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1));
145 +
146 + // Two BGP peers are connected to switch 2 port 1.
147 + String peer1Sw2Eth1 = "192.168.20.1";
148 + configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
149 + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1));
150 +
151 + String peer2Sw2Eth1 = "192.168.20.2";
152 + configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
153 + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1));
154 +
155 + return configuredPeers;
156 + }
157 +
158 + /**
159 + * Sets up logical interfaces, which emulate the configured interfaces
160 + * in SDN-IP application.
161 + *
162 + * @return configured interfaces as a Set
163 + */
164 + private Set<Interface> setUpInterfaces() {
165 +
166 + configuredInterfaces = Sets.newHashSet();
167 +
168 + Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>();
169 + ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24"));
170 + configuredInterfaces.add(
171 + new Interface(new ConnectPoint(
172 + DeviceId.deviceId("of:0000000000000001"),
173 + PortNumber.portNumber(1)),
174 + ipAddressesOnSw1Eth1,
175 + MacAddress.valueOf("00:00:00:00:00:01")));
176 +
177 + Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>();
178 + ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24"));
179 + configuredInterfaces.add(
180 + new Interface(new ConnectPoint(
181 + DeviceId.deviceId("of:0000000000000002"),
182 + PortNumber.portNumber(1)),
183 + ipAddressesOnSw2Eth1,
184 + MacAddress.valueOf("00:00:00:00:00:02")));
185 +
186 + Set<IpPrefix> ipAddressesOnSw3Eth1 = new HashSet<IpPrefix>();
187 + ipAddressesOnSw3Eth1.add(IpPrefix.valueOf("192.168.30.0/24"));
188 + configuredInterfaces.add(
189 + new Interface(new ConnectPoint(
190 + DeviceId.deviceId("of:0000000000000003"),
191 + PortNumber.portNumber(1)),
192 + ipAddressesOnSw3Eth1,
193 + MacAddress.valueOf("00:00:00:00:00:03")));
194 +
195 + return configuredInterfaces;
196 + }
197 +
198 + /**
199 + * This method tests adding a route entry.
200 + */
201 + @Test
202 + public void testProcessRouteAdd() throws TestUtilsException {
203 +
204 + // Construct a route entry
205 + RouteEntry routeEntry = new RouteEntry(
206 + IpPrefix.valueOf("1.1.1.0/24"),
207 + IpAddress.valueOf("192.168.10.1"));
208 +
209 + // Construct a MultiPointToSinglePointIntent intent
210 + TrafficSelector.Builder selectorBuilder =
211 + DefaultTrafficSelector.builder();
212 + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
213 + routeEntry.prefix());
214 +
215 + TrafficTreatment.Builder treatmentBuilder =
216 + DefaultTrafficTreatment.builder();
217 + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
218 +
219 + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
220 + ingressPoints.add(new ConnectPoint(
221 + DeviceId.deviceId("of:0000000000000002"),
222 + PortNumber.portNumber("1")));
223 + ingressPoints.add(new ConnectPoint(
224 + DeviceId.deviceId("of:0000000000000003"),
225 + PortNumber.portNumber("1")));
226 +
227 + ConnectPoint egressPoint = new ConnectPoint(
228 + DeviceId.deviceId("of:0000000000000001"),
229 + PortNumber.portNumber("1"));
230 +
231 + MultiPointToSinglePointIntent intent =
232 + new MultiPointToSinglePointIntent(APPID,
233 + selectorBuilder.build(), treatmentBuilder.build(),
234 + ingressPoints, egressPoint);
235 +
236 + // Reset host service
237 + reset(hostService);
238 + Set<Host> hosts = new HashSet<Host>(1);
239 + Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>();
240 + ipPrefixes.add(IpPrefix.valueOf("192.168.10.1/32"));
241 + hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE,
242 + MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE,
243 + new HostLocation(
244 + DeviceId.deviceId("of:0000000000000001"),
245 + PortNumber.portNumber(1), 1),
246 + ipPrefixes));
247 + expect(hostService.getHostsByIp(
248 + IpPrefix.valueOf("192.168.10.1/32"))).andReturn(hosts);
249 + replay(hostService);
250 +
251 + // Set up test expectation
252 + reset(intentService);
253 + intentService.submit(intent);
254 + replay(intentService);
255 +
256 + // Call the processRouteAdd() method in Router class
257 + router.leaderChanged(true);
258 + TestUtils.setField(router, "isActivatedLeader", true);
259 + router.processRouteAdd(routeEntry);
260 +
261 + // Verify
262 + assertEquals(router.getRoutes().size(), 1);
263 + assertTrue(router.getRoutes().contains(routeEntry));
264 + assertEquals(router.getPushedRouteIntents().size(), 1);
265 + assertEquals(router.getPushedRouteIntents().iterator().next(),
266 + intent);
267 + verify(intentService);
268 + }
269 +
270 + /**
271 + * This method tests updating a route entry.
272 + *
273 + * @throws TestUtilsException
274 + */
275 + @Test
276 + public void testRouteUpdate() throws TestUtilsException {
277 +
278 + // Firstly add a route
279 + testProcessRouteAdd();
280 +
281 + // Construct the existing route entry
282 + RouteEntry routeEntry = new RouteEntry(
283 + IpPrefix.valueOf("1.1.1.0/24"),
284 + IpAddress.valueOf("192.168.10.1"));
285 +
286 + // Construct the existing MultiPointToSinglePointIntent intent
287 + TrafficSelector.Builder selectorBuilder =
288 + DefaultTrafficSelector.builder();
289 + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
290 + routeEntry.prefix());
291 +
292 + TrafficTreatment.Builder treatmentBuilder =
293 + DefaultTrafficTreatment.builder();
294 + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
295 +
296 + ConnectPoint egressPoint = new ConnectPoint(
297 + DeviceId.deviceId("of:0000000000000001"),
298 + PortNumber.portNumber("1"));
299 +
300 + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
301 + ingressPoints.add(new ConnectPoint(
302 + DeviceId.deviceId("of:0000000000000002"),
303 + PortNumber.portNumber("1")));
304 + ingressPoints.add(new ConnectPoint(
305 + DeviceId.deviceId("of:0000000000000003"),
306 + PortNumber.portNumber("1")));
307 +
308 + MultiPointToSinglePointIntent intent =
309 + new MultiPointToSinglePointIntent(APPID,
310 + selectorBuilder.build(), treatmentBuilder.build(),
311 + ingressPoints, egressPoint);
312 +
313 + // Start to construct a new route entry and new intent
314 + RouteEntry routeEntryUpdate = new RouteEntry(
315 + IpPrefix.valueOf("1.1.1.0/24"),
316 + IpAddress.valueOf("192.168.20.1"));
317 +
318 + // Construct a new MultiPointToSinglePointIntent intent
319 + TrafficSelector.Builder selectorBuilderNew =
320 + DefaultTrafficSelector.builder();
321 + selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
322 + routeEntryUpdate.prefix());
323 +
324 + TrafficTreatment.Builder treatmentBuilderNew =
325 + DefaultTrafficTreatment.builder();
326 + treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02"));
327 +
328 + ConnectPoint egressPointNew = new ConnectPoint(
329 + DeviceId.deviceId("of:0000000000000002"),
330 + PortNumber.portNumber("1"));
331 +
332 + Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>();
333 + ingressPointsNew.add(new ConnectPoint(
334 + DeviceId.deviceId("of:0000000000000001"),
335 + PortNumber.portNumber("1")));
336 + ingressPointsNew.add(new ConnectPoint(
337 + DeviceId.deviceId("of:0000000000000003"),
338 + PortNumber.portNumber("1")));
339 +
340 + MultiPointToSinglePointIntent intentNew =
341 + new MultiPointToSinglePointIntent(APPID,
342 + selectorBuilderNew.build(),
343 + treatmentBuilderNew.build(),
344 + ingressPointsNew, egressPointNew);
345 +
346 + // Reset host service
347 + reset(hostService);
348 + Set<Host> hosts = new HashSet<Host>(1);
349 + Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>();
350 + ipPrefixes.add(IpPrefix.valueOf("192.168.20.1/32"));
351 + hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE,
352 + MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE,
353 + new HostLocation(
354 + DeviceId.deviceId("of:0000000000000002"),
355 + PortNumber.portNumber(1), 1),
356 + ipPrefixes));
357 + expect(hostService.getHostsByIp(
358 + IpPrefix.valueOf("192.168.20.1/32"))).andReturn(hosts);
359 + replay(hostService);
360 +
361 + // Set up test expectation
362 + reset(intentService);
363 + intentService.withdraw(intent);
364 + intentService.submit(intentNew);
365 + replay(intentService);
366 +
367 + // Call the processRouteAdd() method in Router class
368 + router.leaderChanged(true);
369 + TestUtils.setField(router, "isActivatedLeader", true);
370 + router.processRouteAdd(routeEntryUpdate);
371 +
372 + // Verify
373 + assertEquals(router.getRoutes().size(), 1);
374 + assertTrue(router.getRoutes().contains(routeEntryUpdate));
375 + assertEquals(router.getPushedRouteIntents().size(), 1);
376 + assertEquals(router.getPushedRouteIntents().iterator().next(),
377 + intentNew);
378 + verify(intentService);
379 + }
380 +
381 + /**
382 + * This method tests deleting a route entry.
383 + */
384 + @Test
385 + public void testProcessRouteDelete() throws TestUtilsException {
386 +
387 + // Firstly add a route
388 + testProcessRouteAdd();
389 +
390 + // Construct the existing route entry
391 + RouteEntry routeEntry = new RouteEntry(
392 + IpPrefix.valueOf("1.1.1.0/24"),
393 + IpAddress.valueOf("192.168.10.1"));
394 +
395 + // Construct the existing MultiPointToSinglePointIntent intent
396 + TrafficSelector.Builder selectorBuilder =
397 + DefaultTrafficSelector.builder();
398 + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(
399 + routeEntry.prefix());
400 +
401 + TrafficTreatment.Builder treatmentBuilder =
402 + DefaultTrafficTreatment.builder();
403 + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01"));
404 +
405 + ConnectPoint egressPoint = new ConnectPoint(
406 + DeviceId.deviceId("of:0000000000000001"),
407 + PortNumber.portNumber("1"));
408 +
409 + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>();
410 + ingressPoints.add(new ConnectPoint(
411 + DeviceId.deviceId("of:0000000000000002"),
412 + PortNumber.portNumber("1")));
413 + ingressPoints.add(new ConnectPoint(
414 + DeviceId.deviceId("of:0000000000000003"),
415 + PortNumber.portNumber("1")));
416 +
417 + MultiPointToSinglePointIntent intent =
418 + new MultiPointToSinglePointIntent(APPID,
419 + selectorBuilder.build(), treatmentBuilder.build(),
420 + ingressPoints, egressPoint);
421 +
422 + // Set up expectation
423 + reset(intentService);
424 + intentService.withdraw(intent);
425 + replay(intentService);
426 +
427 + // Call route deleting method in Router class
428 + router.leaderChanged(true);
429 + TestUtils.setField(router, "isActivatedLeader", true);
430 + router.processRouteDelete(routeEntry);
431 +
432 + // Verify
433 + assertEquals(router.getRoutes().size(), 0);
434 + assertEquals(router.getPushedRouteIntents().size(), 0);
435 + verify(intentService);
436 + }
437 +
438 + /**
439 + * This method tests when the next hop of a route is the local BGP speaker.
440 + *
441 + * @throws TestUtilsException
442 + */
443 + @Test
444 + public void testLocalRouteAdd() throws TestUtilsException {
445 +
446 + // Construct a route entry, the next hop is the local BGP speaker
447 + RouteEntry routeEntry = new RouteEntry(
448 + IpPrefix.valueOf("1.1.1.0/24"), IpAddress.valueOf("0.0.0.0"));
449 +
450 + // Reset intentService to check whether the submit method is called
451 + reset(intentService);
452 + replay(intentService);
453 +
454 + // Call the processRouteAdd() method in Router class
455 + router.leaderChanged(true);
456 + TestUtils.setField(router, "isActivatedLeader", true);
457 + router.processRouteAdd(routeEntry);
458 +
459 + // Verify
460 + assertEquals(router.getRoutes().size(), 1);
461 + assertTrue(router.getRoutes().contains(routeEntry));
462 + assertEquals(router.getPushedRouteIntents().size(), 0);
463 + verify(intentService);
464 + }
465 +}
...@@ -20,8 +20,10 @@ package org.onlab.onos.cli.net; ...@@ -20,8 +20,10 @@ package org.onlab.onos.cli.net;
20 20
21 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.ObjectMapper;
22 import org.apache.karaf.shell.commands.Command; 22 import org.apache.karaf.shell.commands.Command;
23 +import org.apache.karaf.shell.commands.Option;
23 import org.onlab.onos.cli.AbstractShellCommand; 24 import org.onlab.onos.cli.AbstractShellCommand;
24 import org.onlab.onos.net.topology.Topology; 25 import org.onlab.onos.net.topology.Topology;
26 +import org.onlab.onos.net.topology.TopologyProvider;
25 import org.onlab.onos.net.topology.TopologyService; 27 import org.onlab.onos.net.topology.TopologyService;
26 28
27 /** 29 /**
...@@ -35,6 +37,10 @@ public class TopologyCommand extends AbstractShellCommand { ...@@ -35,6 +37,10 @@ public class TopologyCommand extends AbstractShellCommand {
35 private static final String FMT = 37 private static final String FMT =
36 "time=%s, devices=%d, links=%d, clusters=%d, paths=%d"; 38 "time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
37 39
40 + @Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
41 + required = false, multiValued = false)
42 + private boolean recompute = false;
43 +
38 protected TopologyService service; 44 protected TopologyService service;
39 protected Topology topology; 45 protected Topology topology;
40 46
...@@ -49,7 +55,10 @@ public class TopologyCommand extends AbstractShellCommand { ...@@ -49,7 +55,10 @@ public class TopologyCommand extends AbstractShellCommand {
49 @Override 55 @Override
50 protected void execute() { 56 protected void execute() {
51 init(); 57 init();
52 - if (outputJson()) { 58 + if (recompute) {
59 + get(TopologyProvider.class).triggerRecompute();
60 +
61 + } else if (outputJson()) {
53 print("%s", new ObjectMapper().createObjectNode() 62 print("%s", new ObjectMapper().createObjectNode()
54 .put("time", topology.time()) 63 .put("time", topology.time())
55 .put("deviceCount", topology.deviceCount()) 64 .put("deviceCount", topology.deviceCount())
......
...@@ -25,7 +25,18 @@ public interface Link extends Annotated, Provided, NetworkResource { ...@@ -25,7 +25,18 @@ public interface Link extends Annotated, Provided, NetworkResource {
25 /** 25 /**
26 * Signifies that this link is an edge, i.e. host link. 26 * Signifies that this link is an edge, i.e. host link.
27 */ 27 */
28 - EDGE 28 + EDGE,
29 +
30 + /**
31 + * Signifies that this link represents a logical link backed by
32 + * some form of a tunnel.
33 + */
34 + TUNNEL,
35 +
36 + /**
37 + * Signifies that this link is realized by optical connection.
38 + */
39 + OPTICAL
29 } 40 }
30 41
31 /** 42 /**
...@@ -49,6 +60,4 @@ public interface Link extends Annotated, Provided, NetworkResource { ...@@ -49,6 +60,4 @@ public interface Link extends Annotated, Provided, NetworkResource {
49 */ 60 */
50 Type type(); 61 Type type();
51 62
52 - // LinkInfo info(); // Additional link information / decorations
53 -
54 } 63 }
......
...@@ -7,4 +7,9 @@ import org.onlab.onos.net.provider.Provider; ...@@ -7,4 +7,9 @@ import org.onlab.onos.net.provider.Provider;
7 */ 7 */
8 public interface TopologyProvider extends Provider { 8 public interface TopologyProvider extends Provider {
9 9
10 + /**
11 + * Triggers topology recomputation.
12 + */
13 + void triggerRecompute();
14 +
10 } 15 }
......
...@@ -208,7 +208,7 @@ public class LinkManager ...@@ -208,7 +208,7 @@ public class LinkManager
208 LinkEvent event = store.createOrUpdateLink(provider().id(), 208 LinkEvent event = store.createOrUpdateLink(provider().id(),
209 linkDescription); 209 linkDescription);
210 if (event != null) { 210 if (event != null) {
211 - log.debug("Link {} detected", linkDescription); 211 + log.info("Link {} detected", linkDescription);
212 post(event); 212 post(event);
213 } 213 }
214 } 214 }
......
...@@ -5,6 +5,7 @@ import org.apache.felix.scr.annotations.Component; ...@@ -5,6 +5,7 @@ import org.apache.felix.scr.annotations.Component;
5 import org.apache.felix.scr.annotations.Deactivate; 5 import org.apache.felix.scr.annotations.Deactivate;
6 import org.apache.felix.scr.annotations.Reference; 6 import org.apache.felix.scr.annotations.Reference;
7 import org.apache.felix.scr.annotations.ReferenceCardinality; 7 import org.apache.felix.scr.annotations.ReferenceCardinality;
8 +import org.apache.felix.scr.annotations.Service;
8 import org.onlab.onos.event.AbstractEventAccumulator; 9 import org.onlab.onos.event.AbstractEventAccumulator;
9 import org.onlab.onos.event.Event; 10 import org.onlab.onos.event.Event;
10 import org.onlab.onos.event.EventAccumulator; 11 import org.onlab.onos.event.EventAccumulator;
...@@ -39,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -39,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger;
39 * new topology snapshots. 40 * new topology snapshots.
40 */ 41 */
41 @Component(immediate = true) 42 @Component(immediate = true)
43 +@Service
42 public class DefaultTopologyProvider extends AbstractProvider 44 public class DefaultTopologyProvider extends AbstractProvider
43 implements TopologyProvider { 45 implements TopologyProvider {
44 46
...@@ -89,7 +91,7 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -89,7 +91,7 @@ public class DefaultTopologyProvider extends AbstractProvider
89 linkService.addListener(linkListener); 91 linkService.addListener(linkListener);
90 92
91 isStarted = true; 93 isStarted = true;
92 - triggerTopologyBuild(Collections.<Event>emptyList()); 94 + triggerRecompute();
93 log.info("Started"); 95 log.info("Started");
94 } 96 }
95 97
...@@ -108,6 +110,11 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -108,6 +110,11 @@ public class DefaultTopologyProvider extends AbstractProvider
108 log.info("Stopped"); 110 log.info("Stopped");
109 } 111 }
110 112
113 + @Override
114 + public void triggerRecompute() {
115 + triggerTopologyBuild(Collections.<Event>emptyList());
116 + }
117 +
111 /** 118 /**
112 * Triggers assembly of topology data citing the specified events as the 119 * Triggers assembly of topology data citing the specified events as the
113 * reason. 120 * reason.
...@@ -177,7 +184,11 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -177,7 +184,11 @@ public class DefaultTopologyProvider extends AbstractProvider
177 184
178 @Override 185 @Override
179 public void run() { 186 public void run() {
180 - buildTopology(reasons); 187 + try {
188 + buildTopology(reasons);
189 + } catch (Exception e) {
190 + log.warn("Unable to compute topology due to: {}", e.getMessage());
191 + }
181 } 192 }
182 } 193 }
183 194
......
...@@ -195,6 +195,10 @@ public class TopologyManagerTest { ...@@ -195,6 +195,10 @@ public class TopologyManagerTest {
195 public TestProvider() { 195 public TestProvider() {
196 super(PID); 196 super(PID);
197 } 197 }
198 +
199 + @Override
200 + public void triggerRecompute() {
201 + }
198 } 202 }
199 203
200 private static class TestListener implements TopologyListener { 204 private static class TestListener implements TopologyListener {
......
...@@ -192,14 +192,6 @@ public class SimpleLinkStore ...@@ -192,14 +192,6 @@ public class SimpleLinkStore
192 // Creates and stores the link and returns the appropriate event. 192 // Creates and stores the link and returns the appropriate event.
193 // Guarded by linkDescs value (=locking each Link) 193 // Guarded by linkDescs value (=locking each Link)
194 private LinkEvent createLink(LinkKey key, Link newLink) { 194 private LinkEvent createLink(LinkKey key, Link newLink) {
195 -
196 - if (newLink.providerId().isAncillary()) {
197 - // TODO: revisit ancillary only Link handling
198 -
199 - // currently treating ancillary only as down (not visible outside)
200 - return null;
201 - }
202 -
203 links.put(key, newLink); 195 links.put(key, newLink);
204 srcLinks.put(newLink.src().deviceId(), key); 196 srcLinks.put(newLink.src().deviceId(), key);
205 dstLinks.put(newLink.dst().deviceId(), key); 197 dstLinks.put(newLink.dst().deviceId(), key);
...@@ -209,10 +201,8 @@ public class SimpleLinkStore ...@@ -209,10 +201,8 @@ public class SimpleLinkStore
209 // Updates, if necessary the specified link and returns the appropriate event. 201 // Updates, if necessary the specified link and returns the appropriate event.
210 // Guarded by linkDescs value (=locking each Link) 202 // Guarded by linkDescs value (=locking each Link)
211 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) { 203 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
212 -
213 if (newLink.providerId().isAncillary()) { 204 if (newLink.providerId().isAncillary()) {
214 // TODO: revisit ancillary only Link handling 205 // TODO: revisit ancillary only Link handling
215 -
216 // currently treating ancillary only as down (not visible outside) 206 // currently treating ancillary only as down (not visible outside)
217 return null; 207 return null;
218 } 208 }
......
1 package org.onlab.onos.store.trivial.impl; 1 package org.onlab.onos.store.trivial.impl;
2 2
3 -import static org.junit.Assert.*; 3 +import com.google.common.collect.Iterables;
4 -import static org.onlab.onos.net.DeviceId.deviceId;
5 -import static org.onlab.onos.net.Link.Type.*;
6 -import static org.onlab.onos.net.link.LinkEvent.Type.*;
7 -import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
8 -
9 -import java.util.Collections;
10 -import java.util.HashMap;
11 -import java.util.Map;
12 -import java.util.Set;
13 -import java.util.concurrent.CountDownLatch;
14 -import java.util.concurrent.TimeUnit;
15 -
16 import org.junit.After; 4 import org.junit.After;
17 import org.junit.AfterClass; 5 import org.junit.AfterClass;
18 import org.junit.Before; 6 import org.junit.Before;
...@@ -23,17 +11,27 @@ import org.onlab.onos.net.ConnectPoint; ...@@ -23,17 +11,27 @@ import org.onlab.onos.net.ConnectPoint;
23 import org.onlab.onos.net.DefaultAnnotations; 11 import org.onlab.onos.net.DefaultAnnotations;
24 import org.onlab.onos.net.DeviceId; 12 import org.onlab.onos.net.DeviceId;
25 import org.onlab.onos.net.Link; 13 import org.onlab.onos.net.Link;
14 +import org.onlab.onos.net.Link.Type;
26 import org.onlab.onos.net.LinkKey; 15 import org.onlab.onos.net.LinkKey;
27 import org.onlab.onos.net.PortNumber; 16 import org.onlab.onos.net.PortNumber;
28 import org.onlab.onos.net.SparseAnnotations; 17 import org.onlab.onos.net.SparseAnnotations;
29 -import org.onlab.onos.net.Link.Type;
30 import org.onlab.onos.net.link.DefaultLinkDescription; 18 import org.onlab.onos.net.link.DefaultLinkDescription;
31 import org.onlab.onos.net.link.LinkEvent; 19 import org.onlab.onos.net.link.LinkEvent;
32 import org.onlab.onos.net.link.LinkStore; 20 import org.onlab.onos.net.link.LinkStore;
33 import org.onlab.onos.net.link.LinkStoreDelegate; 21 import org.onlab.onos.net.link.LinkStoreDelegate;
34 import org.onlab.onos.net.provider.ProviderId; 22 import org.onlab.onos.net.provider.ProviderId;
35 23
36 -import com.google.common.collect.Iterables; 24 +import java.util.HashMap;
25 +import java.util.Map;
26 +import java.util.Set;
27 +import java.util.concurrent.CountDownLatch;
28 +import java.util.concurrent.TimeUnit;
29 +
30 +import static org.junit.Assert.*;
31 +import static org.onlab.onos.net.DeviceId.deviceId;
32 +import static org.onlab.onos.net.Link.Type.*;
33 +import static org.onlab.onos.net.link.LinkEvent.Type.*;
34 +import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals;
37 35
38 /** 36 /**
39 * Test of the simple LinkStore implementation. 37 * Test of the simple LinkStore implementation.
...@@ -301,7 +299,7 @@ public class SimpleLinkStoreTest { ...@@ -301,7 +299,7 @@ public class SimpleLinkStoreTest {
301 LinkEvent event = linkStore.createOrUpdateLink(PIDA, 299 LinkEvent event = linkStore.createOrUpdateLink(PIDA,
302 new DefaultLinkDescription(src, dst, INDIRECT, A1)); 300 new DefaultLinkDescription(src, dst, INDIRECT, A1));
303 301
304 - assertNull("Ancillary only link is ignored", event); 302 + assertNotNull("Ancillary only link is ignored", event);
305 303
306 // add Primary link 304 // add Primary link
307 LinkEvent event2 = linkStore.createOrUpdateLink(PID, 305 LinkEvent event2 = linkStore.createOrUpdateLink(PID,
...@@ -309,7 +307,7 @@ public class SimpleLinkStoreTest { ...@@ -309,7 +307,7 @@ public class SimpleLinkStoreTest {
309 307
310 assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject()); 308 assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
311 assertAnnotationsEquals(event2.subject().annotations(), A2, A1); 309 assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
312 - assertEquals(LINK_ADDED, event2.type()); 310 + assertEquals(LINK_UPDATED, event2.type());
313 311
314 // update link type 312 // update link type
315 LinkEvent event3 = linkStore.createOrUpdateLink(PID, 313 LinkEvent event3 = linkStore.createOrUpdateLink(PID,
...@@ -375,7 +373,7 @@ public class SimpleLinkStoreTest { ...@@ -375,7 +373,7 @@ public class SimpleLinkStoreTest {
375 } 373 }
376 374
377 @Test 375 @Test
378 - public final void testAncillaryOnlyNotVisible() { 376 + public final void testAncillaryVisible() {
379 ConnectPoint src = new ConnectPoint(DID1, P1); 377 ConnectPoint src = new ConnectPoint(DID1, P1);
380 ConnectPoint dst = new ConnectPoint(DID2, P2); 378 ConnectPoint dst = new ConnectPoint(DID2, P2);
381 379
...@@ -384,18 +382,8 @@ public class SimpleLinkStoreTest { ...@@ -384,18 +382,8 @@ public class SimpleLinkStoreTest {
384 new DefaultLinkDescription(src, dst, INDIRECT, A1)); 382 new DefaultLinkDescription(src, dst, INDIRECT, A1));
385 383
386 // Ancillary only link should not be visible 384 // Ancillary only link should not be visible
387 - assertEquals(0, linkStore.getLinkCount()); 385 + assertEquals(1, linkStore.getLinkCount());
388 - 386 + assertNotNull(linkStore.getLink(src, dst));
389 - assertTrue(Iterables.isEmpty(linkStore.getLinks()));
390 -
391 - assertNull(linkStore.getLink(src, dst));
392 -
393 - assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst));
394 -
395 - assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src));
396 -
397 - assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1));
398 - assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2));
399 } 387 }
400 388
401 // If Delegates should be called only on remote events, 389 // If Delegates should be called only on remote events,
......
...@@ -199,9 +199,16 @@ ...@@ -199,9 +199,16 @@
199 199
200 <feature name="onos-app-metrics" version="1.0.0" 200 <feature name="onos-app-metrics" version="1.0.0"
201 description="ONOS metrics applications"> 201 description="ONOS metrics applications">
202 + <feature>onos-app-metrics-intent</feature>
202 <feature>onos-app-metrics-topology</feature> 203 <feature>onos-app-metrics-topology</feature>
203 </feature> 204 </feature>
204 205
206 + <feature name="onos-app-metrics-intent" version="1.0.0"
207 + description="ONOS intent metrics application">
208 + <feature>onos-api</feature>
209 + <bundle>mvn:org.onlab.onos/onos-app-metrics-intent/1.0.0-SNAPSHOT</bundle>
210 + </feature>
211 +
205 <feature name="onos-app-metrics-topology" version="1.0.0" 212 <feature name="onos-app-metrics-topology" version="1.0.0"
206 description="ONOS topology metrics application"> 213 description="ONOS topology metrics application">
207 <feature>onos-api</feature> 214 <feature>onos-api</feature>
......
...@@ -88,7 +88,6 @@ ...@@ -88,7 +88,6 @@
88 <version>18.0</version> 88 <version>18.0</version>
89 </dependency> 89 </dependency>
90 90
91 -
92 <dependency> 91 <dependency>
93 <groupId>io.netty</groupId> 92 <groupId>io.netty</groupId>
94 <artifactId>netty</artifactId> 93 <artifactId>netty</artifactId>
......
...@@ -7,4 +7,4 @@ export OC1="192.168.56.101" ...@@ -7,4 +7,4 @@ export OC1="192.168.56.101"
7 export OCN="192.168.56.103" 7 export OCN="192.168.56.103"
8 export OCI="${OC1}" 8 export OCI="${OC1}"
9 9
10 -export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}" 10 +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}"
......
1 +{
2 + "devices" : [
3 + {
4 + "uri": "of:0000ffffffffff01", "mac": "ffffffffffff01", "type": "ROADM",
5 + "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
6 + "annotations": { "latitude": 37.6, "longitude": 122.3, "optical.regens": 0 }
7 + },
8 + {
9 + "uri": "of:0000ffffffffff02", "mac": "ffffffffffff02", "type": "ROADM",
10 + "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
11 + "annotations": { "latitude": 37.3, "longitude": 121.9, "optical.regens": 0 }
12 + },
13 + {
14 + "uri": "of:0000ffffffffff03", "mac": "ffffffffffff03", "type": "ROADM",
15 + "mfr": "Linc", "hw": "OE", "sw": "?", "serial": "?",
16 + "annotations": { "latitude": 33.9, "longitude": 118.4, "optical.regens": 2 }
17 + },
18 +
19 + {
20 + "uri": "of:0000ffffffff0001", "mac": "ffffffffff0003", "type": "SWITCH",
21 + "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
22 + "annotations": { "latitude": 37.6, "longitude": 122.3 }
23 + },
24 + {
25 + "uri": "of:0000ffffffff0002", "mac": "ffffffffff0002", "type": "SWITCH",
26 + "mfr": "Linc", "hw": "PK", "sw": "?", "serial": "?",
27 + "annotations": { "latitude": 37.3, "longitude": 121.9 }
28 + }
29 + ],
30 +
31 + "links" : [
32 + { "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff03/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
33 + { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff03/31", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM" } },
34 +
35 + { "src": "of:0000ffffffff0001/10", "dst": "of:0000ffffffffff01/11", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } },
36 + { "src": "of:0000ffffffff0002/10", "dst": "of:0000ffffffffff02/21", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect" } }
37 + ],
38 +
39 + "hosts" : [
40 + { "mac": "a0:00:00:00:00:11", "vlan": -1, "location": "of:0000ffffffff0001/11", "ip": "1.2.3.4" },
41 + { "mac": "a0:00:00:00:00:12", "vlan": -1, "location": "of:0000ffffffff0001/12", "ip": "1.2.3.5" },
42 + { "mac": "a0:00:00:00:00:21", "vlan": -1, "location": "of:0000ffffffff0002/11", "ip": "2.2.3.4" },
43 + { "mac": "a0:00:00:00:00:22", "vlan": -1, "location": "of:0000ffffffff0002/12", "ip": "2.2.3.5" }
44 + ]
45 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -32,7 +32,7 @@ public final class ChassisId { ...@@ -32,7 +32,7 @@ public final class ChassisId {
32 * @param value the value to use. 32 * @param value the value to use.
33 */ 33 */
34 public ChassisId(String value) { 34 public ChassisId(String value) {
35 - this.value = Long.valueOf(value); 35 + this.value = Long.valueOf(value, 16);
36 } 36 }
37 37
38 /** 38 /**
......
1 package org.onlab.nio; 1 package org.onlab.nio;
2 2
3 import org.junit.Before; 3 import org.junit.Before;
4 +import org.junit.Ignore;
4 import org.junit.Test; 5 import org.junit.Test;
5 6
6 import java.net.InetAddress; 7 import java.net.InetAddress;
...@@ -33,7 +34,8 @@ public class IOLoopIntegrationTest { ...@@ -33,7 +34,8 @@ public class IOLoopIntegrationTest {
33 } 34 }
34 } 35 }
35 36
36 - 37 + // TODO: this test can not pass in some environments, need to be improved
38 + @Ignore
37 @Test 39 @Test
38 public void basic() throws Exception { 40 public void basic() throws Exception {
39 runTest(MILLION, MESSAGE_LENGTH, TIMEOUT); 41 runTest(MILLION, MESSAGE_LENGTH, TIMEOUT);
......
...@@ -23,12 +23,6 @@ ...@@ -23,12 +23,6 @@
23 <version>1.0.0-SNAPSHOT</version> 23 <version>1.0.0-SNAPSHOT</version>
24 <scope>test</scope> 24 <scope>test</scope>
25 </dependency> 25 </dependency>
26 - <dependency>
27 - <groupId>com.google.guava</groupId>
28 - <artifactId>guava</artifactId>
29 - <scope>test</scope>
30 - </dependency>
31 -
32 </dependencies> 26 </dependencies>
33 27
34 <properties> 28 <properties>
......
1 +/*
2 + * Licensed to the Apache Software Foundation (ASF) under one
3 + * or more contributor license agreements. See the NOTICE file
4 + * distributed with this work for additional information
5 + * regarding copyright ownership. The ASF licenses this file
6 + * to you under the Apache License, Version 2.0 (the
7 + * "License"); you may not use this file except in compliance
8 + * with the License. You may obtain a copy of the License at
9 + *
10 + * http://www.apache.org/licenses/LICENSE-2.0
11 + *
12 + * Unless required by applicable law or agreed to in writing,
13 + * software distributed under the License is distributed on an
14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 + * KIND, either express or implied. See the License for the
16 + * specific language governing permissions and limitations
17 + * under the License.
18 + */
19 +package org.onlab.onos.rest;
20 +
21 +import com.fasterxml.jackson.databind.JsonNode;
22 +import org.onlab.onos.net.ConnectPoint;
23 +import org.onlab.onos.net.DefaultAnnotations;
24 +import org.onlab.onos.net.Device;
25 +import org.onlab.onos.net.Host;
26 +import org.onlab.onos.net.HostId;
27 +import org.onlab.onos.net.HostLocation;
28 +import org.onlab.onos.net.Link;
29 +import org.onlab.onos.net.MastershipRole;
30 +import org.onlab.onos.net.SparseAnnotations;
31 +import org.onlab.onos.net.device.DefaultDeviceDescription;
32 +import org.onlab.onos.net.device.DeviceDescription;
33 +import org.onlab.onos.net.device.DeviceProvider;
34 +import org.onlab.onos.net.device.DeviceProviderRegistry;
35 +import org.onlab.onos.net.device.DeviceProviderService;
36 +import org.onlab.onos.net.host.DefaultHostDescription;
37 +import org.onlab.onos.net.host.HostProvider;
38 +import org.onlab.onos.net.host.HostProviderRegistry;
39 +import org.onlab.onos.net.host.HostProviderService;
40 +import org.onlab.onos.net.link.DefaultLinkDescription;
41 +import org.onlab.onos.net.link.LinkProvider;
42 +import org.onlab.onos.net.link.LinkProviderRegistry;
43 +import org.onlab.onos.net.link.LinkProviderService;
44 +import org.onlab.onos.net.provider.ProviderId;
45 +import org.onlab.packet.ChassisId;
46 +import org.onlab.packet.IpPrefix;
47 +import org.onlab.packet.MacAddress;
48 +import org.onlab.packet.VlanId;
49 +
50 +import java.net.URI;
51 +import java.util.Iterator;
52 +
53 +import static com.google.common.base.Preconditions.checkNotNull;
54 +import static org.onlab.onos.net.DeviceId.deviceId;
55 +import static org.onlab.onos.net.PortNumber.portNumber;
56 +
57 +/**
58 + * Provider of devices and links parsed from a JSON configuration structure.
59 + */
60 +class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
61 +
62 + private static final ProviderId PID =
63 + new ProviderId("cfg", "org.onlab.onos.rest", true);
64 +
65 + private final JsonNode cfg;
66 + private final DeviceProviderRegistry deviceProviderRegistry;
67 + private final LinkProviderRegistry linkProviderRegistry;
68 + private final HostProviderRegistry hostProviderRegistry;
69 +
70 + /**
71 + * Creates a new configuration provider.
72 + *
73 + * @param cfg JSON configuration
74 + * @param deviceProviderRegistry device provider registry
75 + * @param linkProviderRegistry link provider registry
76 + * @param hostProviderRegistry host provider registry
77 + */
78 + ConfigProvider(JsonNode cfg,
79 + DeviceProviderRegistry deviceProviderRegistry,
80 + LinkProviderRegistry linkProviderRegistry,
81 + HostProviderRegistry hostProviderRegistry) {
82 + this.cfg = checkNotNull(cfg, "Configuration cannot be null");
83 + this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
84 + this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
85 + this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
86 + }
87 +
88 + /**
89 + * Parses the given JSON and provides links as configured.
90 + */
91 + void parse() {
92 + parseDevices();
93 + parseLinks();
94 + parseHosts();
95 + }
96 +
97 + // Parses the given JSON and provides devices.
98 + private void parseDevices() {
99 + try {
100 + DeviceProviderService dps = deviceProviderRegistry.register(this);
101 + JsonNode nodes = cfg.get("devices");
102 + if (nodes != null) {
103 + for (JsonNode node : nodes) {
104 + parseDevice(dps, node);
105 + }
106 + }
107 + } finally {
108 + deviceProviderRegistry.unregister(this);
109 + }
110 + }
111 +
112 + // Parses the given node with device data and supplies the device.
113 + private void parseDevice(DeviceProviderService dps, JsonNode node) {
114 + URI uri = URI.create(get(node, "uri"));
115 + Device.Type type = Device.Type.valueOf(get(node, "type"));
116 + String mfr = get(node, "mfr");
117 + String hw = get(node, "hw");
118 + String sw = get(node, "sw");
119 + String serial = get(node, "serial");
120 + ChassisId cid = new ChassisId(get(node, "mac"));
121 + SparseAnnotations annotations = annotations(node.get("annotations"));
122 +
123 + DeviceDescription desc =
124 + new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
125 + cid, annotations);
126 + dps.deviceConnected(deviceId(uri), desc);
127 + }
128 +
129 + // Parses the given JSON and provides links as configured.
130 + private void parseLinks() {
131 + try {
132 + LinkProviderService lps = linkProviderRegistry.register(this);
133 + JsonNode nodes = cfg.get("links");
134 + if (nodes != null) {
135 + for (JsonNode node : nodes) {
136 + parseLink(lps, node, false);
137 + if (!node.has("halfplex")) {
138 + parseLink(lps, node, true);
139 + }
140 + }
141 + }
142 + } finally {
143 + linkProviderRegistry.unregister(this);
144 + }
145 + }
146 +
147 + // Parses the given node with link data and supplies the link.
148 + private void parseLink(LinkProviderService lps, JsonNode node, boolean reverse) {
149 + ConnectPoint src = connectPoint(get(node, "src"));
150 + ConnectPoint dst = connectPoint(get(node, "dst"));
151 + Link.Type type = Link.Type.valueOf(get(node, "type"));
152 + SparseAnnotations annotations = annotations(node.get("annotations"));
153 +
154 + DefaultLinkDescription desc = reverse ?
155 + new DefaultLinkDescription(dst, src, type, annotations) :
156 + new DefaultLinkDescription(src, dst, type, annotations);
157 + lps.linkDetected(desc);
158 + }
159 +
160 + // Parses the given JSON and provides hosts as configured.
161 + private void parseHosts() {
162 + try {
163 + HostProviderService hps = hostProviderRegistry.register(this);
164 + JsonNode nodes = cfg.get("hosts");
165 + if (nodes != null) {
166 + for (JsonNode node : nodes) {
167 + parseHost(hps, node);
168 + }
169 + }
170 + } finally {
171 + hostProviderRegistry.unregister(this);
172 + }
173 + }
174 +
175 + // Parses the given node with host data and supplies the host.
176 + private void parseHost(HostProviderService hps, JsonNode node) {
177 + MacAddress mac = MacAddress.valueOf(get(node, "mac"));
178 + VlanId vlanId = VlanId.vlanId(node.get("vlan").shortValue());
179 + HostId hostId = HostId.hostId(mac, vlanId);
180 + SparseAnnotations annotations = annotations(node.get("annotations"));
181 + HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
182 + IpPrefix ip = IpPrefix.valueOf(get(node, "ip"));
183 +
184 + DefaultHostDescription desc =
185 + new DefaultHostDescription(mac, vlanId, location, ip, annotations);
186 + hps.hostDetected(hostId, desc);
187 + }
188 +
189 + // Produces set of annotations from the given JSON node.
190 + private SparseAnnotations annotations(JsonNode node) {
191 + if (node == null) {
192 + return null;
193 + }
194 +
195 + DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
196 + Iterator<String> it = node.fieldNames();
197 + while (it.hasNext()) {
198 + String k = it.next();
199 + builder.set(k, node.get(k).asText());
200 + }
201 + return builder.build();
202 + }
203 +
204 + // Produces a connection point from the specified uri/port text.
205 + private ConnectPoint connectPoint(String text) {
206 + int i = text.lastIndexOf("/");
207 + return new ConnectPoint(deviceId(text.substring(0, i)),
208 + portNumber(text.substring(i + 1)));
209 + }
210 +
211 + // Returns string form of the named property in the given JSON object.
212 + private String get(JsonNode node, String name) {
213 + return node.path(name).asText();
214 + }
215 +
216 + @Override
217 + public void triggerProbe(Device device) {
218 + }
219 +
220 + @Override
221 + public void roleChanged(Device device, MastershipRole newRole) {
222 + }
223 +
224 + @Override
225 + public void triggerProbe(Host host) {
226 + }
227 +
228 + @Override
229 + public ProviderId id() {
230 + return PID;
231 + }
232 +}
1 +/*
2 + * Licensed to the Apache Software Foundation (ASF) under one
3 + * or more contributor license agreements. See the NOTICE file
4 + * distributed with this work for additional information
5 + * regarding copyright ownership. The ASF licenses this file
6 + * to you under the Apache License, Version 2.0 (the
7 + * "License"); you may not use this file except in compliance
8 + * with the License. You may obtain a copy of the License at
9 + *
10 + * http://www.apache.org/licenses/LICENSE-2.0
11 + *
12 + * Unless required by applicable law or agreed to in writing,
13 + * software distributed under the License is distributed on an
14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 + * KIND, either express or implied. See the License for the
16 + * specific language governing permissions and limitations
17 + * under the License.
18 + */
19 +package org.onlab.onos.rest;
20 +
21 +import com.fasterxml.jackson.databind.JsonNode;
22 +import com.fasterxml.jackson.databind.ObjectMapper;
23 +import org.onlab.onos.net.device.DeviceProviderRegistry;
24 +import org.onlab.onos.net.host.HostProviderRegistry;
25 +import org.onlab.onos.net.link.LinkProviderRegistry;
26 +import org.onlab.rest.BaseResource;
27 +
28 +import javax.ws.rs.Consumes;
29 +import javax.ws.rs.POST;
30 +import javax.ws.rs.Path;
31 +import javax.ws.rs.Produces;
32 +import javax.ws.rs.core.MediaType;
33 +import javax.ws.rs.core.Response;
34 +import java.io.IOException;
35 +import java.io.InputStream;
36 +
37 +/**
38 + * Resource that acts as an ancillary provider for uploading pre-configured
39 + * devices, ports and links.
40 + */
41 +@Path("config")
42 +public class ConfigResource extends BaseResource {
43 +
44 + @POST
45 + @Path("topology")
46 + @Consumes(MediaType.APPLICATION_JSON)
47 + @Produces(MediaType.APPLICATION_JSON)
48 + public Response topology(InputStream input) throws IOException {
49 + ObjectMapper mapper = new ObjectMapper();
50 + JsonNode cfg = mapper.readTree(input);
51 + new ConfigProvider(cfg, get(DeviceProviderRegistry.class),
52 + get(LinkProviderRegistry.class),
53 + get(HostProviderRegistry.class)).parse();
54 + return Response.ok(mapper.createObjectNode().toString()).build();
55 + }
56 +
57 +}
1 +{
2 + "devices" : [
3 + {
4 + "uri": "of:00000000000001", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
5 + "serial": "ab321", "mac": "00000000000001", "annotations": {"foo": "bar"},
6 + "ports": []
7 + },
8 + {
9 + "uri": "of:00000000000002", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3",
10 + "serial": "ab456", "mac": "00000000000002", "annotations": {"foo": "bar"},
11 + "ports": []
12 + }
13 + ],
14 +
15 + "links" : [
16 + { "src": "of:00000000000001/1", "dst": "of:00000000000002/1", "type": "OPTICAL" },
17 + { "src": "of:00000000000002/1", "dst": "of:00000000000001/1", "type": "OPTICAL" }
18 + ]
19 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -44,6 +44,11 @@ ...@@ -44,6 +44,11 @@
44 </dependency> 44 </dependency>
45 45
46 <dependency> 46 <dependency>
47 + <groupId>com.google.guava</groupId>
48 + <artifactId>guava</artifactId>
49 + </dependency>
50 +
51 + <dependency>
47 <groupId>com.sun.jersey</groupId> 52 <groupId>com.sun.jersey</groupId>
48 <artifactId>jersey-servlet</artifactId> 53 <artifactId>jersey-servlet</artifactId>
49 </dependency> 54 </dependency>
...@@ -93,6 +98,7 @@ ...@@ -93,6 +98,7 @@
93 ${project.groupId}.${project.artifactId} 98 ${project.groupId}.${project.artifactId}
94 </Bundle-SymbolicName> 99 </Bundle-SymbolicName>
95 <Import-Package> 100 <Import-Package>
101 + org.slf4j,
96 org.osgi.framework, 102 org.osgi.framework,
97 javax.ws.rs,javax.ws.rs.core, 103 javax.ws.rs,javax.ws.rs.core,
98 com.sun.jersey.api.core, 104 com.sun.jersey.api.core,
...@@ -100,6 +106,8 @@ ...@@ -100,6 +106,8 @@
100 com.sun.jersey.server.impl.container.servlet, 106 com.sun.jersey.server.impl.container.servlet,
101 com.fasterxml.jackson.databind, 107 com.fasterxml.jackson.databind,
102 com.fasterxml.jackson.databind.node, 108 com.fasterxml.jackson.databind.node,
109 + com.google.common.base.*,
110 + org.onlab.packet.*,
103 org.onlab.rest.*, 111 org.onlab.rest.*,
104 org.onlab.onos.* 112 org.onlab.onos.*
105 </Import-Package> 113 </Import-Package>
......