blob: 6013aeae7822d73f609dc6d953bb819fdf327ea8 [file] [log] [blame]
Marc De Leenheer57a5af02016-12-02 20:54:41 -08001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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 */
16package org.onosproject.provider.tl1.device.impl;
17
Andrea Campanella59b549d2017-04-14 21:58:16 +020018import com.google.common.collect.ImmutableList;
Marc De Leenheer57a5af02016-12-02 20:54:41 -080019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.ChassisId;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.incubator.net.config.basics.ConfigException;
28import org.onosproject.net.AnnotationKeys;
29import org.onosproject.net.DefaultAnnotations;
30import org.onosproject.net.Device;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.MastershipRole;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.SparseAnnotations;
35import org.onosproject.net.config.ConfigFactory;
36import org.onosproject.net.config.NetworkConfigEvent;
37import org.onosproject.net.config.NetworkConfigListener;
38import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella59b549d2017-04-14 21:58:16 +020039import org.onosproject.net.config.basics.SubjectFactories;
Marc De Leenheer57a5af02016-12-02 20:54:41 -080040import org.onosproject.net.device.DefaultDeviceDescription;
41import org.onosproject.net.device.DeviceAdminService;
42import org.onosproject.net.device.DeviceDescription;
43import org.onosproject.net.device.DeviceDescriptionDiscovery;
44import org.onosproject.net.device.DeviceProvider;
45import org.onosproject.net.device.DeviceProviderRegistry;
46import org.onosproject.net.device.DeviceProviderService;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.provider.AbstractProvider;
49import org.onosproject.net.provider.ProviderId;
50import org.onosproject.tl1.Tl1Controller;
51import org.onosproject.tl1.Tl1Device;
52import org.onosproject.tl1.Tl1Listener;
Andrea Campanella59b549d2017-04-14 21:58:16 +020053import org.onosproject.tl1.impl.DefaultTl1Device;
Marc De Leenheer57a5af02016-12-02 20:54:41 -080054import org.slf4j.Logger;
55
56import java.io.IOException;
57import java.net.InetSocketAddress;
58import java.net.Socket;
59import java.net.URI;
60import java.net.URISyntaxException;
Andrea Campanella59b549d2017-04-14 21:58:16 +020061import java.util.List;
Marc De Leenheer57a5af02016-12-02 20:54:41 -080062import java.util.NoSuchElementException;
Andrea Campanella59b549d2017-04-14 21:58:16 +020063import java.util.Set;
Marc De Leenheer57a5af02016-12-02 20:54:41 -080064
65import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
66import static org.slf4j.LoggerFactory.getLogger;
67
68/**
69 * Device provider for TL1 devices.
Andrea Campanella59b549d2017-04-14 21:58:16 +020070 * <p>
Marc De Leenheer57a5af02016-12-02 20:54:41 -080071 * Sits between ONOS provider service and the TL1 controller.
72 * Relies on network config subsystem to know about devices.
73 */
74@Component(immediate = true)
75public class Tl1DeviceProvider extends AbstractProvider implements DeviceProvider {
76 private static final String APP_NAME = "org.onosproject.tl1";
Andrea Campanella59b549d2017-04-14 21:58:16 +020077 protected static final String TL1 = "tl1";
Marc De Leenheer57a5af02016-12-02 20:54:41 -080078 private static final String PROVIDER = "org.onosproject.provider.tl1.device";
79 private static final String UNKNOWN = "unknown";
80 private static final int REACHABILITY_TIMEOUT = 2000; // in milliseconds
81
82 private final Logger log = getLogger(getClass());
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected NetworkConfigRegistry cfgRegistry;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DeviceProviderRegistry providerRegistry;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DeviceAdminService deviceAdminService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected DeviceService deviceService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected Tl1Controller controller;
101
102 private ApplicationId appId;
103 private NetworkConfigListener cfgListener = new InnerConfigListener();
104 private Tl1Listener tl1Listener = new InnerTl1Listener();
105 private DeviceProviderService providerService;
106
Andrea Campanella59b549d2017-04-14 21:58:16 +0200107 private final List<ConfigFactory> factories = ImmutableList.of(
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800108 new ConfigFactory<ApplicationId, Tl1ProviderConfig>(APP_SUBJECT_FACTORY,
Andrea Campanella59b549d2017-04-14 21:58:16 +0200109 Tl1ProviderConfig.class,
110 "tl1_devices",
111 true) {
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800112 @Override
113 public Tl1ProviderConfig createConfig() {
114 return new Tl1ProviderConfig();
115 }
Andrea Campanella59b549d2017-04-14 21:58:16 +0200116 },
117 new ConfigFactory<DeviceId, Tl1DeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
118 Tl1DeviceConfig.class,
119 TL1) {
120 @Override
121 public Tl1DeviceConfig createConfig() {
122 return new Tl1DeviceConfig();
123 }
124 });
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800125
126 @Activate
127 public void activate() {
128 appId = coreService.registerApplication(APP_NAME);
129 providerService = providerRegistry.register(this);
130 cfgRegistry.addListener(cfgListener);
131 controller.addListener(tl1Listener);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200132 factories.forEach(cfgRegistry::registerConfigFactory);
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800133 registerDevices();
Andrea Campanella59b549d2017-04-14 21:58:16 +0200134 connectDevices();
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800135 log.info("Started");
136 }
137
138 @Deactivate
139 public void deactivate() {
140 controller.removeListener(tl1Listener);
141 cfgRegistry.removeListener(cfgListener);
142 controller.getDeviceIds().forEach(deviceId -> {
143 controller.removeDevice(deviceId);
144 deviceAdminService.removeDevice(deviceId);
145 });
146 providerRegistry.unregister(this);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200147 factories.forEach(cfgRegistry::unregisterConfigFactory);
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800148 providerService = null;
149 log.info("Stopped");
150 }
151
152 public Tl1DeviceProvider() {
153 super(new ProviderId(TL1, PROVIDER));
154 }
155
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800156 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800157 public void triggerProbe(DeviceId deviceId) {
158 // TODO
159 }
160
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800161 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800162 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
163 switch (newRole) {
164 case MASTER:
165 controller.connectDevice(deviceId);
166 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
167 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
168 break;
169 case STANDBY:
170 controller.disconnectDevice(deviceId);
171 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
172 break;
173 case NONE:
174 controller.disconnectDevice(deviceId);
175 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
176 break;
177 default:
178 log.error("Invalid mastership state: {}", newRole);
179 }
180 }
181
182 // Assumes device is registered in TL1 controller.
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800183 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800184 public boolean isReachable(DeviceId deviceId) {
185 try {
186 // First check if device is already connected.
187 // If not, try to open a socket.
188 Tl1Device device = controller.getDevice(deviceId).get();
189 if (device.isConnected()) {
190 return true;
191 }
192
193 Socket socket = new Socket();
194 socket.connect(new InetSocketAddress(device.ip().toInetAddress(), device.port()), REACHABILITY_TIMEOUT);
195 socket.close();
196 return true;
197 } catch (NoSuchElementException | IOException | IllegalArgumentException e) {
198 log.error("Cannot reach device {}", deviceId, e);
199 return false;
200 }
201 }
202
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800203 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800204 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
205 // TODO
206 }
207
Andrea Campanella59b549d2017-04-14 21:58:16 +0200208 //Old method to register devices provided via net-cfg under apps/tl1/ tree
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800209 void registerDevices() {
210 Tl1ProviderConfig cfg = cfgRegistry.getConfig(appId, Tl1ProviderConfig.class);
211
212 if (cfg == null) {
213 return;
214 }
215
216 try {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200217 cfg.readDevices().forEach(this::connectDevice);
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800218 } catch (ConfigException e) {
219 log.error("Cannot parse network configuration", e);
220 }
221 }
222
Andrea Campanella59b549d2017-04-14 21:58:16 +0200223 //Method to register devices provided via net-cfg under devices/ tree
224 private void connectDevices() {
225 Set<DeviceId> deviceSubjects =
226 cfgRegistry.getSubjects(DeviceId.class, Tl1DeviceConfig.class);
227 deviceSubjects.forEach(deviceId -> {
228 Tl1DeviceConfig config =
229 cfgRegistry.getConfig(deviceId, Tl1DeviceConfig.class);
230 connectDevice(new DefaultTl1Device(config.ip(), config.port(), config.username(),
231 config.password()));
232 });
233 }
234
235 // Register a device in the core and in the TL1 controller.
236 private void connectDevice(Tl1Device device) {
237 try {
238 // Add device to TL1 controller
239 DeviceId deviceId = DeviceId.deviceId(
240 new URI(TL1, device.ip() + ":" + device.port(), null));
241
242 if (controller.addDevice(deviceId, device)) {
243 SparseAnnotations ann = DefaultAnnotations.builder()
244 .set(AnnotationKeys.PROTOCOL, TL1.toUpperCase())
245 .build();
246 // Register device in the core with default parameters and mark it as unavailable
247 DeviceDescription dd = new DefaultDeviceDescription(deviceId.uri(),
248 Device.Type.SWITCH,
249 UNKNOWN, UNKNOWN,
250 UNKNOWN, UNKNOWN,
251 new ChassisId(),
252 false, ann);
253 providerService.deviceConnected(deviceId, dd);
254 }
255 } catch (URISyntaxException e) {
256 log.error("Skipping device {}", device, e);
257 }
258 }
259
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800260 /**
261 * Tries to update the device and port descriptions through the {@code DeviceDescriptionDiscovery} behaviour.
262 *
263 * @param deviceId the device
264 */
265 void updateDevice(DeviceId deviceId) {
266 Device device = deviceService.getDevice(deviceId);
267
268 if (!device.is(DeviceDescriptionDiscovery.class)) {
269 return;
270 }
271
272 try {
273 // Update device description
274 DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
275 DeviceDescription dd = discovery.discoverDeviceDetails();
276 if (dd == null) {
277 return;
278 }
279 providerService.deviceConnected(deviceId,
Andrea Campanella59b549d2017-04-14 21:58:16 +0200280 new DefaultDeviceDescription(dd, true, dd.annotations()));
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800281 // Update ports
282 providerService.updatePorts(deviceId, discovery.discoverPortDetails());
283 } catch (IllegalStateException | IllegalArgumentException e) {
284 log.error("Cannot update device description {}", deviceId, e);
285 }
286 }
287
288 /**
289 * Listener for network configuration events.
290 */
291 private class InnerConfigListener implements NetworkConfigListener {
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800292 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800293 public void event(NetworkConfigEvent event) {
294 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200295 if (event.configClass().equals(Tl1DeviceConfig.class)) {
296 connectDevices();
297 } else {
298 log.warn("Injecting device via this Json is deprecated, " +
299 "please put configuration under devices/");
300 registerDevices();
301 }
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800302 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
303 // TODO: calculate delta
Andrea Campanella59b549d2017-04-14 21:58:16 +0200304 if (event.configClass().equals(Tl1DeviceConfig.class)) {
305 connectDevices();
306 } else {
307 log.warn("Injecting device via this Json is deprecated, " +
308 "please put configuration under devices/");
309 registerDevices();
310 }
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800311 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
312 controller.getDeviceIds().forEach(deviceId -> {
313 controller.removeDevice(deviceId);
314 deviceAdminService.removeDevice(deviceId);
315 });
316 }
317 }
318
Yuta HIGUCHI08057cb2017-03-06 10:07:06 -0800319 @Override
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800320 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200321 return (event.configClass().equals(Tl1DeviceConfig.class) ||
322 event.configClass().equals(Tl1ProviderConfig.class)) &&
Marc De Leenheer57a5af02016-12-02 20:54:41 -0800323 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
324 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED ||
325 event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED);
326 }
327 }
328
329 /**
330 * Listener for TL1 events.
331 */
332 private class InnerTl1Listener implements Tl1Listener {
333 @Override
334 public void deviceConnected(DeviceId deviceId) {
335 updateDevice(deviceId);
336 }
337
338 @Override
339 public void deviceDisconnected(DeviceId deviceId) {
340 providerService.deviceDisconnected(deviceId);
341 }
342 }
343}