blob: eaafc30c095d6b6a38c49b5cb5d081e18308094a [file] [log] [blame]
Andrea Campanella945ded22016-01-07 13:17:43 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Andrea Campanella945ded22016-01-07 13:17:43 -08003 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.provider.rest.device.impl;
18
19import com.google.common.base.Preconditions;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.ChassisId;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.incubator.net.config.basics.ConfigException;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080029import org.onosproject.net.AnnotationKeys;
Andrea Campanella945ded22016-01-07 13:17:43 -080030import org.onosproject.net.DefaultAnnotations;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070034import org.onosproject.net.PortNumber;
Andrea Campanella945ded22016-01-07 13:17:43 -080035import org.onosproject.net.SparseAnnotations;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080036import org.onosproject.net.behaviour.PortDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080037import org.onosproject.net.config.ConfigFactory;
38import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigRegistry;
41import org.onosproject.net.device.DefaultDeviceDescription;
42import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070043import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella945ded22016-01-07 13:17:43 -080044import org.onosproject.net.device.DeviceProvider;
45import org.onosproject.net.device.DeviceProviderRegistry;
46import org.onosproject.net.device.DeviceProviderService;
Andrea Campanella6c71a052016-04-22 11:56:31 -070047import org.onosproject.net.device.DeviceService;
Andrea Campanella945ded22016-01-07 13:17:43 -080048import org.onosproject.net.provider.AbstractProvider;
49import org.onosproject.net.provider.ProviderId;
50import org.onosproject.protocol.rest.RestSBController;
51import org.onosproject.protocol.rest.RestSBDevice;
52import org.slf4j.Logger;
53
Andrea Campanellac6ecc632016-03-10 17:57:06 -080054import javax.ws.rs.ProcessingException;
Andrea Campanella945ded22016-01-07 13:17:43 -080055import java.util.HashSet;
Andrea Campanella945ded22016-01-07 13:17:43 -080056import java.util.Set;
Andrea Campanella784ee0f2016-02-17 15:50:59 -080057import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
Andrea Campanella945ded22016-01-07 13:17:43 -080059
Andrea Campanella784ee0f2016-02-17 15:50:59 -080060import static org.onlab.util.Tools.groupedThreads;
Andrea Campanella945ded22016-01-07 13:17:43 -080061import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_ADDED;
62import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UPDATED;
63import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
64import static org.slf4j.LoggerFactory.getLogger;
65
66/**
67 * Provider for devices that use REST as means of configuration communication.
68 */
69@Component(immediate = true)
70public class RestDeviceProvider extends AbstractProvider
71 implements DeviceProvider {
72 private static final String APP_NAME = "org.onosproject.restsb";
73 private static final String REST = "rest";
74 private static final String PROVIDER = "org.onosproject.provider.rest.device";
75 private static final String IPADDRESS = "ipaddress";
76 private static final int TEST_CONNECT_TIMEOUT = 1000;
Andrea Campanella2947e622016-01-27 09:23:46 -080077 private static final String HTTPS = "https";
78 private static final String AUTHORIZATION_PROPERTY = "authorization";
79 private static final String BASIC_AUTH_PREFIX = "Basic ";
80 private static final String URL_SEPARATOR = "://";
Andrea Campanella945ded22016-01-07 13:17:43 -080081 private final Logger log = getLogger(getClass());
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceProviderRegistry providerRegistry;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected RestSBController controller;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected NetworkConfigRegistry cfgService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected CoreService coreService;
94
Andrea Campanellad8d92db2016-01-14 16:24:41 -080095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella6c71a052016-04-22 11:56:31 -070096 protected DeviceService deviceService;
Andrea Campanellad8d92db2016-01-14 16:24:41 -080097
Andrea Campanella945ded22016-01-07 13:17:43 -080098
99 private DeviceProviderService providerService;
100 protected static final String ISNOTNULL = "Rest device is not null";
101 private static final String UNKNOWN = "unknown";
102
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800103 private final ExecutorService executor =
Andrea Campanella90f044f2016-03-02 09:14:57 -0800104 Executors.newFixedThreadPool(5, groupedThreads("onos/restsbprovider", "device-installer-%d", log));
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800105
Andrea Campanella945ded22016-01-07 13:17:43 -0800106 private final ConfigFactory factory =
107 new ConfigFactory<ApplicationId, RestProviderConfig>(APP_SUBJECT_FACTORY,
108 RestProviderConfig.class,
109 "restDevices",
110 true) {
111 @Override
112 public RestProviderConfig createConfig() {
113 return new RestProviderConfig();
114 }
115 };
116 private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
117 private ApplicationId appId;
118
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800119 private Set<DeviceId> addedDevices = new HashSet<>();
120
Andrea Campanella945ded22016-01-07 13:17:43 -0800121
122 @Activate
123 public void activate() {
124 appId = coreService.registerApplication(APP_NAME);
125 providerService = providerRegistry.register(this);
126 cfgService.registerConfigFactory(factory);
127 cfgService.addListener(cfgLister);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800128 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800129 log.info("Started");
130 }
131
Andrea Campanella945ded22016-01-07 13:17:43 -0800132 @Deactivate
133 public void deactivate() {
Andrea Campanella86294db2016-03-07 11:42:49 -0800134 cfgService.removeListener(cfgLister);
135 controller.getDevices().keySet().forEach(this::deviceRemoved);
Andrea Campanella945ded22016-01-07 13:17:43 -0800136 providerRegistry.unregister(this);
137 providerService = null;
138 cfgService.unregisterConfigFactory(factory);
Andrea Campanella945ded22016-01-07 13:17:43 -0800139 log.info("Stopped");
140 }
141
142 public RestDeviceProvider() {
143 super(new ProviderId(REST, PROVIDER));
144 }
145
146 @Override
147 public void triggerProbe(DeviceId deviceId) {
148 // TODO: This will be implemented later.
149 log.info("Triggering probe on device {}", deviceId);
150 }
151
152 @Override
153 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
154 // TODO: This will be implemented later.
155 }
156
157
158 @Override
159 public boolean isReachable(DeviceId deviceId) {
160 RestSBDevice restDevice = controller.getDevice(deviceId);
161 if (restDevice == null) {
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800162 log.debug("the requested device id: " +
163 deviceId.toString() +
164 " is not associated to any REST Device");
Andrea Campanella945ded22016-01-07 13:17:43 -0800165 return false;
166 }
167 return restDevice.isActive();
168 }
169
170 private void deviceAdded(RestSBDevice nodeId) {
171 Preconditions.checkNotNull(nodeId, ISNOTNULL);
172 DeviceId deviceId = nodeId.deviceId();
173 ChassisId cid = new ChassisId();
174 String ipAddress = nodeId.ip().toString();
175 SparseAnnotations annotations = DefaultAnnotations.builder()
Marc De Leenheerb0d131c2016-03-01 20:34:59 -0800176 .set(IPADDRESS, ipAddress)
177 .set(AnnotationKeys.PROTOCOL, REST.toUpperCase())
178 .build();
Andrea Campanella945ded22016-01-07 13:17:43 -0800179 DeviceDescription deviceDescription = new DefaultDeviceDescription(
180 deviceId.uri(),
181 Device.Type.SWITCH,
182 UNKNOWN, UNKNOWN,
183 UNKNOWN, UNKNOWN,
184 cid,
185 annotations);
Andrea Campanella945ded22016-01-07 13:17:43 -0800186 nodeId.setActive(true);
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800187 providerService.deviceConnected(deviceId, deviceDescription);
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800188 addedDevices.add(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800189 }
190
Andrea Campanella86294db2016-03-07 11:42:49 -0800191 private void deviceRemoved(DeviceId deviceId) {
192 Preconditions.checkNotNull(deviceId, ISNOTNULL);
Andrea Campanella945ded22016-01-07 13:17:43 -0800193 providerService.deviceDisconnected(deviceId);
Andrea Campanella86294db2016-03-07 11:42:49 -0800194 controller.removeDevice(deviceId);
Andrea Campanella945ded22016-01-07 13:17:43 -0800195 }
196
197 private void connectDevices() {
198 RestProviderConfig cfg = cfgService.getConfig(appId, RestProviderConfig.class);
199 try {
200 if (cfg != null && cfg.getDevicesAddresses() != null) {
201 //Precomputing the devices to be removed
202 Set<RestSBDevice> toBeRemoved = new HashSet<>(controller.getDevices().values());
203 toBeRemoved.removeAll(cfg.getDevicesAddresses());
204 //Adding new devices
205 cfg.getDevicesAddresses().stream()
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800206 .filter(device -> {
207 device.setActive(false);
208 controller.addDevice(device);
209 return testDeviceConnection(device);
210 })
Andrea Campanella945ded22016-01-07 13:17:43 -0800211 .forEach(device -> {
212 deviceAdded(device);
213 });
214 //Removing devices not wanted anymore
Andrea Campanella86294db2016-03-07 11:42:49 -0800215 toBeRemoved.stream().forEach(device -> deviceRemoved(device.deviceId()));
Andrea Campanella945ded22016-01-07 13:17:43 -0800216
217 }
218 } catch (ConfigException e) {
219 log.error("Configuration error {}", e);
220 }
Andrea Campanella2947e622016-01-27 09:23:46 -0800221 log.debug("REST Devices {}", controller.getDevices());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700222 addedDevices.forEach(this::discoverPorts);
Andrea Campanella784ee0f2016-02-17 15:50:59 -0800223 addedDevices.clear();
224
Andrea Campanella945ded22016-01-07 13:17:43 -0800225 }
226
Andrea Campanella6c71a052016-04-22 11:56:31 -0700227 private void discoverPorts(DeviceId deviceId) {
228 Device device = deviceService.getDevice(deviceId);
229 //TODO remove when PortDiscovery is removed from master
230 if (device.is(PortDiscovery.class)) {
231 PortDiscovery portConfig = device.as(PortDiscovery.class);
232 providerService.updatePorts(deviceId,
233 portConfig.getPorts());
234 } else if (device.is(DeviceDescriptionDiscovery.class)) {
235 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
236 device.as(DeviceDescriptionDiscovery.class);
237 providerService.updatePorts(deviceId, deviceDescriptionDiscovery.discoverPortDetails());
238 } else {
239 log.warn("No portGetter behaviour for device {}", deviceId);
240 }
241 }
242
Andrea Campanella945ded22016-01-07 13:17:43 -0800243 private boolean testDeviceConnection(RestSBDevice device) {
244 try {
Andrea Campanellac6ecc632016-03-10 17:57:06 -0800245 return controller.get(device.deviceId(), "", "json") != null;
246 } catch (ProcessingException e) {
247 log.warn("Cannot connect to device {}", device, e);
Andrea Campanella945ded22016-01-07 13:17:43 -0800248 }
249 return false;
250 }
251
252 private class InternalNetworkConfigListener implements NetworkConfigListener {
253
254
255 @Override
256 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800257 executor.execute(RestDeviceProvider.this::connectDevices);
Andrea Campanella945ded22016-01-07 13:17:43 -0800258 }
259
260 @Override
261 public boolean isRelevant(NetworkConfigEvent event) {
262 //TODO refactor
263 return event.configClass().equals(RestProviderConfig.class) &&
264 (event.type() == CONFIG_ADDED ||
265 event.type() == CONFIG_UPDATED);
266 }
267 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700268
269 @Override
270 public void changePortState(DeviceId deviceId, PortNumber portNumber,
271 boolean enable) {
272 // TODO if required
273 }
Andrea Campanella945ded22016-01-07 13:17:43 -0800274}