blob: 5d8c0c5f2b264a80b4518a70542c47e9b92c4a3d [file] [log] [blame]
fahadnaeemkhan71827242017-09-21 15:10:07 -07001/*
2 * Copyright 2016-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 */
16package org.onosproject.drivers.ciena;
17
18import org.onlab.util.Frequency;
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070019import org.onosproject.driver.optical.flowrule.CrossConnectCache;
20import org.onosproject.net.ChannelSpacing;
fahadnaeemkhan71827242017-09-21 15:10:07 -070021import org.onosproject.net.DeviceId;
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070022import org.onosproject.net.Port;
fahadnaeemkhan71827242017-09-21 15:10:07 -070023import org.onosproject.net.PortNumber;
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070024import org.onosproject.net.OchSignal;
25import org.onosproject.net.OchSignalType;
26import org.onosproject.net.device.DeviceService;
fahadnaeemkhan71827242017-09-21 15:10:07 -070027import org.onosproject.net.driver.DriverHandler;
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070028import org.onosproject.net.flow.DefaultFlowEntry;
29import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.FlowEntry;
33import org.onosproject.net.flow.FlowId;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.criteria.Criteria;
fahadnaeemkhan71827242017-09-21 15:10:07 -070038import org.onosproject.protocol.rest.RestSBController;
39import org.slf4j.Logger;
40
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070041import java.util.Objects;
42import java.util.List;
43import java.util.Collection;
44import java.util.stream.Collectors;
45import java.io.IOException;
fahadnaeemkhan71827242017-09-21 15:10:07 -070046import java.io.ByteArrayInputStream;
47import java.io.InputStream;
48import java.nio.charset.StandardCharsets;
49
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070050import javax.ws.rs.core.MediaType;
51import javax.ws.rs.core.Response;
52
53import com.google.common.collect.ImmutableList;
54import org.apache.commons.lang3.tuple.Pair;
55
56import com.fasterxml.jackson.databind.ObjectMapper;
57import com.fasterxml.jackson.databind.JsonNode;
58import com.fasterxml.jackson.databind.ObjectReader;
59
fahadnaeemkhan71827242017-09-21 15:10:07 -070060import static com.google.common.base.Preconditions.checkNotNull;
61import static org.slf4j.LoggerFactory.getLogger;
62
63public class CienaRestDevice {
64 private DeviceId deviceId;
65 private RestSBController controller;
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070066 private CrossConnectCache crossConnectCache;
67 private DeviceService deviceService;
fahadnaeemkhan71827242017-09-21 15:10:07 -070068
69 private final Logger log = getLogger(getClass());
70 private static final String ENABLED = "enabled";
71 private static final String DISABLED = "disabled";
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070072 private static final String VALUE = "value";
73 private static final String STATE = "state";
74 private static final String ADMIN_STATE = "admin-state";
75 private static final String WAVELENGTH = "wavelength";
76 private static final String DATA = "data";
77 private static final String LINE_SYSTEM_CHANNEL_NUMBER = "ciena-ws-ptp-modem:line-system-channel-number";
78 private static final String FREQUENCY_KEY = "ciena-ws-ptp-modem:frequency";
fahadnaeemkhan71827242017-09-21 15:10:07 -070079 private static final Frequency BASE_FREQUENCY = Frequency.ofGHz(193_950);
fahadnaeemkhan71827242017-09-21 15:10:07 -070080
81 //URIs
82 private static final String PORT_URI = "ws-ptps/ptps/%s";
83 private static final String TRANSMITTER_URI = PORT_URI + "/properties/transmitter";
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070084 private static final String PORT_STATE_URI = PORT_URI + "/" + STATE;
85 private static final String WAVELENGTH_URI = TRANSMITTER_URI + "/" + WAVELENGTH;
86 private static final String FREQUENCY_URI = TRANSMITTER_URI + "/" + FREQUENCY_KEY;
87 private static final String CHANNEL_URI = TRANSMITTER_URI + "/" + LINE_SYSTEM_CHANNEL_NUMBER;
88
89 private static final List<String> LINESIDE_PORT_ID = ImmutableList.of("4", "48");
fahadnaeemkhan71827242017-09-21 15:10:07 -070090
91 public CienaRestDevice(DriverHandler handler) throws NullPointerException {
92 deviceId = handler.data().deviceId();
93 controller = checkNotNull(handler.get(RestSBController.class));
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -070094 crossConnectCache = checkNotNull(handler.get(CrossConnectCache.class));
95 deviceService = checkNotNull(handler.get(DeviceService.class));
96 }
97
98 /**
99 * return the Line side ports.
100 *
101 * @return List of Line side ports.
102 */
103 public static List<String> getLinesidePortId() {
104 return LINESIDE_PORT_ID;
105 }
106
107 /**
108 * add the given flow rules to cross connect-cache.
109 *
110 * @param flowRules flow rules that needs to be cached.
111 */
112 public void setCrossConnectCache(Collection<FlowRule> flowRules) {
113 flowRules.forEach(xc -> crossConnectCache.set(
114 Objects.hash(deviceId, xc.selector(), xc.treatment()),
115 xc.id(),
116 xc.priority()));
117 }
118
119 /**
120 * remove the given flow rules form the cross-connect cache.
121 *
122 * @param flowRules flow rules that needs to be removed from cache.
123 */
124 public void removeCrossConnectCache(Collection<FlowRule> flowRules) {
125 flowRules.forEach(xc -> crossConnectCache.remove(Objects.hash(deviceId, xc.selector(), xc.treatment())));
fahadnaeemkhan71827242017-09-21 15:10:07 -0700126 }
127
128 private final String genPortStateRequest(String state) {
129 String request = "{\n" +
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700130 "\"" + STATE + "\": {\n" +
131 "\"" + ADMIN_STATE + "\": \"" + state + "\"\n}\n}";
fahadnaeemkhan71827242017-09-21 15:10:07 -0700132 log.debug("generated request: \n{}", request);
133 return request;
134 }
135
136 private String genWavelengthChangeRequest(String wavelength) {
137 String request = "{\n" +
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700138 "\"" + WAVELENGTH + "\": {\n" +
139 "\"" + VALUE + "\": " + wavelength + "\n" +
fahadnaeemkhan71827242017-09-21 15:10:07 -0700140 "}\n" +
141 "}";
142 log.debug("request:\n{}", request);
143 return request;
144
145 }
146
fahadnaeemkhanffc917f2017-10-03 14:04:46 -0700147 private String genFrequencyChangeRequest(long frequency) {
fahadnaeemkhan71827242017-09-21 15:10:07 -0700148 String request = "{\n" +
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700149 "\"" + FREQUENCY_KEY + "\": {\n" +
150 "\"" + VALUE + "\": " + Long.toString(frequency) + "\n" +
fahadnaeemkhan71827242017-09-21 15:10:07 -0700151 "}\n" +
152 "}";
153 log.debug("request:\n{}", request);
154 return request;
155
156 }
157
158 private String genChannelChangeRequest(int channel) {
159 String request = "{\n" +
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700160 "\"" + LINE_SYSTEM_CHANNEL_NUMBER + "\": " +
fahadnaeemkhan71827242017-09-21 15:10:07 -0700161 Integer.toString(channel) + "\n}";
162 log.debug("request:\n{}", request);
163 return request;
164
165 }
166
167
168 private final String genUri(String uriFormat, PortNumber port) {
169 return String.format(uriFormat, port.name());
170 }
171
172 private boolean changePortState(PortNumber number, String state) {
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700173 log.debug("changing the port {} on device {} state to {}", number, deviceId, state);
fahadnaeemkhan71827242017-09-21 15:10:07 -0700174 String uri = genUri(PORT_STATE_URI, number);
175 String request = genPortStateRequest(state);
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700176 boolean response = putNoReply(uri, request);
177 if (!response) {
178 log.error("unable to change port {} on device {} state to {}", number, deviceId, state);
179 }
180 return response;
fahadnaeemkhan71827242017-09-21 15:10:07 -0700181 }
182
183 public boolean disablePort(PortNumber number) {
184 return changePortState(number, DISABLED);
185 }
186
187 public boolean enablePort(PortNumber number) {
188 return changePortState(number, ENABLED);
189 }
190
191 public final boolean changeFrequency(OchSignal signal, PortNumber outPort) {
192 String uri = genUri(FREQUENCY_URI, outPort);
193 long frequency = toFrequency(signal);
194 String request = genFrequencyChangeRequest(frequency);
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700195 boolean response = putNoReply(uri, request);
196 if (!response) {
197 log.error("unable to change frequency of port {} on device {}", outPort, deviceId);
198 }
199 return response;
fahadnaeemkhan71827242017-09-21 15:10:07 -0700200 }
201
202 public final boolean changeChannel(OchSignal signal, PortNumber outPort) {
203 String uri = genUri(CHANNEL_URI, outPort);
fahadnaeemkhanffc917f2017-10-03 14:04:46 -0700204 int channel = signal.spacingMultiplier();
fahadnaeemkhan71827242017-09-21 15:10:07 -0700205 log.debug("channel is {} for port {} on device {}", channel, outPort.name(), deviceId);
206 String request = genChannelChangeRequest(channel);
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700207 boolean response = putNoReply(uri, request);
208 if (!response) {
209 log.error("unable to change channel to {} for port {} on device {}",
210 channel, outPort.name(), deviceId);
211 }
212 return response;
fahadnaeemkhan71827242017-09-21 15:10:07 -0700213 }
214
215 private final long toFrequency(OchSignal signal) {
216 double frequency = BASE_FREQUENCY.asGHz() +
217 (signal.channelSpacing().frequency().asGHz() * (double) signal.slotGranularity());
218 return Double.valueOf(frequency).longValue();
219 }
220
fahadnaeemkhana62bd2f2017-10-11 12:39:55 -0700221 private final int getChannel(PortNumber port) {
222 try {
223 String uri = genUri(CHANNEL_URI, port);
224 JsonNode response = get(uri);
225 return response.get(LINE_SYSTEM_CHANNEL_NUMBER).asInt();
226 } catch (IOException e) {
227 // this is expected for client side ports as they don't contain channel data
228 log.error("unable to get channel for port {} on device {}:\n{}", port, deviceId, e);
229 return -1;
230 }
231
232 }
233
234 public Collection<FlowEntry> getFlowEntries() {
235 List<Port> ports = deviceService.getPorts(deviceId);
236 //driver only handles lineSide ports
237 //TODO: handle client ports as well
238 return ports.stream()
239 .filter(p -> LINESIDE_PORT_ID.contains(p.number().name()))
240 .map(p -> fetchRule(p.number()))
241 .filter(p -> p != null)
242 .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
243 .collect(Collectors.toList());
244 }
245
246 private FlowRule fetchRule(PortNumber port) {
247 int channel = getChannel(port);
248 if (channel == -1) {
249 return null;
250 }
251
252 /*
253 * both inPort and outPort will be same as WaveServer only deal with same port ppt-indexes
254 * channel and spaceMultiplier are same.
255 * TODO: find a way to get both inPort and outPort for future when inPort may not be equal to outPort
256 */
257
258 TrafficSelector selector = DefaultTrafficSelector.builder()
259 .matchInPort(port)
260 .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
261 .add(Criteria.matchLambda(OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, channel)))
262 .build();
263 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
264 .setOutput(port)
265 .build();
266
267 int hash = Objects.hash(deviceId, selector, treatment);
268 Pair<FlowId, Integer> lookup = crossConnectCache.get(hash);
269 if (lookup == null) {
270 return null;
271 }
272
273 FlowRule fr = DefaultFlowRule.builder()
274 .forDevice(deviceId)
275 .makePermanent()
276 .withSelector(selector)
277 .withTreatment(treatment)
278 .withPriority(lookup.getRight())
279 .withCookie(lookup.getLeft().value())
280 .build();
281
282 return fr;
283 }
284
285 private JsonNode get(String uri) throws IOException {
286 InputStream response = controller.get(deviceId, uri, MediaType.valueOf(MediaType.APPLICATION_JSON));
287 ObjectMapper om = new ObjectMapper();
288 final ObjectReader reader = om.reader();
289 // all waveserver responses contain data node, which contains the requested data
290 return reader.readTree(response).get(DATA);
291 }
292
fahadnaeemkhan71827242017-09-21 15:10:07 -0700293 private int put(String uri, String request) {
294 InputStream payload = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8));
295 int response = controller.put(deviceId, uri, payload, MediaType.valueOf(MediaType.APPLICATION_JSON));
296 log.debug("response: {}", response);
297 return response;
298 }
299
300 private boolean putNoReply(String uri, String request) {
301 return put(uri, request) == Response.Status.NO_CONTENT.getStatusCode();
302 }
303
304}