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