Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
138 changed files
with
4475 additions
and
510 deletions
apps/metrics/intent/pom.xml
0 → 100644
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 | +} |
apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/IntentMetricsService.java
0 → 100644
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 | +} |
apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsListCommand.java
0 → 100644
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 | +} |
apps/metrics/intent/src/main/java/org/onlab/onos/metrics/intent/cli/IntentEventsMetricsCommand.java
0 → 100644
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 | +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> | ||
2 | + | ||
3 | + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> | ||
4 | + <command> | ||
5 | + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsListCommand"/> | ||
6 | + </command> | ||
7 | + <command> | ||
8 | + <action class="org.onlab.onos.metrics.intent.cli.IntentEventsMetricsCommand"/> | ||
9 | + </command> | ||
10 | + </command-bundle> | ||
11 | + | ||
12 | +</blueprint> |
... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
17 | <description>ONOS metrics applications</description> | 17 | <description>ONOS metrics applications</description> |
18 | 18 | ||
19 | <modules> | 19 | <modules> |
20 | + <module>intent</module> | ||
20 | <module>topology</module> | 21 | <module>topology</module> |
21 | </modules> | 22 | </modules> |
22 | 23 | ... | ... |
... | @@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent; | ... | @@ -18,6 +18,15 @@ import org.onlab.metrics.MetricsComponent; |
18 | import org.onlab.metrics.MetricsFeature; | 18 | import org.onlab.metrics.MetricsFeature; |
19 | import org.onlab.metrics.MetricsService; | 19 | import org.onlab.metrics.MetricsService; |
20 | import org.onlab.onos.event.Event; | 20 | import org.onlab.onos.event.Event; |
21 | +import org.onlab.onos.net.device.DeviceEvent; | ||
22 | +import org.onlab.onos.net.device.DeviceListener; | ||
23 | +import org.onlab.onos.net.device.DeviceService; | ||
24 | +import org.onlab.onos.net.host.HostEvent; | ||
25 | +import org.onlab.onos.net.host.HostListener; | ||
26 | +import org.onlab.onos.net.host.HostService; | ||
27 | +import org.onlab.onos.net.link.LinkEvent; | ||
28 | +import org.onlab.onos.net.link.LinkListener; | ||
29 | +import org.onlab.onos.net.link.LinkService; | ||
21 | import org.onlab.onos.net.topology.TopologyEvent; | 30 | import org.onlab.onos.net.topology.TopologyEvent; |
22 | import org.onlab.onos.net.topology.TopologyListener; | 31 | import org.onlab.onos.net.topology.TopologyListener; |
23 | import org.onlab.onos.net.topology.TopologyService; | 32 | import org.onlab.onos.net.topology.TopologyService; |
... | @@ -28,14 +37,26 @@ import org.slf4j.Logger; | ... | @@ -28,14 +37,26 @@ import org.slf4j.Logger; |
28 | */ | 37 | */ |
29 | @Component(immediate = true) | 38 | @Component(immediate = true) |
30 | @Service | 39 | @Service |
31 | -public class TopologyMetrics implements TopologyMetricsService, | 40 | +public class TopologyMetrics implements TopologyMetricsService { |
32 | - TopologyListener { | ||
33 | private static final Logger log = getLogger(TopologyMetrics.class); | 41 | private static final Logger log = getLogger(TopologyMetrics.class); |
34 | 42 | ||
35 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 43 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
44 | + protected DeviceService deviceService; | ||
45 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
46 | + protected HostService hostService; | ||
47 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
48 | + protected LinkService linkService; | ||
49 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
36 | protected TopologyService topologyService; | 50 | protected TopologyService topologyService; |
37 | - private LinkedList<TopologyEvent> lastEvents = new LinkedList<>(); | 51 | + |
38 | - private static final int LAST_EVENTS_MAX_N = 10; | 52 | + private LinkedList<Event> lastEvents = new LinkedList<>(); |
53 | + private static final int LAST_EVENTS_MAX_N = 100; | ||
54 | + | ||
55 | + private final DeviceListener deviceListener = new InnerDeviceListener(); | ||
56 | + private final HostListener hostListener = new InnerHostListener(); | ||
57 | + private final LinkListener linkListener = new InnerLinkListener(); | ||
58 | + private final TopologyListener topologyListener = | ||
59 | + new InnerTopologyListener(); | ||
39 | 60 | ||
40 | // | 61 | // |
41 | // Metrics | 62 | // Metrics |
... | @@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService, | ... | @@ -61,22 +82,33 @@ public class TopologyMetrics implements TopologyMetricsService, |
61 | protected void activate() { | 82 | protected void activate() { |
62 | clear(); | 83 | clear(); |
63 | registerMetrics(); | 84 | registerMetrics(); |
64 | - topologyService.addListener(this); | 85 | + |
86 | + // Register for all topology-related events | ||
87 | + deviceService.addListener(deviceListener); | ||
88 | + hostService.addListener(hostListener); | ||
89 | + linkService.addListener(linkListener); | ||
90 | + topologyService.addListener(topologyListener); | ||
91 | + | ||
65 | log.info("ONOS Topology Metrics started."); | 92 | log.info("ONOS Topology Metrics started."); |
66 | } | 93 | } |
67 | 94 | ||
68 | @Deactivate | 95 | @Deactivate |
69 | public void deactivate() { | 96 | public void deactivate() { |
70 | - topologyService.removeListener(this); | 97 | + // De-register from all topology-related events |
98 | + deviceService.removeListener(deviceListener); | ||
99 | + hostService.removeListener(hostListener); | ||
100 | + linkService.removeListener(linkListener); | ||
101 | + topologyService.removeListener(topologyListener); | ||
102 | + | ||
71 | removeMetrics(); | 103 | removeMetrics(); |
72 | clear(); | 104 | clear(); |
73 | log.info("ONOS Topology Metrics stopped."); | 105 | log.info("ONOS Topology Metrics stopped."); |
74 | } | 106 | } |
75 | 107 | ||
76 | @Override | 108 | @Override |
77 | - public List<TopologyEvent> getEvents() { | 109 | + public List<Event> getEvents() { |
78 | synchronized (lastEvents) { | 110 | synchronized (lastEvents) { |
79 | - return ImmutableList.<TopologyEvent>copyOf(lastEvents); | 111 | + return ImmutableList.<Event>copyOf(lastEvents); |
80 | } | 112 | } |
81 | } | 113 | } |
82 | 114 | ||
... | @@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService, | ... | @@ -90,27 +122,22 @@ public class TopologyMetrics implements TopologyMetricsService, |
90 | return eventRateMeter; | 122 | return eventRateMeter; |
91 | } | 123 | } |
92 | 124 | ||
93 | - @Override | 125 | + /** |
94 | - public void event(TopologyEvent event) { | 126 | + * Records an event. |
95 | - lastEventTimestampEpochMs = System.currentTimeMillis(); | 127 | + * |
96 | - // | 128 | + * @param event the event to record |
97 | - // NOTE: If we want to count each "reason" as a separate event, | 129 | + * @param updateEventRateMeter if true, update the Event Rate Meter |
98 | - // then we should use 'event.reason().size()' instead of '1' to | 130 | + */ |
99 | - // mark the meter below. | 131 | + private void recordEvent(Event event, boolean updateEventRateMeter) { |
100 | - // | ||
101 | - eventRateMeter.mark(1); | ||
102 | - | ||
103 | - log.debug("Topology Event: time = {} type = {} subject = {}", | ||
104 | - event.time(), event.type(), event.subject()); | ||
105 | - for (Event reason : event.reasons()) { | ||
106 | - log.debug("Topology Event Reason: time = {} type = {} subject = {}", | ||
107 | - reason.time(), reason.type(), reason.subject()); | ||
108 | - } | ||
109 | - | ||
110 | - // | ||
111 | - // Keep only the last N events, where N = LAST_EVENTS_MAX_N | ||
112 | - // | ||
113 | synchronized (lastEvents) { | 132 | synchronized (lastEvents) { |
133 | + lastEventTimestampEpochMs = System.currentTimeMillis(); | ||
134 | + if (updateEventRateMeter) { | ||
135 | + eventRateMeter.mark(1); | ||
136 | + } | ||
137 | + | ||
138 | + // | ||
139 | + // Keep only the last N events, where N = LAST_EVENTS_MAX_N | ||
140 | + // | ||
114 | while (lastEvents.size() >= LAST_EVENTS_MAX_N) { | 141 | while (lastEvents.size() >= LAST_EVENTS_MAX_N) { |
115 | lastEvents.remove(); | 142 | lastEvents.remove(); |
116 | } | 143 | } |
... | @@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, | ... | @@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, |
119 | } | 146 | } |
120 | 147 | ||
121 | /** | 148 | /** |
149 | + * Inner Device Event Listener class. | ||
150 | + */ | ||
151 | + private class InnerDeviceListener implements DeviceListener { | ||
152 | + @Override | ||
153 | + public void event(DeviceEvent event) { | ||
154 | + recordEvent(event, true); | ||
155 | + log.debug("Device Event: time = {} type = {} event = {}", | ||
156 | + event.time(), event.type(), event); | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + /** | ||
161 | + * Inner Host Event Listener class. | ||
162 | + */ | ||
163 | + private class InnerHostListener implements HostListener { | ||
164 | + @Override | ||
165 | + public void event(HostEvent event) { | ||
166 | + recordEvent(event, true); | ||
167 | + log.debug("Host Event: time = {} type = {} event = {}", | ||
168 | + event.time(), event.type(), event); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + /** | ||
173 | + * Inner Link Event Listener class. | ||
174 | + */ | ||
175 | + private class InnerLinkListener implements LinkListener { | ||
176 | + @Override | ||
177 | + public void event(LinkEvent event) { | ||
178 | + recordEvent(event, true); | ||
179 | + log.debug("Link Event: time = {} type = {} event = {}", | ||
180 | + event.time(), event.type(), event); | ||
181 | + } | ||
182 | + } | ||
183 | + | ||
184 | + /** | ||
185 | + * Inner Topology Event Listener class. | ||
186 | + */ | ||
187 | + private class InnerTopologyListener implements TopologyListener { | ||
188 | + @Override | ||
189 | + public void event(TopologyEvent event) { | ||
190 | + // | ||
191 | + // NOTE: Don't update the eventRateMeter, because the real | ||
192 | + // events are already captured/counted. | ||
193 | + // | ||
194 | + recordEvent(event, false); | ||
195 | + log.debug("Topology Event: time = {} type = {} event = {}", | ||
196 | + event.time(), event.type(), event); | ||
197 | + for (Event reason : event.reasons()) { | ||
198 | + log.debug("Topology Event Reason: time = {} type = {} event = {}", | ||
199 | + reason.time(), reason.type(), reason); | ||
200 | + } | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + /** | ||
122 | * Clears the internal state. | 205 | * Clears the internal state. |
123 | */ | 206 | */ |
124 | private void clear() { | 207 | private void clear() { |
125 | - lastEventTimestampEpochMs = 0; | ||
126 | synchronized (lastEvents) { | 208 | synchronized (lastEvents) { |
209 | + lastEventTimestampEpochMs = 0; | ||
127 | lastEvents.clear(); | 210 | lastEvents.clear(); |
128 | } | 211 | } |
129 | } | 212 | } | ... | ... |
... | @@ -4,7 +4,7 @@ import java.util.List; | ... | @@ -4,7 +4,7 @@ import java.util.List; |
4 | 4 | ||
5 | import com.codahale.metrics.Gauge; | 5 | import com.codahale.metrics.Gauge; |
6 | import com.codahale.metrics.Meter; | 6 | import com.codahale.metrics.Meter; |
7 | -import org.onlab.onos.net.topology.TopologyEvent; | 7 | +import org.onlab.onos.event.Event; |
8 | 8 | ||
9 | /** | 9 | /** |
10 | * Service interface exported by TopologyMetrics. | 10 | * Service interface exported by TopologyMetrics. |
... | @@ -15,7 +15,7 @@ public interface TopologyMetricsService { | ... | @@ -15,7 +15,7 @@ public interface TopologyMetricsService { |
15 | * | 15 | * |
16 | * @return the last saved topology events. | 16 | * @return the last saved topology events. |
17 | */ | 17 | */ |
18 | - public List<TopologyEvent> getEvents(); | 18 | + public List<Event> getEvents(); |
19 | 19 | ||
20 | /** | 20 | /** |
21 | * Gets the Metrics' Gauge for the last topology event timestamp | 21 | * Gets the Metrics' Gauge for the last topology event timestamp | ... | ... |
... | @@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; | ... | @@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; |
19 | description = "Lists the last topology events") | 19 | description = "Lists the last topology events") |
20 | public class TopologyEventsListCommand extends AbstractShellCommand { | 20 | public class TopologyEventsListCommand extends AbstractShellCommand { |
21 | 21 | ||
22 | - private static final String FORMAT_EVENT = | 22 | + private static final String FORMAT_EVENT = "Event=%s"; |
23 | - "Topology Event time=%d type=%s subject=%s"; | 23 | + private static final String FORMAT_REASON = " Reason=%s"; |
24 | - private static final String FORMAT_REASON = | ||
25 | - " Reason time=%d type=%s subject=%s"; | ||
26 | 24 | ||
27 | @Override | 25 | @Override |
28 | protected void execute() { | 26 | protected void execute() { |
... | @@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
31 | if (outputJson()) { | 29 | if (outputJson()) { |
32 | print("%s", json(service.getEvents())); | 30 | print("%s", json(service.getEvents())); |
33 | } else { | 31 | } else { |
34 | - for (TopologyEvent event : service.getEvents()) { | 32 | + for (Event event : service.getEvents()) { |
35 | - print(FORMAT_EVENT, event.time(), event.type(), | 33 | + print(FORMAT_EVENT, event); |
36 | - event.subject()); | 34 | + if (event instanceof TopologyEvent) { |
37 | - for (Event reason : event.reasons()) { | 35 | + TopologyEvent topologyEvent = (TopologyEvent) event; |
38 | - print(FORMAT_REASON, reason.time(), reason.type(), | 36 | + for (Event reason : topologyEvent.reasons()) { |
39 | - reason.subject()); | 37 | + print(FORMAT_REASON, reason); |
38 | + } | ||
40 | } | 39 | } |
41 | print(""); // Extra empty line for clarity | 40 | print(""); // Extra empty line for clarity |
42 | } | 41 | } |
... | @@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
46 | /** | 45 | /** |
47 | * Produces a JSON array of topology events. | 46 | * Produces a JSON array of topology events. |
48 | * | 47 | * |
49 | - * @param topologyEvents the topology events with the data | 48 | + * @param events the topology events with the data |
50 | * @return JSON array with the topology events | 49 | * @return JSON array with the topology events |
51 | */ | 50 | */ |
52 | - private JsonNode json(List<TopologyEvent> topologyEvents) { | 51 | + private JsonNode json(List<Event> events) { |
53 | ObjectMapper mapper = new ObjectMapper(); | 52 | ObjectMapper mapper = new ObjectMapper(); |
54 | ArrayNode result = mapper.createArrayNode(); | 53 | ArrayNode result = mapper.createArrayNode(); |
55 | 54 | ||
56 | - for (TopologyEvent event : topologyEvents) { | 55 | + for (Event event : events) { |
57 | result.add(json(mapper, event)); | 56 | result.add(json(mapper, event)); |
58 | } | 57 | } |
59 | return result; | 58 | return result; |
... | @@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
66 | * @param topologyEvent the topology event with the data | 65 | * @param topologyEvent the topology event with the data |
67 | * @return JSON object for the topology event | 66 | * @return JSON object for the topology event |
68 | */ | 67 | */ |
69 | - private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) { | ||
70 | - ObjectNode result = mapper.createObjectNode(); | ||
71 | - ArrayNode reasons = mapper.createArrayNode(); | ||
72 | - | ||
73 | - for (Event reason : topologyEvent.reasons()) { | ||
74 | - reasons.add(json(mapper, reason)); | ||
75 | - } | ||
76 | - result.put("time", topologyEvent.time()) | ||
77 | - .put("type", topologyEvent.type().toString()) | ||
78 | - .put("subject", topologyEvent.subject().toString()) | ||
79 | - .put("reasons", reasons); | ||
80 | - return result; | ||
81 | - } | ||
82 | - | ||
83 | - /** | ||
84 | - * Produces JSON object for a generic event. | ||
85 | - * | ||
86 | - * @param event the generic event with the data | ||
87 | - * @return JSON object for the generic event | ||
88 | - */ | ||
89 | private ObjectNode json(ObjectMapper mapper, Event event) { | 68 | private ObjectNode json(ObjectMapper mapper, Event event) { |
90 | ObjectNode result = mapper.createObjectNode(); | 69 | ObjectNode result = mapper.createObjectNode(); |
91 | 70 | ||
92 | result.put("time", event.time()) | 71 | result.put("time", event.time()) |
93 | .put("type", event.type().toString()) | 72 | .put("type", event.type().toString()) |
94 | - .put("subject", event.subject().toString()); | 73 | + .put("event", event.toString()); |
74 | + | ||
75 | + // Add the reasons if a TopologyEvent | ||
76 | + if (event instanceof TopologyEvent) { | ||
77 | + TopologyEvent topologyEvent = (TopologyEvent) event; | ||
78 | + ArrayNode reasons = mapper.createArrayNode(); | ||
79 | + for (Event reason : topologyEvent.reasons()) { | ||
80 | + reasons.add(json(mapper, reason)); | ||
81 | + } | ||
82 | + result.put("reasons", reasons); | ||
83 | + } | ||
84 | + | ||
95 | return result; | 85 | return result; |
96 | } | 86 | } |
97 | } | 87 | } | ... | ... |
... | @@ -285,7 +285,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -285,7 +285,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
285 | DefaultLinkDescription linkDescription = | 285 | DefaultLinkDescription linkDescription = |
286 | new DefaultLinkDescription(srcPoint, | 286 | new DefaultLinkDescription(srcPoint, |
287 | snkPoint, | 287 | snkPoint, |
288 | - Link.Type.DIRECT, | 288 | + Link.Type.OPTICAL, |
289 | extendedAttributes); | 289 | extendedAttributes); |
290 | 290 | ||
291 | linkProviderService.linkDetected(linkDescription); | 291 | linkProviderService.linkDetected(linkDescription); |
... | @@ -316,7 +316,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -316,7 +316,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
316 | DefaultLinkDescription linkDescription = | 316 | DefaultLinkDescription linkDescription = |
317 | new DefaultLinkDescription(srcPoint, | 317 | new DefaultLinkDescription(srcPoint, |
318 | snkPoint, | 318 | snkPoint, |
319 | - Link.Type.DIRECT, | 319 | + Link.Type.OPTICAL, |
320 | extendedAttributes); | 320 | extendedAttributes); |
321 | 321 | ||
322 | linkProviderService.linkDetected(linkDescription); | 322 | linkProviderService.linkDetected(linkDescription); | ... | ... |
1 | { | 1 | { |
2 | - "opticalSwitches": [ | 2 | + "opticalSwitches": [ |
3 | { | 3 | { |
4 | "allowed": true, | 4 | "allowed": true, |
5 | "latitude": 37.6, | 5 | "latitude": 37.6, |
... | @@ -12,7 +12,7 @@ | ... | @@ -12,7 +12,7 @@ |
12 | "type": "Roadm" | 12 | "type": "Roadm" |
13 | }, | 13 | }, |
14 | 14 | ||
15 | - { | 15 | + { |
16 | "allowed": true, | 16 | "allowed": true, |
17 | "latitude": 37.3, | 17 | "latitude": 37.3, |
18 | "longitude": 121.9, | 18 | "longitude": 121.9, |
... | @@ -22,9 +22,9 @@ | ... | @@ -22,9 +22,9 @@ |
22 | "numRegen": 0 | 22 | "numRegen": 0 |
23 | }, | 23 | }, |
24 | "type": "Roadm" | 24 | "type": "Roadm" |
25 | - }, | 25 | + }, |
26 | 26 | ||
27 | - { | 27 | + { |
28 | "allowed": true, | 28 | "allowed": true, |
29 | "latitude": 33.9, | 29 | "latitude": 33.9, |
30 | "longitude": 118.4, | 30 | "longitude": 118.4, |
... | @@ -34,10 +34,10 @@ | ... | @@ -34,10 +34,10 @@ |
34 | "numRegen": 2 | 34 | "numRegen": 2 |
35 | }, | 35 | }, |
36 | "type": "Roadm" | 36 | "type": "Roadm" |
37 | - } | 37 | + } |
38 | ], | 38 | ], |
39 | 39 | ||
40 | - "opticalLinks": [ | 40 | + "opticalLinks": [ |
41 | { | 41 | { |
42 | "allowed": true, | 42 | "allowed": true, |
43 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01", | 43 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01", |
... | @@ -51,10 +51,38 @@ | ... | @@ -51,10 +51,38 @@ |
51 | "port2": 30 | 51 | "port2": 30 |
52 | }, | 52 | }, |
53 | "type": "wdmLink" | 53 | "type": "wdmLink" |
54 | - }, | 54 | + }, |
55 | - | 55 | + { |
56 | - { | 56 | + "allowed": true, |
57 | - "allowed": true, | 57 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", |
58 | + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01", | ||
59 | + "params": { | ||
60 | + "distKms": 1000, | ||
61 | + "nodeName1": "ROADM3", | ||
62 | + "nodeName2": "ROADM1", | ||
63 | + "numWaves": 80, | ||
64 | + "port1": 30, | ||
65 | + "port2": 10 | ||
66 | + }, | ||
67 | + "type": "wdmLink" | ||
68 | + }, | ||
69 | + | ||
70 | + { | ||
71 | + "allowed": true, | ||
72 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02", | ||
73 | + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03", | ||
74 | + "params": { | ||
75 | + "distKms": 2000, | ||
76 | + "nodeName1": "ROADM2", | ||
77 | + "nodeName2": "ROADM3", | ||
78 | + "numWaves": 80, | ||
79 | + "port1": 20, | ||
80 | + "port2": 31 | ||
81 | + }, | ||
82 | + "type": "wdmLink" | ||
83 | + }, | ||
84 | + { | ||
85 | + "allowed": true, | ||
58 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", | 86 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", |
59 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", | 87 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", |
60 | "params": { | 88 | "params": { |
... | @@ -66,10 +94,9 @@ | ... | @@ -66,10 +94,9 @@ |
66 | "port2": 21 | 94 | "port2": 21 |
67 | }, | 95 | }, |
68 | "type": "wdmLink" | 96 | "type": "wdmLink" |
69 | - }, | 97 | + }, |
70 | 98 | ||
71 | - | 99 | + { |
72 | - { | ||
73 | "allowed": true, | 100 | "allowed": true, |
74 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:01", | 101 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:01", |
75 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01", | 102 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01", |
... | @@ -82,8 +109,21 @@ | ... | @@ -82,8 +109,21 @@ |
82 | }, | 109 | }, |
83 | "type": "pktOptLink" | 110 | "type": "pktOptLink" |
84 | }, | 111 | }, |
112 | + { | ||
113 | + "allowed": true, | ||
114 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01", | ||
115 | + "nodeDpid2": "00:00:ff:ff:ff:ff:00:01", | ||
116 | + "params": { | ||
117 | + "nodeName1": "ROADM1", | ||
118 | + "nodeName2": "ROUTER1", | ||
119 | + "bandWidth": 100000, | ||
120 | + "port1": 11, | ||
121 | + "port2": 10 | ||
122 | + }, | ||
123 | + "type": "pktOptLink" | ||
124 | + }, | ||
85 | 125 | ||
86 | - { | 126 | + { |
87 | "allowed": true, | 127 | "allowed": true, |
88 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:02", | 128 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:02", |
89 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", | 129 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", |
... | @@ -95,7 +135,20 @@ | ... | @@ -95,7 +135,20 @@ |
95 | "port2": 11 | 135 | "port2": 11 |
96 | }, | 136 | }, |
97 | "type": "pktOptLink" | 137 | "type": "pktOptLink" |
98 | - } | 138 | + }, |
139 | + { | ||
140 | + "allowed": true, | ||
141 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02", | ||
142 | + "nodeDpid2": "00:00:ff:ff:ff:ff:00:02", | ||
143 | + "params": { | ||
144 | + "nodeName1": "ROADM2", | ||
145 | + "nodeName2": "ROUTER2", | ||
146 | + "bandWidth": 100000, | ||
147 | + "port1": 21, | ||
148 | + "port2": 10 | ||
149 | + }, | ||
150 | + "type": "pktOptLink" | ||
151 | + } | ||
99 | 152 | ||
100 | ] | 153 | ] |
101 | } | 154 | } | ... | ... |
... | @@ -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 | } | ... | ... |
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.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 | + */ | ||
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.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 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 & action pair to be applied to | 24 | * Represents a generalized match & 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 java.util.concurrent.Future; | 21 | import java.util.concurrent.Future; |
4 | 22 | ||
5 | import org.onlab.onos.ApplicationId; | 23 | import org.onlab.onos.ApplicationId; |
6 | -import org.onlab.onos.net.intent.BatchOperation; | ||
7 | import org.onlab.onos.net.provider.Provider; | 24 | import org.onlab.onos.net.provider.Provider; |
8 | 25 | ||
9 | /** | 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 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 org.onlab.onos.ApplicationId; | 21 | import org.onlab.onos.ApplicationId; | ... | ... |
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 & related services API definitions. | 21 | * Flow rule model & related services API definitions. |
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.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. |
5 | */ | 32 | */ |
6 | -public interface IntentOperations { | 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 | ||
59 | + */ | ||
60 | + public static Builder builder() { | ||
61 | + return new Builder(); | ||
62 | + } | ||
63 | + | ||
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 | + } | ||
7 | 112 | ||
8 | - // TODO: elaborate once the revised BatchOperation scheme is in place | 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 & withdraw operations. Such a batch is | 58 | * Submits a batch of submit & 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 | ... | ... |
... | @@ -108,6 +108,9 @@ public class FlowRuleManager | ... | @@ -108,6 +108,9 @@ public class FlowRuleManager |
108 | if (local) { | 108 | if (local) { |
109 | // TODO: aggregate all local rules and push down once? | 109 | // TODO: aggregate all local rules and push down once? |
110 | applyFlowRulesToProviders(f); | 110 | applyFlowRulesToProviders(f); |
111 | + eventDispatcher.post( | ||
112 | + new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, f)); | ||
113 | + | ||
111 | } | 114 | } |
112 | } | 115 | } |
113 | } | 116 | } |
... | @@ -136,6 +139,8 @@ public class FlowRuleManager | ... | @@ -136,6 +139,8 @@ public class FlowRuleManager |
136 | if (local) { | 139 | if (local) { |
137 | // TODO: aggregate all local rules and push down once? | 140 | // TODO: aggregate all local rules and push down once? |
138 | removeFlowRulesFromProviders(f); | 141 | removeFlowRulesFromProviders(f); |
142 | + eventDispatcher.post( | ||
143 | + new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, f)); | ||
139 | } | 144 | } |
140 | } | 145 | } |
141 | } | 146 | } | ... | ... |
... | @@ -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,7 +68,9 @@ implements PacketService, PacketProviderRegistry { | ... | @@ -68,7 +68,9 @@ 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 | - packetProvider.emit(packet); | 71 | + if (packetProvider != null) { |
72 | + packetProvider.emit(packet); | ||
73 | + } | ||
72 | } | 74 | } |
73 | 75 | ||
74 | @Override | 76 | @Override | ... | ... |
... | @@ -10,14 +10,17 @@ import org.onlab.onos.net.ConnectPoint; | ... | @@ -10,14 +10,17 @@ 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 | 12 | ||
13 | +import org.onlab.onos.net.flow.FlowEntry; | ||
13 | import org.onlab.onos.net.flow.FlowRule; | 14 | import org.onlab.onos.net.flow.FlowRule; |
14 | import org.onlab.onos.net.flow.FlowRuleEvent; | 15 | import org.onlab.onos.net.flow.FlowRuleEvent; |
15 | import org.onlab.onos.net.flow.FlowRuleListener; | 16 | import org.onlab.onos.net.flow.FlowRuleListener; |
16 | import org.onlab.onos.net.flow.FlowRuleService; | 17 | import org.onlab.onos.net.flow.FlowRuleService; |
18 | +import org.onlab.onos.net.statistic.DefaultLoad; | ||
17 | import org.onlab.onos.net.statistic.Load; | 19 | import org.onlab.onos.net.statistic.Load; |
18 | import org.onlab.onos.net.statistic.StatisticService; | 20 | import org.onlab.onos.net.statistic.StatisticService; |
19 | import org.onlab.onos.net.statistic.StatisticStore; | 21 | import org.onlab.onos.net.statistic.StatisticStore; |
20 | import org.slf4j.Logger; | 22 | import org.slf4j.Logger; |
23 | +import java.util.Set; | ||
21 | 24 | ||
22 | import static org.slf4j.LoggerFactory.getLogger; | 25 | import static org.slf4j.LoggerFactory.getLogger; |
23 | 26 | ||
... | @@ -54,27 +57,91 @@ public class StatisticManager implements StatisticService { | ... | @@ -54,27 +57,91 @@ public class StatisticManager implements StatisticService { |
54 | 57 | ||
55 | @Override | 58 | @Override |
56 | public Load load(Link link) { | 59 | public Load load(Link link) { |
57 | - return null; | 60 | + return load(link.src()); |
58 | } | 61 | } |
59 | 62 | ||
60 | @Override | 63 | @Override |
61 | public Load load(ConnectPoint connectPoint) { | 64 | public Load load(ConnectPoint connectPoint) { |
62 | - return null; | 65 | + return loadInternal(connectPoint); |
63 | } | 66 | } |
64 | 67 | ||
65 | @Override | 68 | @Override |
66 | public Link max(Path path) { | 69 | public Link max(Path path) { |
67 | - return null; | 70 | + if (path.links().isEmpty()) { |
71 | + return null; | ||
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; | ||
68 | } | 83 | } |
69 | 84 | ||
70 | @Override | 85 | @Override |
71 | public Link min(Path path) { | 86 | public Link min(Path path) { |
72 | - return null; | 87 | + if (path.links().isEmpty()) { |
88 | + return null; | ||
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; | ||
73 | } | 100 | } |
74 | 101 | ||
75 | @Override | 102 | @Override |
76 | public FlowRule highestHitter(ConnectPoint connectPoint) { | 103 | public FlowRule highestHitter(ConnectPoint connectPoint) { |
77 | - return null; | 104 | + Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint); |
105 | + if (hitters.isEmpty()) { | ||
106 | + return null; | ||
107 | + } | ||
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; | ||
78 | } | 145 | } |
79 | 146 | ||
80 | /** | 147 | /** |
... | @@ -84,22 +151,29 @@ public class StatisticManager implements StatisticService { | ... | @@ -84,22 +151,29 @@ public class StatisticManager implements StatisticService { |
84 | 151 | ||
85 | @Override | 152 | @Override |
86 | public void event(FlowRuleEvent event) { | 153 | public void event(FlowRuleEvent event) { |
87 | -// FlowRule rule = event.subject(); | 154 | + FlowRule rule = event.subject(); |
88 | -// switch (event.type()) { | 155 | + switch (event.type()) { |
89 | -// case RULE_ADDED: | 156 | + case RULE_ADDED: |
90 | -// case RULE_UPDATED: | 157 | + case RULE_UPDATED: |
91 | -// if (rule instanceof FlowEntry) { | 158 | + if (rule instanceof FlowEntry) { |
92 | -// statisticStore.addOrUpdateStatistic((FlowEntry) rule); | 159 | + statisticStore.addOrUpdateStatistic((FlowEntry) rule); |
93 | -// } | 160 | + } else { |
94 | -// break; | 161 | + log.warn("IT AIN'T A FLOWENTRY"); |
95 | -// case RULE_ADD_REQUESTED: | 162 | + } |
96 | -// statisticStore.prepareForStatistics(rule); | 163 | + break; |
97 | -// break; | 164 | + case RULE_ADD_REQUESTED: |
98 | -// case RULE_REMOVE_REQUESTED: | 165 | + log.info("Preparing for stats"); |
99 | -// case RULE_REMOVED: | 166 | + statisticStore.prepareForStatistics(rule); |
100 | -// statisticStore.removeFromStatistics(rule); | 167 | + break; |
101 | -// break; | 168 | + case RULE_REMOVE_REQUESTED: |
102 | -// } | 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 | + } | ||
103 | } | 177 | } |
104 | } | 178 | } |
105 | 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() { |
180 | - buildTopology(reasons); | 187 | + try { |
188 | + buildTopology(reasons); | ||
189 | + } catch (Exception e) { | ||
190 | + log.warn("Unable to compute topology due to: {}", e.getMessage()); | ||
191 | + } | ||
181 | } | 192 | } |
182 | } | 193 | } |
183 | 194 | ... | ... |
1 | package org.onlab.onos.net.flow.impl; | 1 | package org.onlab.onos.net.flow.impl; |
2 | 2 | ||
3 | + | ||
4 | + | ||
5 | +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*; | ||
6 | + | ||
7 | + | ||
3 | import java.util.ArrayList; | 8 | import java.util.ArrayList; |
4 | import java.util.Collections; | 9 | import java.util.Collections; |
5 | import java.util.HashMap; | 10 | import java.util.HashMap; |
... | @@ -45,7 +50,7 @@ import org.onlab.onos.net.flow.TrafficSelector; | ... | @@ -45,7 +50,7 @@ import org.onlab.onos.net.flow.TrafficSelector; |
45 | import org.onlab.onos.net.flow.TrafficTreatment; | 50 | import org.onlab.onos.net.flow.TrafficTreatment; |
46 | import org.onlab.onos.net.flow.criteria.Criterion; | 51 | import org.onlab.onos.net.flow.criteria.Criterion; |
47 | import org.onlab.onos.net.flow.instructions.Instruction; | 52 | import org.onlab.onos.net.flow.instructions.Instruction; |
48 | -import org.onlab.onos.net.intent.BatchOperation; | 53 | +import org.onlab.onos.net.flow.BatchOperation; |
49 | import org.onlab.onos.net.provider.AbstractProvider; | 54 | import org.onlab.onos.net.provider.AbstractProvider; |
50 | import org.onlab.onos.net.provider.ProviderId; | 55 | import org.onlab.onos.net.provider.ProviderId; |
51 | import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; | 56 | import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; |
... | @@ -164,7 +169,8 @@ public class FlowRuleManagerTest { | ... | @@ -164,7 +169,8 @@ public class FlowRuleManagerTest { |
164 | assertEquals("2 rules should exist", 2, flowCount()); | 169 | assertEquals("2 rules should exist", 2, flowCount()); |
165 | 170 | ||
166 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); | 171 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); |
167 | - validateEvents(RULE_ADDED, RULE_ADDED); | 172 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
173 | + RULE_ADDED, RULE_ADDED); | ||
168 | 174 | ||
169 | addFlowRule(1); | 175 | addFlowRule(1); |
170 | assertEquals("should still be 2 rules", 2, flowCount()); | 176 | assertEquals("should still be 2 rules", 2, flowCount()); |
... | @@ -218,11 +224,12 @@ public class FlowRuleManagerTest { | ... | @@ -218,11 +224,12 @@ public class FlowRuleManagerTest { |
218 | FlowEntry fe2 = new DefaultFlowEntry(f2); | 224 | FlowEntry fe2 = new DefaultFlowEntry(f2); |
219 | FlowEntry fe3 = new DefaultFlowEntry(f3); | 225 | FlowEntry fe3 = new DefaultFlowEntry(f3); |
220 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); | 226 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); |
221 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); | 227 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
228 | + RULE_ADDED, RULE_ADDED, RULE_ADDED); | ||
222 | 229 | ||
223 | mgr.removeFlowRules(f1, f2); | 230 | mgr.removeFlowRules(f1, f2); |
224 | //removing from north, so no events generated | 231 | //removing from north, so no events generated |
225 | - validateEvents(); | 232 | + validateEvents(RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED); |
226 | assertEquals("3 rule should exist", 3, flowCount()); | 233 | assertEquals("3 rule should exist", 3, flowCount()); |
227 | assertTrue("Entries should be pending remove.", | 234 | assertTrue("Entries should be pending remove.", |
228 | validateState(ImmutableMap.of( | 235 | validateState(ImmutableMap.of( |
... | @@ -244,7 +251,8 @@ public class FlowRuleManagerTest { | ... | @@ -244,7 +251,8 @@ public class FlowRuleManagerTest { |
244 | service.removeFlowRules(f1); | 251 | service.removeFlowRules(f1); |
245 | fe1.setState(FlowEntryState.REMOVED); | 252 | fe1.setState(FlowEntryState.REMOVED); |
246 | providerService.flowRemoved(fe1); | 253 | providerService.flowRemoved(fe1); |
247 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); | 254 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, |
255 | + RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED); | ||
248 | 256 | ||
249 | providerService.flowRemoved(fe1); | 257 | providerService.flowRemoved(fe1); |
250 | validateEvents(); | 258 | validateEvents(); |
... | @@ -253,7 +261,7 @@ public class FlowRuleManagerTest { | ... | @@ -253,7 +261,7 @@ public class FlowRuleManagerTest { |
253 | FlowEntry fe3 = new DefaultFlowEntry(f3); | 261 | FlowEntry fe3 = new DefaultFlowEntry(f3); |
254 | service.applyFlowRules(f3); | 262 | service.applyFlowRules(f3); |
255 | providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); | 263 | providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); |
256 | - validateEvents(RULE_ADDED); | 264 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADDED); |
257 | 265 | ||
258 | providerService.flowRemoved(fe3); | 266 | providerService.flowRemoved(fe3); |
259 | validateEvents(); | 267 | validateEvents(); |
... | @@ -282,7 +290,8 @@ public class FlowRuleManagerTest { | ... | @@ -282,7 +290,8 @@ public class FlowRuleManagerTest { |
282 | f2, FlowEntryState.ADDED, | 290 | f2, FlowEntryState.ADDED, |
283 | f3, FlowEntryState.PENDING_ADD))); | 291 | f3, FlowEntryState.PENDING_ADD))); |
284 | 292 | ||
285 | - validateEvents(RULE_ADDED, RULE_ADDED); | 293 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
294 | + RULE_ADDED, RULE_ADDED); | ||
286 | } | 295 | } |
287 | 296 | ||
288 | @Test | 297 | @Test |
... | @@ -302,7 +311,7 @@ public class FlowRuleManagerTest { | ... | @@ -302,7 +311,7 @@ public class FlowRuleManagerTest { |
302 | 311 | ||
303 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); | 312 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); |
304 | 313 | ||
305 | - validateEvents(RULE_ADDED, RULE_ADDED); | 314 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, RULE_ADDED); |
306 | 315 | ||
307 | } | 316 | } |
308 | 317 | ||
... | @@ -327,7 +336,8 @@ public class FlowRuleManagerTest { | ... | @@ -327,7 +336,8 @@ public class FlowRuleManagerTest { |
327 | 336 | ||
328 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); | 337 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); |
329 | 338 | ||
330 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); | 339 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
340 | + RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED); | ||
331 | 341 | ||
332 | } | 342 | } |
333 | 343 | ... | ... |
... | @@ -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, | ... | ... |
... | @@ -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 | ... | ... |
core/store/dist/src/main/java/org/onlab/onos/store/statistic/impl/DistributedStatisticStore.java
0 → 100644
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 | +} |
core/store/dist/src/main/java/org/onlab/onos/store/statistic/impl/StatisticStoreMessageSubjects.java
0 → 100644
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 | +} |
... | @@ -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 | 97 | + } |
98 | - return MastershipRole.STANDBY; | 98 | + if (roleInfo.contains(STANDBY, nodeId)) { |
99 | - } else { | 99 | + return STANDBY; |
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 | - } | ||
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. |
206 | - rv.add(MASTER, local); | 192 | + //for latter, claim mastership. |
207 | - rv.reassign(local, STANDBY, NONE); | 193 | + if (rv.get(MASTER) == null) { |
194 | + rv.add(MASTER, local); | ||
195 | + rv.reassign(local, STANDBY, NONE); | ||
196 | + updateTerm(deviceId); | ||
197 | + role = MastershipRole.MASTER; | ||
198 | + } else { | ||
199 | + rv.add(STANDBY, local); | ||
200 | + rv.reassign(local, NONE, STANDBY); | ||
201 | + role = MastershipRole.STANDBY; | ||
202 | + } | ||
208 | roleMap.put(deviceId, rv); | 203 | roleMap.put(deviceId, rv); |
209 | - updateTerm(deviceId); | ||
210 | - role = MastershipRole.MASTER; | ||
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; |
... | @@ -26,9 +27,11 @@ import org.onlab.onos.net.Port; | ... | @@ -26,9 +27,11 @@ import org.onlab.onos.net.Port; |
26 | import org.onlab.onos.net.PortNumber; | 27 | import org.onlab.onos.net.PortNumber; |
27 | import org.onlab.onos.net.device.DefaultDeviceDescription; | 28 | import org.onlab.onos.net.device.DefaultDeviceDescription; |
28 | import org.onlab.onos.net.device.DefaultPortDescription; | 29 | import org.onlab.onos.net.device.DefaultPortDescription; |
30 | +import org.onlab.onos.net.flow.DefaultFlowEntry; | ||
29 | import org.onlab.onos.net.flow.DefaultFlowRule; | 31 | import org.onlab.onos.net.flow.DefaultFlowRule; |
30 | import org.onlab.onos.net.flow.DefaultTrafficSelector; | 32 | import org.onlab.onos.net.flow.DefaultTrafficSelector; |
31 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; | 33 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; |
34 | +import org.onlab.onos.net.flow.FlowEntry; | ||
32 | import org.onlab.onos.net.flow.FlowId; | 35 | import org.onlab.onos.net.flow.FlowId; |
33 | import org.onlab.onos.net.flow.criteria.Criteria; | 36 | import org.onlab.onos.net.flow.criteria.Criteria; |
34 | import org.onlab.onos.net.flow.criteria.Criterion; | 37 | import org.onlab.onos.net.flow.criteria.Criterion; |
... | @@ -75,6 +78,7 @@ public final class KryoNamespaces { | ... | @@ -75,6 +78,7 @@ public final class KryoNamespaces { |
75 | ArrayList.class, | 78 | ArrayList.class, |
76 | Arrays.asList().getClass(), | 79 | Arrays.asList().getClass(), |
77 | HashMap.class, | 80 | HashMap.class, |
81 | + HashSet.class, | ||
78 | // | 82 | // |
79 | // | 83 | // |
80 | ControllerNode.State.class, | 84 | ControllerNode.State.class, |
... | @@ -94,6 +98,8 @@ public final class KryoNamespaces { | ... | @@ -94,6 +98,8 @@ public final class KryoNamespaces { |
94 | HostDescription.class, | 98 | HostDescription.class, |
95 | DefaultHostDescription.class, | 99 | DefaultHostDescription.class, |
96 | DefaultFlowRule.class, | 100 | DefaultFlowRule.class, |
101 | + DefaultFlowEntry.class, | ||
102 | + FlowEntry.FlowEntryState.class, | ||
97 | FlowId.class, | 103 | FlowId.class, |
98 | DefaultTrafficSelector.class, | 104 | DefaultTrafficSelector.class, |
99 | Criteria.PortCriterion.class, | 105 | 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 | ||
... | @@ -113,19 +114,17 @@ public class SimpleDeviceStore | ... | @@ -113,19 +114,17 @@ public class SimpleDeviceStore |
113 | 114 | ||
114 | @Override | 115 | @Override |
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, |
128 | - deviceDescription); | 127 | + deviceDescription); |
129 | 128 | ||
130 | Device oldDevice = devices.get(deviceId); | 129 | Device oldDevice = devices.get(deviceId); |
131 | // update description | 130 | // update description |
... | @@ -145,12 +144,11 @@ public class SimpleDeviceStore | ... | @@ -145,12 +144,11 @@ 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, |
152 | - "Unexpected Device in cache. PID:%s [old=%s, new=%s]", | 150 | + "Unexpected Device in cache. PID:%s [old=%s, new=%s]", |
153 | - providerId, oldDevice, newDevice); | 151 | + providerId, oldDevice, newDevice); |
154 | 152 | ||
155 | if (!providerId.isAncillary()) { | 153 | if (!providerId.isAncillary()) { |
156 | availableDevices.add(newDevice.id()); | 154 | availableDevices.add(newDevice.id()); |
... | @@ -162,17 +160,24 @@ public class SimpleDeviceStore | ... | @@ -162,17 +160,24 @@ 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()) |
176 | , newDevice); | 181 | , newDevice); |
177 | } | 182 | } |
178 | if (!providerId.isAncillary()) { | 183 | if (!providerId.isAncillary()) { |
... | @@ -193,7 +198,7 @@ public class SimpleDeviceStore | ... | @@ -193,7 +198,7 @@ public class SimpleDeviceStore |
193 | @Override | 198 | @Override |
194 | public DeviceEvent markOffline(DeviceId deviceId) { | 199 | public DeviceEvent markOffline(DeviceId deviceId) { |
195 | Map<ProviderId, DeviceDescriptions> providerDescs | 200 | Map<ProviderId, DeviceDescriptions> providerDescs |
196 | - = getOrCreateDeviceDescriptions(deviceId); | 201 | + = getOrCreateDeviceDescriptions(deviceId); |
197 | 202 | ||
198 | // locking device | 203 | // locking device |
199 | synchronized (providerDescs) { | 204 | synchronized (providerDescs) { |
... | @@ -212,9 +217,8 @@ public class SimpleDeviceStore | ... | @@ -212,9 +217,8 @@ public class SimpleDeviceStore |
212 | 217 | ||
213 | @Override | 218 | @Override |
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 | ||
... | @@ -226,8 +230,8 @@ public class SimpleDeviceStore | ... | @@ -226,8 +230,8 @@ public class SimpleDeviceStore |
226 | DeviceDescriptions descs = descsMap.get(providerId); | 230 | DeviceDescriptions descs = descsMap.get(providerId); |
227 | // every provider must provide DeviceDescription. | 231 | // every provider must provide DeviceDescription. |
228 | checkArgument(descs != null, | 232 | checkArgument(descs != null, |
229 | - "Device description for Device ID %s from Provider %s was not found", | 233 | + "Device description for Device ID %s from Provider %s was not found", |
230 | - deviceId, providerId); | 234 | + deviceId, providerId); |
231 | 235 | ||
232 | Map<PortNumber, Port> ports = getPortMap(deviceId); | 236 | Map<PortNumber, Port> ports = getPortMap(deviceId); |
233 | 237 | ||
... | @@ -247,8 +251,8 @@ public class SimpleDeviceStore | ... | @@ -247,8 +251,8 @@ public class SimpleDeviceStore |
247 | newPort = composePort(device, number, descsMap); | 251 | newPort = composePort(device, number, descsMap); |
248 | 252 | ||
249 | events.add(oldPort == null ? | 253 | events.add(oldPort == null ? |
250 | - createPort(device, newPort, ports) : | 254 | + createPort(device, newPort, ports) : |
251 | - updatePort(device, oldPort, newPort, ports)); | 255 | + updatePort(device, oldPort, newPort, ports)); |
252 | } | 256 | } |
253 | 257 | ||
254 | events.addAll(pruneOldPorts(device, ports, processed)); | 258 | events.addAll(pruneOldPorts(device, ports, processed)); |
... | @@ -272,7 +276,7 @@ public class SimpleDeviceStore | ... | @@ -272,7 +276,7 @@ public class SimpleDeviceStore |
272 | Port newPort, | 276 | Port newPort, |
273 | Map<PortNumber, Port> ports) { | 277 | Map<PortNumber, Port> ports) { |
274 | if (oldPort.isEnabled() != newPort.isEnabled() || | 278 | if (oldPort.isEnabled() != newPort.isEnabled() || |
275 | - !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) { | 279 | + !AnnotationsUtil.isEqual(oldPort.annotations(), newPort.annotations())) { |
276 | 280 | ||
277 | ports.put(oldPort.number(), newPort); | 281 | ports.put(oldPort.number(), newPort); |
278 | return new DeviceEvent(PORT_UPDATED, device, newPort); | 282 | return new DeviceEvent(PORT_UPDATED, device, newPort); |
... | @@ -303,7 +307,7 @@ public class SimpleDeviceStore | ... | @@ -303,7 +307,7 @@ public class SimpleDeviceStore |
303 | // exist, it creates and registers a new one. | 307 | // exist, it creates and registers a new one. |
304 | private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { | 308 | private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) { |
305 | return createIfAbsentUnchecked(devicePorts, deviceId, | 309 | return createIfAbsentUnchecked(devicePorts, deviceId, |
306 | - NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); | 310 | + NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); |
307 | } | 311 | } |
308 | 312 | ||
309 | private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions( | 313 | private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions( |
... | @@ -325,9 +329,8 @@ public class SimpleDeviceStore | ... | @@ -325,9 +329,8 @@ public class SimpleDeviceStore |
325 | 329 | ||
326 | // Guarded by deviceDescs value (=Device lock) | 330 | // Guarded by deviceDescs value (=Device lock) |
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) { |
... | @@ -340,7 +343,7 @@ public class SimpleDeviceStore | ... | @@ -340,7 +343,7 @@ public class SimpleDeviceStore |
340 | 343 | ||
341 | @Override | 344 | @Override |
342 | public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, | 345 | public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId, |
343 | - PortDescription portDescription) { | 346 | + PortDescription portDescription) { |
344 | Device device = devices.get(deviceId); | 347 | Device device = devices.get(deviceId); |
345 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 348 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
346 | 349 | ||
... | @@ -351,8 +354,8 @@ public class SimpleDeviceStore | ... | @@ -351,8 +354,8 @@ public class SimpleDeviceStore |
351 | DeviceDescriptions descs = descsMap.get(providerId); | 354 | DeviceDescriptions descs = descsMap.get(providerId); |
352 | // assuming all providers must give DeviceDescription first | 355 | // assuming all providers must give DeviceDescription first |
353 | checkArgument(descs != null, | 356 | checkArgument(descs != null, |
354 | - "Device description for Device ID %s from Provider %s was not found", | 357 | + "Device description for Device ID %s from Provider %s was not found", |
355 | - deviceId, providerId); | 358 | + deviceId, providerId); |
356 | 359 | ||
357 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); | 360 | ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); |
358 | final PortNumber number = portDescription.portNumber(); | 361 | final PortNumber number = portDescription.portNumber(); |
... | @@ -404,19 +407,19 @@ public class SimpleDeviceStore | ... | @@ -404,19 +407,19 @@ public class SimpleDeviceStore |
404 | availableDevices.remove(deviceId); | 407 | availableDevices.remove(deviceId); |
405 | descs.clear(); | 408 | descs.clear(); |
406 | return device == null ? null : | 409 | return device == null ? null : |
407 | - new DeviceEvent(DEVICE_REMOVED, device, null); | 410 | + new DeviceEvent(DEVICE_REMOVED, device, null); |
408 | } | 411 | } |
409 | } | 412 | } |
410 | 413 | ||
411 | /** | 414 | /** |
412 | * Returns a Device, merging description given from multiple Providers. | 415 | * Returns a Device, merging description given from multiple Providers. |
413 | * | 416 | * |
414 | - * @param deviceId device identifier | 417 | + * @param deviceId device identifier |
415 | * @param providerDescs Collection of Descriptions from multiple providers | 418 | * @param providerDescs Collection of Descriptions from multiple providers |
416 | * @return Device instance | 419 | * @return Device instance |
417 | */ | 420 | */ |
418 | private Device composeDevice(DeviceId deviceId, | 421 | private Device composeDevice(DeviceId deviceId, |
419 | - Map<ProviderId, DeviceDescriptions> providerDescs) { | 422 | + Map<ProviderId, DeviceDescriptions> providerDescs) { |
420 | 423 | ||
421 | checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied"); | 424 | checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied"); |
422 | 425 | ||
... | @@ -447,21 +450,21 @@ public class SimpleDeviceStore | ... | @@ -447,21 +450,21 @@ 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 | } |
454 | 457 | ||
455 | /** | 458 | /** |
456 | * Returns a Port, merging description given from multiple Providers. | 459 | * Returns a Port, merging description given from multiple Providers. |
457 | * | 460 | * |
458 | - * @param device device the port is on | 461 | + * @param device device the port is on |
459 | - * @param number port number | 462 | + * @param number port number |
460 | * @param descsMap Collection of Descriptions from multiple providers | 463 | * @param descsMap Collection of Descriptions from multiple providers |
461 | * @return Port instance | 464 | * @return Port instance |
462 | */ | 465 | */ |
463 | private Port composePort(Device device, PortNumber number, | 466 | private Port composePort(Device device, PortNumber number, |
464 | - Map<ProviderId, DeviceDescriptions> descsMap) { | 467 | + Map<ProviderId, DeviceDescriptions> descsMap) { |
465 | 468 | ||
466 | ProviderId primary = pickPrimaryPID(descsMap); | 469 | ProviderId primary = pickPrimaryPID(descsMap); |
467 | DeviceDescriptions primDescs = descsMap.get(primary); | 470 | DeviceDescriptions primDescs = descsMap.get(primary); | ... | ... |
... | @@ -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> | ... | ... |
... | @@ -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; |
... | @@ -661,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -661,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
661 | * However, we could be more forgiving | 660 | * However, we could be more forgiving |
662 | * @param h the channel handler that received the message | 661 | * @param h the channel handler that received the message |
663 | * @param m the message | 662 | * @param m the message |
664 | - * @throws SwitchStateException | 663 | + * @throws SwitchStateException we always throw the exception |
665 | - * @throws SwitchStateExeption we always through the execption | ||
666 | */ | 664 | */ |
667 | - // needs to be protected because enum members are acutally subclasses | 665 | + // needs to be protected because enum members are actually subclasses |
668 | protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) | 666 | protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) |
669 | throws SwitchStateException { | 667 | throws SwitchStateException { |
670 | String msg = getSwitchStateMessage(h, m, | 668 | String msg = getSwitchStateMessage(h, m, |
... | @@ -1025,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -1025,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
1025 | // all state for the original switch (with the same dpid), | 1023 | // all state for the original switch (with the same dpid), |
1026 | // which we obviously don't want. | 1024 | // which we obviously don't want. |
1027 | log.info("{}:removal called", getSwitchInfoString()); | 1025 | log.info("{}:removal called", getSwitchInfoString()); |
1028 | - sw.removeConnectedSwitch(); | 1026 | + if (sw != null) { |
1027 | + sw.removeConnectedSwitch(); | ||
1028 | + } | ||
1029 | } else { | 1029 | } else { |
1030 | // A duplicate was disconnected on this ChannelHandler, | 1030 | // A duplicate was disconnected on this ChannelHandler, |
1031 | // this is the same switch reconnecting, but the original state was | 1031 | // this is the same switch reconnecting, but the original state was | ... | ... |
... | @@ -207,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -207,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
207 | + "value for dpid: {}", dpid); | 207 | + "value for dpid: {}", dpid); |
208 | return false; | 208 | return false; |
209 | } else { | 209 | } else { |
210 | - log.error("Added switch {}", dpid); | 210 | + log.info("Added switch {}", dpid); |
211 | connectedSwitches.put(dpid, sw); | 211 | connectedSwitches.put(dpid, sw); |
212 | for (OpenFlowSwitchListener l : ofSwitchListener) { | 212 | for (OpenFlowSwitchListener l : ofSwitchListener) { |
213 | 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,6 +347,12 @@ public class LinkDiscovery implements TimerTask { | ... | @@ -344,6 +347,12 @@ public class LinkDiscovery implements TimerTask { |
344 | } | 347 | } |
345 | 348 | ||
346 | private void sendProbes(Long portNumber) { | 349 | private void sendProbes(Long portNumber) { |
350 | + if (device == null) { | ||
351 | + log.warn("CRAZY SHIT"); | ||
352 | + } | ||
353 | + if (mastershipService == null) { | ||
354 | + log.warn("INSANE"); | ||
355 | + } | ||
347 | if (device.type() != Device.Type.ROADM && | 356 | if (device.type() != Device.Type.ROADM && |
348 | mastershipService.getLocalRole(this.device.id()) == | 357 | mastershipService.getLocalRole(this.device.id()) == |
349 | MastershipRole.MASTER) { | 358 | MastershipRole.MASTER) { | ... | ... |
providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
... | @@ -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; |
... | @@ -103,6 +103,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -103,6 +103,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
103 | private final Map<Long, InstallationFuture> pendingFMs = | 103 | private final Map<Long, InstallationFuture> pendingFMs = |
104 | new ConcurrentHashMap<Long, InstallationFuture>(); | 104 | new ConcurrentHashMap<Long, InstallationFuture>(); |
105 | 105 | ||
106 | + private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); | ||
107 | + | ||
106 | /** | 108 | /** |
107 | * Creates an OpenFlow host provider. | 109 | * Creates an OpenFlow host provider. |
108 | */ | 110 | */ |
... | @@ -115,6 +117,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -115,6 +117,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
115 | providerService = providerRegistry.register(this); | 117 | providerService = providerRegistry.register(this); |
116 | controller.addListener(listener); | 118 | controller.addListener(listener); |
117 | controller.addEventListener(listener); | 119 | controller.addEventListener(listener); |
120 | + | ||
121 | + for (OpenFlowSwitch sw : controller.getSwitches()) { | ||
122 | + FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL); | ||
123 | + fsc.start(); | ||
124 | + collectors.put(new Dpid(sw.getId()), fsc); | ||
125 | + } | ||
126 | + | ||
127 | + | ||
118 | log.info("Started"); | 128 | log.info("Started"); |
119 | } | 129 | } |
120 | 130 | ||
... | @@ -213,7 +223,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -213,7 +223,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
213 | private class InternalFlowProvider | 223 | private class InternalFlowProvider |
214 | implements OpenFlowSwitchListener, OpenFlowEventListener { | 224 | implements OpenFlowSwitchListener, OpenFlowEventListener { |
215 | 225 | ||
216 | - private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); | 226 | + |
217 | private final Multimap<DeviceId, FlowEntry> completeEntries = | 227 | private final Multimap<DeviceId, FlowEntry> completeEntries = |
218 | ArrayListMultimap.create(); | 228 | ArrayListMultimap.create(); |
219 | 229 | ... | ... |
... | @@ -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}" | ... | ... |
tools/test/topos/oe-linear-3.json
0 → 100644
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) |
34 | - #self.checkListening() | 35 | + #self.protocol = protocol ('tcp') |
35 | - | 36 | + #self.checkListening() |
36 | - ONOS_DIR = '/opt/onos/' | 37 | + |
37 | - KARAF_DIR = ONOS_DIR + 'apache-karaf-3.0.1/' | 38 | + self.onosDir = onosDir |
38 | - reactive = True | 39 | + self.karafDir = onosDir + 'apache-karaf-3.0.1/' |
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() | ||
59 | 78 | ||
79 | + # TODO 2. Update etc/hazelcast.xml : interface lines | ||
80 | + #cp etc/hazelcast.xml instances/c1/etc/ | ||
81 | + self.updateHazelcast() | ||
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); | ... | ... |
... | @@ -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 |
web/gui/src/main/webapp/geometry.js
0 → 100644
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> | ... | ... |
... | @@ -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 | } |
... | @@ -152,8 +158,8 @@ | ... | @@ -152,8 +158,8 @@ |
152 | .attr('width', view.width) | 158 | .attr('width', view.width) |
153 | .attr('height', view.height) | 159 | .attr('height', view.height) |
154 | .append('g') | 160 | .append('g') |
161 | + .attr('transform', config.force.translate()); | ||
155 | // .attr('id', 'zoomable') | 162 | // .attr('id', 'zoomable') |
156 | - .attr('transform', config.force.translate()) | ||
157 | // .call(d3.behavior.zoom().on("zoom", zoomRedraw)); | 163 | // .call(d3.behavior.zoom().on("zoom", zoomRedraw)); |
158 | 164 | ||
159 | // function zoomRedraw() { | 165 | // function zoomRedraw() { |
... | @@ -247,7 +253,7 @@ | ... | @@ -247,7 +253,7 @@ |
247 | } | 253 | } |
248 | }); | 254 | }); |
249 | 255 | ||
250 | - // TODO: add drag, mouseover, mouseout behaviors | 256 | + |
251 | network.node = network.svg.selectAll('.node') | 257 | network.node = network.svg.selectAll('.node') |
252 | .data(network.force.nodes(), function(d) {return d.id}) | 258 | .data(network.force.nodes(), function(d) {return d.id}) |
253 | .enter().append('g') | 259 | .enter().append('g') |
... | @@ -279,23 +285,26 @@ | ... | @@ -279,23 +285,26 @@ |
279 | } | 285 | } |
280 | }); | 286 | }); |
281 | 287 | ||
282 | - // TODO: augment stroke and fill functions | ||
283 | network.nodeRect = network.node.append('rect') | 288 | network.nodeRect = network.node.append('rect') |
284 | - // TODO: css for node rects | ||
285 | .attr('rx', 5) | 289 | .attr('rx', 5) |
286 | .attr('ry', 5) | 290 | .attr('ry', 5) |
287 | -// .attr('stroke', function(d) { return '#000'}) | 291 | + .attr('width', 126) |
288 | -// .attr('fill', function(d) { return '#ddf'}) | 292 | + .attr('height', 40); |
289 | - .attr('width', 60) | ||
290 | - .attr('height', 24); | ||
291 | 293 | ||
292 | network.node.each(function(d) { | 294 | network.node.each(function(d) { |
293 | var node = d3.select(this), | 295 | var node = d3.select(this), |
294 | - rect = node.select('rect'); | 296 | + rect = node.select('rect'), |
295 | - var text = node.append('text') | 297 | + img = node.append('svg:image') |
296 | - .text(d.id) | 298 | + .attr('x', -16) |
297 | - .attr('dx', '1em') | 299 | + .attr('y', -16) |
298 | - .attr('dy', '2.1em'); | 300 | + .attr('width', 32) |
301 | + .attr('height', 32) | ||
302 | + .attr('xlink:href', iconUrl(d)), | ||
303 | + text = node.append('text') | ||
304 | + .text(d.id) | ||
305 | + .attr('dy', '1.1em'), | ||
306 | + dummy; | ||
307 | + | ||
299 | }); | 308 | }); |
300 | 309 | ||
301 | // 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 |
... | @@ -308,12 +317,64 @@ | ... | @@ -308,12 +317,64 @@ |
308 | first = true; | 317 | first = true; |
309 | 318 | ||
310 | // 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 | + // ==== | ||
311 | }); | 372 | }); |
312 | 373 | ||
313 | network.numTicks = 0; | 374 | network.numTicks = 0; |
314 | network.preventCollisions = false; | 375 | network.preventCollisions = false; |
315 | network.force.start(); | 376 | network.force.start(); |
316 | - for (var i = 0; i < config.ticksWithoutCollisions; i++) { | 377 | + for (var i = 0; i < config.force.ticksWithoutCollisions; i++) { |
317 | network.force.tick(); | 378 | network.force.tick(); |
318 | } | 379 | } |
319 | network.preventCollisions = true; | 380 | network.preventCollisions = true; |
... | @@ -322,22 +383,78 @@ | ... | @@ -322,22 +383,78 @@ |
322 | 383 | ||
323 | } | 384 | } |
324 | 385 | ||
386 | + function iconUrl(d) { | ||
387 | + return config.iconUrl[d.type]; | ||
388 | + } | ||
389 | + | ||
325 | function translate(x, y) { | 390 | function translate(x, y) { |
326 | return 'translate(' + x + ',' + y + ')'; | 391 | return 'translate(' + x + ',' + y + ')'; |
327 | } | 392 | } |
328 | 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 | + } | ||
329 | 439 | ||
330 | function tick(e) { | 440 | function tick(e) { |
331 | network.numTicks++; | 441 | network.numTicks++; |
332 | 442 | ||
333 | - // adjust the y-coord of each node, based on y-pos constraints | 443 | + if (config.layering) { |
334 | -// network.nodes.forEach(function (n) { | 444 | + // adjust the y-coord of each node, based on y-pos constraints |
335 | -// var z = e.alpha * n.constraint.weight; | 445 | + network.nodes.forEach(function (n) { |
336 | -// if (!isNaN(n.constraint.y)) { | 446 | + var z = e.alpha * n.constraint.weight; |
337 | -// n.y = (n.constraint.y * z + n.y * (1 - z)); | 447 | + if (!isNaN(n.constraint.y)) { |
338 | -// } | 448 | + n.y = (n.constraint.y * z + n.y * (1 - z)); |
339 | -// }); | 449 | + } |
450 | + }); | ||
451 | + } | ||
340 | 452 | ||
453 | + if (network.preventCollisions) { | ||
454 | + preventCollisions(); | ||
455 | + } | ||
456 | + | ||
457 | + // TODO: use intersection technique for source end of link also | ||
341 | network.link | 458 | network.link |
342 | .attr('x1', function(d) { | 459 | .attr('x1', function(d) { |
343 | return d.source.x; | 460 | return d.source.x; |
... | @@ -345,11 +462,24 @@ | ... | @@ -345,11 +462,24 @@ |
345 | .attr('y1', function(d) { | 462 | .attr('y1', function(d) { |
346 | return d.source.y; | 463 | return d.source.y; |
347 | }) | 464 | }) |
348 | - .attr('x2', function(d) { | 465 | + .each(function(d) { |
349 | - return d.target.x; | 466 | + var x = d.target.x, |
350 | - }) | 467 | + y = d.target.y, |
351 | - .attr('y2', function(d) { | 468 | + line = new geo.LineSegment(d.source.x, d.source.y, x, y); |
352 | - 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 | + | ||
353 | }); | 483 | }); |
354 | 484 | ||
355 | 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 | } | ... | ... |
... | @@ -85,12 +85,14 @@ svg .node.selected rect { | ... | @@ -85,12 +85,14 @@ svg .node.selected rect { |
85 | 85 | ||
86 | svg .link.inactive, | 86 | svg .link.inactive, |
87 | svg .node.inactive rect, | 87 | svg .node.inactive rect, |
88 | -svg .node.inactive text { | 88 | +svg .node.inactive text, |
89 | +svg .node.inactive image { | ||
89 | opacity: .2; | 90 | opacity: .2; |
90 | } | 91 | } |
91 | 92 | ||
92 | svg .node.inactive.selected rect, | 93 | svg .node.inactive.selected rect, |
93 | -svg .node.inactive.selected text { | 94 | +svg .node.inactive.selected text, |
95 | +svg .node.inactive.selected image { | ||
94 | opacity: .6; | 96 | opacity: .6; |
95 | } | 97 | } |
96 | 98 | ... | ... |
web/gui/src/main/webapp/opt.png
0 → 100644
1.48 KB
web/gui/src/main/webapp/pkt.png
0 → 100644
2.07 KB
... | @@ -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> | ... | ... |
-
Please register or login to post a comment