blob: 011c3931fa2bcfcf936378f3ff4fdc32d292798e [file] [log] [blame]
Jimmy Yanda878fc2016-09-02 16:32:01 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.roadm;
17
18import com.google.common.collect.Range;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.net.ChannelSpacing;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Direction;
31import org.onosproject.net.OchSignal;
32import org.onosproject.net.OchSignalType;
33import org.onosproject.net.Port;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.behaviour.LambdaQuery;
36import org.onosproject.net.behaviour.PowerConfig;
37import org.onosproject.net.device.DeviceEvent;
38import org.onosproject.net.device.DeviceListener;
39import org.onosproject.net.device.DeviceService;
40import org.onosproject.net.flow.DefaultFlowRule;
41import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowEntry;
44import org.onosproject.net.flow.FlowId;
45import org.onosproject.net.flow.FlowRule;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flow.instructions.Instructions;
51import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54import java.util.Collections;
55import java.util.List;
56import java.util.Optional;
57import java.util.Set;
58import java.util.concurrent.TimeUnit;
59
60import static com.google.common.base.Preconditions.checkNotNull;
61
62/**
63 * Application for monitoring and configuring ROADM devices.
64 */
65@Component(immediate = true)
66@Service
67public class RoadmManager implements RoadmService {
68
69 private static final String APP_NAME = "org.onosproject.roadm";
70 private ApplicationId appId;
71
72 private final Logger log = LoggerFactory.getLogger(getClass());
73
74 private DeviceListener deviceListener = new InternalDeviceListener();
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected RoadmStore roadmStore;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected DeviceService deviceService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected FlowRuleService flowRuleService;
87
88 @Activate
89 protected void activate() {
90 appId = coreService.registerApplication(APP_NAME);
91 deviceService.addListener(deviceListener);
92 initDevices();
93
94 log.info("Started");
95 }
96
97 @Deactivate
98 protected void deactivate() {
99 deviceService.removeListener(deviceListener);
100
101 log.info("Stopped");
102 }
103
104 private PowerConfig<Object> getPowerConfig(DeviceId deviceId) {
105 Device device = deviceService.getDevice(deviceId);
106 if (device != null && device.is(PowerConfig.class)) {
107 return device.as(PowerConfig.class);
108 }
109 log.warn("Unable to load PowerConfig for {}", deviceId);
110 return null;
111 }
112
113 private LambdaQuery getLambdaQuery(DeviceId deviceId) {
114 Device device = deviceService.getDevice(deviceId);
115 if (device != null && device.is(LambdaQuery.class)) {
116 return device.as(LambdaQuery.class);
117 }
118 return null;
119 }
120
121 private void initDevices() {
122 for (Device device : deviceService.getDevices(Device.Type.ROADM)) {
123 initDevice(device.id());
124 setAllInitialTargetPortPowers(device.id());
125 }
126 }
127
128 // Initialize RoadmStore for a device to support target power
129 private void initDevice(DeviceId deviceId) {
130 if (!roadmStore.deviceAvailable(deviceId)) {
131 roadmStore.addDevice(deviceId);
132 }
133 log.info("Initialized device {}", deviceId);
134 }
135
136 // Sets the target port powers for a port on a device
137 // Attempts to read target powers from store. If no value is found then
138 // default value is used instead.
139 private void setInitialTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
140 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
141 if (powerConfig == null) {
142 log.warn("Unable to set default initial powers for port {} on device {}",
143 portNumber, deviceId);
144 return;
145 }
146
147 Optional<Range<Long>> range =
148 powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
149 if (!range.isPresent()) {
150 log.warn("No target power range found for port {} on device {}",
151 portNumber, deviceId);
152 return;
153 }
154
155 Long power = roadmStore.getTargetPower(deviceId, portNumber);
156 if (power == null) {
157 // Set default to middle of the range
158 power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
159 roadmStore.setTargetPower(deviceId, portNumber, power);
160 }
161 powerConfig.setTargetPower(portNumber, Direction.ALL, power);
162 }
163
164 // Sets the target port powers for each each port on a device
165 // Attempts to read target powers from store. If no value is found then
166 // default value is used instead
167 private void setAllInitialTargetPortPowers(DeviceId deviceId) {
168 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
169 if (powerConfig == null) {
170 log.warn("Unable to set default initial powers for device {}",
171 deviceId);
172 return;
173 }
174
175 List<Port> ports = deviceService.getPorts(deviceId);
176 for (Port port : ports) {
177 Optional<Range<Long>> range =
178 powerConfig.getTargetPowerRange(port.number(), Direction.ALL);
179 if (range.isPresent()) {
180 Long power = roadmStore.getTargetPower(deviceId, port.number());
181 if (power == null) {
182 // Set default to middle of the range
183 power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
184 roadmStore.setTargetPower(deviceId, port.number(), power);
185 }
186 powerConfig.setTargetPower(port.number(), Direction.ALL, power);
187 } else {
188 log.warn("No target power range found for port {} on device {}",
189 port.number(), deviceId);
190 }
191 }
192 }
193
194 @Override
195 public void setTargetPortPower(DeviceId deviceId, PortNumber portNumber, long power) {
196 checkNotNull(deviceId);
197 checkNotNull(portNumber);
198 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
199 if (powerConfig != null) {
200 roadmStore.setTargetPower(deviceId, portNumber, power);
201 powerConfig.setTargetPower(portNumber, Direction.ALL, power);
202 } else {
203 log.warn("Unable to set target port power for device {}", deviceId);
204 }
205 }
206
207 @Override
208 public Long getTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
209 checkNotNull(deviceId);
210 checkNotNull(portNumber);
211 // getTargetPortPower is not yet implemented in PowerConfig so we
212 // access store instead
213 return roadmStore.getTargetPower(deviceId, portNumber);
214 }
215
216 @Override
217 public void setAttenuation(DeviceId deviceId, PortNumber portNumber,
218 OchSignal ochSignal, long attenuation) {
219 checkNotNull(deviceId);
220 checkNotNull(portNumber);
221 checkNotNull(ochSignal);
222 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
223 if (powerConfig != null) {
224 powerConfig.setTargetPower(portNumber, ochSignal, attenuation);
225 } else {
226 log.warn("Cannot set attenuation for channel index {} on device {}",
227 ochSignal.spacingMultiplier(), deviceId);
228 }
229 }
230
231 @Override
232 public Long getAttenuation(DeviceId deviceId, PortNumber portNumber,
233 OchSignal ochSignal) {
234 checkNotNull(deviceId);
235 checkNotNull(portNumber);
236 checkNotNull(ochSignal);
237 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
238 if (powerConfig != null) {
239 Optional<Long> attenuation =
240 powerConfig.getTargetPower(portNumber, ochSignal);
241 if (attenuation.isPresent()) {
242 return attenuation.get();
243 }
244 }
245 return null;
246 }
247
248 @Override
249 public Long getCurrentPortPower(DeviceId deviceId, PortNumber portNumber) {
250 checkNotNull(deviceId);
251 checkNotNull(portNumber);
252 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
253 if (powerConfig != null) {
254 Optional<Long> currentPower =
255 powerConfig.currentPower(portNumber, Direction.ALL);
256 if (currentPower.isPresent()) {
257 return currentPower.get();
258 }
259 }
260 return null;
261 }
262
263 @Override
264 public Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber,
265 OchSignal ochSignal) {
266 checkNotNull(deviceId);
267 checkNotNull(portNumber);
268 checkNotNull(ochSignal);
269 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
270 if (powerConfig != null) {
271 Optional<Long> currentPower =
272 powerConfig.currentPower(portNumber, ochSignal);
273 if (currentPower.isPresent()) {
274 return currentPower.get();
275 }
276 }
277 return null;
278 }
279
280 @Override
281 public Set<OchSignal> queryLambdas(DeviceId deviceId, PortNumber portNumber) {
282 checkNotNull(deviceId);
283 checkNotNull(portNumber);
284 LambdaQuery lambdaQuery = getLambdaQuery(deviceId);
285 if (lambdaQuery != null) {
286 return lambdaQuery.queryLambdas(portNumber);
287 }
288 return Collections.emptySet();
289 }
290
291 @Override
292 public FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
293 int timeout, PortNumber inPort, PortNumber outPort,
294 OchSignal ochSignal) {
295 checkNotNull(deviceId);
296 checkNotNull(inPort);
297 checkNotNull(outPort);
298
299 FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder();
300 flowBuilder.fromApp(appId);
301 flowBuilder.withPriority(priority);
302 if (isPermanent) {
303 flowBuilder.makePermanent();
304 } else {
305 flowBuilder.makeTemporary(timeout);
306 }
307 flowBuilder.forDevice(deviceId);
308
309 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
310 selectorBuilder.add(Criteria.matchInPort(inPort));
311 selectorBuilder.add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID));
312 selectorBuilder.add(Criteria.matchLambda(ochSignal));
313 flowBuilder.withSelector(selectorBuilder.build());
314
315 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
316 treatmentBuilder.add(Instructions.createOutput(outPort));
317 flowBuilder.withTreatment(treatmentBuilder.build());
318
319 FlowRule flowRule = flowBuilder.build();
320 flowRuleService.applyFlowRules(flowRule);
321
322 log.info("Created connection from input port {} to output port {}",
323 inPort.toLong(), outPort.toLong());
324
325 return flowRule.id();
326 }
327
328 @Override
329 public FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
330 int timeout, PortNumber inPort, PortNumber outPort,
331 OchSignal ochSignal, long attenuation) {
332 checkNotNull(deviceId);
333 checkNotNull(inPort);
334 checkNotNull(outPort);
335 FlowId flowId = createConnection(deviceId, priority, isPermanent,
336 timeout, inPort, outPort, ochSignal);
337 delayedSetAttenuation(deviceId, outPort, ochSignal, attenuation);
338 return flowId;
339 }
340
341 // Delay the call to setTargetPower because the flow may not be in the store yet
342 private void delayedSetAttenuation(DeviceId deviceId, PortNumber outPort,
343 OchSignal ochSignal, long attenuation) {
344 Runnable setAtt = () -> {
345 try {
346 TimeUnit.SECONDS.sleep(1);
347 } catch (InterruptedException e) {
348 log.warn("Thread interrupted. Setting attenuation early.");
349 }
350 setAttenuation(deviceId, outPort, ochSignal, attenuation);
351 };
352 new Thread(setAtt).start();
353 }
354
355 @Override
356 public void removeConnection(DeviceId deviceId, FlowId flowId) {
357 checkNotNull(deviceId);
358 checkNotNull(flowId);
359 for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
360 if (entry.id().equals(flowId)) {
361 flowRuleService.removeFlowRules(entry);
362 log.info("Deleted connection {}", entry.id());
363 break;
364 }
365 }
366 }
367
368 @Override
369 public boolean hasPortTargetPower(DeviceId deviceId, PortNumber portNumber) {
370 checkNotNull(deviceId);
371 checkNotNull(portNumber);
372 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
373 if (powerConfig != null) {
374 Optional<Range<Long>> range =
375 powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
376 return range.isPresent();
377 }
378 return false;
379 }
380
381 @Override
382 public boolean portTargetPowerInRange(DeviceId deviceId, PortNumber portNumber,
383 long power) {
384 checkNotNull(deviceId);
385 checkNotNull(portNumber);
386 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
387 if (powerConfig != null) {
388 Optional<Range<Long>> range =
389 powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
390 return range.isPresent() && range.get().contains(power);
391 }
392 return false;
393 }
394
395 @Override
396 public boolean attenuationInRange(DeviceId deviceId, PortNumber outPort,
397 long att) {
398 checkNotNull(deviceId);
399 checkNotNull(outPort);
400 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
401 if (powerConfig != null) {
402 OchSignal stubOch = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 0);
403 Optional<Range<Long>> range =
404 powerConfig.getTargetPowerRange(outPort, stubOch);
405 return range.isPresent() && range.get().contains(att);
406 }
407 return false;
408 }
409
410 @Override
411 public boolean validInputPort(DeviceId deviceId, PortNumber portNumber) {
412 checkNotNull(deviceId);
413 checkNotNull(portNumber);
414 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
415 if (powerConfig != null) {
416 Optional<Range<Long>> range =
417 powerConfig.getInputPowerRange(portNumber, Direction.ALL);
418 return range.isPresent();
419 }
420 return false;
421 }
422
423 @Override
424 public boolean validOutputPort(DeviceId deviceId, PortNumber portNumber) {
425 return hasPortTargetPower(deviceId, portNumber);
426 }
427
428 @Override
429 public boolean validChannel(DeviceId deviceId, PortNumber portNumber,
430 OchSignal ochSignal) {
431 checkNotNull(deviceId);
432 checkNotNull(portNumber);
433 LambdaQuery lambdaQuery = getLambdaQuery(deviceId);
434 if (lambdaQuery != null) {
435 Set<OchSignal> channels = lambdaQuery.queryLambdas(portNumber);
436 return channels.contains(ochSignal);
437 }
438 return false;
439 }
440
441 @Override
442 public boolean channelAvailable(DeviceId deviceId, OchSignal ochSignal) {
443 checkNotNull(deviceId);
444 checkNotNull(ochSignal);
445 for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
446 if (ChannelData.fromFlow(entry).ochSignal().equals(ochSignal)) {
447 return false;
448 }
449 }
450 return true;
451 }
452
453 @Override
454 public boolean validConnection(DeviceId deviceId, PortNumber inPort,
455 PortNumber outPort) {
456 checkNotNull(deviceId);
457 checkNotNull(inPort);
458 checkNotNull(outPort);
459 return validInputPort(deviceId, inPort) && validOutputPort(deviceId, outPort);
460 }
461
462 @Override
463 public Range<Long> targetPortPowerRange(DeviceId deviceId, PortNumber portNumber) {
464 checkNotNull(deviceId);
465 checkNotNull(portNumber);
466 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
467 if (powerConfig != null) {
468 Optional<Range<Long>> range =
469 powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
470 if (range.isPresent()) {
471 return range.get();
472 }
473 }
474 return null;
475 }
476
477 @Override
478 public Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber,
479 OchSignal ochSignal) {
480 checkNotNull(deviceId);
481 checkNotNull(portNumber);
482 checkNotNull(ochSignal);
483 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
484 if (powerConfig != null) {
485 Optional<Range<Long>> range =
486 powerConfig.getTargetPowerRange(portNumber, ochSignal);
487 if (range.isPresent()) {
488 return range.get();
489 }
490 }
491 return null;
492 }
493
494 @Override
495 public Range<Long> inputPortPowerRange(DeviceId deviceId, PortNumber portNumber) {
496 checkNotNull(deviceId);
497 checkNotNull(portNumber);
498 PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
499 if (powerConfig != null) {
500 Optional<Range<Long>> range =
501 powerConfig.getInputPowerRange(portNumber, Direction.ALL);
502 if (range.isPresent()) {
503 return range.get();
504 }
505 }
506 return null;
507 }
508
509 // Listens to device events.
510 private class InternalDeviceListener implements DeviceListener {
511 @Override
512 public void event(DeviceEvent deviceEvent) {
513 Device device = deviceEvent.subject();
514
515 switch (deviceEvent.type()) {
516 case DEVICE_ADDED:
517 case DEVICE_UPDATED:
518 initDevice(device.id());
519 break;
520 case PORT_ADDED:
521 case PORT_UPDATED:
522 setInitialTargetPortPower(device.id(), deviceEvent.port().number());
523 break;
524 default:
525 break;
526
527 }
528 }
529 }
530}