blob: da76b8689e1eadb4b5bcaa738ee69e9a94ebee03 [file] [log] [blame]
Thomas Vachuska9252bc32014-10-23 02:33:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska9252bc32014-10-23 02:33:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska9252bc32014-10-23 02:33:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska9252bc32014-10-23 02:33:25 -070015 */
Jonathan Hart9bb32ab2015-05-05 18:17:31 -070016package org.onosproject.rest.resources;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070017
18import com.fasterxml.jackson.databind.JsonNode;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070019import com.google.common.base.Strings;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070020import com.google.common.collect.Lists;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070021import com.google.common.collect.Maps;
Ayaka Koshibeae541732015-05-19 13:37:27 -070022
Thomas Vachuskac40d4632015-04-09 16:55:03 -070023import org.onlab.packet.ChassisId;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.MacAddress;
26import org.onlab.packet.VlanId;
Ayaka Koshibeae541732015-05-19 13:37:27 -070027import org.onlab.util.Frequency;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070028import org.onosproject.net.AnnotationKeys;
Ayaka Koshibeae541732015-05-19 13:37:27 -070029import org.onosproject.net.ChannelSpacing;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.Device;
33import org.onosproject.net.DeviceId;
Ayaka Koshibeae541732015-05-19 13:37:27 -070034import org.onosproject.net.GridType;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.Host;
36import org.onosproject.net.HostId;
37import org.onosproject.net.HostLocation;
38import org.onosproject.net.Link;
39import org.onosproject.net.MastershipRole;
Ayaka Koshibeae541732015-05-19 13:37:27 -070040import org.onosproject.net.OchPort;
41import org.onosproject.net.OchSignal;
42import org.onosproject.net.OduCltPort;
43import org.onosproject.net.OduSignalType;
44import org.onosproject.net.OmsPort;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.Port;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070046import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.SparseAnnotations;
48import org.onosproject.net.device.DefaultDeviceDescription;
49import org.onosproject.net.device.DefaultPortDescription;
50import org.onosproject.net.device.DeviceDescription;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070051import org.onosproject.net.device.DeviceEvent;
52import org.onosproject.net.device.DeviceListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.device.DeviceProvider;
54import org.onosproject.net.device.DeviceProviderRegistry;
55import org.onosproject.net.device.DeviceProviderService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070056import org.onosproject.net.device.DeviceService;
Ayaka Koshibeae541732015-05-19 13:37:27 -070057import org.onosproject.net.device.OchPortDescription;
58import org.onosproject.net.device.OduCltPortDescription;
59import org.onosproject.net.device.OmsPortDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.device.PortDescription;
61import org.onosproject.net.host.DefaultHostDescription;
62import org.onosproject.net.host.HostProvider;
63import org.onosproject.net.host.HostProviderRegistry;
64import org.onosproject.net.host.HostProviderService;
65import org.onosproject.net.link.DefaultLinkDescription;
66import org.onosproject.net.link.LinkProvider;
67import org.onosproject.net.link.LinkProviderRegistry;
68import org.onosproject.net.link.LinkProviderService;
69import org.onosproject.net.provider.ProviderId;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070070import org.slf4j.Logger;
71import org.slf4j.LoggerFactory;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070072
73import java.net.URI;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070074import java.util.ArrayList;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080075import java.util.HashSet;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070076import java.util.Iterator;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070077import java.util.List;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070078import java.util.Map;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080079import java.util.Set;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070080import java.util.concurrent.CountDownLatch;
81import java.util.concurrent.TimeUnit;
82import java.util.stream.Collectors;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070083
84import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorabafb502014-12-02 22:26:20 -080085import static org.onosproject.net.DeviceId.deviceId;
86import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070087import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
88import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070089
90/**
91 * Provider of devices and links parsed from a JSON configuration structure.
92 */
93class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
94
Thomas Vachuskac40d4632015-04-09 16:55:03 -070095 private final Logger log = LoggerFactory.getLogger(getClass());
96
Thomas Vachuska9252bc32014-10-23 02:33:25 -070097 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080098 new ProviderId("cfg", "org.onosproject.rest", true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -070099
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700100 private static final String UNKNOWN = "unknown";
101
Ayaka Koshibeae541732015-05-19 13:37:27 -0700102 private static final Frequency CENTER = Frequency.ofTHz(193.1);
103 // C-band has 4.4 THz (4,400 GHz) total bandwidth
104 private static final Frequency TOTAL = Frequency.ofTHz(4.4);
105
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700106 private CountDownLatch deviceLatch;
107
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700108 private final JsonNode cfg;
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700109 private final DeviceService deviceService;
110
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700111 private final DeviceProviderRegistry deviceProviderRegistry;
112 private final LinkProviderRegistry linkProviderRegistry;
113 private final HostProviderRegistry hostProviderRegistry;
114
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700115 private DeviceProviderService deviceProviderService;
116 private LinkProviderService linkProviderService;
117 private HostProviderService hostProviderService;
118
119 private DeviceListener deviceEventCounter = new DeviceEventCounter();
120 private List<ConnectPoint> connectPoints = Lists.newArrayList();
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700121 private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700122
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700123 /**
124 * Creates a new configuration provider.
125 *
126 * @param cfg JSON configuration
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700127 * @param deviceService device service
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700128 * @param deviceProviderRegistry device provider registry
129 * @param linkProviderRegistry link provider registry
130 * @param hostProviderRegistry host provider registry
131 */
132 ConfigProvider(JsonNode cfg,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700133 DeviceService deviceService,
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700134 DeviceProviderRegistry deviceProviderRegistry,
135 LinkProviderRegistry linkProviderRegistry,
136 HostProviderRegistry hostProviderRegistry) {
137 this.cfg = checkNotNull(cfg, "Configuration cannot be null");
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700138 this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700139 this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
140 this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
141 this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
142 }
143
144 /**
145 * Parses the given JSON and provides links as configured.
146 */
147 void parse() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700148 try {
149 register();
150 parseDevices();
151 parseLinks();
152 parseHosts();
153 addMissingPorts();
154 } finally {
155 unregister();
156 }
157 }
158
159 private void register() {
160 deviceProviderService = deviceProviderRegistry.register(this);
161 linkProviderService = linkProviderRegistry.register(this);
162 hostProviderService = hostProviderRegistry.register(this);
163 }
164
165 private void unregister() {
166 deviceProviderRegistry.unregister(this);
167 linkProviderRegistry.unregister(this);
168 hostProviderRegistry.unregister(this);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700169 }
170
171 // Parses the given JSON and provides devices.
172 private void parseDevices() {
173 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700174 JsonNode nodes = cfg.get("devices");
175 if (nodes != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700176 prepareForDeviceEvents(nodes.size());
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700177 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700178 parseDevice(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700179
180 // FIXME: hack to make sure device attributes take
181 // This will be fixed when GossipDeviceStore uses ECM
182 parseDevice(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700183 }
184 }
185 } finally {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700186 waitForDeviceEvents();
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700187 }
188 }
189
190 // Parses the given node with device data and supplies the device.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700191 private void parseDevice(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700192 URI uri = URI.create(get(node, "uri"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700193 Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
194 String mfr = get(node, "mfr", UNKNOWN);
195 String hw = get(node, "hw", UNKNOWN);
196 String sw = get(node, "sw", UNKNOWN);
197 String serial = get(node, "serial", UNKNOWN);
198 ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700199 SparseAnnotations annotations = annotations(node.get("annotations"));
200
201 DeviceDescription desc =
202 new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
203 cid, annotations);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700204 DeviceId deviceId = deviceId(uri);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700205 deviceProviderService.deviceConnected(deviceId, desc);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700206
207 JsonNode ports = node.get("ports");
208 if (ports != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700209 parsePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700210 }
211 }
212
213 // Parses the given node with list of device ports.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700214 private void parsePorts(DeviceId deviceId, JsonNode nodes) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700215 List<PortDescription> ports = new ArrayList<>();
216 for (JsonNode node : nodes) {
217 ports.add(parsePort(node));
218 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700219 deviceProviderService.updatePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700220 }
221
222 // Parses the given node with port information.
223 private PortDescription parsePort(JsonNode node) {
224 Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700225 PortNumber port = portNumber(node.path("port").asLong(0));
226 String portName = Strings.emptyToNull(port.name());
227 SparseAnnotations annotations = null;
228 if (portName != null) {
229 annotations = DefaultAnnotations.builder()
230 .set(AnnotationKeys.PORT_NAME, portName).build();
231 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700232 switch (type) {
233 case COPPER:
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700234 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
235 type, node.path("speed").asLong(1_000),
236 annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700237 case FIBER:
238 // Currently, assume OMS when FIBER. Provide sane defaults.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700239 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
240 CENTER, CENTER.add(TOTAL),
241 Frequency.ofGHz(100), annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700242 default:
243 log.warn("{}: Unsupported Port Type");
244 }
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700245 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
246 type, node.path("speed").asLong(1_000),
247 annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700248 }
249
250 // Parses the given JSON and provides links as configured.
251 private void parseLinks() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700252 JsonNode nodes = cfg.get("links");
253 if (nodes != null) {
254 for (JsonNode node : nodes) {
255 parseLink(node, false);
256 if (!node.has("halfplex")) {
257 parseLink(node, true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700258 }
259 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700260 }
261 }
262
263 // Parses the given node with link data and supplies the link.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700264 private void parseLink(JsonNode node, boolean reverse) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700265 ConnectPoint src = connectPoint(get(node, "src"));
266 ConnectPoint dst = connectPoint(get(node, "dst"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700267 Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700268 SparseAnnotations annotations = annotations(node.get("annotations"));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700269 // take annotations to update optical ports with correct attributes.
270 updatePorts(src, dst, annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700271 DefaultLinkDescription desc = reverse ?
272 new DefaultLinkDescription(dst, src, type, annotations) :
273 new DefaultLinkDescription(src, dst, type, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700274 linkProviderService.linkDetected(desc);
275
276 connectPoints.add(src);
277 connectPoints.add(dst);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700278 }
279
Ayaka Koshibeae541732015-05-19 13:37:27 -0700280 private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
Ayaka Koshibe91006242015-06-03 12:09:16 -0700281 if (annotations == null) {
282 return;
283 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700284 final String linkType = annotations.value("optical.type");
285 if ("cross-connect".equals(linkType)) {
286 String value = annotations.value("bandwidth").trim();
287 try {
288 double bw = Double.parseDouble(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700289 updateOchPort(bw, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700290 } catch (NumberFormatException e) {
291 log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
292 return;
293 }
294 } else if ("WDM".equals(linkType)) {
295 String value = annotations.value("optical.waves").trim();
296 try {
297 int numChls = Integer.parseInt(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700298 updateOMSPorts(numChls, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700299 } catch (NumberFormatException e) {
300 log.warn("Invalid channel ({}), can't configure port(s)", value);
301 return;
302 }
303 }
304 }
305
306 // uses 'bandwidth' annotation to determine the channel spacing.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700307 private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
308 Device src = deviceService.getDevice(srcCp.deviceId());
309 Device dst = deviceService.getDevice(dstCp.deviceId());
310 // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
Ayaka Koshibeae541732015-05-19 13:37:27 -0700311 Frequency spacing = Frequency.ofMHz(bw);
312 // channel bandwidth is smaller than smallest standard channel spacing.
313 ChannelSpacing chsp = null;
314 if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
315 chsp = ChannelSpacing.CHL_6P25GHZ;
316 }
317 for (int i = 1; i < ChannelSpacing.values().length; i++) {
318 Frequency val = ChannelSpacing.values()[i].frequency();
319 // pick the next highest or equal channel interval.
320 if (val.isLessThan(spacing)) {
321 chsp = ChannelSpacing.values()[i - 1];
322 break;
323 }
324 }
325 if (chsp == null) {
326 log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
327 return;
328 }
329 OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
330 if (src.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700331 PortDescription portDesc = new OchPortDescription(srcCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700332 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700333 descriptions.put(srcCp, portDesc);
334 deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700335 }
336 if (dst.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700337 PortDescription portDesc = new OchPortDescription(dstCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700338 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700339 descriptions.put(dstCp, portDesc);
340 deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700341 }
342 }
343
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700344 private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
345 // round down to largest slot that allows numChl channels to fit into C band range
Ayaka Koshibeae541732015-05-19 13:37:27 -0700346 ChannelSpacing chl = null;
347 Frequency perChl = TOTAL.floorDivision(numChls);
348 for (int i = 0; i < ChannelSpacing.values().length; i++) {
349 Frequency val = ChannelSpacing.values()[i].frequency();
350 if (val.isLessThan(perChl)) {
351 chl = ChannelSpacing.values()[i];
352 break;
353 }
354 }
355 if (chl == null) {
356 chl = ChannelSpacing.CHL_6P25GHZ;
357 }
358
359 // if true, there was less channels than can be tightly packed.
360 Frequency grid = (chl == null) ? Frequency.ofGHz(100) : chl.frequency();
361 // say Linc's 1st slot starts at CENTER and goes up from there.
362 Frequency min = CENTER.add(grid);
363 Frequency max = CENTER.add(grid.multiply(numChls));
364
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700365 PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
366 PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
367 descriptions.put(srcCp, srcPortDesc);
368 descriptions.put(dstCp, dstPortDesc);
369 deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
370 deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700371 }
372
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700373 // Parses the given JSON and provides hosts as configured.
374 private void parseHosts() {
375 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700376 JsonNode nodes = cfg.get("hosts");
377 if (nodes != null) {
378 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700379 parseHost(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700380
381 // FIXME: hack to make sure host attributes take
382 // This will be fixed when GossipHostStore uses ECM
383 parseHost(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700384 }
385 }
386 } finally {
387 hostProviderRegistry.unregister(this);
388 }
389 }
390
391 // Parses the given node with host data and supplies the host.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700392 private void parseHost(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700393 MacAddress mac = MacAddress.valueOf(get(node, "mac"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700394 VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700395 HostId hostId = HostId.hostId(mac, vlanId);
396 SparseAnnotations annotations = annotations(node.get("annotations"));
397 HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800398
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700399 String[] ipStrings = get(node, "ip", "").split(",");
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800400 Set<IpAddress> ips = new HashSet<>();
401 for (String ip : ipStrings) {
402 ips.add(IpAddress.valueOf(ip.trim()));
403 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700404
405 DefaultHostDescription desc =
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800406 new DefaultHostDescription(mac, vlanId, location, ips, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700407 hostProviderService.hostDetected(hostId, desc);
408
409 connectPoints.add(location);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700410 }
411
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700412 // Adds any missing device ports for configured links and host locations.
413 private void addMissingPorts() {
414 deviceService.getDevices().forEach(this::addMissingPorts);
415 }
416
417 // Adds any missing device ports.
418 private void addMissingPorts(Device device) {
419 List<Port> ports = deviceService.getPorts(device.id());
420 Set<ConnectPoint> existing = ports.stream()
421 .map(p -> new ConnectPoint(device.id(), p.number()))
422 .collect(Collectors.toSet());
423 Set<ConnectPoint> missing = connectPoints.stream()
424 .filter(cp -> !existing.contains(cp))
425 .collect(Collectors.toSet());
426
427 if (!missing.isEmpty()) {
428 List<PortDescription> newPorts = Lists.newArrayList();
429 ports.forEach(p -> newPorts.add(description(p)));
430 missing.forEach(cp -> newPorts.add(description(cp)));
431 deviceProviderService.updatePorts(device.id(), newPorts);
432 }
433 }
434
435 // Creates a port description from the specified port.
436 private PortDescription description(Port p) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700437 switch (p.type()) {
438 case OMS:
439 OmsPort op = (OmsPort) p;
440 return new OmsPortDescription(
441 op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
442 case OCH:
443 OchPort ochp = (OchPort) p;
444 return new OchPortDescription(
445 ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
446 case ODUCLT:
447 OduCltPort odup = (OduCltPort) p;
448 return new OduCltPortDescription(
449 odup.number(), odup.isEnabled(), odup.signalType());
450 default:
451 return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
452 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700453 }
454
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700455 // Creates a port description from the specified connection point if none created earlier.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700456 private PortDescription description(ConnectPoint cp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700457 PortDescription saved = descriptions.get(cp);
458 if (saved != null) {
459 return saved;
460 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700461 Port p = deviceService.getPort(cp.deviceId(), cp.port());
462 if (p == null) {
463 return new DefaultPortDescription(cp.port(), true);
464 }
465 return description(p);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700466 }
467
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700468 // Produces set of annotations from the given JSON node.
469 private SparseAnnotations annotations(JsonNode node) {
470 if (node == null) {
471 return null;
472 }
473
474 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
475 Iterator<String> it = node.fieldNames();
476 while (it.hasNext()) {
477 String k = it.next();
478 builder.set(k, node.get(k).asText());
479 }
480 return builder.build();
481 }
482
483 // Produces a connection point from the specified uri/port text.
484 private ConnectPoint connectPoint(String text) {
485 int i = text.lastIndexOf("/");
486 return new ConnectPoint(deviceId(text.substring(0, i)),
487 portNumber(text.substring(i + 1)));
488 }
489
490 // Returns string form of the named property in the given JSON object.
491 private String get(JsonNode node, String name) {
492 return node.path(name).asText();
493 }
494
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700495 // Returns string form of the named property in the given JSON object.
496 private String get(JsonNode node, String name, String defaultValue) {
497 return node.path(name).asText(defaultValue);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700498 }
499
500 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700501 public void roleChanged(DeviceId device, MastershipRole newRole) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700502 deviceProviderService.receivedRoleReply(device, newRole, newRole);
503 }
504
505 @Override
506 public void triggerProbe(DeviceId deviceId) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700507 }
508
509 @Override
510 public void triggerProbe(Host host) {
511 }
512
513 @Override
514 public ProviderId id() {
515 return PID;
516 }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700517
518 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700519 public boolean isReachable(DeviceId device) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700520 return true;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700521 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700522
523 /**
524 * Prepares to count device added/available/removed events.
525 *
526 * @param count number of events to count
527 */
528 protected void prepareForDeviceEvents(int count) {
529 deviceLatch = new CountDownLatch(count);
530 deviceService.addListener(deviceEventCounter);
531 }
532
533 /**
534 * Waits for all expected device added/available/removed events.
535 */
536 protected void waitForDeviceEvents() {
537 try {
538 deviceLatch.await(2, TimeUnit.SECONDS);
539 } catch (InterruptedException e) {
540 log.warn("Device events did not arrive in time");
541 }
542 deviceService.removeListener(deviceEventCounter);
543 }
544
545 // Counts down number of device added/available/removed events.
546 private class DeviceEventCounter implements DeviceListener {
547 @Override
548 public void event(DeviceEvent event) {
549 DeviceEvent.Type type = event.type();
550 if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
551 deviceLatch.countDown();
552 }
553 }
554 }
555
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700556}