blob: 2775c2e986be7d2dcf61165ffa1521ee900895da [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;
Sho SHIMIZU70e76d82016-01-05 16:26:26 -080028import org.onlab.util.Spectrum;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070029import org.onosproject.net.AnnotationKeys;
Ayaka Koshibeae541732015-05-19 13:37:27 -070030import org.onosproject.net.ChannelSpacing;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.net.ConnectPoint;
32import org.onosproject.net.DefaultAnnotations;
33import org.onosproject.net.Device;
34import org.onosproject.net.DeviceId;
Ayaka Koshibeae541732015-05-19 13:37:27 -070035import org.onosproject.net.GridType;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.Host;
37import org.onosproject.net.HostId;
38import org.onosproject.net.HostLocation;
39import org.onosproject.net.Link;
40import org.onosproject.net.MastershipRole;
Ayaka Koshibeae541732015-05-19 13:37:27 -070041import org.onosproject.net.OchPort;
42import org.onosproject.net.OchSignal;
43import org.onosproject.net.OduCltPort;
44import org.onosproject.net.OduSignalType;
45import org.onosproject.net.OmsPort;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.Port;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070047import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.SparseAnnotations;
49import org.onosproject.net.device.DefaultDeviceDescription;
50import org.onosproject.net.device.DefaultPortDescription;
51import org.onosproject.net.device.DeviceDescription;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070052import org.onosproject.net.device.DeviceEvent;
53import org.onosproject.net.device.DeviceListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.device.DeviceProvider;
55import org.onosproject.net.device.DeviceProviderRegistry;
56import org.onosproject.net.device.DeviceProviderService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070057import org.onosproject.net.device.DeviceService;
Ayaka Koshibeae541732015-05-19 13:37:27 -070058import org.onosproject.net.device.OchPortDescription;
59import org.onosproject.net.device.OduCltPortDescription;
60import org.onosproject.net.device.OmsPortDescription;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.device.PortDescription;
62import org.onosproject.net.host.DefaultHostDescription;
63import org.onosproject.net.host.HostProvider;
64import org.onosproject.net.host.HostProviderRegistry;
65import org.onosproject.net.host.HostProviderService;
66import org.onosproject.net.link.DefaultLinkDescription;
67import org.onosproject.net.link.LinkProvider;
68import org.onosproject.net.link.LinkProviderRegistry;
69import org.onosproject.net.link.LinkProviderService;
70import org.onosproject.net.provider.ProviderId;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070071import org.slf4j.Logger;
72import org.slf4j.LoggerFactory;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070073
74import java.net.URI;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070075import java.util.ArrayList;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080076import java.util.HashSet;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070077import java.util.Iterator;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070078import java.util.List;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070079import java.util.Map;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080080import java.util.Set;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070081import java.util.concurrent.CountDownLatch;
82import java.util.concurrent.TimeUnit;
83import java.util.stream.Collectors;
Sho SHIMIZUbb825d72015-06-11 10:56:32 -070084import java.util.stream.Stream;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070085
86import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorabafb502014-12-02 22:26:20 -080087import static org.onosproject.net.DeviceId.deviceId;
88import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070089import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
90import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070091
92/**
93 * Provider of devices and links parsed from a JSON configuration structure.
Thomas Vachuskaf9879c52016-01-15 11:12:19 -080094 *
95 * @deprecated in 1.5.0 (Falcon)
Thomas Vachuska9252bc32014-10-23 02:33:25 -070096 */
Thomas Vachuskaf9879c52016-01-15 11:12:19 -080097@Deprecated
Thomas Vachuska9252bc32014-10-23 02:33:25 -070098class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
99
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700100 private final Logger log = LoggerFactory.getLogger(getClass());
101
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700102 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -0800103 new ProviderId("cfg", "org.onosproject.rest", true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700104
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700105 private static final String UNKNOWN = "unknown";
106
Ayaka Koshibeae541732015-05-19 13:37:27 -0700107 // C-band has 4.4 THz (4,400 GHz) total bandwidth
Sho SHIMIZU00762ee2016-01-05 16:32:24 -0800108 private static final Frequency TOTAL = Frequency.ofGHz(4_400);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700109
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700110 private CountDownLatch deviceLatch;
111
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700112 private final JsonNode cfg;
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700113 private final DeviceService deviceService;
114
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700115 private final DeviceProviderRegistry deviceProviderRegistry;
116 private final LinkProviderRegistry linkProviderRegistry;
117 private final HostProviderRegistry hostProviderRegistry;
118
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700119 private DeviceProviderService deviceProviderService;
120 private LinkProviderService linkProviderService;
121 private HostProviderService hostProviderService;
122
123 private DeviceListener deviceEventCounter = new DeviceEventCounter();
124 private List<ConnectPoint> connectPoints = Lists.newArrayList();
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700125 private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700126
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700127 /**
128 * Creates a new configuration provider.
129 *
130 * @param cfg JSON configuration
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700131 * @param deviceService device service
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700132 * @param deviceProviderRegistry device provider registry
133 * @param linkProviderRegistry link provider registry
134 * @param hostProviderRegistry host provider registry
135 */
136 ConfigProvider(JsonNode cfg,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700137 DeviceService deviceService,
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700138 DeviceProviderRegistry deviceProviderRegistry,
139 LinkProviderRegistry linkProviderRegistry,
140 HostProviderRegistry hostProviderRegistry) {
141 this.cfg = checkNotNull(cfg, "Configuration cannot be null");
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700142 this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700143 this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
144 this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
145 this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
146 }
147
148 /**
149 * Parses the given JSON and provides links as configured.
150 */
151 void parse() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700152 try {
153 register();
154 parseDevices();
155 parseLinks();
156 parseHosts();
157 addMissingPorts();
158 } finally {
159 unregister();
160 }
161 }
162
163 private void register() {
164 deviceProviderService = deviceProviderRegistry.register(this);
165 linkProviderService = linkProviderRegistry.register(this);
166 hostProviderService = hostProviderRegistry.register(this);
167 }
168
169 private void unregister() {
170 deviceProviderRegistry.unregister(this);
171 linkProviderRegistry.unregister(this);
172 hostProviderRegistry.unregister(this);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700173 }
174
175 // Parses the given JSON and provides devices.
176 private void parseDevices() {
177 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700178 JsonNode nodes = cfg.get("devices");
179 if (nodes != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700180 prepareForDeviceEvents(nodes.size());
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700181 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700182 parseDevice(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700183
184 // FIXME: hack to make sure device attributes take
185 // This will be fixed when GossipDeviceStore uses ECM
186 parseDevice(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700187 }
188 }
189 } finally {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700190 waitForDeviceEvents();
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700191 }
192 }
193
194 // Parses the given node with device data and supplies the device.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700195 private void parseDevice(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700196 URI uri = URI.create(get(node, "uri"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700197 Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
198 String mfr = get(node, "mfr", UNKNOWN);
199 String hw = get(node, "hw", UNKNOWN);
200 String sw = get(node, "sw", UNKNOWN);
201 String serial = get(node, "serial", UNKNOWN);
202 ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700203 SparseAnnotations annotations = annotations(node.get("annotations"));
204
205 DeviceDescription desc =
206 new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
207 cid, annotations);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700208 DeviceId deviceId = deviceId(uri);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700209 deviceProviderService.deviceConnected(deviceId, desc);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700210
211 JsonNode ports = node.get("ports");
212 if (ports != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700213 parsePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700214 }
215 }
216
217 // Parses the given node with list of device ports.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700218 private void parsePorts(DeviceId deviceId, JsonNode nodes) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700219 List<PortDescription> ports = new ArrayList<>();
220 for (JsonNode node : nodes) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700221 ports.add(parsePort(deviceId, node));
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700222 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700223 deviceProviderService.updatePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700224 }
225
226 // Parses the given node with port information.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700227 private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700228 Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
Marc De Leenheer723f5532015-06-03 20:16:17 -0700229 // TL1-based ports have a name
230 PortNumber port = null;
231 if (node.has("name")) {
232 for (Port p : deviceService.getPorts(deviceId)) {
233 if (p.number().name().equals(node.get("name").asText())) {
234 port = p.number();
235 break;
236 }
237 }
238 } else {
239 port = portNumber(node.path("port").asLong(0));
240 }
241
Marc De Leenheerc9733082015-06-04 12:22:38 -0700242 if (port == null) {
243 log.error("Cannot find port given in node {}", node);
244 return null;
245 }
246
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700247 String portName = Strings.emptyToNull(port.name());
248 SparseAnnotations annotations = null;
249 if (portName != null) {
250 annotations = DefaultAnnotations.builder()
251 .set(AnnotationKeys.PORT_NAME, portName).build();
252 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700253 switch (type) {
254 case COPPER:
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700255 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
256 type, node.path("speed").asLong(1_000),
257 annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700258 case FIBER:
259 // Currently, assume OMS when FIBER. Provide sane defaults.
Marc De Leenheerfc913dd2015-07-30 16:04:55 -0700260 annotations = annotations(node.get("annotations"));
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700261 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
Sho SHIMIZU70e76d82016-01-05 16:26:26 -0800262 Spectrum.CENTER_FREQUENCY, Spectrum.CENTER_FREQUENCY.add(TOTAL),
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700263 Frequency.ofGHz(100), annotations);
Marc De Leenheer723f5532015-06-03 20:16:17 -0700264 case ODUCLT:
265 annotations = annotations(node.get("annotations"));
266 OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
267 return new OduCltPortDescription(port, node.path("enabled").asBoolean(true),
268 oduCltPort.signalType(), annotations);
269 case OCH:
270 annotations = annotations(node.get("annotations"));
271 OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
272 return new OchPortDescription(port, node.path("enabled").asBoolean(true),
273 ochPort.signalType(), ochPort.isTunable(),
274 ochPort.lambda(), annotations);
275 case OMS:
276 annotations = annotations(node.get("annotations"));
277 OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
278 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
279 omsPort.minFrequency(), omsPort.maxFrequency(), omsPort.grid(), annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700280 default:
281 log.warn("{}: Unsupported Port Type");
282 }
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700283 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
284 type, node.path("speed").asLong(1_000),
285 annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700286 }
287
288 // Parses the given JSON and provides links as configured.
289 private void parseLinks() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700290 JsonNode nodes = cfg.get("links");
291 if (nodes != null) {
292 for (JsonNode node : nodes) {
293 parseLink(node, false);
294 if (!node.has("halfplex")) {
295 parseLink(node, true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700296 }
297 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700298 }
299 }
300
301 // Parses the given node with link data and supplies the link.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700302 private void parseLink(JsonNode node, boolean reverse) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700303 ConnectPoint src = connectPoint(get(node, "src"));
304 ConnectPoint dst = connectPoint(get(node, "dst"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700305 Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700306 SparseAnnotations annotations = annotations(node.get("annotations"));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700307 // take annotations to update optical ports with correct attributes.
308 updatePorts(src, dst, annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700309 DefaultLinkDescription desc = reverse ?
310 new DefaultLinkDescription(dst, src, type, annotations) :
311 new DefaultLinkDescription(src, dst, type, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700312 linkProviderService.linkDetected(desc);
313
314 connectPoints.add(src);
315 connectPoints.add(dst);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700316 }
317
Ayaka Koshibeae541732015-05-19 13:37:27 -0700318 private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700319 final String linkType = annotations.value("optical.type");
320 if ("cross-connect".equals(linkType)) {
321 String value = annotations.value("bandwidth").trim();
322 try {
323 double bw = Double.parseDouble(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700324 updateOchPort(bw, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700325 } catch (NumberFormatException e) {
326 log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
327 return;
328 }
329 } else if ("WDM".equals(linkType)) {
330 String value = annotations.value("optical.waves").trim();
331 try {
332 int numChls = Integer.parseInt(value);
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800333 updateOmsPorts(numChls, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700334 } catch (NumberFormatException e) {
335 log.warn("Invalid channel ({}), can't configure port(s)", value);
336 return;
337 }
338 }
339 }
340
341 // uses 'bandwidth' annotation to determine the channel spacing.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700342 private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
343 Device src = deviceService.getDevice(srcCp.deviceId());
344 Device dst = deviceService.getDevice(dstCp.deviceId());
345 // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
Ayaka Koshibeae541732015-05-19 13:37:27 -0700346 Frequency spacing = Frequency.ofMHz(bw);
347 // channel bandwidth is smaller than smallest standard channel spacing.
348 ChannelSpacing chsp = null;
349 if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
350 chsp = ChannelSpacing.CHL_6P25GHZ;
351 }
352 for (int i = 1; i < ChannelSpacing.values().length; i++) {
353 Frequency val = ChannelSpacing.values()[i].frequency();
354 // pick the next highest or equal channel interval.
355 if (val.isLessThan(spacing)) {
356 chsp = ChannelSpacing.values()[i - 1];
357 break;
358 }
359 }
360 if (chsp == null) {
361 log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
362 return;
363 }
364 OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
365 if (src.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700366 PortDescription portDesc = new OchPortDescription(srcCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700367 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700368 descriptions.put(srcCp, portDesc);
369 deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700370 }
371 if (dst.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700372 PortDescription portDesc = new OchPortDescription(dstCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700373 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700374 descriptions.put(dstCp, portDesc);
375 deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700376 }
377 }
378
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800379 private void updateOmsPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700380 // round down to largest slot that allows numChl channels to fit into C band range
Ayaka Koshibeae541732015-05-19 13:37:27 -0700381 ChannelSpacing chl = null;
382 Frequency perChl = TOTAL.floorDivision(numChls);
383 for (int i = 0; i < ChannelSpacing.values().length; i++) {
384 Frequency val = ChannelSpacing.values()[i].frequency();
385 if (val.isLessThan(perChl)) {
386 chl = ChannelSpacing.values()[i];
387 break;
388 }
389 }
390 if (chl == null) {
391 chl = ChannelSpacing.CHL_6P25GHZ;
392 }
393
394 // if true, there was less channels than can be tightly packed.
Sho SHIMIZUf6501c02015-06-10 19:30:24 -0700395 Frequency grid = chl.frequency();
Ayaka Koshibeae541732015-05-19 13:37:27 -0700396 // say Linc's 1st slot starts at CENTER and goes up from there.
Sho SHIMIZU70e76d82016-01-05 16:26:26 -0800397 Frequency min = Spectrum.CENTER_FREQUENCY.add(grid);
398 Frequency max = Spectrum.CENTER_FREQUENCY.add(grid.multiply(numChls));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700399
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700400 PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
401 PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
402 descriptions.put(srcCp, srcPortDesc);
403 descriptions.put(dstCp, dstPortDesc);
404 deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
405 deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700406 }
407
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700408 // Parses the given JSON and provides hosts as configured.
409 private void parseHosts() {
410 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700411 JsonNode nodes = cfg.get("hosts");
412 if (nodes != null) {
413 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700414 parseHost(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700415
416 // FIXME: hack to make sure host attributes take
417 // This will be fixed when GossipHostStore uses ECM
418 parseHost(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700419 }
420 }
421 } finally {
422 hostProviderRegistry.unregister(this);
423 }
424 }
425
426 // Parses the given node with host data and supplies the host.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700427 private void parseHost(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700428 MacAddress mac = MacAddress.valueOf(get(node, "mac"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700429 VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700430 HostId hostId = HostId.hostId(mac, vlanId);
431 SparseAnnotations annotations = annotations(node.get("annotations"));
432 HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800433
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700434 String[] ipStrings = get(node, "ip", "").split(",");
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800435 Set<IpAddress> ips = new HashSet<>();
436 for (String ip : ipStrings) {
437 ips.add(IpAddress.valueOf(ip.trim()));
438 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700439
440 DefaultHostDescription desc =
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800441 new DefaultHostDescription(mac, vlanId, location, ips, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700442 hostProviderService.hostDetected(hostId, desc);
443
444 connectPoints.add(location);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700445 }
446
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700447 // Adds any missing device ports for configured links and host locations.
448 private void addMissingPorts() {
449 deviceService.getDevices().forEach(this::addMissingPorts);
450 }
451
452 // Adds any missing device ports.
453 private void addMissingPorts(Device device) {
Jonathan Harteb431c12015-07-27 10:43:50 -0700454 try {
455 List<Port> ports = deviceService.getPorts(device.id());
456 Set<ConnectPoint> existing = ports.stream()
457 .map(p -> new ConnectPoint(device.id(), p.number()))
458 .collect(Collectors.toSet());
459 Set<ConnectPoint> missing = connectPoints.stream()
460 .filter(cp -> cp.deviceId().equals(device.id()))
461 .filter(cp -> !existing.contains(cp))
462 .collect(Collectors.toSet());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700463
Jonathan Harteb431c12015-07-27 10:43:50 -0700464 if (!missing.isEmpty()) {
465 List<PortDescription> newPorts = Stream.concat(
466 ports.stream().map(this::description),
467 missing.stream().map(this::description)
468 ).collect(Collectors.toList());
469 deviceProviderService.updatePorts(device.id(), newPorts);
470 }
471 } catch (IllegalArgumentException e) {
472 log.warn("Error pushing ports: {}", e.getMessage());
473 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700474 }
475
476 // Creates a port description from the specified port.
477 private PortDescription description(Port p) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700478 switch (p.type()) {
479 case OMS:
480 OmsPort op = (OmsPort) p;
481 return new OmsPortDescription(
482 op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
483 case OCH:
484 OchPort ochp = (OchPort) p;
485 return new OchPortDescription(
486 ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
487 case ODUCLT:
488 OduCltPort odup = (OduCltPort) p;
489 return new OduCltPortDescription(
490 odup.number(), odup.isEnabled(), odup.signalType());
491 default:
492 return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
493 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700494 }
495
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700496 // Creates a port description from the specified connection point if none created earlier.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700497 private PortDescription description(ConnectPoint cp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700498 PortDescription saved = descriptions.get(cp);
499 if (saved != null) {
500 return saved;
501 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700502 Port p = deviceService.getPort(cp.deviceId(), cp.port());
503 if (p == null) {
504 return new DefaultPortDescription(cp.port(), true);
505 }
506 return description(p);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700507 }
508
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700509 // Produces set of annotations from the given JSON node.
510 private SparseAnnotations annotations(JsonNode node) {
511 if (node == null) {
Sho SHIMIZUa3d67cd2015-06-04 11:46:48 -0700512 return DefaultAnnotations.EMPTY;
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700513 }
514
515 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
516 Iterator<String> it = node.fieldNames();
517 while (it.hasNext()) {
518 String k = it.next();
519 builder.set(k, node.get(k).asText());
520 }
521 return builder.build();
522 }
523
524 // Produces a connection point from the specified uri/port text.
525 private ConnectPoint connectPoint(String text) {
526 int i = text.lastIndexOf("/");
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700527 String portName = text.substring(i + 1);
528 DeviceId deviceId = deviceId(text.substring(0, i));
529
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700530 for (Port port : deviceService.getPorts(deviceId)) {
531 PortNumber pn = port.number();
532 if (pn.name().equals(portName)) {
533 return new ConnectPoint(deviceId, pn);
534 }
535 }
Sho SHIMIZUccf40c72015-06-11 16:55:20 -0700536
537 long portNum;
538 try {
539 portNum = Long.parseLong(portName);
540 } catch (NumberFormatException e) {
541 portNum = 0;
542 }
543
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700544 return new ConnectPoint(deviceId, portNumber(portNum, portName));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700545 }
546
547 // Returns string form of the named property in the given JSON object.
548 private String get(JsonNode node, String name) {
549 return node.path(name).asText();
550 }
551
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700552 // Returns string form of the named property in the given JSON object.
553 private String get(JsonNode node, String name, String defaultValue) {
554 return node.path(name).asText(defaultValue);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700555 }
556
557 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700558 public void roleChanged(DeviceId device, MastershipRole newRole) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700559 deviceProviderService.receivedRoleReply(device, newRole, newRole);
560 }
561
562 @Override
563 public void triggerProbe(DeviceId deviceId) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700564 }
565
566 @Override
567 public void triggerProbe(Host host) {
568 }
569
570 @Override
571 public ProviderId id() {
572 return PID;
573 }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700574
575 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700576 public boolean isReachable(DeviceId device) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700577 return true;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700578 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700579
580 /**
581 * Prepares to count device added/available/removed events.
582 *
583 * @param count number of events to count
584 */
585 protected void prepareForDeviceEvents(int count) {
586 deviceLatch = new CountDownLatch(count);
587 deviceService.addListener(deviceEventCounter);
588 }
589
590 /**
591 * Waits for all expected device added/available/removed events.
592 */
593 protected void waitForDeviceEvents() {
594 try {
595 deviceLatch.await(2, TimeUnit.SECONDS);
596 } catch (InterruptedException e) {
597 log.warn("Device events did not arrive in time");
598 }
599 deviceService.removeListener(deviceEventCounter);
600 }
601
602 // Counts down number of device added/available/removed events.
603 private class DeviceEventCounter implements DeviceListener {
604 @Override
605 public void event(DeviceEvent event) {
606 DeviceEvent.Type type = event.type();
607 if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
608 deviceLatch.countDown();
609 }
610 }
611 }
612
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700613}