blob: 08e74f04364b877708557ac8d1bb79efc7095ca3 [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.fasterxml.jackson.databind.node.ObjectNode;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Range;
21import org.onlab.osgi.ServiceDirectory;
22import org.onosproject.net.ChannelSpacing;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.OchSignal;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.flow.FlowEntry;
27import org.onosproject.net.flow.FlowId;
28import org.onosproject.net.flow.FlowRuleService;
29import org.onosproject.ui.RequestHandler;
30import org.onosproject.ui.UiConnection;
31import org.onosproject.ui.UiMessageHandler;
32import org.onosproject.ui.table.TableModel;
33import org.onosproject.ui.table.TableRequestHandler;
34import org.onosproject.ui.table.cell.HexLongFormatter;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38import java.util.Collection;
39import java.util.Collections;
40import java.util.Comparator;
41import java.util.Set;
42
43import static org.onosproject.ui.JsonUtils.node;
44import static org.onosproject.ui.JsonUtils.number;
45
46/**
47 * Table-View message handler for ROADM flow view.
48 */
49public class RoadmFlowViewMessageHandler extends UiMessageHandler {
50
51 private static final String ROADM_FLOW_DATA_REQ = "roadmFlowDataRequest";
52 private static final String ROADM_FLOW_DATA_RESP = "roadmFlowDataResponse";
53 private static final String ROADM_FLOWS = "roadmFlows";
54
55 private static final String ROADM_SET_ATTENUATION_REQ = "roadmSetAttenuationRequest";
56 private static final String ROADM_SET_ATTENUATION_RESP = "roadmSetAttenuationResponse";
57
58 private static final String ROADM_DELETE_FLOW_REQ = "roadmDeleteFlowRequest";
59
60 private static final String ROADM_CREATE_FLOW_REQ = "roadmCreateFlowRequest";
61 private static final String ROADM_CREATE_FLOW_RESP = "roadmCreateFlowResponse";
62
63 private static final String NO_ROWS_MESSAGE = "No items found";
64
65 private static final String DEV_ID = "devId";
66
67 private static final String ID = "id";
68 private static final String FLOW_ID = "flowId";
69 private static final String APP_ID = "appId";
70 private static final String GROUP_ID = "groupId";
71 private static final String TABLE_ID = "tableId";
72 private static final String PRIORITY = "priority";
73 private static final String PERMANENT = "permanent";
74 private static final String TIMEOUT = "timeout";
75 private static final String STATE = "state";
76 private static final String IN_PORT = "inPort";
77 private static final String OUT_PORT = "outPort";
78 private static final String CHANNEL_SPACING = "spacing";
79 private static final String CHANNEL_MULTIPLIER = "multiplier";
80 private static final String CURRENT_POWER = "currentPower";
81 private static final String ATTENUATION = "attenuation";
82 private static final String HAS_ATTENUATION = "hasAttenuation";
83
84 private static final String[] COLUMN_IDS = {
85 ID, FLOW_ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, TIMEOUT,
86 PERMANENT, STATE, IN_PORT, OUT_PORT, CHANNEL_SPACING,
87 CHANNEL_MULTIPLIER, CURRENT_POWER, ATTENUATION, HAS_ATTENUATION
88 };
89
90 private static final String NA = "N/A";
91 private static final String UNKNOWN = "Unknown";
92
93 private static final long GHZ = 1_000_000_000L;
94
95 private FlowRuleService flowRuleService;
96 private RoadmService roadmService;
97
98 private final Logger log = LoggerFactory.getLogger(getClass());
99
100 @Override
101 public void init(UiConnection connection, ServiceDirectory directory) {
102 super.init(connection, directory);
103 flowRuleService = get(FlowRuleService.class);
104 roadmService = get(RoadmService.class);
105 }
106
107 @Override
108 protected Collection<RequestHandler> createRequestHandlers() {
109 return ImmutableSet.of(
110 new FlowTableDataRequestHandler(),
111 new SetAttenuationRequestHandler(),
112 new DeleteConnectionRequestHandler(),
113 new CreateConnectionRequestHandler()
114 );
115 }
116
117 // Handler for sample table requests
118 private final class FlowTableDataRequestHandler extends TableRequestHandler {
119
120 private FlowTableDataRequestHandler() {
121 super(ROADM_FLOW_DATA_REQ, ROADM_FLOW_DATA_RESP, ROADM_FLOWS);
122 }
123
124 @Override
125 protected String[] getColumnIds() {
126 return COLUMN_IDS;
127 }
128
129 @Override
130 protected String noRowsMessage(ObjectNode payload) {
131 return NO_ROWS_MESSAGE;
132 }
133
134 @Override
135 protected TableModel createTableModel() {
136 TableModel tm = super.createTableModel();
137 tm.setFormatter(FLOW_ID, HexLongFormatter.INSTANCE);
138 return tm;
139 }
140
141 @Override
142 protected void populateTable(TableModel tm, ObjectNode payload) {
143 DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
144
145 Iterable<FlowEntry> flowEntries = flowRuleService.getFlowEntries(deviceId);
146 for (FlowEntry flowEntry : flowEntries) {
147 populateRow(tm.addRow(), flowEntry, deviceId);
148 }
149 }
150
151 private void populateRow(TableModel.Row row, FlowEntry entry, DeviceId deviceId) {
152 ChannelData cd = ChannelData.fromFlow(entry);
153 row.cell(ID, entry.id().value())
154 .cell(FLOW_ID, entry.id().value())
155 .cell(APP_ID, entry.appId())
156 .cell(PRIORITY, entry.priority())
157 .cell(TIMEOUT, entry.timeout())
158 .cell(PERMANENT, entry.isPermanent())
159 .cell(STATE, entry.state().toString())
160 .cell(IN_PORT, cd.inPort().toLong())
161 .cell(OUT_PORT, cd.outPort().toLong())
162 .cell(CHANNEL_SPACING, cd.ochSignal().channelSpacing().frequency().asHz() / GHZ)
163 .cell(CHANNEL_MULTIPLIER, cd.ochSignal().spacingMultiplier())
164 .cell(CURRENT_POWER, getCurrentPower(deviceId, cd))
165 .cell(ATTENUATION, getAttenuation(deviceId, cd));
166 }
167
168 private String getCurrentPower(DeviceId deviceId, ChannelData channelData) {
169 Range<Long> range =
170 roadmService.attenuationRange(deviceId,
171 channelData.outPort(),
172 channelData.ochSignal());
173 if (range != null) {
174 Long currentPower =
175 roadmService.getCurrentChannelPower(deviceId,
176 channelData.outPort(),
177 channelData.ochSignal());
178 if (currentPower != null) {
179 return String.valueOf(currentPower);
180 }
181 }
182 return NA;
183 }
184
185 private String getAttenuation(DeviceId deviceId, ChannelData channelData) {
186 Long attenuation =
187 roadmService.getAttenuation(deviceId, channelData.outPort(),
188 channelData.ochSignal());
189 if (attenuation != null) {
190 return String.valueOf(attenuation);
191 }
192 return UNKNOWN;
193 }
194 }
195
196 // Handler for setting attenuation
197 private final class SetAttenuationRequestHandler extends RequestHandler {
198
199 // Keys for response message
200 private static final String VALID = "valid";
201 private static final String MESSAGE = "message";
202
203 // Error messages to display to user
204 private static final String ATTENUATION_RANGE_MSG =
205 "Attenuation must be in range %s.";
206 private static final String NO_ATTENUATION_MSG =
207 "Cannot set attenuation for this connection";
208
209 private SetAttenuationRequestHandler() {
210 super(ROADM_SET_ATTENUATION_REQ);
211 }
212
213 @Override
214 public void process(ObjectNode payload) {
215 DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
216 FlowId flowId = FlowId.valueOf(number(payload, FLOW_ID));
217 long attenuation = payload.get(ATTENUATION).asLong();
218
219 // Get connection information from the flow
220 FlowEntry entry = findFlow(deviceId, flowId);
221 if (entry == null) {
222 log.error("Unable to find flow rule to set attenuation");
223 return;
224 }
225 ChannelData cd = ChannelData.fromFlow(entry);
226 Range<Long> range =
227 roadmService.attenuationRange(deviceId, cd.outPort(),
228 cd.ochSignal());
229
230 boolean validAttenuation = (range != null && range.contains(attenuation));
231 if (validAttenuation) {
232 roadmService.setAttenuation(deviceId, cd.outPort(),
233 cd.ochSignal(), attenuation);
234 }
235
236 ObjectNode rootNode = objectNode();
237 // Send back flowId so view can identify which callback function to use
238 rootNode.put(FLOW_ID, payload.get(FLOW_ID).asText());
239 rootNode.put(VALID, validAttenuation);
240 if (range != null) {
241 rootNode.put(MESSAGE, String.format(ATTENUATION_RANGE_MSG,
242 range.toString()));
243 } else {
244 rootNode.put(MESSAGE, NO_ATTENUATION_MSG);
245 }
246 sendMessage(ROADM_SET_ATTENUATION_RESP, rootNode);
247 }
248
249 private FlowEntry findFlow(DeviceId deviceId, FlowId flowId) {
250 for (FlowEntry entry : flowRuleService.getFlowEntries(deviceId)) {
251 if (entry.id().equals(flowId)) {
252 return entry;
253 }
254 }
255 return null;
256 }
257 }
258
259 // Handler for deleting a connection
260 private final class DeleteConnectionRequestHandler extends RequestHandler {
261 private DeleteConnectionRequestHandler() {
262 super(ROADM_DELETE_FLOW_REQ);
263 }
264
265 @Override
266 public void process(ObjectNode payload) {
267 DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
268 FlowId flowId = FlowId.valueOf(payload.get(ID).asLong());
269 roadmService.removeConnection(deviceId, flowId);
270 }
271 }
272
273 // Handler for creating a creating a connection from form data
274 private final class CreateConnectionRequestHandler extends RequestHandler {
275
276 // Keys to load from JSON
277 private static final String FORM_DATA = "formData";
278 private static final String CHANNEL_SPACING_INDEX = "index";
279 private static final String INCLUDE_ATTENUATION = "includeAttenuation";
280
281 // Keys for validation results
282 private static final String CONNECTION = "connection";
283 private static final String CHANNEL_AVAILABLE = "channelAvailable";
284
285 // Error messages to display to user
286 private static final String IN_PORT_ERR_MSG = "Invalid input port.";
287 private static final String OUT_PORT_ERR_MSG = "Invalid output port.";
288 private static final String CONNECTION_ERR_MSG =
289 "Invalid connection from input port to output port.";
290 private static final String CHANNEL_SPACING_ERR_MSG =
291 "Channel spacing not supported.";
292 private static final String CHANNEL_ERR_MSG =
293 "Channel index must be in range %s.";
294 private static final String CHANNEL_AVAILABLE_ERR_MSG =
295 "Channel is already being used.";
296 private static final String ATTENUATION_ERR_MSG =
297 "Attenuation must be in range %s.";
298
299 // Keys for validation object
300 private static final String VALID = "valid";
301 private static final String MESSAGE = "message";
302
303 private CreateConnectionRequestHandler() {
304 super(ROADM_CREATE_FLOW_REQ);
305 }
306
307 @Override
308 public void process(ObjectNode payload) {
309 DeviceId did = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
310 ObjectNode flowNode = node(payload, FORM_DATA);
311 int priority = (int) number(flowNode, PRIORITY);
312 boolean permanent = bool(flowNode, PERMANENT);
313 int timeout = (int) number(flowNode, TIMEOUT);
314 PortNumber inPort = PortNumber.portNumber(number(flowNode, IN_PORT));
315 PortNumber outPort = PortNumber.portNumber(number(flowNode, OUT_PORT));
316 ObjectNode chNode = node(flowNode, CHANNEL_SPACING);
317 ChannelSpacing spacing =
318 channelSpacing((int) number(chNode, CHANNEL_SPACING_INDEX));
319 int multiplier = (int) number(flowNode, CHANNEL_MULTIPLIER);
320 OchSignal och = OchSignal.newDwdmSlot(spacing, multiplier);
321 boolean includeAttenuation = bool(flowNode, INCLUDE_ATTENUATION);
322 long att = number(flowNode, ATTENUATION);
323
324 boolean validInPort = roadmService.validInputPort(did, inPort);
325 boolean validOutPort = roadmService.validOutputPort(did, outPort);
326 boolean validConnect = roadmService.validConnection(did, inPort, outPort);
327 boolean validSpacing = true;
328 boolean validChannel = roadmService.validChannel(did, inPort, och);
329 boolean channelAvailable = roadmService.channelAvailable(did, och);
330 boolean validAttenuation = roadmService.attenuationInRange(did, outPort, att);
331
332 if (validConnect && validChannel && channelAvailable) {
333 if (includeAttenuation && validAttenuation) {
334 roadmService.createConnection(did, priority, permanent,
335 timeout, inPort, outPort,
336 och, att);
337 } else if (!includeAttenuation) {
338 roadmService.createConnection(did, priority, permanent,
339 timeout, inPort, outPort,
340 och);
341 }
342 }
343
344 // Construct error for channel
345 String channelMessage = "Invalid channel";
346 if (!validChannel) {
347 Set<OchSignal> lambdas = roadmService.queryLambdas(did, outPort);
348 if (lambdas != null) {
349 Range<Integer> range = channelRange(lambdas);
350 if (range.contains(och.spacingMultiplier())) {
351 // Channel spacing error
352 validSpacing = false;
353 } else {
354 channelMessage = String.format(CHANNEL_ERR_MSG, range.toString());
355 }
356 }
357 }
358
359 // Construct error for attenuation
360 String attenuationMessage = "Invalid attenuation";
361 if (!validAttenuation) {
362 Range<Long> range =
363 roadmService.attenuationRange(did, outPort, och);
364 if (range != null) {
365 attenuationMessage =
366 String.format(ATTENUATION_ERR_MSG, range.toString());
367 }
368 }
369
370 // Build response
371 ObjectNode node = objectNode();
372
373 node.set(IN_PORT, validationObject(validInPort, IN_PORT_ERR_MSG));
374 node.set(OUT_PORT, validationObject(validOutPort, OUT_PORT_ERR_MSG));
375 node.set(CONNECTION, validationObject(validConnect, CONNECTION_ERR_MSG));
376 node.set(CHANNEL_SPACING, validationObject(validChannel || validSpacing,
377 CHANNEL_SPACING_ERR_MSG));
378 node.set(CHANNEL_MULTIPLIER, validationObject(validChannel || !validSpacing,
379 channelMessage));
380 node.set(CHANNEL_AVAILABLE, validationObject(!validChannel || channelAvailable,
381 CHANNEL_AVAILABLE_ERR_MSG));
382 node.set(ATTENUATION, validationObject(validAttenuation, attenuationMessage));
383 node.put(INCLUDE_ATTENUATION, includeAttenuation);
384
385 sendMessage(ROADM_CREATE_FLOW_RESP, node);
386 }
387
388 // Returns the ChannelSpacing based on the selection made
389 private ChannelSpacing channelSpacing(int selectionIndex) {
390 switch (selectionIndex) {
391 case 0: return ChannelSpacing.CHL_100GHZ;
392 case 1: return ChannelSpacing.CHL_50GHZ;
393 case 2: return ChannelSpacing.CHL_25GHZ;
394 case 3: return ChannelSpacing.CHL_12P5GHZ;
395 // 6.25GHz cannot be used with ChannelSpacing.newDwdmSlot
396 // case 4: return ChannelSpacing.CHL_6P25GHZ;
397 default: return ChannelSpacing.CHL_50GHZ;
398 }
399 }
400
401 // Construct validation object to return to the view
402 private ObjectNode validationObject(boolean result, String message) {
403 ObjectNode node = objectNode();
404 node.put(VALID, result);
405 if (!result) {
406 // return error message to display if validation failed
407 node.put(MESSAGE, message);
408 }
409 return node;
410 }
411
412 // Returns the minimum and maximum channel spacing
413 private Range<Integer> channelRange(Set<OchSignal> signals) {
414 Comparator<OchSignal> compare =
415 (OchSignal a, OchSignal b) -> a.spacingMultiplier() - b.spacingMultiplier();
416 OchSignal minOch = Collections.min(signals, compare);
417 OchSignal maxOch = Collections.max(signals, compare);
418 return Range.closed(minOch.spacingMultiplier(), maxOch.spacingMultiplier());
419 }
420 }
421}