blob: 453f2066ea74f2a849ca61cc676dbd8ee7b67de5 [file] [log] [blame]
Thomas Vachuska9252bc32014-10-23 02:33:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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.OchSignal;
Ayaka Koshibeae541732015-05-19 13:37:27 -070042import org.onosproject.net.OduSignalType;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.Port;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070044import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.SparseAnnotations;
46import org.onosproject.net.device.DefaultDeviceDescription;
47import org.onosproject.net.device.DefaultPortDescription;
48import org.onosproject.net.device.DeviceDescription;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070049import org.onosproject.net.device.DeviceEvent;
50import org.onosproject.net.device.DeviceListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.net.device.DeviceProvider;
52import org.onosproject.net.device.DeviceProviderRegistry;
53import org.onosproject.net.device.DeviceProviderService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070054import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.device.PortDescription;
56import org.onosproject.net.host.DefaultHostDescription;
57import org.onosproject.net.host.HostProvider;
58import org.onosproject.net.host.HostProviderRegistry;
59import org.onosproject.net.host.HostProviderService;
60import org.onosproject.net.link.DefaultLinkDescription;
61import org.onosproject.net.link.LinkProvider;
62import org.onosproject.net.link.LinkProviderRegistry;
63import org.onosproject.net.link.LinkProviderService;
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -080064import org.onosproject.net.optical.OchPort;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -070065import org.onosproject.net.optical.OduCltPort;
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -070066import org.onosproject.net.optical.OmsPort;
Brian O'Connorabafb502014-12-02 22:26:20 -080067import org.onosproject.net.provider.ProviderId;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070068import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070070
71import java.net.URI;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070072import java.util.ArrayList;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080073import java.util.HashSet;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070074import java.util.Iterator;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070075import java.util.List;
Ayaka Koshibe74b55272015-05-28 15:16:04 -070076import java.util.Map;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080077import java.util.Set;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070078import java.util.concurrent.CountDownLatch;
79import java.util.concurrent.TimeUnit;
80import java.util.stream.Collectors;
Sho SHIMIZUbb825d72015-06-11 10:56:32 -070081import java.util.stream.Stream;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070082
83import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorabafb502014-12-02 22:26:20 -080084import static org.onosproject.net.DeviceId.deviceId;
85import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070086import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
87import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -080088import static org.onosproject.net.optical.device.OchPortHelper.ochPortDescription;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -070089import static org.onosproject.net.optical.device.OduCltPortHelper.oduCltPortDescription;
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -070090import static org.onosproject.net.optical.device.OmsPortHelper.omsPortDescription;
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -080091import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070092
93/**
94 * Provider of devices and links parsed from a JSON configuration structure.
Thomas Vachuskaf9879c52016-01-15 11:12:19 -080095 *
96 * @deprecated in 1.5.0 (Falcon)
Thomas Vachuska9252bc32014-10-23 02:33:25 -070097 */
Thomas Vachuskaf9879c52016-01-15 11:12:19 -080098@Deprecated
Thomas Vachuska9252bc32014-10-23 02:33:25 -070099class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
100
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700101 private final Logger log = LoggerFactory.getLogger(getClass());
102
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700103 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -0800104 new ProviderId("cfg", "org.onosproject.rest", true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700105
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700106 private static final String UNKNOWN = "unknown";
107
Ayaka Koshibeae541732015-05-19 13:37:27 -0700108 // C-band has 4.4 THz (4,400 GHz) total bandwidth
Sho SHIMIZU00762ee2016-01-05 16:32:24 -0800109 private static final Frequency TOTAL = Frequency.ofGHz(4_400);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700110
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700111 private CountDownLatch deviceLatch;
112
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700113 private final JsonNode cfg;
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700114 private final DeviceService deviceService;
115
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700116 private final DeviceProviderRegistry deviceProviderRegistry;
117 private final LinkProviderRegistry linkProviderRegistry;
118 private final HostProviderRegistry hostProviderRegistry;
119
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700120 private DeviceProviderService deviceProviderService;
121 private LinkProviderService linkProviderService;
122 private HostProviderService hostProviderService;
123
124 private DeviceListener deviceEventCounter = new DeviceEventCounter();
125 private List<ConnectPoint> connectPoints = Lists.newArrayList();
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700126 private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700127
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700128 /**
129 * Creates a new configuration provider.
130 *
131 * @param cfg JSON configuration
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700132 * @param deviceService device service
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700133 * @param deviceProviderRegistry device provider registry
134 * @param linkProviderRegistry link provider registry
135 * @param hostProviderRegistry host provider registry
136 */
137 ConfigProvider(JsonNode cfg,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700138 DeviceService deviceService,
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700139 DeviceProviderRegistry deviceProviderRegistry,
140 LinkProviderRegistry linkProviderRegistry,
141 HostProviderRegistry hostProviderRegistry) {
142 this.cfg = checkNotNull(cfg, "Configuration cannot be null");
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -0800143 this.deviceService = opticalView(checkNotNull(deviceService, "Device service cannot be null"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700144 this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
145 this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
146 this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
147 }
148
149 /**
150 * Parses the given JSON and provides links as configured.
151 */
152 void parse() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700153 try {
154 register();
155 parseDevices();
156 parseLinks();
157 parseHosts();
158 addMissingPorts();
159 } finally {
160 unregister();
161 }
162 }
163
164 private void register() {
165 deviceProviderService = deviceProviderRegistry.register(this);
166 linkProviderService = linkProviderRegistry.register(this);
167 hostProviderService = hostProviderRegistry.register(this);
168 }
169
170 private void unregister() {
171 deviceProviderRegistry.unregister(this);
172 linkProviderRegistry.unregister(this);
173 hostProviderRegistry.unregister(this);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700174 }
175
176 // Parses the given JSON and provides devices.
177 private void parseDevices() {
178 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700179 JsonNode nodes = cfg.get("devices");
180 if (nodes != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700181 prepareForDeviceEvents(nodes.size());
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700182 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700183 parseDevice(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700184
185 // FIXME: hack to make sure device attributes take
186 // This will be fixed when GossipDeviceStore uses ECM
187 parseDevice(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700188 }
189 }
190 } finally {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700191 waitForDeviceEvents();
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700192 }
193 }
194
195 // Parses the given node with device data and supplies the device.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700196 private void parseDevice(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700197 URI uri = URI.create(get(node, "uri"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700198 Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
199 String mfr = get(node, "mfr", UNKNOWN);
200 String hw = get(node, "hw", UNKNOWN);
201 String sw = get(node, "sw", UNKNOWN);
202 String serial = get(node, "serial", UNKNOWN);
203 ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700204 SparseAnnotations annotations = annotations(node.get("annotations"));
205
206 DeviceDescription desc =
207 new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
208 cid, annotations);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700209 DeviceId deviceId = deviceId(uri);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700210 deviceProviderService.deviceConnected(deviceId, desc);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700211
212 JsonNode ports = node.get("ports");
213 if (ports != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700214 parsePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700215 }
216 }
217
218 // Parses the given node with list of device ports.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700219 private void parsePorts(DeviceId deviceId, JsonNode nodes) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700220 List<PortDescription> ports = new ArrayList<>();
221 for (JsonNode node : nodes) {
Marc De Leenheer723f5532015-06-03 20:16:17 -0700222 ports.add(parsePort(deviceId, node));
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700223 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700224 deviceProviderService.updatePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700225 }
226
227 // Parses the given node with port information.
Marc De Leenheer723f5532015-06-03 20:16:17 -0700228 private PortDescription parsePort(DeviceId deviceId, JsonNode node) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700229 Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
Marc De Leenheer723f5532015-06-03 20:16:17 -0700230 // TL1-based ports have a name
231 PortNumber port = null;
232 if (node.has("name")) {
233 for (Port p : deviceService.getPorts(deviceId)) {
234 if (p.number().name().equals(node.get("name").asText())) {
235 port = p.number();
236 break;
237 }
238 }
239 } else {
240 port = portNumber(node.path("port").asLong(0));
241 }
242
Marc De Leenheerc9733082015-06-04 12:22:38 -0700243 if (port == null) {
244 log.error("Cannot find port given in node {}", node);
245 return null;
246 }
247
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700248 String portName = Strings.emptyToNull(port.name());
249 SparseAnnotations annotations = null;
250 if (portName != null) {
251 annotations = DefaultAnnotations.builder()
252 .set(AnnotationKeys.PORT_NAME, portName).build();
253 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700254 switch (type) {
255 case COPPER:
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700256 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
257 type, node.path("speed").asLong(1_000),
258 annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700259 case FIBER:
260 // Currently, assume OMS when FIBER. Provide sane defaults.
Marc De Leenheerfc913dd2015-07-30 16:04:55 -0700261 annotations = annotations(node.get("annotations"));
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -0700262 return omsPortDescription(port, node.path("enabled").asBoolean(true),
Sho SHIMIZU70e76d82016-01-05 16:26:26 -0800263 Spectrum.CENTER_FREQUENCY, Spectrum.CENTER_FREQUENCY.add(TOTAL),
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700264 Frequency.ofGHz(100), annotations);
Marc De Leenheer723f5532015-06-03 20:16:17 -0700265 case ODUCLT:
266 annotations = annotations(node.get("annotations"));
267 OduCltPort oduCltPort = (OduCltPort) deviceService.getPort(deviceId, port);
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -0700268 return oduCltPortDescription(port, node.path("enabled").asBoolean(true),
Marc De Leenheer723f5532015-06-03 20:16:17 -0700269 oduCltPort.signalType(), annotations);
270 case OCH:
271 annotations = annotations(node.get("annotations"));
272 OchPort ochPort = (OchPort) deviceService.getPort(deviceId, port);
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -0800273 return ochPortDescription(port, node.path("enabled").asBoolean(true),
Marc De Leenheer723f5532015-06-03 20:16:17 -0700274 ochPort.signalType(), ochPort.isTunable(),
275 ochPort.lambda(), annotations);
276 case OMS:
277 annotations = annotations(node.get("annotations"));
278 OmsPort omsPort = (OmsPort) deviceService.getPort(deviceId, port);
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -0700279 return omsPortDescription(port, node.path("enabled").asBoolean(true),
Marc De Leenheer723f5532015-06-03 20:16:17 -0700280 omsPort.minFrequency(), omsPort.maxFrequency(), omsPort.grid(), annotations);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700281 default:
282 log.warn("{}: Unsupported Port Type");
283 }
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700284 return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
285 type, node.path("speed").asLong(1_000),
286 annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700287 }
288
289 // Parses the given JSON and provides links as configured.
290 private void parseLinks() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700291 JsonNode nodes = cfg.get("links");
292 if (nodes != null) {
293 for (JsonNode node : nodes) {
294 parseLink(node, false);
295 if (!node.has("halfplex")) {
296 parseLink(node, true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700297 }
298 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700299 }
300 }
301
302 // Parses the given node with link data and supplies the link.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700303 private void parseLink(JsonNode node, boolean reverse) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700304 ConnectPoint src = connectPoint(get(node, "src"));
305 ConnectPoint dst = connectPoint(get(node, "dst"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700306 Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700307 SparseAnnotations annotations = annotations(node.get("annotations"));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700308 // take annotations to update optical ports with correct attributes.
309 updatePorts(src, dst, annotations);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700310 DefaultLinkDescription desc = reverse ?
311 new DefaultLinkDescription(dst, src, type, annotations) :
312 new DefaultLinkDescription(src, dst, type, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700313 linkProviderService.linkDetected(desc);
314
315 connectPoints.add(src);
316 connectPoints.add(dst);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700317 }
318
Ayaka Koshibeae541732015-05-19 13:37:27 -0700319 private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700320 final String linkType = annotations.value("optical.type");
321 if ("cross-connect".equals(linkType)) {
322 String value = annotations.value("bandwidth").trim();
323 try {
324 double bw = Double.parseDouble(value);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700325 updateOchPort(bw, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700326 } catch (NumberFormatException e) {
327 log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
328 return;
329 }
330 } else if ("WDM".equals(linkType)) {
331 String value = annotations.value("optical.waves").trim();
332 try {
333 int numChls = Integer.parseInt(value);
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800334 updateOmsPorts(numChls, src, dst);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700335 } catch (NumberFormatException e) {
336 log.warn("Invalid channel ({}), can't configure port(s)", value);
337 return;
338 }
339 }
340 }
341
342 // uses 'bandwidth' annotation to determine the channel spacing.
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700343 private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
344 Device src = deviceService.getDevice(srcCp.deviceId());
345 Device dst = deviceService.getDevice(dstCp.deviceId());
346 // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
Ayaka Koshibeae541732015-05-19 13:37:27 -0700347 Frequency spacing = Frequency.ofMHz(bw);
348 // channel bandwidth is smaller than smallest standard channel spacing.
349 ChannelSpacing chsp = null;
350 if (spacing.compareTo(ChannelSpacing.CHL_6P25GHZ.frequency()) <= 0) {
351 chsp = ChannelSpacing.CHL_6P25GHZ;
352 }
353 for (int i = 1; i < ChannelSpacing.values().length; i++) {
354 Frequency val = ChannelSpacing.values()[i].frequency();
355 // pick the next highest or equal channel interval.
356 if (val.isLessThan(spacing)) {
357 chsp = ChannelSpacing.values()[i - 1];
358 break;
359 }
360 }
361 if (chsp == null) {
362 log.warn("Invalid channel spacing ({}), can't configure port(s)", spacing);
363 return;
364 }
365 OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
366 if (src.type() == Device.Type.ROADM) {
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -0800367 PortDescription portDesc = ochPortDescription(srcCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700368 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700369 descriptions.put(srcCp, portDesc);
370 deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700371 }
372 if (dst.type() == Device.Type.ROADM) {
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -0800373 PortDescription portDesc = ochPortDescription(dstCp.port(), true,
Ayaka Koshibeae541732015-05-19 13:37:27 -0700374 OduSignalType.ODU4, true, signal);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700375 descriptions.put(dstCp, portDesc);
376 deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700377 }
378 }
379
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800380 private void updateOmsPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700381 // round down to largest slot that allows numChl channels to fit into C band range
Ayaka Koshibeae541732015-05-19 13:37:27 -0700382 ChannelSpacing chl = null;
383 Frequency perChl = TOTAL.floorDivision(numChls);
384 for (int i = 0; i < ChannelSpacing.values().length; i++) {
385 Frequency val = ChannelSpacing.values()[i].frequency();
386 if (val.isLessThan(perChl)) {
387 chl = ChannelSpacing.values()[i];
388 break;
389 }
390 }
391 if (chl == null) {
392 chl = ChannelSpacing.CHL_6P25GHZ;
393 }
394
395 // if true, there was less channels than can be tightly packed.
Sho SHIMIZUf6501c02015-06-10 19:30:24 -0700396 Frequency grid = chl.frequency();
Ayaka Koshibeae541732015-05-19 13:37:27 -0700397 // say Linc's 1st slot starts at CENTER and goes up from there.
Sho SHIMIZU70e76d82016-01-05 16:26:26 -0800398 Frequency min = Spectrum.CENTER_FREQUENCY.add(grid);
399 Frequency max = Spectrum.CENTER_FREQUENCY.add(grid.multiply(numChls));
Ayaka Koshibeae541732015-05-19 13:37:27 -0700400
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -0700401 PortDescription srcPortDesc = omsPortDescription(srcCp.port(), true, min, max, grid);
402 PortDescription dstPortDesc = omsPortDescription(dstCp.port(), true, min, max, grid);
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700403 descriptions.put(srcCp, srcPortDesc);
404 descriptions.put(dstCp, dstPortDesc);
405 deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
406 deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
Ayaka Koshibeae541732015-05-19 13:37:27 -0700407 }
408
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700409 // Parses the given JSON and provides hosts as configured.
410 private void parseHosts() {
411 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700412 JsonNode nodes = cfg.get("hosts");
413 if (nodes != null) {
414 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700415 parseHost(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700416
417 // FIXME: hack to make sure host attributes take
418 // This will be fixed when GossipHostStore uses ECM
419 parseHost(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700420 }
421 }
422 } finally {
423 hostProviderRegistry.unregister(this);
424 }
425 }
426
427 // Parses the given node with host data and supplies the host.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700428 private void parseHost(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700429 MacAddress mac = MacAddress.valueOf(get(node, "mac"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700430 VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700431 HostId hostId = HostId.hostId(mac, vlanId);
432 SparseAnnotations annotations = annotations(node.get("annotations"));
433 HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800434
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700435 String[] ipStrings = get(node, "ip", "").split(",");
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800436 Set<IpAddress> ips = new HashSet<>();
437 for (String ip : ipStrings) {
438 ips.add(IpAddress.valueOf(ip.trim()));
439 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700440
441 DefaultHostDescription desc =
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800442 new DefaultHostDescription(mac, vlanId, location, ips, annotations);
Ray Milkeydc083442016-02-22 11:27:57 -0800443 hostProviderService.hostDetected(hostId, desc, false);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700444
445 connectPoints.add(location);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700446 }
447
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700448 // Adds any missing device ports for configured links and host locations.
449 private void addMissingPorts() {
450 deviceService.getDevices().forEach(this::addMissingPorts);
451 }
452
453 // Adds any missing device ports.
454 private void addMissingPorts(Device device) {
Jonathan Harteb431c12015-07-27 10:43:50 -0700455 try {
456 List<Port> ports = deviceService.getPorts(device.id());
457 Set<ConnectPoint> existing = ports.stream()
458 .map(p -> new ConnectPoint(device.id(), p.number()))
459 .collect(Collectors.toSet());
460 Set<ConnectPoint> missing = connectPoints.stream()
461 .filter(cp -> cp.deviceId().equals(device.id()))
462 .filter(cp -> !existing.contains(cp))
463 .collect(Collectors.toSet());
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700464
Jonathan Harteb431c12015-07-27 10:43:50 -0700465 if (!missing.isEmpty()) {
466 List<PortDescription> newPorts = Stream.concat(
467 ports.stream().map(this::description),
468 missing.stream().map(this::description)
469 ).collect(Collectors.toList());
470 deviceProviderService.updatePorts(device.id(), newPorts);
471 }
472 } catch (IllegalArgumentException e) {
473 log.warn("Error pushing ports: {}", e.getMessage());
474 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700475 }
476
477 // Creates a port description from the specified port.
478 private PortDescription description(Port p) {
Ayaka Koshibeae541732015-05-19 13:37:27 -0700479 switch (p.type()) {
480 case OMS:
481 OmsPort op = (OmsPort) p;
HIGUCHI Yuta95d83e82016-04-26 12:13:48 -0700482 return omsPortDescription(
Ayaka Koshibeae541732015-05-19 13:37:27 -0700483 op.number(), op.isEnabled(), op.minFrequency(), op.maxFrequency(), op.grid());
484 case OCH:
485 OchPort ochp = (OchPort) p;
HIGUCHI Yuta34a3f692016-01-09 21:08:57 -0800486 return ochPortDescription(
Ayaka Koshibeae541732015-05-19 13:37:27 -0700487 ochp.number(), ochp.isEnabled(), ochp.signalType(), ochp.isTunable(), ochp.lambda());
488 case ODUCLT:
489 OduCltPort odup = (OduCltPort) p;
HIGUCHI Yuta4c0ef6b2016-05-02 19:45:41 -0700490 return oduCltPortDescription(
Ayaka Koshibeae541732015-05-19 13:37:27 -0700491 odup.number(), odup.isEnabled(), odup.signalType());
492 default:
493 return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
494 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700495 }
496
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700497 // Creates a port description from the specified connection point if none created earlier.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700498 private PortDescription description(ConnectPoint cp) {
Ayaka Koshibe74b55272015-05-28 15:16:04 -0700499 PortDescription saved = descriptions.get(cp);
500 if (saved != null) {
501 return saved;
502 }
Ayaka Koshibeae541732015-05-19 13:37:27 -0700503 Port p = deviceService.getPort(cp.deviceId(), cp.port());
504 if (p == null) {
505 return new DefaultPortDescription(cp.port(), true);
506 }
507 return description(p);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700508 }
509
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700510 // Produces set of annotations from the given JSON node.
511 private SparseAnnotations annotations(JsonNode node) {
512 if (node == null) {
Sho SHIMIZUa3d67cd2015-06-04 11:46:48 -0700513 return DefaultAnnotations.EMPTY;
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700514 }
515
516 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
517 Iterator<String> it = node.fieldNames();
518 while (it.hasNext()) {
519 String k = it.next();
520 builder.set(k, node.get(k).asText());
521 }
522 return builder.build();
523 }
524
525 // Produces a connection point from the specified uri/port text.
526 private ConnectPoint connectPoint(String text) {
527 int i = text.lastIndexOf("/");
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700528 String portName = text.substring(i + 1);
529 DeviceId deviceId = deviceId(text.substring(0, i));
530
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700531 for (Port port : deviceService.getPorts(deviceId)) {
532 PortNumber pn = port.number();
533 if (pn.name().equals(portName)) {
534 return new ConnectPoint(deviceId, pn);
535 }
536 }
Sho SHIMIZUccf40c72015-06-11 16:55:20 -0700537
538 long portNum;
539 try {
540 portNum = Long.parseLong(portName);
541 } catch (NumberFormatException e) {
542 portNum = 0;
543 }
544
Ayaka Koshibec10d1512015-06-04 15:17:58 -0700545 return new ConnectPoint(deviceId, portNumber(portNum, portName));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700546 }
547
548 // Returns string form of the named property in the given JSON object.
549 private String get(JsonNode node, String name) {
550 return node.path(name).asText();
551 }
552
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700553 // Returns string form of the named property in the given JSON object.
554 private String get(JsonNode node, String name, String defaultValue) {
555 return node.path(name).asText(defaultValue);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700556 }
557
558 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700559 public void roleChanged(DeviceId device, MastershipRole newRole) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700560 deviceProviderService.receivedRoleReply(device, newRole, newRole);
561 }
562
563 @Override
564 public void triggerProbe(DeviceId deviceId) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700565 }
566
567 @Override
568 public void triggerProbe(Host host) {
569 }
570
571 @Override
Saurav Dasa2d37502016-03-25 17:50:40 -0700572 public void changePortState(DeviceId deviceId, PortNumber portNumber,
573 boolean enable) {
574 }
575
576 @Override
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700577 public ProviderId id() {
578 return PID;
579 }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700580
581 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700582 public boolean isReachable(DeviceId device) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700583 return true;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700584 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700585
586 /**
587 * Prepares to count device added/available/removed events.
588 *
589 * @param count number of events to count
590 */
591 protected void prepareForDeviceEvents(int count) {
592 deviceLatch = new CountDownLatch(count);
593 deviceService.addListener(deviceEventCounter);
594 }
595
596 /**
597 * Waits for all expected device added/available/removed events.
598 */
599 protected void waitForDeviceEvents() {
600 try {
601 deviceLatch.await(2, TimeUnit.SECONDS);
602 } catch (InterruptedException e) {
603 log.warn("Device events did not arrive in time");
604 }
605 deviceService.removeListener(deviceEventCounter);
606 }
607
608 // Counts down number of device added/available/removed events.
609 private class DeviceEventCounter implements DeviceListener {
610 @Override
611 public void event(DeviceEvent event) {
612 DeviceEvent.Type type = event.type();
613 if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
614 deviceLatch.countDown();
615 }
616 }
617 }
618
Saurav Dasa2d37502016-03-25 17:50:40 -0700619
620
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700621}