blob: 19b247efaad9d00069f644d3c1e048dcf252bd94 [file] [log] [blame]
Andrea Campanella3a361452019-08-02 10:17:53 +02001/*
2 * Copyright 2018-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 * This work was partially supported by EC H2020 project METRO-HAUL (761727).
17 */
18
19package org.onosproject.drivers.odtn;
20
21import com.google.common.collect.ImmutableList;
22import org.apache.commons.configuration.HierarchicalConfiguration;
23import org.apache.commons.configuration.XMLConfiguration;
24import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
25import org.onlab.packet.ChassisId;
26import org.onosproject.drivers.utilities.XmlConfigParser;
27import org.onosproject.net.ChannelSpacing;
28import org.onosproject.net.DefaultAnnotations;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.OchSignal;
32import org.onosproject.net.OduSignalType;
33import org.onosproject.net.Port.Type;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.device.DefaultDeviceDescription;
36import org.onosproject.net.device.DefaultPortDescription;
37import org.onosproject.net.device.DefaultPortDescription.Builder;
38import org.onosproject.net.device.DeviceDescription;
39import org.onosproject.net.device.DeviceDescriptionDiscovery;
40import org.onosproject.net.device.PortDescription;
41import org.onosproject.net.driver.AbstractHandlerBehaviour;
42import org.onosproject.net.optical.device.OchPortHelper;
43import org.onosproject.netconf.NetconfController;
44import org.onosproject.netconf.NetconfDevice;
45import org.onosproject.netconf.NetconfSession;
46import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
47import org.slf4j.Logger;
48
49import java.util.HashMap;
50import java.util.List;
51import java.util.Map;
52import java.util.Objects;
53import java.util.concurrent.CompletableFuture;
54import java.util.regex.Matcher;
55import java.util.regex.Pattern;
56import java.util.stream.Collectors;
57
58import static org.slf4j.LoggerFactory.getLogger;
59
60
61/**
62 * Driver Implementation of the DeviceDescrption discovery for OpenConfig
63 * terminal devices.
64 *
65 */
66public class CassiniTerminalDeviceDiscoveryOld
67 extends AbstractHandlerBehaviour
68 implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
69
70 private static final String RPC_TAG_NETCONF_BASE =
71 "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
72
73 private static final String RPC_CLOSE_TAG = "</rpc>";
74
75 private static final String OC_PLATFORM_TYPES_TRANSCEIVER =
76 "oc-platform-types:TRANSCEIVER";
77
78 private static final String OC_PLATFORM_TYPES_PORT =
79 "oc-platform-types:PORT";
80
81 private static final String OC_TRANSPORT_TYPES_OPTICAL_CHANNEL =
82 "oc-opt-types:OPTICAL_CHANNEL";
83
84 private static final Logger log = getLogger(CassiniTerminalDeviceDiscoveryOld.class);
85
86
87 /**
88 * Returns the NetconfSession with the device for which the method was called.
89 *
90 * @param deviceId device indetifier
91 *
92 * @return The netconf session or null
93 */
94 private NetconfSession getNetconfSession(DeviceId deviceId) {
95 NetconfController controller = handler().get(NetconfController.class);
96 NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
97 if (ncdev == null) {
98 log.trace("No netconf device, returning null session");
99 return null;
100 }
101 return ncdev.getSession();
102 }
103
104
105 /**
106 * Get the deviceId for which the methods apply.
107 *
108 * @return The deviceId as contained in the handler data
109 */
110 private DeviceId did() {
111 return handler().data().deviceId();
112 }
113
114
115 /**
116 * Construct a String with a Netconf filtered get RPC Message.
117 *
118 * @param filter A valid XML tree with the filter to apply in the get
119 * @return a String containing the RPC XML Document
120 */
121 private String filteredGetBuilder(String filter) {
122 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
123 rpc.append("<get>");
124 rpc.append("<filter type='subtree'>");
125 rpc.append(filter);
126 rpc.append("</filter>");
127 rpc.append("</get>");
128 rpc.append(RPC_CLOSE_TAG);
129 return rpc.toString();
130 }
131
132
133 /**
134 * Builds a request to get Device Components, config and operational data.
135 *
136 * @return A string with the Netconf RPC for a get with subtree rpcing based on
137 * /components/
138 */
139 private String getTerminalDeviceBuilder() {
140 return filteredGetBuilder("<terminal-device xmlns='http://openconfig.net/yang/terminal-device'/>");
141 }
142
143
144 @Override
145 public DeviceDescription discoverDeviceDetails() {
146 return new DefaultDeviceDescription(handler().data().deviceId().uri(),
147 Device.Type.TERMINAL_DEVICE,
148 "EDGECORE",
149 "Cassini",
150 "OcNOS",
151 "",
152 new ChassisId("1"));
153 }
154
155
156
157 /**
158 * Returns a list of PortDescriptions for the device.
159 *
160 * @return a list of descriptions.
161 *
162 * The RPC reply follows the following pattern:
163 * //CHECKSTYLE:OFF
164 * <pre>{@code
165 * <?xml version="1.0" encoding="UTF-8"?>
166 * <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
167 * <data>
168 * <components xmlns="http://openconfig.net/yang/platform">
169 * <component>....
170 * </component>
171 * <component>....
172 * </component>
173 * </components>
174 * </data>
175 * </rpc-reply>
176 * }</pre>
177 * //CHECKSTYLE:ON
178 */
179 @Override
180 public List<PortDescription> discoverPortDetails() {
181 try {
182 NetconfSession session = getNetconfSession(did());
183 if (session == null) {
184 log.error("discoverPortDetails called with null session for {}", did());
185 return ImmutableList.of();
186 }
187
188 CompletableFuture<String> fut = session.rpc(getTerminalDeviceBuilder());
189 String rpcReply = fut.get();
190
191 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReply);
192 xconf.setExpressionEngine(new XPathExpressionEngine());
193
194 HierarchicalConfiguration logicalChannels = xconf.configurationAt("data/terminal-device/logical-channels");
195 return parseLogicalChannels(logicalChannels);
196 } catch (Exception e) {
197 log.error("Exception discoverPortDetails() {}", did(), e);
198 return ImmutableList.of();
199 }
200 }
201
202
203
204
205 /**
206 * Parses transceiver information from OpenConfig XML configuration.
207 *
208 * @param terminalDevice the XML document with components root.
209 * @return List of ports
210 *
211 * //CHECKSTYLE:OFF
212 * <pre>{@code
213 * <components xmlns="http://openconfig.net/yang/platform">
214 * <component>....
215 * </component>
216 * <component>....
217 * </component>
218 * </components>
219 * }</pre>
220 * //CHECKSTYLE:ON
221 */
222 protected List<PortDescription> parseLogicalChannels(HierarchicalConfiguration terminalDevice) {
223 return terminalDevice.configurationsAt("channel")
224 .stream()
225 .filter(channel -> !channel.getString("index", "unknown").equals("unknown"))
226 .map(channel -> {
227 try {
228 // Pass the root document for cross-reference
229 return parseLogicalChannel(channel);
230 } catch (Exception e) {
231 return null;
232 }
233 })
234 .filter(Objects::nonNull)
235 .collect(Collectors.toList());
236 }
237
238
239 /**
240 * Parses a component XML doc into a PortDescription.
241 *
242 * @param channel subtree to parse. It must be a component ot type PORT.
243 * case we need to check transceivers or optical channels.
244 *
245 * @return PortDescription or null if component does not have onos-index
246 */
247 private PortDescription parseLogicalChannel(
248 HierarchicalConfiguration channel) {
249
250 HierarchicalConfiguration config = channel.configurationAt("config");
251 String logicalChannelIndex = config.getString("index");
252 String description = config.getString("description");
253 String rateClass = config.getString("rate-class");
254 log.info("Parsing Component {} type {} rate {}", logicalChannelIndex, description, rateClass);
255
256 Map<String, String> annotations = new HashMap<>();
257 annotations.put(OdtnDeviceDescriptionDiscovery.OC_LOGICAL_CHANNEL, logicalChannelIndex);
258 annotations.put(OdtnDeviceDescriptionDiscovery.OC_NAME, description);
259
260 // Store all properties as port properties
261
262 Pattern clientPattern = Pattern.compile("xe(\\d*)/1"); // e.g. xe1/1
263 Pattern linePattern = Pattern.compile("oe(\\d*)/(\\d*)"); // e.g. oe1
264 Matcher clientMatch = clientPattern.matcher(description);
265 Matcher lineMatch = linePattern.matcher(description);
266
267 Pattern portSpeedPattern = Pattern.compile("TRIB_RATE_([0-9.]*)G");
268 Matcher portSpeedMatch = portSpeedPattern.matcher(rateClass);
269
270
271 Builder builder = DefaultPortDescription.builder();
272
273 if (portSpeedMatch.find()) {
274 Long speed = Long.parseLong(portSpeedMatch.group(1));
275 builder.portSpeed(speed * 1000);
276 }
277
278 if (clientMatch.find()) {
279 Long num = Long.parseLong(clientMatch.group(1));
280 Long portNum = 100 + num;
281 String connectionId = "connection:" + num.toString();
282
283 annotations.putIfAbsent(PORT_TYPE, OdtnPortType.CLIENT.value());
284 annotations.putIfAbsent(ONOS_PORT_INDEX, portNum.toString());
285 annotations.putIfAbsent(CONNECTION_ID, connectionId);
286
287 builder.withPortNumber(PortNumber.portNumber(portNum));
288 builder.type(Type.PACKET);
289
290 builder.annotations(DefaultAnnotations.builder().putAll(annotations).build());
291 return builder.build();
292
293 } else if (lineMatch.find()) {
294 Long num = (Long.parseLong(lineMatch.group(1)) - 1) * 2 + Long.parseLong(lineMatch.group(2));
295 Long portNum = 200 + num;
296 String connectionId = "connection:" + num.toString();
297
298 annotations.putIfAbsent(PORT_TYPE, OdtnPortType.LINE.value());
299 annotations.putIfAbsent(ONOS_PORT_INDEX, portNum.toString());
300 annotations.putIfAbsent(CONNECTION_ID, connectionId);
301
302 OchSignal signalId = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 1);
303 return OchPortHelper.ochPortDescription(
304 PortNumber.portNumber(portNum),
305 true,
306 OduSignalType.ODU4, // TODO Client signal to be discovered
307 true,
308 signalId,
309 DefaultAnnotations.builder().putAll(annotations).build());
310
311 } else {
312 log.warn("Unexpected component description: {}", description);
313 return null;
314 }
315
316 }
317}