blob: dba2f6fc371fb577309792fff009fca7cde3856e [file] [log] [blame]
MaoLu819fde22017-04-20 17:17:49 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016 Open Networking Foundation
MaoLu819fde22017-04-20 17:17:49 -07003 *
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 */
16
17package org.onosproject.drivers.oplink;
18
19import com.google.common.collect.Range;
20import org.apache.commons.configuration.HierarchicalConfiguration;
21import org.onosproject.driver.extensions.OplinkAttenuation;
MaoLue00b7422017-05-11 20:27:35 -070022import org.onosproject.net.Device;
MaoLu819fde22017-04-20 17:17:49 -070023import org.onosproject.net.Direction;
24import org.onosproject.net.OchSignal;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.behaviour.PowerConfig;
MaoLue00b7422017-05-11 20:27:35 -070027import org.onosproject.net.device.DeviceService;
MaoLu819fde22017-04-20 17:17:49 -070028import org.onosproject.net.driver.AbstractHandlerBehaviour;
29import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.FlowEntry;
32import org.onosproject.net.flow.FlowRuleService;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.slf4j.Logger;
35
36import java.util.Optional;
37
MaoLu819fde22017-04-20 17:17:49 -070038import static org.onosproject.drivers.oplink.OplinkOpticalUtility.POWER_MULTIPLIER;
MaoLue00b7422017-05-11 20:27:35 -070039import static org.onosproject.drivers.oplink.OplinkOpticalUtility.RANGE_ATT;
40import static org.onosproject.drivers.oplink.OplinkOpticalUtility.RANGE_GENERAL;
MaoLu819fde22017-04-20 17:17:49 -070041import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
MaoLue00b7422017-05-11 20:27:35 -070042import static org.slf4j.LoggerFactory.getLogger;
MaoLu819fde22017-04-20 17:17:49 -070043
44/**
45 * Get current or target port/channel power from an Oplink optical netconf device.
46 * Set target port power or channel attenuation to an optical netconf device.
47 */
48public class OplinkOpticalPowerConfig<T> extends AbstractHandlerBehaviour
49 implements PowerConfig<T> {
50
51 // key
52 public static final String KEY_CHNUM = "wavelength-number";
53 public static final String KEY_CHPWR = "wavelength-power";
54 public static final String KEY_CHSTATS = "wavelength-stats";
55 public static final String KEY_OCMSTATS = "ocm-stats";
56 public static final String KEY_PORTDIRECT_RX = "rx";
57 public static final String KEY_PORTDIRECT_TX = "tx";
58 public static final String KEY_PORTTARPWR = "port-target-power";
59 public static final String KEY_PORTCURPWR = "port-current-power";
60 public static final String KEY_PORTPROPERTY = "port-property";
61 public static final String KEY_PORTPWRCAPMINRX = "port-power-capability-min-rx";
62 public static final String KEY_PORTPWRCAPMAXRX = "port-power-capability-max-rx";
63 public static final String KEY_PORTPWRCAPMINTX = "port-power-capability-min-tx";
64 public static final String KEY_PORTPWRCAPMAXTX = "port-power-capability-max-tx";
65 public static final String KEY_PORTS_PORT = String.format("%s.%s", KEY_DATA_PORTS, KEY_PORT);
66 public static final String KEY_PORTS_PORT_PROPERTY = String.format("%s.%s", KEY_PORTS_PORT, KEY_PORTPROPERTY);
wei wangd271fbc2017-12-04 15:36:31 -080067 public static final String KEY_OCMS = "ocms";
MaoLu819fde22017-04-20 17:17:49 -070068 // log
69 private static final Logger log = getLogger(OplinkOpticalPowerConfig.class);
70
MaoLu819fde22017-04-20 17:17:49 -070071 @Override
Andrea Campanelladadf6402019-08-07 15:24:11 +020072 public Optional<Double> getTargetPower(PortNumber port, T component) {
73 Long power = acquireTargetPower(port, component);
74 if (power == null) {
75 return Optional.empty();
76 }
77 return Optional.of(power.doubleValue());
MaoLu819fde22017-04-20 17:17:49 -070078 }
79
80 @Override
Andrea Campanelladadf6402019-08-07 15:24:11 +020081 public void setTargetPower(PortNumber port, T component, double power) {
MaoLu819fde22017-04-20 17:17:49 -070082 if (component instanceof OchSignal) {
Andrea Campanelladadf6402019-08-07 15:24:11 +020083 setChannelTargetPower(port, (OchSignal) component, (long) power);
MaoLu819fde22017-04-20 17:17:49 -070084 } else {
Andrea Campanelladadf6402019-08-07 15:24:11 +020085 setPortTargetPower(port, (long) power);
MaoLu819fde22017-04-20 17:17:49 -070086 }
87 }
88
89 @Override
Andrea Campanelladadf6402019-08-07 15:24:11 +020090 public Optional<Double> currentPower(PortNumber port, T component) {
91 Long power = acquireCurrentPower(port, component);
92 if (power == null) {
93 return Optional.empty();
94 }
95 return Optional.of(power.doubleValue());
MaoLu819fde22017-04-20 17:17:49 -070096 }
97
98 @Override
Andrea Campanelladadf6402019-08-07 15:24:11 +020099 public Optional<Range<Double>> getTargetPowerRange(PortNumber port, T component) {
100 Range<Long> power = getTxPowerRange(port, component);
101 if (power == null) {
102 return Optional.empty();
103 }
104 return Optional.of(Range.closed((double) power.lowerEndpoint(), (double) power.upperEndpoint()));
MaoLu819fde22017-04-20 17:17:49 -0700105 }
106
107 @Override
Andrea Campanelladadf6402019-08-07 15:24:11 +0200108 public Optional<Range<Double>> getInputPowerRange(PortNumber port, T component) {
109 Range<Long> power = getRxPowerRange(port, component);
110 if (power == null) {
111 return Optional.empty();
112 }
113 return Optional.of(Range.closed((double) power.lowerEndpoint(), (double) power.upperEndpoint()));
MaoLu819fde22017-04-20 17:17:49 -0700114 }
115
116 private String getPortPowerFilter(PortNumber port, String selection) {
117 return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
118 .append(xmlOpen(KEY_PORTS))
Andrea Campanelladadf6402019-08-07 15:24:11 +0200119 .append(xml(KEY_PORTID, Double.toString(port.toLong())))
MaoLu819fde22017-04-20 17:17:49 -0700120 .append(xmlOpen(KEY_PORT))
121 .append(xmlEmpty(selection))
122 .append(xmlClose(KEY_PORT))
123 .append(xmlClose(KEY_PORTS))
124 .append(xmlClose(KEY_OPENOPTICALDEV))
125 .toString();
126 }
127
128 private String getChannelPowerFilter(PortNumber port, OchSignal channel) {
129 return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
wei wangd271fbc2017-12-04 15:36:31 -0800130 .append(xmlOpen(KEY_OCMS))
Andrea Campanelladadf6402019-08-07 15:24:11 +0200131 .append(xml(KEY_PORTID, Double.toString(port.toLong())))
MaoLu819fde22017-04-20 17:17:49 -0700132 .append(xmlOpen(KEY_OCMSTATS))
133 .append(xml(KEY_CHNUM, Integer.toString(channel.spacingMultiplier())))
134 .append(xmlEmpty(KEY_CHSTATS))
135 .append(xmlClose(KEY_OCMSTATS))
wei wangd271fbc2017-12-04 15:36:31 -0800136 .append(xmlClose(KEY_OCMS))
MaoLu819fde22017-04-20 17:17:49 -0700137 .append(xmlClose(KEY_OPENOPTICALDEV))
138 .toString();
139 }
140
141 private String getChannelAttenuationFilter(PortNumber port, OchSignal channel) {
142 return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
143 .append(xmlOpen(KEY_CONNS))
144 .append(xml(KEY_CONNID, Integer.toString(channel.spacingMultiplier())))
145 .append(xmlEmpty(KEY_CHATT))
146 .append(xmlClose(KEY_CONNS))
147 .append(xmlClose(KEY_OPENOPTICALDEV))
148 .toString();
149 }
150
151 private String getPowerRangeFilter(PortNumber port, String direction) {
152 return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
153 .append(xmlOpen(KEY_PORTS))
Andrea Campanelladadf6402019-08-07 15:24:11 +0200154 .append(xml(KEY_PORTID, Double.toString(port.toLong())))
MaoLu819fde22017-04-20 17:17:49 -0700155 .append(xmlOpen(KEY_PORT))
156 .append(xml(KEY_PORTDIRECT, direction))
157 .append(xmlEmpty(KEY_PORTPROPERTY))
158 .append(xmlClose(KEY_PORT))
159 .append(xmlClose(KEY_PORTS))
160 .append(xmlClose(KEY_OPENOPTICALDEV))
161 .toString();
162 }
163
164 private Long acquireTargetPower(PortNumber port, T component) {
165 if (component instanceof OchSignal) {
166 return acquireChannelAttenuation(port, (OchSignal) component);
167 }
168 log.debug("Get port{} target power...", port);
169 return acquirePortPower(port, KEY_PORTTARPWR);
170 }
171
172 private Long acquireCurrentPower(PortNumber port, T component) {
173 if (component instanceof OchSignal) {
174 return acquireChannelPower(port, (OchSignal) component);
175 }
176 log.debug("Get port{} current power...", port);
177 return acquirePortPower(port, KEY_PORTCURPWR);
178 }
179
180 private Long acquirePortPower(PortNumber port, String selection) {
MaoLue00b7422017-05-11 20:27:35 -0700181 String reply = netconfGet(handler(), getPortPowerFilter(port, selection));
MaoLu819fde22017-04-20 17:17:49 -0700182 HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT);
183 if (info == null) {
184 return null;
185 }
186 return (long) (info.getDouble(selection) * POWER_MULTIPLIER);
187 }
188
189 private Long acquireChannelAttenuation(PortNumber port, OchSignal channel) {
190 log.debug("Get port{} channel{} attenuation...", port, channel.channelSpacing());
MaoLue00b7422017-05-11 20:27:35 -0700191 String reply = netconfGet(handler(), getChannelAttenuationFilter(port, channel));
MaoLu819fde22017-04-20 17:17:49 -0700192 HierarchicalConfiguration info = configAt(reply, KEY_CONNS);
193 if (info == null) {
194 return null;
195 }
196 return (long) (info.getDouble(KEY_CHATT) * POWER_MULTIPLIER);
197 }
198
199 private Long acquireChannelPower(PortNumber port, OchSignal channel) {
200 log.debug("Get port{} channel{} power...", port, channel.channelSpacing());
MaoLue00b7422017-05-11 20:27:35 -0700201 String reply = netconfGet(handler(), getChannelPowerFilter(port, channel));
MaoLu819fde22017-04-20 17:17:49 -0700202 HierarchicalConfiguration info = configAt(reply, KEY_DATA_CONNS);
203 if (info == null) {
204 return null;
205 }
206 return (long) (info.getDouble(KEY_CHPWR) * POWER_MULTIPLIER);
207 }
208
209 private boolean setPortTargetPower(PortNumber port, long power) {
210 log.debug("Set port{} target power...", port);
211 String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
212 .append(xmlOpen(KEY_PORTS))
Andrea Campanelladadf6402019-08-07 15:24:11 +0200213 .append(xml(KEY_PORTID, Double.toString(port.toLong())))
MaoLu819fde22017-04-20 17:17:49 -0700214 .append(xmlOpen(KEY_PORT))
Andrea Campanelladadf6402019-08-07 15:24:11 +0200215 .append(xml(KEY_PORTTARPWR, Double.toString(power)))
MaoLu819fde22017-04-20 17:17:49 -0700216 .append(xmlClose(KEY_PORT))
217 .append(xmlClose(KEY_PORTS))
218 .append(xmlClose(KEY_OPENOPTICALDEV))
219 .toString();
220 return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
221 }
222
223 private boolean setChannelTargetPower(PortNumber port, OchSignal channel, long power) {
224 log.debug("Set port{} channel{} attenuation.", port, channel.channelSpacing());
225 FlowRuleService service = handler().get(FlowRuleService.class);
226 Iterable<FlowEntry> entries = service.getFlowEntries(data().deviceId());
227 for (FlowEntry entry : entries) {
Andrea Campanelladadf6402019-08-07 15:24:11 +0200228 OplinkCrossConnect crossConnect = OplinkOpticalUtility.fromFlowRule(this, entry);
MaoLu819fde22017-04-20 17:17:49 -0700229 // The channel port might be input port or output port.
230 if ((port.equals(crossConnect.getInPort()) || port.equals(crossConnect.getOutPort())) &&
231 channel.spacingMultiplier() == crossConnect.getChannel()) {
232 log.debug("Flow is found, modify the flow with attenuation.");
233 // Modify attenuation in treatment
234 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
235 .setOutput(crossConnect.getOutPort())
236 .extension(new OplinkAttenuation((int) power), data().deviceId())
237 .build();
238 // Apply the new flow rule
239 service.applyFlowRules(DefaultFlowRule.builder()
240 .forDevice(data().deviceId())
241 .makePermanent()
242 .withSelector(entry.selector())
243 .withTreatment(treatment)
244 .withPriority(entry.priority())
245 .withCookie(entry.id().value())
246 .build());
247 return true;
248 }
249 }
250 return false;
251 }
252
253 private Range<Long> getPowerRange(PortNumber port, String directionKey, String minKey, String maxKey) {
MaoLue00b7422017-05-11 20:27:35 -0700254 // TODO
255 // Optical protection switch does not support power range configuration, it'll reply error.
256 // To prevent replying error log flooding from netconf session when polling all ports information,
257 // use general power range of [-60, 60] instead.
258 if (handler().get(DeviceService.class).getDevice(data().deviceId()).type()
259 == Device.Type.FIBER_SWITCH) {
260 return RANGE_GENERAL;
261 }
262 String reply = netconfGet(handler(), getPowerRangeFilter(port, directionKey));
MaoLu819fde22017-04-20 17:17:49 -0700263 HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT_PROPERTY);
264 if (info == null) {
265 return null;
266 }
267 long minPower = (long) (info.getDouble(minKey) * POWER_MULTIPLIER);
268 long maxPower = (long) (info.getDouble(maxKey) * POWER_MULTIPLIER);
269 return Range.closed(minPower, maxPower);
270 }
271
272 private Range<Long> getTxPowerRange(PortNumber port, T component) {
273 if (component instanceof Direction) {
274 log.debug("Get target port{} power range...", port);
275 return getPowerRange(port, KEY_PORTDIRECT_TX, KEY_PORTPWRCAPMINTX, KEY_PORTPWRCAPMAXTX);
276 } else {
277 log.debug("Get channel attenuation range...");
MaoLue00b7422017-05-11 20:27:35 -0700278 return RANGE_ATT;
MaoLu819fde22017-04-20 17:17:49 -0700279 }
280 }
281
282 private Range<Long> getRxPowerRange(PortNumber port, T component) {
283 log.debug("Get input port{} power range...", port);
284 return getPowerRange(port, KEY_PORTDIRECT_RX, KEY_PORTPWRCAPMINRX, KEY_PORTPWRCAPMAXRX);
285 }
286}