blob: cc05bffc3df1700a5236418a8172b831638aaa26 [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;
Sho SHIMIZUbb825d72015-06-11 10:56:32 -070083import java.util.stream.Stream;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070084
85import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorabafb502014-12-02 22:26:20 -080086import static org.onosproject.net.DeviceId.deviceId;
87import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070088import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
89import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070090
91/**
92 * Provider of devices and links parsed from a JSON configuration structure.
93 */
94class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
95
Thomas Vachuskac40d4632015-04-09 16:55:03 -070096 private final Logger log = LoggerFactory.getLogger(getClass());
97
Thomas Vachuska9252bc32014-10-23 02:33:25 -070098 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080099 new ProviderId("cfg", "org.onosproject.rest", true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700100
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700101 private static final String UNKNOWN = "unknown";
102
Ayaka Koshibeae541732015-05-19 13:37:27 -0700103 private static final Frequency CENTER = Frequency.ofTHz(193.1);
104 // C-band has 4.4 THz (4,400 GHz) total bandwidth
105 private static final Frequency TOTAL = Frequency.ofTHz(4.4);
106
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700107 private CountDownLatch deviceLatch;
108
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700109 private final JsonNode cfg;
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700110 private final DeviceService deviceService;
111
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700112 private final DeviceProviderRegistry deviceProviderRegistry;
113 private final LinkProviderRegistry linkProviderRegistry;
114 private final HostProviderRegistry hostProviderRegistry;
115
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700116 private DeviceProviderService deviceProviderService;
117 private LinkProviderService linkProviderService;
118 private HostProviderService hostProviderService;
119
120 private DeviceListener deviceEventCounter = new DeviceEventCounter();
121 private List<ConnectPoint> connectPoints = Lists.newArrayList();
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700122 private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700123
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700124 /**
125 * Creates a new configuration provider.
126 *
127 * @param cfg JSON configuration
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700128 * @param deviceService device service
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700129 * @param deviceProviderRegistry device provider registry
130 * @param linkProviderRegistry link provider registry
131 * @param hostProviderRegistry host provider registry
132 */
133 ConfigProvider(JsonNode cfg,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700134 DeviceService deviceService,
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700135 DeviceProviderRegistry deviceProviderRegistry,
136 LinkProviderRegistry linkProviderRegistry,
137 HostProviderRegistry hostProviderRegistry) {
138 this.cfg = checkNotNull(cfg, "Configuration cannot be null");
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700139 this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700140 this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
141 this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
142 this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
143 }
144
145 /**
146 * Parses the given JSON and provides links as configured.
147 */
148 void parse() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700149 try {
150 register();
151 parseDevices();
152 parseLinks();
153 parseHosts();
154 addMissingPorts();
155 } finally {
156 unregister();
157 }
158 }
159
160 private void register() {
161 deviceProviderService = deviceProviderRegistry.register(this);
162 linkProviderService = linkProviderRegistry.register(this);
163 hostProviderService = hostProviderRegistry.register(this);
164 }
165
166 private void unregister() {
167 deviceProviderRegistry.unregister(this);
168 linkProviderRegistry.unregister(this);
169 hostProviderRegistry.unregister(this);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700170 }
171
172 // Parses the given JSON and provides devices.
173 private void parseDevices() {
174 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700175 JsonNode nodes = cfg.get("devices");
176 if (nodes != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700177 prepareForDeviceEvents(nodes.size());
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700178 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700179 parseDevice(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700180
181 // FIXME: hack to make sure device attributes take
182 // This will be fixed when GossipDeviceStore uses ECM
183 parseDevice(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700184 }
185 }
186 } finally {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700187 waitForDeviceEvents();
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700188 }
189 }
190
191 // Parses the given node with device data and supplies the device.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700192 private void parseDevice(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700193 URI uri = URI.create(get(node, "uri"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700194 Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
195 String mfr = get(node, "mfr", UNKNOWN);
196 String hw = get(node, "hw", UNKNOWN);
197 String sw = get(node, "sw", UNKNOWN);
198 String serial = get(node, "serial", UNKNOWN);
199 ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700200 SparseAnnotations annotations = annotations(node.get("annotations"));
201
202 DeviceDescription desc =
203 new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
204 cid, annotations);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700205 DeviceId deviceId = deviceId(uri);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700206 deviceProviderService.deviceConnected(deviceId, desc);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700207
208 JsonNode ports = node.get("ports");
209 if (ports != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700210 parsePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700211 }
212 }
213
214 // Parses the given node with list of device ports.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700215 private void parsePorts(DeviceId deviceId, JsonNode nodes) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700216 List<PortDescription> ports = new ArrayList<>();
217 for (JsonNode node : nodes) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700218 ports.add(parsePort(deviceId, node));
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700219 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700220 deviceProviderService.updatePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700221 }
222
223 // Parses the given node with port information.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700224 private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700225 Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
Marc De Leenheer723f5532015-06-03 20:16:17 -0700226 // TL1-based ports have a name
227 PortNumber port = null;
228 if (node.has("name")) {
229 for (Port p : deviceService.getPorts(deviceId)) {
230 if (p.number().name().equals(node.get("name").asText())) {
231 port = p.number();
232 break;
233 }
234 }
235 } else {
236 port = portNumber(node.path("port").asLong(0));
237 }
238
Marc De Leenheerc9733082015-06-04 12:22:38 -0700239 if (port == null) {
240 log.error("Cannot find port given in node {}", node);
241 return null;
242 }
243
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700244 String portName = Strings.emptyToNull(port.name());
245 SparseAnnotations annotations = null;
246 if (portName != null) {
247 annotations = DefaultAnnotations.builder()
248 .set(AnnotationKeys.PORT_NAME, portName).build();
249 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700250 switch (type) {
251 case COPPER:
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700252 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
253 type, node.path("speed").asLong(1_000),
254 annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700255 case FIBER:
256 // Currently, assume OMS when FIBER. Provide sane defaults.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700257 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
258 CENTER, CENTER.add(TOTAL),
259 Frequency.ofGHz(100), annotations);
Marc De Leenheer723f5532015-06-03 20:16:17 -0700260 case ODUCLT:
261 annotations = annotations(node.get("annotations"));
262 OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
263 return new OduCltPortDescription(port, node.path("enabled").asBoolean(true),
264 oduCltPort.signalType(), annotations);
265 case OCH:
266 annotations = annotations(node.get("annotations"));
267 OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
268 return new OchPortDescription(port, node.path("enabled").asBoolean(true),
269 ochPort.signalType(), ochPort.isTunable(),
270 ochPort.lambda(), annotations);
271 case OMS:
272 annotations = annotations(node.get("annotations"));
273 OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
274 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
275 omsPort.minFrequency(), omsPort.maxFrequency(), omsPort.grid(), annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700276 default:
277 log.warn("{}: Unsupported Port Type");
278 }
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700279 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
280 type, node.path("speed").asLong(1_000),
281 annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700282 }
283
284 // Parses the given JSON and provides links as configured.
285 private void parseLinks() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700286 JsonNode nodes = cfg.get("links");
287 if (nodes != null) {
288 for (JsonNode node : nodes) {
289 parseLink(node, false);
290 if (!node.has("halfplex")) {
291 parseLink(node, true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700292 }
293 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700294 }
295 }
296
297 // Parses the given node with link data and supplies the link.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700298 private void parseLink(JsonNode node, boolean reverse) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700299 ConnectPoint src = connectPoint(get(node, "src"));
300 ConnectPoint dst = connectPoint(get(node, "dst"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700301 Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700302 SparseAnnotations annotations = annotations(node.get("annotations"));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700303 // take annotations to update optical ports with correct attributes.
304 updatePorts(src, dst, annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700305 DefaultLinkDescription desc = reverse ?
306 new DefaultLinkDescription(dst, src, type, annotations) :
307 new DefaultLinkDescription(src, dst, type, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700308 linkProviderService.linkDetected(desc);
309
310 connectPoints.add(src);
311 connectPoints.add(dst);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700312 }
313
Ayaka Koshibeae541732015-05-19 13:37:27 -0700314 private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700315 final String linkType = annotations.value("optical.type");
316 if ("cross-connect".equals(linkType)) {
317 String value = annotations.value("bandwidth").trim();
318 try {
319 double bw = Double.parseDouble(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700320 updateOchPort(bw, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700321 } catch (NumberFormatException e) {
322 log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
323 return;
324 }
325 } else if ("WDM".equals(linkType)) {
326 String value = annotations.value("optical.waves").trim();
327 try {
328 int numChls = Integer.parseInt(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700329 updateOMSPorts(numChls, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700330 } catch (NumberFormatException e) {
331 log.warn("Invalid channel ({}), can't configure port(s)", value);
332 return;
333 }
334 }
335 }
336
337 // uses 'bandwidth' annotation to determine the channel spacing.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700338 private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
339 Device src = deviceService.getDevice(srcCp.deviceId());
340 Device dst = deviceService.getDevice(dstCp.deviceId());
341 // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
Ayaka Koshibeae541732015-05-19 13:37:27 -0700342 Frequency spacing = Frequency.ofMHz(bw);
343 // channel bandwidth is smaller than smallest standard channel spacing.
344 ChannelSpacing chsp = null;
345 if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
346 chsp = ChannelSpacing.CHL_6P25GHZ;
347 }
348 for (int i = 1; i < ChannelSpacing.values().length; i++) {
349 Frequency val = ChannelSpacing.values()[i].frequency();
350 // pick the next highest or equal channel interval.
351 if (val.isLessThan(spacing)) {
352 chsp = ChannelSpacing.values()[i - 1];
353 break;
354 }
355 }
356 if (chsp == null) {
357 log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
358 return;
359 }
360 OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
361 if (src.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700362 PortDescription portDesc = new OchPortDescription(srcCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700363 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700364 descriptions.put(srcCp, portDesc);
365 deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700366 }
367 if (dst.type() == Device.Type.ROADM) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700368 PortDescription portDesc = new OchPortDescription(dstCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700369 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700370 descriptions.put(dstCp, portDesc);
371 deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700372 }
373 }
374
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700375 private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
376 // round down to largest slot that allows numChl channels to fit into C band range
Ayaka Koshibeae541732015-05-19 13:37:27 -0700377 ChannelSpacing chl = null;
378 Frequency perChl = TOTAL.floorDivision(numChls);
379 for (int i = 0; i < ChannelSpacing.values().length; i++) {
380 Frequency val = ChannelSpacing.values()[i].frequency();
381 if (val.isLessThan(perChl)) {
382 chl = ChannelSpacing.values()[i];
383 break;
384 }
385 }
386 if (chl == null) {
387 chl = ChannelSpacing.CHL_6P25GHZ;
388 }
389
390 // if true, there was less channels than can be tightly packed.
Sho SHIMIZUf6501c02015-06-10 19:30:24 -0700391 Frequency grid = chl.frequency();
Ayaka Koshibeae541732015-05-19 13:37:27 -0700392 // say Linc's 1st slot starts at CENTER and goes up from there.
393 Frequency min = CENTER.add(grid);
394 Frequency max = CENTER.add(grid.multiply(numChls));
395
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700396 PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
397 PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
398 descriptions.put(srcCp, srcPortDesc);
399 descriptions.put(dstCp, dstPortDesc);
400 deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
401 deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700402 }
403
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700404 // Parses the given JSON and provides hosts as configured.
405 private void parseHosts() {
406 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700407 JsonNode nodes = cfg.get("hosts");
408 if (nodes != null) {
409 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700410 parseHost(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700411
412 // FIXME: hack to make sure host attributes take
413 // This will be fixed when GossipHostStore uses ECM
414 parseHost(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700415 }
416 }
417 } finally {
418 hostProviderRegistry.unregister(this);
419 }
420 }
421
422 // Parses the given node with host data and supplies the host.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700423 private void parseHost(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700424 MacAddress mac = MacAddress.valueOf(get(node, "mac"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700425 VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700426 HostId hostId = HostId.hostId(mac, vlanId);
427 SparseAnnotations annotations = annotations(node.get("annotations"));
428 HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800429
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700430 String[] ipStrings = get(node, "ip", "").split(",");
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800431 Set<IpAddress> ips = new HashSet<>();
432 for (String ip : ipStrings) {
433 ips.add(IpAddress.valueOf(ip.trim()));
434 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700435
436 DefaultHostDescription desc =
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800437 new DefaultHostDescription(mac, vlanId, location, ips, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700438 hostProviderService.hostDetected(hostId, desc);
439
440 connectPoints.add(location);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700441 }
442
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700443 // Adds any missing device ports for configured links and host locations.
444 private void addMissingPorts() {
445 deviceService.getDevices().forEach(this::addMissingPorts);
446 }
447
448 // Adds any missing device ports.
449 private void addMissingPorts(Device device) {
Jonathan Harteb431c12015-07-27 10:43:50 -0700450 try {
451 List<Port> ports = deviceService.getPorts(device.id());
452 Set<ConnectPoint> existing = ports.stream()
453 .map(p -> new ConnectPoint(device.id(), p.number()))
454 .collect(Collectors.toSet());
455 Set<ConnectPoint> missing = connectPoints.stream()
456 .filter(cp -> cp.deviceId().equals(device.id()))
457 .filter(cp -> !existing.contains(cp))
458 .collect(Collectors.toSet());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700459
Jonathan Harteb431c12015-07-27 10:43:50 -0700460 if (!missing.isEmpty()) {
461 List<PortDescription> newPorts = Stream.concat(
462 ports.stream().map(this::description),
463 missing.stream().map(this::description)
464 ).collect(Collectors.toList());
465 deviceProviderService.updatePorts(device.id(), newPorts);
466 }
467 } catch (IllegalArgumentException e) {
468 log.warn("Error pushing ports: {}", e.getMessage());
469 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700470 }
471
472 // Creates a port description from the specified port.
473 private PortDescription description(Port p) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700474 switch (p.type()) {
475 case OMS:
476 OmsPort op = (OmsPort) p;
477 return new OmsPortDescription(
478 op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
479 case OCH:
480 OchPort ochp = (OchPort) p;
481 return new OchPortDescription(
482 ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
483 case ODUCLT:
484 OduCltPort odup = (OduCltPort) p;
485 return new OduCltPortDescription(
486 odup.number(), odup.isEnabled(), odup.signalType());
487 default:
488 return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
489 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700490 }
491
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700492 // Creates a port description from the specified connection point if none created earlier.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700493 private PortDescription description(ConnectPoint cp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700494 PortDescription saved = descriptions.get(cp);
495 if (saved != null) {
496 return saved;
497 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700498 Port p = deviceService.getPort(cp.deviceId(), cp.port());
499 if (p == null) {
500 return new DefaultPortDescription(cp.port(), true);
501 }
502 return description(p);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700503 }
504
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700505 // Produces set of annotations from the given JSON node.
506 private SparseAnnotations annotations(JsonNode node) {
507 if (node == null) {
Sho SHIMIZUa3d67cd2015-06-04 11:46:48 -0700508 return DefaultAnnotations.EMPTY;
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700509 }
510
511 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
512 Iterator<String> it = node.fieldNames();
513 while (it.hasNext()) {
514 String k = it.next();
515 builder.set(k, node.get(k).asText());
516 }
517 return builder.build();
518 }
519
520 // Produces a connection point from the specified uri/port text.
521 private ConnectPoint connectPoint(String text) {
522 int i = text.lastIndexOf("/");
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700523 String portName = text.substring(i + 1);
524 DeviceId deviceId = deviceId(text.substring(0, i));
525
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700526 for (Port port : deviceService.getPorts(deviceId)) {
527 PortNumber pn = port.number();
528 if (pn.name().equals(portName)) {
529 return new ConnectPoint(deviceId, pn);
530 }
531 }
Sho SHIMIZUccf40c72015-06-11 16:55:20 -0700532
533 long portNum;
534 try {
535 portNum = Long.parseLong(portName);
536 } catch (NumberFormatException e) {
537 portNum = 0;
538 }
539
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700540 return new ConnectPoint(deviceId, portNumber(portNum, portName));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700541 }
542
543 // Returns string form of the named property in the given JSON object.
544 private String get(JsonNode node, String name) {
545 return node.path(name).asText();
546 }
547
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700548 // Returns string form of the named property in the given JSON object.
549 private String get(JsonNode node, String name, String defaultValue) {
550 return node.path(name).asText(defaultValue);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700551 }
552
553 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700554 public void roleChanged(DeviceId device, MastershipRole newRole) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700555 deviceProviderService.receivedRoleReply(device, newRole, newRole);
556 }
557
558 @Override
559 public void triggerProbe(DeviceId deviceId) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700560 }
561
562 @Override
563 public void triggerProbe(Host host) {
564 }
565
566 @Override
567 public ProviderId id() {
568 return PID;
569 }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700570
571 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700572 public boolean isReachable(DeviceId device) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700573 return true;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700574 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700575
576 /**
577 * Prepares to count device added/available/removed events.
578 *
579 * @param count number of events to count
580 */
581 protected void prepareForDeviceEvents(int count) {
582 deviceLatch = new CountDownLatch(count);
583 deviceService.addListener(deviceEventCounter);
584 }
585
586 /**
587 * Waits for all expected device added/available/removed events.
588 */
589 protected void waitForDeviceEvents() {
590 try {
591 deviceLatch.await(2, TimeUnit.SECONDS);
592 } catch (InterruptedException e) {
593 log.warn("Device events did not arrive in time");
594 }
595 deviceService.removeListener(deviceEventCounter);
596 }
597
598 // Counts down number of device added/available/removed events.
599 private class DeviceEventCounter implements DeviceListener {
600 @Override
601 public void event(DeviceEvent event) {
602 DeviceEvent.Type type = event.type();
603 if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
604 deviceLatch.countDown();
605 }
606 }
607 }
608
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700609}