blob: cb9d95a134f0335af2d4e1ef4945ecc54cb36d08 [file] [log] [blame]
adibrastegarnia025da382019-07-24 12:18:18 -07001/*
2 * Copyright 2019-present Open Networking Foundation
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 */
16
17
adibrastegarnia025da382019-07-24 12:18:18 -070018package org.onosproject.net.optical.cli;
19
20import com.google.common.collect.ImmutableMap;
21import org.apache.karaf.shell.api.action.Argument;
22import org.apache.karaf.shell.api.action.Command;
23import org.apache.karaf.shell.api.action.Completion;
24import org.apache.karaf.shell.api.action.lifecycle.Service;
25import org.onosproject.cli.AbstractShellCommand;
alessiob2f5b412021-09-23 18:36:37 +020026import org.onosproject.cli.PlaceholderCompleter;
Andrea Campanella3a361452019-08-02 10:17:53 +020027import org.onosproject.cli.net.NetconfOperationCompleter;
adibrastegarnia025da382019-07-24 12:18:18 -070028import org.onosproject.cli.net.OpticalConnectPointCompleter;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
adibrastegarnia025da382019-07-24 12:18:18 -070031import org.onosproject.net.device.DeviceService;
Andrea Campanella3a361452019-08-02 10:17:53 +020032import org.onosproject.net.flow.DefaultFlowRule;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.FlowRuleService;
adibrastegarnia025da382019-07-24 12:18:18 -070037import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
alessiob2f5b412021-09-23 18:36:37 +020039import org.onosproject.net.flow.criteria.Criteria;
adibrastegarnia025da382019-07-24 12:18:18 -070040import org.onosproject.net.flow.instructions.Instructions;
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -070041import org.onosproject.net.optical.OchPort;
42import org.onosproject.net.optical.device.OchPortHelper;
alessiob2f5b412021-09-23 18:36:37 +020043import org.onosproject.net.ChannelSpacing;
44import org.onosproject.net.OchSignal;
45import org.onosproject.net.GridType;
46import org.onosproject.net.Port;
47import org.onosproject.net.ConnectPoint;
48import org.onosproject.net.Device;
49import org.onosproject.net.OchSignalType;
adibrastegarnia025da382019-07-24 12:18:18 -070050
51import java.util.Map;
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -070052import java.util.Optional;
adibrastegarnia025da382019-07-24 12:18:18 -070053
54import static com.google.common.base.Preconditions.checkArgument;
55import static com.google.common.base.Preconditions.checkNotNull;
56
57/**
58 * Enable the optical channel and tune the wavelength via a flow rule based on a given Signal.
59 */
60@Service
Andrea Campanella3a361452019-08-02 10:17:53 +020061@Command(scope = "onos", name = "wavelength-config",
alessiob2f5b412021-09-23 18:36:37 +020062 description = "Send a flow rule to TERMINAL_DEVICE or ROADM devices")
adibrastegarnia025da382019-07-24 12:18:18 -070063public class PortWaveLengthCommand extends AbstractShellCommand {
64
65 private static final String SIGNAL_FORMAT = "slotGranularity/channelSpacing(in GHz e.g 6.25,12.5,25,50,100)/" +
alessiob2f5b412021-09-23 18:36:37 +020066 "spaceMultiplier/gridType(cwdm, flex, dwdm) " + "e.g 4/50/12/dwdm";
adibrastegarnia025da382019-07-24 12:18:18 -070067
68 private static final String CH_6P25 = "6.25";
69 private static final String CH_12P5 = "12.5";
70 private static final String CH_25 = "25";
71 private static final String CH_50 = "50";
72 private static final String CH_100 = "100";
Andrea Campanella6ac13ff2019-08-29 16:51:14 -070073 private static final long BASE_FREQUENCY = 193100000; //Working in Mhz
adibrastegarnia025da382019-07-24 12:18:18 -070074
75 private static final Map<String, ChannelSpacing> CHANNEL_SPACING_MAP = ImmutableMap
76 .<String, ChannelSpacing>builder()
77 .put(CH_6P25, ChannelSpacing.CHL_6P25GHZ)
78 .put(CH_12P5, ChannelSpacing.CHL_12P5GHZ)
79 .put(CH_25, ChannelSpacing.CHL_25GHZ)
80 .put(CH_50, ChannelSpacing.CHL_50GHZ)
81 .put(CH_100, ChannelSpacing.CHL_100GHZ)
82 .build();
alessiob2f5b412021-09-23 18:36:37 +020083
84 @Argument(index = 0, name = "operation",
85 description = "Netconf operation (supported: edit-config and delete-config)",
Andrea Campanella3a361452019-08-02 10:17:53 +020086 required = true, multiValued = false)
87 @Completion(NetconfOperationCompleter.class)
88 private String operation = null;
adibrastegarnia025da382019-07-24 12:18:18 -070089
alessiob2f5b412021-09-23 18:36:37 +020090 @Argument(index = 1, name = "input connectPoint",
91 description = "Input connectPoint (format device/port)",
adibrastegarnia025da382019-07-24 12:18:18 -070092 required = true, multiValued = false)
93 @Completion(OpticalConnectPointCompleter.class)
alessiob2f5b412021-09-23 18:36:37 +020094 private String inConnectPointString = null;
adibrastegarnia025da382019-07-24 12:18:18 -070095
alessiob2f5b412021-09-23 18:36:37 +020096 @Argument(index = 2, name = "OchSignal",
97 description = "Optical Signal or wavelength. Provide wavelength in MHz, or Och Format = "
98 + SIGNAL_FORMAT, required = true, multiValued = false)
99 @Completion(PlaceholderCompleter.class)
100 private String parameter = null;
adibrastegarnia025da382019-07-24 12:18:18 -0700101
alessiob2f5b412021-09-23 18:36:37 +0200102 @Argument(index = 3, name = "output connectPoint",
103 description = "Output connectPoint, required for ROADM devices",
104 required = false, multiValued = false)
105 @Completion(OpticalConnectPointCompleter.class)
106 private String outConnectPointString = null;
adibrastegarnia025da382019-07-24 12:18:18 -0700107
108 private OchSignal createOchSignal() throws IllegalArgumentException {
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700109 if (parameter == null) {
adibrastegarnia025da382019-07-24 12:18:18 -0700110 return null;
111 }
112 try {
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700113 String[] splitted = parameter.split("/");
adibrastegarnia025da382019-07-24 12:18:18 -0700114 checkArgument(splitted.length == 4,
115 "signal requires 4 parameters: " + SIGNAL_FORMAT);
116 int slotGranularity = Integer.parseInt(splitted[0]);
117 String chSpacing = splitted[1];
118 ChannelSpacing channelSpacing = checkNotNull(CHANNEL_SPACING_MAP.get(chSpacing),
119 String.format("invalid channel spacing: %s", chSpacing));
120 int multiplier = Integer.parseInt(splitted[2]);
121 String gdType = splitted[3].toUpperCase();
122 GridType gridType = GridType.valueOf(gdType);
123 return new OchSignal(gridType, channelSpacing, multiplier, slotGranularity);
124 } catch (RuntimeException e) {
125 /* catching RuntimeException as both NullPointerException (thrown by
126 * checkNotNull) and IllegalArgumentException (thrown by checkArgument)
127 * are subclasses of RuntimeException.
128 */
129 String msg = String.format("Invalid signal format: %s, expected format is %s.",
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700130 parameter, SIGNAL_FORMAT);
adibrastegarnia025da382019-07-24 12:18:18 -0700131 print(msg);
132 throw new IllegalArgumentException(msg, e);
133 }
134 }
135
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700136 private OchSignal createOchSignalFromWavelength(DeviceService deviceService, ConnectPoint cp) {
137 long wavelength = Long.parseLong(parameter);
138 if (wavelength == 0L) {
139 return null;
140 }
141 Port port = deviceService.getPort(cp);
142 Optional<OchPort> ochPortOpt = OchPortHelper.asOchPort(port);
143
144 if (ochPortOpt.isPresent()) {
145 OchPort ochPort = ochPortOpt.get();
146 GridType gridType = ochPort.lambda().gridType();
147 ChannelSpacing channelSpacing = ochPort.lambda().channelSpacing();
148 int slotGranularity = ochPort.lambda().slotGranularity();
Andrea Campanella6ac13ff2019-08-29 16:51:14 -0700149 int multiplier = getMultplier(wavelength, gridType, channelSpacing);
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700150 return new OchSignal(gridType, channelSpacing, multiplier, slotGranularity);
151 } else {
alessiob2f5b412021-09-23 18:36:37 +0200152 print("[ERROR] connect point %s is not OChPort", cp);
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700153 return null;
154 }
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700155 }
156
Andrea Campanella6ac13ff2019-08-29 16:51:14 -0700157 private int getMultplier(long wavelength, GridType gridType, ChannelSpacing channelSpacing) {
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700158 long baseFreq;
159 switch (gridType) {
160 case DWDM:
161 baseFreq = BASE_FREQUENCY;
162 break;
163 case CWDM:
164 case FLEX:
165 case UNKNOWN:
166 default:
167 baseFreq = 0L;
168 break;
169 }
170 if (wavelength > baseFreq) {
171 return (int) ((wavelength - baseFreq) / (channelSpacing.frequency().asMHz()));
172 } else {
173 return (int) ((baseFreq - wavelength) / (channelSpacing.frequency().asMHz()));
174 }
175 }
176
adibrastegarnia025da382019-07-24 12:18:18 -0700177
178 @Override
179 protected void doExecute() throws Exception {
alessiob2f5b412021-09-23 18:36:37 +0200180 if (operation.equals("edit-config") || operation.equals("delete-config")) {
Andrea Campanella3a361452019-08-02 10:17:53 +0200181 FlowRuleService flowService = get(FlowRuleService.class);
182 DeviceService deviceService = get(DeviceService.class);
183 CoreService coreService = get(CoreService.class);
adibrastegarnia025da382019-07-24 12:18:18 -0700184
Andrea Campanella3a361452019-08-02 10:17:53 +0200185 TrafficSelector.Builder trafficSelectorBuilder = DefaultTrafficSelector.builder();
186 TrafficTreatment.Builder trafficTreatmentBuilder = DefaultTrafficTreatment.builder();
187 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
adibrastegarnia025da382019-07-24 12:18:18 -0700188
alessiob2f5b412021-09-23 18:36:37 +0200189 ConnectPoint inCp, outCp = null;
190 Device inDevice, outDevice = null;
adibrastegarnia025da382019-07-24 12:18:18 -0700191
alessiob2f5b412021-09-23 18:36:37 +0200192 inCp = ConnectPoint.deviceConnectPoint(inConnectPointString);
193 inDevice = deviceService.getDevice(inCp.deviceId());
194 if (outConnectPointString != null) {
195 outCp = ConnectPoint.deviceConnectPoint(outConnectPointString);
196 outDevice = deviceService.getDevice(outCp.deviceId());
Andrea Campanella02e2eb4e2019-08-29 11:46:57 -0700197 }
adibrastegarnia025da382019-07-24 12:18:18 -0700198
alessiob2f5b412021-09-23 18:36:37 +0200199 if (inDevice.type().equals(Device.Type.TERMINAL_DEVICE)) {
200
201 //Parsing the ochSignal
202 OchSignal ochSignal;
203 if (parameter.contains("/")) {
204 ochSignal = createOchSignal();
205 } else if (parameter.matches("-?\\d+(\\.\\d+)?")) {
206 ochSignal = createOchSignalFromWavelength(deviceService, inCp);
207 } else {
208 print("[ERROR] signal or wavelength %s are in uncorrect format");
209 return;
210 }
211 if (ochSignal == null) {
212 print("[ERROR] problem while creating OchSignal");
213 return;
214 }
215
216 //Traffic selector
217 TrafficSelector trafficSelector = trafficSelectorBuilder
218 .matchInPort(inCp.port())
219 .build();
220
221 //Traffic treatment including ochSignal
222 TrafficTreatment trafficTreatment = trafficTreatmentBuilder
223 .add(Instructions.modL0Lambda(ochSignal))
224 .add(Instructions.createOutput(deviceService.getPort(inCp).number()))
225 .build();
226
227 int priority = 100;
228 ApplicationId appId = coreService.registerApplication("org.onosproject.optical-model");
229
230 //Flow rule using selector and treatment
231 FlowRule addFlow = flowRuleBuilder
232 .withPriority(priority)
233 .fromApp(appId)
234 .withTreatment(trafficTreatment)
235 .withSelector(trafficSelector)
236 .forDevice(inDevice.id())
237 .makePermanent()
238 .build();
239
240 //Print output on CLI
241 if (operation.equals("edit-config")) {
242 flowService.applyFlowRules(addFlow);
243 print("[INFO] Setting ochSignal on TERMINAL_DEVICE %s", ochSignal);
244 print("--- device: %s", inDevice.id());
245 print("--- port: %s", inCp.port());
246 print("--- central frequency (GHz): %s", ochSignal.centralFrequency().asGHz());
247 } else {
248 //This is delete-config
249 flowService.removeFlowRules(addFlow);
250 print("[INFO] Deleting ochSignal on TERMINAL_DEVICE %s", ochSignal);
251 print("--- device: %s", inDevice.id());
252 print("--- port: %s", inCp.port());
253 print("--- central frequency (GHz): %s", ochSignal.centralFrequency().asGHz());
254 }
255 }
256
257 if (inDevice.type().equals(Device.Type.ROADM)) {
258
259 if (outConnectPointString == null) {
260 print("[ERROR] output port is required for ROADM devices");
261 return;
262 }
263
264 if (!inDevice.equals(outDevice)) {
265 print("[ERROR] input and output ports must be on the same device");
266 return;
267 }
268
269 //Parsing the ochSignal
270 OchSignal ochSignal;
271 if (parameter.contains("/")) {
272 ochSignal = createOchSignal();
273 } else if (parameter.matches("-?\\d+(\\.\\d+)?")) {
274 ochSignal = createOchSignalFromWavelength(deviceService, inCp);
275 } else {
276 print("[ERROR] signal or wavelength %s are in uncorrect format");
277 return;
278 }
279 if (ochSignal == null) {
280 print("[ERROR] problem while creating OchSignal");
281 return;
282 }
283
284 //Traffic selector
285 TrafficSelector trafficSelector = trafficSelectorBuilder
286 .matchInPort(inCp.port())
287 .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
288 .add(Criteria.matchLambda(ochSignal))
289 .build();
290
291 //Traffic treatment
292 TrafficTreatment trafficTreatment = trafficTreatmentBuilder
293 .add(Instructions.modL0Lambda(ochSignal))
294 .add(Instructions.createOutput(deviceService.getPort(outCp).number()))
295 .build();
296
297 int priority = 100;
298 ApplicationId appId = coreService.registerApplication("org.onosproject.optical-model");
299
300 //Flow rule using selector and treatment
301 FlowRule addFlow = flowRuleBuilder
302 .withPriority(priority)
303 .fromApp(appId)
304 .withTreatment(trafficTreatment)
305 .withSelector(trafficSelector)
306 .forDevice(inDevice.id())
307 .makePermanent()
308 .build();
309
310 //Print output on CLI
311 if (operation.equals("edit-config")) {
312 flowService.applyFlowRules(addFlow);
313 print("[INFO] Setting ochSignal on ROADM %s", ochSignal);
314 print("--- device: %s", inDevice.id());
315 print("--- input port %s, outpot port %s", inCp.port(), outCp.port());
316 print("--- central frequency (GHz): %s", ochSignal.centralFrequency().asGHz());
317 print("--- frequency slot width (GHz): %s", ochSignal.slotGranularity() * 12.5);
318 } else {
319 //This is delete-config
320 flowService.removeFlowRules(addFlow);
321 print("[INFO] Deleting ochSignal on ROADM %s", ochSignal);
322 print("--- device: %s", inDevice.id());
323 print("--- input port %s, outpot port %s", inCp.port(), outCp.port());
324 print("--- central frequency (GHz): %s", ochSignal.centralFrequency().asGHz());
325 print("--- frequency slot width (GHz): %s", ochSignal.slotGranularity() * 12.5);
326 }
327 }
328
329 if (!inDevice.type().equals(Device.Type.ROADM) && !inDevice.type().equals(Device.Type.TERMINAL_DEVICE)) {
330 print("[ERROR] wrong device type: %s", inDevice.type());
331 }
332
Andrea Campanella3a361452019-08-02 10:17:53 +0200333 } else {
alessiob2f5b412021-09-23 18:36:37 +0200334 print("[ERROR] operation %s is not yet supported", operation);
Andrea Campanella3a361452019-08-02 10:17:53 +0200335 }
adibrastegarnia025da382019-07-24 12:18:18 -0700336
337 }
338}