blob: 6411d4aaf0e9943b9003d78e8da8b4313e6b45d9 [file] [log] [blame]
hiroki9e1484d2018-12-07 09:36:49 -08001/*
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
Andrea Campanella3a361452019-08-02 10:17:53 +020021import com.google.common.annotations.VisibleForTesting;
hiroki9e1484d2018-12-07 09:36:49 -080022import com.google.common.collect.ImmutableList;
23import org.apache.commons.configuration.HierarchicalConfiguration;
24import org.apache.commons.configuration.XMLConfiguration;
25import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
26import org.onlab.packet.ChassisId;
27import org.onosproject.drivers.utilities.XmlConfigParser;
Andrea Campanella3ccee482019-08-02 19:15:18 +020028import org.onosproject.net.AnnotationKeys;
Andrea Campanella3a361452019-08-02 10:17:53 +020029import org.onosproject.net.ChannelSpacing;
hiroki9e1484d2018-12-07 09:36:49 -080030import org.onosproject.net.DefaultAnnotations;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
Andrea Campanella3a361452019-08-02 10:17:53 +020033import org.onosproject.net.OchSignal;
34import org.onosproject.net.OduSignalType;
hiroki9e1484d2018-12-07 09:36:49 -080035import org.onosproject.net.PortNumber;
36import org.onosproject.net.device.DefaultDeviceDescription;
hiroki9e1484d2018-12-07 09:36:49 -080037import org.onosproject.net.device.DeviceDescription;
38import org.onosproject.net.device.DeviceDescriptionDiscovery;
39import org.onosproject.net.device.PortDescription;
40import org.onosproject.net.driver.AbstractHandlerBehaviour;
Andrea Campanella3a361452019-08-02 10:17:53 +020041import org.onosproject.net.optical.device.OchPortHelper;
hiroki9e1484d2018-12-07 09:36:49 -080042import org.onosproject.netconf.NetconfController;
43import org.onosproject.netconf.NetconfDevice;
44import org.onosproject.netconf.NetconfSession;
45import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
46import org.slf4j.Logger;
47
48import java.util.HashMap;
49import java.util.List;
50import java.util.Map;
51import java.util.Objects;
52import java.util.concurrent.CompletableFuture;
hiroki9e1484d2018-12-07 09:36:49 -080053import java.util.stream.Collectors;
54
Andrea Campanella3a361452019-08-02 10:17:53 +020055import static com.google.common.base.Preconditions.checkNotNull;
hiroki9e1484d2018-12-07 09:36:49 -080056import static org.slf4j.LoggerFactory.getLogger;
57
58
59/**
60 * Driver Implementation of the DeviceDescrption discovery for OpenConfig
61 * terminal devices.
hiroki9e1484d2018-12-07 09:36:49 -080062 */
63public class CassiniTerminalDeviceDiscovery
Andrea Campanella3a361452019-08-02 10:17:53 +020064 extends AbstractHandlerBehaviour
65 implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
hiroki9e1484d2018-12-07 09:36:49 -080066
67 private static final String RPC_TAG_NETCONF_BASE =
Andrea Campanella3a361452019-08-02 10:17:53 +020068 "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
hiroki9e1484d2018-12-07 09:36:49 -080069
70 private static final String RPC_CLOSE_TAG = "</rpc>";
71
hiroki9e1484d2018-12-07 09:36:49 -080072 private static final String OC_TRANSPORT_TYPES_OPTICAL_CHANNEL =
Andrea Campanella3a361452019-08-02 10:17:53 +020073 "OPTICAL_CHANNEL";
hiroki9e1484d2018-12-07 09:36:49 -080074
75 private static final Logger log = getLogger(CassiniTerminalDeviceDiscovery.class);
76
77
78 /**
79 * Returns the NetconfSession with the device for which the method was called.
80 *
81 * @param deviceId device indetifier
hiroki9e1484d2018-12-07 09:36:49 -080082 * @return The netconf session or null
83 */
84 private NetconfSession getNetconfSession(DeviceId deviceId) {
85 NetconfController controller = handler().get(NetconfController.class);
86 NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
87 if (ncdev == null) {
88 log.trace("No netconf device, returning null session");
89 return null;
90 }
91 return ncdev.getSession();
92 }
93
94
95 /**
96 * Get the deviceId for which the methods apply.
97 *
98 * @return The deviceId as contained in the handler data
99 */
100 private DeviceId did() {
101 return handler().data().deviceId();
102 }
103
104
105 /**
106 * Construct a String with a Netconf filtered get RPC Message.
107 *
108 * @param filter A valid XML tree with the filter to apply in the get
109 * @return a String containing the RPC XML Document
110 */
111 private String filteredGetBuilder(String filter) {
112 StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
113 rpc.append("<get>");
114 rpc.append("<filter type='subtree'>");
115 rpc.append(filter);
116 rpc.append("</filter>");
117 rpc.append("</get>");
118 rpc.append(RPC_CLOSE_TAG);
119 return rpc.toString();
120 }
121
122
hiroki9e1484d2018-12-07 09:36:49 -0800123 @Override
124 public DeviceDescription discoverDeviceDetails() {
125 return new DefaultDeviceDescription(handler().data().deviceId().uri(),
Andrea Campanella3a361452019-08-02 10:17:53 +0200126 Device.Type.TERMINAL_DEVICE,
127 "EDGECORE",
128 "Cassini",
129 "OcNOS",
130 "",
131 new ChassisId("1"));
hiroki9e1484d2018-12-07 09:36:49 -0800132 }
133
134
hiroki9e1484d2018-12-07 09:36:49 -0800135 /**
136 * Returns a list of PortDescriptions for the device.
137 *
138 * @return a list of descriptions.
Andrea Campanella3a361452019-08-02 10:17:53 +0200139 * <p>
hiroki9e1484d2018-12-07 09:36:49 -0800140 * The RPC reply follows the following pattern:
141 * //CHECKSTYLE:OFF
142 * <pre>{@code
143 * <?xml version="1.0" encoding="UTF-8"?>
144 * <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
145 * <data>
146 * <components xmlns="http://openconfig.net/yang/platform">
147 * <component>....
148 * </component>
149 * <component>....
150 * </component>
151 * </components>
152 * </data>
153 * </rpc-reply>
154 * }</pre>
155 * //CHECKSTYLE:ON
156 */
157 @Override
158 public List<PortDescription> discoverPortDetails() {
159 try {
160 NetconfSession session = getNetconfSession(did());
161 if (session == null) {
162 log.error("discoverPortDetails called with null session for {}", did());
163 return ImmutableList.of();
164 }
Andrea Campanella3a361452019-08-02 10:17:53 +0200165 CompletableFuture<CharSequence> fut1 = session.asyncGet();
166 String rpcReplyTest = fut1.get().toString();
hiroki9e1484d2018-12-07 09:36:49 -0800167
Andrea Campanella3a361452019-08-02 10:17:53 +0200168 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReplyTest);
hiroki9e1484d2018-12-07 09:36:49 -0800169 xconf.setExpressionEngine(new XPathExpressionEngine());
170
Andrea Campanella3a361452019-08-02 10:17:53 +0200171 HierarchicalConfiguration logicalChannels = xconf.configurationAt("components");
172 return discoverPorts(logicalChannels);
hiroki9e1484d2018-12-07 09:36:49 -0800173 } catch (Exception e) {
174 log.error("Exception discoverPortDetails() {}", did(), e);
175 return ImmutableList.of();
176 }
177 }
178
hiroki9e1484d2018-12-07 09:36:49 -0800179 /**
Andrea Campanella3a361452019-08-02 10:17:53 +0200180 * Parses port information from OpenConfig XML configuration.
hiroki9e1484d2018-12-07 09:36:49 -0800181 *
Andrea Campanella3a361452019-08-02 10:17:53 +0200182 * @param cfg tree where the root node is {@literal <data>}
hiroki9e1484d2018-12-07 09:36:49 -0800183 * @return List of ports
hiroki9e1484d2018-12-07 09:36:49 -0800184 */
Andrea Campanella3a361452019-08-02 10:17:53 +0200185 @VisibleForTesting
186 private List<PortDescription> discoverPorts(HierarchicalConfiguration cfg) {
187 // If we want to use XPath
188 cfg.setExpressionEngine(new XPathExpressionEngine());
189 // converting components into PortDescription.
190 List<HierarchicalConfiguration> components = cfg.configurationsAt("component");
191 return components.stream()
192 .map(this::toPortDescriptionInternal)
193 .filter(Objects::nonNull)
194 .collect(Collectors.toList());
hiroki9e1484d2018-12-07 09:36:49 -0800195 }
196
Andrea Campanella3a361452019-08-02 10:17:53 +0200197 /**
198 * Converts Component subtree to PortDescription.
199 *
200 * @param component subtree to parse
201 * @return PortDescription or null if component is not an ONOS Port
202 */
203 private PortDescription toPortDescriptionInternal(HierarchicalConfiguration component) {
204 Map<String, String> annotations = new HashMap<>();
205 /*
206 <components xmlns="http://openconfig.net/yang/platform">
207 <component>
208 <name>oc1/0</name>
209 <config>
210 <name>oc1/0</name>
211 </config>
212 <state>
213 <name>oc1/0</name>
214 <type>OPTICAL_CHANNEL</type>
215 <id/>
216 <description/>
217 <mfg-name/>
218 <hardware-version/>
219 <firmware-version/>
220 <software-version/>
221 <serial-no/>
222 <part-no/>
223 <removable>true</removable>
224 <empty>false</empty>
225 <parent/>
226 <temperature>
227 <instant>0.0</instant>
228 <avg>0.0</avg>
229 <min>0.0</min>
230 <max>0.0</max>
231 <interval>0</interval>
232 <min-time>0</min-time>
233 <max-time>0</max-time>
234 <alarm-status>true</alarm-status>
235 <alarm-threshold>0</alarm-threshold>
236 </temperature>
237 <memory>
238 <available>0</available>
239 <utilized>0</utilized>
240 </memory>
241 <allocated-power>0</allocated-power>
242 <used-power>0</used-power>
243 </state>
244 <optical-channel xmlns="http://openconfig.net/yang/terminal-device">
245 <config>
246 <line-port>port-10101</line-port>
247 </config>
248 <state>
249 <output-power/>
250 <input-power/>
251 </state>
252 </optical-channel>
253 </component>
254 */
255 String name = component.getString("name");
256 String type = component.getString("state/type");
257 checkNotNull(name, "name not found");
258 checkNotNull(type, "state/type not found");
259 annotations.put(OdtnDeviceDescriptionDiscovery.OC_NAME, name);
260 annotations.put(OdtnDeviceDescriptionDiscovery.OC_TYPE, type);
hiroki9e1484d2018-12-07 09:36:49 -0800261
Andrea Campanella3a361452019-08-02 10:17:53 +0200262 //TODO this currently support only line-side ports through parsing of optical channels.
263 if (type.equals(OC_TRANSPORT_TYPES_OPTICAL_CHANNEL)) {
264 String portName = component.getString("optical-channel/config/line-port");
265 String portIndex = portName.split("-")[1];
Andrea Campanella3ccee482019-08-02 19:15:18 +0200266 annotations.putIfAbsent(AnnotationKeys.PORT_NAME, portName);
Andrea Campanella3a361452019-08-02 10:17:53 +0200267 annotations.putIfAbsent(PORT_TYPE, OdtnPortType.LINE.value());
268 annotations.putIfAbsent(ONOS_PORT_INDEX, portIndex);
269 annotations.putIfAbsent(CONNECTION_ID, "connection-" + portIndex);
hiroki9e1484d2018-12-07 09:36:49 -0800270
Andrea Campanella3a361452019-08-02 10:17:53 +0200271 OchSignal signalId = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 1);
272 return OchPortHelper.ochPortDescription(
273 PortNumber.portNumber(Long.parseLong(portIndex)),
274 true,
275 OduSignalType.ODU4, // TODO Client signal to be discovered
276 true,
277 signalId,
278 DefaultAnnotations.builder().putAll(annotations).build());
hiroki9e1484d2018-12-07 09:36:49 -0800279
Andrea Campanella3a361452019-08-02 10:17:53 +0200280 } else {
281 log.debug("Unknown port component type {}", type);
282 return null;
283 }
284 }
hiroki9e1484d2018-12-07 09:36:49 -0800285}