blob: 2a454c531b8b7af37ffeec038498f0d724d381d6 [file] [log] [blame]
quan PHAM VAN32d70e52018-08-01 17:35:30 -07001/*
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 */
16package org.onosproject.drivers.odtn;
17
18import com.google.common.annotations.VisibleForTesting;
19import com.google.common.collect.ImmutableList;
20import com.google.common.io.CharSource;
21import org.apache.commons.configuration.HierarchicalConfiguration;
22import org.apache.commons.configuration.XMLConfiguration;
23import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
24import org.onlab.packet.ChassisId;
25import org.onosproject.drivers.utilities.XmlConfigParser;
26import org.onosproject.net.DefaultAnnotations;
27import org.onosproject.net.Device;
28import org.onosproject.net.DeviceId;
29import org.onosproject.net.Port.Type;
30import org.onosproject.net.PortNumber;
31import org.onosproject.net.device.DeviceDescription;
32import org.onosproject.net.device.DeviceDescriptionDiscovery;
33import org.onosproject.net.device.DefaultPortDescription.Builder;
34import org.onosproject.net.device.DefaultDeviceDescription;
35import org.onosproject.net.device.PortDescription;
36import org.onosproject.net.device.DefaultPortDescription;
37import org.onosproject.net.driver.AbstractHandlerBehaviour;
38import org.onosproject.netconf.NetconfController;
39import org.onosproject.netconf.NetconfDevice;
40import org.onosproject.netconf.NetconfException;
41import org.onosproject.netconf.NetconfSession;
42import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
qphamvan9aedb562019-02-19 14:38:53 +010043import org.onosproject.net.OchSignal;
44import org.onosproject.net.optical.device.OchPortHelper;
45import org.onosproject.net.OduSignalType;
46import org.onosproject.net.ChannelSpacing;
quan PHAM VAN32d70e52018-08-01 17:35:30 -070047import org.slf4j.Logger;
48
49import java.util.HashMap;
50import java.util.List;
51import java.util.Map;
52import java.util.Objects;
53import java.util.stream.Collectors;
54
55import static com.google.common.base.Preconditions.checkNotNull;
56import static org.slf4j.LoggerFactory.getLogger;
57
58/**
59 * Nokia OpenConfig based device and port discovery.
60 */
61public class NokiaOpenConfigDeviceDiscovery
62 extends AbstractHandlerBehaviour
63 implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
64
65 private static final Logger log = getLogger(NokiaOpenConfigDeviceDiscovery.class);
66 private static final String RPC_TAG_NETCONF_BASE = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
67 private static final String RPC_CLOSE_TAG = "</rpc>";
68 private static final String OPTICAL_CHANNEL = "OCH";
69 private static final String TRANSCEIVER = "TRANSCEIVER";
70
71 //TODO should be loaded from a file config or something else
72 //The user and password are different from the user and password in the netconf-cfg file
73 private static final String USER_NAME = "admin";
74 private static final String PASSWORD = "admin";
75
76 @Override
77 public DeviceDescription discoverDeviceDetails() {
78 DeviceId did = data().deviceId();
79 NetconfSession ns = getNetconfSessionAndLogin(did, USER_NAME, PASSWORD);
80 if (ns == null) {
81 log.error("DiscoverDeviceDetails called with null session for {}", did);
82 return null;
83 }
84 log.info("Discovering device details {}", handler().data().deviceId());
85 String hwVersion = "1830", swVersion = "OpenAgent";
86 try {
87 String reply = ns.requestSync(buildGetSystemSoftwareRpc());
88 XMLConfiguration cfg = (XMLConfiguration) XmlConfigParser.loadXmlString(getDataOfRpcReply(reply));
89 hwVersion = cfg.getString("components.component.state.description");
90 swVersion = cfg.getString("components.component.state.version");
91 } catch (NetconfException e) {
92 log.error("Error discovering device details on {}", data().deviceId(), e);
93 }
94 return new DefaultDeviceDescription(handler().data().deviceId().uri(),
95 Device.Type.ROADM_OTN,
96 "NOKIA",
97 hwVersion,
98 swVersion,
99 "",
100 new ChassisId("1"));
101 }
102
103 @Override
104 public List<PortDescription> discoverPortDetails() {
105 DeviceId did = data().deviceId();
106 XMLConfiguration cfg = new XMLConfiguration();
107 NetconfSession ns = getNetconfSessionAndLogin(did, USER_NAME, PASSWORD);
108 if (ns == null) {
109 log.error("discoverPorts called with null session for {}", did);
110 return ImmutableList.of();
111 }
112 log.info("Discovering ports details {}", handler().data().deviceId());
113 try {
114 String reply = ns.requestSync(buildGetPlatformComponentsRpc());
115 String data = getDataOfRpcReply(reply);
116 if (data == null) {
117 log.error("No valid response found from {}:\n{}", did, reply);
118 return ImmutableList.of();
119 }
120 cfg.load(CharSource.wrap(data).openStream());
121 return discoverPorts(cfg);
122 } catch (Exception e) {
123 log.error("Error discovering port details on {}", data().deviceId(), e);
124 return ImmutableList.of();
125 }
126 }
127
128 /**
129 * Parses port information from OpenConfig XML configuration.
130 *
131 * @param cfg tree where the root node is {@literal <data>}
132 * @return List of ports
133 */
134 @VisibleForTesting
135 private List<PortDescription> discoverPorts(XMLConfiguration cfg) {
136 // If we want to use XPath
137 cfg.setExpressionEngine(new XPathExpressionEngine());
138 // converting components into PortDescription.
139 List<HierarchicalConfiguration> components = cfg.configurationsAt("components/component");
140 return components.stream()
141 .map(this::toPortDescriptionInternal)
142 .filter(Objects::nonNull)
143 .collect(Collectors.toList());
144 }
145
146 /**
147 * Converts Component subtree to PortDescription.
148 *
149 * @param component subtree to parse
150 * @return PortDescription or null if component is not an ONOS Port
151 */
152 private PortDescription toPortDescriptionInternal(HierarchicalConfiguration component) {
qphamvan9aedb562019-02-19 14:38:53 +0100153 Map<String, String> annotations = new HashMap<>();
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700154 String name = component.getString("name");
155 String type = component.getString("state/type");
156 checkNotNull(name, "name not found");
157 checkNotNull(type, "state/type not found");
qphamvan9aedb562019-02-19 14:38:53 +0100158 annotations.put(OdtnDeviceDescriptionDiscovery.OC_NAME, name);
159 annotations.put(OdtnDeviceDescriptionDiscovery.OC_TYPE, type);
160
161 component.configurationsAt("properties/property")
162 .forEach(property -> {
163 String pn = property.getString("name");
164 String pv = property.getString("state/value");
165 annotations.put(pn, pv);
166 });
167
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700168 if (type.equals("oc-platform-types:PORT")) {
qphamvan9aedb562019-02-19 14:38:53 +0100169
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700170 String subComponentName = component.getString("subcomponents/subcomponent/name");
171 String[] textStr = subComponentName.split("-");
172 String portComponentType = textStr[0];
173 String portComponentIndex = textStr[textStr.length - 1];
qphamvan9aedb562019-02-19 14:38:53 +0100174
175 if (portComponentType.equals(OPTICAL_CHANNEL)) {
176
177 annotations.putIfAbsent(PORT_TYPE, OdtnPortType.LINE.value());
178 annotations.putIfAbsent(ONOS_PORT_INDEX, portComponentIndex.toString());
179 annotations.putIfAbsent(CONNECTION_ID, "connection" + portComponentIndex.toString());
180
181 OchSignal signalId = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 1);
182 return OchPortHelper.ochPortDescription(
183 PortNumber.portNumber(Long.parseLong(portComponentIndex)),
184 true,
185 OduSignalType.ODU4, // TODO Client signal to be discovered
186 true,
187 signalId,
188 DefaultAnnotations.builder().putAll(annotations).build());
189
190 } else if (portComponentType.equals(TRANSCEIVER)) {
191
192 Builder builder = DefaultPortDescription.builder();
193 annotations.putIfAbsent(PORT_TYPE, OdtnPortType.CLIENT.value());
194 annotations.putIfAbsent(ONOS_PORT_INDEX, portComponentIndex.toString());
195 annotations.putIfAbsent(CONNECTION_ID, "connection" + portComponentIndex.toString());
196
197 builder.withPortNumber(PortNumber.portNumber(Long.parseLong(portComponentIndex), subComponentName));
198 builder.type(Type.PACKET);
199
200 builder.annotations(DefaultAnnotations.builder().putAll(annotations).build());
201 return builder.build();
202
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700203 } else {
204 log.debug("Unknown port component type {}", type);
205 return null;
206 }
207 } else {
208 log.debug("Another component type {}", type);
209 return null;
210 }
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700211 }
212
213 /**
214 * Login to the device by providing the correct user and password in order to configure the device
215 * Returns the NetconfSession with the device for which the method was called.
216 *
217 * @param deviceId device indetifier
218 * @param userName
219 * @param passwd
220 * @return The netconf session or null
221 */
222 private NetconfSession getNetconfSessionAndLogin(DeviceId deviceId, String userName, String passwd) {
223 NetconfController nc = handler().get(NetconfController.class);
224 NetconfDevice ndev = nc.getDevicesMap().get(deviceId);
225 if (ndev == null) {
226 log.debug("netconf device " + deviceId + " is not found, returning null session");
227 return null;
228 }
229 NetconfSession ns = ndev.getSession();
230 if (ns == null) {
231 log.error("discoverPorts called with null session for {}", deviceId);
232 return null;
233 }
234 try {
235 String reply = ns.requestSync(buildLoginRpc(userName, passwd));
236 if (reply.contains("<ok/>")) {
237 return ns;
238 } else {
239 log.debug(reply);
240 return null;
241 }
242 } catch (NetconfException e) {
243 log.error("can not login to device", e);
244 }
qphamvan9aedb562019-02-19 14:38:53 +0100245 return ns;
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700246 }
247
248 //crude way of removing rpc-reply envelope (copy from netconf session)
249 private String getDataOfRpcReply(String rpcReply) {
250 String data = null;
251 int begin = rpcReply.indexOf("<data>");
252 int end = rpcReply.lastIndexOf("</data>");
253 if (begin != -1 && end != -1) {
254 data = (String) rpcReply.subSequence(begin, end + "</data>".length());
255 } else {
256 data = rpcReply;
257 }
258 return data;
259 }
260
261 /**
262 * Construct a rpc request message to get system software component.
263 *
264 * @return RPC message
265 */
266 private String buildGetSystemSoftwareRpc() {
267
268 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
269 rpc.append("<get>");
270 rpc.append("<filter type='subtree'>");
271 rpc.append("<components xmlns=\"http://openconfig.net/yang/platform\">");
272 rpc.append("<component>");
273 rpc.append("<name>SYSTEM-SOFTWARE</name>");
274 rpc.append("</component>");
275 rpc.append("</components>");
276 rpc.append("</filter>");
277 rpc.append("</get>");
278 rpc.append(RPC_CLOSE_TAG);
279 return rpc.toString();
280 }
281
282 /**
283 * Construct a rpc request message to get openconfig platform components.
284 *
285 * @return RPC message
286 */
287 private String buildGetPlatformComponentsRpc() {
288 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
289 rpc.append("<get>");
290 rpc.append("<filter type='subtree'>");
291 rpc.append("<components xmlns=\"http://openconfig.net/yang/platform\"></components>");
292 rpc.append("</filter>");
293 rpc.append("</get>");
294 rpc.append(RPC_CLOSE_TAG);
295 return rpc.toString();
296 }
297
298 /**
299 * Construct a rpc login message.
300 *
301 * @param userName
302 * @param passwd
303 * @return RPC message
304 */
305 private String buildLoginRpc(String userName, String passwd) {
306 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
307 rpc.append("<login xmlns=\"http://nokia.com/yang/nokia-security\">");
qphamvan9aedb562019-02-19 14:38:53 +0100308 rpc.append("<user>");
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700309 rpc.append(userName);
qphamvan9aedb562019-02-19 14:38:53 +0100310 rpc.append("</user>");
quan PHAM VAN32d70e52018-08-01 17:35:30 -0700311 rpc.append("<password>");
312 rpc.append(passwd);
313 rpc.append("</password>");
314 rpc.append("</login>");
315 rpc.append(RPC_CLOSE_TAG);
316 return rpc.toString();
317 }
318
319}