Madan Jampani

Resolving merge conflicts

Showing 151 changed files with 4699 additions and 489 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.
127 + *
128 + * @param event the event to record
129 + * @param updateEventRateMeter if true, update the Event Rate Meter
130 + */
131 + private void recordEvent(Event event, boolean updateEventRateMeter) {
132 + synchronized (lastEvents) {
95 lastEventTimestampEpochMs = System.currentTimeMillis(); 133 lastEventTimestampEpochMs = System.currentTimeMillis();
96 - // 134 + if (updateEventRateMeter) {
97 - // NOTE: If we want to count each "reason" as a separate event,
98 - // then we should use 'event.reason().size()' instead of '1' to
99 - // mark the meter below.
100 - //
101 eventRateMeter.mark(1); 135 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 } 136 }
109 137
110 // 138 //
111 // Keep only the last N events, where N = LAST_EVENTS_MAX_N 139 // Keep only the last N events, where N = LAST_EVENTS_MAX_N
112 // 140 //
113 - synchronized (lastEvents) {
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) { 68 + private ObjectNode json(ObjectMapper mapper, Event event) {
70 ObjectNode result = mapper.createObjectNode(); 69 ObjectNode result = mapper.createObjectNode();
71 - ArrayNode reasons = mapper.createArrayNode();
72 70
71 + result.put("time", event.time())
72 + .put("type", event.type().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();
73 for (Event reason : topologyEvent.reasons()) { 79 for (Event reason : topologyEvent.reasons()) {
74 reasons.add(json(mapper, reason)); 80 reasons.add(json(mapper, reason));
75 } 81 }
76 - result.put("time", topologyEvent.time()) 82 + result.put("reasons", reasons);
77 - .put("type", topologyEvent.type().toString())
78 - .put("subject", topologyEvent.subject().toString())
79 - .put("reasons", reasons);
80 - return result;
81 } 83 }
82 84
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) {
90 - ObjectNode result = mapper.createObjectNode();
91 -
92 - result.put("time", event.time())
93 - .put("type", event.type().toString())
94 - .put("subject", event.subject().toString());
95 return result; 85 return result;
96 } 86 }
97 } 87 }
......
...@@ -83,7 +83,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro ...@@ -83,7 +83,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
83 protected OpticalNetworkConfig opticalNetworkConfig; 83 protected OpticalNetworkConfig opticalNetworkConfig;
84 84
85 public OpticalConfigProvider() { 85 public OpticalConfigProvider() {
86 - super(new ProviderId("of", "org.onlab.onos.provider.opticalConfig", true)); 86 + super(new ProviderId("optical", "org.onlab.onos.provider.opticalConfig", true));
87 } 87 }
88 88
89 @Activate 89 @Activate
...@@ -238,7 +238,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro ...@@ -238,7 +238,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro
238 while (iterWdmNode.hasNext()) { 238 while (iterWdmNode.hasNext()) {
239 Roadm value = iterWdmNode.next(); 239 Roadm value = iterWdmNode.next();
240 DeviceId did = deviceId("of:" + value.getNodeId().replace(":", "")); 240 DeviceId did = deviceId("of:" + value.getNodeId().replace(":", ""));
241 - ChassisId cid = new ChassisId(value.getNodeId()); 241 + ChassisId cid = new ChassisId();
242 DefaultAnnotations extendedAttributes = DefaultAnnotations.builder() 242 DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
243 .set(OPTICAL_ANNOTATION + "switchType", "ROADM") 243 .set(OPTICAL_ANNOTATION + "switchType", "ROADM")
244 .set(OPTICAL_ANNOTATION + "switchName", value.getName()) 244 .set(OPTICAL_ANNOTATION + "switchName", value.getName())
...@@ -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);
......
...@@ -52,9 +52,37 @@ ...@@ -52,9 +52,37 @@
52 }, 52 },
53 "type": "wdmLink" 53 "type": "wdmLink"
54 }, 54 },
55 + {
56 + "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 + },
55 69
56 { 70 {
57 "allowed": true, 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": {
...@@ -68,7 +96,6 @@ ...@@ -68,7 +96,6 @@
68 "type": "wdmLink" 96 "type": "wdmLink"
69 }, 97 },
70 98
71 -
72 { 99 {
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",
...@@ -82,6 +109,19 @@ ...@@ -82,6 +109,19 @@
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,
...@@ -95,6 +135,19 @@ ...@@ -95,6 +135,19 @@
95 "port2": 21 135 "port2": 21
96 }, 136 },
97 "type": "pktOptLink" 137 "type": "pktOptLink"
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"
98 } 151 }
99 152
100 ] 153 ]
......
...@@ -4,19 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -4,19 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
4 4
5 import java.util.Set; 5 import java.util.Set;
6 6
7 -import org.apache.commons.lang.NotImplementedException;
8 import org.onlab.onos.net.ConnectPoint; 7 import org.onlab.onos.net.ConnectPoint;
9 import org.onlab.onos.net.host.HostService; 8 import org.onlab.onos.net.host.HostService;
10 import org.onlab.onos.net.host.PortAddresses; 9 import org.onlab.onos.net.host.PortAddresses;
11 import org.onlab.onos.sdnip.config.Interface; 10 import org.onlab.onos.sdnip.config.Interface;
12 import org.onlab.packet.IpAddress; 11 import org.onlab.packet.IpAddress;
12 +import org.onlab.packet.IpPrefix;
13 13
14 import com.google.common.collect.Sets; 14 import com.google.common.collect.Sets;
15 15
16 -
17 -
18 /** 16 /**
19 - * Provides IntefaceService using PortAddresses data from the HostService. 17 + * Provides InterfaceService using PortAddresses data from the HostService.
20 */ 18 */
21 public class HostToInterfaceAdaptor implements InterfaceService { 19 public class HostToInterfaceAdaptor implements InterfaceService {
22 20
...@@ -52,8 +50,17 @@ public class HostToInterfaceAdaptor implements InterfaceService { ...@@ -52,8 +50,17 @@ public class HostToInterfaceAdaptor implements InterfaceService {
52 50
53 @Override 51 @Override
54 public Interface getMatchingInterface(IpAddress ipAddress) { 52 public Interface getMatchingInterface(IpAddress ipAddress) {
55 - // TODO implement 53 + checkNotNull(ipAddress);
56 - throw new NotImplementedException("getMatchingInteface is not yet implemented"); 54 +
55 + for (PortAddresses portAddresses : hostService.getAddressBindings()) {
56 + for (IpPrefix p : portAddresses.ips()) {
57 + if (p.contains(ipAddress)) {
58 + return new Interface(portAddresses);
59 + }
60 + }
61 + }
62 +
63 + return null;
57 } 64 }
58 65
59 } 66 }
......
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 {
......
...@@ -64,6 +64,7 @@ public class SdnIp implements SdnIpService { ...@@ -64,6 +64,7 @@ public class SdnIp implements SdnIpService {
64 bgpSessionManager.startUp(2000); // TODO 64 bgpSessionManager.startUp(2000); // TODO
65 65
66 // TODO need to disable link discovery on external ports 66 // TODO need to disable link discovery on external ports
67 +
67 } 68 }
68 69
69 @Deactivate 70 @Deactivate
......
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.junit.Assert.assertEquals;
8 +import static org.junit.Assert.assertNull;
9 +import static org.junit.Assert.assertTrue;
10 +
11 +import java.util.Map;
12 +import java.util.Set;
13 +
14 +import org.junit.Before;
15 +import org.junit.Test;
16 +import org.onlab.onos.net.ConnectPoint;
17 +import org.onlab.onos.net.DeviceId;
18 +import org.onlab.onos.net.PortNumber;
19 +import org.onlab.onos.net.host.HostService;
20 +import org.onlab.onos.net.host.PortAddresses;
21 +import org.onlab.onos.sdnip.config.Interface;
22 +import org.onlab.packet.IpAddress;
23 +import org.onlab.packet.IpPrefix;
24 +import org.onlab.packet.MacAddress;
25 +
26 +import com.google.common.collect.Maps;
27 +import com.google.common.collect.Sets;
28 +
29 +/**
30 + * Unit tests for the HostToInterfaceAdaptor class.
31 + */
32 +public class HostToInterfaceAdaptorTest {
33 +
34 + private HostService hostService;
35 + private HostToInterfaceAdaptor adaptor;
36 +
37 + private Set<PortAddresses> portAddresses;
38 + private Map<ConnectPoint, Interface> interfaces;
39 +
40 + private static final ConnectPoint CP1 = new ConnectPoint(
41 + DeviceId.deviceId("of:1"), PortNumber.portNumber(1));
42 + private static final ConnectPoint CP2 = new ConnectPoint(
43 + DeviceId.deviceId("of:1"), PortNumber.portNumber(2));
44 + private static final ConnectPoint CP3 = new ConnectPoint(
45 + DeviceId.deviceId("of:2"), PortNumber.portNumber(1));
46 +
47 + private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint(
48 + DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1));
49 +
50 + private static final PortAddresses DEFAULT_PA = new PortAddresses(
51 + NON_EXISTENT_CP, null, null);
52 +
53 +
54 + @Before
55 + public void setUp() throws Exception {
56 + hostService = createMock(HostService.class);
57 +
58 + portAddresses = Sets.newHashSet();
59 + interfaces = Maps.newHashMap();
60 +
61 + createPortAddressesAndInterface(CP1,
62 + Sets.newHashSet(IpPrefix.valueOf("192.168.1.1/24")),
63 + MacAddress.valueOf("00:00:00:00:00:01"));
64 +
65 + // Two addresses in the same subnet
66 + createPortAddressesAndInterface(CP2,
67 + Sets.newHashSet(IpPrefix.valueOf("192.168.2.1/24"),
68 + IpPrefix.valueOf("192.168.2.2/24")),
69 + MacAddress.valueOf("00:00:00:00:00:02"));
70 +
71 + // Two addresses in different subnets
72 + createPortAddressesAndInterface(CP3,
73 + Sets.newHashSet(IpPrefix.valueOf("192.168.3.1/24"),
74 + IpPrefix.valueOf("192.168.4.1/24")),
75 + MacAddress.valueOf("00:00:00:00:00:03"));
76 +
77 + expect(hostService.getAddressBindings()).andReturn(portAddresses).anyTimes();
78 +
79 + replay(hostService);
80 +
81 + adaptor = new HostToInterfaceAdaptor(hostService);
82 + }
83 +
84 + /**
85 + * Creates both a PortAddresses and an Interface for the given inputs and
86 + * places them in the correct global data stores.
87 + *
88 + * @param cp the connect point
89 + * @param ips the set of IP addresses
90 + * @param mac the MAC address
91 + */
92 + private void createPortAddressesAndInterface(
93 + ConnectPoint cp, Set<IpPrefix> ips, MacAddress mac) {
94 + PortAddresses pa = new PortAddresses(cp, ips, mac);
95 + portAddresses.add(pa);
96 + expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes();
97 +
98 + Interface intf = new Interface(cp, ips, mac);
99 + interfaces.put(cp, intf);
100 + }
101 +
102 + /**
103 + * Tests {@link HostToInterfaceAdaptor#getInterfaces()}.
104 + * Verifies that the set of interfaces returned matches what is expected
105 + * based on the input PortAddresses data.
106 + */
107 + @Test
108 + public void testGetInterfaces() {
109 + Set<Interface> adaptorIntfs = adaptor.getInterfaces();
110 +
111 + assertEquals(3, adaptorIntfs.size());
112 + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP1)));
113 + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP2)));
114 + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP3)));
115 + }
116 +
117 + /**
118 + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)}.
119 + * Verifies that the correct interface is returned for a given connect
120 + * point.
121 + */
122 + @Test
123 + public void testGetInterface() {
124 + assertEquals(this.interfaces.get(CP1), adaptor.getInterface(CP1));
125 + assertEquals(this.interfaces.get(CP2), adaptor.getInterface(CP2));
126 + assertEquals(this.interfaces.get(CP3), adaptor.getInterface(CP3));
127 +
128 + // Try and get an interface for a connect point with no addresses
129 + reset(hostService);
130 + expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP))
131 + .andReturn(DEFAULT_PA).anyTimes();
132 + replay(hostService);
133 +
134 + assertNull(adaptor.getInterface(NON_EXISTENT_CP));
135 + }
136 +
137 + /**
138 + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)} in the
139 + * case that the input connect point is null.
140 + * Verifies that a NullPointerException is thrown.
141 + */
142 + @Test(expected = NullPointerException.class)
143 + public void testGetInterfaceNull() {
144 + adaptor.getInterface(null);
145 + }
146 +
147 + /**
148 + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)}.
149 + * Verifies that the correct interface is returned based on the given IP
150 + * address.
151 + */
152 + @Test
153 + public void testGetMatchingInterface() {
154 + assertEquals(this.interfaces.get(CP1),
155 + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.1.100")));
156 + assertEquals(this.interfaces.get(CP2),
157 + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.2.100")));
158 + assertEquals(this.interfaces.get(CP3),
159 + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.3.100")));
160 + assertEquals(this.interfaces.get(CP3),
161 + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.4.100")));
162 +
163 + // Try and match an address we don't have subnet configured for
164 + assertNull(adaptor.getMatchingInterface(IpAddress.valueOf("1.1.1.1")));
165 + }
166 +
167 + /**
168 + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)} in the
169 + * case that the input IP address is null.
170 + * Verifies that a NullPointerException is thrown.
171 + */
172 + @Test(expected = NullPointerException.class)
173 + public void testGetMatchingInterfaceNull() {
174 + adaptor.getMatchingInterface(null);
175 + }
176 +
177 +}
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 +}
...@@ -18,10 +18,13 @@ ...@@ -18,10 +18,13 @@
18 */ 18 */
19 package org.onlab.onos.cli; 19 package org.onlab.onos.cli;
20 20
21 +import com.fasterxml.jackson.databind.ObjectMapper;
22 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 import org.apache.karaf.shell.commands.Option; 23 import org.apache.karaf.shell.commands.Option;
22 import org.apache.karaf.shell.console.OsgiCommandSupport; 24 import org.apache.karaf.shell.console.OsgiCommandSupport;
23 import org.onlab.onos.ApplicationId; 25 import org.onlab.onos.ApplicationId;
24 import org.onlab.onos.CoreService; 26 import org.onlab.onos.CoreService;
27 +import org.onlab.onos.net.Annotations;
25 import org.onlab.osgi.DefaultServiceDirectory; 28 import org.onlab.osgi.DefaultServiceDirectory;
26 import org.onlab.osgi.ServiceNotFoundException; 29 import org.onlab.osgi.ServiceNotFoundException;
27 30
...@@ -76,6 +79,34 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport { ...@@ -76,6 +79,34 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport {
76 } 79 }
77 80
78 /** 81 /**
82 + * Produces a string image of the specified key/value annotations.
83 + *
84 + * @param annotations key/value annotations
85 + * @return string image with ", k1=v1, k2=v2, ..." pairs
86 + */
87 + public static String annotations(Annotations annotations) {
88 + StringBuilder sb = new StringBuilder();
89 + for (String key : annotations.keys()) {
90 + sb.append(", ").append(key).append('=').append(annotations.value(key));
91 + }
92 + return sb.toString();
93 + }
94 +
95 + /**
96 + * Produces a JSON object from the specified key/value annotations.
97 + *
98 + * @param annotations key/value annotations
99 + * @return JSON object
100 + */
101 + public static ObjectNode annotations(ObjectMapper mapper, Annotations annotations) {
102 + ObjectNode result = mapper.createObjectNode();
103 + for (String key : annotations.keys()) {
104 + result.put(key, annotations.value(key));
105 + }
106 + return result;
107 + }
108 +
109 + /**
79 * Executes this command. 110 * Executes this command.
80 */ 111 */
81 protected abstract void execute(); 112 protected abstract void execute();
......
...@@ -43,7 +43,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; ...@@ -43,7 +43,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
43 description = "Lists all ports or all ports of a device") 43 description = "Lists all ports or all ports of a device")
44 public class DevicePortsListCommand extends DevicesListCommand { 44 public class DevicePortsListCommand extends DevicesListCommand {
45 45
46 - private static final String FMT = " port=%s, state=%s"; 46 + private static final String FMT = " port=%s, state=%s%s";
47 47
48 @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports", 48 @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports",
49 required = false, multiValued = false) 49 required = false, multiValued = false)
...@@ -112,7 +112,8 @@ public class DevicePortsListCommand extends DevicesListCommand { ...@@ -112,7 +112,8 @@ public class DevicePortsListCommand extends DevicesListCommand {
112 if (isIncluded(port)) { 112 if (isIncluded(port)) {
113 ports.add(mapper.createObjectNode() 113 ports.add(mapper.createObjectNode()
114 .put("port", port.number().toString()) 114 .put("port", port.number().toString())
115 - .put("isEnabled", port.isEnabled())); 115 + .put("isEnabled", port.isEnabled())
116 + .set("annotations", annotations(mapper, port.annotations())));
116 } 117 }
117 } 118 }
118 return result.put("device", device.id().toString()).set("ports", ports); 119 return result.put("device", device.id().toString()).set("ports", ports);
...@@ -131,7 +132,8 @@ public class DevicePortsListCommand extends DevicesListCommand { ...@@ -131,7 +132,8 @@ public class DevicePortsListCommand extends DevicesListCommand {
131 Collections.sort(ports, Comparators.PORT_COMPARATOR); 132 Collections.sort(ports, Comparators.PORT_COMPARATOR);
132 for (Port port : ports) { 133 for (Port port : ports) {
133 if (isIncluded(port)) { 134 if (isIncluded(port)) {
134 - print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled"); 135 + print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled",
136 + annotations(port.annotations()));
135 } 137 }
136 } 138 }
137 } 139 }
......
...@@ -41,7 +41,7 @@ import static com.google.common.collect.Lists.newArrayList; ...@@ -41,7 +41,7 @@ import static com.google.common.collect.Lists.newArrayList;
41 public class DevicesListCommand extends AbstractShellCommand { 41 public class DevicesListCommand extends AbstractShellCommand {
42 42
43 private static final String FMT = 43 private static final String FMT =
44 - "id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s"; 44 + "id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s%s";
45 45
46 @Override 46 @Override
47 protected void execute() { 47 protected void execute() {
...@@ -89,7 +89,8 @@ public class DevicesListCommand extends AbstractShellCommand { ...@@ -89,7 +89,8 @@ public class DevicesListCommand extends AbstractShellCommand {
89 .put("mfr", device.manufacturer()) 89 .put("mfr", device.manufacturer())
90 .put("hw", device.hwVersion()) 90 .put("hw", device.hwVersion())
91 .put("sw", device.swVersion()) 91 .put("sw", device.swVersion())
92 - .put("serial", device.serialNumber()); 92 + .put("serial", device.serialNumber())
93 + .set("annotations", annotations(mapper, device.annotations()));
93 } 94 }
94 return result; 95 return result;
95 } 96 }
...@@ -117,7 +118,7 @@ public class DevicesListCommand extends AbstractShellCommand { ...@@ -117,7 +118,7 @@ public class DevicesListCommand extends AbstractShellCommand {
117 print(FMT, device.id(), service.isAvailable(device.id()), 118 print(FMT, device.id(), service.isAvailable(device.id()),
118 service.getRole(device.id()), device.type(), 119 service.getRole(device.id()), device.type(),
119 device.manufacturer(), device.hwVersion(), device.swVersion(), 120 device.manufacturer(), device.hwVersion(), device.swVersion(),
120 - device.serialNumber()); 121 + device.serialNumber(), annotations(device.annotations()));
121 } 122 }
122 } 123 }
123 124
......
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.cli.net;
20 +
21 +import org.apache.karaf.shell.commands.Argument;
22 +import org.apache.karaf.shell.commands.Command;
23 +import org.onlab.onos.cli.AbstractShellCommand;
24 +import org.onlab.onos.net.ConnectPoint;
25 +import org.onlab.onos.net.DeviceId;
26 +import org.onlab.onos.net.PortNumber;
27 +
28 +import org.onlab.onos.net.statistic.Load;
29 +import org.onlab.onos.net.statistic.StatisticService;
30 +
31 +
32 +import static org.onlab.onos.net.DeviceId.deviceId;
33 +import static org.onlab.onos.net.PortNumber.portNumber;
34 +
35 +/**
36 + * Fetches statistics.
37 + */
38 +@Command(scope = "onos", name = "get-stats",
39 + description = "Fetches stats for a connection point")
40 +public class GetStatistics extends AbstractShellCommand {
41 +
42 + @Argument(index = 0, name = "connectPoint",
43 + description = "Device/Port Description",
44 + required = true, multiValued = false)
45 + String connectPoint = null;
46 +
47 +
48 + @Override
49 + protected void execute() {
50 + StatisticService service = get(StatisticService.class);
51 +
52 + DeviceId ingressDeviceId = deviceId(getDeviceId(connectPoint));
53 + PortNumber ingressPortNumber = portNumber(getPortNumber(connectPoint));
54 + ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
55 +
56 + Load load = service.load(cp);
57 +
58 + print("Load on %s -> %s", cp, load);
59 + }
60 +
61 + /**
62 + * Extracts the port number portion of the ConnectPoint.
63 + *
64 + * @param deviceString string representing the device/port
65 + * @return port number as a string, empty string if the port is not found
66 + */
67 + private String getPortNumber(String deviceString) {
68 + int slash = deviceString.indexOf('/');
69 + if (slash <= 0) {
70 + return "";
71 + }
72 + return deviceString.substring(slash + 1, deviceString.length());
73 + }
74 +
75 + /**
76 + * Extracts the device ID portion of the ConnectPoint.
77 + *
78 + * @param deviceString string representing the device/port
79 + * @return device ID string
80 + */
81 + private String getDeviceId(String deviceString) {
82 + int slash = deviceString.indexOf('/');
83 + if (slash <= 0) {
84 + return "";
85 + }
86 + return deviceString.substring(0, slash);
87 + }
88 +}
...@@ -42,7 +42,7 @@ import static com.google.common.collect.Lists.newArrayList; ...@@ -42,7 +42,7 @@ import static com.google.common.collect.Lists.newArrayList;
42 public class HostsListCommand extends AbstractShellCommand { 42 public class HostsListCommand extends AbstractShellCommand {
43 43
44 private static final String FMT = 44 private static final String FMT =
45 - "id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s"; 45 + "id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s%s";
46 46
47 @Override 47 @Override
48 protected void execute() { 48 protected void execute() {
...@@ -80,6 +80,7 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -80,6 +80,7 @@ public class HostsListCommand extends AbstractShellCommand {
80 .put("vlan", host.vlan().toString()); 80 .put("vlan", host.vlan().toString());
81 result.set("location", loc); 81 result.set("location", loc);
82 result.set("ips", ips); 82 result.set("ips", ips);
83 + result.set("annotations", annotations(mapper, host.annotations()));
83 return result; 84 return result;
84 } 85 }
85 86
...@@ -105,7 +106,8 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -105,7 +106,8 @@ public class HostsListCommand extends AbstractShellCommand {
105 print(FMT, host.id(), host.mac(), 106 print(FMT, host.id(), host.mac(),
106 host.location().deviceId(), 107 host.location().deviceId(),
107 host.location().port(), 108 host.location().port(),
108 - host.vlan(), host.ipAddresses()); 109 + host.vlan(), host.ipAddresses(),
110 + annotations(host.annotations()));
109 } 111 }
110 } 112 }
111 } 113 }
......
...@@ -38,7 +38,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; ...@@ -38,7 +38,7 @@ import static org.onlab.onos.net.DeviceId.deviceId;
38 description = "Lists all infrastructure links") 38 description = "Lists all infrastructure links")
39 public class LinksListCommand extends AbstractShellCommand { 39 public class LinksListCommand extends AbstractShellCommand {
40 40
41 - private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s"; 41 + private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s%s";
42 private static final String COMPACT = "%s/%s-%s/%s"; 42 private static final String COMPACT = "%s/%s-%s/%s";
43 43
44 @Argument(index = 0, name = "uri", description = "Device ID", 44 @Argument(index = 0, name = "uri", description = "Device ID",
...@@ -85,6 +85,7 @@ public class LinksListCommand extends AbstractShellCommand { ...@@ -85,6 +85,7 @@ public class LinksListCommand extends AbstractShellCommand {
85 ObjectNode result = mapper.createObjectNode(); 85 ObjectNode result = mapper.createObjectNode();
86 result.set("src", json(mapper, link.src())); 86 result.set("src", json(mapper, link.src()));
87 result.set("dst", json(mapper, link.dst())); 87 result.set("dst", json(mapper, link.dst()));
88 + result.set("annotations", annotations(mapper, link.annotations()));
88 return result; 89 return result;
89 } 90 }
90 91
...@@ -109,7 +110,8 @@ public class LinksListCommand extends AbstractShellCommand { ...@@ -109,7 +110,8 @@ public class LinksListCommand extends AbstractShellCommand {
109 */ 110 */
110 public static String linkString(Link link) { 111 public static String linkString(Link link) {
111 return String.format(FMT, link.src().deviceId(), link.src().port(), 112 return String.format(FMT, link.src().deviceId(), link.src().port(),
112 - link.dst().deviceId(), link.dst().port(), link.type()); 113 + link.dst().deviceId(), link.dst().port(), link.type(),
114 + annotations(link.annotations()));
113 } 115 }
114 116
115 /** 117 /**
......
...@@ -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())
......
...@@ -119,6 +119,12 @@ ...@@ -119,6 +119,12 @@
119 </optional-completers> 119 </optional-completers>
120 </command> 120 </command>
121 <command> 121 <command>
122 + <action class="org.onlab.onos.cli.net.GetStatistics"/>
123 + <completers>
124 + <ref component-id="connectPointCompleter"/>
125 + </completers>
126 + </command>
127 + <command>
122 <action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/> 128 <action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/>
123 <completers> 129 <completers>
124 <ref component-id="connectPointCompleter"/> 130 <ref component-id="connectPointCompleter"/>
......
1 package org.onlab.onos.mastership; 1 package org.onlab.onos.mastership;
2 2
3 -import org.onlab.onos.cluster.NodeId;
4 import org.onlab.onos.cluster.RoleInfo; 3 import org.onlab.onos.cluster.RoleInfo;
5 import org.onlab.onos.event.AbstractEvent; 4 import org.onlab.onos.event.AbstractEvent;
6 import org.onlab.onos.net.DeviceId; 5 import org.onlab.onos.net.DeviceId;
...@@ -56,19 +55,6 @@ public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceI ...@@ -56,19 +55,6 @@ public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceI
56 } 55 }
57 56
58 /** 57 /**
59 - * Returns the NodeID of the node associated with the event.
60 - * For MASTER_CHANGED this is the newly elected master, and for
61 - * BACKUPS_CHANGED, this is the node that was newly added, removed, or
62 - * whose position was changed in the list.
63 - *
64 - * @return node ID as a subject
65 - */
66 - //XXX to-be removed - or keep for convenience?
67 - public NodeId node() {
68 - return roleInfo.master();
69 - }
70 -
71 - /**
72 * Returns the current role state for the subject. 58 * Returns the current role state for the subject.
73 * 59 *
74 * @return RoleInfo associated with Device ID subject 60 * @return RoleInfo associated with Device ID subject
......
...@@ -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 }
......
...@@ -4,6 +4,8 @@ import org.onlab.onos.event.AbstractEvent; ...@@ -4,6 +4,8 @@ import org.onlab.onos.event.AbstractEvent;
4 import org.onlab.onos.net.Device; 4 import org.onlab.onos.net.Device;
5 import org.onlab.onos.net.Port; 5 import org.onlab.onos.net.Port;
6 6
7 +import static com.google.common.base.MoreObjects.toStringHelper;
8 +
7 /** 9 /**
8 * Describes infrastructure device event. 10 * Describes infrastructure device event.
9 */ 11 */
...@@ -109,4 +111,12 @@ public class DeviceEvent extends AbstractEvent<DeviceEvent.Type, Device> { ...@@ -109,4 +111,12 @@ public class DeviceEvent extends AbstractEvent<DeviceEvent.Type, Device> {
109 return port; 111 return port;
110 } 112 }
111 113
114 + @Override
115 + public String toString() {
116 + if (port == null) {
117 + return super.toString();
118 + }
119 + return toStringHelper(this).add("time", time()).add("type", type())
120 + .add("subject", subject()).add("port", port).toString();
121 + }
112 } 122 }
......
...@@ -13,7 +13,7 @@ public interface DeviceProvider extends Provider { ...@@ -13,7 +13,7 @@ public interface DeviceProvider extends Provider {
13 13
14 /** 14 /**
15 * Triggers an asynchronous probe of the specified device, intended to 15 * Triggers an asynchronous probe of the specified device, intended to
16 - * determine whether the host is present or not. An indirect result of this 16 + * determine whether the device is present or not. An indirect result of this
17 * should be invocation of 17 * should be invocation of
18 * {@link org.onlab.onos.net.device.DeviceProviderService#deviceConnected} )} or 18 * {@link org.onlab.onos.net.device.DeviceProviderService#deviceConnected} )} or
19 * {@link org.onlab.onos.net.device.DeviceProviderService#deviceDisconnected} 19 * {@link org.onlab.onos.net.device.DeviceProviderService#deviceDisconnected}
......
1 -package org.onlab.onos.net.intent; 1 +/*
2 -//TODO is this the right package? 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.net.flow;
3 20
4 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.base.Preconditions.checkNotNull;
5 22
......
1 -package org.onlab.onos.net.intent; 1 +/*
2 -//TODO is this the right package? 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.net.flow;
3 20
4 import java.util.Objects; 21 import java.util.Objects;
5 22
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.Set; 21 import java.util.Set;
......
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.net.flow;
20 +
21 +/**
22 + * An interface of the class which is assigned to BatchOperation.
23 + */
24 +public interface BatchOperationTarget {
25 +
26 +}
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.Set; 21 import java.util.Set;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.HashMap; 21 import java.util.HashMap;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.LinkedList; 21 import java.util.LinkedList;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 21
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import com.google.common.base.Objects; 21 import com.google.common.base.Objects;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.net.DeviceId; 21 import org.onlab.onos.net.DeviceId;
4 -import org.onlab.onos.net.intent.BatchOperationTarget;
5 22
6 /** 23 /**
7 * Represents a generalized match &amp; action pair to be applied to 24 * Represents a generalized match &amp; action pair to be applied to
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation; 21 import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
4 -import org.onlab.onos.net.intent.BatchOperationEntry;
5 22
6 23
7 public class FlowRuleBatchEntry 24 public class FlowRuleBatchEntry
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.Collection; 21 import java.util.Collection;
4 22
5 -import org.onlab.onos.net.intent.BatchOperation;
6 -
7 public class FlowRuleBatchOperation 23 public class FlowRuleBatchOperation
8 extends BatchOperation<FlowRuleBatchEntry> { 24 extends BatchOperation<FlowRuleBatchEntry> {
9 25
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.event.AbstractEvent; 21 import org.onlab.onos.event.AbstractEvent;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.event.EventListener; 21 import org.onlab.onos.event.EventListener;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.ApplicationId; 21 import org.onlab.onos.ApplicationId;
4 -import org.onlab.onos.net.intent.BatchOperation;
5 import org.onlab.onos.net.provider.Provider; 22 import org.onlab.onos.net.provider.Provider;
6 23
7 import com.google.common.util.concurrent.ListenableFuture; 24 import com.google.common.util.concurrent.ListenableFuture;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.net.provider.ProviderRegistry; 21 import org.onlab.onos.net.provider.ProviderRegistry;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.net.DeviceId; 21 import org.onlab.onos.net.DeviceId;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.concurrent.Future; 21 import java.util.concurrent.Future;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.concurrent.Future; 21 import java.util.concurrent.Future;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.store.StoreDelegate; 21 import org.onlab.onos.store.StoreDelegate;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 21
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.Set; 21 import java.util.Set;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import java.util.List; 21 import java.util.List;
......
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 + */
1 package org.onlab.onos.net.flow; 19 package org.onlab.onos.net.flow;
2 20
3 import org.onlab.onos.net.PortNumber; 21 import org.onlab.onos.net.PortNumber;
......
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 + */
1 package org.onlab.onos.net.flow.criteria; 19 package org.onlab.onos.net.flow.criteria;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 + */
1 package org.onlab.onos.net.flow.criteria; 19 package org.onlab.onos.net.flow.criteria;
2 20
3 21
......
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 +
1 /** 20 /**
2 * Traffic selection criteria model. 21 * Traffic selection criteria model.
3 */ 22 */
......
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 + */
1 package org.onlab.onos.net.flow.instructions; 19 package org.onlab.onos.net.flow.instructions;
2 20
3 /** 21 /**
......
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 + */
1 package org.onlab.onos.net.flow.instructions; 19 package org.onlab.onos.net.flow.instructions;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 + */
1 package org.onlab.onos.net.flow.instructions; 19 package org.onlab.onos.net.flow.instructions;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 + */
1 package org.onlab.onos.net.flow.instructions; 19 package org.onlab.onos.net.flow.instructions;
2 20
3 import static com.google.common.base.MoreObjects.toStringHelper; 21 import static com.google.common.base.MoreObjects.toStringHelper;
......
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 +
1 /** 20 /**
2 * Traffic treatment model. 21 * Traffic treatment model.
3 */ 22 */
......
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 +
1 /** 20 /**
2 * Flow rule model &amp; related services API definitions. 21 * Flow rule model &amp; related services API definitions.
3 */ 22 */
......
1 -package org.onlab.onos.net.intent;
2 -//TODO is this the right package?
3 -
4 -/**
5 - * An interface of the class which is assigned to BatchOperation.
6 - */
7 -public interface BatchOperationTarget {
8 -
9 -}
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.collect.ImmutableSet; 21 import com.google.common.collect.ImmutableSet;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import org.onlab.onos.ApplicationId; 21 import org.onlab.onos.ApplicationId;
4 import org.onlab.onos.net.NetworkResource; 22 import org.onlab.onos.net.NetworkResource;
23 +import org.onlab.onos.net.flow.BatchOperationTarget;
5 24
6 import java.util.Collection; 25 import java.util.Collection;
7 import java.util.Objects; 26 import java.util.Objects;
......
1 -package org.onlab.onos.net.intent;
2 -
3 -/**
4 - * A list of intent operations.
5 - */
6 -public class IntentBatchOperation extends
7 - BatchOperation<BatchOperationEntry<IntentBatchOperation.Operator, ?>> {
8 - /**
9 - * The intent operators.
10 - */
11 - public enum Operator {
12 - ADD,
13 - REMOVE,
14 - }
15 -
16 - /**
17 - * Adds an add-intent operation.
18 - *
19 - * @param intent the intent to be added
20 - * @return the IntentBatchOperation object if succeeded, null otherwise
21 - */
22 - public IntentBatchOperation addAddIntentOperation(Intent intent) {
23 - return (null == super.addOperation(
24 - new BatchOperationEntry<Operator, Intent>(Operator.ADD, intent)))
25 - ? null : this;
26 - }
27 -
28 - /**
29 - * Adds a remove-intent operation.
30 - *
31 - * @param id the ID of intent to be removed
32 - * @return the IntentBatchOperation object if succeeded, null otherwise
33 - */
34 - public IntentBatchOperation addRemoveIntentOperation(IntentId id) {
35 - return (null == super.addOperation(
36 - new BatchOperationEntry<Operator, IntentId>(Operator.REMOVE, id)))
37 - ? null : this;
38 - }
39 -}
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import java.util.List; 21 import java.util.List;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import org.onlab.onos.event.AbstractEvent; 21 import org.onlab.onos.event.AbstractEvent;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 /** 21 /**
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import java.util.Map; 21 import java.util.Map;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
21 +import org.onlab.onos.net.flow.BatchOperationTarget;
22 +
3 /** 23 /**
4 * Intent identifier suitable as an external key. 24 * Intent identifier suitable as an external key.
5 * <p/> 25 * <p/>
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import java.util.List; 21 import java.util.List;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import org.onlab.onos.event.EventListener; 21 import org.onlab.onos.event.EventListener;
......
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.net.intent;
20 +
21 +/**
22 + * Abstraction of an intent-related operation, e.g. add, remove, replace.
23 + */
24 +public class IntentOperation {
25 +
26 + private final Type type;
27 + private final IntentId intentId;
28 + private final Intent intent;
29 +
30 + /**
31 + * Operation type.
32 + */
33 + enum Type {
34 + /**
35 + * Indicates that an intent should be added.
36 + */
37 + SUBMIT,
38 +
39 + /**
40 + * Indicates that an intent should be removed.
41 + */
42 + WITHDRAW,
43 +
44 + /**
45 + * Indicates that an intent should be replaced with another.
46 + */
47 + REPLACE
48 + }
49 +
50 + /**
51 + * Creates an intent operation.
52 + *
53 + * @param type operation type
54 + * @param intentId identifier of the intent subject to the operation
55 + * @param intent intent subject
56 + */
57 + IntentOperation(Type type, IntentId intentId, Intent intent) {
58 + this.type = type;
59 + this.intentId = intentId;
60 + this.intent = intent;
61 + }
62 +
63 + /**
64 + * Returns the type of the operation.
65 + *
66 + * @return operation type
67 + */
68 + public Type type() {
69 + return type;
70 + }
71 +
72 + /**
73 + * Returns the identifier of the intent to which this operation applies.
74 + *
75 + * @return intent identifier
76 + */
77 + public IntentId intentId() {
78 + return intentId;
79 + }
80 +
81 + /**
82 + * Returns the intent to which this operation applied. For remove,
83 + * this can be null.
84 + *
85 + * @return intent that is the subject of the operation; null for remove
86 + */
87 + public Intent intent() {
88 + return intent;
89 + }
90 +
91 +}
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
21 +import com.google.common.collect.ImmutableList;
22 +
23 +import java.util.List;
24 +
25 +import static com.google.common.base.Preconditions.checkNotNull;
26 +import static org.onlab.onos.net.intent.IntentOperation.Type.REPLACE;
27 +import static org.onlab.onos.net.intent.IntentOperation.Type.SUBMIT;
28 +import static org.onlab.onos.net.intent.IntentOperation.Type.WITHDRAW;
29 +
3 /** 30 /**
4 - * Abstraction of a batch of intent submit/withdraw operations. 31 + * Batch of intent submit/withdraw/replace operations.
32 + */
33 +public final class IntentOperations {
34 +
35 + private final List<IntentOperation> operations;
36 +
37 + /**
38 + * Creates a batch of intent operations using the supplied list.
39 + *
40 + * @param operations list of intent operations
41 + */
42 + private IntentOperations(List<IntentOperation> operations) {
43 + this.operations = operations;
44 + }
45 +
46 + /**
47 + * List of operations that need to be executed as a unit.
48 + *
49 + * @return list of intent operations
50 + */
51 + public List<IntentOperation> operations() {
52 + return operations;
53 + }
54 +
55 + /**
56 + * Returns a builder for intent operation batches.
57 + *
58 + * @return intent operations builder
5 */ 59 */
6 -public interface IntentOperations { 60 + public static Builder builder() {
61 + return new Builder();
62 + }
7 63
8 - // TODO: elaborate once the revised BatchOperation scheme is in place 64 + /**
65 + * Builder for batches of intent operations.
66 + */
67 + public static final class Builder {
68 +
69 + private final ImmutableList.Builder<IntentOperation> builder = ImmutableList.builder();
70 +
71 + // Public construction is forbidden.
72 + private Builder() {
73 + }
74 +
75 + /**
76 + * Adds an intent submit operation.
77 + *
78 + * @param intent intent to be submitted
79 + * @return self
80 + */
81 + public Builder addSubmitOperation(Intent intent) {
82 + checkNotNull(intent, "Intent cannot be null");
83 + builder.add(new IntentOperation(SUBMIT, intent.id(), intent));
84 + return this;
85 + }
86 +
87 + /**
88 + * Adds an intent submit operation.
89 + *
90 + * @param oldIntentId intent to be replaced
91 + * @param newIntent replacement intent
92 + * @return self
93 + */
94 + public Builder addReplaceOperation(IntentId oldIntentId, Intent newIntent) {
95 + checkNotNull(oldIntentId, "Intent ID cannot be null");
96 + checkNotNull(newIntent, "Intent cannot be null");
97 + builder.add(new IntentOperation(REPLACE, oldIntentId, newIntent));
98 + return this;
99 + }
100 +
101 + /**
102 + * Adds an intent submit operation.
103 + *
104 + * @param intentId identifier of the intent to be withdrawn
105 + * @return self
106 + */
107 + public Builder addWithdrawOperation(IntentId intentId) {
108 + checkNotNull(intentId, "Intent ID cannot be null");
109 + builder.add(new IntentOperation(WITHDRAW, intentId, null));
110 + return this;
111 + }
112 +
113 + /**
114 + * Builds a batch of intent operations.
115 + *
116 + * @return immutable batch of intent operations
117 + */
118 + public IntentOperations build() {
119 + return new IntentOperations(builder.build());
120 + }
9 121
122 + }
10 } 123 }
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 21
4 import java.util.List; 22 import java.util.List;
23 +import java.util.concurrent.Future;
5 24
6 /** 25 /**
7 * Service for application submitting or withdrawing their intents. 26 * Service for application submitting or withdrawing their intents.
...@@ -28,6 +47,14 @@ public interface IntentService { ...@@ -28,6 +47,14 @@ public interface IntentService {
28 void withdraw(Intent intent); 47 void withdraw(Intent intent);
29 48
30 /** 49 /**
50 + * Replaces the specified intent with a new one.
51 + *
52 + * @param oldIntentId identifier of the old intent being replaced
53 + * @param newIntent new intent replacing the old one
54 + */
55 + void replace(IntentId oldIntentId, Intent newIntent);
56 +
57 + /**
31 * Submits a batch of submit &amp; withdraw operations. Such a batch is 58 * Submits a batch of submit &amp; withdraw operations. Such a batch is
32 * assumed to be processed together. 59 * assumed to be processed together.
33 * <p/> 60 * <p/>
...@@ -36,7 +63,7 @@ public interface IntentService { ...@@ -36,7 +63,7 @@ public interface IntentService {
36 * 63 *
37 * @param operations batch of intent operations 64 * @param operations batch of intent operations
38 */ 65 */
39 - void execute(IntentOperations operations); 66 + Future<IntentOperations> execute(IntentOperations operations);
40 67
41 /** 68 /**
42 * Returns an iterable of intents currently in the system. 69 * Returns an iterable of intents currently in the system.
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 /** 21 /**
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import org.onlab.onos.store.Store; 21 import org.onlab.onos.store.Store;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import org.onlab.onos.store.StoreDelegate; 21 import org.onlab.onos.store.StoreDelegate;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 + */
1 package org.onlab.onos.net.intent; 19 package org.onlab.onos.net.intent;
2 20
3 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
......
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 +
1 /** 20 /**
2 * Set of abstractions for conveying high-level intents for treatment of 21 * Set of abstractions for conveying high-level intents for treatment of
3 * selected network traffic by allowing applications to express the 22 * selected network traffic by allowing applications to express the
......
1 +package org.onlab.onos.net.statistic;
2 +
3 +import com.google.common.base.MoreObjects;
4 +import org.onlab.onos.net.flow.FlowRuleProvider;
5 +
6 +/**
7 + * Implementation of a load.
8 + */
9 +public class DefaultLoad implements Load {
10 +
11 + private final boolean isValid;
12 + private final long current;
13 + private final long previous;
14 + private final long time;
15 +
16 + /**
17 + * Creates an invalid load.
18 + */
19 + public DefaultLoad() {
20 + this.isValid = false;
21 + this.time = System.currentTimeMillis();
22 + this.current = -1;
23 + this.previous = -1;
24 + }
25 +
26 + /**
27 + * Creates a load value from the parameters.
28 + * @param current the current value
29 + * @param previous the previous value
30 + */
31 + public DefaultLoad(long current, long previous) {
32 + this.current = current;
33 + this.previous = previous;
34 + this.time = System.currentTimeMillis();
35 + this.isValid = true;
36 + }
37 +
38 + @Override
39 + public long rate() {
40 + return (current - previous) / FlowRuleProvider.POLL_INTERVAL;
41 + }
42 +
43 + @Override
44 + public long latest() {
45 + return current;
46 + }
47 +
48 + @Override
49 + public boolean isValid() {
50 + return isValid;
51 + }
52 +
53 + @Override
54 + public long time() {
55 + return time;
56 + }
57 +
58 + @Override
59 + public String toString() {
60 + return MoreObjects.toStringHelper("Load").add("rate", rate())
61 + .add("latest", latest()).toString();
62 +
63 + }
64 +}
...@@ -6,15 +6,27 @@ package org.onlab.onos.net.statistic; ...@@ -6,15 +6,27 @@ package org.onlab.onos.net.statistic;
6 public interface Load { 6 public interface Load {
7 7
8 /** 8 /**
9 - * Obtain the current observed rate on a link. 9 + * Obtain the current observed rate (in bytes/s) on a link.
10 * @return long value 10 * @return long value
11 */ 11 */
12 long rate(); 12 long rate();
13 13
14 /** 14 /**
15 - * Obtain the latest counter viewed on that link. 15 + * Obtain the latest bytes counter viewed on that link.
16 * @return long value 16 * @return long value
17 */ 17 */
18 long latest(); 18 long latest();
19 19
20 + /**
21 + * Indicates whether this load was built on valid values.
22 + * @return boolean
23 + */
24 + boolean isValid();
25 +
26 + /**
27 + * Returns when this value was seen.
28 + * @return epoch time
29 + */
30 + long time();
31 +
20 } 32 }
......
...@@ -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 }
......
1 package org.onlab.onos.store.cluster.messaging; 1 package org.onlab.onos.store.cluster.messaging;
2 2
3 +import java.util.concurrent.Future;
3 import java.util.concurrent.TimeUnit; 4 import java.util.concurrent.TimeUnit;
4 import java.util.concurrent.TimeoutException; 5 import java.util.concurrent.TimeoutException;
5 6
6 import org.onlab.onos.cluster.NodeId; 7 import org.onlab.onos.cluster.NodeId;
7 8
8 -public interface ClusterMessageResponse { 9 +public interface ClusterMessageResponse extends Future<byte[]> {
10 +
9 public NodeId sender(); 11 public NodeId sender();
10 - public byte[] get(long timeout, TimeUnit timeunit) throws TimeoutException; 12 +
11 - public byte[] get(long timeout) throws InterruptedException; 13 + // TODO InterruptedException, ExecutionException removed from original
14 + // Future declaration. Revisit if we ever need those.
15 + @Override
16 + public byte[] get(long timeout, TimeUnit unit) throws TimeoutException;
17 +
12 } 18 }
......
...@@ -9,6 +9,7 @@ import java.util.Map; ...@@ -9,6 +9,7 @@ import java.util.Map;
9 import java.util.Set; 9 import java.util.Set;
10 import java.util.concurrent.ExecutorService; 10 import java.util.concurrent.ExecutorService;
11 import java.util.concurrent.Executors; 11 import java.util.concurrent.Executors;
12 +import java.util.concurrent.Future;
12 13
13 /** 14 /**
14 * Fake implementation of the intent service to assist in developing tests of 15 * Fake implementation of the intent service to assist in developing tests of
...@@ -171,11 +172,17 @@ public class FakeIntentManager implements TestableIntentService { ...@@ -171,11 +172,17 @@ public class FakeIntentManager implements TestableIntentService {
171 } 172 }
172 173
173 @Override 174 @Override
174 - public void execute(IntentOperations operations) { 175 + public void replace(IntentId oldIntentId, Intent newIntent) {
175 // TODO: implement later 176 // TODO: implement later
176 } 177 }
177 178
178 @Override 179 @Override
180 + public Future<IntentOperations> execute(IntentOperations operations) {
181 + // TODO: implement later
182 + return null;
183 + }
184 +
185 + @Override
179 public Set<Intent> getIntents() { 186 public Set<Intent> getIntents() {
180 return Collections.unmodifiableSet(new HashSet<>(intents.values())); 187 return Collections.unmodifiableSet(new HashSet<>(intents.values()));
181 } 188 }
......
...@@ -227,10 +227,14 @@ implements MastershipService, MastershipAdminService { ...@@ -227,10 +227,14 @@ implements MastershipService, MastershipAdminService {
227 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) { 227 if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) {
228 return true; 228 return true;
229 } 229 }
230 - //else { 230 +// else {
231 //FIXME: break tie for equal-sized clusters, by number of 231 //FIXME: break tie for equal-sized clusters, by number of
232 // connected switches, then masters, then nodeId hash 232 // connected switches, then masters, then nodeId hash
233 - // } 233 + // problem is, how do we get at channel info cleanly here?
234 + // Also, what's the time hit for a distributed store look-up
235 + // versus channel re-negotiation? bet on the latter being worse.
236 +
237 +// }
234 return false; 238 return false;
235 } 239 }
236 240
......
...@@ -161,6 +161,17 @@ public class DeviceManager ...@@ -161,6 +161,17 @@ public class DeviceManager
161 } 161 }
162 } 162 }
163 163
164 + // Queries a device for port information.
165 + private void queryPortInfo(DeviceId deviceId) {
166 + Device device = store.getDevice(deviceId);
167 + // FIXME: Device might not be there yet. (eventual consistent)
168 + if (device == null) {
169 + return;
170 + }
171 + DeviceProvider provider = getProvider(device.providerId());
172 + provider.triggerProbe(device);
173 + }
174 +
164 @Override 175 @Override
165 public void removeDevice(DeviceId deviceId) { 176 public void removeDevice(DeviceId deviceId) {
166 checkNotNull(deviceId, DEVICE_ID_NULL); 177 checkNotNull(deviceId, DEVICE_ID_NULL);
...@@ -210,8 +221,6 @@ public class DeviceManager ...@@ -210,8 +221,6 @@ public class DeviceManager
210 log.info("Device {} connected", deviceId); 221 log.info("Device {} connected", deviceId);
211 // check my Role 222 // check my Role
212 MastershipRole role = mastershipService.requestRoleFor(deviceId); 223 MastershipRole role = mastershipService.requestRoleFor(deviceId);
213 - log.info("## - our role for {} is {} [master is {}]", deviceId, role,
214 - mastershipService.getMasterFor(deviceId));
215 if (role != MastershipRole.MASTER) { 224 if (role != MastershipRole.MASTER) {
216 // TODO: Do we need to explicitly tell the Provider that 225 // TODO: Do we need to explicitly tell the Provider that
217 // this instance is no longer the MASTER? probably not 226 // this instance is no longer the MASTER? probably not
...@@ -265,7 +274,6 @@ public class DeviceManager ...@@ -265,7 +274,6 @@ public class DeviceManager
265 // but if I was the last STANDBY connection, etc. and no one else 274 // but if I was the last STANDBY connection, etc. and no one else
266 // was there to mark the device offline, this instance may need to 275 // was there to mark the device offline, this instance may need to
267 // temporarily request for Master Role and mark offline. 276 // temporarily request for Master Role and mark offline.
268 - log.info("## for {} role is {}", deviceId, mastershipService.getLocalRole(deviceId));
269 if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) { 277 if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
270 log.debug("Device {} disconnected, but I am not the master", deviceId); 278 log.debug("Device {} disconnected, but I am not the master", deviceId);
271 //let go of ability to be backup 279 //let go of ability to be backup
...@@ -373,7 +381,6 @@ public class DeviceManager ...@@ -373,7 +381,6 @@ public class DeviceManager
373 final DeviceId did = event.subject(); 381 final DeviceId did = event.subject();
374 final NodeId myNodeId = clusterService.getLocalNode().id(); 382 final NodeId myNodeId = clusterService.getLocalNode().id();
375 383
376 - log.info("## got Mastershipevent for dev {}", did);
377 if (myNodeId.equals(event.roleInfo().master())) { 384 if (myNodeId.equals(event.roleInfo().master())) {
378 MastershipTerm term = termService.getMastershipTerm(did); 385 MastershipTerm term = termService.getMastershipTerm(did);
379 386
...@@ -384,7 +391,6 @@ public class DeviceManager ...@@ -384,7 +391,6 @@ public class DeviceManager
384 return; 391 return;
385 } 392 }
386 393
387 - log.info("## setting term for CPS as new master for {}", did);
388 // only set the new term if I am the master 394 // only set the new term if I am the master
389 deviceClockProviderService.setMastershipTerm(did, term); 395 deviceClockProviderService.setMastershipTerm(did, term);
390 396
...@@ -404,6 +410,7 @@ public class DeviceManager ...@@ -404,6 +410,7 @@ public class DeviceManager
404 device.serialNumber(), device.chassisId())); 410 device.serialNumber(), device.chassisId()));
405 } 411 }
406 //TODO re-collect device information to fix potential staleness 412 //TODO re-collect device information to fix potential staleness
413 + queryPortInfo(did);
407 applyRole(did, MastershipRole.MASTER); 414 applyRole(did, MastershipRole.MASTER);
408 } else if (event.roleInfo().backups().contains(myNodeId)) { 415 } else if (event.roleInfo().backups().contains(myNodeId)) {
409 applyRole(did, MastershipRole.STANDBY); 416 applyRole(did, MastershipRole.STANDBY);
......
...@@ -286,7 +286,8 @@ public class FlowRuleManager ...@@ -286,7 +286,8 @@ public class FlowRuleManager
286 private void extraneousFlow(FlowRule flowRule) { 286 private void extraneousFlow(FlowRule flowRule) {
287 checkNotNull(flowRule, FLOW_RULE_NULL); 287 checkNotNull(flowRule, FLOW_RULE_NULL);
288 checkValidity(); 288 checkValidity();
289 - removeFlowRules(flowRule); 289 + FlowRuleProvider frp = getProvider(flowRule.deviceId());
290 + frp.removeFlowRule(flowRule);
290 log.debug("Flow {} is on switch but not in store.", flowRule); 291 log.debug("Flow {} is on switch but not in store.", flowRule);
291 } 292 }
292 293
...@@ -380,14 +381,14 @@ public class FlowRuleManager ...@@ -380,14 +381,14 @@ public class FlowRuleManager
380 final FlowRuleBatchRequest request = event.subject(); 381 final FlowRuleBatchRequest request = event.subject();
381 switch (event.type()) { 382 switch (event.type()) {
382 case BATCH_OPERATION_REQUESTED: 383 case BATCH_OPERATION_REQUESTED:
383 -// for (FlowEntry entry : request.toAdd()) { 384 + for (FlowEntry entry : request.toAdd()) {
384 -// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry)); 385 + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry));
385 -// } 386 + }
386 -// for (FlowEntry entry : request.toRemove()) { 387 + for (FlowEntry entry : request.toRemove()) {
387 -// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry)); 388 + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry));
388 -// } 389 + }
389 -// // FIXME: what about op.equals(FlowRuleOperation.MODIFY) ? 390 + // FIXME: what about op.equals(FlowRuleOperation.MODIFY) ?
390 -// 391 +
391 FlowRuleBatchOperation batchOperation = request.asBatchOperation(); 392 FlowRuleBatchOperation batchOperation = request.asBatchOperation();
392 393
393 FlowRuleProvider flowRuleProvider = 394 FlowRuleProvider flowRuleProvider =
......
...@@ -126,7 +126,13 @@ public class IntentManager ...@@ -126,7 +126,13 @@ public class IntentManager
126 126
127 // FIXME: implement this method 127 // FIXME: implement this method
128 @Override 128 @Override
129 - public void execute(IntentOperations operations) { 129 + public void replace(IntentId oldIntentId, Intent newIntent) {
130 + throw new UnsupportedOperationException("execute() is not implemented yet");
131 + }
132 +
133 + // FIXME: implement this method
134 + @Override
135 + public Future<IntentOperations> execute(IntentOperations operations) {
130 throw new UnsupportedOperationException("execute() is not implemented yet"); 136 throw new UnsupportedOperationException("execute() is not implemented yet");
131 } 137 }
132 138
......
...@@ -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 }
......
...@@ -68,8 +68,10 @@ implements PacketService, PacketProviderRegistry { ...@@ -68,8 +68,10 @@ implements PacketService, PacketProviderRegistry {
68 checkNotNull(packet, "Packet cannot be null"); 68 checkNotNull(packet, "Packet cannot be null");
69 final Device device = deviceService.getDevice(packet.sendThrough()); 69 final Device device = deviceService.getDevice(packet.sendThrough());
70 final PacketProvider packetProvider = getProvider(device.providerId()); 70 final PacketProvider packetProvider = getProvider(device.providerId());
71 + if (packetProvider != null) {
71 packetProvider.emit(packet); 72 packetProvider.emit(packet);
72 } 73 }
74 + }
73 75
74 @Override 76 @Override
75 protected PacketProviderService createProviderService(PacketProvider provider) { 77 protected PacketProviderService createProviderService(PacketProvider provider) {
......
...@@ -9,14 +9,18 @@ import org.apache.felix.scr.annotations.Service; ...@@ -9,14 +9,18 @@ import org.apache.felix.scr.annotations.Service;
9 import org.onlab.onos.net.ConnectPoint; 9 import org.onlab.onos.net.ConnectPoint;
10 import org.onlab.onos.net.Link; 10 import org.onlab.onos.net.Link;
11 import org.onlab.onos.net.Path; 11 import org.onlab.onos.net.Path;
12 +
13 +import org.onlab.onos.net.flow.FlowEntry;
12 import org.onlab.onos.net.flow.FlowRule; 14 import org.onlab.onos.net.flow.FlowRule;
13 import org.onlab.onos.net.flow.FlowRuleEvent; 15 import org.onlab.onos.net.flow.FlowRuleEvent;
14 import org.onlab.onos.net.flow.FlowRuleListener; 16 import org.onlab.onos.net.flow.FlowRuleListener;
15 import org.onlab.onos.net.flow.FlowRuleService; 17 import org.onlab.onos.net.flow.FlowRuleService;
18 +import org.onlab.onos.net.statistic.DefaultLoad;
16 import org.onlab.onos.net.statistic.Load; 19 import org.onlab.onos.net.statistic.Load;
17 import org.onlab.onos.net.statistic.StatisticService; 20 import org.onlab.onos.net.statistic.StatisticService;
18 import org.onlab.onos.net.statistic.StatisticStore; 21 import org.onlab.onos.net.statistic.StatisticStore;
19 import org.slf4j.Logger; 22 import org.slf4j.Logger;
23 +import java.util.Set;
20 24
21 import static org.slf4j.LoggerFactory.getLogger; 25 import static org.slf4j.LoggerFactory.getLogger;
22 26
...@@ -35,12 +39,14 @@ public class StatisticManager implements StatisticService { ...@@ -35,12 +39,14 @@ public class StatisticManager implements StatisticService {
35 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 39 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
36 protected StatisticStore statisticStore; 40 protected StatisticStore statisticStore;
37 41
42 +
38 private final InternalFlowRuleListener listener = new InternalFlowRuleListener(); 43 private final InternalFlowRuleListener listener = new InternalFlowRuleListener();
39 44
40 @Activate 45 @Activate
41 public void activate() { 46 public void activate() {
42 flowRuleService.addListener(listener); 47 flowRuleService.addListener(listener);
43 log.info("Started"); 48 log.info("Started");
49 +
44 } 50 }
45 51
46 @Deactivate 52 @Deactivate
...@@ -51,29 +57,93 @@ public class StatisticManager implements StatisticService { ...@@ -51,29 +57,93 @@ public class StatisticManager implements StatisticService {
51 57
52 @Override 58 @Override
53 public Load load(Link link) { 59 public Load load(Link link) {
54 - return null; 60 + return load(link.src());
55 } 61 }
56 62
57 @Override 63 @Override
58 public Load load(ConnectPoint connectPoint) { 64 public Load load(ConnectPoint connectPoint) {
59 - return null; 65 + return loadInternal(connectPoint);
60 } 66 }
61 67
62 @Override 68 @Override
63 public Link max(Path path) { 69 public Link max(Path path) {
70 + if (path.links().isEmpty()) {
64 return null; 71 return null;
65 } 72 }
73 + Load maxLoad = new DefaultLoad();
74 + Link maxLink = null;
75 + for (Link link : path.links()) {
76 + Load load = loadInternal(link.src());
77 + if (load.rate() > maxLoad.rate()) {
78 + maxLoad = load;
79 + maxLink = link;
80 + }
81 + }
82 + return maxLink;
83 + }
66 84
67 @Override 85 @Override
68 public Link min(Path path) { 86 public Link min(Path path) {
87 + if (path.links().isEmpty()) {
69 return null; 88 return null;
70 } 89 }
90 + Load minLoad = new DefaultLoad();
91 + Link minLink = null;
92 + for (Link link : path.links()) {
93 + Load load = loadInternal(link.src());
94 + if (load.rate() < minLoad.rate()) {
95 + minLoad = load;
96 + minLink = link;
97 + }
98 + }
99 + return minLink;
100 + }
71 101
72 @Override 102 @Override
73 public FlowRule highestHitter(ConnectPoint connectPoint) { 103 public FlowRule highestHitter(ConnectPoint connectPoint) {
104 + Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint);
105 + if (hitters.isEmpty()) {
74 return null; 106 return null;
75 } 107 }
76 108
109 + FlowEntry max = hitters.iterator().next();
110 + for (FlowEntry entry : hitters) {
111 + if (entry.bytes() > max.bytes()) {
112 + max = entry;
113 + }
114 + }
115 + return max;
116 + }
117 +
118 + private Load loadInternal(ConnectPoint connectPoint) {
119 + Set<FlowEntry> current;
120 + Set<FlowEntry> previous;
121 + synchronized (statisticStore) {
122 + current = statisticStore.getCurrentStatistic(connectPoint);
123 + previous = statisticStore.getPreviousStatistic(connectPoint);
124 + }
125 + if (current == null || previous == null) {
126 + return new DefaultLoad();
127 + }
128 + long currentAggregate = aggregate(current);
129 + long previousAggregate = aggregate(previous);
130 +
131 + return new DefaultLoad(currentAggregate, previousAggregate);
132 + }
133 +
134 + /**
135 + * Aggregates a set of values.
136 + * @param values the values to aggregate
137 + * @return a long value
138 + */
139 + private long aggregate(Set<FlowEntry> values) {
140 + long sum = 0;
141 + for (FlowEntry f : values) {
142 + sum += f.bytes();
143 + }
144 + return sum;
145 + }
146 +
77 /** 147 /**
78 * Internal flow rule event listener. 148 * Internal flow rule event listener.
79 */ 149 */
...@@ -81,7 +151,29 @@ public class StatisticManager implements StatisticService { ...@@ -81,7 +151,29 @@ public class StatisticManager implements StatisticService {
81 151
82 @Override 152 @Override
83 public void event(FlowRuleEvent event) { 153 public void event(FlowRuleEvent event) {
84 - 154 + FlowRule rule = event.subject();
155 + switch (event.type()) {
156 + case RULE_ADDED:
157 + case RULE_UPDATED:
158 + if (rule instanceof FlowEntry) {
159 + statisticStore.addOrUpdateStatistic((FlowEntry) rule);
160 + } else {
161 + log.warn("IT AIN'T A FLOWENTRY");
162 + }
163 + break;
164 + case RULE_ADD_REQUESTED:
165 + log.info("Preparing for stats");
166 + statisticStore.prepareForStatistics(rule);
167 + break;
168 + case RULE_REMOVE_REQUESTED:
169 + log.info("Removing stats");
170 + statisticStore.removeFromStatistics(rule);
171 + break;
172 + case RULE_REMOVED:
173 + break;
174 + default:
175 + log.warn("Unknown flow rule event {}", event);
176 + }
85 } 177 }
86 } 178 }
87 179
......
...@@ -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() {
187 + try {
180 buildTopology(reasons); 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
......
...@@ -15,6 +15,7 @@ public class TestEventDispatcher extends DefaultEventSinkRegistry ...@@ -15,6 +15,7 @@ public class TestEventDispatcher extends DefaultEventSinkRegistry
15 implements EventDeliveryService { 15 implements EventDeliveryService {
16 16
17 @Override 17 @Override
18 + @SuppressWarnings("unchecked")
18 public void post(Event event) { 19 public void post(Event event) {
19 EventSink sink = getSink(event.getClass()); 20 EventSink sink = getSink(event.getClass());
20 checkState(sink != null, "No sink for event %s", event); 21 checkState(sink != null, "No sink for event %s", event);
......
...@@ -9,6 +9,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; ...@@ -9,6 +9,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED;
9 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 9 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
10 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED; 10 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED;
11 11
12 +
13 +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*;
14 +
12 import java.util.ArrayList; 15 import java.util.ArrayList;
13 import java.util.Collections; 16 import java.util.Collections;
14 import java.util.HashMap; 17 import java.util.HashMap;
...@@ -55,7 +58,7 @@ import org.onlab.onos.net.flow.TrafficSelector; ...@@ -55,7 +58,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
55 import org.onlab.onos.net.flow.TrafficTreatment; 58 import org.onlab.onos.net.flow.TrafficTreatment;
56 import org.onlab.onos.net.flow.criteria.Criterion; 59 import org.onlab.onos.net.flow.criteria.Criterion;
57 import org.onlab.onos.net.flow.instructions.Instruction; 60 import org.onlab.onos.net.flow.instructions.Instruction;
58 -import org.onlab.onos.net.intent.BatchOperation; 61 +import org.onlab.onos.net.flow.BatchOperation;
59 import org.onlab.onos.net.provider.AbstractProvider; 62 import org.onlab.onos.net.provider.AbstractProvider;
60 import org.onlab.onos.net.provider.ProviderId; 63 import org.onlab.onos.net.provider.ProviderId;
61 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; 64 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
...@@ -165,7 +168,8 @@ public class FlowRuleManagerTest { ...@@ -165,7 +168,8 @@ public class FlowRuleManagerTest {
165 assertEquals("2 rules should exist", 2, flowCount()); 168 assertEquals("2 rules should exist", 2, flowCount());
166 169
167 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); 170 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
168 - validateEvents(RULE_ADDED, RULE_ADDED); 171 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
172 + RULE_ADDED, RULE_ADDED);
169 173
170 addFlowRule(1); 174 addFlowRule(1);
171 assertEquals("should still be 2 rules", 2, flowCount()); 175 assertEquals("should still be 2 rules", 2, flowCount());
...@@ -218,11 +222,12 @@ public class FlowRuleManagerTest { ...@@ -218,11 +222,12 @@ public class FlowRuleManagerTest {
218 FlowEntry fe2 = new DefaultFlowEntry(f2); 222 FlowEntry fe2 = new DefaultFlowEntry(f2);
219 FlowEntry fe3 = new DefaultFlowEntry(f3); 223 FlowEntry fe3 = new DefaultFlowEntry(f3);
220 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); 224 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3));
221 - validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); 225 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
226 + RULE_ADDED, RULE_ADDED, RULE_ADDED);
222 227
223 mgr.removeFlowRules(f1, f2); 228 mgr.removeFlowRules(f1, f2);
224 //removing from north, so no events generated 229 //removing from north, so no events generated
225 - validateEvents(); 230 + validateEvents(RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED);
226 assertEquals("3 rule should exist", 3, flowCount()); 231 assertEquals("3 rule should exist", 3, flowCount());
227 assertTrue("Entries should be pending remove.", 232 assertTrue("Entries should be pending remove.",
228 validateState(ImmutableMap.of( 233 validateState(ImmutableMap.of(
...@@ -244,7 +249,8 @@ public class FlowRuleManagerTest { ...@@ -244,7 +249,8 @@ public class FlowRuleManagerTest {
244 service.removeFlowRules(f1); 249 service.removeFlowRules(f1);
245 fe1.setState(FlowEntryState.REMOVED); 250 fe1.setState(FlowEntryState.REMOVED);
246 providerService.flowRemoved(fe1); 251 providerService.flowRemoved(fe1);
247 - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); 252 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED,
253 + RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED);
248 254
249 providerService.flowRemoved(fe1); 255 providerService.flowRemoved(fe1);
250 validateEvents(); 256 validateEvents();
...@@ -253,7 +259,7 @@ public class FlowRuleManagerTest { ...@@ -253,7 +259,7 @@ public class FlowRuleManagerTest {
253 FlowEntry fe3 = new DefaultFlowEntry(f3); 259 FlowEntry fe3 = new DefaultFlowEntry(f3);
254 service.applyFlowRules(f3); 260 service.applyFlowRules(f3);
255 providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); 261 providerService.pushFlowMetrics(DID, Collections.singletonList(fe3));
256 - validateEvents(RULE_ADDED); 262 + validateEvents(RULE_ADD_REQUESTED, RULE_ADDED);
257 263
258 providerService.flowRemoved(fe3); 264 providerService.flowRemoved(fe3);
259 validateEvents(); 265 validateEvents();
...@@ -282,7 +288,8 @@ public class FlowRuleManagerTest { ...@@ -282,7 +288,8 @@ public class FlowRuleManagerTest {
282 f2, FlowEntryState.ADDED, 288 f2, FlowEntryState.ADDED,
283 f3, FlowEntryState.PENDING_ADD))); 289 f3, FlowEntryState.PENDING_ADD)));
284 290
285 - validateEvents(RULE_ADDED, RULE_ADDED); 291 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
292 + RULE_ADDED, RULE_ADDED);
286 } 293 }
287 294
288 @Test 295 @Test
...@@ -302,7 +309,7 @@ public class FlowRuleManagerTest { ...@@ -302,7 +309,7 @@ public class FlowRuleManagerTest {
302 309
303 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); 310 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3));
304 311
305 - validateEvents(RULE_ADDED, RULE_ADDED); 312 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, RULE_ADDED);
306 313
307 } 314 }
308 315
...@@ -327,7 +334,8 @@ public class FlowRuleManagerTest { ...@@ -327,7 +334,8 @@ public class FlowRuleManagerTest {
327 334
328 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); 335 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
329 336
330 - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); 337 + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED,
338 + RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED);
331 339
332 } 340 }
333 341
......
...@@ -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 {
......
...@@ -4,9 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument; ...@@ -4,9 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument;
4 4
5 import java.io.IOException; 5 import java.io.IOException;
6 import java.util.Set; 6 import java.util.Set;
7 +import java.util.concurrent.ExecutionException;
7 import java.util.concurrent.TimeUnit; 8 import java.util.concurrent.TimeUnit;
8 import java.util.concurrent.TimeoutException; 9 import java.util.concurrent.TimeoutException;
9 -
10 import org.apache.felix.scr.annotations.Activate; 10 import org.apache.felix.scr.annotations.Activate;
11 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
12 import org.apache.felix.scr.annotations.Deactivate; 12 import org.apache.felix.scr.annotations.Deactivate;
...@@ -181,10 +181,13 @@ public class ClusterCommunicationManager ...@@ -181,10 +181,13 @@ public class ClusterCommunicationManager
181 } 181 }
182 } 182 }
183 183
184 - private static final class InternalClusterMessageResponse implements ClusterMessageResponse { 184 + private static final class InternalClusterMessageResponse
185 + implements ClusterMessageResponse {
185 186
186 private final NodeId sender; 187 private final NodeId sender;
187 private final Response responseFuture; 188 private final Response responseFuture;
189 + private volatile boolean isCancelled = false;
190 + private volatile boolean isDone = false;
188 191
189 public InternalClusterMessageResponse(NodeId sender, Response responseFuture) { 192 public InternalClusterMessageResponse(NodeId sender, Response responseFuture) {
190 this.sender = sender; 193 this.sender = sender;
...@@ -198,12 +201,39 @@ public class ClusterCommunicationManager ...@@ -198,12 +201,39 @@ public class ClusterCommunicationManager
198 @Override 201 @Override
199 public byte[] get(long timeout, TimeUnit timeunit) 202 public byte[] get(long timeout, TimeUnit timeunit)
200 throws TimeoutException { 203 throws TimeoutException {
201 - return responseFuture.get(timeout, timeunit); 204 + final byte[] result = responseFuture.get(timeout, timeunit);
205 + isDone = true;
206 + return result;
207 + }
208 +
209 + @Override
210 + public boolean cancel(boolean mayInterruptIfRunning) {
211 + if (isDone()) {
212 + return false;
213 + }
214 + // doing nothing for now
215 + // when onlab.netty Response support cancel, call them.
216 + isCancelled = true;
217 + return true;
218 + }
219 +
220 + @Override
221 + public boolean isCancelled() {
222 + return isCancelled;
223 + }
224 +
225 + @Override
226 + public boolean isDone() {
227 + return this.isDone || isCancelled();
202 } 228 }
203 229
204 @Override 230 @Override
205 - public byte[] get(long timeout) throws InterruptedException { 231 + public byte[] get() throws InterruptedException, ExecutionException {
206 - return responseFuture.get(); 232 + // TODO: consider forbidding this call and force the use of timed get
233 + // to enforce handling of remote peer failure scenario
234 + final byte[] result = responseFuture.get();
235 + isDone = true;
236 + return result;
207 } 237 }
208 } 238 }
209 } 239 }
......
...@@ -290,12 +290,17 @@ public class GossipDeviceStore ...@@ -290,12 +290,17 @@ public class GossipDeviceStore
290 private DeviceEvent updateDevice(ProviderId providerId, 290 private DeviceEvent updateDevice(ProviderId providerId,
291 Device oldDevice, 291 Device oldDevice,
292 Device newDevice, Timestamp newTimestamp) { 292 Device newDevice, Timestamp newTimestamp) {
293 -
294 // We allow only certain attributes to trigger update 293 // We allow only certain attributes to trigger update
295 - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 294 + boolean propertiesChanged =
296 - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 295 + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
297 - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { 296 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
298 - 297 + boolean annotationsChanged =
298 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
299 +
300 + // Primary providers can respond to all changes, but ancillary ones
301 + // should respond only to annotation changes.
302 + if ((providerId.isAncillary() && annotationsChanged) ||
303 + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
299 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); 304 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
300 if (!replaced) { 305 if (!replaced) {
301 verify(replaced, 306 verify(replaced,
......
...@@ -35,6 +35,8 @@ public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEven ...@@ -35,6 +35,8 @@ public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEven
35 Class<InternalDeviceEvent> type) { 35 Class<InternalDeviceEvent> type) {
36 ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); 36 ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
37 DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); 37 DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
38 +
39 + @SuppressWarnings("unchecked")
38 Timestamped<DeviceDescription> deviceDescription 40 Timestamped<DeviceDescription> deviceDescription
39 = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input); 41 = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input);
40 42
......
...@@ -37,6 +37,8 @@ public class InternalPortEventSerializer extends Serializer<InternalPortEvent> { ...@@ -37,6 +37,8 @@ public class InternalPortEventSerializer extends Serializer<InternalPortEvent> {
37 Class<InternalPortEvent> type) { 37 Class<InternalPortEvent> type) {
38 ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); 38 ProviderId providerId = (ProviderId) kryo.readClassAndObject(input);
39 DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); 39 DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
40 +
41 + @SuppressWarnings("unchecked")
40 Timestamped<List<PortDescription>> portDescriptions 42 Timestamped<List<PortDescription>> portDescriptions
41 = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input); 43 = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input);
42 44
......
...@@ -86,7 +86,7 @@ public class ReplicaInfoManager implements ReplicaInfoService { ...@@ -86,7 +86,7 @@ public class ReplicaInfoManager implements ReplicaInfoService {
86 final List<NodeId> standbyList = Collections.<NodeId>emptyList(); 86 final List<NodeId> standbyList = Collections.<NodeId>emptyList();
87 eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED, 87 eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED,
88 event.subject(), 88 event.subject(),
89 - new ReplicaInfo(event.node(), standbyList))); 89 + new ReplicaInfo(event.roleInfo().master(), standbyList)));
90 } 90 }
91 } 91 }
92 92
......
...@@ -82,12 +82,28 @@ public class DistributedIntentStore ...@@ -82,12 +82,28 @@ public class DistributedIntentStore
82 public IntentEvent setState(Intent intent, IntentState state) { 82 public IntentEvent setState(Intent intent, IntentState state) {
83 IntentId id = intent.id(); 83 IntentId id = intent.id();
84 states.put(id, state); 84 states.put(id, state);
85 - IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED : 85 + IntentEvent.Type type = null;
86 - (state == INSTALLED ? IntentEvent.Type.INSTALLED : 86 +
87 - (state == FAILED ? IntentEvent.Type.FAILED : 87 + switch (state) {
88 - state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN : 88 + case SUBMITTED:
89 - null))); 89 + type = IntentEvent.Type.SUBMITTED;
90 - return type == null ? null : new IntentEvent(type, intent); 90 + break;
91 + case INSTALLED:
92 + type = IntentEvent.Type.INSTALLED;
93 + break;
94 + case FAILED:
95 + type = IntentEvent.Type.FAILED;
96 + break;
97 + case WITHDRAWN:
98 + type = IntentEvent.Type.WITHDRAWN;
99 + break;
100 + default:
101 + break;
102 + }
103 + if (type == null) {
104 + return null;
105 + }
106 + return new IntentEvent(type, intent);
91 } 107 }
92 108
93 @Override 109 @Override
......
1 +package org.onlab.onos.store.statistic.impl;
2 +
3 +import static org.onlab.onos.store.statistic.impl.StatisticStoreMessageSubjects.*;
4 +import static org.slf4j.LoggerFactory.getLogger;
5 +
6 +import com.google.common.collect.Sets;
7 +import org.apache.felix.scr.annotations.Activate;
8 +import org.apache.felix.scr.annotations.Component;
9 +import org.apache.felix.scr.annotations.Deactivate;
10 +import org.apache.felix.scr.annotations.Reference;
11 +import org.apache.felix.scr.annotations.ReferenceCardinality;
12 +import org.apache.felix.scr.annotations.Service;
13 +import org.onlab.onos.cluster.ClusterService;
14 +import org.onlab.onos.net.ConnectPoint;
15 +import org.onlab.onos.net.PortNumber;
16 +import org.onlab.onos.net.flow.FlowEntry;
17 +import org.onlab.onos.net.flow.FlowRule;
18 +import org.onlab.onos.net.flow.instructions.Instruction;
19 +import org.onlab.onos.net.flow.instructions.Instructions;
20 +import org.onlab.onos.net.statistic.StatisticStore;
21 +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
22 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
23 +import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
24 +import org.onlab.onos.store.cluster.messaging.ClusterMessageResponse;
25 +import org.onlab.onos.store.flow.ReplicaInfo;
26 +import org.onlab.onos.store.flow.ReplicaInfoService;
27 +import org.onlab.onos.store.serializers.KryoNamespaces;
28 +import org.onlab.onos.store.serializers.KryoSerializer;
29 +import org.onlab.util.KryoNamespace;
30 +import org.slf4j.Logger;
31 +
32 +import java.io.IOException;
33 +import java.util.HashSet;
34 +import java.util.Map;
35 +import java.util.Set;
36 +import java.util.concurrent.ConcurrentHashMap;
37 +import java.util.concurrent.TimeUnit;
38 +import java.util.concurrent.TimeoutException;
39 +import java.util.concurrent.atomic.AtomicInteger;
40 +
41 +
42 +/**
43 + * Maintains statistics using RPC calls to collect stats from remote instances
44 + * on demand.
45 + */
46 +@Component(immediate = true)
47 +@Service
48 +public class DistributedStatisticStore implements StatisticStore {
49 +
50 + private final Logger log = getLogger(getClass());
51 +
52 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
53 + private ReplicaInfoService replicaInfoManager;
54 +
55 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
56 + private ClusterCommunicationService clusterCommunicator;
57 +
58 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 + private ClusterService clusterService;
60 +
61 + private Map<ConnectPoint, InternalStatisticRepresentation> representations =
62 + new ConcurrentHashMap<>();
63 +
64 + private Map<ConnectPoint, Set<FlowEntry>> previous =
65 + new ConcurrentHashMap<>();
66 +
67 + private Map<ConnectPoint, Set<FlowEntry>> current =
68 + new ConcurrentHashMap<>();
69 +
70 + protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
71 + @Override
72 + protected void setupKryoPool() {
73 + serializerPool = KryoNamespace.newBuilder()
74 + .register(KryoNamespaces.API)
75 + // register this store specific classes here
76 + .build()
77 + .populate(1);
78 + }
79 + };;
80 +
81 + private static final long STATISTIC_STORE_TIMEOUT_MILLIS = 3000;
82 +
83 + @Activate
84 + public void activate() {
85 + clusterCommunicator.addSubscriber(GET_CURRENT, new ClusterMessageHandler() {
86 +
87 + @Override
88 + public void handle(ClusterMessage message) {
89 + ConnectPoint cp = SERIALIZER.decode(message.payload());
90 + try {
91 + message.respond(SERIALIZER.encode(getCurrentStatisticInternal(cp)));
92 + } catch (IOException e) {
93 + log.error("Failed to respond back", e);
94 + }
95 + }
96 + });
97 +
98 + clusterCommunicator.addSubscriber(GET_PREVIOUS, new ClusterMessageHandler() {
99 +
100 + @Override
101 + public void handle(ClusterMessage message) {
102 + ConnectPoint cp = SERIALIZER.decode(message.payload());
103 + try {
104 + message.respond(SERIALIZER.encode(getPreviousStatisticInternal(cp)));
105 + } catch (IOException e) {
106 + log.error("Failed to respond back", e);
107 + }
108 + }
109 + });
110 + log.info("Started");
111 + }
112 +
113 + @Deactivate
114 + public void deactivate() {
115 + log.info("Stopped");
116 + }
117 +
118 + @Override
119 + public void prepareForStatistics(FlowRule rule) {
120 + ConnectPoint cp = buildConnectPoint(rule);
121 + if (cp == null) {
122 + return;
123 + }
124 + InternalStatisticRepresentation rep;
125 + synchronized (representations) {
126 + rep = getOrCreateRepresentation(cp);
127 + }
128 + rep.prepare();
129 + }
130 +
131 + @Override
132 + public synchronized void removeFromStatistics(FlowRule rule) {
133 + ConnectPoint cp = buildConnectPoint(rule);
134 + if (cp == null) {
135 + return;
136 + }
137 + InternalStatisticRepresentation rep = representations.get(cp);
138 + if (rep != null) {
139 + rep.remove(rule);
140 + }
141 + Set<FlowEntry> values = current.get(cp);
142 + if (values != null) {
143 + values.remove(rule);
144 + }
145 + values = previous.get(cp);
146 + if (values != null) {
147 + values.remove(rule);
148 + }
149 +
150 + }
151 +
152 + @Override
153 + public void addOrUpdateStatistic(FlowEntry rule) {
154 + ConnectPoint cp = buildConnectPoint(rule);
155 + if (cp == null) {
156 + return;
157 + }
158 + InternalStatisticRepresentation rep = representations.get(cp);
159 + if (rep != null && rep.submit(rule)) {
160 + updatePublishedStats(cp, rep.get());
161 + }
162 + }
163 +
164 + private synchronized void updatePublishedStats(ConnectPoint cp,
165 + Set<FlowEntry> flowEntries) {
166 + Set<FlowEntry> curr = current.get(cp);
167 + if (curr == null) {
168 + curr = new HashSet<>();
169 + }
170 + previous.put(cp, curr);
171 + current.put(cp, flowEntries);
172 +
173 + }
174 +
175 + @Override
176 + public Set<FlowEntry> getCurrentStatistic(ConnectPoint connectPoint) {
177 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(connectPoint.deviceId());
178 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
179 + return getCurrentStatisticInternal(connectPoint);
180 + } else {
181 + ClusterMessage message = new ClusterMessage(
182 + clusterService.getLocalNode().id(),
183 + GET_CURRENT,
184 + SERIALIZER.encode(connectPoint));
185 +
186 + try {
187 + ClusterMessageResponse response =
188 + clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
189 + return SERIALIZER.decode(response.get(STATISTIC_STORE_TIMEOUT_MILLIS,
190 + TimeUnit.MILLISECONDS));
191 + } catch (IOException | TimeoutException e) {
192 + // FIXME: throw a StatsStoreException
193 + throw new RuntimeException(e);
194 + }
195 + }
196 +
197 + }
198 +
199 + private synchronized Set<FlowEntry> getCurrentStatisticInternal(ConnectPoint connectPoint) {
200 + return current.get(connectPoint);
201 + }
202 +
203 + @Override
204 + public Set<FlowEntry> getPreviousStatistic(ConnectPoint connectPoint) {
205 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(connectPoint.deviceId());
206 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
207 + return getPreviousStatisticInternal(connectPoint);
208 + } else {
209 + ClusterMessage message = new ClusterMessage(
210 + clusterService.getLocalNode().id(),
211 + GET_PREVIOUS,
212 + SERIALIZER.encode(connectPoint));
213 +
214 + try {
215 + ClusterMessageResponse response =
216 + clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
217 + return SERIALIZER.decode(response.get(STATISTIC_STORE_TIMEOUT_MILLIS,
218 + TimeUnit.MILLISECONDS));
219 + } catch (IOException | TimeoutException e) {
220 + // FIXME: throw a StatsStoreException
221 + throw new RuntimeException(e);
222 + }
223 + }
224 +
225 + }
226 +
227 + private synchronized Set<FlowEntry> getPreviousStatisticInternal(ConnectPoint connectPoint) {
228 + return previous.get(connectPoint);
229 + }
230 +
231 + private InternalStatisticRepresentation getOrCreateRepresentation(ConnectPoint cp) {
232 +
233 + if (representations.containsKey(cp)) {
234 + return representations.get(cp);
235 + } else {
236 + InternalStatisticRepresentation rep = new InternalStatisticRepresentation();
237 + representations.put(cp, rep);
238 + return rep;
239 + }
240 +
241 + }
242 +
243 + private ConnectPoint buildConnectPoint(FlowRule rule) {
244 + PortNumber port = getOutput(rule);
245 + if (port == null) {
246 + log.warn("Rule {} has no output.", rule);
247 + return null;
248 + }
249 + ConnectPoint cp = new ConnectPoint(rule.deviceId(), port);
250 + return cp;
251 + }
252 +
253 + private PortNumber getOutput(FlowRule rule) {
254 + for (Instruction i : rule.treatment().instructions()) {
255 + if (i.type() == Instruction.Type.OUTPUT) {
256 + Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
257 + return out.port();
258 + }
259 + if (i.type() == Instruction.Type.DROP) {
260 + return PortNumber.P0;
261 + }
262 + }
263 + return null;
264 + }
265 +
266 + private class InternalStatisticRepresentation {
267 +
268 + private final AtomicInteger counter = new AtomicInteger(0);
269 + private final Set<FlowEntry> rules = new HashSet<>();
270 +
271 + public void prepare() {
272 + counter.incrementAndGet();
273 + }
274 +
275 + public synchronized void remove(FlowRule rule) {
276 + rules.remove(rule);
277 + counter.decrementAndGet();
278 + }
279 +
280 + public synchronized boolean submit(FlowEntry rule) {
281 + if (rules.contains(rule)) {
282 + rules.remove(rule);
283 + }
284 + rules.add(rule);
285 + if (counter.get() == 0) {
286 + return true;
287 + } else {
288 + return counter.decrementAndGet() == 0;
289 + }
290 + }
291 +
292 + public synchronized Set<FlowEntry> get() {
293 + counter.set(rules.size());
294 + return Sets.newHashSet(rules);
295 + }
296 +
297 +
298 + }
299 +
300 +}
1 +package org.onlab.onos.store.statistic.impl;
2 +
3 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 +
5 +/**
6 + * MessageSubjects used by DistributedStatisticStore peer-peer communication.
7 + */
8 +public final class StatisticStoreMessageSubjects {
9 + private StatisticStoreMessageSubjects() {}
10 + public static final MessageSubject GET_CURRENT =
11 + new MessageSubject("peer-return-current");
12 + public static final MessageSubject GET_PREVIOUS =
13 + new MessageSubject("peer-return-previous");
14 +
15 +}
1 +/**
2 + * Implementation of the statistic store.
3 + */
4 +package org.onlab.onos.store.statistic.impl;
...\ No newline at end of file ...\ No newline at end of file
...@@ -91,23 +91,14 @@ implements MastershipStore { ...@@ -91,23 +91,14 @@ implements MastershipStore {
91 91
92 @Override 92 @Override
93 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { 93 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
94 - NodeId current = getNode(MASTER, deviceId); 94 + final RoleValue roleInfo = getRoleValue(deviceId);
95 - if (current == null) { 95 + if (roleInfo.contains(MASTER, nodeId)) {
96 - if (isRole(STANDBY, nodeId, deviceId)) { 96 + return MASTER;
97 - //was previously standby, or set to standby from master
98 - return MastershipRole.STANDBY;
99 - } else {
100 - return MastershipRole.NONE;
101 - }
102 - } else {
103 - if (current.equals(nodeId)) {
104 - //*should* be in unusable, not always
105 - return MastershipRole.MASTER;
106 - } else {
107 - //may be in backups or unusable from earlier retirement
108 - return MastershipRole.STANDBY;
109 } 97 }
98 + if (roleInfo.contains(STANDBY, nodeId)) {
99 + return STANDBY;
110 } 100 }
101 + return NONE;
111 } 102 }
112 103
113 @Override 104 @Override
...@@ -124,10 +115,11 @@ implements MastershipStore { ...@@ -124,10 +115,11 @@ implements MastershipStore {
124 roleMap.put(deviceId, rv); 115 roleMap.put(deviceId, rv);
125 return null; 116 return null;
126 case STANDBY: 117 case STANDBY:
118 + case NONE:
127 NodeId current = rv.get(MASTER); 119 NodeId current = rv.get(MASTER);
128 if (current != null) { 120 if (current != null) {
129 //backup and replace current master 121 //backup and replace current master
130 - rv.reassign(nodeId, NONE, STANDBY); 122 + rv.reassign(current, NONE, STANDBY);
131 rv.replace(current, nodeId, MASTER); 123 rv.replace(current, nodeId, MASTER);
132 } else { 124 } else {
133 //no master before so just add. 125 //no master before so just add.
...@@ -137,12 +129,6 @@ implements MastershipStore { ...@@ -137,12 +129,6 @@ implements MastershipStore {
137 roleMap.put(deviceId, rv); 129 roleMap.put(deviceId, rv);
138 updateTerm(deviceId); 130 updateTerm(deviceId);
139 return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); 131 return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo());
140 - case NONE:
141 - rv.add(MASTER, nodeId);
142 - rv.reassign(nodeId, STANDBY, NONE);
143 - roleMap.put(deviceId, rv);
144 - updateTerm(deviceId);
145 - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo());
146 default: 132 default:
147 log.warn("unknown Mastership Role {}", role); 133 log.warn("unknown Mastership Role {}", role);
148 return null; 134 return null;
...@@ -193,21 +179,28 @@ implements MastershipStore { ...@@ -193,21 +179,28 @@ implements MastershipStore {
193 switch (role) { 179 switch (role) {
194 case MASTER: 180 case MASTER:
195 rv.reassign(local, STANDBY, NONE); 181 rv.reassign(local, STANDBY, NONE);
182 + terms.putIfAbsent(deviceId, INIT);
196 roleMap.put(deviceId, rv); 183 roleMap.put(deviceId, rv);
197 break; 184 break;
198 case STANDBY: 185 case STANDBY:
199 rv.reassign(local, NONE, STANDBY); 186 rv.reassign(local, NONE, STANDBY);
200 roleMap.put(deviceId, rv); 187 roleMap.put(deviceId, rv);
201 terms.putIfAbsent(deviceId, INIT); 188 terms.putIfAbsent(deviceId, INIT);
202 -
203 break; 189 break;
204 case NONE: 190 case NONE:
205 - //claim mastership 191 + //either we're the first standby, or first to device.
192 + //for latter, claim mastership.
193 + if (rv.get(MASTER) == null) {
206 rv.add(MASTER, local); 194 rv.add(MASTER, local);
207 rv.reassign(local, STANDBY, NONE); 195 rv.reassign(local, STANDBY, NONE);
208 - roleMap.put(deviceId, rv);
209 updateTerm(deviceId); 196 updateTerm(deviceId);
210 role = MastershipRole.MASTER; 197 role = MastershipRole.MASTER;
198 + } else {
199 + rv.add(STANDBY, local);
200 + rv.reassign(local, NONE, STANDBY);
201 + role = MastershipRole.STANDBY;
202 + }
203 + roleMap.put(deviceId, rv);
211 break; 204 break;
212 default: 205 default:
213 log.warn("unknown Mastership Role {}", role); 206 log.warn("unknown Mastership Role {}", role);
...@@ -315,7 +308,10 @@ implements MastershipStore { ...@@ -315,7 +308,10 @@ implements MastershipStore {
315 RoleValue value = roleMap.get(deviceId); 308 RoleValue value = roleMap.get(deviceId);
316 if (value == null) { 309 if (value == null) {
317 value = new RoleValue(); 310 value = new RoleValue();
318 - roleMap.put(deviceId, value); 311 + RoleValue concurrentlyAdded = roleMap.putIfAbsent(deviceId, value);
312 + if (concurrentlyAdded != null) {
313 + return concurrentlyAdded;
314 + }
319 } 315 }
320 return value; 316 return value;
321 } 317 }
...@@ -329,16 +325,6 @@ implements MastershipStore { ...@@ -329,16 +325,6 @@ implements MastershipStore {
329 return null; 325 return null;
330 } 326 }
331 327
332 - //check if node is a certain role given a device
333 - private boolean isRole(
334 - MastershipRole role, NodeId nodeId, DeviceId deviceId) {
335 - RoleValue value = roleMap.get(deviceId);
336 - if (value != null) {
337 - return value.contains(role, nodeId);
338 - }
339 - return false;
340 - }
341 -
342 //adds or updates term information. 328 //adds or updates term information.
343 private void updateTerm(DeviceId deviceId) { 329 private void updateTerm(DeviceId deviceId) {
344 terms.lock(deviceId); 330 terms.lock(deviceId);
......
...@@ -97,6 +97,7 @@ public class DistributedMastershipStoreTest { ...@@ -97,6 +97,7 @@ public class DistributedMastershipStoreTest {
97 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1)); 97 assertEquals("wrong role:", NONE, dms.getRole(N1, DID1));
98 testStore.put(DID1, N1, true, false, true); 98 testStore.put(DID1, N1, true, false, true);
99 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1)); 99 assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1));
100 + testStore.put(DID1, N2, false, true, false);
100 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1)); 101 assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1));
101 } 102 }
102 103
...@@ -155,6 +156,7 @@ public class DistributedMastershipStoreTest { ...@@ -155,6 +156,7 @@ public class DistributedMastershipStoreTest {
155 156
156 //switch over to N2 157 //switch over to N2
157 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type()); 158 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type());
159 + System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber());
158 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1)); 160 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1));
159 161
160 //orphan switch - should be rare case 162 //orphan switch - should be rare case
...@@ -182,14 +184,9 @@ public class DistributedMastershipStoreTest { ...@@ -182,14 +184,9 @@ public class DistributedMastershipStoreTest {
182 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type()); 184 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type());
183 assertEquals("wrong master", N2, dms.getMaster(DID1)); 185 assertEquals("wrong master", N2, dms.getMaster(DID1));
184 186
185 - //STANDBY - nothing here, either
186 - assertNull("wrong event:", dms.relinquishRole(N1, DID1));
187 - assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1));
188 -
189 //all nodes "give up" on device, which goes back to NONE. 187 //all nodes "give up" on device, which goes back to NONE.
190 assertNull("wrong event:", dms.relinquishRole(N2, DID1)); 188 assertNull("wrong event:", dms.relinquishRole(N2, DID1));
191 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1)); 189 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
192 - assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
193 190
194 assertEquals("wrong number of retired nodes", 2, 191 assertEquals("wrong number of retired nodes", 2,
195 dms.roleMap.get(DID1).nodesOfRole(NONE).size()); 192 dms.roleMap.get(DID1).nodesOfRole(NONE).size());
...@@ -201,6 +198,10 @@ public class DistributedMastershipStoreTest { ...@@ -201,6 +198,10 @@ public class DistributedMastershipStoreTest {
201 assertEquals("wrong number of backup nodes", 1, 198 assertEquals("wrong number of backup nodes", 1,
202 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size()); 199 dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
203 200
201 + //If STANDBY, should drop to NONE
202 + assertNull("wrong event:", dms.relinquishRole(N1, DID1));
203 + assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
204 +
204 //NONE - nothing happens 205 //NONE - nothing happens
205 assertNull("wrong event:", dms.relinquishRole(N1, DID2)); 206 assertNull("wrong event:", dms.relinquishRole(N1, DID2));
206 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2)); 207 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2));
...@@ -218,7 +219,7 @@ public class DistributedMastershipStoreTest { ...@@ -218,7 +219,7 @@ public class DistributedMastershipStoreTest {
218 public void notify(MastershipEvent event) { 219 public void notify(MastershipEvent event) {
219 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type()); 220 assertEquals("wrong event:", Type.MASTER_CHANGED, event.type());
220 assertEquals("wrong subject", DID1, event.subject()); 221 assertEquals("wrong subject", DID1, event.subject());
221 - assertEquals("wrong subject", N1, event.node()); 222 + assertEquals("wrong subject", N1, event.roleInfo().master());
222 addLatch.countDown(); 223 addLatch.countDown();
223 } 224 }
224 }; 225 };
......
...@@ -4,6 +4,7 @@ import java.net.URI; ...@@ -4,6 +4,7 @@ import java.net.URI;
4 import java.util.ArrayList; 4 import java.util.ArrayList;
5 import java.util.Arrays; 5 import java.util.Arrays;
6 import java.util.HashMap; 6 import java.util.HashMap;
7 +import java.util.HashSet;
7 8
8 import org.onlab.onos.cluster.ControllerNode; 9 import org.onlab.onos.cluster.ControllerNode;
9 import org.onlab.onos.cluster.DefaultControllerNode; 10 import org.onlab.onos.cluster.DefaultControllerNode;
...@@ -30,6 +31,7 @@ import org.onlab.onos.net.flow.DefaultFlowEntry; ...@@ -30,6 +31,7 @@ import org.onlab.onos.net.flow.DefaultFlowEntry;
30 import org.onlab.onos.net.flow.DefaultFlowRule; 31 import org.onlab.onos.net.flow.DefaultFlowRule;
31 import org.onlab.onos.net.flow.DefaultTrafficSelector; 32 import org.onlab.onos.net.flow.DefaultTrafficSelector;
32 import org.onlab.onos.net.flow.DefaultTrafficTreatment; 33 import org.onlab.onos.net.flow.DefaultTrafficTreatment;
34 +import org.onlab.onos.net.flow.FlowEntry;
33 import org.onlab.onos.net.flow.FlowId; 35 import org.onlab.onos.net.flow.FlowId;
34 import org.onlab.onos.net.flow.StoredFlowEntry; 36 import org.onlab.onos.net.flow.StoredFlowEntry;
35 import org.onlab.onos.net.flow.criteria.Criteria; 37 import org.onlab.onos.net.flow.criteria.Criteria;
...@@ -77,6 +79,7 @@ public final class KryoNamespaces { ...@@ -77,6 +79,7 @@ public final class KryoNamespaces {
77 ArrayList.class, 79 ArrayList.class,
78 Arrays.asList().getClass(), 80 Arrays.asList().getClass(),
79 HashMap.class, 81 HashMap.class,
82 + HashSet.class,
80 // 83 //
81 // 84 //
82 ControllerNode.State.class, 85 ControllerNode.State.class,
...@@ -98,6 +101,8 @@ public final class KryoNamespaces { ...@@ -98,6 +101,8 @@ public final class KryoNamespaces {
98 DefaultFlowEntry.class, 101 DefaultFlowEntry.class,
99 StoredFlowEntry.class, 102 StoredFlowEntry.class,
100 DefaultFlowRule.class, 103 DefaultFlowRule.class,
104 + DefaultFlowEntry.class,
105 + FlowEntry.FlowEntryState.class,
101 FlowId.class, 106 FlowId.class,
102 DefaultTrafficSelector.class, 107 DefaultTrafficSelector.class,
103 Criteria.PortCriterion.class, 108 Criteria.PortCriterion.class,
......
...@@ -70,15 +70,16 @@ public class SimpleDeviceStore ...@@ -70,15 +70,16 @@ public class SimpleDeviceStore
70 70
71 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; 71 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
72 72
73 - // collection of Description given from various providers 73 + // Collection of Description given from various providers
74 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>> 74 private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
75 deviceDescs = Maps.newConcurrentMap(); 75 deviceDescs = Maps.newConcurrentMap();
76 76
77 - // cache of Device and Ports generated by compositing descriptions from providers 77 + // Cache of Device and Ports generated by compositing descriptions from providers
78 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); 78 private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
79 - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap(); 79 + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
80 + devicePorts = Maps.newConcurrentMap();
80 81
81 - // available(=UP) devices 82 + // Available (=UP) devices
82 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); 83 private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
83 84
84 85
...@@ -115,13 +116,11 @@ public class SimpleDeviceStore ...@@ -115,13 +116,11 @@ public class SimpleDeviceStore
115 public DeviceEvent createOrUpdateDevice(ProviderId providerId, 116 public DeviceEvent createOrUpdateDevice(ProviderId providerId,
116 DeviceId deviceId, 117 DeviceId deviceId,
117 DeviceDescription deviceDescription) { 118 DeviceDescription deviceDescription) {
118 -
119 Map<ProviderId, DeviceDescriptions> providerDescs 119 Map<ProviderId, DeviceDescriptions> providerDescs
120 = getOrCreateDeviceDescriptions(deviceId); 120 = getOrCreateDeviceDescriptions(deviceId);
121 121
122 synchronized (providerDescs) { 122 synchronized (providerDescs) {
123 // locking per device 123 // locking per device
124 -
125 DeviceDescriptions descs 124 DeviceDescriptions descs
126 = getOrCreateProviderDeviceDescriptions(providerDescs, 125 = getOrCreateProviderDeviceDescriptions(providerDescs,
127 providerId, 126 providerId,
...@@ -145,7 +144,6 @@ public class SimpleDeviceStore ...@@ -145,7 +144,6 @@ public class SimpleDeviceStore
145 // Creates the device and returns the appropriate event if necessary. 144 // Creates the device and returns the appropriate event if necessary.
146 // Guarded by deviceDescs value (=Device lock) 145 // Guarded by deviceDescs value (=Device lock)
147 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { 146 private DeviceEvent createDevice(ProviderId providerId, Device newDevice) {
148 -
149 // update composed device cache 147 // update composed device cache
150 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); 148 Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
151 verify(oldDevice == null, 149 verify(oldDevice == null,
...@@ -162,14 +160,21 @@ public class SimpleDeviceStore ...@@ -162,14 +160,21 @@ public class SimpleDeviceStore
162 // Updates the device and returns the appropriate event if necessary. 160 // Updates the device and returns the appropriate event if necessary.
163 // Guarded by deviceDescs value (=Device lock) 161 // Guarded by deviceDescs value (=Device lock)
164 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { 162 private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) {
165 -
166 // We allow only certain attributes to trigger update 163 // We allow only certain attributes to trigger update
167 - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 164 + boolean propertiesChanged =
168 - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || 165 + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
169 - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { 166 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion());
167 + boolean annotationsChanged =
168 + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations());
169 +
170 + // Primary providers can respond to all changes, but ancillary ones
171 + // should respond only to annotation changes.
172 + if ((providerId.isAncillary() && annotationsChanged) ||
173 + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) {
170 174
171 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); 175 boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
172 if (!replaced) { 176 if (!replaced) {
177 + // FIXME: Is the enclosing if required here?
173 verify(replaced, 178 verify(replaced,
174 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]", 179 "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
175 providerId, oldDevice, devices.get(newDevice.id()) 180 providerId, oldDevice, devices.get(newDevice.id())
...@@ -214,7 +219,6 @@ public class SimpleDeviceStore ...@@ -214,7 +219,6 @@ public class SimpleDeviceStore
214 public List<DeviceEvent> updatePorts(ProviderId providerId, 219 public List<DeviceEvent> updatePorts(ProviderId providerId,
215 DeviceId deviceId, 220 DeviceId deviceId,
216 List<PortDescription> portDescriptions) { 221 List<PortDescription> portDescriptions) {
217 -
218 Device device = devices.get(deviceId); 222 Device device = devices.get(deviceId);
219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 223 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
220 224
...@@ -327,7 +331,6 @@ public class SimpleDeviceStore ...@@ -327,7 +331,6 @@ public class SimpleDeviceStore
327 private DeviceDescriptions getOrCreateProviderDeviceDescriptions( 331 private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
328 Map<ProviderId, DeviceDescriptions> device, 332 Map<ProviderId, DeviceDescriptions> device,
329 ProviderId providerId, DeviceDescription deltaDesc) { 333 ProviderId providerId, DeviceDescription deltaDesc) {
330 -
331 synchronized (device) { 334 synchronized (device) {
332 DeviceDescriptions r = device.get(providerId); 335 DeviceDescriptions r = device.get(providerId);
333 if (r == null) { 336 if (r == null) {
...@@ -447,7 +450,7 @@ public class SimpleDeviceStore ...@@ -447,7 +450,7 @@ public class SimpleDeviceStore
447 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations()); 450 annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
448 } 451 }
449 452
450 - return new DefaultDevice(primary, deviceId , type, manufacturer, 453 + return new DefaultDevice(primary, deviceId, type, manufacturer,
451 hwVersion, swVersion, serialNumber, 454 hwVersion, swVersion, serialNumber,
452 chassisId, annotations); 455 chassisId, annotations);
453 } 456 }
......
...@@ -187,17 +187,23 @@ public class SimpleFlowRuleStore ...@@ -187,17 +187,23 @@ public class SimpleFlowRuleStore
187 public void deleteFlowRule(FlowRule rule) { 187 public void deleteFlowRule(FlowRule rule) {
188 188
189 List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id()); 189 List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id());
190 +
190 synchronized (entries) { 191 synchronized (entries) {
191 for (StoredFlowEntry entry : entries) { 192 for (StoredFlowEntry entry : entries) {
192 if (entry.equals(rule)) { 193 if (entry.equals(rule)) {
193 synchronized (entry) { 194 synchronized (entry) {
194 entry.setState(FlowEntryState.PENDING_REMOVE); 195 entry.setState(FlowEntryState.PENDING_REMOVE);
195 // TODO: Should we notify only if it's "remote" event? 196 // TODO: Should we notify only if it's "remote" event?
196 - //notifyDelegate(new FlowRuleEvent(Type.RULE_REMOVE_REQUESTED, rule)); 197 + notifyDelegate(FlowRuleBatchEvent.create(
198 + new FlowRuleBatchRequest(
199 + Collections.<FlowEntry>emptyList(),
200 + Arrays.<FlowEntry>asList(entry))));
197 } 201 }
198 } 202 }
199 } 203 }
200 } 204 }
205 +
206 +
201 //log.warn("Cannot find rule {}", rule); 207 //log.warn("Cannot find rule {}", rule);
202 } 208 }
203 209
......
...@@ -82,12 +82,28 @@ public class SimpleIntentStore ...@@ -82,12 +82,28 @@ public class SimpleIntentStore
82 public IntentEvent setState(Intent intent, IntentState state) { 82 public IntentEvent setState(Intent intent, IntentState state) {
83 IntentId id = intent.id(); 83 IntentId id = intent.id();
84 states.put(id, state); 84 states.put(id, state);
85 - IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED : 85 + IntentEvent.Type type = null;
86 - (state == INSTALLED ? IntentEvent.Type.INSTALLED : 86 +
87 - (state == FAILED ? IntentEvent.Type.FAILED : 87 + switch (state) {
88 - state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN : 88 + case SUBMITTED:
89 - null))); 89 + type = IntentEvent.Type.SUBMITTED;
90 - return type == null ? null : new IntentEvent(type, intent); 90 + break;
91 + case INSTALLED:
92 + type = IntentEvent.Type.INSTALLED;
93 + break;
94 + case FAILED:
95 + type = IntentEvent.Type.FAILED;
96 + break;
97 + case WITHDRAWN:
98 + type = IntentEvent.Type.WITHDRAWN;
99 + break;
100 + default:
101 + break;
102 + }
103 + if (type == null) {
104 + return null;
105 + }
106 + return new IntentEvent(type, intent);
91 } 107 }
92 108
93 @Override 109 @Override
......
...@@ -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>
......
...@@ -110,8 +110,7 @@ public interface OpenFlowSwitch { ...@@ -110,8 +110,7 @@ public interface OpenFlowSwitch {
110 * 110 *
111 * @param role the failed role 111 * @param role the failed role
112 */ 112 */
113 - void returnRoleAssertFailure(RoleState role); 113 + public void returnRoleAssertFailure(RoleState role);
114 -
115 114
116 /** 115 /**
117 * Indicates if this switch is optical. 116 * Indicates if this switch is optical.
...@@ -120,5 +119,4 @@ public interface OpenFlowSwitch { ...@@ -120,5 +119,4 @@ public interface OpenFlowSwitch {
120 */ 119 */
121 public boolean isOptical(); 120 public boolean isOptical();
122 121
123 -
124 } 122 }
......
...@@ -20,6 +20,12 @@ public interface OpenFlowSwitchListener { ...@@ -20,6 +20,12 @@ public interface OpenFlowSwitchListener {
20 public void switchRemoved(Dpid dpid); 20 public void switchRemoved(Dpid dpid);
21 21
22 /** 22 /**
23 + * Notify that the switch has changed in some way.
24 + * @param dpid the switch that changed
25 + */
26 + public void switchChanged(Dpid dpid);
27 +
28 + /**
23 * Notify that a port has changed. 29 * Notify that a port has changed.
24 * @param dpid the switch on which the change happened. 30 * @param dpid the switch on which the change happened.
25 * @param status the new state of the port. 31 * @param status the new state of the port.
......
...@@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.OFHello; ...@@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.OFHello;
41 import org.projectfloodlight.openflow.protocol.OFHelloElem; 41 import org.projectfloodlight.openflow.protocol.OFHelloElem;
42 import org.projectfloodlight.openflow.protocol.OFMessage; 42 import org.projectfloodlight.openflow.protocol.OFMessage;
43 import org.projectfloodlight.openflow.protocol.OFPacketIn; 43 import org.projectfloodlight.openflow.protocol.OFPacketIn;
44 -import org.projectfloodlight.openflow.protocol.OFPacketOut;
45 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; 44 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
46 import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest; 45 import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
47 import org.projectfloodlight.openflow.protocol.OFPortStatus; 46 import org.projectfloodlight.openflow.protocol.OFPortStatus;
...@@ -565,6 +564,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -565,6 +564,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
565 @Override 564 @Override
566 void processOFStatisticsReply(OFChannelHandler h, 565 void processOFStatisticsReply(OFChannelHandler h,
567 OFStatsReply m) { 566 OFStatsReply m) {
567 + if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
568 + h.sw.setPortDescReply((OFPortDescStatsReply) m);
569 + }
568 h.dispatchMessage(m); 570 h.dispatchMessage(m);
569 } 571 }
570 572
...@@ -608,6 +610,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -608,6 +610,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
608 h.dispatchMessage(m); 610 h.dispatchMessage(m);
609 } 611 }
610 612
613 + @Override
614 + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
615 + h.sw.setFeaturesReply(m);
616 + h.dispatchMessage(m);
617 + }
618 +
611 }; 619 };
612 620
613 private final boolean handshakeComplete; 621 private final boolean handshakeComplete;
...@@ -652,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -652,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
652 * However, we could be more forgiving 660 * However, we could be more forgiving
653 * @param h the channel handler that received the message 661 * @param h the channel handler that received the message
654 * @param m the message 662 * @param m the message
655 - * @throws SwitchStateException 663 + * @throws SwitchStateException we always throw the exception
656 - * @throws SwitchStateExeption we always through the execption
657 */ 664 */
658 - // needs to be protected because enum members are acutally subclasses 665 + // needs to be protected because enum members are actually subclasses
659 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) 666 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
660 throws SwitchStateException { 667 throws SwitchStateException {
661 String msg = getSwitchStateMessage(h, m, 668 String msg = getSwitchStateMessage(h, m,
...@@ -1016,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { ...@@ -1016,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
1016 // all state for the original switch (with the same dpid), 1023 // all state for the original switch (with the same dpid),
1017 // which we obviously don't want. 1024 // which we obviously don't want.
1018 log.info("{}:removal called", getSwitchInfoString()); 1025 log.info("{}:removal called", getSwitchInfoString());
1026 + if (sw != null) {
1019 sw.removeConnectedSwitch(); 1027 sw.removeConnectedSwitch();
1028 + }
1020 } else { 1029 } else {
1021 // A duplicate was disconnected on this ChannelHandler, 1030 // A duplicate was disconnected on this ChannelHandler,
1022 // this is the same switch reconnecting, but the original state was 1031 // this is the same switch reconnecting, but the original state was
......
...@@ -27,6 +27,8 @@ import org.onlab.onos.openflow.controller.driver.OpenFlowAgent; ...@@ -27,6 +27,8 @@ import org.onlab.onos.openflow.controller.driver.OpenFlowAgent;
27 import org.projectfloodlight.openflow.protocol.OFMessage; 27 import org.projectfloodlight.openflow.protocol.OFMessage;
28 import org.projectfloodlight.openflow.protocol.OFPacketIn; 28 import org.projectfloodlight.openflow.protocol.OFPacketIn;
29 import org.projectfloodlight.openflow.protocol.OFPortStatus; 29 import org.projectfloodlight.openflow.protocol.OFPortStatus;
30 +import org.projectfloodlight.openflow.protocol.OFStatsReply;
31 +import org.projectfloodlight.openflow.protocol.OFStatsType;
30 import org.slf4j.Logger; 32 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 33 import org.slf4j.LoggerFactory;
32 34
...@@ -146,6 +148,11 @@ public class OpenFlowControllerImpl implements OpenFlowController { ...@@ -146,6 +148,11 @@ public class OpenFlowControllerImpl implements OpenFlowController {
146 l.portChanged(dpid, (OFPortStatus) msg); 148 l.portChanged(dpid, (OFPortStatus) msg);
147 } 149 }
148 break; 150 break;
151 + case FEATURES_REPLY:
152 + for (OpenFlowSwitchListener l : ofSwitchListener) {
153 + l.switchChanged(dpid);
154 + }
155 + break;
149 case PACKET_IN: 156 case PACKET_IN:
150 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext 157 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
151 .packetContextFromPacketIn(this.getSwitch(dpid), 158 .packetContextFromPacketIn(this.getSwitch(dpid),
...@@ -154,9 +161,15 @@ public class OpenFlowControllerImpl implements OpenFlowController { ...@@ -154,9 +161,15 @@ public class OpenFlowControllerImpl implements OpenFlowController {
154 p.handlePacket(pktCtx); 161 p.handlePacket(pktCtx);
155 } 162 }
156 break; 163 break;
164 + case STATS_REPLY:
165 + OFStatsReply reply = (OFStatsReply) msg;
166 + if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) {
167 + for (OpenFlowSwitchListener l : ofSwitchListener) {
168 + l.switchChanged(dpid);
169 + }
170 + }
157 case FLOW_REMOVED: 171 case FLOW_REMOVED:
158 case ERROR: 172 case ERROR:
159 - case STATS_REPLY:
160 case BARRIER_REPLY: 173 case BARRIER_REPLY:
161 executor.submit(new OFMessageHandler(dpid, msg)); 174 executor.submit(new OFMessageHandler(dpid, msg));
162 break; 175 break;
...@@ -194,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { ...@@ -194,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController {
194 + "value for dpid: {}", dpid); 207 + "value for dpid: {}", dpid);
195 return false; 208 return false;
196 } else { 209 } else {
197 - log.error("Added switch {}", dpid); 210 + log.info("Added switch {}", dpid);
198 connectedSwitches.put(dpid, sw); 211 connectedSwitches.put(dpid, sw);
199 for (OpenFlowSwitchListener l : ofSwitchListener) { 212 for (OpenFlowSwitchListener l : ofSwitchListener) {
200 l.switchAdded(dpid); 213 l.switchAdded(dpid);
......
...@@ -1188,7 +1188,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { ...@@ -1188,7 +1188,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
1188 .setHardTimeout(0) 1188 .setHardTimeout(0)
1189 .setXid(getNextTransactionId()) 1189 .setXid(getNextTransactionId())
1190 .build(); 1190 .build();
1191 - sendMsg(tableMissEntry); 1191 +
1192 + write(tableMissEntry);
1192 } 1193 }
1193 1194
1194 private void sendBarrier(boolean finalBarrier) { 1195 private void sendBarrier(boolean finalBarrier) {
...@@ -1200,7 +1201,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { ...@@ -1200,7 +1201,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
1200 .buildBarrierRequest() 1201 .buildBarrierRequest()
1201 .setXid(xid) 1202 .setXid(xid)
1202 .build(); 1203 .build();
1203 - sendMsg(br); 1204 +
1205 + write(br);
1204 } 1206 }
1205 1207
1206 @Override 1208 @Override
...@@ -1210,7 +1212,7 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { ...@@ -1210,7 +1212,7 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch {
1210 1212
1211 @Override 1213 @Override
1212 public void write(OFMessage msg) { 1214 public void write(OFMessage msg) {
1213 - this.channel.write(msg); 1215 + this.channel.write(Collections.singletonList(msg));
1214 1216
1215 } 1217 }
1216 1218
......
...@@ -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>
...@@ -610,7 +609,8 @@ ...@@ -610,7 +609,8 @@
610 </plugin> 609 </plugin>
611 610
612 </plugins> 611 </plugins>
613 -
614 </reporting> 612 </reporting>
615 - 613 + <prerequisites>
614 + <maven>3.0.0</maven>
615 + </prerequisites>
616 </project> 616 </project>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onlab.onos.provider.lldp.impl; 16 package org.onlab.onos.provider.lldp.impl;
17 17
18 18
19 +import static com.google.common.base.Preconditions.checkNotNull;
19 import static org.slf4j.LoggerFactory.getLogger; 20 import static org.slf4j.LoggerFactory.getLogger;
20 21
21 import java.nio.ByteBuffer; 22 import java.nio.ByteBuffer;
...@@ -95,11 +96,13 @@ public class LinkDiscovery implements TimerTask { ...@@ -95,11 +96,13 @@ public class LinkDiscovery implements TimerTask {
95 */ 96 */
96 public LinkDiscovery(Device device, PacketService pktService, 97 public LinkDiscovery(Device device, PacketService pktService,
97 MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) { 98 MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) {
99 +
98 this.device = device; 100 this.device = device;
99 this.probeRate = 3000; 101 this.probeRate = 3000;
100 this.linkProvider = providerService; 102 this.linkProvider = providerService;
101 this.pktService = pktService; 103 this.pktService = pktService;
102 - this.mastershipService = masterService; 104 +
105 + this.mastershipService = checkNotNull(masterService, "WTF!");
103 this.slowPorts = Collections.synchronizedSet(new HashSet<Long>()); 106 this.slowPorts = Collections.synchronizedSet(new HashSet<Long>());
104 this.fastPorts = Collections.synchronizedSet(new HashSet<Long>()); 107 this.fastPorts = Collections.synchronizedSet(new HashSet<Long>());
105 this.portProbeCount = new HashMap<>(); 108 this.portProbeCount = new HashMap<>();
...@@ -344,7 +347,14 @@ public class LinkDiscovery implements TimerTask { ...@@ -344,7 +347,14 @@ public class LinkDiscovery implements TimerTask {
344 } 347 }
345 348
346 private void sendProbes(Long portNumber) { 349 private void sendProbes(Long portNumber) {
347 - if (mastershipService.getLocalRole(this.device.id()) == 350 + if (device == null) {
351 + log.warn("CRAZY SHIT");
352 + }
353 + if (mastershipService == null) {
354 + log.warn("INSANE");
355 + }
356 + if (device.type() != Device.Type.ROADM &&
357 + mastershipService.getLocalRole(this.device.id()) ==
348 MastershipRole.MASTER) { 358 MastershipRole.MASTER) {
349 OutboundPacket pkt = this.createOutBoundLLDP(portNumber); 359 OutboundPacket pkt = this.createOutBoundLLDP(portNumber);
350 pktService.emit(pkt); 360 pktService.emit(pkt);
......
...@@ -23,7 +23,9 @@ import org.onlab.onos.openflow.controller.OpenFlowController; ...@@ -23,7 +23,9 @@ import org.onlab.onos.openflow.controller.OpenFlowController;
23 import org.onlab.onos.openflow.controller.OpenFlowSwitch; 23 import org.onlab.onos.openflow.controller.OpenFlowSwitch;
24 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener; 24 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
25 import org.onlab.onos.openflow.controller.RoleState; 25 import org.onlab.onos.openflow.controller.RoleState;
26 +import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
26 import org.onlab.packet.ChassisId; 27 import org.onlab.packet.ChassisId;
28 +import org.projectfloodlight.openflow.protocol.OFFactory;
27 import org.projectfloodlight.openflow.protocol.OFPortConfig; 29 import org.projectfloodlight.openflow.protocol.OFPortConfig;
28 import org.projectfloodlight.openflow.protocol.OFPortDesc; 30 import org.projectfloodlight.openflow.protocol.OFPortDesc;
29 import org.projectfloodlight.openflow.protocol.OFPortState; 31 import org.projectfloodlight.openflow.protocol.OFPortState;
...@@ -89,6 +91,28 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ...@@ -89,6 +91,28 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
89 @Override 91 @Override
90 public void triggerProbe(Device device) { 92 public void triggerProbe(Device device) {
91 LOG.info("Triggering probe on device {}", device.id()); 93 LOG.info("Triggering probe on device {}", device.id());
94 +
95 + // 1. check device liveness
96 + // FIXME if possible, we might want this to be part of
97 + // OpenFlowSwitch interface so the driver interface isn't misused.
98 + OpenFlowSwitch sw = controller.getSwitch(dpid(device.id().uri()));
99 + if (!((OpenFlowSwitchDriver) sw).isConnected()) {
100 + providerService.deviceDisconnected(device.id());
101 + return;
102 + }
103 +
104 + // 2. Prompt an update of port information. Do we have an XID for this?
105 + OFFactory fact = sw.factory();
106 + switch (fact.getVersion()) {
107 + case OF_10:
108 + sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build());
109 + break;
110 + case OF_13:
111 + sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build());
112 + break;
113 + default:
114 + LOG.warn("Unhandled protocol version");
115 + }
92 } 116 }
93 117
94 @Override 118 @Override
...@@ -141,6 +165,17 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ...@@ -141,6 +165,17 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
141 providerService.deviceDisconnected(deviceId(uri(dpid))); 165 providerService.deviceDisconnected(deviceId(uri(dpid)));
142 } 166 }
143 167
168 +
169 + @Override
170 + public void switchChanged(Dpid dpid) {
171 + if (providerService == null) {
172 + return;
173 + }
174 + DeviceId did = deviceId(uri(dpid));
175 + OpenFlowSwitch sw = controller.getSwitch(dpid);
176 + providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
177 + }
178 +
144 @Override 179 @Override
145 public void portChanged(Dpid dpid, OFPortStatus status) { 180 public void portChanged(Dpid dpid, OFPortStatus status) {
146 PortDescription portDescription = buildPortDescription(status.getDesc()); 181 PortDescription portDescription = buildPortDescription(status.getDesc());
......
...@@ -31,7 +31,7 @@ import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation; ...@@ -31,7 +31,7 @@ import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
31 import org.onlab.onos.net.flow.FlowRuleProvider; 31 import org.onlab.onos.net.flow.FlowRuleProvider;
32 import org.onlab.onos.net.flow.FlowRuleProviderRegistry; 32 import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
33 import org.onlab.onos.net.flow.FlowRuleProviderService; 33 import org.onlab.onos.net.flow.FlowRuleProviderService;
34 -import org.onlab.onos.net.intent.BatchOperation; 34 +import org.onlab.onos.net.flow.BatchOperation;
35 import org.onlab.onos.net.provider.AbstractProvider; 35 import org.onlab.onos.net.provider.AbstractProvider;
36 import org.onlab.onos.net.provider.ProviderId; 36 import org.onlab.onos.net.provider.ProviderId;
37 import org.onlab.onos.net.topology.TopologyService; 37 import org.onlab.onos.net.topology.TopologyService;
...@@ -107,6 +107,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -107,6 +107,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
107 private final Map<Long, InstallationFuture> pendingFMs = 107 private final Map<Long, InstallationFuture> pendingFMs =
108 new ConcurrentHashMap<Long, InstallationFuture>(); 108 new ConcurrentHashMap<Long, InstallationFuture>();
109 109
110 + private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
111 +
110 /** 112 /**
111 * Creates an OpenFlow host provider. 113 * Creates an OpenFlow host provider.
112 */ 114 */
...@@ -119,6 +121,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -119,6 +121,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
119 providerService = providerRegistry.register(this); 121 providerService = providerRegistry.register(this);
120 controller.addListener(listener); 122 controller.addListener(listener);
121 controller.addEventListener(listener); 123 controller.addEventListener(listener);
124 +
125 + for (OpenFlowSwitch sw : controller.getSwitches()) {
126 + FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL);
127 + fsc.start();
128 + collectors.put(new Dpid(sw.getId()), fsc);
129 + }
130 +
131 +
122 log.info("Started"); 132 log.info("Started");
123 } 133 }
124 134
...@@ -217,7 +227,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -217,7 +227,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
217 private class InternalFlowProvider 227 private class InternalFlowProvider
218 implements OpenFlowSwitchListener, OpenFlowEventListener { 228 implements OpenFlowSwitchListener, OpenFlowEventListener {
219 229
220 - private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); 230 +
221 private final Multimap<DeviceId, FlowEntry> completeEntries = 231 private final Multimap<DeviceId, FlowEntry> completeEntries =
222 ArrayListMultimap.create(); 232 ArrayListMultimap.create();
223 233
...@@ -237,6 +247,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -237,6 +247,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
237 } 247 }
238 248
239 @Override 249 @Override
250 + public void switchChanged(Dpid dpid) {
251 + }
252 +
253 + @Override
240 public void portChanged(Dpid dpid, OFPortStatus status) { 254 public void portChanged(Dpid dpid, OFPortStatus status) {
241 //TODO: Decide whether to evict flows internal store. 255 //TODO: Decide whether to evict flows internal store.
242 } 256 }
...@@ -317,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -317,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
317 } 331 }
318 return false; 332 return false;
319 } 333 }
334 +
320 } 335 }
321 336
322 private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> { 337 private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> {
......
...@@ -118,6 +118,12 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid ...@@ -118,6 +118,12 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid
118 DeviceId.deviceId("of:" + Long.toHexString(dpid.value()))); 118 DeviceId.deviceId("of:" + Long.toHexString(dpid.value())));
119 } 119 }
120 120
121 +
122 + @Override
123 + public void switchChanged(Dpid dpid) {
124 + //might not need to do anything since DeviceManager is notified
125 + }
126 +
121 @Override 127 @Override
122 public void portChanged(Dpid dpid, OFPortStatus status) { 128 public void portChanged(Dpid dpid, OFPortStatus status) {
123 LinkDiscovery ld = discoverers.get(dpid); 129 LinkDiscovery ld = discoverers.get(dpid);
......
...@@ -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
...@@ -16,58 +16,95 @@ from functools import partial ...@@ -16,58 +16,95 @@ from functools import partial
16 import time 16 import time
17 from sys import argv 17 from sys import argv
18 from time import sleep 18 from time import sleep
19 +from sets import Set
19 20
20 class ONOS( Controller ): 21 class ONOS( Controller ):
21 - #def __init__( self, name, command='/opt/onos/bin/onos-service', **kwargs ): 22 + "TODO"
22 - # Controller.__init__( self, name, command=command, inNamespace=True, **kwargs ) 23 +
23 - #def __init__( self, name, inNamespace=False, command='controller', 24 + onosDir = '/opt/onos/'
24 - # cargs='-v ptcp:%d', cdir=None, ip="127.0.0.1", 25 +
25 - # port=6633, protocol='tcp', **params ): 26 + def __init__( self, name, onosDir=onosDir,
26 - #self.command = command 27 + reactive=True, features=[ 'onos-app-tvue' ],
27 - #self.cargs = cargs 28 + **kwargs ):
28 - #self.cdir = cdir 29 + '''TODO'''
29 - #self.ip = ip 30 +
30 - #self.port = port 31 + Controller.__init__( self, name, **kwargs )
31 - #self.protocol = protocol 32 + # the following have been done for us:
32 - #Node.__init__( self, name, inNamespace=inNamespace, 33 + #self.ip = ip ('127.0.0.1')
33 - # ip=ip, **params ) 34 + #self.port = port (6633)
35 + #self.protocol = protocol ('tcp')
34 #self.checkListening() 36 #self.checkListening()
35 37
36 - ONOS_DIR = '/opt/onos/' 38 + self.onosDir = onosDir
37 - KARAF_DIR = ONOS_DIR + 'apache-karaf-3.0.1/' 39 + self.karafDir = onosDir + 'apache-karaf-3.0.1/'
38 - reactive = True 40 + self.instanceDir = self.karafDir
41 +
42 + # add default modules
43 + # TODO: consider an ordered set
44 + self.features = Set([ 'webconsole',
45 + 'onos-api',
46 + 'onos-cli',
47 + 'onos-openflow' ])
48 + self.features.update( features )
49 + # add reactive forwarding modules
50 + if reactive:
51 + self.features.update( ['onos-app-fwd',
52 + 'onos-app-proxyarp',
53 + 'onos-app-mobility' ] )
54 + # add the distributed core if we are in a namespace with no trivial core
55 + if self.inNamespace and 'onos-core-trivial' not in self.features:
56 + self.features.add( 'onos-core' )
57 + # if there is no core, add the trivial one
58 + if 'onos-core' not in self.features:
59 + self.features.add( 'onos-core-trivial' )
60 + print self.features
39 61
40 def start( self ): 62 def start( self ):
41 - # switch to the non-root user because karaf gets upset otherwise
42 - # TODO we should look into why....
43 - self.sendCmd( 'sudo su - %s' % self.findUser() )
44 - self.waiting = False
45 -
46 if self.inNamespace: 63 if self.inNamespace:
47 - self.cmd( self.KARAF_DIR + 'bin/instance create %s' % self.name ) 64 + instanceOpts = ( '-furl mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features '
48 - src = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg' 65 + '-s 8101' )
49 - dst = self.KARAF_DIR + 'instances/%s/etc/org.apache.karaf.features.cfg' % self.name 66 + self.userCmd( self.karafDir + 'bin/instance create %s %s' % ( instanceOpts, self.name ) )
50 - self.cmd( 'cp %s %s' % (src, dst) ) 67 + self.instanceDir = self.karafDir + 'instances/%s/' % self.name
51 - self.updateProperties( dst )
52 - self.cmd( self.KARAF_DIR + 'bin/instance start %s' % self.name )
53 else: 68 else:
54 # we are running in the root namespace, so let's use the root instance 69 # we are running in the root namespace, so let's use the root instance
55 - self.cmd( 'rm -rf '+ self.KARAF_DIR + 'data/' ) 70 + # clean up the data directory
56 - filename = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg' 71 + #self.userCmd( 'rm -rf '+ self.karafDir + 'data/' )
57 - self.updateProperties( filename ) 72 + pass
58 - self.cmd( self.KARAF_DIR + 'bin/start' ) 73 +
74 + self.userCmd( 'rm -rf '+ self.instanceDir + 'data/' )
75 +
76 + # Update etc/org.apache.karaf.features.cfg
77 + self.updateFeatures()
78 +
79 + # TODO 2. Update etc/hazelcast.xml : interface lines
80 + #cp etc/hazelcast.xml instances/c1/etc/
81 + self.updateHazelcast()
59 82
83 + # TODO 3. Update etc/system.properties : onos.ip
84 + # TODO 4. Update config/cluster.json : with all nodes
85 +
86 + # start onos
87 + self.userCmd( self.instanceDir + 'bin/start' )
60 #TODO we should wait for startup... 88 #TODO we should wait for startup...
61 89
62 def stop( self ): 90 def stop( self ):
63 - if self.inNamespace: 91 + self.userCmd( self.instanceDir + 'bin/stop' )
64 - self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance stop %s' % self.name ) 92 + #if self.inNamespace:
65 - self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance destroy %s' % self.name ) 93 + # self.userCmd( self.karafDir + 'bin/instance destroy %s' % self.name )
66 - else:
67 - self.cmd( self.ONOS_DIR + 'apache-karaf-3.0.1/bin/stop' )
68 self.terminate() 94 self.terminate()
69 95
70 - def updateProperties( self, filename ): 96 + def updateHazelcast( self ):
97 + readfile = self.karafDir + 'etc/hazelcast.xml'
98 + writefile = self.instanceDir + 'etc/hazelcast.xml'
99 + with open( readfile, 'r' ) as r:
100 + with open( writefile, 'w' ) as w:
101 + for line in r.readlines():
102 + if '<interface>' in line:
103 + line = '<interface>' + '192.168.123.*' + '</interface>\n'
104 + w.write( line )
105 +
106 + def updateFeatures( self ):
107 + filename = self.instanceDir + 'etc/org.apache.karaf.features.cfg'
71 with open( filename, 'r+' ) as f: 108 with open( filename, 'r+' ) as f:
72 lines = f.readlines() 109 lines = f.readlines()
73 f.seek(0) 110 f.seek(0)
...@@ -75,17 +112,25 @@ class ONOS( Controller ): ...@@ -75,17 +112,25 @@ class ONOS( Controller ):
75 for line in lines: 112 for line in lines:
76 #print '?', line, 113 #print '?', line,
77 if 'featuresBoot=' in line: 114 if 'featuresBoot=' in line:
78 - line = line.rstrip() 115 + # parse the features from the line
79 - #print ord(line[-1]), ord(line[-2]), ord(line[-3]) 116 + features = line.rstrip().split('=')[1].split(',')
80 - if self.reactive: 117 + # add the features to our features set
81 - line += ',onos-app-fwd' 118 + self.features.update( features )
82 - line += '\n' 119 + # generate the new features line
120 + line = 'featuresBoot=' + ','.join( self.features ) + '\n'
83 #print '!', line, 121 #print '!', line,
84 f.write( line ) 122 f.write( line )
85 123
124 +
86 @classmethod 125 @classmethod
87 def isAvailable( self ): 126 def isAvailable( self ):
88 - return quietRun( 'ls /opt/onos' ) 127 + return quietRun( 'ls %s' % self.onosDir )
128 +
129 + def userCmd( self, cmd ):
130 + # switch to the non-root user because karaf gets upset otherwise
131 + # because the .m2repo is not stored with root
132 + cmd = 'sudo -u %s %s' % ( self.findUser(), cmd )
133 + return self.cmd( cmd )
89 134
90 @staticmethod 135 @staticmethod
91 def findUser(): 136 def findUser():
...@@ -111,7 +156,7 @@ class ControlNetwork( Topo ): ...@@ -111,7 +156,7 @@ class ControlNetwork( Topo ):
111 # Connect everything to a single switch 156 # Connect everything to a single switch
112 cs0 = self.addSwitch( 'cs0' ) 157 cs0 = self.addSwitch( 'cs0' )
113 # Add hosts which will serve as data network controllers 158 # Add hosts which will serve as data network controllers
114 - for i in range( 0, n ): 159 + for i in range( 1, n+1 ):
115 c = self.addHost( 'c%s' % i, cls=dataController, 160 c = self.addHost( 'c%s' % i, cls=dataController,
116 inNamespace=True ) 161 inNamespace=True )
117 self.addLink( c, cs0 ) 162 self.addLink( c, cs0 )
...@@ -122,7 +167,7 @@ class ControlNetwork( Topo ): ...@@ -122,7 +167,7 @@ class ControlNetwork( Topo ):
122 167
123 class ONOSCluster( Controller ): 168 class ONOSCluster( Controller ):
124 # TODO 169 # TODO
125 - n = 4 170 + n = 3
126 171
127 def start( self ): 172 def start( self ):
128 ctopo = ControlNetwork( n=self.n, dataController=ONOS ) 173 ctopo = ControlNetwork( n=self.n, dataController=ONOS )
...@@ -137,6 +182,9 @@ class ONOSCluster( Controller ): ...@@ -137,6 +182,9 @@ class ONOSCluster( Controller ):
137 host.start() 182 host.start()
138 183
139 def stop( self ): 184 def stop( self ):
185 + for host in self.cnet.hosts:
186 + if isinstance( host, Controller ):
187 + host.stop()
140 self.cnet.stop() 188 self.cnet.stop()
141 189
142 def clist( self ): 190 def clist( self ):
...@@ -158,10 +206,11 @@ switches = { 'ovso': OVSSwitchONOS } ...@@ -158,10 +206,11 @@ switches = { 'ovso': OVSSwitchONOS }
158 206
159 if __name__ == '__main__': 207 if __name__ == '__main__':
160 # Simple test for ONOS() controller class 208 # Simple test for ONOS() controller class
161 - setLogLevel( 'info' ) 209 + setLogLevel( 'info' ) #TODO info
162 size = 2 if len( argv ) != 2 else int( argv[ 1 ] ) 210 size = 2 if len( argv ) != 2 else int( argv[ 1 ] )
163 net = Mininet( topo=LinearTopo( size ), 211 net = Mininet( topo=LinearTopo( size ),
164 - controller=partial( ONOSCluster, n=4 ), 212 + #controller=ONOS,
213 + controller=partial( ONOSCluster, n=3 ), #TODO
165 switch=OVSSwitchONOS ) 214 switch=OVSSwitchONOS )
166 net.start() 215 net.start()
167 #waitConnected( net.switches ) 216 #waitConnected( net.switches )
......
...@@ -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);
......
...@@ -6,6 +6,7 @@ import com.google.common.collect.MutableClassToInstanceMap; ...@@ -6,6 +6,7 @@ import com.google.common.collect.MutableClassToInstanceMap;
6 /** 6 /**
7 * Service directory implementation suitable for testing. 7 * Service directory implementation suitable for testing.
8 */ 8 */
9 +@SuppressWarnings("unchecked")
9 public class TestServiceDirectory implements ServiceDirectory { 10 public class TestServiceDirectory implements ServiceDirectory {
10 11
11 private ClassToInstanceMap<Object> services = MutableClassToInstanceMap.create(); 12 private ClassToInstanceMap<Object> services = MutableClassToInstanceMap.create();
......
...@@ -23,13 +23,6 @@ ...@@ -23,13 +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 - <version>17.0</version>
30 - <scope>test</scope>
31 - </dependency>
32 -
33 </dependencies> 26 </dependencies>
34 27
35 <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
1 +/*
2 + Geometry library - based on work by Mike Bostock.
3 + */
4 +
5 +(function() {
6 +
7 + if (typeof geo == 'undefined') {
8 + geo = {};
9 + }
10 +
11 + var tolerance = 1e-10;
12 +
13 + function eq(a, b) {
14 + return (Math.abs(a - b) < tolerance);
15 + }
16 +
17 + function gt(a, b) {
18 + return (a - b > -tolerance);
19 + }
20 +
21 + function lt(a, b) {
22 + return gt(b, a);
23 + }
24 +
25 + geo.eq = eq;
26 + geo.gt = gt;
27 + geo.lt = lt;
28 +
29 + geo.LineSegment = function(x1, y1, x2, y2) {
30 + this.x1 = x1;
31 + this.y1 = y1;
32 + this.x2 = x2;
33 + this.y2 = y2;
34 +
35 + // Ax + By = C
36 + this.a = y2 - y1;
37 + this.b = x1 - x2;
38 + this.c = x1 * this.a + y1 * this.b;
39 +
40 + if (eq(this.a, 0) && eq(this.b, 0)) {
41 + throw new Error(
42 + 'Cannot construct a LineSegment with two equal endpoints.');
43 + }
44 + };
45 +
46 + geo.LineSegment.prototype.intersect = function(that) {
47 + var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
48 + (this.y1 - this.y2) * (that.x1 - that.x2);
49 +
50 + if (eq(d, 0)) {
51 + // The two lines are parallel or very close.
52 + return {
53 + x : NaN,
54 + y : NaN
55 + };
56 + }
57 +
58 + var t1 = this.x1 * this.y2 - this.y1 * this.x2,
59 + t2 = that.x1 * that.y2 - that.y1 * that.x2,
60 + x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
61 + y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
62 + in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
63 + gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
64 + in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
65 + gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));
66 +
67 + return {
68 + x : x,
69 + y : y,
70 + in1 : in1,
71 + in2 : in2
72 + };
73 + };
74 +
75 + geo.LineSegment.prototype.x = function(y) {
76 + // x = (C - By) / a;
77 + if (this.a) {
78 + return (this.c - this.b * y) / this.a;
79 + } else {
80 + // a == 0 -> horizontal line
81 + return NaN;
82 + }
83 + };
84 +
85 + geo.LineSegment.prototype.y = function(x) {
86 + // y = (C - Ax) / b;
87 + if (this.b) {
88 + return (this.c - this.a * x) / this.b;
89 + } else {
90 + // b == 0 -> vertical line
91 + return NaN;
92 + }
93 + };
94 +
95 + geo.LineSegment.prototype.length = function() {
96 + return Math.sqrt(
97 + (this.y2 - this.y1) * (this.y2 - this.y1) +
98 + (this.x2 - this.x1) * (this.x2 - this.x1));
99 + };
100 +
101 + geo.LineSegment.prototype.offset = function(x, y) {
102 + return new geo.LineSegment(
103 + this.x1 + x, this.y1 + y,
104 + this.x2 + x, this.y2 + y);
105 + };
106 +
107 +})();
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
14 <link rel="stylesheet" href="base.css"> 14 <link rel="stylesheet" href="base.css">
15 <link rel="stylesheet" href="onos.css"> 15 <link rel="stylesheet" href="onos.css">
16 16
17 + <script src="geometry.js"></script>
17 <script src="onosui.js"></script> 18 <script src="onosui.js"></script>
18 19
19 </head> 20 </head>
...@@ -32,20 +33,20 @@ ...@@ -32,20 +33,20 @@
32 <div id="view"></div> 33 <div id="view"></div>
33 </div> 34 </div>
34 35
35 - // Initialize the UI... 36 + <!-- Initialize the UI...-->
36 <script type="text/javascript"> 37 <script type="text/javascript">
37 var ONOS = $.onos({note: "config, if needed"}); 38 var ONOS = $.onos({note: "config, if needed"});
38 </script> 39 </script>
39 40
40 - // include module files 41 + <!-- include module files-->
41 - // + mast.js 42 + <!-- + mast.js-->
42 - // + nav.js 43 + <!-- + nav.js-->
43 - // + .... application views 44 + <!-- + .... application views-->
44 45
45 - // for now, we are just bootstrapping the network visualization 46 + <!-- for now, we are just bootstrapping the network visualization-->
46 <script src="network.js" type="text/javascript"></script> 47 <script src="network.js" type="text/javascript"></script>
47 48
48 - // finally, build the UI 49 + <!-- finally, build the UI-->
49 <script type="text/javascript"> 50 <script type="text/javascript">
50 $(ONOS.buildUi); 51 $(ONOS.buildUi);
51 </script> 52 </script>
......
...@@ -10,11 +10,16 @@ ...@@ -10,11 +10,16 @@
10 var api = onos.api; 10 var api = onos.api;
11 11
12 var config = { 12 var config = {
13 + layering: false,
13 jsonUrl: 'network.json', 14 jsonUrl: 'network.json',
15 + iconUrl: {
16 + pkt: 'pkt.png',
17 + opt: 'opt.png'
18 + },
14 mastHeight: 32, 19 mastHeight: 32,
15 force: { 20 force: {
16 - linkDistance: 150, 21 + linkDistance: 240,
17 - linkStrength: 0.9, 22 + linkStrength: 0.8,
18 charge: -400, 23 charge: -400,
19 ticksWithoutCollisions: 50, 24 ticksWithoutCollisions: 50,
20 marginLR: 20, 25 marginLR: 20,
...@@ -26,8 +31,9 @@ ...@@ -26,8 +31,9 @@
26 } 31 }
27 }, 32 },
28 labels: { 33 labels: {
29 - padLR: 3, 34 + imgPad: 22,
30 - padTB: 2, 35 + padLR: 8,
36 + padTB: 6,
31 marginLR: 3, 37 marginLR: 3,
32 marginTB: 2 38 marginTB: 2
33 }, 39 },
...@@ -53,7 +59,7 @@ ...@@ -53,7 +59,7 @@
53 d3.json(config.jsonUrl, function (err, data) { 59 d3.json(config.jsonUrl, function (err, data) {
54 if (err) { 60 if (err) {
55 alert('Oops! Error reading JSON...\n\n' + 61 alert('Oops! Error reading JSON...\n\n' +
56 - 'URL: ' + jsonUrl + '\n\n' + 62 + 'URL: ' + config.jsonUrl + '\n\n' +
57 'Error: ' + err.message); 63 'Error: ' + err.message);
58 return; 64 return;
59 } 65 }
...@@ -100,7 +106,7 @@ ...@@ -100,7 +106,7 @@
100 106
101 network.data.nodes.forEach(function(n) { 107 network.data.nodes.forEach(function(n) {
102 var ypc = yPosConstraintForNode(n), 108 var ypc = yPosConstraintForNode(n),
103 - ix = Math.random() * 0.8 * nw + 0.1 * nw, 109 + ix = Math.random() * 0.6 * nw + 0.2 * nw,
104 iy = ypc * nh, 110 iy = ypc * nh,
105 node = { 111 node = {
106 id: n.id, 112 id: n.id,
...@@ -153,10 +159,48 @@ ...@@ -153,10 +159,48 @@
153 .attr('height', view.height) 159 .attr('height', view.height)
154 .append('g') 160 .append('g')
155 .attr('transform', config.force.translate()); 161 .attr('transform', config.force.translate());
162 +// .attr('id', 'zoomable')
163 +// .call(d3.behavior.zoom().on("zoom", zoomRedraw));
164 +
165 +// function zoomRedraw() {
166 +// d3.select("#zoomable").attr("transform",
167 +// "translate(" + d3.event.translate + ")"
168 +// + " scale(" + d3.event.scale + ")");
169 +// }
170 +
171 + // TODO: svg.append('defs') for markers?
172 +
173 + // TODO: move glow/blur stuff to util script
174 + var glow = network.svg.append('filter')
175 + .attr('x', '-50%')
176 + .attr('y', '-50%')
177 + .attr('width', '200%')
178 + .attr('height', '200%')
179 + .attr('id', 'blue-glow');
180 +
181 + glow.append('feColorMatrix')
182 + .attr('type', 'matrix')
183 + .attr('values', '0 0 0 0 0 ' +
184 + '0 0 0 0 0 ' +
185 + '0 0 0 0 .7 ' +
186 + '0 0 0 1 0 ');
187 +
188 + glow.append('feGaussianBlur')
189 + .attr('stdDeviation', 3)
190 + .attr('result', 'coloredBlur');
191 +
192 + glow.append('feMerge').selectAll('feMergeNode')
193 + .data(['coloredBlur', 'SourceGraphic'])
194 + .enter().append('feMergeNode')
195 + .attr('in', String);
156 196
157 - // TODO: svg.append('defs')
158 - // TODO: glow/blur stuff
159 // TODO: legend (and auto adjust on scroll) 197 // TODO: legend (and auto adjust on scroll)
198 +// $('#view').on('scroll', function() {
199 +//
200 +// });
201 +
202 +
203 +
160 204
161 network.link = network.svg.append('g').selectAll('.link') 205 network.link = network.svg.append('g').selectAll('.link')
162 .data(network.force.links(), function(d) {return d.id}) 206 .data(network.force.links(), function(d) {return d.id})
...@@ -164,37 +208,103 @@ ...@@ -164,37 +208,103 @@
164 .attr('class', 'link'); 208 .attr('class', 'link');
165 209
166 // TODO: drag behavior 210 // TODO: drag behavior
167 - // TODO: closest node deselect 211 + network.draggedThreshold = d3.scale.linear()
212 + .domain([0, 0.1])
213 + .range([5, 20])
214 + .clamp(true);
215 +
216 + function dragged(d) {
217 + var threshold = network.draggedThreshold(network.force.alpha()),
218 + dx = d.oldX - d.px,
219 + dy = d.oldY - d.py;
220 + if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
221 + d.dragged = true;
222 + }
223 + return d.dragged;
224 + }
225 +
226 + network.drag = d3.behavior.drag()
227 + .origin(function(d) { return d; })
228 + .on('dragstart', function(d) {
229 + d.oldX = d.x;
230 + d.oldY = d.y;
231 + d.dragged = false;
232 + d.fixed |= 2;
233 + })
234 + .on('drag', function(d) {
235 + d.px = d3.event.x;
236 + d.py = d3.event.y;
237 + if (dragged(d)) {
238 + if (!network.force.alpha()) {
239 + network.force.alpha(.025);
240 + }
241 + }
242 + })
243 + .on('dragend', function(d) {
244 + if (!dragged(d)) {
245 + selectObject(d, this);
246 + }
247 + d.fixed &= ~6;
248 + });
249 +
250 + $('#view').on('click', function(e) {
251 + if (!$(e.target).closest('.node').length) {
252 + deselectObject();
253 + }
254 + });
255 +
168 256
169 - // TODO: add drag, mouseover, mouseout behaviors
170 network.node = network.svg.selectAll('.node') 257 network.node = network.svg.selectAll('.node')
171 .data(network.force.nodes(), function(d) {return d.id}) 258 .data(network.force.nodes(), function(d) {return d.id})
172 .enter().append('g') 259 .enter().append('g')
173 - .attr('class', 'node') 260 + .attr('class', function(d) {
261 + return 'node ' + d.type;
262 + })
174 .attr('transform', function(d) { 263 .attr('transform', function(d) {
175 return translate(d.x, d.y); 264 return translate(d.x, d.y);
176 }) 265 })
177 - // .call(network.drag) 266 + .call(network.drag)
178 - .on('mouseover', function(d) {}) 267 + .on('mouseover', function(d) {
179 - .on('mouseout', function(d) {}); 268 + if (!selected.obj) {
269 + if (network.mouseoutTimeout) {
270 + clearTimeout(network.mouseoutTimeout);
271 + network.mouseoutTimeout = null;
272 + }
273 + highlightObject(d);
274 + }
275 + })
276 + .on('mouseout', function(d) {
277 + if (!selected.obj) {
278 + if (network.mouseoutTimeout) {
279 + clearTimeout(network.mouseoutTimeout);
280 + network.mouseoutTimeout = null;
281 + }
282 + network.mouseoutTimeout = setTimeout(function() {
283 + highlightObject(null);
284 + }, 160);
285 + }
286 + });
180 287
181 - // TODO: augment stroke and fill functions
182 network.nodeRect = network.node.append('rect') 288 network.nodeRect = network.node.append('rect')
183 - // TODO: css for node rects
184 .attr('rx', 5) 289 .attr('rx', 5)
185 .attr('ry', 5) 290 .attr('ry', 5)
186 - .attr('stroke', function(d) { return '#000'}) 291 + .attr('width', 126)
187 - .attr('fill', function(d) { return '#ddf'}) 292 + .attr('height', 40);
188 - .attr('width', 60)
189 - .attr('height', 24);
190 293
191 network.node.each(function(d) { 294 network.node.each(function(d) {
192 var node = d3.select(this), 295 var node = d3.select(this),
193 - rect = node.select('rect'); 296 + rect = node.select('rect'),
194 - var text = node.append('text') 297 + img = node.append('svg:image')
298 + .attr('x', -16)
299 + .attr('y', -16)
300 + .attr('width', 32)
301 + .attr('height', 32)
302 + .attr('xlink:href', iconUrl(d)),
303 + text = node.append('text')
195 .text(d.id) 304 .text(d.id)
196 - .attr('dx', '1em') 305 + .attr('dy', '1.1em'),
197 - .attr('dy', '2.1em'); 306 + dummy;
307 +
198 }); 308 });
199 309
200 // this function is scheduled to happen soon after the given thread ends 310 // this function is scheduled to happen soon after the given thread ends
...@@ -207,12 +317,64 @@ ...@@ -207,12 +317,64 @@
207 first = true; 317 first = true;
208 318
209 // NOTE: probably unnecessary code if we only have one line. 319 // NOTE: probably unnecessary code if we only have one line.
320 + text.each(function() {
321 + var box = this.getBBox();
322 + if (first || box.x < bounds.x1) {
323 + bounds.x1 = box.x;
324 + }
325 + if (first || box.y < bounds.y1) {
326 + bounds.y1 = box.y;
327 + }
328 + if (first || box.x + box.width < bounds.x2) {
329 + bounds.x2 = box.x + box.width;
330 + }
331 + if (first || box.y + box.height < bounds.y2) {
332 + bounds.y2 = box.y + box.height;
333 + }
334 + first = false;
335 + }).attr('text-anchor', 'middle');
336 +
337 + var lab = config.labels,
338 + oldWidth = bounds.x2 - bounds.x1;
339 +
340 + bounds.x1 -= oldWidth / 2;
341 + bounds.x2 -= oldWidth / 2;
342 +
343 + bounds.x1 -= (lab.padLR + lab.imgPad);
344 + bounds.y1 -= lab.padTB;
345 + bounds.x2 += lab.padLR;
346 + bounds.y2 += lab.padTB;
347 +
348 + node.select('rect')
349 + .attr('x', bounds.x1)
350 + .attr('y', bounds.y1)
351 + .attr('width', bounds.x2 - bounds.x1)
352 + .attr('height', bounds.y2 - bounds.y1);
353 +
354 + node.select('image')
355 + .attr('x', bounds.x1);
356 +
357 + d.extent = {
358 + left: bounds.x1 - lab.marginLR,
359 + right: bounds.x2 + lab.marginLR,
360 + top: bounds.y1 - lab.marginTB,
361 + bottom: bounds.y2 + lab.marginTB
362 + };
363 +
364 + d.edge = {
365 + left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2),
366 + right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2),
367 + top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1),
368 + bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2)
369 + };
370 +
371 + // ====
210 }); 372 });
211 373
212 network.numTicks = 0; 374 network.numTicks = 0;
213 network.preventCollisions = false; 375 network.preventCollisions = false;
214 network.force.start(); 376 network.force.start();
215 - for (var i = 0; i < config.ticksWithoutCollisions; i++) { 377 + for (var i = 0; i < config.force.ticksWithoutCollisions; i++) {
216 network.force.tick(); 378 network.force.tick();
217 } 379 }
218 network.preventCollisions = true; 380 network.preventCollisions = true;
...@@ -221,22 +383,78 @@ ...@@ -221,22 +383,78 @@
221 383
222 } 384 }
223 385
386 + function iconUrl(d) {
387 + return config.iconUrl[d.type];
388 + }
389 +
224 function translate(x, y) { 390 function translate(x, y) {
225 return 'translate(' + x + ',' + y + ')'; 391 return 'translate(' + x + ',' + y + ')';
226 } 392 }
227 393
394 + function preventCollisions() {
395 + var quadtree = d3.geom.quadtree(network.nodes);
396 +
397 + network.nodes.forEach(function(n) {
398 + var nx1 = n.x + n.extent.left,
399 + nx2 = n.x + n.extent.right,
400 + ny1 = n.y + n.extent.top,
401 + ny2 = n.y + n.extent.bottom;
402 +
403 + quadtree.visit(function(quad, x1, y1, x2, y2) {
404 + if (quad.point && quad.point !== n) {
405 + // check if the rectangles intersect
406 + var p = quad.point,
407 + px1 = p.x + p.extent.left,
408 + px2 = p.x + p.extent.right,
409 + py1 = p.y + p.extent.top,
410 + py2 = p.y + p.extent.bottom,
411 + ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2);
412 + if (ix) {
413 + var xa1 = nx2 - px1, // shift n left , p right
414 + xa2 = px2 - nx1, // shift n right, p left
415 + ya1 = ny2 - py1, // shift n up , p down
416 + ya2 = py2 - ny1, // shift n down , p up
417 + adj = Math.min(xa1, xa2, ya1, ya2);
418 +
419 + if (adj == xa1) {
420 + n.x -= adj / 2;
421 + p.x += adj / 2;
422 + } else if (adj == xa2) {
423 + n.x += adj / 2;
424 + p.x -= adj / 2;
425 + } else if (adj == ya1) {
426 + n.y -= adj / 2;
427 + p.y += adj / 2;
428 + } else if (adj == ya2) {
429 + n.y += adj / 2;
430 + p.y -= adj / 2;
431 + }
432 + }
433 + return ix;
434 + }
435 + });
436 +
437 + });
438 + }
228 439
229 function tick(e) { 440 function tick(e) {
230 network.numTicks++; 441 network.numTicks++;
231 442
443 + if (config.layering) {
232 // adjust the y-coord of each node, based on y-pos constraints 444 // adjust the y-coord of each node, based on y-pos constraints
233 -// network.nodes.forEach(function (n) { 445 + network.nodes.forEach(function (n) {
234 -// var z = e.alpha * n.constraint.weight; 446 + var z = e.alpha * n.constraint.weight;
235 -// if (!isNaN(n.constraint.y)) { 447 + if (!isNaN(n.constraint.y)) {
236 -// n.y = (n.constraint.y * z + n.y * (1 - z)); 448 + n.y = (n.constraint.y * z + n.y * (1 - z));
237 -// } 449 + }
238 -// }); 450 + });
451 + }
452 +
453 + if (network.preventCollisions) {
454 + preventCollisions();
455 + }
239 456
457 + // TODO: use intersection technique for source end of link also
240 network.link 458 network.link
241 .attr('x1', function(d) { 459 .attr('x1', function(d) {
242 return d.source.x; 460 return d.source.x;
...@@ -244,11 +462,24 @@ ...@@ -244,11 +462,24 @@
244 .attr('y1', function(d) { 462 .attr('y1', function(d) {
245 return d.source.y; 463 return d.source.y;
246 }) 464 })
247 - .attr('x2', function(d) { 465 + .each(function(d) {
248 - return d.target.x; 466 + var x = d.target.x,
249 - }) 467 + y = d.target.y,
250 - .attr('y2', function(d) { 468 + line = new geo.LineSegment(d.source.x, d.source.y, x, y);
251 - return d.target.y; 469 +
470 + for (var e in d.target.edge) {
471 + var ix = line.intersect(d.target.edge[e].offset(x,y));
472 + if (ix.in1 && ix.in2) {
473 + x = ix.x;
474 + y = ix.y;
475 + break;
476 + }
477 + }
478 +
479 + d3.select(this)
480 + .attr('x2', x)
481 + .attr('y2', y);
482 +
252 }); 483 });
253 484
254 network.node 485 network.node
......
...@@ -7,50 +7,50 @@ ...@@ -7,50 +7,50 @@
7 }, 7 },
8 "nodes": [ 8 "nodes": [
9 { 9 {
10 - "id": "switch-1", 10 + "id": "sample1",
11 "type": "opt", 11 "type": "opt",
12 "status": "good" 12 "status": "good"
13 }, 13 },
14 { 14 {
15 - "id": "switch-2", 15 + "id": "00:00:00:00:00:00:00:02",
16 "type": "opt", 16 "type": "opt",
17 "status": "good" 17 "status": "good"
18 }, 18 },
19 { 19 {
20 - "id": "switch-3", 20 + "id": "00:00:00:00:00:00:00:03",
21 "type": "opt", 21 "type": "opt",
22 "status": "good" 22 "status": "good"
23 }, 23 },
24 { 24 {
25 - "id": "switch-4", 25 + "id": "00:00:00:00:00:00:00:04",
26 "type": "opt", 26 "type": "opt",
27 "status": "good" 27 "status": "good"
28 }, 28 },
29 { 29 {
30 - "id": "switch-11", 30 + "id": "00:00:00:00:00:00:00:11",
31 "type": "pkt", 31 "type": "pkt",
32 "status": "good" 32 "status": "good"
33 }, 33 },
34 { 34 {
35 - "id": "switch-12", 35 + "id": "00:00:00:00:00:00:00:12",
36 "type": "pkt", 36 "type": "pkt",
37 "status": "good" 37 "status": "good"
38 }, 38 },
39 { 39 {
40 - "id": "switch-13", 40 + "id": "00:00:00:00:00:00:00:13",
41 "type": "pkt", 41 "type": "pkt",
42 "status": "good" 42 "status": "good"
43 } 43 }
44 ], 44 ],
45 "links": [ 45 "links": [
46 - { "src": "switch-1", "dst": "switch-2" }, 46 + { "src": "sample1", "dst": "00:00:00:00:00:00:00:02" },
47 - { "src": "switch-1", "dst": "switch-3" }, 47 + { "src": "sample1", "dst": "00:00:00:00:00:00:00:03" },
48 - { "src": "switch-1", "dst": "switch-4" }, 48 + { "src": "sample1", "dst": "00:00:00:00:00:00:00:04" },
49 - { "src": "switch-2", "dst": "switch-3" }, 49 + { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:03" },
50 - { "src": "switch-2", "dst": "switch-4" }, 50 + { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:04" },
51 - { "src": "switch-3", "dst": "switch-4" }, 51 + { "src": "00:00:00:00:00:00:00:03", "dst": "00:00:00:00:00:00:00:04" },
52 - { "src": "switch-13", "dst": "switch-3" }, 52 + { "src": "00:00:00:00:00:00:00:13", "dst": "00:00:00:00:00:00:00:03" },
53 - { "src": "switch-12", "dst": "switch-2" }, 53 + { "src": "00:00:00:00:00:00:00:12", "dst": "00:00:00:00:00:00:00:02" },
54 - { "src": "switch-11", "dst": "switch-1" } 54 + { "src": "00:00:00:00:00:00:00:11", "dst": "sample1" }
55 ] 55 ]
56 } 56 }
......
...@@ -13,7 +13,7 @@ body, html { ...@@ -13,7 +13,7 @@ body, html {
13 */ 13 */
14 14
15 span.title { 15 span.title {
16 - color: red; 16 + color: darkblue;
17 font-size: 16pt; 17 font-size: 16pt;
18 font-style: italic; 18 font-style: italic;
19 } 19 }
...@@ -30,7 +30,7 @@ span.right { ...@@ -30,7 +30,7 @@ span.right {
30 * === DEBUGGING ====== 30 * === DEBUGGING ======
31 */ 31 */
32 svg { 32 svg {
33 - border: 1px dashed red; 33 + /*border: 1px dashed red;*/
34 } 34 }
35 35
36 36
...@@ -64,36 +64,47 @@ marker#end { ...@@ -64,36 +64,47 @@ marker#end {
64 -moz-transition: opacity 250ms; 64 -moz-transition: opacity 250ms;
65 } 65 }
66 66
67 -.node text { 67 +/*differentiate between packet and optical nodes*/
68 - fill: #000; 68 +svg .node.pkt rect {
69 + fill: #77a;
70 +}
71 +
72 +svg .node.opt rect {
73 + fill: #7a7;
74 +}
75 +
76 +svg .node text {
77 + fill: white;
69 font: 10px sans-serif; 78 font: 10px sans-serif;
70 pointer-events: none; 79 pointer-events: none;
71 } 80 }
72 81
73 -.node.selected rect { 82 +svg .node.selected rect {
74 filter: url(#blue-glow); 83 filter: url(#blue-glow);
75 } 84 }
76 85
77 -.link.inactive, 86 +svg .link.inactive,
78 -.node.inactive rect, 87 +svg .node.inactive rect,
79 -.node.inactive text { 88 +svg .node.inactive text,
89 +svg .node.inactive image {
80 opacity: .2; 90 opacity: .2;
81 } 91 }
82 92
83 -.node.inactive.selected rect, 93 +svg .node.inactive.selected rect,
84 -.node.inactive.selected text { 94 +svg .node.inactive.selected text,
95 +svg .node.inactive.selected image {
85 opacity: .6; 96 opacity: .6;
86 } 97 }
87 98
88 -.legend { 99 +svg .legend {
89 position: fixed; 100 position: fixed;
90 } 101 }
91 102
92 -.legend .category rect { 103 +svg .legend .category rect {
93 stroke-width: 1px; 104 stroke-width: 1px;
94 } 105 }
95 106
96 -.legend .category text { 107 +svg .legend .category text {
97 fill: #000; 108 fill: #000;
98 font: 10px sans-serif; 109 font: 10px sans-serif;
99 pointer-events: none; 110 pointer-events: none;
...@@ -110,15 +121,15 @@ marker#end { ...@@ -110,15 +121,15 @@ marker#end {
110 #frame { 121 #frame {
111 width: 100%; 122 width: 100%;
112 height: 100%; 123 height: 100%;
113 - background-color: #ffd; 124 + background-color: #cdf;
114 } 125 }
115 126
116 #mast { 127 #mast {
117 height: 32px; 128 height: 32px;
118 - background-color: #dda; 129 + background-color: #abe;
119 vertical-align: baseline; 130 vertical-align: baseline;
120 } 131 }
121 132
122 #main { 133 #main {
123 - background-color: #99b; 134 + background-color: #99c;
124 } 135 }
......
...@@ -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>
......