Showing
151 changed files
with
4699 additions
and
489 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. |
127 | + * | ||
128 | + * @param event the event to record | ||
129 | + * @param updateEventRateMeter if true, update the Event Rate Meter | ||
130 | + */ | ||
131 | + private void recordEvent(Event event, boolean updateEventRateMeter) { | ||
132 | + synchronized (lastEvents) { | ||
95 | lastEventTimestampEpochMs = System.currentTimeMillis(); | 133 | lastEventTimestampEpochMs = System.currentTimeMillis(); |
96 | - // | 134 | + if (updateEventRateMeter) { |
97 | - // NOTE: If we want to count each "reason" as a separate event, | ||
98 | - // then we should use 'event.reason().size()' instead of '1' to | ||
99 | - // mark the meter below. | ||
100 | - // | ||
101 | eventRateMeter.mark(1); | 135 | eventRateMeter.mark(1); |
102 | - | ||
103 | - log.debug("Topology Event: time = {} type = {} subject = {}", | ||
104 | - event.time(), event.type(), event.subject()); | ||
105 | - for (Event reason : event.reasons()) { | ||
106 | - log.debug("Topology Event Reason: time = {} type = {} subject = {}", | ||
107 | - reason.time(), reason.type(), reason.subject()); | ||
108 | } | 136 | } |
109 | 137 | ||
110 | // | 138 | // |
111 | // Keep only the last N events, where N = LAST_EVENTS_MAX_N | 139 | // Keep only the last N events, where N = LAST_EVENTS_MAX_N |
112 | // | 140 | // |
113 | - synchronized (lastEvents) { | ||
114 | while (lastEvents.size() >= LAST_EVENTS_MAX_N) { | 141 | while (lastEvents.size() >= LAST_EVENTS_MAX_N) { |
115 | lastEvents.remove(); | 142 | lastEvents.remove(); |
116 | } | 143 | } |
... | @@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, | ... | @@ -119,11 +146,67 @@ public class TopologyMetrics implements TopologyMetricsService, |
119 | } | 146 | } |
120 | 147 | ||
121 | /** | 148 | /** |
149 | + * Inner Device Event Listener class. | ||
150 | + */ | ||
151 | + private class InnerDeviceListener implements DeviceListener { | ||
152 | + @Override | ||
153 | + public void event(DeviceEvent event) { | ||
154 | + recordEvent(event, true); | ||
155 | + log.debug("Device Event: time = {} type = {} event = {}", | ||
156 | + event.time(), event.type(), event); | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + /** | ||
161 | + * Inner Host Event Listener class. | ||
162 | + */ | ||
163 | + private class InnerHostListener implements HostListener { | ||
164 | + @Override | ||
165 | + public void event(HostEvent event) { | ||
166 | + recordEvent(event, true); | ||
167 | + log.debug("Host Event: time = {} type = {} event = {}", | ||
168 | + event.time(), event.type(), event); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + /** | ||
173 | + * Inner Link Event Listener class. | ||
174 | + */ | ||
175 | + private class InnerLinkListener implements LinkListener { | ||
176 | + @Override | ||
177 | + public void event(LinkEvent event) { | ||
178 | + recordEvent(event, true); | ||
179 | + log.debug("Link Event: time = {} type = {} event = {}", | ||
180 | + event.time(), event.type(), event); | ||
181 | + } | ||
182 | + } | ||
183 | + | ||
184 | + /** | ||
185 | + * Inner Topology Event Listener class. | ||
186 | + */ | ||
187 | + private class InnerTopologyListener implements TopologyListener { | ||
188 | + @Override | ||
189 | + public void event(TopologyEvent event) { | ||
190 | + // | ||
191 | + // NOTE: Don't update the eventRateMeter, because the real | ||
192 | + // events are already captured/counted. | ||
193 | + // | ||
194 | + recordEvent(event, false); | ||
195 | + log.debug("Topology Event: time = {} type = {} event = {}", | ||
196 | + event.time(), event.type(), event); | ||
197 | + for (Event reason : event.reasons()) { | ||
198 | + log.debug("Topology Event Reason: time = {} type = {} event = {}", | ||
199 | + reason.time(), reason.type(), reason); | ||
200 | + } | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + /** | ||
122 | * Clears the internal state. | 205 | * Clears the internal state. |
123 | */ | 206 | */ |
124 | private void clear() { | 207 | private void clear() { |
125 | - lastEventTimestampEpochMs = 0; | ||
126 | synchronized (lastEvents) { | 208 | synchronized (lastEvents) { |
209 | + lastEventTimestampEpochMs = 0; | ||
127 | lastEvents.clear(); | 210 | lastEvents.clear(); |
128 | } | 211 | } |
129 | } | 212 | } | ... | ... |
... | @@ -4,7 +4,7 @@ import java.util.List; | ... | @@ -4,7 +4,7 @@ import java.util.List; |
4 | 4 | ||
5 | import com.codahale.metrics.Gauge; | 5 | import com.codahale.metrics.Gauge; |
6 | import com.codahale.metrics.Meter; | 6 | import com.codahale.metrics.Meter; |
7 | -import org.onlab.onos.net.topology.TopologyEvent; | 7 | +import org.onlab.onos.event.Event; |
8 | 8 | ||
9 | /** | 9 | /** |
10 | * Service interface exported by TopologyMetrics. | 10 | * Service interface exported by TopologyMetrics. |
... | @@ -15,7 +15,7 @@ public interface TopologyMetricsService { | ... | @@ -15,7 +15,7 @@ public interface TopologyMetricsService { |
15 | * | 15 | * |
16 | * @return the last saved topology events. | 16 | * @return the last saved topology events. |
17 | */ | 17 | */ |
18 | - public List<TopologyEvent> getEvents(); | 18 | + public List<Event> getEvents(); |
19 | 19 | ||
20 | /** | 20 | /** |
21 | * Gets the Metrics' Gauge for the last topology event timestamp | 21 | * Gets the Metrics' Gauge for the last topology event timestamp | ... | ... |
... | @@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; | ... | @@ -19,10 +19,8 @@ import org.onlab.onos.net.topology.TopologyEvent; |
19 | description = "Lists the last topology events") | 19 | description = "Lists the last topology events") |
20 | public class TopologyEventsListCommand extends AbstractShellCommand { | 20 | public class TopologyEventsListCommand extends AbstractShellCommand { |
21 | 21 | ||
22 | - private static final String FORMAT_EVENT = | 22 | + private static final String FORMAT_EVENT = "Event=%s"; |
23 | - "Topology Event time=%d type=%s subject=%s"; | 23 | + private static final String FORMAT_REASON = " Reason=%s"; |
24 | - private static final String FORMAT_REASON = | ||
25 | - " Reason time=%d type=%s subject=%s"; | ||
26 | 24 | ||
27 | @Override | 25 | @Override |
28 | protected void execute() { | 26 | protected void execute() { |
... | @@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -31,12 +29,13 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
31 | if (outputJson()) { | 29 | if (outputJson()) { |
32 | print("%s", json(service.getEvents())); | 30 | print("%s", json(service.getEvents())); |
33 | } else { | 31 | } else { |
34 | - for (TopologyEvent event : service.getEvents()) { | 32 | + for (Event event : service.getEvents()) { |
35 | - print(FORMAT_EVENT, event.time(), event.type(), | 33 | + print(FORMAT_EVENT, event); |
36 | - event.subject()); | 34 | + if (event instanceof TopologyEvent) { |
37 | - for (Event reason : event.reasons()) { | 35 | + TopologyEvent topologyEvent = (TopologyEvent) event; |
38 | - print(FORMAT_REASON, reason.time(), reason.type(), | 36 | + for (Event reason : topologyEvent.reasons()) { |
39 | - reason.subject()); | 37 | + print(FORMAT_REASON, reason); |
38 | + } | ||
40 | } | 39 | } |
41 | print(""); // Extra empty line for clarity | 40 | print(""); // Extra empty line for clarity |
42 | } | 41 | } |
... | @@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -46,14 +45,14 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
46 | /** | 45 | /** |
47 | * Produces a JSON array of topology events. | 46 | * Produces a JSON array of topology events. |
48 | * | 47 | * |
49 | - * @param topologyEvents the topology events with the data | 48 | + * @param events the topology events with the data |
50 | * @return JSON array with the topology events | 49 | * @return JSON array with the topology events |
51 | */ | 50 | */ |
52 | - private JsonNode json(List<TopologyEvent> topologyEvents) { | 51 | + private JsonNode json(List<Event> events) { |
53 | ObjectMapper mapper = new ObjectMapper(); | 52 | ObjectMapper mapper = new ObjectMapper(); |
54 | ArrayNode result = mapper.createArrayNode(); | 53 | ArrayNode result = mapper.createArrayNode(); |
55 | 54 | ||
56 | - for (TopologyEvent event : topologyEvents) { | 55 | + for (Event event : events) { |
57 | result.add(json(mapper, event)); | 56 | result.add(json(mapper, event)); |
58 | } | 57 | } |
59 | return result; | 58 | return result; |
... | @@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { | ... | @@ -66,32 +65,23 @@ public class TopologyEventsListCommand extends AbstractShellCommand { |
66 | * @param topologyEvent the topology event with the data | 65 | * @param topologyEvent the topology event with the data |
67 | * @return JSON object for the topology event | 66 | * @return JSON object for the topology event |
68 | */ | 67 | */ |
69 | - private ObjectNode json(ObjectMapper mapper, TopologyEvent topologyEvent) { | 68 | + private ObjectNode json(ObjectMapper mapper, Event event) { |
70 | ObjectNode result = mapper.createObjectNode(); | 69 | ObjectNode result = mapper.createObjectNode(); |
71 | - ArrayNode reasons = mapper.createArrayNode(); | ||
72 | 70 | ||
71 | + result.put("time", event.time()) | ||
72 | + .put("type", event.type().toString()) | ||
73 | + .put("event", event.toString()); | ||
74 | + | ||
75 | + // Add the reasons if a TopologyEvent | ||
76 | + if (event instanceof TopologyEvent) { | ||
77 | + TopologyEvent topologyEvent = (TopologyEvent) event; | ||
78 | + ArrayNode reasons = mapper.createArrayNode(); | ||
73 | for (Event reason : topologyEvent.reasons()) { | 79 | for (Event reason : topologyEvent.reasons()) { |
74 | reasons.add(json(mapper, reason)); | 80 | reasons.add(json(mapper, reason)); |
75 | } | 81 | } |
76 | - result.put("time", topologyEvent.time()) | 82 | + result.put("reasons", reasons); |
77 | - .put("type", topologyEvent.type().toString()) | ||
78 | - .put("subject", topologyEvent.subject().toString()) | ||
79 | - .put("reasons", reasons); | ||
80 | - return result; | ||
81 | } | 83 | } |
82 | 84 | ||
83 | - /** | ||
84 | - * Produces JSON object for a generic event. | ||
85 | - * | ||
86 | - * @param event the generic event with the data | ||
87 | - * @return JSON object for the generic event | ||
88 | - */ | ||
89 | - private ObjectNode json(ObjectMapper mapper, Event event) { | ||
90 | - ObjectNode result = mapper.createObjectNode(); | ||
91 | - | ||
92 | - result.put("time", event.time()) | ||
93 | - .put("type", event.type().toString()) | ||
94 | - .put("subject", event.subject().toString()); | ||
95 | return result; | 85 | return result; |
96 | } | 86 | } |
97 | } | 87 | } | ... | ... |
... | @@ -83,7 +83,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -83,7 +83,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
83 | protected OpticalNetworkConfig opticalNetworkConfig; | 83 | protected OpticalNetworkConfig opticalNetworkConfig; |
84 | 84 | ||
85 | public OpticalConfigProvider() { | 85 | public OpticalConfigProvider() { |
86 | - super(new ProviderId("of", "org.onlab.onos.provider.opticalConfig", true)); | 86 | + super(new ProviderId("optical", "org.onlab.onos.provider.opticalConfig", true)); |
87 | } | 87 | } |
88 | 88 | ||
89 | @Activate | 89 | @Activate |
... | @@ -238,7 +238,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -238,7 +238,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
238 | while (iterWdmNode.hasNext()) { | 238 | while (iterWdmNode.hasNext()) { |
239 | Roadm value = iterWdmNode.next(); | 239 | Roadm value = iterWdmNode.next(); |
240 | DeviceId did = deviceId("of:" + value.getNodeId().replace(":", "")); | 240 | DeviceId did = deviceId("of:" + value.getNodeId().replace(":", "")); |
241 | - ChassisId cid = new ChassisId(value.getNodeId()); | 241 | + ChassisId cid = new ChassisId(); |
242 | DefaultAnnotations extendedAttributes = DefaultAnnotations.builder() | 242 | DefaultAnnotations extendedAttributes = DefaultAnnotations.builder() |
243 | .set(OPTICAL_ANNOTATION + "switchType", "ROADM") | 243 | .set(OPTICAL_ANNOTATION + "switchType", "ROADM") |
244 | .set(OPTICAL_ANNOTATION + "switchName", value.getName()) | 244 | .set(OPTICAL_ANNOTATION + "switchName", value.getName()) |
... | @@ -284,7 +284,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -284,7 +284,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
284 | DefaultLinkDescription linkDescription = | 284 | DefaultLinkDescription linkDescription = |
285 | new DefaultLinkDescription(srcPoint, | 285 | new DefaultLinkDescription(srcPoint, |
286 | snkPoint, | 286 | snkPoint, |
287 | - Link.Type.DIRECT, | 287 | + Link.Type.OPTICAL, |
288 | extendedAttributes); | 288 | extendedAttributes); |
289 | 289 | ||
290 | linkProviderService.linkDetected(linkDescription); | 290 | linkProviderService.linkDetected(linkDescription); |
... | @@ -315,7 +315,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro | ... | @@ -315,7 +315,7 @@ public class OpticalConfigProvider extends AbstractProvider implements DevicePro |
315 | DefaultLinkDescription linkDescription = | 315 | DefaultLinkDescription linkDescription = |
316 | new DefaultLinkDescription(srcPoint, | 316 | new DefaultLinkDescription(srcPoint, |
317 | snkPoint, | 317 | snkPoint, |
318 | - Link.Type.DIRECT, | 318 | + Link.Type.OPTICAL, |
319 | extendedAttributes); | 319 | extendedAttributes); |
320 | 320 | ||
321 | linkProviderService.linkDetected(linkDescription); | 321 | linkProviderService.linkDetected(linkDescription); | ... | ... |
... | @@ -52,9 +52,37 @@ | ... | @@ -52,9 +52,37 @@ |
52 | }, | 52 | }, |
53 | "type": "wdmLink" | 53 | "type": "wdmLink" |
54 | }, | 54 | }, |
55 | + { | ||
56 | + "allowed": true, | ||
57 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", | ||
58 | + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01", | ||
59 | + "params": { | ||
60 | + "distKms": 1000, | ||
61 | + "nodeName1": "ROADM3", | ||
62 | + "nodeName2": "ROADM1", | ||
63 | + "numWaves": 80, | ||
64 | + "port1": 30, | ||
65 | + "port2": 10 | ||
66 | + }, | ||
67 | + "type": "wdmLink" | ||
68 | + }, | ||
55 | 69 | ||
56 | { | 70 | { |
57 | "allowed": true, | 71 | "allowed": true, |
72 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02", | ||
73 | + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03", | ||
74 | + "params": { | ||
75 | + "distKms": 2000, | ||
76 | + "nodeName1": "ROADM2", | ||
77 | + "nodeName2": "ROADM3", | ||
78 | + "numWaves": 80, | ||
79 | + "port1": 20, | ||
80 | + "port2": 31 | ||
81 | + }, | ||
82 | + "type": "wdmLink" | ||
83 | + }, | ||
84 | + { | ||
85 | + "allowed": true, | ||
58 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", | 86 | "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03", |
59 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", | 87 | "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02", |
60 | "params": { | 88 | "params": { |
... | @@ -68,7 +96,6 @@ | ... | @@ -68,7 +96,6 @@ |
68 | "type": "wdmLink" | 96 | "type": "wdmLink" |
69 | }, | 97 | }, |
70 | 98 | ||
71 | - | ||
72 | { | 99 | { |
73 | "allowed": true, | 100 | "allowed": true, |
74 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:01", | 101 | "nodeDpid1": "00:00:ff:ff:ff:ff:00:01", |
... | @@ -82,6 +109,19 @@ | ... | @@ -82,6 +109,19 @@ |
82 | }, | 109 | }, |
83 | "type": "pktOptLink" | 110 | "type": "pktOptLink" |
84 | }, | 111 | }, |
112 | + { | ||
113 | + "allowed": true, | ||
114 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01", | ||
115 | + "nodeDpid2": "00:00:ff:ff:ff:ff:00:01", | ||
116 | + "params": { | ||
117 | + "nodeName1": "ROADM1", | ||
118 | + "nodeName2": "ROUTER1", | ||
119 | + "bandWidth": 100000, | ||
120 | + "port1": 11, | ||
121 | + "port2": 10 | ||
122 | + }, | ||
123 | + "type": "pktOptLink" | ||
124 | + }, | ||
85 | 125 | ||
86 | { | 126 | { |
87 | "allowed": true, | 127 | "allowed": true, |
... | @@ -95,6 +135,19 @@ | ... | @@ -95,6 +135,19 @@ |
95 | "port2": 21 | 135 | "port2": 21 |
96 | }, | 136 | }, |
97 | "type": "pktOptLink" | 137 | "type": "pktOptLink" |
138 | + }, | ||
139 | + { | ||
140 | + "allowed": true, | ||
141 | + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02", | ||
142 | + "nodeDpid2": "00:00:ff:ff:ff:ff:00:02", | ||
143 | + "params": { | ||
144 | + "nodeName1": "ROADM2", | ||
145 | + "nodeName2": "ROUTER2", | ||
146 | + "bandWidth": 100000, | ||
147 | + "port1": 21, | ||
148 | + "port2": 10 | ||
149 | + }, | ||
150 | + "type": "pktOptLink" | ||
98 | } | 151 | } |
99 | 152 | ||
100 | ] | 153 | ] | ... | ... |
... | @@ -4,19 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -4,19 +4,17 @@ import static com.google.common.base.Preconditions.checkNotNull; |
4 | 4 | ||
5 | import java.util.Set; | 5 | import java.util.Set; |
6 | 6 | ||
7 | -import org.apache.commons.lang.NotImplementedException; | ||
8 | import org.onlab.onos.net.ConnectPoint; | 7 | import org.onlab.onos.net.ConnectPoint; |
9 | import org.onlab.onos.net.host.HostService; | 8 | import org.onlab.onos.net.host.HostService; |
10 | import org.onlab.onos.net.host.PortAddresses; | 9 | import org.onlab.onos.net.host.PortAddresses; |
11 | import org.onlab.onos.sdnip.config.Interface; | 10 | import org.onlab.onos.sdnip.config.Interface; |
12 | import org.onlab.packet.IpAddress; | 11 | import org.onlab.packet.IpAddress; |
12 | +import org.onlab.packet.IpPrefix; | ||
13 | 13 | ||
14 | import com.google.common.collect.Sets; | 14 | import com.google.common.collect.Sets; |
15 | 15 | ||
16 | - | ||
17 | - | ||
18 | /** | 16 | /** |
19 | - * Provides IntefaceService using PortAddresses data from the HostService. | 17 | + * Provides InterfaceService using PortAddresses data from the HostService. |
20 | */ | 18 | */ |
21 | public class HostToInterfaceAdaptor implements InterfaceService { | 19 | public class HostToInterfaceAdaptor implements InterfaceService { |
22 | 20 | ||
... | @@ -52,8 +50,17 @@ public class HostToInterfaceAdaptor implements InterfaceService { | ... | @@ -52,8 +50,17 @@ public class HostToInterfaceAdaptor implements InterfaceService { |
52 | 50 | ||
53 | @Override | 51 | @Override |
54 | public Interface getMatchingInterface(IpAddress ipAddress) { | 52 | public Interface getMatchingInterface(IpAddress ipAddress) { |
55 | - // TODO implement | 53 | + checkNotNull(ipAddress); |
56 | - throw new NotImplementedException("getMatchingInteface is not yet implemented"); | 54 | + |
55 | + for (PortAddresses portAddresses : hostService.getAddressBindings()) { | ||
56 | + for (IpPrefix p : portAddresses.ips()) { | ||
57 | + if (p.contains(ipAddress)) { | ||
58 | + return new Interface(portAddresses); | ||
59 | + } | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + return null; | ||
57 | } | 64 | } |
58 | 65 | ||
59 | } | 66 | } | ... | ... |
1 | package org.onlab.onos.sdnip; | 1 | package org.onlab.onos.sdnip; |
2 | 2 | ||
3 | -import com.google.common.base.Objects; | 3 | +import java.util.Collection; |
4 | -import com.google.common.collect.HashMultimap; | 4 | +import java.util.HashMap; |
5 | -import com.google.common.collect.Multimaps; | 5 | +import java.util.HashSet; |
6 | -import com.google.common.collect.SetMultimap; | 6 | +import java.util.Iterator; |
7 | -import com.google.common.util.concurrent.ThreadFactoryBuilder; | 7 | +import java.util.LinkedList; |
8 | -import com.googlecode.concurrenttrees.common.KeyValuePair; | 8 | +import java.util.List; |
9 | -import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory; | 9 | +import java.util.Map; |
10 | -import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; | 10 | +import java.util.Set; |
11 | -import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; | 11 | +import java.util.concurrent.BlockingQueue; |
12 | +import java.util.concurrent.ConcurrentHashMap; | ||
13 | +import java.util.concurrent.ExecutorService; | ||
14 | +import java.util.concurrent.Executors; | ||
15 | +import java.util.concurrent.LinkedBlockingQueue; | ||
16 | +import java.util.concurrent.Semaphore; | ||
17 | + | ||
12 | import org.apache.commons.lang3.tuple.Pair; | 18 | import org.apache.commons.lang3.tuple.Pair; |
13 | import org.onlab.onos.ApplicationId; | 19 | import org.onlab.onos.ApplicationId; |
14 | import org.onlab.onos.net.ConnectPoint; | 20 | import org.onlab.onos.net.ConnectPoint; |
... | @@ -36,20 +42,15 @@ import org.onlab.packet.MacAddress; | ... | @@ -36,20 +42,15 @@ import org.onlab.packet.MacAddress; |
36 | import org.slf4j.Logger; | 42 | import org.slf4j.Logger; |
37 | import org.slf4j.LoggerFactory; | 43 | import org.slf4j.LoggerFactory; |
38 | 44 | ||
39 | -import java.util.Collection; | 45 | +import com.google.common.base.Objects; |
40 | -import java.util.HashMap; | 46 | +import com.google.common.collect.HashMultimap; |
41 | -import java.util.HashSet; | 47 | +import com.google.common.collect.Multimaps; |
42 | -import java.util.Iterator; | 48 | +import com.google.common.collect.SetMultimap; |
43 | -import java.util.LinkedList; | 49 | +import com.google.common.util.concurrent.ThreadFactoryBuilder; |
44 | -import java.util.List; | 50 | +import com.googlecode.concurrenttrees.common.KeyValuePair; |
45 | -import java.util.Map; | 51 | +import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory; |
46 | -import java.util.Set; | 52 | +import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree; |
47 | -import java.util.concurrent.BlockingQueue; | 53 | +import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree; |
48 | -import java.util.concurrent.ConcurrentHashMap; | ||
49 | -import java.util.concurrent.ExecutorService; | ||
50 | -import java.util.concurrent.Executors; | ||
51 | -import java.util.concurrent.LinkedBlockingQueue; | ||
52 | -import java.util.concurrent.Semaphore; | ||
53 | 54 | ||
54 | /** | 55 | /** |
55 | * This class processes BGP route update, translates each update into a intent | 56 | * This class processes BGP route update, translates each update into a intent |
... | @@ -744,6 +745,21 @@ public class Router implements RouteListener { | ... | @@ -744,6 +745,21 @@ public class Router implements RouteListener { |
744 | } | 745 | } |
745 | 746 | ||
746 | /** | 747 | /** |
748 | + * Gets the pushed route intents. | ||
749 | + * | ||
750 | + * @return the pushed route intents | ||
751 | + */ | ||
752 | + public Collection<MultiPointToSinglePointIntent> getPushedRouteIntents() { | ||
753 | + List<MultiPointToSinglePointIntent> pushedIntents = new LinkedList<>(); | ||
754 | + | ||
755 | + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : | ||
756 | + pushedRouteIntents.entrySet()) { | ||
757 | + pushedIntents.add(entry.getValue()); | ||
758 | + } | ||
759 | + return pushedIntents; | ||
760 | + } | ||
761 | + | ||
762 | + /** | ||
747 | * Listener for host events. | 763 | * Listener for host events. |
748 | */ | 764 | */ |
749 | class InternalHostListener implements HostListener { | 765 | class InternalHostListener implements HostListener { | ... | ... |
... | @@ -64,6 +64,7 @@ public class SdnIp implements SdnIpService { | ... | @@ -64,6 +64,7 @@ public class SdnIp implements SdnIpService { |
64 | bgpSessionManager.startUp(2000); // TODO | 64 | bgpSessionManager.startUp(2000); // TODO |
65 | 65 | ||
66 | // TODO need to disable link discovery on external ports | 66 | // TODO need to disable link discovery on external ports |
67 | + | ||
67 | } | 68 | } |
68 | 69 | ||
69 | @Deactivate | 70 | @Deactivate | ... | ... |
1 | +package org.onlab.onos.sdnip; | ||
2 | + | ||
3 | +import static org.easymock.EasyMock.createMock; | ||
4 | +import static org.easymock.EasyMock.expect; | ||
5 | +import static org.easymock.EasyMock.replay; | ||
6 | +import static org.easymock.EasyMock.reset; | ||
7 | +import static org.junit.Assert.assertEquals; | ||
8 | +import static org.junit.Assert.assertNull; | ||
9 | +import static org.junit.Assert.assertTrue; | ||
10 | + | ||
11 | +import java.util.Map; | ||
12 | +import java.util.Set; | ||
13 | + | ||
14 | +import org.junit.Before; | ||
15 | +import org.junit.Test; | ||
16 | +import org.onlab.onos.net.ConnectPoint; | ||
17 | +import org.onlab.onos.net.DeviceId; | ||
18 | +import org.onlab.onos.net.PortNumber; | ||
19 | +import org.onlab.onos.net.host.HostService; | ||
20 | +import org.onlab.onos.net.host.PortAddresses; | ||
21 | +import org.onlab.onos.sdnip.config.Interface; | ||
22 | +import org.onlab.packet.IpAddress; | ||
23 | +import org.onlab.packet.IpPrefix; | ||
24 | +import org.onlab.packet.MacAddress; | ||
25 | + | ||
26 | +import com.google.common.collect.Maps; | ||
27 | +import com.google.common.collect.Sets; | ||
28 | + | ||
29 | +/** | ||
30 | + * Unit tests for the HostToInterfaceAdaptor class. | ||
31 | + */ | ||
32 | +public class HostToInterfaceAdaptorTest { | ||
33 | + | ||
34 | + private HostService hostService; | ||
35 | + private HostToInterfaceAdaptor adaptor; | ||
36 | + | ||
37 | + private Set<PortAddresses> portAddresses; | ||
38 | + private Map<ConnectPoint, Interface> interfaces; | ||
39 | + | ||
40 | + private static final ConnectPoint CP1 = new ConnectPoint( | ||
41 | + DeviceId.deviceId("of:1"), PortNumber.portNumber(1)); | ||
42 | + private static final ConnectPoint CP2 = new ConnectPoint( | ||
43 | + DeviceId.deviceId("of:1"), PortNumber.portNumber(2)); | ||
44 | + private static final ConnectPoint CP3 = new ConnectPoint( | ||
45 | + DeviceId.deviceId("of:2"), PortNumber.portNumber(1)); | ||
46 | + | ||
47 | + private static final ConnectPoint NON_EXISTENT_CP = new ConnectPoint( | ||
48 | + DeviceId.deviceId("doesnotexist"), PortNumber.portNumber(1)); | ||
49 | + | ||
50 | + private static final PortAddresses DEFAULT_PA = new PortAddresses( | ||
51 | + NON_EXISTENT_CP, null, null); | ||
52 | + | ||
53 | + | ||
54 | + @Before | ||
55 | + public void setUp() throws Exception { | ||
56 | + hostService = createMock(HostService.class); | ||
57 | + | ||
58 | + portAddresses = Sets.newHashSet(); | ||
59 | + interfaces = Maps.newHashMap(); | ||
60 | + | ||
61 | + createPortAddressesAndInterface(CP1, | ||
62 | + Sets.newHashSet(IpPrefix.valueOf("192.168.1.1/24")), | ||
63 | + MacAddress.valueOf("00:00:00:00:00:01")); | ||
64 | + | ||
65 | + // Two addresses in the same subnet | ||
66 | + createPortAddressesAndInterface(CP2, | ||
67 | + Sets.newHashSet(IpPrefix.valueOf("192.168.2.1/24"), | ||
68 | + IpPrefix.valueOf("192.168.2.2/24")), | ||
69 | + MacAddress.valueOf("00:00:00:00:00:02")); | ||
70 | + | ||
71 | + // Two addresses in different subnets | ||
72 | + createPortAddressesAndInterface(CP3, | ||
73 | + Sets.newHashSet(IpPrefix.valueOf("192.168.3.1/24"), | ||
74 | + IpPrefix.valueOf("192.168.4.1/24")), | ||
75 | + MacAddress.valueOf("00:00:00:00:00:03")); | ||
76 | + | ||
77 | + expect(hostService.getAddressBindings()).andReturn(portAddresses).anyTimes(); | ||
78 | + | ||
79 | + replay(hostService); | ||
80 | + | ||
81 | + adaptor = new HostToInterfaceAdaptor(hostService); | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * Creates both a PortAddresses and an Interface for the given inputs and | ||
86 | + * places them in the correct global data stores. | ||
87 | + * | ||
88 | + * @param cp the connect point | ||
89 | + * @param ips the set of IP addresses | ||
90 | + * @param mac the MAC address | ||
91 | + */ | ||
92 | + private void createPortAddressesAndInterface( | ||
93 | + ConnectPoint cp, Set<IpPrefix> ips, MacAddress mac) { | ||
94 | + PortAddresses pa = new PortAddresses(cp, ips, mac); | ||
95 | + portAddresses.add(pa); | ||
96 | + expect(hostService.getAddressBindingsForPort(cp)).andReturn(pa).anyTimes(); | ||
97 | + | ||
98 | + Interface intf = new Interface(cp, ips, mac); | ||
99 | + interfaces.put(cp, intf); | ||
100 | + } | ||
101 | + | ||
102 | + /** | ||
103 | + * Tests {@link HostToInterfaceAdaptor#getInterfaces()}. | ||
104 | + * Verifies that the set of interfaces returned matches what is expected | ||
105 | + * based on the input PortAddresses data. | ||
106 | + */ | ||
107 | + @Test | ||
108 | + public void testGetInterfaces() { | ||
109 | + Set<Interface> adaptorIntfs = adaptor.getInterfaces(); | ||
110 | + | ||
111 | + assertEquals(3, adaptorIntfs.size()); | ||
112 | + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP1))); | ||
113 | + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP2))); | ||
114 | + assertTrue(adaptorIntfs.contains(this.interfaces.get(CP3))); | ||
115 | + } | ||
116 | + | ||
117 | + /** | ||
118 | + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)}. | ||
119 | + * Verifies that the correct interface is returned for a given connect | ||
120 | + * point. | ||
121 | + */ | ||
122 | + @Test | ||
123 | + public void testGetInterface() { | ||
124 | + assertEquals(this.interfaces.get(CP1), adaptor.getInterface(CP1)); | ||
125 | + assertEquals(this.interfaces.get(CP2), adaptor.getInterface(CP2)); | ||
126 | + assertEquals(this.interfaces.get(CP3), adaptor.getInterface(CP3)); | ||
127 | + | ||
128 | + // Try and get an interface for a connect point with no addresses | ||
129 | + reset(hostService); | ||
130 | + expect(hostService.getAddressBindingsForPort(NON_EXISTENT_CP)) | ||
131 | + .andReturn(DEFAULT_PA).anyTimes(); | ||
132 | + replay(hostService); | ||
133 | + | ||
134 | + assertNull(adaptor.getInterface(NON_EXISTENT_CP)); | ||
135 | + } | ||
136 | + | ||
137 | + /** | ||
138 | + * Tests {@link HostToInterfaceAdaptor#getInterface(ConnectPoint)} in the | ||
139 | + * case that the input connect point is null. | ||
140 | + * Verifies that a NullPointerException is thrown. | ||
141 | + */ | ||
142 | + @Test(expected = NullPointerException.class) | ||
143 | + public void testGetInterfaceNull() { | ||
144 | + adaptor.getInterface(null); | ||
145 | + } | ||
146 | + | ||
147 | + /** | ||
148 | + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)}. | ||
149 | + * Verifies that the correct interface is returned based on the given IP | ||
150 | + * address. | ||
151 | + */ | ||
152 | + @Test | ||
153 | + public void testGetMatchingInterface() { | ||
154 | + assertEquals(this.interfaces.get(CP1), | ||
155 | + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.1.100"))); | ||
156 | + assertEquals(this.interfaces.get(CP2), | ||
157 | + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.2.100"))); | ||
158 | + assertEquals(this.interfaces.get(CP3), | ||
159 | + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.3.100"))); | ||
160 | + assertEquals(this.interfaces.get(CP3), | ||
161 | + adaptor.getMatchingInterface(IpAddress.valueOf("192.168.4.100"))); | ||
162 | + | ||
163 | + // Try and match an address we don't have subnet configured for | ||
164 | + assertNull(adaptor.getMatchingInterface(IpAddress.valueOf("1.1.1.1"))); | ||
165 | + } | ||
166 | + | ||
167 | + /** | ||
168 | + * Tests {@link HostToInterfaceAdaptor#getMatchingInterface(IpAddress)} in the | ||
169 | + * case that the input IP address is null. | ||
170 | + * Verifies that a NullPointerException is thrown. | ||
171 | + */ | ||
172 | + @Test(expected = NullPointerException.class) | ||
173 | + public void testGetMatchingInterfaceNull() { | ||
174 | + adaptor.getMatchingInterface(null); | ||
175 | + } | ||
176 | + | ||
177 | +} |
1 | +package org.onlab.onos.sdnip; | ||
2 | + | ||
3 | +import static org.easymock.EasyMock.createMock; | ||
4 | +import static org.easymock.EasyMock.expect; | ||
5 | +import static org.easymock.EasyMock.replay; | ||
6 | +import static org.easymock.EasyMock.reset; | ||
7 | +import static org.easymock.EasyMock.verify; | ||
8 | +import static org.junit.Assert.assertEquals; | ||
9 | +import static org.junit.Assert.assertTrue; | ||
10 | + | ||
11 | +import java.util.HashMap; | ||
12 | +import java.util.HashSet; | ||
13 | +import java.util.Map; | ||
14 | +import java.util.Set; | ||
15 | + | ||
16 | +import org.junit.Before; | ||
17 | +import org.junit.Test; | ||
18 | +import org.onlab.onos.ApplicationId; | ||
19 | +import org.onlab.onos.net.ConnectPoint; | ||
20 | +import org.onlab.onos.net.DefaultHost; | ||
21 | +import org.onlab.onos.net.DeviceId; | ||
22 | +import org.onlab.onos.net.Host; | ||
23 | +import org.onlab.onos.net.HostId; | ||
24 | +import org.onlab.onos.net.HostLocation; | ||
25 | +import org.onlab.onos.net.PortNumber; | ||
26 | +import org.onlab.onos.net.flow.DefaultTrafficSelector; | ||
27 | +import org.onlab.onos.net.flow.DefaultTrafficTreatment; | ||
28 | +import org.onlab.onos.net.flow.TrafficSelector; | ||
29 | +import org.onlab.onos.net.flow.TrafficTreatment; | ||
30 | +import org.onlab.onos.net.host.HostService; | ||
31 | +import org.onlab.onos.net.intent.IntentService; | ||
32 | +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent; | ||
33 | +import org.onlab.onos.net.provider.ProviderId; | ||
34 | +import org.onlab.onos.sdnip.config.BgpPeer; | ||
35 | +import org.onlab.onos.sdnip.config.Interface; | ||
36 | +import org.onlab.onos.sdnip.config.SdnIpConfigService; | ||
37 | +import org.onlab.packet.Ethernet; | ||
38 | +import org.onlab.packet.IpAddress; | ||
39 | +import org.onlab.packet.IpPrefix; | ||
40 | +import org.onlab.packet.MacAddress; | ||
41 | +import org.onlab.packet.VlanId; | ||
42 | +import org.onlab.util.TestUtils; | ||
43 | +import org.onlab.util.TestUtils.TestUtilsException; | ||
44 | + | ||
45 | +import com.google.common.collect.Sets; | ||
46 | + | ||
47 | +/** | ||
48 | + * This class tests adding a route, updating a route, deleting a route, | ||
49 | + * and adding a route whose next hop is the local BGP speaker. | ||
50 | + */ | ||
51 | +public class RouterTest { | ||
52 | + | ||
53 | + private SdnIpConfigService sdnIpConfigService; | ||
54 | + private InterfaceService interfaceService; | ||
55 | + private IntentService intentService; | ||
56 | + private HostService hostService; | ||
57 | + | ||
58 | + private Map<IpAddress, BgpPeer> bgpPeers; | ||
59 | + private Map<IpAddress, BgpPeer> configuredPeers; | ||
60 | + private Set<Interface> interfaces; | ||
61 | + private Set<Interface> configuredInterfaces; | ||
62 | + | ||
63 | + private static final ApplicationId APPID = new ApplicationId() { | ||
64 | + @Override | ||
65 | + public short id() { | ||
66 | + return 1; | ||
67 | + } | ||
68 | + | ||
69 | + @Override | ||
70 | + public String name() { | ||
71 | + return "SDNIP"; | ||
72 | + } | ||
73 | + }; | ||
74 | + | ||
75 | + private Router router; | ||
76 | + | ||
77 | + @Before | ||
78 | + public void setUp() throws Exception { | ||
79 | + bgpPeers = setUpBgpPeers(); | ||
80 | + interfaces = setUpInterfaces(); | ||
81 | + initRouter(); | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * Initializes Router class. | ||
86 | + */ | ||
87 | + private void initRouter() { | ||
88 | + | ||
89 | + intentService = createMock(IntentService.class); | ||
90 | + hostService = createMock(HostService.class); | ||
91 | + | ||
92 | + interfaceService = createMock(InterfaceService.class); | ||
93 | + expect(interfaceService.getInterfaces()).andReturn( | ||
94 | + interfaces).anyTimes(); | ||
95 | + | ||
96 | + Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>(); | ||
97 | + ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24")); | ||
98 | + Interface expectedInterface = | ||
99 | + new Interface(new ConnectPoint( | ||
100 | + DeviceId.deviceId("of:0000000000000001"), | ||
101 | + PortNumber.portNumber("1")), | ||
102 | + ipAddressesOnSw1Eth1, | ||
103 | + MacAddress.valueOf("00:00:00:00:00:01")); | ||
104 | + ConnectPoint egressPoint = new ConnectPoint( | ||
105 | + DeviceId.deviceId("of:0000000000000001"), | ||
106 | + PortNumber.portNumber(1)); | ||
107 | + expect(interfaceService.getInterface(egressPoint)).andReturn( | ||
108 | + expectedInterface).anyTimes(); | ||
109 | + | ||
110 | + Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>(); | ||
111 | + ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24")); | ||
112 | + Interface expectedInterfaceNew = | ||
113 | + new Interface(new ConnectPoint( | ||
114 | + DeviceId.deviceId("of:0000000000000002"), | ||
115 | + PortNumber.portNumber("1")), | ||
116 | + ipAddressesOnSw2Eth1, | ||
117 | + MacAddress.valueOf("00:00:00:00:00:02")); | ||
118 | + ConnectPoint egressPointNew = new ConnectPoint( | ||
119 | + DeviceId.deviceId("of:0000000000000002"), | ||
120 | + PortNumber.portNumber(1)); | ||
121 | + expect(interfaceService.getInterface(egressPointNew)).andReturn( | ||
122 | + expectedInterfaceNew).anyTimes(); | ||
123 | + replay(interfaceService); | ||
124 | + | ||
125 | + sdnIpConfigService = createMock(SdnIpConfigService.class); | ||
126 | + expect(sdnIpConfigService.getBgpPeers()).andReturn(bgpPeers).anyTimes(); | ||
127 | + replay(sdnIpConfigService); | ||
128 | + | ||
129 | + router = new Router(APPID, intentService, | ||
130 | + hostService, sdnIpConfigService, interfaceService); | ||
131 | + } | ||
132 | + | ||
133 | + /** | ||
134 | + * Sets up BGP peers in external networks. | ||
135 | + * | ||
136 | + * @return configured BGP peers as a Map from peer IP address to BgpPeer | ||
137 | + */ | ||
138 | + private Map<IpAddress, BgpPeer> setUpBgpPeers() { | ||
139 | + | ||
140 | + configuredPeers = new HashMap<>(); | ||
141 | + | ||
142 | + String peerSw1Eth1 = "192.168.10.1"; | ||
143 | + configuredPeers.put(IpAddress.valueOf(peerSw1Eth1), | ||
144 | + new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1)); | ||
145 | + | ||
146 | + // Two BGP peers are connected to switch 2 port 1. | ||
147 | + String peer1Sw2Eth1 = "192.168.20.1"; | ||
148 | + configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1), | ||
149 | + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1)); | ||
150 | + | ||
151 | + String peer2Sw2Eth1 = "192.168.20.2"; | ||
152 | + configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1), | ||
153 | + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1)); | ||
154 | + | ||
155 | + return configuredPeers; | ||
156 | + } | ||
157 | + | ||
158 | + /** | ||
159 | + * Sets up logical interfaces, which emulate the configured interfaces | ||
160 | + * in SDN-IP application. | ||
161 | + * | ||
162 | + * @return configured interfaces as a Set | ||
163 | + */ | ||
164 | + private Set<Interface> setUpInterfaces() { | ||
165 | + | ||
166 | + configuredInterfaces = Sets.newHashSet(); | ||
167 | + | ||
168 | + Set<IpPrefix> ipAddressesOnSw1Eth1 = new HashSet<IpPrefix>(); | ||
169 | + ipAddressesOnSw1Eth1.add(IpPrefix.valueOf("192.168.10.0/24")); | ||
170 | + configuredInterfaces.add( | ||
171 | + new Interface(new ConnectPoint( | ||
172 | + DeviceId.deviceId("of:0000000000000001"), | ||
173 | + PortNumber.portNumber(1)), | ||
174 | + ipAddressesOnSw1Eth1, | ||
175 | + MacAddress.valueOf("00:00:00:00:00:01"))); | ||
176 | + | ||
177 | + Set<IpPrefix> ipAddressesOnSw2Eth1 = new HashSet<IpPrefix>(); | ||
178 | + ipAddressesOnSw2Eth1.add(IpPrefix.valueOf("192.168.20.0/24")); | ||
179 | + configuredInterfaces.add( | ||
180 | + new Interface(new ConnectPoint( | ||
181 | + DeviceId.deviceId("of:0000000000000002"), | ||
182 | + PortNumber.portNumber(1)), | ||
183 | + ipAddressesOnSw2Eth1, | ||
184 | + MacAddress.valueOf("00:00:00:00:00:02"))); | ||
185 | + | ||
186 | + Set<IpPrefix> ipAddressesOnSw3Eth1 = new HashSet<IpPrefix>(); | ||
187 | + ipAddressesOnSw3Eth1.add(IpPrefix.valueOf("192.168.30.0/24")); | ||
188 | + configuredInterfaces.add( | ||
189 | + new Interface(new ConnectPoint( | ||
190 | + DeviceId.deviceId("of:0000000000000003"), | ||
191 | + PortNumber.portNumber(1)), | ||
192 | + ipAddressesOnSw3Eth1, | ||
193 | + MacAddress.valueOf("00:00:00:00:00:03"))); | ||
194 | + | ||
195 | + return configuredInterfaces; | ||
196 | + } | ||
197 | + | ||
198 | + /** | ||
199 | + * This method tests adding a route entry. | ||
200 | + */ | ||
201 | + @Test | ||
202 | + public void testProcessRouteAdd() throws TestUtilsException { | ||
203 | + | ||
204 | + // Construct a route entry | ||
205 | + RouteEntry routeEntry = new RouteEntry( | ||
206 | + IpPrefix.valueOf("1.1.1.0/24"), | ||
207 | + IpAddress.valueOf("192.168.10.1")); | ||
208 | + | ||
209 | + // Construct a MultiPointToSinglePointIntent intent | ||
210 | + TrafficSelector.Builder selectorBuilder = | ||
211 | + DefaultTrafficSelector.builder(); | ||
212 | + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( | ||
213 | + routeEntry.prefix()); | ||
214 | + | ||
215 | + TrafficTreatment.Builder treatmentBuilder = | ||
216 | + DefaultTrafficTreatment.builder(); | ||
217 | + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); | ||
218 | + | ||
219 | + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>(); | ||
220 | + ingressPoints.add(new ConnectPoint( | ||
221 | + DeviceId.deviceId("of:0000000000000002"), | ||
222 | + PortNumber.portNumber("1"))); | ||
223 | + ingressPoints.add(new ConnectPoint( | ||
224 | + DeviceId.deviceId("of:0000000000000003"), | ||
225 | + PortNumber.portNumber("1"))); | ||
226 | + | ||
227 | + ConnectPoint egressPoint = new ConnectPoint( | ||
228 | + DeviceId.deviceId("of:0000000000000001"), | ||
229 | + PortNumber.portNumber("1")); | ||
230 | + | ||
231 | + MultiPointToSinglePointIntent intent = | ||
232 | + new MultiPointToSinglePointIntent(APPID, | ||
233 | + selectorBuilder.build(), treatmentBuilder.build(), | ||
234 | + ingressPoints, egressPoint); | ||
235 | + | ||
236 | + // Reset host service | ||
237 | + reset(hostService); | ||
238 | + Set<Host> hosts = new HashSet<Host>(1); | ||
239 | + Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>(); | ||
240 | + ipPrefixes.add(IpPrefix.valueOf("192.168.10.1/32")); | ||
241 | + hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE, | ||
242 | + MacAddress.valueOf("00:00:00:00:00:01"), VlanId.NONE, | ||
243 | + new HostLocation( | ||
244 | + DeviceId.deviceId("of:0000000000000001"), | ||
245 | + PortNumber.portNumber(1), 1), | ||
246 | + ipPrefixes)); | ||
247 | + expect(hostService.getHostsByIp( | ||
248 | + IpPrefix.valueOf("192.168.10.1/32"))).andReturn(hosts); | ||
249 | + replay(hostService); | ||
250 | + | ||
251 | + // Set up test expectation | ||
252 | + reset(intentService); | ||
253 | + intentService.submit(intent); | ||
254 | + replay(intentService); | ||
255 | + | ||
256 | + // Call the processRouteAdd() method in Router class | ||
257 | + router.leaderChanged(true); | ||
258 | + TestUtils.setField(router, "isActivatedLeader", true); | ||
259 | + router.processRouteAdd(routeEntry); | ||
260 | + | ||
261 | + // Verify | ||
262 | + assertEquals(router.getRoutes().size(), 1); | ||
263 | + assertTrue(router.getRoutes().contains(routeEntry)); | ||
264 | + assertEquals(router.getPushedRouteIntents().size(), 1); | ||
265 | + assertEquals(router.getPushedRouteIntents().iterator().next(), | ||
266 | + intent); | ||
267 | + verify(intentService); | ||
268 | + } | ||
269 | + | ||
270 | + /** | ||
271 | + * This method tests updating a route entry. | ||
272 | + * | ||
273 | + * @throws TestUtilsException | ||
274 | + */ | ||
275 | + @Test | ||
276 | + public void testRouteUpdate() throws TestUtilsException { | ||
277 | + | ||
278 | + // Firstly add a route | ||
279 | + testProcessRouteAdd(); | ||
280 | + | ||
281 | + // Construct the existing route entry | ||
282 | + RouteEntry routeEntry = new RouteEntry( | ||
283 | + IpPrefix.valueOf("1.1.1.0/24"), | ||
284 | + IpAddress.valueOf("192.168.10.1")); | ||
285 | + | ||
286 | + // Construct the existing MultiPointToSinglePointIntent intent | ||
287 | + TrafficSelector.Builder selectorBuilder = | ||
288 | + DefaultTrafficSelector.builder(); | ||
289 | + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( | ||
290 | + routeEntry.prefix()); | ||
291 | + | ||
292 | + TrafficTreatment.Builder treatmentBuilder = | ||
293 | + DefaultTrafficTreatment.builder(); | ||
294 | + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); | ||
295 | + | ||
296 | + ConnectPoint egressPoint = new ConnectPoint( | ||
297 | + DeviceId.deviceId("of:0000000000000001"), | ||
298 | + PortNumber.portNumber("1")); | ||
299 | + | ||
300 | + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>(); | ||
301 | + ingressPoints.add(new ConnectPoint( | ||
302 | + DeviceId.deviceId("of:0000000000000002"), | ||
303 | + PortNumber.portNumber("1"))); | ||
304 | + ingressPoints.add(new ConnectPoint( | ||
305 | + DeviceId.deviceId("of:0000000000000003"), | ||
306 | + PortNumber.portNumber("1"))); | ||
307 | + | ||
308 | + MultiPointToSinglePointIntent intent = | ||
309 | + new MultiPointToSinglePointIntent(APPID, | ||
310 | + selectorBuilder.build(), treatmentBuilder.build(), | ||
311 | + ingressPoints, egressPoint); | ||
312 | + | ||
313 | + // Start to construct a new route entry and new intent | ||
314 | + RouteEntry routeEntryUpdate = new RouteEntry( | ||
315 | + IpPrefix.valueOf("1.1.1.0/24"), | ||
316 | + IpAddress.valueOf("192.168.20.1")); | ||
317 | + | ||
318 | + // Construct a new MultiPointToSinglePointIntent intent | ||
319 | + TrafficSelector.Builder selectorBuilderNew = | ||
320 | + DefaultTrafficSelector.builder(); | ||
321 | + selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( | ||
322 | + routeEntryUpdate.prefix()); | ||
323 | + | ||
324 | + TrafficTreatment.Builder treatmentBuilderNew = | ||
325 | + DefaultTrafficTreatment.builder(); | ||
326 | + treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02")); | ||
327 | + | ||
328 | + ConnectPoint egressPointNew = new ConnectPoint( | ||
329 | + DeviceId.deviceId("of:0000000000000002"), | ||
330 | + PortNumber.portNumber("1")); | ||
331 | + | ||
332 | + Set<ConnectPoint> ingressPointsNew = new HashSet<ConnectPoint>(); | ||
333 | + ingressPointsNew.add(new ConnectPoint( | ||
334 | + DeviceId.deviceId("of:0000000000000001"), | ||
335 | + PortNumber.portNumber("1"))); | ||
336 | + ingressPointsNew.add(new ConnectPoint( | ||
337 | + DeviceId.deviceId("of:0000000000000003"), | ||
338 | + PortNumber.portNumber("1"))); | ||
339 | + | ||
340 | + MultiPointToSinglePointIntent intentNew = | ||
341 | + new MultiPointToSinglePointIntent(APPID, | ||
342 | + selectorBuilderNew.build(), | ||
343 | + treatmentBuilderNew.build(), | ||
344 | + ingressPointsNew, egressPointNew); | ||
345 | + | ||
346 | + // Reset host service | ||
347 | + reset(hostService); | ||
348 | + Set<Host> hosts = new HashSet<Host>(1); | ||
349 | + Set<IpPrefix> ipPrefixes = new HashSet<IpPrefix>(); | ||
350 | + ipPrefixes.add(IpPrefix.valueOf("192.168.20.1/32")); | ||
351 | + hosts.add(new DefaultHost(ProviderId.NONE, HostId.NONE, | ||
352 | + MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE, | ||
353 | + new HostLocation( | ||
354 | + DeviceId.deviceId("of:0000000000000002"), | ||
355 | + PortNumber.portNumber(1), 1), | ||
356 | + ipPrefixes)); | ||
357 | + expect(hostService.getHostsByIp( | ||
358 | + IpPrefix.valueOf("192.168.20.1/32"))).andReturn(hosts); | ||
359 | + replay(hostService); | ||
360 | + | ||
361 | + // Set up test expectation | ||
362 | + reset(intentService); | ||
363 | + intentService.withdraw(intent); | ||
364 | + intentService.submit(intentNew); | ||
365 | + replay(intentService); | ||
366 | + | ||
367 | + // Call the processRouteAdd() method in Router class | ||
368 | + router.leaderChanged(true); | ||
369 | + TestUtils.setField(router, "isActivatedLeader", true); | ||
370 | + router.processRouteAdd(routeEntryUpdate); | ||
371 | + | ||
372 | + // Verify | ||
373 | + assertEquals(router.getRoutes().size(), 1); | ||
374 | + assertTrue(router.getRoutes().contains(routeEntryUpdate)); | ||
375 | + assertEquals(router.getPushedRouteIntents().size(), 1); | ||
376 | + assertEquals(router.getPushedRouteIntents().iterator().next(), | ||
377 | + intentNew); | ||
378 | + verify(intentService); | ||
379 | + } | ||
380 | + | ||
381 | + /** | ||
382 | + * This method tests deleting a route entry. | ||
383 | + */ | ||
384 | + @Test | ||
385 | + public void testProcessRouteDelete() throws TestUtilsException { | ||
386 | + | ||
387 | + // Firstly add a route | ||
388 | + testProcessRouteAdd(); | ||
389 | + | ||
390 | + // Construct the existing route entry | ||
391 | + RouteEntry routeEntry = new RouteEntry( | ||
392 | + IpPrefix.valueOf("1.1.1.0/24"), | ||
393 | + IpAddress.valueOf("192.168.10.1")); | ||
394 | + | ||
395 | + // Construct the existing MultiPointToSinglePointIntent intent | ||
396 | + TrafficSelector.Builder selectorBuilder = | ||
397 | + DefaultTrafficSelector.builder(); | ||
398 | + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( | ||
399 | + routeEntry.prefix()); | ||
400 | + | ||
401 | + TrafficTreatment.Builder treatmentBuilder = | ||
402 | + DefaultTrafficTreatment.builder(); | ||
403 | + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); | ||
404 | + | ||
405 | + ConnectPoint egressPoint = new ConnectPoint( | ||
406 | + DeviceId.deviceId("of:0000000000000001"), | ||
407 | + PortNumber.portNumber("1")); | ||
408 | + | ||
409 | + Set<ConnectPoint> ingressPoints = new HashSet<ConnectPoint>(); | ||
410 | + ingressPoints.add(new ConnectPoint( | ||
411 | + DeviceId.deviceId("of:0000000000000002"), | ||
412 | + PortNumber.portNumber("1"))); | ||
413 | + ingressPoints.add(new ConnectPoint( | ||
414 | + DeviceId.deviceId("of:0000000000000003"), | ||
415 | + PortNumber.portNumber("1"))); | ||
416 | + | ||
417 | + MultiPointToSinglePointIntent intent = | ||
418 | + new MultiPointToSinglePointIntent(APPID, | ||
419 | + selectorBuilder.build(), treatmentBuilder.build(), | ||
420 | + ingressPoints, egressPoint); | ||
421 | + | ||
422 | + // Set up expectation | ||
423 | + reset(intentService); | ||
424 | + intentService.withdraw(intent); | ||
425 | + replay(intentService); | ||
426 | + | ||
427 | + // Call route deleting method in Router class | ||
428 | + router.leaderChanged(true); | ||
429 | + TestUtils.setField(router, "isActivatedLeader", true); | ||
430 | + router.processRouteDelete(routeEntry); | ||
431 | + | ||
432 | + // Verify | ||
433 | + assertEquals(router.getRoutes().size(), 0); | ||
434 | + assertEquals(router.getPushedRouteIntents().size(), 0); | ||
435 | + verify(intentService); | ||
436 | + } | ||
437 | + | ||
438 | + /** | ||
439 | + * This method tests when the next hop of a route is the local BGP speaker. | ||
440 | + * | ||
441 | + * @throws TestUtilsException | ||
442 | + */ | ||
443 | + @Test | ||
444 | + public void testLocalRouteAdd() throws TestUtilsException { | ||
445 | + | ||
446 | + // Construct a route entry, the next hop is the local BGP speaker | ||
447 | + RouteEntry routeEntry = new RouteEntry( | ||
448 | + IpPrefix.valueOf("1.1.1.0/24"), IpAddress.valueOf("0.0.0.0")); | ||
449 | + | ||
450 | + // Reset intentService to check whether the submit method is called | ||
451 | + reset(intentService); | ||
452 | + replay(intentService); | ||
453 | + | ||
454 | + // Call the processRouteAdd() method in Router class | ||
455 | + router.leaderChanged(true); | ||
456 | + TestUtils.setField(router, "isActivatedLeader", true); | ||
457 | + router.processRouteAdd(routeEntry); | ||
458 | + | ||
459 | + // Verify | ||
460 | + assertEquals(router.getRoutes().size(), 1); | ||
461 | + assertTrue(router.getRoutes().contains(routeEntry)); | ||
462 | + assertEquals(router.getPushedRouteIntents().size(), 0); | ||
463 | + verify(intentService); | ||
464 | + } | ||
465 | +} |
... | @@ -18,10 +18,13 @@ | ... | @@ -18,10 +18,13 @@ |
18 | */ | 18 | */ |
19 | package org.onlab.onos.cli; | 19 | package org.onlab.onos.cli; |
20 | 20 | ||
21 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
22 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | import org.apache.karaf.shell.commands.Option; | 23 | import org.apache.karaf.shell.commands.Option; |
22 | import org.apache.karaf.shell.console.OsgiCommandSupport; | 24 | import org.apache.karaf.shell.console.OsgiCommandSupport; |
23 | import org.onlab.onos.ApplicationId; | 25 | import org.onlab.onos.ApplicationId; |
24 | import org.onlab.onos.CoreService; | 26 | import org.onlab.onos.CoreService; |
27 | +import org.onlab.onos.net.Annotations; | ||
25 | import org.onlab.osgi.DefaultServiceDirectory; | 28 | import org.onlab.osgi.DefaultServiceDirectory; |
26 | import org.onlab.osgi.ServiceNotFoundException; | 29 | import org.onlab.osgi.ServiceNotFoundException; |
27 | 30 | ||
... | @@ -76,6 +79,34 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport { | ... | @@ -76,6 +79,34 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport { |
76 | } | 79 | } |
77 | 80 | ||
78 | /** | 81 | /** |
82 | + * Produces a string image of the specified key/value annotations. | ||
83 | + * | ||
84 | + * @param annotations key/value annotations | ||
85 | + * @return string image with ", k1=v1, k2=v2, ..." pairs | ||
86 | + */ | ||
87 | + public static String annotations(Annotations annotations) { | ||
88 | + StringBuilder sb = new StringBuilder(); | ||
89 | + for (String key : annotations.keys()) { | ||
90 | + sb.append(", ").append(key).append('=').append(annotations.value(key)); | ||
91 | + } | ||
92 | + return sb.toString(); | ||
93 | + } | ||
94 | + | ||
95 | + /** | ||
96 | + * Produces a JSON object from the specified key/value annotations. | ||
97 | + * | ||
98 | + * @param annotations key/value annotations | ||
99 | + * @return JSON object | ||
100 | + */ | ||
101 | + public static ObjectNode annotations(ObjectMapper mapper, Annotations annotations) { | ||
102 | + ObjectNode result = mapper.createObjectNode(); | ||
103 | + for (String key : annotations.keys()) { | ||
104 | + result.put(key, annotations.value(key)); | ||
105 | + } | ||
106 | + return result; | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
79 | * Executes this command. | 110 | * Executes this command. |
80 | */ | 111 | */ |
81 | protected abstract void execute(); | 112 | protected abstract void execute(); | ... | ... |
... | @@ -43,7 +43,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; | ... | @@ -43,7 +43,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; |
43 | description = "Lists all ports or all ports of a device") | 43 | description = "Lists all ports or all ports of a device") |
44 | public class DevicePortsListCommand extends DevicesListCommand { | 44 | public class DevicePortsListCommand extends DevicesListCommand { |
45 | 45 | ||
46 | - private static final String FMT = " port=%s, state=%s"; | 46 | + private static final String FMT = " port=%s, state=%s%s"; |
47 | 47 | ||
48 | @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports", | 48 | @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports", |
49 | required = false, multiValued = false) | 49 | required = false, multiValued = false) |
... | @@ -112,7 +112,8 @@ public class DevicePortsListCommand extends DevicesListCommand { | ... | @@ -112,7 +112,8 @@ public class DevicePortsListCommand extends DevicesListCommand { |
112 | if (isIncluded(port)) { | 112 | if (isIncluded(port)) { |
113 | ports.add(mapper.createObjectNode() | 113 | ports.add(mapper.createObjectNode() |
114 | .put("port", port.number().toString()) | 114 | .put("port", port.number().toString()) |
115 | - .put("isEnabled", port.isEnabled())); | 115 | + .put("isEnabled", port.isEnabled()) |
116 | + .set("annotations", annotations(mapper, port.annotations()))); | ||
116 | } | 117 | } |
117 | } | 118 | } |
118 | return result.put("device", device.id().toString()).set("ports", ports); | 119 | return result.put("device", device.id().toString()).set("ports", ports); |
... | @@ -131,7 +132,8 @@ public class DevicePortsListCommand extends DevicesListCommand { | ... | @@ -131,7 +132,8 @@ public class DevicePortsListCommand extends DevicesListCommand { |
131 | Collections.sort(ports, Comparators.PORT_COMPARATOR); | 132 | Collections.sort(ports, Comparators.PORT_COMPARATOR); |
132 | for (Port port : ports) { | 133 | for (Port port : ports) { |
133 | if (isIncluded(port)) { | 134 | if (isIncluded(port)) { |
134 | - print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled"); | 135 | + print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled", |
136 | + annotations(port.annotations())); | ||
135 | } | 137 | } |
136 | } | 138 | } |
137 | } | 139 | } | ... | ... |
... | @@ -41,7 +41,7 @@ import static com.google.common.collect.Lists.newArrayList; | ... | @@ -41,7 +41,7 @@ import static com.google.common.collect.Lists.newArrayList; |
41 | public class DevicesListCommand extends AbstractShellCommand { | 41 | public class DevicesListCommand extends AbstractShellCommand { |
42 | 42 | ||
43 | private static final String FMT = | 43 | private static final String FMT = |
44 | - "id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s"; | 44 | + "id=%s, available=%s, role=%s, type=%s, mfr=%s, hw=%s, sw=%s, serial=%s%s"; |
45 | 45 | ||
46 | @Override | 46 | @Override |
47 | protected void execute() { | 47 | protected void execute() { |
... | @@ -89,7 +89,8 @@ public class DevicesListCommand extends AbstractShellCommand { | ... | @@ -89,7 +89,8 @@ public class DevicesListCommand extends AbstractShellCommand { |
89 | .put("mfr", device.manufacturer()) | 89 | .put("mfr", device.manufacturer()) |
90 | .put("hw", device.hwVersion()) | 90 | .put("hw", device.hwVersion()) |
91 | .put("sw", device.swVersion()) | 91 | .put("sw", device.swVersion()) |
92 | - .put("serial", device.serialNumber()); | 92 | + .put("serial", device.serialNumber()) |
93 | + .set("annotations", annotations(mapper, device.annotations())); | ||
93 | } | 94 | } |
94 | return result; | 95 | return result; |
95 | } | 96 | } |
... | @@ -117,7 +118,7 @@ public class DevicesListCommand extends AbstractShellCommand { | ... | @@ -117,7 +118,7 @@ public class DevicesListCommand extends AbstractShellCommand { |
117 | print(FMT, device.id(), service.isAvailable(device.id()), | 118 | print(FMT, device.id(), service.isAvailable(device.id()), |
118 | service.getRole(device.id()), device.type(), | 119 | service.getRole(device.id()), device.type(), |
119 | device.manufacturer(), device.hwVersion(), device.swVersion(), | 120 | device.manufacturer(), device.hwVersion(), device.swVersion(), |
120 | - device.serialNumber()); | 121 | + device.serialNumber(), annotations(device.annotations())); |
121 | } | 122 | } |
122 | } | 123 | } |
123 | 124 | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.cli.net; | ||
20 | + | ||
21 | +import org.apache.karaf.shell.commands.Argument; | ||
22 | +import org.apache.karaf.shell.commands.Command; | ||
23 | +import org.onlab.onos.cli.AbstractShellCommand; | ||
24 | +import org.onlab.onos.net.ConnectPoint; | ||
25 | +import org.onlab.onos.net.DeviceId; | ||
26 | +import org.onlab.onos.net.PortNumber; | ||
27 | + | ||
28 | +import org.onlab.onos.net.statistic.Load; | ||
29 | +import org.onlab.onos.net.statistic.StatisticService; | ||
30 | + | ||
31 | + | ||
32 | +import static org.onlab.onos.net.DeviceId.deviceId; | ||
33 | +import static org.onlab.onos.net.PortNumber.portNumber; | ||
34 | + | ||
35 | +/** | ||
36 | + * Fetches statistics. | ||
37 | + */ | ||
38 | +@Command(scope = "onos", name = "get-stats", | ||
39 | + description = "Fetches stats for a connection point") | ||
40 | +public class GetStatistics extends AbstractShellCommand { | ||
41 | + | ||
42 | + @Argument(index = 0, name = "connectPoint", | ||
43 | + description = "Device/Port Description", | ||
44 | + required = true, multiValued = false) | ||
45 | + String connectPoint = null; | ||
46 | + | ||
47 | + | ||
48 | + @Override | ||
49 | + protected void execute() { | ||
50 | + StatisticService service = get(StatisticService.class); | ||
51 | + | ||
52 | + DeviceId ingressDeviceId = deviceId(getDeviceId(connectPoint)); | ||
53 | + PortNumber ingressPortNumber = portNumber(getPortNumber(connectPoint)); | ||
54 | + ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber); | ||
55 | + | ||
56 | + Load load = service.load(cp); | ||
57 | + | ||
58 | + print("Load on %s -> %s", cp, load); | ||
59 | + } | ||
60 | + | ||
61 | + /** | ||
62 | + * Extracts the port number portion of the ConnectPoint. | ||
63 | + * | ||
64 | + * @param deviceString string representing the device/port | ||
65 | + * @return port number as a string, empty string if the port is not found | ||
66 | + */ | ||
67 | + private String getPortNumber(String deviceString) { | ||
68 | + int slash = deviceString.indexOf('/'); | ||
69 | + if (slash <= 0) { | ||
70 | + return ""; | ||
71 | + } | ||
72 | + return deviceString.substring(slash + 1, deviceString.length()); | ||
73 | + } | ||
74 | + | ||
75 | + /** | ||
76 | + * Extracts the device ID portion of the ConnectPoint. | ||
77 | + * | ||
78 | + * @param deviceString string representing the device/port | ||
79 | + * @return device ID string | ||
80 | + */ | ||
81 | + private String getDeviceId(String deviceString) { | ||
82 | + int slash = deviceString.indexOf('/'); | ||
83 | + if (slash <= 0) { | ||
84 | + return ""; | ||
85 | + } | ||
86 | + return deviceString.substring(0, slash); | ||
87 | + } | ||
88 | +} |
... | @@ -42,7 +42,7 @@ import static com.google.common.collect.Lists.newArrayList; | ... | @@ -42,7 +42,7 @@ import static com.google.common.collect.Lists.newArrayList; |
42 | public class HostsListCommand extends AbstractShellCommand { | 42 | public class HostsListCommand extends AbstractShellCommand { |
43 | 43 | ||
44 | private static final String FMT = | 44 | private static final String FMT = |
45 | - "id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s"; | 45 | + "id=%s, mac=%s, location=%s/%s, vlan=%s, ip(s)=%s%s"; |
46 | 46 | ||
47 | @Override | 47 | @Override |
48 | protected void execute() { | 48 | protected void execute() { |
... | @@ -80,6 +80,7 @@ public class HostsListCommand extends AbstractShellCommand { | ... | @@ -80,6 +80,7 @@ public class HostsListCommand extends AbstractShellCommand { |
80 | .put("vlan", host.vlan().toString()); | 80 | .put("vlan", host.vlan().toString()); |
81 | result.set("location", loc); | 81 | result.set("location", loc); |
82 | result.set("ips", ips); | 82 | result.set("ips", ips); |
83 | + result.set("annotations", annotations(mapper, host.annotations())); | ||
83 | return result; | 84 | return result; |
84 | } | 85 | } |
85 | 86 | ||
... | @@ -105,7 +106,8 @@ public class HostsListCommand extends AbstractShellCommand { | ... | @@ -105,7 +106,8 @@ public class HostsListCommand extends AbstractShellCommand { |
105 | print(FMT, host.id(), host.mac(), | 106 | print(FMT, host.id(), host.mac(), |
106 | host.location().deviceId(), | 107 | host.location().deviceId(), |
107 | host.location().port(), | 108 | host.location().port(), |
108 | - host.vlan(), host.ipAddresses()); | 109 | + host.vlan(), host.ipAddresses(), |
110 | + annotations(host.annotations())); | ||
109 | } | 111 | } |
110 | } | 112 | } |
111 | } | 113 | } | ... | ... |
... | @@ -38,7 +38,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; | ... | @@ -38,7 +38,7 @@ import static org.onlab.onos.net.DeviceId.deviceId; |
38 | description = "Lists all infrastructure links") | 38 | description = "Lists all infrastructure links") |
39 | public class LinksListCommand extends AbstractShellCommand { | 39 | public class LinksListCommand extends AbstractShellCommand { |
40 | 40 | ||
41 | - private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s"; | 41 | + private static final String FMT = "src=%s/%s, dst=%s/%s, type=%s%s"; |
42 | private static final String COMPACT = "%s/%s-%s/%s"; | 42 | private static final String COMPACT = "%s/%s-%s/%s"; |
43 | 43 | ||
44 | @Argument(index = 0, name = "uri", description = "Device ID", | 44 | @Argument(index = 0, name = "uri", description = "Device ID", |
... | @@ -85,6 +85,7 @@ public class LinksListCommand extends AbstractShellCommand { | ... | @@ -85,6 +85,7 @@ public class LinksListCommand extends AbstractShellCommand { |
85 | ObjectNode result = mapper.createObjectNode(); | 85 | ObjectNode result = mapper.createObjectNode(); |
86 | result.set("src", json(mapper, link.src())); | 86 | result.set("src", json(mapper, link.src())); |
87 | result.set("dst", json(mapper, link.dst())); | 87 | result.set("dst", json(mapper, link.dst())); |
88 | + result.set("annotations", annotations(mapper, link.annotations())); | ||
88 | return result; | 89 | return result; |
89 | } | 90 | } |
90 | 91 | ||
... | @@ -109,7 +110,8 @@ public class LinksListCommand extends AbstractShellCommand { | ... | @@ -109,7 +110,8 @@ public class LinksListCommand extends AbstractShellCommand { |
109 | */ | 110 | */ |
110 | public static String linkString(Link link) { | 111 | public static String linkString(Link link) { |
111 | return String.format(FMT, link.src().deviceId(), link.src().port(), | 112 | return String.format(FMT, link.src().deviceId(), link.src().port(), |
112 | - link.dst().deviceId(), link.dst().port(), link.type()); | 113 | + link.dst().deviceId(), link.dst().port(), link.type(), |
114 | + annotations(link.annotations())); | ||
113 | } | 115 | } |
114 | 116 | ||
115 | /** | 117 | /** | ... | ... |
... | @@ -20,8 +20,10 @@ package org.onlab.onos.cli.net; | ... | @@ -20,8 +20,10 @@ package org.onlab.onos.cli.net; |
20 | 20 | ||
21 | import com.fasterxml.jackson.databind.ObjectMapper; | 21 | import com.fasterxml.jackson.databind.ObjectMapper; |
22 | import org.apache.karaf.shell.commands.Command; | 22 | import org.apache.karaf.shell.commands.Command; |
23 | +import org.apache.karaf.shell.commands.Option; | ||
23 | import org.onlab.onos.cli.AbstractShellCommand; | 24 | import org.onlab.onos.cli.AbstractShellCommand; |
24 | import org.onlab.onos.net.topology.Topology; | 25 | import org.onlab.onos.net.topology.Topology; |
26 | +import org.onlab.onos.net.topology.TopologyProvider; | ||
25 | import org.onlab.onos.net.topology.TopologyService; | 27 | import org.onlab.onos.net.topology.TopologyService; |
26 | 28 | ||
27 | /** | 29 | /** |
... | @@ -35,6 +37,10 @@ public class TopologyCommand extends AbstractShellCommand { | ... | @@ -35,6 +37,10 @@ public class TopologyCommand extends AbstractShellCommand { |
35 | private static final String FMT = | 37 | private static final String FMT = |
36 | "time=%s, devices=%d, links=%d, clusters=%d, paths=%d"; | 38 | "time=%s, devices=%d, links=%d, clusters=%d, paths=%d"; |
37 | 39 | ||
40 | + @Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation", | ||
41 | + required = false, multiValued = false) | ||
42 | + private boolean recompute = false; | ||
43 | + | ||
38 | protected TopologyService service; | 44 | protected TopologyService service; |
39 | protected Topology topology; | 45 | protected Topology topology; |
40 | 46 | ||
... | @@ -49,7 +55,10 @@ public class TopologyCommand extends AbstractShellCommand { | ... | @@ -49,7 +55,10 @@ public class TopologyCommand extends AbstractShellCommand { |
49 | @Override | 55 | @Override |
50 | protected void execute() { | 56 | protected void execute() { |
51 | init(); | 57 | init(); |
52 | - if (outputJson()) { | 58 | + if (recompute) { |
59 | + get(TopologyProvider.class).triggerRecompute(); | ||
60 | + | ||
61 | + } else if (outputJson()) { | ||
53 | print("%s", new ObjectMapper().createObjectNode() | 62 | print("%s", new ObjectMapper().createObjectNode() |
54 | .put("time", topology.time()) | 63 | .put("time", topology.time()) |
55 | .put("deviceCount", topology.deviceCount()) | 64 | .put("deviceCount", topology.deviceCount()) | ... | ... |
... | @@ -119,6 +119,12 @@ | ... | @@ -119,6 +119,12 @@ |
119 | </optional-completers> | 119 | </optional-completers> |
120 | </command> | 120 | </command> |
121 | <command> | 121 | <command> |
122 | + <action class="org.onlab.onos.cli.net.GetStatistics"/> | ||
123 | + <completers> | ||
124 | + <ref component-id="connectPointCompleter"/> | ||
125 | + </completers> | ||
126 | + </command> | ||
127 | + <command> | ||
122 | <action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/> | 128 | <action class="org.onlab.onos.cli.net.AddMultiPointToSinglePointIntentCommand"/> |
123 | <completers> | 129 | <completers> |
124 | <ref component-id="connectPointCompleter"/> | 130 | <ref component-id="connectPointCompleter"/> | ... | ... |
1 | package org.onlab.onos.mastership; | 1 | package org.onlab.onos.mastership; |
2 | 2 | ||
3 | -import org.onlab.onos.cluster.NodeId; | ||
4 | import org.onlab.onos.cluster.RoleInfo; | 3 | import org.onlab.onos.cluster.RoleInfo; |
5 | import org.onlab.onos.event.AbstractEvent; | 4 | import org.onlab.onos.event.AbstractEvent; |
6 | import org.onlab.onos.net.DeviceId; | 5 | import org.onlab.onos.net.DeviceId; |
... | @@ -56,19 +55,6 @@ public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceI | ... | @@ -56,19 +55,6 @@ public class MastershipEvent extends AbstractEvent<MastershipEvent.Type, DeviceI |
56 | } | 55 | } |
57 | 56 | ||
58 | /** | 57 | /** |
59 | - * Returns the NodeID of the node associated with the event. | ||
60 | - * For MASTER_CHANGED this is the newly elected master, and for | ||
61 | - * BACKUPS_CHANGED, this is the node that was newly added, removed, or | ||
62 | - * whose position was changed in the list. | ||
63 | - * | ||
64 | - * @return node ID as a subject | ||
65 | - */ | ||
66 | - //XXX to-be removed - or keep for convenience? | ||
67 | - public NodeId node() { | ||
68 | - return roleInfo.master(); | ||
69 | - } | ||
70 | - | ||
71 | - /** | ||
72 | * Returns the current role state for the subject. | 58 | * Returns the current role state for the subject. |
73 | * | 59 | * |
74 | * @return RoleInfo associated with Device ID subject | 60 | * @return RoleInfo associated with Device ID subject | ... | ... |
... | @@ -25,7 +25,18 @@ public interface Link extends Annotated, Provided, NetworkResource { | ... | @@ -25,7 +25,18 @@ public interface Link extends Annotated, Provided, NetworkResource { |
25 | /** | 25 | /** |
26 | * Signifies that this link is an edge, i.e. host link. | 26 | * Signifies that this link is an edge, i.e. host link. |
27 | */ | 27 | */ |
28 | - EDGE | 28 | + EDGE, |
29 | + | ||
30 | + /** | ||
31 | + * Signifies that this link represents a logical link backed by | ||
32 | + * some form of a tunnel. | ||
33 | + */ | ||
34 | + TUNNEL, | ||
35 | + | ||
36 | + /** | ||
37 | + * Signifies that this link is realized by optical connection. | ||
38 | + */ | ||
39 | + OPTICAL | ||
29 | } | 40 | } |
30 | 41 | ||
31 | /** | 42 | /** |
... | @@ -49,6 +60,4 @@ public interface Link extends Annotated, Provided, NetworkResource { | ... | @@ -49,6 +60,4 @@ public interface Link extends Annotated, Provided, NetworkResource { |
49 | */ | 60 | */ |
50 | Type type(); | 61 | Type type(); |
51 | 62 | ||
52 | - // LinkInfo info(); // Additional link information / decorations | ||
53 | - | ||
54 | } | 63 | } | ... | ... |
... | @@ -4,6 +4,8 @@ import org.onlab.onos.event.AbstractEvent; | ... | @@ -4,6 +4,8 @@ import org.onlab.onos.event.AbstractEvent; |
4 | import org.onlab.onos.net.Device; | 4 | import org.onlab.onos.net.Device; |
5 | import org.onlab.onos.net.Port; | 5 | import org.onlab.onos.net.Port; |
6 | 6 | ||
7 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
8 | + | ||
7 | /** | 9 | /** |
8 | * Describes infrastructure device event. | 10 | * Describes infrastructure device event. |
9 | */ | 11 | */ |
... | @@ -109,4 +111,12 @@ public class DeviceEvent extends AbstractEvent<DeviceEvent.Type, Device> { | ... | @@ -109,4 +111,12 @@ public class DeviceEvent extends AbstractEvent<DeviceEvent.Type, Device> { |
109 | return port; | 111 | return port; |
110 | } | 112 | } |
111 | 113 | ||
114 | + @Override | ||
115 | + public String toString() { | ||
116 | + if (port == null) { | ||
117 | + return super.toString(); | ||
118 | + } | ||
119 | + return toStringHelper(this).add("time", time()).add("type", type()) | ||
120 | + .add("subject", subject()).add("port", port).toString(); | ||
121 | + } | ||
112 | } | 122 | } | ... | ... |
... | @@ -13,7 +13,7 @@ public interface DeviceProvider extends Provider { | ... | @@ -13,7 +13,7 @@ public interface DeviceProvider extends Provider { |
13 | 13 | ||
14 | /** | 14 | /** |
15 | * Triggers an asynchronous probe of the specified device, intended to | 15 | * Triggers an asynchronous probe of the specified device, intended to |
16 | - * determine whether the host is present or not. An indirect result of this | 16 | + * determine whether the device is present or not. An indirect result of this |
17 | * should be invocation of | 17 | * should be invocation of |
18 | * {@link org.onlab.onos.net.device.DeviceProviderService#deviceConnected} )} or | 18 | * {@link org.onlab.onos.net.device.DeviceProviderService#deviceConnected} )} or |
19 | * {@link org.onlab.onos.net.device.DeviceProviderService#deviceDisconnected} | 19 | * {@link org.onlab.onos.net.device.DeviceProviderService#deviceDisconnected} | ... | ... |
1 | -package org.onlab.onos.net.intent; | 1 | +/* |
2 | -//TODO is this the right package? | 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.net.flow; | ||
3 | 20 | ||
4 | import static com.google.common.base.Preconditions.checkNotNull; | 21 | import static com.google.common.base.Preconditions.checkNotNull; |
5 | 22 | ... | ... |
1 | -package org.onlab.onos.net.intent; | 1 | +/* |
2 | -//TODO is this the right package? | 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.net.flow; | ||
3 | 20 | ||
4 | import java.util.Objects; | 21 | import java.util.Objects; |
5 | 22 | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.Set; | 21 | import java.util.Set; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.net.flow; | ||
20 | + | ||
21 | +/** | ||
22 | + * An interface of the class which is assigned to BatchOperation. | ||
23 | + */ | ||
24 | +public interface BatchOperationTarget { | ||
25 | + | ||
26 | +} |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.Set; | 21 | import java.util.Set; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.HashMap; | 21 | import java.util.HashMap; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.LinkedList; | 21 | import java.util.LinkedList; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | 21 | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import com.google.common.base.Objects; | 21 | import com.google.common.base.Objects; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import org.onlab.onos.net.DeviceId; | 21 | import org.onlab.onos.net.DeviceId; |
4 | -import org.onlab.onos.net.intent.BatchOperationTarget; | ||
5 | 22 | ||
6 | /** | 23 | /** |
7 | * Represents a generalized match & 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 org.onlab.onos.ApplicationId; | 21 | import org.onlab.onos.ApplicationId; |
4 | -import org.onlab.onos.net.intent.BatchOperation; | ||
5 | import org.onlab.onos.net.provider.Provider; | 22 | import org.onlab.onos.net.provider.Provider; |
6 | 23 | ||
7 | import com.google.common.util.concurrent.ListenableFuture; | 24 | import com.google.common.util.concurrent.ListenableFuture; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import org.onlab.onos.net.provider.ProviderRegistry; | 21 | import org.onlab.onos.net.provider.ProviderRegistry; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import org.onlab.onos.net.DeviceId; | 21 | import org.onlab.onos.net.DeviceId; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.concurrent.Future; | 21 | import java.util.concurrent.Future; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.concurrent.Future; | 21 | import java.util.concurrent.Future; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import org.onlab.onos.store.StoreDelegate; | 21 | import org.onlab.onos.store.StoreDelegate; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | 21 | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.Set; | 21 | import java.util.Set; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import java.util.List; | 21 | import java.util.List; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow; | 19 | package org.onlab.onos.net.flow; |
2 | 20 | ||
3 | import org.onlab.onos.net.PortNumber; | 21 | import org.onlab.onos.net.PortNumber; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.criteria; | 19 | package org.onlab.onos.net.flow.criteria; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.criteria; | 19 | package org.onlab.onos.net.flow.criteria; |
2 | 20 | ||
3 | 21 | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | + | ||
1 | /** | 20 | /** |
2 | * Traffic selection criteria model. | 21 | * Traffic selection criteria model. |
3 | */ | 22 | */ | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.instructions; | 19 | package org.onlab.onos.net.flow.instructions; |
2 | 20 | ||
3 | /** | 21 | /** | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.instructions; | 19 | package org.onlab.onos.net.flow.instructions; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.instructions; | 19 | package org.onlab.onos.net.flow.instructions; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.flow.instructions; | 19 | package org.onlab.onos.net.flow.instructions; |
2 | 20 | ||
3 | import static com.google.common.base.MoreObjects.toStringHelper; | 21 | import static com.google.common.base.MoreObjects.toStringHelper; | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | + | ||
1 | /** | 20 | /** |
2 | * Traffic treatment model. | 21 | * Traffic treatment model. |
3 | */ | 22 | */ | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | + | ||
1 | /** | 20 | /** |
2 | * Flow rule model & 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. |
32 | + */ | ||
33 | +public final class IntentOperations { | ||
34 | + | ||
35 | + private final List<IntentOperation> operations; | ||
36 | + | ||
37 | + /** | ||
38 | + * Creates a batch of intent operations using the supplied list. | ||
39 | + * | ||
40 | + * @param operations list of intent operations | ||
41 | + */ | ||
42 | + private IntentOperations(List<IntentOperation> operations) { | ||
43 | + this.operations = operations; | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * List of operations that need to be executed as a unit. | ||
48 | + * | ||
49 | + * @return list of intent operations | ||
50 | + */ | ||
51 | + public List<IntentOperation> operations() { | ||
52 | + return operations; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * Returns a builder for intent operation batches. | ||
57 | + * | ||
58 | + * @return intent operations builder | ||
5 | */ | 59 | */ |
6 | -public interface IntentOperations { | 60 | + public static Builder builder() { |
61 | + return new Builder(); | ||
62 | + } | ||
7 | 63 | ||
8 | - // TODO: elaborate once the revised BatchOperation scheme is in place | 64 | + /** |
65 | + * Builder for batches of intent operations. | ||
66 | + */ | ||
67 | + public static final class Builder { | ||
68 | + | ||
69 | + private final ImmutableList.Builder<IntentOperation> builder = ImmutableList.builder(); | ||
70 | + | ||
71 | + // Public construction is forbidden. | ||
72 | + private Builder() { | ||
73 | + } | ||
74 | + | ||
75 | + /** | ||
76 | + * Adds an intent submit operation. | ||
77 | + * | ||
78 | + * @param intent intent to be submitted | ||
79 | + * @return self | ||
80 | + */ | ||
81 | + public Builder addSubmitOperation(Intent intent) { | ||
82 | + checkNotNull(intent, "Intent cannot be null"); | ||
83 | + builder.add(new IntentOperation(SUBMIT, intent.id(), intent)); | ||
84 | + return this; | ||
85 | + } | ||
86 | + | ||
87 | + /** | ||
88 | + * Adds an intent submit operation. | ||
89 | + * | ||
90 | + * @param oldIntentId intent to be replaced | ||
91 | + * @param newIntent replacement intent | ||
92 | + * @return self | ||
93 | + */ | ||
94 | + public Builder addReplaceOperation(IntentId oldIntentId, Intent newIntent) { | ||
95 | + checkNotNull(oldIntentId, "Intent ID cannot be null"); | ||
96 | + checkNotNull(newIntent, "Intent cannot be null"); | ||
97 | + builder.add(new IntentOperation(REPLACE, oldIntentId, newIntent)); | ||
98 | + return this; | ||
99 | + } | ||
100 | + | ||
101 | + /** | ||
102 | + * Adds an intent submit operation. | ||
103 | + * | ||
104 | + * @param intentId identifier of the intent to be withdrawn | ||
105 | + * @return self | ||
106 | + */ | ||
107 | + public Builder addWithdrawOperation(IntentId intentId) { | ||
108 | + checkNotNull(intentId, "Intent ID cannot be null"); | ||
109 | + builder.add(new IntentOperation(WITHDRAW, intentId, null)); | ||
110 | + return this; | ||
111 | + } | ||
112 | + | ||
113 | + /** | ||
114 | + * Builds a batch of intent operations. | ||
115 | + * | ||
116 | + * @return immutable batch of intent operations | ||
117 | + */ | ||
118 | + public IntentOperations build() { | ||
119 | + return new IntentOperations(builder.build()); | ||
120 | + } | ||
9 | 121 | ||
122 | + } | ||
10 | } | 123 | } | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
1 | package org.onlab.onos.net.intent; | 19 | package org.onlab.onos.net.intent; |
2 | 20 | ||
3 | 21 | ||
4 | import java.util.List; | 22 | import java.util.List; |
23 | +import java.util.concurrent.Future; | ||
5 | 24 | ||
6 | /** | 25 | /** |
7 | * Service for application submitting or withdrawing their intents. | 26 | * Service for application submitting or withdrawing their intents. |
... | @@ -28,6 +47,14 @@ public interface IntentService { | ... | @@ -28,6 +47,14 @@ public interface IntentService { |
28 | void withdraw(Intent intent); | 47 | void withdraw(Intent intent); |
29 | 48 | ||
30 | /** | 49 | /** |
50 | + * Replaces the specified intent with a new one. | ||
51 | + * | ||
52 | + * @param oldIntentId identifier of the old intent being replaced | ||
53 | + * @param newIntent new intent replacing the old one | ||
54 | + */ | ||
55 | + void replace(IntentId oldIntentId, Intent newIntent); | ||
56 | + | ||
57 | + /** | ||
31 | * Submits a batch of submit & 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 | ... | ... |
... | @@ -161,6 +161,17 @@ public class DeviceManager | ... | @@ -161,6 +161,17 @@ public class DeviceManager |
161 | } | 161 | } |
162 | } | 162 | } |
163 | 163 | ||
164 | + // Queries a device for port information. | ||
165 | + private void queryPortInfo(DeviceId deviceId) { | ||
166 | + Device device = store.getDevice(deviceId); | ||
167 | + // FIXME: Device might not be there yet. (eventual consistent) | ||
168 | + if (device == null) { | ||
169 | + return; | ||
170 | + } | ||
171 | + DeviceProvider provider = getProvider(device.providerId()); | ||
172 | + provider.triggerProbe(device); | ||
173 | + } | ||
174 | + | ||
164 | @Override | 175 | @Override |
165 | public void removeDevice(DeviceId deviceId) { | 176 | public void removeDevice(DeviceId deviceId) { |
166 | checkNotNull(deviceId, DEVICE_ID_NULL); | 177 | checkNotNull(deviceId, DEVICE_ID_NULL); |
... | @@ -210,8 +221,6 @@ public class DeviceManager | ... | @@ -210,8 +221,6 @@ public class DeviceManager |
210 | log.info("Device {} connected", deviceId); | 221 | log.info("Device {} connected", deviceId); |
211 | // check my Role | 222 | // check my Role |
212 | MastershipRole role = mastershipService.requestRoleFor(deviceId); | 223 | MastershipRole role = mastershipService.requestRoleFor(deviceId); |
213 | - log.info("## - our role for {} is {} [master is {}]", deviceId, role, | ||
214 | - mastershipService.getMasterFor(deviceId)); | ||
215 | if (role != MastershipRole.MASTER) { | 224 | if (role != MastershipRole.MASTER) { |
216 | // TODO: Do we need to explicitly tell the Provider that | 225 | // TODO: Do we need to explicitly tell the Provider that |
217 | // this instance is no longer the MASTER? probably not | 226 | // this instance is no longer the MASTER? probably not |
... | @@ -265,7 +274,6 @@ public class DeviceManager | ... | @@ -265,7 +274,6 @@ public class DeviceManager |
265 | // but if I was the last STANDBY connection, etc. and no one else | 274 | // but if I was the last STANDBY connection, etc. and no one else |
266 | // was there to mark the device offline, this instance may need to | 275 | // was there to mark the device offline, this instance may need to |
267 | // temporarily request for Master Role and mark offline. | 276 | // temporarily request for Master Role and mark offline. |
268 | - log.info("## for {} role is {}", deviceId, mastershipService.getLocalRole(deviceId)); | ||
269 | if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) { | 277 | if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) { |
270 | log.debug("Device {} disconnected, but I am not the master", deviceId); | 278 | log.debug("Device {} disconnected, but I am not the master", deviceId); |
271 | //let go of ability to be backup | 279 | //let go of ability to be backup |
... | @@ -373,7 +381,6 @@ public class DeviceManager | ... | @@ -373,7 +381,6 @@ public class DeviceManager |
373 | final DeviceId did = event.subject(); | 381 | final DeviceId did = event.subject(); |
374 | final NodeId myNodeId = clusterService.getLocalNode().id(); | 382 | final NodeId myNodeId = clusterService.getLocalNode().id(); |
375 | 383 | ||
376 | - log.info("## got Mastershipevent for dev {}", did); | ||
377 | if (myNodeId.equals(event.roleInfo().master())) { | 384 | if (myNodeId.equals(event.roleInfo().master())) { |
378 | MastershipTerm term = termService.getMastershipTerm(did); | 385 | MastershipTerm term = termService.getMastershipTerm(did); |
379 | 386 | ||
... | @@ -384,7 +391,6 @@ public class DeviceManager | ... | @@ -384,7 +391,6 @@ public class DeviceManager |
384 | return; | 391 | return; |
385 | } | 392 | } |
386 | 393 | ||
387 | - log.info("## setting term for CPS as new master for {}", did); | ||
388 | // only set the new term if I am the master | 394 | // only set the new term if I am the master |
389 | deviceClockProviderService.setMastershipTerm(did, term); | 395 | deviceClockProviderService.setMastershipTerm(did, term); |
390 | 396 | ||
... | @@ -404,6 +410,7 @@ public class DeviceManager | ... | @@ -404,6 +410,7 @@ public class DeviceManager |
404 | device.serialNumber(), device.chassisId())); | 410 | device.serialNumber(), device.chassisId())); |
405 | } | 411 | } |
406 | //TODO re-collect device information to fix potential staleness | 412 | //TODO re-collect device information to fix potential staleness |
413 | + queryPortInfo(did); | ||
407 | applyRole(did, MastershipRole.MASTER); | 414 | applyRole(did, MastershipRole.MASTER); |
408 | } else if (event.roleInfo().backups().contains(myNodeId)) { | 415 | } else if (event.roleInfo().backups().contains(myNodeId)) { |
409 | applyRole(did, MastershipRole.STANDBY); | 416 | applyRole(did, MastershipRole.STANDBY); | ... | ... |
... | @@ -286,7 +286,8 @@ public class FlowRuleManager | ... | @@ -286,7 +286,8 @@ public class FlowRuleManager |
286 | private void extraneousFlow(FlowRule flowRule) { | 286 | private void extraneousFlow(FlowRule flowRule) { |
287 | checkNotNull(flowRule, FLOW_RULE_NULL); | 287 | checkNotNull(flowRule, FLOW_RULE_NULL); |
288 | checkValidity(); | 288 | checkValidity(); |
289 | - removeFlowRules(flowRule); | 289 | + FlowRuleProvider frp = getProvider(flowRule.deviceId()); |
290 | + frp.removeFlowRule(flowRule); | ||
290 | log.debug("Flow {} is on switch but not in store.", flowRule); | 291 | log.debug("Flow {} is on switch but not in store.", flowRule); |
291 | } | 292 | } |
292 | 293 | ||
... | @@ -380,14 +381,14 @@ public class FlowRuleManager | ... | @@ -380,14 +381,14 @@ public class FlowRuleManager |
380 | final FlowRuleBatchRequest request = event.subject(); | 381 | final FlowRuleBatchRequest request = event.subject(); |
381 | switch (event.type()) { | 382 | switch (event.type()) { |
382 | case BATCH_OPERATION_REQUESTED: | 383 | case BATCH_OPERATION_REQUESTED: |
383 | -// for (FlowEntry entry : request.toAdd()) { | 384 | + for (FlowEntry entry : request.toAdd()) { |
384 | -// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry)); | 385 | + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADD_REQUESTED, entry)); |
385 | -// } | 386 | + } |
386 | -// for (FlowEntry entry : request.toRemove()) { | 387 | + for (FlowEntry entry : request.toRemove()) { |
387 | -// //eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry)); | 388 | + eventDispatcher.post(new FlowRuleEvent(FlowRuleEvent.Type.RULE_REMOVE_REQUESTED, entry)); |
388 | -// } | 389 | + } |
389 | -// // FIXME: what about op.equals(FlowRuleOperation.MODIFY) ? | 390 | + // FIXME: what about op.equals(FlowRuleOperation.MODIFY) ? |
390 | -// | 391 | + |
391 | FlowRuleBatchOperation batchOperation = request.asBatchOperation(); | 392 | FlowRuleBatchOperation batchOperation = request.asBatchOperation(); |
392 | 393 | ||
393 | FlowRuleProvider flowRuleProvider = | 394 | FlowRuleProvider flowRuleProvider = | ... | ... |
... | @@ -126,7 +126,13 @@ public class IntentManager | ... | @@ -126,7 +126,13 @@ public class IntentManager |
126 | 126 | ||
127 | // FIXME: implement this method | 127 | // FIXME: implement this method |
128 | @Override | 128 | @Override |
129 | - public void execute(IntentOperations operations) { | 129 | + public void replace(IntentId oldIntentId, Intent newIntent) { |
130 | + throw new UnsupportedOperationException("execute() is not implemented yet"); | ||
131 | + } | ||
132 | + | ||
133 | + // FIXME: implement this method | ||
134 | + @Override | ||
135 | + public Future<IntentOperations> execute(IntentOperations operations) { | ||
130 | throw new UnsupportedOperationException("execute() is not implemented yet"); | 136 | throw new UnsupportedOperationException("execute() is not implemented yet"); |
131 | } | 137 | } |
132 | 138 | ... | ... |
... | @@ -208,7 +208,7 @@ public class LinkManager | ... | @@ -208,7 +208,7 @@ public class LinkManager |
208 | LinkEvent event = store.createOrUpdateLink(provider().id(), | 208 | LinkEvent event = store.createOrUpdateLink(provider().id(), |
209 | linkDescription); | 209 | linkDescription); |
210 | if (event != null) { | 210 | if (event != null) { |
211 | - log.debug("Link {} detected", linkDescription); | 211 | + log.info("Link {} detected", linkDescription); |
212 | post(event); | 212 | post(event); |
213 | } | 213 | } |
214 | } | 214 | } | ... | ... |
... | @@ -68,8 +68,10 @@ implements PacketService, PacketProviderRegistry { | ... | @@ -68,8 +68,10 @@ implements PacketService, PacketProviderRegistry { |
68 | checkNotNull(packet, "Packet cannot be null"); | 68 | checkNotNull(packet, "Packet cannot be null"); |
69 | final Device device = deviceService.getDevice(packet.sendThrough()); | 69 | final Device device = deviceService.getDevice(packet.sendThrough()); |
70 | final PacketProvider packetProvider = getProvider(device.providerId()); | 70 | final PacketProvider packetProvider = getProvider(device.providerId()); |
71 | + if (packetProvider != null) { | ||
71 | packetProvider.emit(packet); | 72 | packetProvider.emit(packet); |
72 | } | 73 | } |
74 | + } | ||
73 | 75 | ||
74 | @Override | 76 | @Override |
75 | protected PacketProviderService createProviderService(PacketProvider provider) { | 77 | protected PacketProviderService createProviderService(PacketProvider provider) { | ... | ... |
... | @@ -9,14 +9,18 @@ import org.apache.felix.scr.annotations.Service; | ... | @@ -9,14 +9,18 @@ import org.apache.felix.scr.annotations.Service; |
9 | import org.onlab.onos.net.ConnectPoint; | 9 | import org.onlab.onos.net.ConnectPoint; |
10 | import org.onlab.onos.net.Link; | 10 | import org.onlab.onos.net.Link; |
11 | import org.onlab.onos.net.Path; | 11 | import org.onlab.onos.net.Path; |
12 | + | ||
13 | +import org.onlab.onos.net.flow.FlowEntry; | ||
12 | import org.onlab.onos.net.flow.FlowRule; | 14 | import org.onlab.onos.net.flow.FlowRule; |
13 | import org.onlab.onos.net.flow.FlowRuleEvent; | 15 | import org.onlab.onos.net.flow.FlowRuleEvent; |
14 | import org.onlab.onos.net.flow.FlowRuleListener; | 16 | import org.onlab.onos.net.flow.FlowRuleListener; |
15 | import org.onlab.onos.net.flow.FlowRuleService; | 17 | import org.onlab.onos.net.flow.FlowRuleService; |
18 | +import org.onlab.onos.net.statistic.DefaultLoad; | ||
16 | import org.onlab.onos.net.statistic.Load; | 19 | import org.onlab.onos.net.statistic.Load; |
17 | import org.onlab.onos.net.statistic.StatisticService; | 20 | import org.onlab.onos.net.statistic.StatisticService; |
18 | import org.onlab.onos.net.statistic.StatisticStore; | 21 | import org.onlab.onos.net.statistic.StatisticStore; |
19 | import org.slf4j.Logger; | 22 | import org.slf4j.Logger; |
23 | +import java.util.Set; | ||
20 | 24 | ||
21 | import static org.slf4j.LoggerFactory.getLogger; | 25 | import static org.slf4j.LoggerFactory.getLogger; |
22 | 26 | ||
... | @@ -35,12 +39,14 @@ public class StatisticManager implements StatisticService { | ... | @@ -35,12 +39,14 @@ public class StatisticManager implements StatisticService { |
35 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 39 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
36 | protected StatisticStore statisticStore; | 40 | protected StatisticStore statisticStore; |
37 | 41 | ||
42 | + | ||
38 | private final InternalFlowRuleListener listener = new InternalFlowRuleListener(); | 43 | private final InternalFlowRuleListener listener = new InternalFlowRuleListener(); |
39 | 44 | ||
40 | @Activate | 45 | @Activate |
41 | public void activate() { | 46 | public void activate() { |
42 | flowRuleService.addListener(listener); | 47 | flowRuleService.addListener(listener); |
43 | log.info("Started"); | 48 | log.info("Started"); |
49 | + | ||
44 | } | 50 | } |
45 | 51 | ||
46 | @Deactivate | 52 | @Deactivate |
... | @@ -51,29 +57,93 @@ public class StatisticManager implements StatisticService { | ... | @@ -51,29 +57,93 @@ public class StatisticManager implements StatisticService { |
51 | 57 | ||
52 | @Override | 58 | @Override |
53 | public Load load(Link link) { | 59 | public Load load(Link link) { |
54 | - return null; | 60 | + return load(link.src()); |
55 | } | 61 | } |
56 | 62 | ||
57 | @Override | 63 | @Override |
58 | public Load load(ConnectPoint connectPoint) { | 64 | public Load load(ConnectPoint connectPoint) { |
59 | - return null; | 65 | + return loadInternal(connectPoint); |
60 | } | 66 | } |
61 | 67 | ||
62 | @Override | 68 | @Override |
63 | public Link max(Path path) { | 69 | public Link max(Path path) { |
70 | + if (path.links().isEmpty()) { | ||
64 | return null; | 71 | return null; |
65 | } | 72 | } |
73 | + Load maxLoad = new DefaultLoad(); | ||
74 | + Link maxLink = null; | ||
75 | + for (Link link : path.links()) { | ||
76 | + Load load = loadInternal(link.src()); | ||
77 | + if (load.rate() > maxLoad.rate()) { | ||
78 | + maxLoad = load; | ||
79 | + maxLink = link; | ||
80 | + } | ||
81 | + } | ||
82 | + return maxLink; | ||
83 | + } | ||
66 | 84 | ||
67 | @Override | 85 | @Override |
68 | public Link min(Path path) { | 86 | public Link min(Path path) { |
87 | + if (path.links().isEmpty()) { | ||
69 | return null; | 88 | return null; |
70 | } | 89 | } |
90 | + Load minLoad = new DefaultLoad(); | ||
91 | + Link minLink = null; | ||
92 | + for (Link link : path.links()) { | ||
93 | + Load load = loadInternal(link.src()); | ||
94 | + if (load.rate() < minLoad.rate()) { | ||
95 | + minLoad = load; | ||
96 | + minLink = link; | ||
97 | + } | ||
98 | + } | ||
99 | + return minLink; | ||
100 | + } | ||
71 | 101 | ||
72 | @Override | 102 | @Override |
73 | public FlowRule highestHitter(ConnectPoint connectPoint) { | 103 | public FlowRule highestHitter(ConnectPoint connectPoint) { |
104 | + Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint); | ||
105 | + if (hitters.isEmpty()) { | ||
74 | return null; | 106 | return null; |
75 | } | 107 | } |
76 | 108 | ||
109 | + FlowEntry max = hitters.iterator().next(); | ||
110 | + for (FlowEntry entry : hitters) { | ||
111 | + if (entry.bytes() > max.bytes()) { | ||
112 | + max = entry; | ||
113 | + } | ||
114 | + } | ||
115 | + return max; | ||
116 | + } | ||
117 | + | ||
118 | + private Load loadInternal(ConnectPoint connectPoint) { | ||
119 | + Set<FlowEntry> current; | ||
120 | + Set<FlowEntry> previous; | ||
121 | + synchronized (statisticStore) { | ||
122 | + current = statisticStore.getCurrentStatistic(connectPoint); | ||
123 | + previous = statisticStore.getPreviousStatistic(connectPoint); | ||
124 | + } | ||
125 | + if (current == null || previous == null) { | ||
126 | + return new DefaultLoad(); | ||
127 | + } | ||
128 | + long currentAggregate = aggregate(current); | ||
129 | + long previousAggregate = aggregate(previous); | ||
130 | + | ||
131 | + return new DefaultLoad(currentAggregate, previousAggregate); | ||
132 | + } | ||
133 | + | ||
134 | + /** | ||
135 | + * Aggregates a set of values. | ||
136 | + * @param values the values to aggregate | ||
137 | + * @return a long value | ||
138 | + */ | ||
139 | + private long aggregate(Set<FlowEntry> values) { | ||
140 | + long sum = 0; | ||
141 | + for (FlowEntry f : values) { | ||
142 | + sum += f.bytes(); | ||
143 | + } | ||
144 | + return sum; | ||
145 | + } | ||
146 | + | ||
77 | /** | 147 | /** |
78 | * Internal flow rule event listener. | 148 | * Internal flow rule event listener. |
79 | */ | 149 | */ |
... | @@ -81,7 +151,29 @@ public class StatisticManager implements StatisticService { | ... | @@ -81,7 +151,29 @@ public class StatisticManager implements StatisticService { |
81 | 151 | ||
82 | @Override | 152 | @Override |
83 | public void event(FlowRuleEvent event) { | 153 | public void event(FlowRuleEvent event) { |
84 | - | 154 | + FlowRule rule = event.subject(); |
155 | + switch (event.type()) { | ||
156 | + case RULE_ADDED: | ||
157 | + case RULE_UPDATED: | ||
158 | + if (rule instanceof FlowEntry) { | ||
159 | + statisticStore.addOrUpdateStatistic((FlowEntry) rule); | ||
160 | + } else { | ||
161 | + log.warn("IT AIN'T A FLOWENTRY"); | ||
162 | + } | ||
163 | + break; | ||
164 | + case RULE_ADD_REQUESTED: | ||
165 | + log.info("Preparing for stats"); | ||
166 | + statisticStore.prepareForStatistics(rule); | ||
167 | + break; | ||
168 | + case RULE_REMOVE_REQUESTED: | ||
169 | + log.info("Removing stats"); | ||
170 | + statisticStore.removeFromStatistics(rule); | ||
171 | + break; | ||
172 | + case RULE_REMOVED: | ||
173 | + break; | ||
174 | + default: | ||
175 | + log.warn("Unknown flow rule event {}", event); | ||
176 | + } | ||
85 | } | 177 | } |
86 | } | 178 | } |
87 | 179 | ... | ... |
... | @@ -5,6 +5,7 @@ import org.apache.felix.scr.annotations.Component; | ... | @@ -5,6 +5,7 @@ import org.apache.felix.scr.annotations.Component; |
5 | import org.apache.felix.scr.annotations.Deactivate; | 5 | import org.apache.felix.scr.annotations.Deactivate; |
6 | import org.apache.felix.scr.annotations.Reference; | 6 | import org.apache.felix.scr.annotations.Reference; |
7 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 7 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
8 | +import org.apache.felix.scr.annotations.Service; | ||
8 | import org.onlab.onos.event.AbstractEventAccumulator; | 9 | import org.onlab.onos.event.AbstractEventAccumulator; |
9 | import org.onlab.onos.event.Event; | 10 | import org.onlab.onos.event.Event; |
10 | import org.onlab.onos.event.EventAccumulator; | 11 | import org.onlab.onos.event.EventAccumulator; |
... | @@ -39,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -39,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger; |
39 | * new topology snapshots. | 40 | * new topology snapshots. |
40 | */ | 41 | */ |
41 | @Component(immediate = true) | 42 | @Component(immediate = true) |
43 | +@Service | ||
42 | public class DefaultTopologyProvider extends AbstractProvider | 44 | public class DefaultTopologyProvider extends AbstractProvider |
43 | implements TopologyProvider { | 45 | implements TopologyProvider { |
44 | 46 | ||
... | @@ -89,7 +91,7 @@ public class DefaultTopologyProvider extends AbstractProvider | ... | @@ -89,7 +91,7 @@ public class DefaultTopologyProvider extends AbstractProvider |
89 | linkService.addListener(linkListener); | 91 | linkService.addListener(linkListener); |
90 | 92 | ||
91 | isStarted = true; | 93 | isStarted = true; |
92 | - triggerTopologyBuild(Collections.<Event>emptyList()); | 94 | + triggerRecompute(); |
93 | log.info("Started"); | 95 | log.info("Started"); |
94 | } | 96 | } |
95 | 97 | ||
... | @@ -108,6 +110,11 @@ public class DefaultTopologyProvider extends AbstractProvider | ... | @@ -108,6 +110,11 @@ public class DefaultTopologyProvider extends AbstractProvider |
108 | log.info("Stopped"); | 110 | log.info("Stopped"); |
109 | } | 111 | } |
110 | 112 | ||
113 | + @Override | ||
114 | + public void triggerRecompute() { | ||
115 | + triggerTopologyBuild(Collections.<Event>emptyList()); | ||
116 | + } | ||
117 | + | ||
111 | /** | 118 | /** |
112 | * Triggers assembly of topology data citing the specified events as the | 119 | * Triggers assembly of topology data citing the specified events as the |
113 | * reason. | 120 | * reason. |
... | @@ -177,7 +184,11 @@ public class DefaultTopologyProvider extends AbstractProvider | ... | @@ -177,7 +184,11 @@ public class DefaultTopologyProvider extends AbstractProvider |
177 | 184 | ||
178 | @Override | 185 | @Override |
179 | public void run() { | 186 | public void run() { |
187 | + try { | ||
180 | buildTopology(reasons); | 188 | buildTopology(reasons); |
189 | + } catch (Exception e) { | ||
190 | + log.warn("Unable to compute topology due to: {}", e.getMessage()); | ||
191 | + } | ||
181 | } | 192 | } |
182 | } | 193 | } |
183 | 194 | ... | ... |
... | @@ -15,6 +15,7 @@ public class TestEventDispatcher extends DefaultEventSinkRegistry | ... | @@ -15,6 +15,7 @@ public class TestEventDispatcher extends DefaultEventSinkRegistry |
15 | implements EventDeliveryService { | 15 | implements EventDeliveryService { |
16 | 16 | ||
17 | @Override | 17 | @Override |
18 | + @SuppressWarnings("unchecked") | ||
18 | public void post(Event event) { | 19 | public void post(Event event) { |
19 | EventSink sink = getSink(event.getClass()); | 20 | EventSink sink = getSink(event.getClass()); |
20 | checkState(sink != null, "No sink for event %s", event); | 21 | checkState(sink != null, "No sink for event %s", event); | ... | ... |
... | @@ -9,6 +9,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; | ... | @@ -9,6 +9,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_ADDED; |
9 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; | 9 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; |
10 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED; | 10 | import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED; |
11 | 11 | ||
12 | + | ||
13 | +import static org.onlab.onos.net.flow.FlowRuleEvent.Type.*; | ||
14 | + | ||
12 | import java.util.ArrayList; | 15 | import java.util.ArrayList; |
13 | import java.util.Collections; | 16 | import java.util.Collections; |
14 | import java.util.HashMap; | 17 | import java.util.HashMap; |
... | @@ -55,7 +58,7 @@ import org.onlab.onos.net.flow.TrafficSelector; | ... | @@ -55,7 +58,7 @@ import org.onlab.onos.net.flow.TrafficSelector; |
55 | import org.onlab.onos.net.flow.TrafficTreatment; | 58 | import org.onlab.onos.net.flow.TrafficTreatment; |
56 | import org.onlab.onos.net.flow.criteria.Criterion; | 59 | import org.onlab.onos.net.flow.criteria.Criterion; |
57 | import org.onlab.onos.net.flow.instructions.Instruction; | 60 | import org.onlab.onos.net.flow.instructions.Instruction; |
58 | -import org.onlab.onos.net.intent.BatchOperation; | 61 | +import org.onlab.onos.net.flow.BatchOperation; |
59 | import org.onlab.onos.net.provider.AbstractProvider; | 62 | import org.onlab.onos.net.provider.AbstractProvider; |
60 | import org.onlab.onos.net.provider.ProviderId; | 63 | import org.onlab.onos.net.provider.ProviderId; |
61 | import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; | 64 | import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; |
... | @@ -165,7 +168,8 @@ public class FlowRuleManagerTest { | ... | @@ -165,7 +168,8 @@ public class FlowRuleManagerTest { |
165 | assertEquals("2 rules should exist", 2, flowCount()); | 168 | assertEquals("2 rules should exist", 2, flowCount()); |
166 | 169 | ||
167 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); | 170 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); |
168 | - validateEvents(RULE_ADDED, RULE_ADDED); | 171 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
172 | + RULE_ADDED, RULE_ADDED); | ||
169 | 173 | ||
170 | addFlowRule(1); | 174 | addFlowRule(1); |
171 | assertEquals("should still be 2 rules", 2, flowCount()); | 175 | assertEquals("should still be 2 rules", 2, flowCount()); |
... | @@ -218,11 +222,12 @@ public class FlowRuleManagerTest { | ... | @@ -218,11 +222,12 @@ public class FlowRuleManagerTest { |
218 | FlowEntry fe2 = new DefaultFlowEntry(f2); | 222 | FlowEntry fe2 = new DefaultFlowEntry(f2); |
219 | FlowEntry fe3 = new DefaultFlowEntry(f3); | 223 | FlowEntry fe3 = new DefaultFlowEntry(f3); |
220 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); | 224 | providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2, fe3)); |
221 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_ADDED); | 225 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
226 | + RULE_ADDED, RULE_ADDED, RULE_ADDED); | ||
222 | 227 | ||
223 | mgr.removeFlowRules(f1, f2); | 228 | mgr.removeFlowRules(f1, f2); |
224 | //removing from north, so no events generated | 229 | //removing from north, so no events generated |
225 | - validateEvents(); | 230 | + validateEvents(RULE_REMOVE_REQUESTED, RULE_REMOVE_REQUESTED); |
226 | assertEquals("3 rule should exist", 3, flowCount()); | 231 | assertEquals("3 rule should exist", 3, flowCount()); |
227 | assertTrue("Entries should be pending remove.", | 232 | assertTrue("Entries should be pending remove.", |
228 | validateState(ImmutableMap.of( | 233 | validateState(ImmutableMap.of( |
... | @@ -244,7 +249,8 @@ public class FlowRuleManagerTest { | ... | @@ -244,7 +249,8 @@ public class FlowRuleManagerTest { |
244 | service.removeFlowRules(f1); | 249 | service.removeFlowRules(f1); |
245 | fe1.setState(FlowEntryState.REMOVED); | 250 | fe1.setState(FlowEntryState.REMOVED); |
246 | providerService.flowRemoved(fe1); | 251 | providerService.flowRemoved(fe1); |
247 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); | 252 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, |
253 | + RULE_ADDED, RULE_REMOVE_REQUESTED, RULE_REMOVED); | ||
248 | 254 | ||
249 | providerService.flowRemoved(fe1); | 255 | providerService.flowRemoved(fe1); |
250 | validateEvents(); | 256 | validateEvents(); |
... | @@ -253,7 +259,7 @@ public class FlowRuleManagerTest { | ... | @@ -253,7 +259,7 @@ public class FlowRuleManagerTest { |
253 | FlowEntry fe3 = new DefaultFlowEntry(f3); | 259 | FlowEntry fe3 = new DefaultFlowEntry(f3); |
254 | service.applyFlowRules(f3); | 260 | service.applyFlowRules(f3); |
255 | providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); | 261 | providerService.pushFlowMetrics(DID, Collections.singletonList(fe3)); |
256 | - validateEvents(RULE_ADDED); | 262 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADDED); |
257 | 263 | ||
258 | providerService.flowRemoved(fe3); | 264 | providerService.flowRemoved(fe3); |
259 | validateEvents(); | 265 | validateEvents(); |
... | @@ -282,7 +288,8 @@ public class FlowRuleManagerTest { | ... | @@ -282,7 +288,8 @@ public class FlowRuleManagerTest { |
282 | f2, FlowEntryState.ADDED, | 288 | f2, FlowEntryState.ADDED, |
283 | f3, FlowEntryState.PENDING_ADD))); | 289 | f3, FlowEntryState.PENDING_ADD))); |
284 | 290 | ||
285 | - validateEvents(RULE_ADDED, RULE_ADDED); | 291 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
292 | + RULE_ADDED, RULE_ADDED); | ||
286 | } | 293 | } |
287 | 294 | ||
288 | @Test | 295 | @Test |
... | @@ -302,7 +309,7 @@ public class FlowRuleManagerTest { | ... | @@ -302,7 +309,7 @@ public class FlowRuleManagerTest { |
302 | 309 | ||
303 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); | 310 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2, fe3)); |
304 | 311 | ||
305 | - validateEvents(RULE_ADDED, RULE_ADDED); | 312 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADDED, RULE_ADDED); |
306 | 313 | ||
307 | } | 314 | } |
308 | 315 | ||
... | @@ -327,7 +334,8 @@ public class FlowRuleManagerTest { | ... | @@ -327,7 +334,8 @@ public class FlowRuleManagerTest { |
327 | 334 | ||
328 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); | 335 | providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); |
329 | 336 | ||
330 | - validateEvents(RULE_ADDED, RULE_ADDED, RULE_REMOVED); | 337 | + validateEvents(RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, RULE_ADD_REQUESTED, |
338 | + RULE_REMOVE_REQUESTED, RULE_ADDED, RULE_ADDED, RULE_REMOVED); | ||
331 | 339 | ||
332 | } | 340 | } |
333 | 341 | ... | ... |
... | @@ -195,6 +195,10 @@ public class TopologyManagerTest { | ... | @@ -195,6 +195,10 @@ public class TopologyManagerTest { |
195 | public TestProvider() { | 195 | public TestProvider() { |
196 | super(PID); | 196 | super(PID); |
197 | } | 197 | } |
198 | + | ||
199 | + @Override | ||
200 | + public void triggerRecompute() { | ||
201 | + } | ||
198 | } | 202 | } |
199 | 203 | ||
200 | private static class TestListener implements TopologyListener { | 204 | private static class TestListener implements TopologyListener { | ... | ... |
... | @@ -4,9 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument; | ... | @@ -4,9 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument; |
4 | 4 | ||
5 | import java.io.IOException; | 5 | import java.io.IOException; |
6 | import java.util.Set; | 6 | import java.util.Set; |
7 | +import java.util.concurrent.ExecutionException; | ||
7 | import java.util.concurrent.TimeUnit; | 8 | import java.util.concurrent.TimeUnit; |
8 | import java.util.concurrent.TimeoutException; | 9 | import java.util.concurrent.TimeoutException; |
9 | - | ||
10 | import org.apache.felix.scr.annotations.Activate; | 10 | import org.apache.felix.scr.annotations.Activate; |
11 | import org.apache.felix.scr.annotations.Component; | 11 | import org.apache.felix.scr.annotations.Component; |
12 | import org.apache.felix.scr.annotations.Deactivate; | 12 | import org.apache.felix.scr.annotations.Deactivate; |
... | @@ -181,10 +181,13 @@ public class ClusterCommunicationManager | ... | @@ -181,10 +181,13 @@ public class ClusterCommunicationManager |
181 | } | 181 | } |
182 | } | 182 | } |
183 | 183 | ||
184 | - private static final class InternalClusterMessageResponse implements ClusterMessageResponse { | 184 | + private static final class InternalClusterMessageResponse |
185 | + implements ClusterMessageResponse { | ||
185 | 186 | ||
186 | private final NodeId sender; | 187 | private final NodeId sender; |
187 | private final Response responseFuture; | 188 | private final Response responseFuture; |
189 | + private volatile boolean isCancelled = false; | ||
190 | + private volatile boolean isDone = false; | ||
188 | 191 | ||
189 | public InternalClusterMessageResponse(NodeId sender, Response responseFuture) { | 192 | public InternalClusterMessageResponse(NodeId sender, Response responseFuture) { |
190 | this.sender = sender; | 193 | this.sender = sender; |
... | @@ -198,12 +201,39 @@ public class ClusterCommunicationManager | ... | @@ -198,12 +201,39 @@ public class ClusterCommunicationManager |
198 | @Override | 201 | @Override |
199 | public byte[] get(long timeout, TimeUnit timeunit) | 202 | public byte[] get(long timeout, TimeUnit timeunit) |
200 | throws TimeoutException { | 203 | throws TimeoutException { |
201 | - return responseFuture.get(timeout, timeunit); | 204 | + final byte[] result = responseFuture.get(timeout, timeunit); |
205 | + isDone = true; | ||
206 | + return result; | ||
207 | + } | ||
208 | + | ||
209 | + @Override | ||
210 | + public boolean cancel(boolean mayInterruptIfRunning) { | ||
211 | + if (isDone()) { | ||
212 | + return false; | ||
213 | + } | ||
214 | + // doing nothing for now | ||
215 | + // when onlab.netty Response support cancel, call them. | ||
216 | + isCancelled = true; | ||
217 | + return true; | ||
218 | + } | ||
219 | + | ||
220 | + @Override | ||
221 | + public boolean isCancelled() { | ||
222 | + return isCancelled; | ||
223 | + } | ||
224 | + | ||
225 | + @Override | ||
226 | + public boolean isDone() { | ||
227 | + return this.isDone || isCancelled(); | ||
202 | } | 228 | } |
203 | 229 | ||
204 | @Override | 230 | @Override |
205 | - public byte[] get(long timeout) throws InterruptedException { | 231 | + public byte[] get() throws InterruptedException, ExecutionException { |
206 | - return responseFuture.get(); | 232 | + // TODO: consider forbidding this call and force the use of timed get |
233 | + // to enforce handling of remote peer failure scenario | ||
234 | + final byte[] result = responseFuture.get(); | ||
235 | + isDone = true; | ||
236 | + return result; | ||
207 | } | 237 | } |
208 | } | 238 | } |
209 | } | 239 | } | ... | ... |
... | @@ -290,12 +290,17 @@ public class GossipDeviceStore | ... | @@ -290,12 +290,17 @@ public class GossipDeviceStore |
290 | private DeviceEvent updateDevice(ProviderId providerId, | 290 | private DeviceEvent updateDevice(ProviderId providerId, |
291 | Device oldDevice, | 291 | Device oldDevice, |
292 | Device newDevice, Timestamp newTimestamp) { | 292 | Device newDevice, Timestamp newTimestamp) { |
293 | - | ||
294 | // We allow only certain attributes to trigger update | 293 | // We allow only certain attributes to trigger update |
295 | - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || | 294 | + boolean propertiesChanged = |
296 | - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || | 295 | + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || |
297 | - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { | 296 | + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()); |
298 | - | 297 | + boolean annotationsChanged = |
298 | + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations()); | ||
299 | + | ||
300 | + // Primary providers can respond to all changes, but ancillary ones | ||
301 | + // should respond only to annotation changes. | ||
302 | + if ((providerId.isAncillary() && annotationsChanged) || | ||
303 | + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) { | ||
299 | boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); | 304 | boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); |
300 | if (!replaced) { | 305 | if (!replaced) { |
301 | verify(replaced, | 306 | verify(replaced, | ... | ... |
... | @@ -35,6 +35,8 @@ public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEven | ... | @@ -35,6 +35,8 @@ public class InternalDeviceEventSerializer extends Serializer<InternalDeviceEven |
35 | Class<InternalDeviceEvent> type) { | 35 | Class<InternalDeviceEvent> type) { |
36 | ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); | 36 | ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); |
37 | DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); | 37 | DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); |
38 | + | ||
39 | + @SuppressWarnings("unchecked") | ||
38 | Timestamped<DeviceDescription> deviceDescription | 40 | Timestamped<DeviceDescription> deviceDescription |
39 | = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input); | 41 | = (Timestamped<DeviceDescription>) kryo.readClassAndObject(input); |
40 | 42 | ... | ... |
... | @@ -37,6 +37,8 @@ public class InternalPortEventSerializer extends Serializer<InternalPortEvent> { | ... | @@ -37,6 +37,8 @@ public class InternalPortEventSerializer extends Serializer<InternalPortEvent> { |
37 | Class<InternalPortEvent> type) { | 37 | Class<InternalPortEvent> type) { |
38 | ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); | 38 | ProviderId providerId = (ProviderId) kryo.readClassAndObject(input); |
39 | DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); | 39 | DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); |
40 | + | ||
41 | + @SuppressWarnings("unchecked") | ||
40 | Timestamped<List<PortDescription>> portDescriptions | 42 | Timestamped<List<PortDescription>> portDescriptions |
41 | = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input); | 43 | = (Timestamped<List<PortDescription>>) kryo.readClassAndObject(input); |
42 | 44 | ... | ... |
... | @@ -86,7 +86,7 @@ public class ReplicaInfoManager implements ReplicaInfoService { | ... | @@ -86,7 +86,7 @@ public class ReplicaInfoManager implements ReplicaInfoService { |
86 | final List<NodeId> standbyList = Collections.<NodeId>emptyList(); | 86 | final List<NodeId> standbyList = Collections.<NodeId>emptyList(); |
87 | eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED, | 87 | eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED, |
88 | event.subject(), | 88 | event.subject(), |
89 | - new ReplicaInfo(event.node(), standbyList))); | 89 | + new ReplicaInfo(event.roleInfo().master(), standbyList))); |
90 | } | 90 | } |
91 | } | 91 | } |
92 | 92 | ... | ... |
... | @@ -82,12 +82,28 @@ public class DistributedIntentStore | ... | @@ -82,12 +82,28 @@ public class DistributedIntentStore |
82 | public IntentEvent setState(Intent intent, IntentState state) { | 82 | public IntentEvent setState(Intent intent, IntentState state) { |
83 | IntentId id = intent.id(); | 83 | IntentId id = intent.id(); |
84 | states.put(id, state); | 84 | states.put(id, state); |
85 | - IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED : | 85 | + IntentEvent.Type type = null; |
86 | - (state == INSTALLED ? IntentEvent.Type.INSTALLED : | 86 | + |
87 | - (state == FAILED ? IntentEvent.Type.FAILED : | 87 | + switch (state) { |
88 | - state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN : | 88 | + case SUBMITTED: |
89 | - null))); | 89 | + type = IntentEvent.Type.SUBMITTED; |
90 | - return type == null ? null : new IntentEvent(type, intent); | 90 | + break; |
91 | + case INSTALLED: | ||
92 | + type = IntentEvent.Type.INSTALLED; | ||
93 | + break; | ||
94 | + case FAILED: | ||
95 | + type = IntentEvent.Type.FAILED; | ||
96 | + break; | ||
97 | + case WITHDRAWN: | ||
98 | + type = IntentEvent.Type.WITHDRAWN; | ||
99 | + break; | ||
100 | + default: | ||
101 | + break; | ||
102 | + } | ||
103 | + if (type == null) { | ||
104 | + return null; | ||
105 | + } | ||
106 | + return new IntentEvent(type, intent); | ||
91 | } | 107 | } |
92 | 108 | ||
93 | @Override | 109 | @Override | ... | ... |
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 | ||
98 | - return MastershipRole.STANDBY; | ||
99 | - } else { | ||
100 | - return MastershipRole.NONE; | ||
101 | - } | ||
102 | - } else { | ||
103 | - if (current.equals(nodeId)) { | ||
104 | - //*should* be in unusable, not always | ||
105 | - return MastershipRole.MASTER; | ||
106 | - } else { | ||
107 | - //may be in backups or unusable from earlier retirement | ||
108 | - return MastershipRole.STANDBY; | ||
109 | } | 97 | } |
98 | + if (roleInfo.contains(STANDBY, nodeId)) { | ||
99 | + return STANDBY; | ||
110 | } | 100 | } |
101 | + return NONE; | ||
111 | } | 102 | } |
112 | 103 | ||
113 | @Override | 104 | @Override |
... | @@ -124,10 +115,11 @@ implements MastershipStore { | ... | @@ -124,10 +115,11 @@ implements MastershipStore { |
124 | roleMap.put(deviceId, rv); | 115 | roleMap.put(deviceId, rv); |
125 | return null; | 116 | return null; |
126 | case STANDBY: | 117 | case STANDBY: |
118 | + case NONE: | ||
127 | NodeId current = rv.get(MASTER); | 119 | NodeId current = rv.get(MASTER); |
128 | if (current != null) { | 120 | if (current != null) { |
129 | //backup and replace current master | 121 | //backup and replace current master |
130 | - rv.reassign(nodeId, NONE, STANDBY); | 122 | + rv.reassign(current, NONE, STANDBY); |
131 | rv.replace(current, nodeId, MASTER); | 123 | rv.replace(current, nodeId, MASTER); |
132 | } else { | 124 | } else { |
133 | //no master before so just add. | 125 | //no master before so just add. |
... | @@ -137,12 +129,6 @@ implements MastershipStore { | ... | @@ -137,12 +129,6 @@ implements MastershipStore { |
137 | roleMap.put(deviceId, rv); | 129 | roleMap.put(deviceId, rv); |
138 | updateTerm(deviceId); | 130 | updateTerm(deviceId); |
139 | return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); | 131 | return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); |
140 | - case NONE: | ||
141 | - rv.add(MASTER, nodeId); | ||
142 | - rv.reassign(nodeId, STANDBY, NONE); | ||
143 | - roleMap.put(deviceId, rv); | ||
144 | - updateTerm(deviceId); | ||
145 | - return new MastershipEvent(MASTER_CHANGED, deviceId, rv.roleInfo()); | ||
146 | default: | 132 | default: |
147 | log.warn("unknown Mastership Role {}", role); | 133 | log.warn("unknown Mastership Role {}", role); |
148 | return null; | 134 | return null; |
... | @@ -193,21 +179,28 @@ implements MastershipStore { | ... | @@ -193,21 +179,28 @@ implements MastershipStore { |
193 | switch (role) { | 179 | switch (role) { |
194 | case MASTER: | 180 | case MASTER: |
195 | rv.reassign(local, STANDBY, NONE); | 181 | rv.reassign(local, STANDBY, NONE); |
182 | + terms.putIfAbsent(deviceId, INIT); | ||
196 | roleMap.put(deviceId, rv); | 183 | roleMap.put(deviceId, rv); |
197 | break; | 184 | break; |
198 | case STANDBY: | 185 | case STANDBY: |
199 | rv.reassign(local, NONE, STANDBY); | 186 | rv.reassign(local, NONE, STANDBY); |
200 | roleMap.put(deviceId, rv); | 187 | roleMap.put(deviceId, rv); |
201 | terms.putIfAbsent(deviceId, INIT); | 188 | terms.putIfAbsent(deviceId, INIT); |
202 | - | ||
203 | break; | 189 | break; |
204 | case NONE: | 190 | case NONE: |
205 | - //claim mastership | 191 | + //either we're the first standby, or first to device. |
192 | + //for latter, claim mastership. | ||
193 | + if (rv.get(MASTER) == null) { | ||
206 | rv.add(MASTER, local); | 194 | rv.add(MASTER, local); |
207 | rv.reassign(local, STANDBY, NONE); | 195 | rv.reassign(local, STANDBY, NONE); |
208 | - roleMap.put(deviceId, rv); | ||
209 | updateTerm(deviceId); | 196 | updateTerm(deviceId); |
210 | role = MastershipRole.MASTER; | 197 | role = MastershipRole.MASTER; |
198 | + } else { | ||
199 | + rv.add(STANDBY, local); | ||
200 | + rv.reassign(local, NONE, STANDBY); | ||
201 | + role = MastershipRole.STANDBY; | ||
202 | + } | ||
203 | + roleMap.put(deviceId, rv); | ||
211 | break; | 204 | break; |
212 | default: | 205 | default: |
213 | log.warn("unknown Mastership Role {}", role); | 206 | log.warn("unknown Mastership Role {}", role); |
... | @@ -315,7 +308,10 @@ implements MastershipStore { | ... | @@ -315,7 +308,10 @@ implements MastershipStore { |
315 | RoleValue value = roleMap.get(deviceId); | 308 | RoleValue value = roleMap.get(deviceId); |
316 | if (value == null) { | 309 | if (value == null) { |
317 | value = new RoleValue(); | 310 | value = new RoleValue(); |
318 | - roleMap.put(deviceId, value); | 311 | + RoleValue concurrentlyAdded = roleMap.putIfAbsent(deviceId, value); |
312 | + if (concurrentlyAdded != null) { | ||
313 | + return concurrentlyAdded; | ||
314 | + } | ||
319 | } | 315 | } |
320 | return value; | 316 | return value; |
321 | } | 317 | } |
... | @@ -329,16 +325,6 @@ implements MastershipStore { | ... | @@ -329,16 +325,6 @@ implements MastershipStore { |
329 | return null; | 325 | return null; |
330 | } | 326 | } |
331 | 327 | ||
332 | - //check if node is a certain role given a device | ||
333 | - private boolean isRole( | ||
334 | - MastershipRole role, NodeId nodeId, DeviceId deviceId) { | ||
335 | - RoleValue value = roleMap.get(deviceId); | ||
336 | - if (value != null) { | ||
337 | - return value.contains(role, nodeId); | ||
338 | - } | ||
339 | - return false; | ||
340 | - } | ||
341 | - | ||
342 | //adds or updates term information. | 328 | //adds or updates term information. |
343 | private void updateTerm(DeviceId deviceId) { | 329 | private void updateTerm(DeviceId deviceId) { |
344 | terms.lock(deviceId); | 330 | terms.lock(deviceId); | ... | ... |
... | @@ -97,6 +97,7 @@ public class DistributedMastershipStoreTest { | ... | @@ -97,6 +97,7 @@ public class DistributedMastershipStoreTest { |
97 | assertEquals("wrong role:", NONE, dms.getRole(N1, DID1)); | 97 | assertEquals("wrong role:", NONE, dms.getRole(N1, DID1)); |
98 | testStore.put(DID1, N1, true, false, true); | 98 | testStore.put(DID1, N1, true, false, true); |
99 | assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1)); | 99 | assertEquals("wrong role:", MASTER, dms.getRole(N1, DID1)); |
100 | + testStore.put(DID1, N2, false, true, false); | ||
100 | assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1)); | 101 | assertEquals("wrong role:", STANDBY, dms.getRole(N2, DID1)); |
101 | } | 102 | } |
102 | 103 | ||
... | @@ -155,6 +156,7 @@ public class DistributedMastershipStoreTest { | ... | @@ -155,6 +156,7 @@ public class DistributedMastershipStoreTest { |
155 | 156 | ||
156 | //switch over to N2 | 157 | //switch over to N2 |
157 | assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type()); | 158 | assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID1).type()); |
159 | + System.out.println(dms.getTermFor(DID1).master() + ":" + dms.getTermFor(DID1).termNumber()); | ||
158 | assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1)); | 160 | assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID1)); |
159 | 161 | ||
160 | //orphan switch - should be rare case | 162 | //orphan switch - should be rare case |
... | @@ -182,14 +184,9 @@ public class DistributedMastershipStoreTest { | ... | @@ -182,14 +184,9 @@ public class DistributedMastershipStoreTest { |
182 | assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type()); | 184 | assertEquals("wrong event:", Type.MASTER_CHANGED, dms.relinquishRole(N1, DID1).type()); |
183 | assertEquals("wrong master", N2, dms.getMaster(DID1)); | 185 | assertEquals("wrong master", N2, dms.getMaster(DID1)); |
184 | 186 | ||
185 | - //STANDBY - nothing here, either | ||
186 | - assertNull("wrong event:", dms.relinquishRole(N1, DID1)); | ||
187 | - assertEquals("wrong role for node:", STANDBY, dms.getRole(N1, DID1)); | ||
188 | - | ||
189 | //all nodes "give up" on device, which goes back to NONE. | 187 | //all nodes "give up" on device, which goes back to NONE. |
190 | assertNull("wrong event:", dms.relinquishRole(N2, DID1)); | 188 | assertNull("wrong event:", dms.relinquishRole(N2, DID1)); |
191 | assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1)); | 189 | assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1)); |
192 | - assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1)); | ||
193 | 190 | ||
194 | assertEquals("wrong number of retired nodes", 2, | 191 | assertEquals("wrong number of retired nodes", 2, |
195 | dms.roleMap.get(DID1).nodesOfRole(NONE).size()); | 192 | dms.roleMap.get(DID1).nodesOfRole(NONE).size()); |
... | @@ -201,6 +198,10 @@ public class DistributedMastershipStoreTest { | ... | @@ -201,6 +198,10 @@ public class DistributedMastershipStoreTest { |
201 | assertEquals("wrong number of backup nodes", 1, | 198 | assertEquals("wrong number of backup nodes", 1, |
202 | dms.roleMap.get(DID1).nodesOfRole(STANDBY).size()); | 199 | dms.roleMap.get(DID1).nodesOfRole(STANDBY).size()); |
203 | 200 | ||
201 | + //If STANDBY, should drop to NONE | ||
202 | + assertNull("wrong event:", dms.relinquishRole(N1, DID1)); | ||
203 | + assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1)); | ||
204 | + | ||
204 | //NONE - nothing happens | 205 | //NONE - nothing happens |
205 | assertNull("wrong event:", dms.relinquishRole(N1, DID2)); | 206 | assertNull("wrong event:", dms.relinquishRole(N1, DID2)); |
206 | assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2)); | 207 | assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID2)); |
... | @@ -218,7 +219,7 @@ public class DistributedMastershipStoreTest { | ... | @@ -218,7 +219,7 @@ public class DistributedMastershipStoreTest { |
218 | public void notify(MastershipEvent event) { | 219 | public void notify(MastershipEvent event) { |
219 | assertEquals("wrong event:", Type.MASTER_CHANGED, event.type()); | 220 | assertEquals("wrong event:", Type.MASTER_CHANGED, event.type()); |
220 | assertEquals("wrong subject", DID1, event.subject()); | 221 | assertEquals("wrong subject", DID1, event.subject()); |
221 | - assertEquals("wrong subject", N1, event.node()); | 222 | + assertEquals("wrong subject", N1, event.roleInfo().master()); |
222 | addLatch.countDown(); | 223 | addLatch.countDown(); |
223 | } | 224 | } |
224 | }; | 225 | }; | ... | ... |
... | @@ -4,6 +4,7 @@ import java.net.URI; | ... | @@ -4,6 +4,7 @@ import java.net.URI; |
4 | import java.util.ArrayList; | 4 | import java.util.ArrayList; |
5 | import java.util.Arrays; | 5 | import java.util.Arrays; |
6 | import java.util.HashMap; | 6 | import java.util.HashMap; |
7 | +import java.util.HashSet; | ||
7 | 8 | ||
8 | import org.onlab.onos.cluster.ControllerNode; | 9 | import org.onlab.onos.cluster.ControllerNode; |
9 | import org.onlab.onos.cluster.DefaultControllerNode; | 10 | import org.onlab.onos.cluster.DefaultControllerNode; |
... | @@ -30,6 +31,7 @@ import org.onlab.onos.net.flow.DefaultFlowEntry; | ... | @@ -30,6 +31,7 @@ import org.onlab.onos.net.flow.DefaultFlowEntry; |
30 | import org.onlab.onos.net.flow.DefaultFlowRule; | 31 | import org.onlab.onos.net.flow.DefaultFlowRule; |
31 | import org.onlab.onos.net.flow.DefaultTrafficSelector; | 32 | import org.onlab.onos.net.flow.DefaultTrafficSelector; |
32 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; | 33 | import org.onlab.onos.net.flow.DefaultTrafficTreatment; |
34 | +import org.onlab.onos.net.flow.FlowEntry; | ||
33 | import org.onlab.onos.net.flow.FlowId; | 35 | import org.onlab.onos.net.flow.FlowId; |
34 | import org.onlab.onos.net.flow.StoredFlowEntry; | 36 | import org.onlab.onos.net.flow.StoredFlowEntry; |
35 | import org.onlab.onos.net.flow.criteria.Criteria; | 37 | import org.onlab.onos.net.flow.criteria.Criteria; |
... | @@ -77,6 +79,7 @@ public final class KryoNamespaces { | ... | @@ -77,6 +79,7 @@ public final class KryoNamespaces { |
77 | ArrayList.class, | 79 | ArrayList.class, |
78 | Arrays.asList().getClass(), | 80 | Arrays.asList().getClass(), |
79 | HashMap.class, | 81 | HashMap.class, |
82 | + HashSet.class, | ||
80 | // | 83 | // |
81 | // | 84 | // |
82 | ControllerNode.State.class, | 85 | ControllerNode.State.class, |
... | @@ -98,6 +101,8 @@ public final class KryoNamespaces { | ... | @@ -98,6 +101,8 @@ public final class KryoNamespaces { |
98 | DefaultFlowEntry.class, | 101 | DefaultFlowEntry.class, |
99 | StoredFlowEntry.class, | 102 | StoredFlowEntry.class, |
100 | DefaultFlowRule.class, | 103 | DefaultFlowRule.class, |
104 | + DefaultFlowEntry.class, | ||
105 | + FlowEntry.FlowEntryState.class, | ||
101 | FlowId.class, | 106 | FlowId.class, |
102 | DefaultTrafficSelector.class, | 107 | DefaultTrafficSelector.class, |
103 | Criteria.PortCriterion.class, | 108 | Criteria.PortCriterion.class, | ... | ... |
... | @@ -70,15 +70,16 @@ public class SimpleDeviceStore | ... | @@ -70,15 +70,16 @@ public class SimpleDeviceStore |
70 | 70 | ||
71 | public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; | 71 | public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; |
72 | 72 | ||
73 | - // collection of Description given from various providers | 73 | + // Collection of Description given from various providers |
74 | private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>> | 74 | private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>> |
75 | deviceDescs = Maps.newConcurrentMap(); | 75 | deviceDescs = Maps.newConcurrentMap(); |
76 | 76 | ||
77 | - // cache of Device and Ports generated by compositing descriptions from providers | 77 | + // Cache of Device and Ports generated by compositing descriptions from providers |
78 | private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); | 78 | private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap(); |
79 | - private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap(); | 79 | + private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> |
80 | + devicePorts = Maps.newConcurrentMap(); | ||
80 | 81 | ||
81 | - // available(=UP) devices | 82 | + // Available (=UP) devices |
82 | private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); | 83 | private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet(); |
83 | 84 | ||
84 | 85 | ||
... | @@ -115,13 +116,11 @@ public class SimpleDeviceStore | ... | @@ -115,13 +116,11 @@ public class SimpleDeviceStore |
115 | public DeviceEvent createOrUpdateDevice(ProviderId providerId, | 116 | public DeviceEvent createOrUpdateDevice(ProviderId providerId, |
116 | DeviceId deviceId, | 117 | DeviceId deviceId, |
117 | DeviceDescription deviceDescription) { | 118 | DeviceDescription deviceDescription) { |
118 | - | ||
119 | Map<ProviderId, DeviceDescriptions> providerDescs | 119 | Map<ProviderId, DeviceDescriptions> providerDescs |
120 | = getOrCreateDeviceDescriptions(deviceId); | 120 | = getOrCreateDeviceDescriptions(deviceId); |
121 | 121 | ||
122 | synchronized (providerDescs) { | 122 | synchronized (providerDescs) { |
123 | // locking per device | 123 | // locking per device |
124 | - | ||
125 | DeviceDescriptions descs | 124 | DeviceDescriptions descs |
126 | = getOrCreateProviderDeviceDescriptions(providerDescs, | 125 | = getOrCreateProviderDeviceDescriptions(providerDescs, |
127 | providerId, | 126 | providerId, |
... | @@ -145,7 +144,6 @@ public class SimpleDeviceStore | ... | @@ -145,7 +144,6 @@ public class SimpleDeviceStore |
145 | // Creates the device and returns the appropriate event if necessary. | 144 | // Creates the device and returns the appropriate event if necessary. |
146 | // Guarded by deviceDescs value (=Device lock) | 145 | // Guarded by deviceDescs value (=Device lock) |
147 | private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { | 146 | private DeviceEvent createDevice(ProviderId providerId, Device newDevice) { |
148 | - | ||
149 | // update composed device cache | 147 | // update composed device cache |
150 | Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); | 148 | Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice); |
151 | verify(oldDevice == null, | 149 | verify(oldDevice == null, |
... | @@ -162,14 +160,21 @@ public class SimpleDeviceStore | ... | @@ -162,14 +160,21 @@ public class SimpleDeviceStore |
162 | // Updates the device and returns the appropriate event if necessary. | 160 | // Updates the device and returns the appropriate event if necessary. |
163 | // Guarded by deviceDescs value (=Device lock) | 161 | // Guarded by deviceDescs value (=Device lock) |
164 | private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { | 162 | private DeviceEvent updateDevice(ProviderId providerId, Device oldDevice, Device newDevice) { |
165 | - | ||
166 | // We allow only certain attributes to trigger update | 163 | // We allow only certain attributes to trigger update |
167 | - if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || | 164 | + boolean propertiesChanged = |
168 | - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) || | 165 | + !Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || |
169 | - !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations())) { | 166 | + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()); |
167 | + boolean annotationsChanged = | ||
168 | + !AnnotationsUtil.isEqual(oldDevice.annotations(), newDevice.annotations()); | ||
169 | + | ||
170 | + // Primary providers can respond to all changes, but ancillary ones | ||
171 | + // should respond only to annotation changes. | ||
172 | + if ((providerId.isAncillary() && annotationsChanged) || | ||
173 | + (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) { | ||
170 | 174 | ||
171 | boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); | 175 | boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); |
172 | if (!replaced) { | 176 | if (!replaced) { |
177 | + // FIXME: Is the enclosing if required here? | ||
173 | verify(replaced, | 178 | verify(replaced, |
174 | "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]", | 179 | "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]", |
175 | providerId, oldDevice, devices.get(newDevice.id()) | 180 | providerId, oldDevice, devices.get(newDevice.id()) |
... | @@ -214,7 +219,6 @@ public class SimpleDeviceStore | ... | @@ -214,7 +219,6 @@ public class SimpleDeviceStore |
214 | public List<DeviceEvent> updatePorts(ProviderId providerId, | 219 | public List<DeviceEvent> updatePorts(ProviderId providerId, |
215 | DeviceId deviceId, | 220 | DeviceId deviceId, |
216 | List<PortDescription> portDescriptions) { | 221 | List<PortDescription> portDescriptions) { |
217 | - | ||
218 | Device device = devices.get(deviceId); | 222 | Device device = devices.get(deviceId); |
219 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); | 223 | checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); |
220 | 224 | ||
... | @@ -327,7 +331,6 @@ public class SimpleDeviceStore | ... | @@ -327,7 +331,6 @@ public class SimpleDeviceStore |
327 | private DeviceDescriptions getOrCreateProviderDeviceDescriptions( | 331 | private DeviceDescriptions getOrCreateProviderDeviceDescriptions( |
328 | Map<ProviderId, DeviceDescriptions> device, | 332 | Map<ProviderId, DeviceDescriptions> device, |
329 | ProviderId providerId, DeviceDescription deltaDesc) { | 333 | ProviderId providerId, DeviceDescription deltaDesc) { |
330 | - | ||
331 | synchronized (device) { | 334 | synchronized (device) { |
332 | DeviceDescriptions r = device.get(providerId); | 335 | DeviceDescriptions r = device.get(providerId); |
333 | if (r == null) { | 336 | if (r == null) { |
... | @@ -447,7 +450,7 @@ public class SimpleDeviceStore | ... | @@ -447,7 +450,7 @@ public class SimpleDeviceStore |
447 | annotations = merge(annotations, e.getValue().getDeviceDesc().annotations()); | 450 | annotations = merge(annotations, e.getValue().getDeviceDesc().annotations()); |
448 | } | 451 | } |
449 | 452 | ||
450 | - return new DefaultDevice(primary, deviceId , type, manufacturer, | 453 | + return new DefaultDevice(primary, deviceId, type, manufacturer, |
451 | hwVersion, swVersion, serialNumber, | 454 | hwVersion, swVersion, serialNumber, |
452 | chassisId, annotations); | 455 | chassisId, annotations); |
453 | } | 456 | } | ... | ... |
... | @@ -187,17 +187,23 @@ public class SimpleFlowRuleStore | ... | @@ -187,17 +187,23 @@ public class SimpleFlowRuleStore |
187 | public void deleteFlowRule(FlowRule rule) { | 187 | public void deleteFlowRule(FlowRule rule) { |
188 | 188 | ||
189 | List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id()); | 189 | List<StoredFlowEntry> entries = getFlowEntries(rule.deviceId(), rule.id()); |
190 | + | ||
190 | synchronized (entries) { | 191 | synchronized (entries) { |
191 | for (StoredFlowEntry entry : entries) { | 192 | for (StoredFlowEntry entry : entries) { |
192 | if (entry.equals(rule)) { | 193 | if (entry.equals(rule)) { |
193 | synchronized (entry) { | 194 | synchronized (entry) { |
194 | entry.setState(FlowEntryState.PENDING_REMOVE); | 195 | entry.setState(FlowEntryState.PENDING_REMOVE); |
195 | // TODO: Should we notify only if it's "remote" event? | 196 | // TODO: Should we notify only if it's "remote" event? |
196 | - //notifyDelegate(new FlowRuleEvent(Type.RULE_REMOVE_REQUESTED, rule)); | 197 | + notifyDelegate(FlowRuleBatchEvent.create( |
198 | + new FlowRuleBatchRequest( | ||
199 | + Collections.<FlowEntry>emptyList(), | ||
200 | + Arrays.<FlowEntry>asList(entry)))); | ||
197 | } | 201 | } |
198 | } | 202 | } |
199 | } | 203 | } |
200 | } | 204 | } |
205 | + | ||
206 | + | ||
201 | //log.warn("Cannot find rule {}", rule); | 207 | //log.warn("Cannot find rule {}", rule); |
202 | } | 208 | } |
203 | 209 | ... | ... |
... | @@ -82,12 +82,28 @@ public class SimpleIntentStore | ... | @@ -82,12 +82,28 @@ public class SimpleIntentStore |
82 | public IntentEvent setState(Intent intent, IntentState state) { | 82 | public IntentEvent setState(Intent intent, IntentState state) { |
83 | IntentId id = intent.id(); | 83 | IntentId id = intent.id(); |
84 | states.put(id, state); | 84 | states.put(id, state); |
85 | - IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED : | 85 | + IntentEvent.Type type = null; |
86 | - (state == INSTALLED ? IntentEvent.Type.INSTALLED : | 86 | + |
87 | - (state == FAILED ? IntentEvent.Type.FAILED : | 87 | + switch (state) { |
88 | - state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN : | 88 | + case SUBMITTED: |
89 | - null))); | 89 | + type = IntentEvent.Type.SUBMITTED; |
90 | - return type == null ? null : new IntentEvent(type, intent); | 90 | + break; |
91 | + case INSTALLED: | ||
92 | + type = IntentEvent.Type.INSTALLED; | ||
93 | + break; | ||
94 | + case FAILED: | ||
95 | + type = IntentEvent.Type.FAILED; | ||
96 | + break; | ||
97 | + case WITHDRAWN: | ||
98 | + type = IntentEvent.Type.WITHDRAWN; | ||
99 | + break; | ||
100 | + default: | ||
101 | + break; | ||
102 | + } | ||
103 | + if (type == null) { | ||
104 | + return null; | ||
105 | + } | ||
106 | + return new IntentEvent(type, intent); | ||
91 | } | 107 | } |
92 | 108 | ||
93 | @Override | 109 | @Override | ... | ... |
... | @@ -192,14 +192,6 @@ public class SimpleLinkStore | ... | @@ -192,14 +192,6 @@ public class SimpleLinkStore |
192 | // Creates and stores the link and returns the appropriate event. | 192 | // Creates and stores the link and returns the appropriate event. |
193 | // Guarded by linkDescs value (=locking each Link) | 193 | // Guarded by linkDescs value (=locking each Link) |
194 | private LinkEvent createLink(LinkKey key, Link newLink) { | 194 | private LinkEvent createLink(LinkKey key, Link newLink) { |
195 | - | ||
196 | - if (newLink.providerId().isAncillary()) { | ||
197 | - // TODO: revisit ancillary only Link handling | ||
198 | - | ||
199 | - // currently treating ancillary only as down (not visible outside) | ||
200 | - return null; | ||
201 | - } | ||
202 | - | ||
203 | links.put(key, newLink); | 195 | links.put(key, newLink); |
204 | srcLinks.put(newLink.src().deviceId(), key); | 196 | srcLinks.put(newLink.src().deviceId(), key); |
205 | dstLinks.put(newLink.dst().deviceId(), key); | 197 | dstLinks.put(newLink.dst().deviceId(), key); |
... | @@ -209,10 +201,8 @@ public class SimpleLinkStore | ... | @@ -209,10 +201,8 @@ public class SimpleLinkStore |
209 | // Updates, if necessary the specified link and returns the appropriate event. | 201 | // Updates, if necessary the specified link and returns the appropriate event. |
210 | // Guarded by linkDescs value (=locking each Link) | 202 | // Guarded by linkDescs value (=locking each Link) |
211 | private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) { | 203 | private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) { |
212 | - | ||
213 | if (newLink.providerId().isAncillary()) { | 204 | if (newLink.providerId().isAncillary()) { |
214 | // TODO: revisit ancillary only Link handling | 205 | // TODO: revisit ancillary only Link handling |
215 | - | ||
216 | // currently treating ancillary only as down (not visible outside) | 206 | // currently treating ancillary only as down (not visible outside) |
217 | return null; | 207 | return null; |
218 | } | 208 | } | ... | ... |
1 | package org.onlab.onos.store.trivial.impl; | 1 | package org.onlab.onos.store.trivial.impl; |
2 | 2 | ||
3 | -import static org.junit.Assert.*; | 3 | +import com.google.common.collect.Iterables; |
4 | -import static org.onlab.onos.net.DeviceId.deviceId; | ||
5 | -import static org.onlab.onos.net.Link.Type.*; | ||
6 | -import static org.onlab.onos.net.link.LinkEvent.Type.*; | ||
7 | -import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals; | ||
8 | - | ||
9 | -import java.util.Collections; | ||
10 | -import java.util.HashMap; | ||
11 | -import java.util.Map; | ||
12 | -import java.util.Set; | ||
13 | -import java.util.concurrent.CountDownLatch; | ||
14 | -import java.util.concurrent.TimeUnit; | ||
15 | - | ||
16 | import org.junit.After; | 4 | import org.junit.After; |
17 | import org.junit.AfterClass; | 5 | import org.junit.AfterClass; |
18 | import org.junit.Before; | 6 | import org.junit.Before; |
... | @@ -23,17 +11,27 @@ import org.onlab.onos.net.ConnectPoint; | ... | @@ -23,17 +11,27 @@ import org.onlab.onos.net.ConnectPoint; |
23 | import org.onlab.onos.net.DefaultAnnotations; | 11 | import org.onlab.onos.net.DefaultAnnotations; |
24 | import org.onlab.onos.net.DeviceId; | 12 | import org.onlab.onos.net.DeviceId; |
25 | import org.onlab.onos.net.Link; | 13 | import org.onlab.onos.net.Link; |
14 | +import org.onlab.onos.net.Link.Type; | ||
26 | import org.onlab.onos.net.LinkKey; | 15 | import org.onlab.onos.net.LinkKey; |
27 | import org.onlab.onos.net.PortNumber; | 16 | import org.onlab.onos.net.PortNumber; |
28 | import org.onlab.onos.net.SparseAnnotations; | 17 | import org.onlab.onos.net.SparseAnnotations; |
29 | -import org.onlab.onos.net.Link.Type; | ||
30 | import org.onlab.onos.net.link.DefaultLinkDescription; | 18 | import org.onlab.onos.net.link.DefaultLinkDescription; |
31 | import org.onlab.onos.net.link.LinkEvent; | 19 | import org.onlab.onos.net.link.LinkEvent; |
32 | import org.onlab.onos.net.link.LinkStore; | 20 | import org.onlab.onos.net.link.LinkStore; |
33 | import org.onlab.onos.net.link.LinkStoreDelegate; | 21 | import org.onlab.onos.net.link.LinkStoreDelegate; |
34 | import org.onlab.onos.net.provider.ProviderId; | 22 | import org.onlab.onos.net.provider.ProviderId; |
35 | 23 | ||
36 | -import com.google.common.collect.Iterables; | 24 | +import java.util.HashMap; |
25 | +import java.util.Map; | ||
26 | +import java.util.Set; | ||
27 | +import java.util.concurrent.CountDownLatch; | ||
28 | +import java.util.concurrent.TimeUnit; | ||
29 | + | ||
30 | +import static org.junit.Assert.*; | ||
31 | +import static org.onlab.onos.net.DeviceId.deviceId; | ||
32 | +import static org.onlab.onos.net.Link.Type.*; | ||
33 | +import static org.onlab.onos.net.link.LinkEvent.Type.*; | ||
34 | +import static org.onlab.onos.store.trivial.impl.SimpleDeviceStoreTest.assertAnnotationsEquals; | ||
37 | 35 | ||
38 | /** | 36 | /** |
39 | * Test of the simple LinkStore implementation. | 37 | * Test of the simple LinkStore implementation. |
... | @@ -301,7 +299,7 @@ public class SimpleLinkStoreTest { | ... | @@ -301,7 +299,7 @@ public class SimpleLinkStoreTest { |
301 | LinkEvent event = linkStore.createOrUpdateLink(PIDA, | 299 | LinkEvent event = linkStore.createOrUpdateLink(PIDA, |
302 | new DefaultLinkDescription(src, dst, INDIRECT, A1)); | 300 | new DefaultLinkDescription(src, dst, INDIRECT, A1)); |
303 | 301 | ||
304 | - assertNull("Ancillary only link is ignored", event); | 302 | + assertNotNull("Ancillary only link is ignored", event); |
305 | 303 | ||
306 | // add Primary link | 304 | // add Primary link |
307 | LinkEvent event2 = linkStore.createOrUpdateLink(PID, | 305 | LinkEvent event2 = linkStore.createOrUpdateLink(PID, |
... | @@ -309,7 +307,7 @@ public class SimpleLinkStoreTest { | ... | @@ -309,7 +307,7 @@ public class SimpleLinkStoreTest { |
309 | 307 | ||
310 | assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject()); | 308 | assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject()); |
311 | assertAnnotationsEquals(event2.subject().annotations(), A2, A1); | 309 | assertAnnotationsEquals(event2.subject().annotations(), A2, A1); |
312 | - assertEquals(LINK_ADDED, event2.type()); | 310 | + assertEquals(LINK_UPDATED, event2.type()); |
313 | 311 | ||
314 | // update link type | 312 | // update link type |
315 | LinkEvent event3 = linkStore.createOrUpdateLink(PID, | 313 | LinkEvent event3 = linkStore.createOrUpdateLink(PID, |
... | @@ -375,7 +373,7 @@ public class SimpleLinkStoreTest { | ... | @@ -375,7 +373,7 @@ public class SimpleLinkStoreTest { |
375 | } | 373 | } |
376 | 374 | ||
377 | @Test | 375 | @Test |
378 | - public final void testAncillaryOnlyNotVisible() { | 376 | + public final void testAncillaryVisible() { |
379 | ConnectPoint src = new ConnectPoint(DID1, P1); | 377 | ConnectPoint src = new ConnectPoint(DID1, P1); |
380 | ConnectPoint dst = new ConnectPoint(DID2, P2); | 378 | ConnectPoint dst = new ConnectPoint(DID2, P2); |
381 | 379 | ||
... | @@ -384,18 +382,8 @@ public class SimpleLinkStoreTest { | ... | @@ -384,18 +382,8 @@ public class SimpleLinkStoreTest { |
384 | new DefaultLinkDescription(src, dst, INDIRECT, A1)); | 382 | new DefaultLinkDescription(src, dst, INDIRECT, A1)); |
385 | 383 | ||
386 | // Ancillary only link should not be visible | 384 | // Ancillary only link should not be visible |
387 | - assertEquals(0, linkStore.getLinkCount()); | 385 | + assertEquals(1, linkStore.getLinkCount()); |
388 | - | 386 | + assertNotNull(linkStore.getLink(src, dst)); |
389 | - assertTrue(Iterables.isEmpty(linkStore.getLinks())); | ||
390 | - | ||
391 | - assertNull(linkStore.getLink(src, dst)); | ||
392 | - | ||
393 | - assertEquals(Collections.emptySet(), linkStore.getIngressLinks(dst)); | ||
394 | - | ||
395 | - assertEquals(Collections.emptySet(), linkStore.getEgressLinks(src)); | ||
396 | - | ||
397 | - assertEquals(Collections.emptySet(), linkStore.getDeviceEgressLinks(DID1)); | ||
398 | - assertEquals(Collections.emptySet(), linkStore.getDeviceIngressLinks(DID2)); | ||
399 | } | 387 | } |
400 | 388 | ||
401 | // If Delegates should be called only on remote events, | 389 | // If Delegates should be called only on remote events, | ... | ... |
... | @@ -199,9 +199,16 @@ | ... | @@ -199,9 +199,16 @@ |
199 | 199 | ||
200 | <feature name="onos-app-metrics" version="1.0.0" | 200 | <feature name="onos-app-metrics" version="1.0.0" |
201 | description="ONOS metrics applications"> | 201 | description="ONOS metrics applications"> |
202 | + <feature>onos-app-metrics-intent</feature> | ||
202 | <feature>onos-app-metrics-topology</feature> | 203 | <feature>onos-app-metrics-topology</feature> |
203 | </feature> | 204 | </feature> |
204 | 205 | ||
206 | + <feature name="onos-app-metrics-intent" version="1.0.0" | ||
207 | + description="ONOS intent metrics application"> | ||
208 | + <feature>onos-api</feature> | ||
209 | + <bundle>mvn:org.onlab.onos/onos-app-metrics-intent/1.0.0-SNAPSHOT</bundle> | ||
210 | + </feature> | ||
211 | + | ||
205 | <feature name="onos-app-metrics-topology" version="1.0.0" | 212 | <feature name="onos-app-metrics-topology" version="1.0.0" |
206 | description="ONOS topology metrics application"> | 213 | description="ONOS topology metrics application"> |
207 | <feature>onos-api</feature> | 214 | <feature>onos-api</feature> | ... | ... |
... | @@ -110,8 +110,7 @@ public interface OpenFlowSwitch { | ... | @@ -110,8 +110,7 @@ public interface OpenFlowSwitch { |
110 | * | 110 | * |
111 | * @param role the failed role | 111 | * @param role the failed role |
112 | */ | 112 | */ |
113 | - void returnRoleAssertFailure(RoleState role); | 113 | + public void returnRoleAssertFailure(RoleState role); |
114 | - | ||
115 | 114 | ||
116 | /** | 115 | /** |
117 | * Indicates if this switch is optical. | 116 | * Indicates if this switch is optical. |
... | @@ -120,5 +119,4 @@ public interface OpenFlowSwitch { | ... | @@ -120,5 +119,4 @@ public interface OpenFlowSwitch { |
120 | */ | 119 | */ |
121 | public boolean isOptical(); | 120 | public boolean isOptical(); |
122 | 121 | ||
123 | - | ||
124 | } | 122 | } | ... | ... |
... | @@ -20,6 +20,12 @@ public interface OpenFlowSwitchListener { | ... | @@ -20,6 +20,12 @@ public interface OpenFlowSwitchListener { |
20 | public void switchRemoved(Dpid dpid); | 20 | public void switchRemoved(Dpid dpid); |
21 | 21 | ||
22 | /** | 22 | /** |
23 | + * Notify that the switch has changed in some way. | ||
24 | + * @param dpid the switch that changed | ||
25 | + */ | ||
26 | + public void switchChanged(Dpid dpid); | ||
27 | + | ||
28 | + /** | ||
23 | * Notify that a port has changed. | 29 | * Notify that a port has changed. |
24 | * @param dpid the switch on which the change happened. | 30 | * @param dpid the switch on which the change happened. |
25 | * @param status the new state of the port. | 31 | * @param status the new state of the port. | ... | ... |
... | @@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.OFHello; | ... | @@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.OFHello; |
41 | import org.projectfloodlight.openflow.protocol.OFHelloElem; | 41 | import org.projectfloodlight.openflow.protocol.OFHelloElem; |
42 | import org.projectfloodlight.openflow.protocol.OFMessage; | 42 | import org.projectfloodlight.openflow.protocol.OFMessage; |
43 | import org.projectfloodlight.openflow.protocol.OFPacketIn; | 43 | import org.projectfloodlight.openflow.protocol.OFPacketIn; |
44 | -import org.projectfloodlight.openflow.protocol.OFPacketOut; | ||
45 | import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; | 44 | import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; |
46 | import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest; | 45 | import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest; |
47 | import org.projectfloodlight.openflow.protocol.OFPortStatus; | 46 | import org.projectfloodlight.openflow.protocol.OFPortStatus; |
... | @@ -565,6 +564,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -565,6 +564,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
565 | @Override | 564 | @Override |
566 | void processOFStatisticsReply(OFChannelHandler h, | 565 | void processOFStatisticsReply(OFChannelHandler h, |
567 | OFStatsReply m) { | 566 | OFStatsReply m) { |
567 | + if (m.getStatsType().equals(OFStatsType.PORT_DESC)) { | ||
568 | + h.sw.setPortDescReply((OFPortDescStatsReply) m); | ||
569 | + } | ||
568 | h.dispatchMessage(m); | 570 | h.dispatchMessage(m); |
569 | } | 571 | } |
570 | 572 | ||
... | @@ -608,6 +610,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -608,6 +610,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
608 | h.dispatchMessage(m); | 610 | h.dispatchMessage(m); |
609 | } | 611 | } |
610 | 612 | ||
613 | + @Override | ||
614 | + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) { | ||
615 | + h.sw.setFeaturesReply(m); | ||
616 | + h.dispatchMessage(m); | ||
617 | + } | ||
618 | + | ||
611 | }; | 619 | }; |
612 | 620 | ||
613 | private final boolean handshakeComplete; | 621 | private final boolean handshakeComplete; |
... | @@ -652,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -652,10 +660,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
652 | * However, we could be more forgiving | 660 | * However, we could be more forgiving |
653 | * @param h the channel handler that received the message | 661 | * @param h the channel handler that received the message |
654 | * @param m the message | 662 | * @param m the message |
655 | - * @throws SwitchStateException | 663 | + * @throws SwitchStateException we always throw the exception |
656 | - * @throws SwitchStateExeption we always through the execption | ||
657 | */ | 664 | */ |
658 | - // needs to be protected because enum members are acutally subclasses | 665 | + // needs to be protected because enum members are actually subclasses |
659 | protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) | 666 | protected void illegalMessageReceived(OFChannelHandler h, OFMessage m) |
660 | throws SwitchStateException { | 667 | throws SwitchStateException { |
661 | String msg = getSwitchStateMessage(h, m, | 668 | String msg = getSwitchStateMessage(h, m, |
... | @@ -1016,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { | ... | @@ -1016,7 +1023,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { |
1016 | // all state for the original switch (with the same dpid), | 1023 | // all state for the original switch (with the same dpid), |
1017 | // which we obviously don't want. | 1024 | // which we obviously don't want. |
1018 | log.info("{}:removal called", getSwitchInfoString()); | 1025 | log.info("{}:removal called", getSwitchInfoString()); |
1026 | + if (sw != null) { | ||
1019 | sw.removeConnectedSwitch(); | 1027 | sw.removeConnectedSwitch(); |
1028 | + } | ||
1020 | } else { | 1029 | } else { |
1021 | // A duplicate was disconnected on this ChannelHandler, | 1030 | // A duplicate was disconnected on this ChannelHandler, |
1022 | // this is the same switch reconnecting, but the original state was | 1031 | // this is the same switch reconnecting, but the original state was | ... | ... |
... | @@ -27,6 +27,8 @@ import org.onlab.onos.openflow.controller.driver.OpenFlowAgent; | ... | @@ -27,6 +27,8 @@ import org.onlab.onos.openflow.controller.driver.OpenFlowAgent; |
27 | import org.projectfloodlight.openflow.protocol.OFMessage; | 27 | import org.projectfloodlight.openflow.protocol.OFMessage; |
28 | import org.projectfloodlight.openflow.protocol.OFPacketIn; | 28 | import org.projectfloodlight.openflow.protocol.OFPacketIn; |
29 | import org.projectfloodlight.openflow.protocol.OFPortStatus; | 29 | import org.projectfloodlight.openflow.protocol.OFPortStatus; |
30 | +import org.projectfloodlight.openflow.protocol.OFStatsReply; | ||
31 | +import org.projectfloodlight.openflow.protocol.OFStatsType; | ||
30 | import org.slf4j.Logger; | 32 | import org.slf4j.Logger; |
31 | import org.slf4j.LoggerFactory; | 33 | import org.slf4j.LoggerFactory; |
32 | 34 | ||
... | @@ -146,6 +148,11 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -146,6 +148,11 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
146 | l.portChanged(dpid, (OFPortStatus) msg); | 148 | l.portChanged(dpid, (OFPortStatus) msg); |
147 | } | 149 | } |
148 | break; | 150 | break; |
151 | + case FEATURES_REPLY: | ||
152 | + for (OpenFlowSwitchListener l : ofSwitchListener) { | ||
153 | + l.switchChanged(dpid); | ||
154 | + } | ||
155 | + break; | ||
149 | case PACKET_IN: | 156 | case PACKET_IN: |
150 | OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext | 157 | OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext |
151 | .packetContextFromPacketIn(this.getSwitch(dpid), | 158 | .packetContextFromPacketIn(this.getSwitch(dpid), |
... | @@ -154,9 +161,15 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -154,9 +161,15 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
154 | p.handlePacket(pktCtx); | 161 | p.handlePacket(pktCtx); |
155 | } | 162 | } |
156 | break; | 163 | break; |
164 | + case STATS_REPLY: | ||
165 | + OFStatsReply reply = (OFStatsReply) msg; | ||
166 | + if (reply.getStatsType().equals(OFStatsType.PORT_DESC)) { | ||
167 | + for (OpenFlowSwitchListener l : ofSwitchListener) { | ||
168 | + l.switchChanged(dpid); | ||
169 | + } | ||
170 | + } | ||
157 | case FLOW_REMOVED: | 171 | case FLOW_REMOVED: |
158 | case ERROR: | 172 | case ERROR: |
159 | - case STATS_REPLY: | ||
160 | case BARRIER_REPLY: | 173 | case BARRIER_REPLY: |
161 | executor.submit(new OFMessageHandler(dpid, msg)); | 174 | executor.submit(new OFMessageHandler(dpid, msg)); |
162 | break; | 175 | break; |
... | @@ -194,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { | ... | @@ -194,7 +207,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { |
194 | + "value for dpid: {}", dpid); | 207 | + "value for dpid: {}", dpid); |
195 | return false; | 208 | return false; |
196 | } else { | 209 | } else { |
197 | - log.error("Added switch {}", dpid); | 210 | + log.info("Added switch {}", dpid); |
198 | connectedSwitches.put(dpid, sw); | 211 | connectedSwitches.put(dpid, sw); |
199 | for (OpenFlowSwitchListener l : ofSwitchListener) { | 212 | for (OpenFlowSwitchListener l : ofSwitchListener) { |
200 | l.switchAdded(dpid); | 213 | l.switchAdded(dpid); | ... | ... |
... | @@ -1188,7 +1188,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { | ... | @@ -1188,7 +1188,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { |
1188 | .setHardTimeout(0) | 1188 | .setHardTimeout(0) |
1189 | .setXid(getNextTransactionId()) | 1189 | .setXid(getNextTransactionId()) |
1190 | .build(); | 1190 | .build(); |
1191 | - sendMsg(tableMissEntry); | 1191 | + |
1192 | + write(tableMissEntry); | ||
1192 | } | 1193 | } |
1193 | 1194 | ||
1194 | private void sendBarrier(boolean finalBarrier) { | 1195 | private void sendBarrier(boolean finalBarrier) { |
... | @@ -1200,7 +1201,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { | ... | @@ -1200,7 +1201,8 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { |
1200 | .buildBarrierRequest() | 1201 | .buildBarrierRequest() |
1201 | .setXid(xid) | 1202 | .setXid(xid) |
1202 | .build(); | 1203 | .build(); |
1203 | - sendMsg(br); | 1204 | + |
1205 | + write(br); | ||
1204 | } | 1206 | } |
1205 | 1207 | ||
1206 | @Override | 1208 | @Override |
... | @@ -1210,7 +1212,7 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { | ... | @@ -1210,7 +1212,7 @@ public class OFSwitchImplCPqD13 extends AbstractOpenFlowSwitch { |
1210 | 1212 | ||
1211 | @Override | 1213 | @Override |
1212 | public void write(OFMessage msg) { | 1214 | public void write(OFMessage msg) { |
1213 | - this.channel.write(msg); | 1215 | + this.channel.write(Collections.singletonList(msg)); |
1214 | 1216 | ||
1215 | } | 1217 | } |
1216 | 1218 | ... | ... |
... | @@ -88,7 +88,6 @@ | ... | @@ -88,7 +88,6 @@ |
88 | <version>18.0</version> | 88 | <version>18.0</version> |
89 | </dependency> | 89 | </dependency> |
90 | 90 | ||
91 | - | ||
92 | <dependency> | 91 | <dependency> |
93 | <groupId>io.netty</groupId> | 92 | <groupId>io.netty</groupId> |
94 | <artifactId>netty</artifactId> | 93 | <artifactId>netty</artifactId> |
... | @@ -610,7 +609,8 @@ | ... | @@ -610,7 +609,8 @@ |
610 | </plugin> | 609 | </plugin> |
611 | 610 | ||
612 | </plugins> | 611 | </plugins> |
613 | - | ||
614 | </reporting> | 612 | </reporting> |
615 | - | 613 | + <prerequisites> |
614 | + <maven>3.0.0</maven> | ||
615 | + </prerequisites> | ||
616 | </project> | 616 | </project> | ... | ... |
... | @@ -16,6 +16,7 @@ | ... | @@ -16,6 +16,7 @@ |
16 | package org.onlab.onos.provider.lldp.impl; | 16 | package org.onlab.onos.provider.lldp.impl; |
17 | 17 | ||
18 | 18 | ||
19 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
19 | import static org.slf4j.LoggerFactory.getLogger; | 20 | import static org.slf4j.LoggerFactory.getLogger; |
20 | 21 | ||
21 | import java.nio.ByteBuffer; | 22 | import java.nio.ByteBuffer; |
... | @@ -95,11 +96,13 @@ public class LinkDiscovery implements TimerTask { | ... | @@ -95,11 +96,13 @@ public class LinkDiscovery implements TimerTask { |
95 | */ | 96 | */ |
96 | public LinkDiscovery(Device device, PacketService pktService, | 97 | public LinkDiscovery(Device device, PacketService pktService, |
97 | MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) { | 98 | MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) { |
99 | + | ||
98 | this.device = device; | 100 | this.device = device; |
99 | this.probeRate = 3000; | 101 | this.probeRate = 3000; |
100 | this.linkProvider = providerService; | 102 | this.linkProvider = providerService; |
101 | this.pktService = pktService; | 103 | this.pktService = pktService; |
102 | - this.mastershipService = masterService; | 104 | + |
105 | + this.mastershipService = checkNotNull(masterService, "WTF!"); | ||
103 | this.slowPorts = Collections.synchronizedSet(new HashSet<Long>()); | 106 | this.slowPorts = Collections.synchronizedSet(new HashSet<Long>()); |
104 | this.fastPorts = Collections.synchronizedSet(new HashSet<Long>()); | 107 | this.fastPorts = Collections.synchronizedSet(new HashSet<Long>()); |
105 | this.portProbeCount = new HashMap<>(); | 108 | this.portProbeCount = new HashMap<>(); |
... | @@ -344,7 +347,14 @@ public class LinkDiscovery implements TimerTask { | ... | @@ -344,7 +347,14 @@ public class LinkDiscovery implements TimerTask { |
344 | } | 347 | } |
345 | 348 | ||
346 | private void sendProbes(Long portNumber) { | 349 | private void sendProbes(Long portNumber) { |
347 | - if (mastershipService.getLocalRole(this.device.id()) == | 350 | + if (device == null) { |
351 | + log.warn("CRAZY SHIT"); | ||
352 | + } | ||
353 | + if (mastershipService == null) { | ||
354 | + log.warn("INSANE"); | ||
355 | + } | ||
356 | + if (device.type() != Device.Type.ROADM && | ||
357 | + mastershipService.getLocalRole(this.device.id()) == | ||
348 | MastershipRole.MASTER) { | 358 | MastershipRole.MASTER) { |
349 | OutboundPacket pkt = this.createOutBoundLLDP(portNumber); | 359 | OutboundPacket pkt = this.createOutBoundLLDP(portNumber); |
350 | pktService.emit(pkt); | 360 | pktService.emit(pkt); | ... | ... |
... | @@ -23,7 +23,9 @@ import org.onlab.onos.openflow.controller.OpenFlowController; | ... | @@ -23,7 +23,9 @@ import org.onlab.onos.openflow.controller.OpenFlowController; |
23 | import org.onlab.onos.openflow.controller.OpenFlowSwitch; | 23 | import org.onlab.onos.openflow.controller.OpenFlowSwitch; |
24 | import org.onlab.onos.openflow.controller.OpenFlowSwitchListener; | 24 | import org.onlab.onos.openflow.controller.OpenFlowSwitchListener; |
25 | import org.onlab.onos.openflow.controller.RoleState; | 25 | import org.onlab.onos.openflow.controller.RoleState; |
26 | +import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver; | ||
26 | import org.onlab.packet.ChassisId; | 27 | import org.onlab.packet.ChassisId; |
28 | +import org.projectfloodlight.openflow.protocol.OFFactory; | ||
27 | import org.projectfloodlight.openflow.protocol.OFPortConfig; | 29 | import org.projectfloodlight.openflow.protocol.OFPortConfig; |
28 | import org.projectfloodlight.openflow.protocol.OFPortDesc; | 30 | import org.projectfloodlight.openflow.protocol.OFPortDesc; |
29 | import org.projectfloodlight.openflow.protocol.OFPortState; | 31 | import org.projectfloodlight.openflow.protocol.OFPortState; |
... | @@ -89,6 +91,28 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr | ... | @@ -89,6 +91,28 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr |
89 | @Override | 91 | @Override |
90 | public void triggerProbe(Device device) { | 92 | public void triggerProbe(Device device) { |
91 | LOG.info("Triggering probe on device {}", device.id()); | 93 | LOG.info("Triggering probe on device {}", device.id()); |
94 | + | ||
95 | + // 1. check device liveness | ||
96 | + // FIXME if possible, we might want this to be part of | ||
97 | + // OpenFlowSwitch interface so the driver interface isn't misused. | ||
98 | + OpenFlowSwitch sw = controller.getSwitch(dpid(device.id().uri())); | ||
99 | + if (!((OpenFlowSwitchDriver) sw).isConnected()) { | ||
100 | + providerService.deviceDisconnected(device.id()); | ||
101 | + return; | ||
102 | + } | ||
103 | + | ||
104 | + // 2. Prompt an update of port information. Do we have an XID for this? | ||
105 | + OFFactory fact = sw.factory(); | ||
106 | + switch (fact.getVersion()) { | ||
107 | + case OF_10: | ||
108 | + sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build()); | ||
109 | + break; | ||
110 | + case OF_13: | ||
111 | + sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build()); | ||
112 | + break; | ||
113 | + default: | ||
114 | + LOG.warn("Unhandled protocol version"); | ||
115 | + } | ||
92 | } | 116 | } |
93 | 117 | ||
94 | @Override | 118 | @Override |
... | @@ -141,6 +165,17 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr | ... | @@ -141,6 +165,17 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr |
141 | providerService.deviceDisconnected(deviceId(uri(dpid))); | 165 | providerService.deviceDisconnected(deviceId(uri(dpid))); |
142 | } | 166 | } |
143 | 167 | ||
168 | + | ||
169 | + @Override | ||
170 | + public void switchChanged(Dpid dpid) { | ||
171 | + if (providerService == null) { | ||
172 | + return; | ||
173 | + } | ||
174 | + DeviceId did = deviceId(uri(dpid)); | ||
175 | + OpenFlowSwitch sw = controller.getSwitch(dpid); | ||
176 | + providerService.updatePorts(did, buildPortDescriptions(sw.getPorts())); | ||
177 | + } | ||
178 | + | ||
144 | @Override | 179 | @Override |
145 | public void portChanged(Dpid dpid, OFPortStatus status) { | 180 | public void portChanged(Dpid dpid, OFPortStatus status) { |
146 | PortDescription portDescription = buildPortDescription(status.getDesc()); | 181 | PortDescription portDescription = buildPortDescription(status.getDesc()); | ... | ... |
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; |
... | @@ -107,6 +107,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -107,6 +107,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
107 | private final Map<Long, InstallationFuture> pendingFMs = | 107 | private final Map<Long, InstallationFuture> pendingFMs = |
108 | new ConcurrentHashMap<Long, InstallationFuture>(); | 108 | new ConcurrentHashMap<Long, InstallationFuture>(); |
109 | 109 | ||
110 | + private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); | ||
111 | + | ||
110 | /** | 112 | /** |
111 | * Creates an OpenFlow host provider. | 113 | * Creates an OpenFlow host provider. |
112 | */ | 114 | */ |
... | @@ -119,6 +121,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -119,6 +121,14 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
119 | providerService = providerRegistry.register(this); | 121 | providerService = providerRegistry.register(this); |
120 | controller.addListener(listener); | 122 | controller.addListener(listener); |
121 | controller.addEventListener(listener); | 123 | controller.addEventListener(listener); |
124 | + | ||
125 | + for (OpenFlowSwitch sw : controller.getSwitches()) { | ||
126 | + FlowStatsCollector fsc = new FlowStatsCollector(sw, POLL_INTERVAL); | ||
127 | + fsc.start(); | ||
128 | + collectors.put(new Dpid(sw.getId()), fsc); | ||
129 | + } | ||
130 | + | ||
131 | + | ||
122 | log.info("Started"); | 132 | log.info("Started"); |
123 | } | 133 | } |
124 | 134 | ||
... | @@ -217,7 +227,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -217,7 +227,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
217 | private class InternalFlowProvider | 227 | private class InternalFlowProvider |
218 | implements OpenFlowSwitchListener, OpenFlowEventListener { | 228 | implements OpenFlowSwitchListener, OpenFlowEventListener { |
219 | 229 | ||
220 | - private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); | 230 | + |
221 | private final Multimap<DeviceId, FlowEntry> completeEntries = | 231 | private final Multimap<DeviceId, FlowEntry> completeEntries = |
222 | ArrayListMultimap.create(); | 232 | ArrayListMultimap.create(); |
223 | 233 | ||
... | @@ -237,6 +247,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -237,6 +247,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
237 | } | 247 | } |
238 | 248 | ||
239 | @Override | 249 | @Override |
250 | + public void switchChanged(Dpid dpid) { | ||
251 | + } | ||
252 | + | ||
253 | + @Override | ||
240 | public void portChanged(Dpid dpid, OFPortStatus status) { | 254 | public void portChanged(Dpid dpid, OFPortStatus status) { |
241 | //TODO: Decide whether to evict flows internal store. | 255 | //TODO: Decide whether to evict flows internal store. |
242 | } | 256 | } |
... | @@ -317,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr | ... | @@ -317,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr |
317 | } | 331 | } |
318 | return false; | 332 | return false; |
319 | } | 333 | } |
334 | + | ||
320 | } | 335 | } |
321 | 336 | ||
322 | private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> { | 337 | private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> { | ... | ... |
providers/openflow/link/src/main/java/org/onlab/onos/provider/of/link/impl/OpenFlowLinkProvider.java
... | @@ -118,6 +118,12 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid | ... | @@ -118,6 +118,12 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid |
118 | DeviceId.deviceId("of:" + Long.toHexString(dpid.value()))); | 118 | DeviceId.deviceId("of:" + Long.toHexString(dpid.value()))); |
119 | } | 119 | } |
120 | 120 | ||
121 | + | ||
122 | + @Override | ||
123 | + public void switchChanged(Dpid dpid) { | ||
124 | + //might not need to do anything since DeviceManager is notified | ||
125 | + } | ||
126 | + | ||
121 | @Override | 127 | @Override |
122 | public void portChanged(Dpid dpid, OFPortStatus status) { | 128 | public void portChanged(Dpid dpid, OFPortStatus status) { |
123 | LinkDiscovery ld = discoverers.get(dpid); | 129 | LinkDiscovery ld = discoverers.get(dpid); | ... | ... |
... | @@ -7,4 +7,4 @@ export OC1="192.168.56.101" | ... | @@ -7,4 +7,4 @@ export OC1="192.168.56.101" |
7 | export OCN="192.168.56.103" | 7 | export OCN="192.168.56.103" |
8 | export OCI="${OC1}" | 8 | export OCI="${OC1}" |
9 | 9 | ||
10 | -export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}" | 10 | +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-rest,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}" | ... | ... |
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) |
35 | + #self.protocol = protocol ('tcp') | ||
34 | #self.checkListening() | 36 | #self.checkListening() |
35 | 37 | ||
36 | - ONOS_DIR = '/opt/onos/' | 38 | + self.onosDir = onosDir |
37 | - KARAF_DIR = ONOS_DIR + 'apache-karaf-3.0.1/' | 39 | + self.karafDir = onosDir + 'apache-karaf-3.0.1/' |
38 | - reactive = True | 40 | + self.instanceDir = self.karafDir |
41 | + | ||
42 | + # add default modules | ||
43 | + # TODO: consider an ordered set | ||
44 | + self.features = Set([ 'webconsole', | ||
45 | + 'onos-api', | ||
46 | + 'onos-cli', | ||
47 | + 'onos-openflow' ]) | ||
48 | + self.features.update( features ) | ||
49 | + # add reactive forwarding modules | ||
50 | + if reactive: | ||
51 | + self.features.update( ['onos-app-fwd', | ||
52 | + 'onos-app-proxyarp', | ||
53 | + 'onos-app-mobility' ] ) | ||
54 | + # add the distributed core if we are in a namespace with no trivial core | ||
55 | + if self.inNamespace and 'onos-core-trivial' not in self.features: | ||
56 | + self.features.add( 'onos-core' ) | ||
57 | + # if there is no core, add the trivial one | ||
58 | + if 'onos-core' not in self.features: | ||
59 | + self.features.add( 'onos-core-trivial' ) | ||
60 | + print self.features | ||
39 | 61 | ||
40 | def start( self ): | 62 | def start( self ): |
41 | - # switch to the non-root user because karaf gets upset otherwise | ||
42 | - # TODO we should look into why.... | ||
43 | - self.sendCmd( 'sudo su - %s' % self.findUser() ) | ||
44 | - self.waiting = False | ||
45 | - | ||
46 | if self.inNamespace: | 63 | if self.inNamespace: |
47 | - self.cmd( self.KARAF_DIR + 'bin/instance create %s' % self.name ) | 64 | + instanceOpts = ( '-furl mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features ' |
48 | - src = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg' | 65 | + '-s 8101' ) |
49 | - dst = self.KARAF_DIR + 'instances/%s/etc/org.apache.karaf.features.cfg' % self.name | 66 | + self.userCmd( self.karafDir + 'bin/instance create %s %s' % ( instanceOpts, self.name ) ) |
50 | - self.cmd( 'cp %s %s' % (src, dst) ) | 67 | + self.instanceDir = self.karafDir + 'instances/%s/' % self.name |
51 | - self.updateProperties( dst ) | ||
52 | - self.cmd( self.KARAF_DIR + 'bin/instance start %s' % self.name ) | ||
53 | else: | 68 | else: |
54 | # we are running in the root namespace, so let's use the root instance | 69 | # we are running in the root namespace, so let's use the root instance |
55 | - self.cmd( 'rm -rf '+ self.KARAF_DIR + 'data/' ) | 70 | + # clean up the data directory |
56 | - filename = self.KARAF_DIR + 'etc/org.apache.karaf.features.cfg' | 71 | + #self.userCmd( 'rm -rf '+ self.karafDir + 'data/' ) |
57 | - self.updateProperties( filename ) | 72 | + pass |
58 | - self.cmd( self.KARAF_DIR + 'bin/start' ) | 73 | + |
74 | + self.userCmd( 'rm -rf '+ self.instanceDir + 'data/' ) | ||
75 | + | ||
76 | + # Update etc/org.apache.karaf.features.cfg | ||
77 | + self.updateFeatures() | ||
78 | + | ||
79 | + # TODO 2. Update etc/hazelcast.xml : interface lines | ||
80 | + #cp etc/hazelcast.xml instances/c1/etc/ | ||
81 | + self.updateHazelcast() | ||
59 | 82 | ||
83 | + # TODO 3. Update etc/system.properties : onos.ip | ||
84 | + # TODO 4. Update config/cluster.json : with all nodes | ||
85 | + | ||
86 | + # start onos | ||
87 | + self.userCmd( self.instanceDir + 'bin/start' ) | ||
60 | #TODO we should wait for startup... | 88 | #TODO we should wait for startup... |
61 | 89 | ||
62 | def stop( self ): | 90 | def stop( self ): |
63 | - if self.inNamespace: | 91 | + self.userCmd( self.instanceDir + 'bin/stop' ) |
64 | - self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance stop %s' % self.name ) | 92 | + #if self.inNamespace: |
65 | - self.cmd( '/opt/onos/apache-karaf-3.0.1/bin/instance destroy %s' % self.name ) | 93 | + # self.userCmd( self.karafDir + 'bin/instance destroy %s' % self.name ) |
66 | - else: | ||
67 | - self.cmd( self.ONOS_DIR + 'apache-karaf-3.0.1/bin/stop' ) | ||
68 | self.terminate() | 94 | self.terminate() |
69 | 95 | ||
70 | - def updateProperties( self, filename ): | 96 | + def updateHazelcast( self ): |
97 | + readfile = self.karafDir + 'etc/hazelcast.xml' | ||
98 | + writefile = self.instanceDir + 'etc/hazelcast.xml' | ||
99 | + with open( readfile, 'r' ) as r: | ||
100 | + with open( writefile, 'w' ) as w: | ||
101 | + for line in r.readlines(): | ||
102 | + if '<interface>' in line: | ||
103 | + line = '<interface>' + '192.168.123.*' + '</interface>\n' | ||
104 | + w.write( line ) | ||
105 | + | ||
106 | + def updateFeatures( self ): | ||
107 | + filename = self.instanceDir + 'etc/org.apache.karaf.features.cfg' | ||
71 | with open( filename, 'r+' ) as f: | 108 | with open( filename, 'r+' ) as f: |
72 | lines = f.readlines() | 109 | lines = f.readlines() |
73 | f.seek(0) | 110 | f.seek(0) |
... | @@ -75,17 +112,25 @@ class ONOS( Controller ): | ... | @@ -75,17 +112,25 @@ class ONOS( Controller ): |
75 | for line in lines: | 112 | for line in lines: |
76 | #print '?', line, | 113 | #print '?', line, |
77 | if 'featuresBoot=' in line: | 114 | if 'featuresBoot=' in line: |
78 | - line = line.rstrip() | 115 | + # parse the features from the line |
79 | - #print ord(line[-1]), ord(line[-2]), ord(line[-3]) | 116 | + features = line.rstrip().split('=')[1].split(',') |
80 | - if self.reactive: | 117 | + # add the features to our features set |
81 | - line += ',onos-app-fwd' | 118 | + self.features.update( features ) |
82 | - line += '\n' | 119 | + # generate the new features line |
120 | + line = 'featuresBoot=' + ','.join( self.features ) + '\n' | ||
83 | #print '!', line, | 121 | #print '!', line, |
84 | f.write( line ) | 122 | f.write( line ) |
85 | 123 | ||
124 | + | ||
86 | @classmethod | 125 | @classmethod |
87 | def isAvailable( self ): | 126 | def isAvailable( self ): |
88 | - return quietRun( 'ls /opt/onos' ) | 127 | + return quietRun( 'ls %s' % self.onosDir ) |
128 | + | ||
129 | + def userCmd( self, cmd ): | ||
130 | + # switch to the non-root user because karaf gets upset otherwise | ||
131 | + # because the .m2repo is not stored with root | ||
132 | + cmd = 'sudo -u %s %s' % ( self.findUser(), cmd ) | ||
133 | + return self.cmd( cmd ) | ||
89 | 134 | ||
90 | @staticmethod | 135 | @staticmethod |
91 | def findUser(): | 136 | def findUser(): |
... | @@ -111,7 +156,7 @@ class ControlNetwork( Topo ): | ... | @@ -111,7 +156,7 @@ class ControlNetwork( Topo ): |
111 | # Connect everything to a single switch | 156 | # Connect everything to a single switch |
112 | cs0 = self.addSwitch( 'cs0' ) | 157 | cs0 = self.addSwitch( 'cs0' ) |
113 | # Add hosts which will serve as data network controllers | 158 | # Add hosts which will serve as data network controllers |
114 | - for i in range( 0, n ): | 159 | + for i in range( 1, n+1 ): |
115 | c = self.addHost( 'c%s' % i, cls=dataController, | 160 | c = self.addHost( 'c%s' % i, cls=dataController, |
116 | inNamespace=True ) | 161 | inNamespace=True ) |
117 | self.addLink( c, cs0 ) | 162 | self.addLink( c, cs0 ) |
... | @@ -122,7 +167,7 @@ class ControlNetwork( Topo ): | ... | @@ -122,7 +167,7 @@ class ControlNetwork( Topo ): |
122 | 167 | ||
123 | class ONOSCluster( Controller ): | 168 | class ONOSCluster( Controller ): |
124 | # TODO | 169 | # TODO |
125 | - n = 4 | 170 | + n = 3 |
126 | 171 | ||
127 | def start( self ): | 172 | def start( self ): |
128 | ctopo = ControlNetwork( n=self.n, dataController=ONOS ) | 173 | ctopo = ControlNetwork( n=self.n, dataController=ONOS ) |
... | @@ -137,6 +182,9 @@ class ONOSCluster( Controller ): | ... | @@ -137,6 +182,9 @@ class ONOSCluster( Controller ): |
137 | host.start() | 182 | host.start() |
138 | 183 | ||
139 | def stop( self ): | 184 | def stop( self ): |
185 | + for host in self.cnet.hosts: | ||
186 | + if isinstance( host, Controller ): | ||
187 | + host.stop() | ||
140 | self.cnet.stop() | 188 | self.cnet.stop() |
141 | 189 | ||
142 | def clist( self ): | 190 | def clist( self ): |
... | @@ -158,10 +206,11 @@ switches = { 'ovso': OVSSwitchONOS } | ... | @@ -158,10 +206,11 @@ switches = { 'ovso': OVSSwitchONOS } |
158 | 206 | ||
159 | if __name__ == '__main__': | 207 | if __name__ == '__main__': |
160 | # Simple test for ONOS() controller class | 208 | # Simple test for ONOS() controller class |
161 | - setLogLevel( 'info' ) | 209 | + setLogLevel( 'info' ) #TODO info |
162 | size = 2 if len( argv ) != 2 else int( argv[ 1 ] ) | 210 | size = 2 if len( argv ) != 2 else int( argv[ 1 ] ) |
163 | net = Mininet( topo=LinearTopo( size ), | 211 | net = Mininet( topo=LinearTopo( size ), |
164 | - controller=partial( ONOSCluster, n=4 ), | 212 | + #controller=ONOS, |
213 | + controller=partial( ONOSCluster, n=3 ), #TODO | ||
165 | switch=OVSSwitchONOS ) | 214 | switch=OVSSwitchONOS ) |
166 | net.start() | 215 | net.start() |
167 | #waitConnected( net.switches ) | 216 | #waitConnected( net.switches ) | ... | ... |
... | @@ -32,7 +32,7 @@ public final class ChassisId { | ... | @@ -32,7 +32,7 @@ public final class ChassisId { |
32 | * @param value the value to use. | 32 | * @param value the value to use. |
33 | */ | 33 | */ |
34 | public ChassisId(String value) { | 34 | public ChassisId(String value) { |
35 | - this.value = Long.valueOf(value); | 35 | + this.value = Long.valueOf(value, 16); |
36 | } | 36 | } |
37 | 37 | ||
38 | /** | 38 | /** | ... | ... |
1 | package org.onlab.nio; | 1 | package org.onlab.nio; |
2 | 2 | ||
3 | import org.junit.Before; | 3 | import org.junit.Before; |
4 | +import org.junit.Ignore; | ||
4 | import org.junit.Test; | 5 | import org.junit.Test; |
5 | 6 | ||
6 | import java.net.InetAddress; | 7 | import java.net.InetAddress; |
... | @@ -33,7 +34,8 @@ public class IOLoopIntegrationTest { | ... | @@ -33,7 +34,8 @@ public class IOLoopIntegrationTest { |
33 | } | 34 | } |
34 | } | 35 | } |
35 | 36 | ||
36 | - | 37 | + // TODO: this test can not pass in some environments, need to be improved |
38 | + @Ignore | ||
37 | @Test | 39 | @Test |
38 | public void basic() throws Exception { | 40 | public void basic() throws Exception { |
39 | runTest(MILLION, MESSAGE_LENGTH, TIMEOUT); | 41 | runTest(MILLION, MESSAGE_LENGTH, TIMEOUT); | ... | ... |
... | @@ -6,6 +6,7 @@ import com.google.common.collect.MutableClassToInstanceMap; | ... | @@ -6,6 +6,7 @@ import com.google.common.collect.MutableClassToInstanceMap; |
6 | /** | 6 | /** |
7 | * Service directory implementation suitable for testing. | 7 | * Service directory implementation suitable for testing. |
8 | */ | 8 | */ |
9 | +@SuppressWarnings("unchecked") | ||
9 | public class TestServiceDirectory implements ServiceDirectory { | 10 | public class TestServiceDirectory implements ServiceDirectory { |
10 | 11 | ||
11 | private ClassToInstanceMap<Object> services = MutableClassToInstanceMap.create(); | 12 | private ClassToInstanceMap<Object> services = MutableClassToInstanceMap.create(); | ... | ... |
... | @@ -23,13 +23,6 @@ | ... | @@ -23,13 +23,6 @@ |
23 | <version>1.0.0-SNAPSHOT</version> | 23 | <version>1.0.0-SNAPSHOT</version> |
24 | <scope>test</scope> | 24 | <scope>test</scope> |
25 | </dependency> | 25 | </dependency> |
26 | - <dependency> | ||
27 | - <groupId>com.google.guava</groupId> | ||
28 | - <artifactId>guava</artifactId> | ||
29 | - <version>17.0</version> | ||
30 | - <scope>test</scope> | ||
31 | - </dependency> | ||
32 | - | ||
33 | </dependencies> | 26 | </dependencies> |
34 | 27 | ||
35 | <properties> | 28 | <properties> | ... | ... |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.rest; | ||
20 | + | ||
21 | +import com.fasterxml.jackson.databind.JsonNode; | ||
22 | +import org.onlab.onos.net.ConnectPoint; | ||
23 | +import org.onlab.onos.net.DefaultAnnotations; | ||
24 | +import org.onlab.onos.net.Device; | ||
25 | +import org.onlab.onos.net.Host; | ||
26 | +import org.onlab.onos.net.HostId; | ||
27 | +import org.onlab.onos.net.HostLocation; | ||
28 | +import org.onlab.onos.net.Link; | ||
29 | +import org.onlab.onos.net.MastershipRole; | ||
30 | +import org.onlab.onos.net.SparseAnnotations; | ||
31 | +import org.onlab.onos.net.device.DefaultDeviceDescription; | ||
32 | +import org.onlab.onos.net.device.DeviceDescription; | ||
33 | +import org.onlab.onos.net.device.DeviceProvider; | ||
34 | +import org.onlab.onos.net.device.DeviceProviderRegistry; | ||
35 | +import org.onlab.onos.net.device.DeviceProviderService; | ||
36 | +import org.onlab.onos.net.host.DefaultHostDescription; | ||
37 | +import org.onlab.onos.net.host.HostProvider; | ||
38 | +import org.onlab.onos.net.host.HostProviderRegistry; | ||
39 | +import org.onlab.onos.net.host.HostProviderService; | ||
40 | +import org.onlab.onos.net.link.DefaultLinkDescription; | ||
41 | +import org.onlab.onos.net.link.LinkProvider; | ||
42 | +import org.onlab.onos.net.link.LinkProviderRegistry; | ||
43 | +import org.onlab.onos.net.link.LinkProviderService; | ||
44 | +import org.onlab.onos.net.provider.ProviderId; | ||
45 | +import org.onlab.packet.ChassisId; | ||
46 | +import org.onlab.packet.IpPrefix; | ||
47 | +import org.onlab.packet.MacAddress; | ||
48 | +import org.onlab.packet.VlanId; | ||
49 | + | ||
50 | +import java.net.URI; | ||
51 | +import java.util.Iterator; | ||
52 | + | ||
53 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
54 | +import static org.onlab.onos.net.DeviceId.deviceId; | ||
55 | +import static org.onlab.onos.net.PortNumber.portNumber; | ||
56 | + | ||
57 | +/** | ||
58 | + * Provider of devices and links parsed from a JSON configuration structure. | ||
59 | + */ | ||
60 | +class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider { | ||
61 | + | ||
62 | + private static final ProviderId PID = | ||
63 | + new ProviderId("cfg", "org.onlab.onos.rest", true); | ||
64 | + | ||
65 | + private final JsonNode cfg; | ||
66 | + private final DeviceProviderRegistry deviceProviderRegistry; | ||
67 | + private final LinkProviderRegistry linkProviderRegistry; | ||
68 | + private final HostProviderRegistry hostProviderRegistry; | ||
69 | + | ||
70 | + /** | ||
71 | + * Creates a new configuration provider. | ||
72 | + * | ||
73 | + * @param cfg JSON configuration | ||
74 | + * @param deviceProviderRegistry device provider registry | ||
75 | + * @param linkProviderRegistry link provider registry | ||
76 | + * @param hostProviderRegistry host provider registry | ||
77 | + */ | ||
78 | + ConfigProvider(JsonNode cfg, | ||
79 | + DeviceProviderRegistry deviceProviderRegistry, | ||
80 | + LinkProviderRegistry linkProviderRegistry, | ||
81 | + HostProviderRegistry hostProviderRegistry) { | ||
82 | + this.cfg = checkNotNull(cfg, "Configuration cannot be null"); | ||
83 | + this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null"); | ||
84 | + this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null"); | ||
85 | + this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null"); | ||
86 | + } | ||
87 | + | ||
88 | + /** | ||
89 | + * Parses the given JSON and provides links as configured. | ||
90 | + */ | ||
91 | + void parse() { | ||
92 | + parseDevices(); | ||
93 | + parseLinks(); | ||
94 | + parseHosts(); | ||
95 | + } | ||
96 | + | ||
97 | + // Parses the given JSON and provides devices. | ||
98 | + private void parseDevices() { | ||
99 | + try { | ||
100 | + DeviceProviderService dps = deviceProviderRegistry.register(this); | ||
101 | + JsonNode nodes = cfg.get("devices"); | ||
102 | + if (nodes != null) { | ||
103 | + for (JsonNode node : nodes) { | ||
104 | + parseDevice(dps, node); | ||
105 | + } | ||
106 | + } | ||
107 | + } finally { | ||
108 | + deviceProviderRegistry.unregister(this); | ||
109 | + } | ||
110 | + } | ||
111 | + | ||
112 | + // Parses the given node with device data and supplies the device. | ||
113 | + private void parseDevice(DeviceProviderService dps, JsonNode node) { | ||
114 | + URI uri = URI.create(get(node, "uri")); | ||
115 | + Device.Type type = Device.Type.valueOf(get(node, "type")); | ||
116 | + String mfr = get(node, "mfr"); | ||
117 | + String hw = get(node, "hw"); | ||
118 | + String sw = get(node, "sw"); | ||
119 | + String serial = get(node, "serial"); | ||
120 | + ChassisId cid = new ChassisId(get(node, "mac")); | ||
121 | + SparseAnnotations annotations = annotations(node.get("annotations")); | ||
122 | + | ||
123 | + DeviceDescription desc = | ||
124 | + new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial, | ||
125 | + cid, annotations); | ||
126 | + dps.deviceConnected(deviceId(uri), desc); | ||
127 | + } | ||
128 | + | ||
129 | + // Parses the given JSON and provides links as configured. | ||
130 | + private void parseLinks() { | ||
131 | + try { | ||
132 | + LinkProviderService lps = linkProviderRegistry.register(this); | ||
133 | + JsonNode nodes = cfg.get("links"); | ||
134 | + if (nodes != null) { | ||
135 | + for (JsonNode node : nodes) { | ||
136 | + parseLink(lps, node, false); | ||
137 | + if (!node.has("halfplex")) { | ||
138 | + parseLink(lps, node, true); | ||
139 | + } | ||
140 | + } | ||
141 | + } | ||
142 | + } finally { | ||
143 | + linkProviderRegistry.unregister(this); | ||
144 | + } | ||
145 | + } | ||
146 | + | ||
147 | + // Parses the given node with link data and supplies the link. | ||
148 | + private void parseLink(LinkProviderService lps, JsonNode node, boolean reverse) { | ||
149 | + ConnectPoint src = connectPoint(get(node, "src")); | ||
150 | + ConnectPoint dst = connectPoint(get(node, "dst")); | ||
151 | + Link.Type type = Link.Type.valueOf(get(node, "type")); | ||
152 | + SparseAnnotations annotations = annotations(node.get("annotations")); | ||
153 | + | ||
154 | + DefaultLinkDescription desc = reverse ? | ||
155 | + new DefaultLinkDescription(dst, src, type, annotations) : | ||
156 | + new DefaultLinkDescription(src, dst, type, annotations); | ||
157 | + lps.linkDetected(desc); | ||
158 | + } | ||
159 | + | ||
160 | + // Parses the given JSON and provides hosts as configured. | ||
161 | + private void parseHosts() { | ||
162 | + try { | ||
163 | + HostProviderService hps = hostProviderRegistry.register(this); | ||
164 | + JsonNode nodes = cfg.get("hosts"); | ||
165 | + if (nodes != null) { | ||
166 | + for (JsonNode node : nodes) { | ||
167 | + parseHost(hps, node); | ||
168 | + } | ||
169 | + } | ||
170 | + } finally { | ||
171 | + hostProviderRegistry.unregister(this); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + // Parses the given node with host data and supplies the host. | ||
176 | + private void parseHost(HostProviderService hps, JsonNode node) { | ||
177 | + MacAddress mac = MacAddress.valueOf(get(node, "mac")); | ||
178 | + VlanId vlanId = VlanId.vlanId(node.get("vlan").shortValue()); | ||
179 | + HostId hostId = HostId.hostId(mac, vlanId); | ||
180 | + SparseAnnotations annotations = annotations(node.get("annotations")); | ||
181 | + HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0); | ||
182 | + IpPrefix ip = IpPrefix.valueOf(get(node, "ip")); | ||
183 | + | ||
184 | + DefaultHostDescription desc = | ||
185 | + new DefaultHostDescription(mac, vlanId, location, ip, annotations); | ||
186 | + hps.hostDetected(hostId, desc); | ||
187 | + } | ||
188 | + | ||
189 | + // Produces set of annotations from the given JSON node. | ||
190 | + private SparseAnnotations annotations(JsonNode node) { | ||
191 | + if (node == null) { | ||
192 | + return null; | ||
193 | + } | ||
194 | + | ||
195 | + DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); | ||
196 | + Iterator<String> it = node.fieldNames(); | ||
197 | + while (it.hasNext()) { | ||
198 | + String k = it.next(); | ||
199 | + builder.set(k, node.get(k).asText()); | ||
200 | + } | ||
201 | + return builder.build(); | ||
202 | + } | ||
203 | + | ||
204 | + // Produces a connection point from the specified uri/port text. | ||
205 | + private ConnectPoint connectPoint(String text) { | ||
206 | + int i = text.lastIndexOf("/"); | ||
207 | + return new ConnectPoint(deviceId(text.substring(0, i)), | ||
208 | + portNumber(text.substring(i + 1))); | ||
209 | + } | ||
210 | + | ||
211 | + // Returns string form of the named property in the given JSON object. | ||
212 | + private String get(JsonNode node, String name) { | ||
213 | + return node.path(name).asText(); | ||
214 | + } | ||
215 | + | ||
216 | + @Override | ||
217 | + public void triggerProbe(Device device) { | ||
218 | + } | ||
219 | + | ||
220 | + @Override | ||
221 | + public void roleChanged(Device device, MastershipRole newRole) { | ||
222 | + } | ||
223 | + | ||
224 | + @Override | ||
225 | + public void triggerProbe(Host host) { | ||
226 | + } | ||
227 | + | ||
228 | + @Override | ||
229 | + public ProviderId id() { | ||
230 | + return PID; | ||
231 | + } | ||
232 | +} |
1 | +/* | ||
2 | + * Licensed to the Apache Software Foundation (ASF) under one | ||
3 | + * or more contributor license agreements. See the NOTICE file | ||
4 | + * distributed with this work for additional information | ||
5 | + * regarding copyright ownership. The ASF licenses this file | ||
6 | + * to you under the Apache License, Version 2.0 (the | ||
7 | + * "License"); you may not use this file except in compliance | ||
8 | + * with the License. You may obtain a copy of the License at | ||
9 | + * | ||
10 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
11 | + * | ||
12 | + * Unless required by applicable law or agreed to in writing, | ||
13 | + * software distributed under the License is distributed on an | ||
14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
15 | + * KIND, either express or implied. See the License for the | ||
16 | + * specific language governing permissions and limitations | ||
17 | + * under the License. | ||
18 | + */ | ||
19 | +package org.onlab.onos.rest; | ||
20 | + | ||
21 | +import com.fasterxml.jackson.databind.JsonNode; | ||
22 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
23 | +import org.onlab.onos.net.device.DeviceProviderRegistry; | ||
24 | +import org.onlab.onos.net.host.HostProviderRegistry; | ||
25 | +import org.onlab.onos.net.link.LinkProviderRegistry; | ||
26 | +import org.onlab.rest.BaseResource; | ||
27 | + | ||
28 | +import javax.ws.rs.Consumes; | ||
29 | +import javax.ws.rs.POST; | ||
30 | +import javax.ws.rs.Path; | ||
31 | +import javax.ws.rs.Produces; | ||
32 | +import javax.ws.rs.core.MediaType; | ||
33 | +import javax.ws.rs.core.Response; | ||
34 | +import java.io.IOException; | ||
35 | +import java.io.InputStream; | ||
36 | + | ||
37 | +/** | ||
38 | + * Resource that acts as an ancillary provider for uploading pre-configured | ||
39 | + * devices, ports and links. | ||
40 | + */ | ||
41 | +@Path("config") | ||
42 | +public class ConfigResource extends BaseResource { | ||
43 | + | ||
44 | + @POST | ||
45 | + @Path("topology") | ||
46 | + @Consumes(MediaType.APPLICATION_JSON) | ||
47 | + @Produces(MediaType.APPLICATION_JSON) | ||
48 | + public Response topology(InputStream input) throws IOException { | ||
49 | + ObjectMapper mapper = new ObjectMapper(); | ||
50 | + JsonNode cfg = mapper.readTree(input); | ||
51 | + new ConfigProvider(cfg, get(DeviceProviderRegistry.class), | ||
52 | + get(LinkProviderRegistry.class), | ||
53 | + get(HostProviderRegistry.class)).parse(); | ||
54 | + return Response.ok(mapper.createObjectNode().toString()).build(); | ||
55 | + } | ||
56 | + | ||
57 | +} |
1 | +{ | ||
2 | + "devices" : [ | ||
3 | + { | ||
4 | + "uri": "of:00000000000001", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3", | ||
5 | + "serial": "ab321", "mac": "00000000000001", "annotations": {"foo": "bar"}, | ||
6 | + "ports": [] | ||
7 | + }, | ||
8 | + { | ||
9 | + "uri": "of:00000000000002", "type": "ROADM", "mfr": "Foo, Inc.", "hw": "Alpha", "sw": "1.2.3", | ||
10 | + "serial": "ab456", "mac": "00000000000002", "annotations": {"foo": "bar"}, | ||
11 | + "ports": [] | ||
12 | + } | ||
13 | + ], | ||
14 | + | ||
15 | + "links" : [ | ||
16 | + { "src": "of:00000000000001/1", "dst": "of:00000000000002/1", "type": "OPTICAL" }, | ||
17 | + { "src": "of:00000000000002/1", "dst": "of:00000000000001/1", "type": "OPTICAL" } | ||
18 | + ] | ||
19 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
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> |
... | @@ -32,20 +33,20 @@ | ... | @@ -32,20 +33,20 @@ |
32 | <div id="view"></div> | 33 | <div id="view"></div> |
33 | </div> | 34 | </div> |
34 | 35 | ||
35 | - // Initialize the UI... | 36 | + <!-- Initialize the UI...--> |
36 | <script type="text/javascript"> | 37 | <script type="text/javascript"> |
37 | var ONOS = $.onos({note: "config, if needed"}); | 38 | var ONOS = $.onos({note: "config, if needed"}); |
38 | </script> | 39 | </script> |
39 | 40 | ||
40 | - // include module files | 41 | + <!-- include module files--> |
41 | - // + mast.js | 42 | + <!-- + mast.js--> |
42 | - // + nav.js | 43 | + <!-- + nav.js--> |
43 | - // + .... application views | 44 | + <!-- + .... application views--> |
44 | 45 | ||
45 | - // for now, we are just bootstrapping the network visualization | 46 | + <!-- for now, we are just bootstrapping the network visualization--> |
46 | <script src="network.js" type="text/javascript"></script> | 47 | <script src="network.js" type="text/javascript"></script> |
47 | 48 | ||
48 | - // finally, build the UI | 49 | + <!-- finally, build the UI--> |
49 | <script type="text/javascript"> | 50 | <script type="text/javascript"> |
50 | $(ONOS.buildUi); | 51 | $(ONOS.buildUi); |
51 | </script> | 52 | </script> | ... | ... |
... | @@ -10,11 +10,16 @@ | ... | @@ -10,11 +10,16 @@ |
10 | var api = onos.api; | 10 | var api = onos.api; |
11 | 11 | ||
12 | var config = { | 12 | var config = { |
13 | + layering: false, | ||
13 | jsonUrl: 'network.json', | 14 | jsonUrl: 'network.json', |
15 | + iconUrl: { | ||
16 | + pkt: 'pkt.png', | ||
17 | + opt: 'opt.png' | ||
18 | + }, | ||
14 | mastHeight: 32, | 19 | mastHeight: 32, |
15 | force: { | 20 | force: { |
16 | - linkDistance: 150, | 21 | + linkDistance: 240, |
17 | - linkStrength: 0.9, | 22 | + linkStrength: 0.8, |
18 | charge: -400, | 23 | charge: -400, |
19 | ticksWithoutCollisions: 50, | 24 | ticksWithoutCollisions: 50, |
20 | marginLR: 20, | 25 | marginLR: 20, |
... | @@ -26,8 +31,9 @@ | ... | @@ -26,8 +31,9 @@ |
26 | } | 31 | } |
27 | }, | 32 | }, |
28 | labels: { | 33 | labels: { |
29 | - padLR: 3, | 34 | + imgPad: 22, |
30 | - padTB: 2, | 35 | + padLR: 8, |
36 | + padTB: 6, | ||
31 | marginLR: 3, | 37 | marginLR: 3, |
32 | marginTB: 2 | 38 | marginTB: 2 |
33 | }, | 39 | }, |
... | @@ -53,7 +59,7 @@ | ... | @@ -53,7 +59,7 @@ |
53 | d3.json(config.jsonUrl, function (err, data) { | 59 | d3.json(config.jsonUrl, function (err, data) { |
54 | if (err) { | 60 | if (err) { |
55 | alert('Oops! Error reading JSON...\n\n' + | 61 | alert('Oops! Error reading JSON...\n\n' + |
56 | - 'URL: ' + jsonUrl + '\n\n' + | 62 | + 'URL: ' + config.jsonUrl + '\n\n' + |
57 | 'Error: ' + err.message); | 63 | 'Error: ' + err.message); |
58 | return; | 64 | return; |
59 | } | 65 | } |
... | @@ -100,7 +106,7 @@ | ... | @@ -100,7 +106,7 @@ |
100 | 106 | ||
101 | network.data.nodes.forEach(function(n) { | 107 | network.data.nodes.forEach(function(n) { |
102 | var ypc = yPosConstraintForNode(n), | 108 | var ypc = yPosConstraintForNode(n), |
103 | - ix = Math.random() * 0.8 * nw + 0.1 * nw, | 109 | + ix = Math.random() * 0.6 * nw + 0.2 * nw, |
104 | iy = ypc * nh, | 110 | iy = ypc * nh, |
105 | node = { | 111 | node = { |
106 | id: n.id, | 112 | id: n.id, |
... | @@ -153,10 +159,48 @@ | ... | @@ -153,10 +159,48 @@ |
153 | .attr('height', view.height) | 159 | .attr('height', view.height) |
154 | .append('g') | 160 | .append('g') |
155 | .attr('transform', config.force.translate()); | 161 | .attr('transform', config.force.translate()); |
162 | +// .attr('id', 'zoomable') | ||
163 | +// .call(d3.behavior.zoom().on("zoom", zoomRedraw)); | ||
164 | + | ||
165 | +// function zoomRedraw() { | ||
166 | +// d3.select("#zoomable").attr("transform", | ||
167 | +// "translate(" + d3.event.translate + ")" | ||
168 | +// + " scale(" + d3.event.scale + ")"); | ||
169 | +// } | ||
170 | + | ||
171 | + // TODO: svg.append('defs') for markers? | ||
172 | + | ||
173 | + // TODO: move glow/blur stuff to util script | ||
174 | + var glow = network.svg.append('filter') | ||
175 | + .attr('x', '-50%') | ||
176 | + .attr('y', '-50%') | ||
177 | + .attr('width', '200%') | ||
178 | + .attr('height', '200%') | ||
179 | + .attr('id', 'blue-glow'); | ||
180 | + | ||
181 | + glow.append('feColorMatrix') | ||
182 | + .attr('type', 'matrix') | ||
183 | + .attr('values', '0 0 0 0 0 ' + | ||
184 | + '0 0 0 0 0 ' + | ||
185 | + '0 0 0 0 .7 ' + | ||
186 | + '0 0 0 1 0 '); | ||
187 | + | ||
188 | + glow.append('feGaussianBlur') | ||
189 | + .attr('stdDeviation', 3) | ||
190 | + .attr('result', 'coloredBlur'); | ||
191 | + | ||
192 | + glow.append('feMerge').selectAll('feMergeNode') | ||
193 | + .data(['coloredBlur', 'SourceGraphic']) | ||
194 | + .enter().append('feMergeNode') | ||
195 | + .attr('in', String); | ||
156 | 196 | ||
157 | - // TODO: svg.append('defs') | ||
158 | - // TODO: glow/blur stuff | ||
159 | // TODO: legend (and auto adjust on scroll) | 197 | // TODO: legend (and auto adjust on scroll) |
198 | +// $('#view').on('scroll', function() { | ||
199 | +// | ||
200 | +// }); | ||
201 | + | ||
202 | + | ||
203 | + | ||
160 | 204 | ||
161 | network.link = network.svg.append('g').selectAll('.link') | 205 | network.link = network.svg.append('g').selectAll('.link') |
162 | .data(network.force.links(), function(d) {return d.id}) | 206 | .data(network.force.links(), function(d) {return d.id}) |
... | @@ -164,37 +208,103 @@ | ... | @@ -164,37 +208,103 @@ |
164 | .attr('class', 'link'); | 208 | .attr('class', 'link'); |
165 | 209 | ||
166 | // TODO: drag behavior | 210 | // TODO: drag behavior |
167 | - // TODO: closest node deselect | 211 | + network.draggedThreshold = d3.scale.linear() |
212 | + .domain([0, 0.1]) | ||
213 | + .range([5, 20]) | ||
214 | + .clamp(true); | ||
215 | + | ||
216 | + function dragged(d) { | ||
217 | + var threshold = network.draggedThreshold(network.force.alpha()), | ||
218 | + dx = d.oldX - d.px, | ||
219 | + dy = d.oldY - d.py; | ||
220 | + if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) { | ||
221 | + d.dragged = true; | ||
222 | + } | ||
223 | + return d.dragged; | ||
224 | + } | ||
225 | + | ||
226 | + network.drag = d3.behavior.drag() | ||
227 | + .origin(function(d) { return d; }) | ||
228 | + .on('dragstart', function(d) { | ||
229 | + d.oldX = d.x; | ||
230 | + d.oldY = d.y; | ||
231 | + d.dragged = false; | ||
232 | + d.fixed |= 2; | ||
233 | + }) | ||
234 | + .on('drag', function(d) { | ||
235 | + d.px = d3.event.x; | ||
236 | + d.py = d3.event.y; | ||
237 | + if (dragged(d)) { | ||
238 | + if (!network.force.alpha()) { | ||
239 | + network.force.alpha(.025); | ||
240 | + } | ||
241 | + } | ||
242 | + }) | ||
243 | + .on('dragend', function(d) { | ||
244 | + if (!dragged(d)) { | ||
245 | + selectObject(d, this); | ||
246 | + } | ||
247 | + d.fixed &= ~6; | ||
248 | + }); | ||
249 | + | ||
250 | + $('#view').on('click', function(e) { | ||
251 | + if (!$(e.target).closest('.node').length) { | ||
252 | + deselectObject(); | ||
253 | + } | ||
254 | + }); | ||
255 | + | ||
168 | 256 | ||
169 | - // TODO: add drag, mouseover, mouseout behaviors | ||
170 | network.node = network.svg.selectAll('.node') | 257 | network.node = network.svg.selectAll('.node') |
171 | .data(network.force.nodes(), function(d) {return d.id}) | 258 | .data(network.force.nodes(), function(d) {return d.id}) |
172 | .enter().append('g') | 259 | .enter().append('g') |
173 | - .attr('class', 'node') | 260 | + .attr('class', function(d) { |
261 | + return 'node ' + d.type; | ||
262 | + }) | ||
174 | .attr('transform', function(d) { | 263 | .attr('transform', function(d) { |
175 | return translate(d.x, d.y); | 264 | return translate(d.x, d.y); |
176 | }) | 265 | }) |
177 | - // .call(network.drag) | 266 | + .call(network.drag) |
178 | - .on('mouseover', function(d) {}) | 267 | + .on('mouseover', function(d) { |
179 | - .on('mouseout', function(d) {}); | 268 | + if (!selected.obj) { |
269 | + if (network.mouseoutTimeout) { | ||
270 | + clearTimeout(network.mouseoutTimeout); | ||
271 | + network.mouseoutTimeout = null; | ||
272 | + } | ||
273 | + highlightObject(d); | ||
274 | + } | ||
275 | + }) | ||
276 | + .on('mouseout', function(d) { | ||
277 | + if (!selected.obj) { | ||
278 | + if (network.mouseoutTimeout) { | ||
279 | + clearTimeout(network.mouseoutTimeout); | ||
280 | + network.mouseoutTimeout = null; | ||
281 | + } | ||
282 | + network.mouseoutTimeout = setTimeout(function() { | ||
283 | + highlightObject(null); | ||
284 | + }, 160); | ||
285 | + } | ||
286 | + }); | ||
180 | 287 | ||
181 | - // TODO: augment stroke and fill functions | ||
182 | network.nodeRect = network.node.append('rect') | 288 | network.nodeRect = network.node.append('rect') |
183 | - // TODO: css for node rects | ||
184 | .attr('rx', 5) | 289 | .attr('rx', 5) |
185 | .attr('ry', 5) | 290 | .attr('ry', 5) |
186 | - .attr('stroke', function(d) { return '#000'}) | 291 | + .attr('width', 126) |
187 | - .attr('fill', function(d) { return '#ddf'}) | 292 | + .attr('height', 40); |
188 | - .attr('width', 60) | ||
189 | - .attr('height', 24); | ||
190 | 293 | ||
191 | network.node.each(function(d) { | 294 | network.node.each(function(d) { |
192 | var node = d3.select(this), | 295 | var node = d3.select(this), |
193 | - rect = node.select('rect'); | 296 | + rect = node.select('rect'), |
194 | - var text = node.append('text') | 297 | + img = node.append('svg:image') |
298 | + .attr('x', -16) | ||
299 | + .attr('y', -16) | ||
300 | + .attr('width', 32) | ||
301 | + .attr('height', 32) | ||
302 | + .attr('xlink:href', iconUrl(d)), | ||
303 | + text = node.append('text') | ||
195 | .text(d.id) | 304 | .text(d.id) |
196 | - .attr('dx', '1em') | 305 | + .attr('dy', '1.1em'), |
197 | - .attr('dy', '2.1em'); | 306 | + dummy; |
307 | + | ||
198 | }); | 308 | }); |
199 | 309 | ||
200 | // this function is scheduled to happen soon after the given thread ends | 310 | // this function is scheduled to happen soon after the given thread ends |
... | @@ -207,12 +317,64 @@ | ... | @@ -207,12 +317,64 @@ |
207 | first = true; | 317 | first = true; |
208 | 318 | ||
209 | // NOTE: probably unnecessary code if we only have one line. | 319 | // NOTE: probably unnecessary code if we only have one line. |
320 | + text.each(function() { | ||
321 | + var box = this.getBBox(); | ||
322 | + if (first || box.x < bounds.x1) { | ||
323 | + bounds.x1 = box.x; | ||
324 | + } | ||
325 | + if (first || box.y < bounds.y1) { | ||
326 | + bounds.y1 = box.y; | ||
327 | + } | ||
328 | + if (first || box.x + box.width < bounds.x2) { | ||
329 | + bounds.x2 = box.x + box.width; | ||
330 | + } | ||
331 | + if (first || box.y + box.height < bounds.y2) { | ||
332 | + bounds.y2 = box.y + box.height; | ||
333 | + } | ||
334 | + first = false; | ||
335 | + }).attr('text-anchor', 'middle'); | ||
336 | + | ||
337 | + var lab = config.labels, | ||
338 | + oldWidth = bounds.x2 - bounds.x1; | ||
339 | + | ||
340 | + bounds.x1 -= oldWidth / 2; | ||
341 | + bounds.x2 -= oldWidth / 2; | ||
342 | + | ||
343 | + bounds.x1 -= (lab.padLR + lab.imgPad); | ||
344 | + bounds.y1 -= lab.padTB; | ||
345 | + bounds.x2 += lab.padLR; | ||
346 | + bounds.y2 += lab.padTB; | ||
347 | + | ||
348 | + node.select('rect') | ||
349 | + .attr('x', bounds.x1) | ||
350 | + .attr('y', bounds.y1) | ||
351 | + .attr('width', bounds.x2 - bounds.x1) | ||
352 | + .attr('height', bounds.y2 - bounds.y1); | ||
353 | + | ||
354 | + node.select('image') | ||
355 | + .attr('x', bounds.x1); | ||
356 | + | ||
357 | + d.extent = { | ||
358 | + left: bounds.x1 - lab.marginLR, | ||
359 | + right: bounds.x2 + lab.marginLR, | ||
360 | + top: bounds.y1 - lab.marginTB, | ||
361 | + bottom: bounds.y2 + lab.marginTB | ||
362 | + }; | ||
363 | + | ||
364 | + d.edge = { | ||
365 | + left : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x1, bounds.y2), | ||
366 | + right : new geo.LineSegment(bounds.x2, bounds.y1, bounds.x2, bounds.y2), | ||
367 | + top : new geo.LineSegment(bounds.x1, bounds.y1, bounds.x2, bounds.y1), | ||
368 | + bottom : new geo.LineSegment(bounds.x1, bounds.y2, bounds.x2, bounds.y2) | ||
369 | + }; | ||
370 | + | ||
371 | + // ==== | ||
210 | }); | 372 | }); |
211 | 373 | ||
212 | network.numTicks = 0; | 374 | network.numTicks = 0; |
213 | network.preventCollisions = false; | 375 | network.preventCollisions = false; |
214 | network.force.start(); | 376 | network.force.start(); |
215 | - for (var i = 0; i < config.ticksWithoutCollisions; i++) { | 377 | + for (var i = 0; i < config.force.ticksWithoutCollisions; i++) { |
216 | network.force.tick(); | 378 | network.force.tick(); |
217 | } | 379 | } |
218 | network.preventCollisions = true; | 380 | network.preventCollisions = true; |
... | @@ -221,22 +383,78 @@ | ... | @@ -221,22 +383,78 @@ |
221 | 383 | ||
222 | } | 384 | } |
223 | 385 | ||
386 | + function iconUrl(d) { | ||
387 | + return config.iconUrl[d.type]; | ||
388 | + } | ||
389 | + | ||
224 | function translate(x, y) { | 390 | function translate(x, y) { |
225 | return 'translate(' + x + ',' + y + ')'; | 391 | return 'translate(' + x + ',' + y + ')'; |
226 | } | 392 | } |
227 | 393 | ||
394 | + function preventCollisions() { | ||
395 | + var quadtree = d3.geom.quadtree(network.nodes); | ||
396 | + | ||
397 | + network.nodes.forEach(function(n) { | ||
398 | + var nx1 = n.x + n.extent.left, | ||
399 | + nx2 = n.x + n.extent.right, | ||
400 | + ny1 = n.y + n.extent.top, | ||
401 | + ny2 = n.y + n.extent.bottom; | ||
402 | + | ||
403 | + quadtree.visit(function(quad, x1, y1, x2, y2) { | ||
404 | + if (quad.point && quad.point !== n) { | ||
405 | + // check if the rectangles intersect | ||
406 | + var p = quad.point, | ||
407 | + px1 = p.x + p.extent.left, | ||
408 | + px2 = p.x + p.extent.right, | ||
409 | + py1 = p.y + p.extent.top, | ||
410 | + py2 = p.y + p.extent.bottom, | ||
411 | + ix = (px1 <= nx2 && nx1 <= px2 && py1 <= ny2 && ny1 <= py2); | ||
412 | + if (ix) { | ||
413 | + var xa1 = nx2 - px1, // shift n left , p right | ||
414 | + xa2 = px2 - nx1, // shift n right, p left | ||
415 | + ya1 = ny2 - py1, // shift n up , p down | ||
416 | + ya2 = py2 - ny1, // shift n down , p up | ||
417 | + adj = Math.min(xa1, xa2, ya1, ya2); | ||
418 | + | ||
419 | + if (adj == xa1) { | ||
420 | + n.x -= adj / 2; | ||
421 | + p.x += adj / 2; | ||
422 | + } else if (adj == xa2) { | ||
423 | + n.x += adj / 2; | ||
424 | + p.x -= adj / 2; | ||
425 | + } else if (adj == ya1) { | ||
426 | + n.y -= adj / 2; | ||
427 | + p.y += adj / 2; | ||
428 | + } else if (adj == ya2) { | ||
429 | + n.y += adj / 2; | ||
430 | + p.y -= adj / 2; | ||
431 | + } | ||
432 | + } | ||
433 | + return ix; | ||
434 | + } | ||
435 | + }); | ||
436 | + | ||
437 | + }); | ||
438 | + } | ||
228 | 439 | ||
229 | function tick(e) { | 440 | function tick(e) { |
230 | network.numTicks++; | 441 | network.numTicks++; |
231 | 442 | ||
443 | + if (config.layering) { | ||
232 | // adjust the y-coord of each node, based on y-pos constraints | 444 | // adjust the y-coord of each node, based on y-pos constraints |
233 | -// network.nodes.forEach(function (n) { | 445 | + network.nodes.forEach(function (n) { |
234 | -// var z = e.alpha * n.constraint.weight; | 446 | + var z = e.alpha * n.constraint.weight; |
235 | -// if (!isNaN(n.constraint.y)) { | 447 | + if (!isNaN(n.constraint.y)) { |
236 | -// n.y = (n.constraint.y * z + n.y * (1 - z)); | 448 | + n.y = (n.constraint.y * z + n.y * (1 - z)); |
237 | -// } | 449 | + } |
238 | -// }); | 450 | + }); |
451 | + } | ||
452 | + | ||
453 | + if (network.preventCollisions) { | ||
454 | + preventCollisions(); | ||
455 | + } | ||
239 | 456 | ||
457 | + // TODO: use intersection technique for source end of link also | ||
240 | network.link | 458 | network.link |
241 | .attr('x1', function(d) { | 459 | .attr('x1', function(d) { |
242 | return d.source.x; | 460 | return d.source.x; |
... | @@ -244,11 +462,24 @@ | ... | @@ -244,11 +462,24 @@ |
244 | .attr('y1', function(d) { | 462 | .attr('y1', function(d) { |
245 | return d.source.y; | 463 | return d.source.y; |
246 | }) | 464 | }) |
247 | - .attr('x2', function(d) { | 465 | + .each(function(d) { |
248 | - return d.target.x; | 466 | + var x = d.target.x, |
249 | - }) | 467 | + y = d.target.y, |
250 | - .attr('y2', function(d) { | 468 | + line = new geo.LineSegment(d.source.x, d.source.y, x, y); |
251 | - return d.target.y; | 469 | + |
470 | + for (var e in d.target.edge) { | ||
471 | + var ix = line.intersect(d.target.edge[e].offset(x,y)); | ||
472 | + if (ix.in1 && ix.in2) { | ||
473 | + x = ix.x; | ||
474 | + y = ix.y; | ||
475 | + break; | ||
476 | + } | ||
477 | + } | ||
478 | + | ||
479 | + d3.select(this) | ||
480 | + .attr('x2', x) | ||
481 | + .attr('y2', y); | ||
482 | + | ||
252 | }); | 483 | }); |
253 | 484 | ||
254 | network.node | 485 | network.node | ... | ... |
... | @@ -7,50 +7,50 @@ | ... | @@ -7,50 +7,50 @@ |
7 | }, | 7 | }, |
8 | "nodes": [ | 8 | "nodes": [ |
9 | { | 9 | { |
10 | - "id": "switch-1", | 10 | + "id": "sample1", |
11 | "type": "opt", | 11 | "type": "opt", |
12 | "status": "good" | 12 | "status": "good" |
13 | }, | 13 | }, |
14 | { | 14 | { |
15 | - "id": "switch-2", | 15 | + "id": "00:00:00:00:00:00:00:02", |
16 | "type": "opt", | 16 | "type": "opt", |
17 | "status": "good" | 17 | "status": "good" |
18 | }, | 18 | }, |
19 | { | 19 | { |
20 | - "id": "switch-3", | 20 | + "id": "00:00:00:00:00:00:00:03", |
21 | "type": "opt", | 21 | "type": "opt", |
22 | "status": "good" | 22 | "status": "good" |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - "id": "switch-4", | 25 | + "id": "00:00:00:00:00:00:00:04", |
26 | "type": "opt", | 26 | "type": "opt", |
27 | "status": "good" | 27 | "status": "good" |
28 | }, | 28 | }, |
29 | { | 29 | { |
30 | - "id": "switch-11", | 30 | + "id": "00:00:00:00:00:00:00:11", |
31 | "type": "pkt", | 31 | "type": "pkt", |
32 | "status": "good" | 32 | "status": "good" |
33 | }, | 33 | }, |
34 | { | 34 | { |
35 | - "id": "switch-12", | 35 | + "id": "00:00:00:00:00:00:00:12", |
36 | "type": "pkt", | 36 | "type": "pkt", |
37 | "status": "good" | 37 | "status": "good" |
38 | }, | 38 | }, |
39 | { | 39 | { |
40 | - "id": "switch-13", | 40 | + "id": "00:00:00:00:00:00:00:13", |
41 | "type": "pkt", | 41 | "type": "pkt", |
42 | "status": "good" | 42 | "status": "good" |
43 | } | 43 | } |
44 | ], | 44 | ], |
45 | "links": [ | 45 | "links": [ |
46 | - { "src": "switch-1", "dst": "switch-2" }, | 46 | + { "src": "sample1", "dst": "00:00:00:00:00:00:00:02" }, |
47 | - { "src": "switch-1", "dst": "switch-3" }, | 47 | + { "src": "sample1", "dst": "00:00:00:00:00:00:00:03" }, |
48 | - { "src": "switch-1", "dst": "switch-4" }, | 48 | + { "src": "sample1", "dst": "00:00:00:00:00:00:00:04" }, |
49 | - { "src": "switch-2", "dst": "switch-3" }, | 49 | + { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:03" }, |
50 | - { "src": "switch-2", "dst": "switch-4" }, | 50 | + { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:04" }, |
51 | - { "src": "switch-3", "dst": "switch-4" }, | 51 | + { "src": "00:00:00:00:00:00:00:03", "dst": "00:00:00:00:00:00:00:04" }, |
52 | - { "src": "switch-13", "dst": "switch-3" }, | 52 | + { "src": "00:00:00:00:00:00:00:13", "dst": "00:00:00:00:00:00:00:03" }, |
53 | - { "src": "switch-12", "dst": "switch-2" }, | 53 | + { "src": "00:00:00:00:00:00:00:12", "dst": "00:00:00:00:00:00:00:02" }, |
54 | - { "src": "switch-11", "dst": "switch-1" } | 54 | + { "src": "00:00:00:00:00:00:00:11", "dst": "sample1" } |
55 | ] | 55 | ] |
56 | } | 56 | } | ... | ... |
... | @@ -13,7 +13,7 @@ body, html { | ... | @@ -13,7 +13,7 @@ body, html { |
13 | */ | 13 | */ |
14 | 14 | ||
15 | span.title { | 15 | span.title { |
16 | - color: red; | 16 | + color: darkblue; |
17 | font-size: 16pt; | 17 | font-size: 16pt; |
18 | font-style: italic; | 18 | font-style: italic; |
19 | } | 19 | } |
... | @@ -30,7 +30,7 @@ span.right { | ... | @@ -30,7 +30,7 @@ span.right { |
30 | * === DEBUGGING ====== | 30 | * === DEBUGGING ====== |
31 | */ | 31 | */ |
32 | svg { | 32 | svg { |
33 | - border: 1px dashed red; | 33 | + /*border: 1px dashed red;*/ |
34 | } | 34 | } |
35 | 35 | ||
36 | 36 | ||
... | @@ -64,36 +64,47 @@ marker#end { | ... | @@ -64,36 +64,47 @@ marker#end { |
64 | -moz-transition: opacity 250ms; | 64 | -moz-transition: opacity 250ms; |
65 | } | 65 | } |
66 | 66 | ||
67 | -.node text { | 67 | +/*differentiate between packet and optical nodes*/ |
68 | - fill: #000; | 68 | +svg .node.pkt rect { |
69 | + fill: #77a; | ||
70 | +} | ||
71 | + | ||
72 | +svg .node.opt rect { | ||
73 | + fill: #7a7; | ||
74 | +} | ||
75 | + | ||
76 | +svg .node text { | ||
77 | + fill: white; | ||
69 | font: 10px sans-serif; | 78 | font: 10px sans-serif; |
70 | pointer-events: none; | 79 | pointer-events: none; |
71 | } | 80 | } |
72 | 81 | ||
73 | -.node.selected rect { | 82 | +svg .node.selected rect { |
74 | filter: url(#blue-glow); | 83 | filter: url(#blue-glow); |
75 | } | 84 | } |
76 | 85 | ||
77 | -.link.inactive, | 86 | +svg .link.inactive, |
78 | -.node.inactive rect, | 87 | +svg .node.inactive rect, |
79 | -.node.inactive text { | 88 | +svg .node.inactive text, |
89 | +svg .node.inactive image { | ||
80 | opacity: .2; | 90 | opacity: .2; |
81 | } | 91 | } |
82 | 92 | ||
83 | -.node.inactive.selected rect, | 93 | +svg .node.inactive.selected rect, |
84 | -.node.inactive.selected text { | 94 | +svg .node.inactive.selected text, |
95 | +svg .node.inactive.selected image { | ||
85 | opacity: .6; | 96 | opacity: .6; |
86 | } | 97 | } |
87 | 98 | ||
88 | -.legend { | 99 | +svg .legend { |
89 | position: fixed; | 100 | position: fixed; |
90 | } | 101 | } |
91 | 102 | ||
92 | -.legend .category rect { | 103 | +svg .legend .category rect { |
93 | stroke-width: 1px; | 104 | stroke-width: 1px; |
94 | } | 105 | } |
95 | 106 | ||
96 | -.legend .category text { | 107 | +svg .legend .category text { |
97 | fill: #000; | 108 | fill: #000; |
98 | font: 10px sans-serif; | 109 | font: 10px sans-serif; |
99 | pointer-events: none; | 110 | pointer-events: none; |
... | @@ -110,15 +121,15 @@ marker#end { | ... | @@ -110,15 +121,15 @@ marker#end { |
110 | #frame { | 121 | #frame { |
111 | width: 100%; | 122 | width: 100%; |
112 | height: 100%; | 123 | height: 100%; |
113 | - background-color: #ffd; | 124 | + background-color: #cdf; |
114 | } | 125 | } |
115 | 126 | ||
116 | #mast { | 127 | #mast { |
117 | height: 32px; | 128 | height: 32px; |
118 | - background-color: #dda; | 129 | + background-color: #abe; |
119 | vertical-align: baseline; | 130 | vertical-align: baseline; |
120 | } | 131 | } |
121 | 132 | ||
122 | #main { | 133 | #main { |
123 | - background-color: #99b; | 134 | + background-color: #99c; |
124 | } | 135 | } | ... | ... |
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