blob: f25531b728bfc0b6aa537f7e7d200ea77e7a1021 [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;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070019import com.google.common.collect.Lists;
20import org.onlab.packet.ChassisId;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
Brian O'Connorabafb502014-12-02 22:26:20 -080024import org.onosproject.net.ConnectPoint;
25import org.onosproject.net.DefaultAnnotations;
26import org.onosproject.net.Device;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.Host;
29import org.onosproject.net.HostId;
30import org.onosproject.net.HostLocation;
31import org.onosproject.net.Link;
32import org.onosproject.net.MastershipRole;
33import org.onosproject.net.Port;
34import org.onosproject.net.SparseAnnotations;
35import org.onosproject.net.device.DefaultDeviceDescription;
36import org.onosproject.net.device.DefaultPortDescription;
37import org.onosproject.net.device.DeviceDescription;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070038import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.net.device.DeviceProvider;
41import org.onosproject.net.device.DeviceProviderRegistry;
42import org.onosproject.net.device.DeviceProviderService;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070043import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.device.PortDescription;
45import org.onosproject.net.host.DefaultHostDescription;
46import org.onosproject.net.host.HostProvider;
47import org.onosproject.net.host.HostProviderRegistry;
48import org.onosproject.net.host.HostProviderService;
49import org.onosproject.net.link.DefaultLinkDescription;
50import org.onosproject.net.link.LinkProvider;
51import org.onosproject.net.link.LinkProviderRegistry;
52import org.onosproject.net.link.LinkProviderService;
53import org.onosproject.net.provider.ProviderId;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070054import org.slf4j.Logger;
55import org.slf4j.LoggerFactory;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070056
57import java.net.URI;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070058import java.util.ArrayList;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080059import java.util.HashSet;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070060import java.util.Iterator;
Thomas Vachuskad16ce182014-10-29 17:25:29 -070061import java.util.List;
Thomas Vachuskaece59ee2014-11-19 19:06:11 -080062import java.util.Set;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070063import java.util.concurrent.CountDownLatch;
64import java.util.concurrent.TimeUnit;
65import java.util.stream.Collectors;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070066
67import static com.google.common.base.Preconditions.checkNotNull;
Brian O'Connorabafb502014-12-02 22:26:20 -080068import static org.onosproject.net.DeviceId.deviceId;
69import static org.onosproject.net.PortNumber.portNumber;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070070import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
71import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
Thomas Vachuska9252bc32014-10-23 02:33:25 -070072
73/**
74 * Provider of devices and links parsed from a JSON configuration structure.
75 */
76class ConfigProvider implements DeviceProvider, LinkProvider, HostProvider {
77
Thomas Vachuskac40d4632015-04-09 16:55:03 -070078 private final Logger log = LoggerFactory.getLogger(getClass());
79
Thomas Vachuska9252bc32014-10-23 02:33:25 -070080 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080081 new ProviderId("cfg", "org.onosproject.rest", true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -070082
Thomas Vachuskac40d4632015-04-09 16:55:03 -070083 private static final String UNKNOWN = "unknown";
84
85 private CountDownLatch deviceLatch;
86
Thomas Vachuska9252bc32014-10-23 02:33:25 -070087 private final JsonNode cfg;
Thomas Vachuskac40d4632015-04-09 16:55:03 -070088 private final DeviceService deviceService;
89
Thomas Vachuska9252bc32014-10-23 02:33:25 -070090 private final DeviceProviderRegistry deviceProviderRegistry;
91 private final LinkProviderRegistry linkProviderRegistry;
92 private final HostProviderRegistry hostProviderRegistry;
93
Thomas Vachuskac40d4632015-04-09 16:55:03 -070094 private DeviceProviderService deviceProviderService;
95 private LinkProviderService linkProviderService;
96 private HostProviderService hostProviderService;
97
98 private DeviceListener deviceEventCounter = new DeviceEventCounter();
99 private List<ConnectPoint> connectPoints = Lists.newArrayList();
100
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700101 /**
102 * Creates a new configuration provider.
103 *
104 * @param cfg JSON configuration
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700105 * @param deviceService device service
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700106 * @param deviceProviderRegistry device provider registry
107 * @param linkProviderRegistry link provider registry
108 * @param hostProviderRegistry host provider registry
109 */
110 ConfigProvider(JsonNode cfg,
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700111 DeviceService deviceService,
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700112 DeviceProviderRegistry deviceProviderRegistry,
113 LinkProviderRegistry linkProviderRegistry,
114 HostProviderRegistry hostProviderRegistry) {
115 this.cfg = checkNotNull(cfg, "Configuration cannot be null");
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700116 this.deviceService = checkNotNull(deviceService, "Device service cannot be null");
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700117 this.deviceProviderRegistry = checkNotNull(deviceProviderRegistry, "Device provider registry cannot be null");
118 this.linkProviderRegistry = checkNotNull(linkProviderRegistry, "Link provider registry cannot be null");
119 this.hostProviderRegistry = checkNotNull(hostProviderRegistry, "Host provider registry cannot be null");
120 }
121
122 /**
123 * Parses the given JSON and provides links as configured.
124 */
125 void parse() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700126 try {
127 register();
128 parseDevices();
129 parseLinks();
130 parseHosts();
131 addMissingPorts();
132 } finally {
133 unregister();
134 }
135 }
136
137 private void register() {
138 deviceProviderService = deviceProviderRegistry.register(this);
139 linkProviderService = linkProviderRegistry.register(this);
140 hostProviderService = hostProviderRegistry.register(this);
141 }
142
143 private void unregister() {
144 deviceProviderRegistry.unregister(this);
145 linkProviderRegistry.unregister(this);
146 hostProviderRegistry.unregister(this);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700147 }
148
149 // Parses the given JSON and provides devices.
150 private void parseDevices() {
151 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700152 JsonNode nodes = cfg.get("devices");
153 if (nodes != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700154 prepareForDeviceEvents(nodes.size());
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700155 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700156 parseDevice(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700157
158 // FIXME: hack to make sure device attributes take
159 // This will be fixed when GossipDeviceStore uses ECM
160 parseDevice(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700161 }
162 }
163 } finally {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700164 waitForDeviceEvents();
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700165 }
166 }
167
168 // Parses the given node with device data and supplies the device.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700169 private void parseDevice(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700170 URI uri = URI.create(get(node, "uri"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700171 Device.Type type = Device.Type.valueOf(get(node, "type", "SWITCH"));
172 String mfr = get(node, "mfr", UNKNOWN);
173 String hw = get(node, "hw", UNKNOWN);
174 String sw = get(node, "sw", UNKNOWN);
175 String serial = get(node, "serial", UNKNOWN);
176 ChassisId cid = new ChassisId(get(node, "mac", "000000000000"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700177 SparseAnnotations annotations = annotations(node.get("annotations"));
178
179 DeviceDescription desc =
180 new DefaultDeviceDescription(uri, type, mfr, hw, sw, serial,
181 cid, annotations);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700182 DeviceId deviceId = deviceId(uri);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700183 deviceProviderService.deviceConnected(deviceId, desc);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700184
185 JsonNode ports = node.get("ports");
186 if (ports != null) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700187 parsePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700188 }
189 }
190
191 // Parses the given node with list of device ports.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700192 private void parsePorts(DeviceId deviceId, JsonNode nodes) {
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700193 List<PortDescription> ports = new ArrayList<>();
194 for (JsonNode node : nodes) {
195 ports.add(parsePort(node));
196 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700197 deviceProviderService.updatePorts(deviceId, ports);
Thomas Vachuskad16ce182014-10-29 17:25:29 -0700198 }
199
200 // Parses the given node with port information.
201 private PortDescription parsePort(JsonNode node) {
202 Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
203 return new DefaultPortDescription(portNumber(node.path("port").asLong(0)),
204 node.path("enabled").asBoolean(true),
205 type, node.path("speed").asLong(1_000));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700206 }
207
208 // Parses the given JSON and provides links as configured.
209 private void parseLinks() {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700210 JsonNode nodes = cfg.get("links");
211 if (nodes != null) {
212 for (JsonNode node : nodes) {
213 parseLink(node, false);
214 if (!node.has("halfplex")) {
215 parseLink(node, true);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700216 }
217 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700218 }
219 }
220
221 // Parses the given node with link data and supplies the link.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700222 private void parseLink(JsonNode node, boolean reverse) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700223 ConnectPoint src = connectPoint(get(node, "src"));
224 ConnectPoint dst = connectPoint(get(node, "dst"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700225 Link.Type type = Link.Type.valueOf(get(node, "type", "DIRECT"));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700226 SparseAnnotations annotations = annotations(node.get("annotations"));
227
228 DefaultLinkDescription desc = reverse ?
229 new DefaultLinkDescription(dst, src, type, annotations) :
230 new DefaultLinkDescription(src, dst, type, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700231 linkProviderService.linkDetected(desc);
232
233 connectPoints.add(src);
234 connectPoints.add(dst);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700235 }
236
237 // Parses the given JSON and provides hosts as configured.
238 private void parseHosts() {
239 try {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700240 JsonNode nodes = cfg.get("hosts");
241 if (nodes != null) {
242 for (JsonNode node : nodes) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700243 parseHost(node);
Thomas Vachuska3b84c862015-04-28 12:09:48 -0700244
245 // FIXME: hack to make sure host attributes take
246 // This will be fixed when GossipHostStore uses ECM
247 parseHost(node);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700248 }
249 }
250 } finally {
251 hostProviderRegistry.unregister(this);
252 }
253 }
254
255 // Parses the given node with host data and supplies the host.
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700256 private void parseHost(JsonNode node) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700257 MacAddress mac = MacAddress.valueOf(get(node, "mac"));
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700258 VlanId vlanId = VlanId.vlanId((short) node.get("vlan").asInt(VlanId.UNTAGGED));
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700259 HostId hostId = HostId.hostId(mac, vlanId);
260 SparseAnnotations annotations = annotations(node.get("annotations"));
261 HostLocation location = new HostLocation(connectPoint(get(node, "location")), 0);
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800262
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700263 String[] ipStrings = get(node, "ip", "").split(",");
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800264 Set<IpAddress> ips = new HashSet<>();
265 for (String ip : ipStrings) {
266 ips.add(IpAddress.valueOf(ip.trim()));
267 }
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700268
269 DefaultHostDescription desc =
Thomas Vachuskaece59ee2014-11-19 19:06:11 -0800270 new DefaultHostDescription(mac, vlanId, location, ips, annotations);
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700271 hostProviderService.hostDetected(hostId, desc);
272
273 connectPoints.add(location);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700274 }
275
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700276 // Adds any missing device ports for configured links and host locations.
277 private void addMissingPorts() {
278 deviceService.getDevices().forEach(this::addMissingPorts);
279 }
280
281 // Adds any missing device ports.
282 private void addMissingPorts(Device device) {
283 List<Port> ports = deviceService.getPorts(device.id());
284 Set<ConnectPoint> existing = ports.stream()
285 .map(p -> new ConnectPoint(device.id(), p.number()))
286 .collect(Collectors.toSet());
287 Set<ConnectPoint> missing = connectPoints.stream()
288 .filter(cp -> !existing.contains(cp))
289 .collect(Collectors.toSet());
290
291 if (!missing.isEmpty()) {
292 List<PortDescription> newPorts = Lists.newArrayList();
293 ports.forEach(p -> newPorts.add(description(p)));
294 missing.forEach(cp -> newPorts.add(description(cp)));
295 deviceProviderService.updatePorts(device.id(), newPorts);
296 }
297 }
298
299 // Creates a port description from the specified port.
300 private PortDescription description(Port p) {
301 return new DefaultPortDescription(p.number(), p.isEnabled(), p.type(), p.portSpeed());
302 }
303
304 // Creates a port description from the specified connection point.
305 private PortDescription description(ConnectPoint cp) {
306 return new DefaultPortDescription(cp.port(), true);
307 }
308
309
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700310 // Produces set of annotations from the given JSON node.
311 private SparseAnnotations annotations(JsonNode node) {
312 if (node == null) {
313 return null;
314 }
315
316 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
317 Iterator<String> it = node.fieldNames();
318 while (it.hasNext()) {
319 String k = it.next();
320 builder.set(k, node.get(k).asText());
321 }
322 return builder.build();
323 }
324
325 // Produces a connection point from the specified uri/port text.
326 private ConnectPoint connectPoint(String text) {
327 int i = text.lastIndexOf("/");
328 return new ConnectPoint(deviceId(text.substring(0, i)),
329 portNumber(text.substring(i + 1)));
330 }
331
332 // Returns string form of the named property in the given JSON object.
333 private String get(JsonNode node, String name) {
334 return node.path(name).asText();
335 }
336
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700337 // Returns string form of the named property in the given JSON object.
338 private String get(JsonNode node, String name, String defaultValue) {
339 return node.path(name).asText(defaultValue);
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700340 }
341
342 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700343 public void roleChanged(DeviceId device, MastershipRole newRole) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700344 deviceProviderService.receivedRoleReply(device, newRole, newRole);
345 }
346
347 @Override
348 public void triggerProbe(DeviceId deviceId) {
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700349 }
350
351 @Override
352 public void triggerProbe(Host host) {
353 }
354
355 @Override
356 public ProviderId id() {
357 return PID;
358 }
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700359
360 @Override
Yuta HIGUCHI54815322014-10-31 23:17:08 -0700361 public boolean isReachable(DeviceId device) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700362 return true;
Ayaka Koshibee60d4522014-10-28 15:07:00 -0700363 }
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700364
365 /**
366 * Prepares to count device added/available/removed events.
367 *
368 * @param count number of events to count
369 */
370 protected void prepareForDeviceEvents(int count) {
371 deviceLatch = new CountDownLatch(count);
372 deviceService.addListener(deviceEventCounter);
373 }
374
375 /**
376 * Waits for all expected device added/available/removed events.
377 */
378 protected void waitForDeviceEvents() {
379 try {
380 deviceLatch.await(2, TimeUnit.SECONDS);
381 } catch (InterruptedException e) {
382 log.warn("Device events did not arrive in time");
383 }
384 deviceService.removeListener(deviceEventCounter);
385 }
386
387 // Counts down number of device added/available/removed events.
388 private class DeviceEventCounter implements DeviceListener {
389 @Override
390 public void event(DeviceEvent event) {
391 DeviceEvent.Type type = event.type();
392 if (type == DEVICE_ADDED || type == DEVICE_AVAILABILITY_CHANGED) {
393 deviceLatch.countDown();
394 }
395 }
396 }
397
Thomas Vachuska9252bc32014-10-23 02:33:25 -0700398}