blob: 7e984968c5297afd262cb299e770582c0c7e7029 [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.onlab.packet.ChassisId;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.incubator.net.config.basics.ConfigException;
27import org.onosproject.net.AnnotationKeys;
28import org.onosproject.net.DefaultAnnotations;
29import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.MastershipRole;
32import org.onosproject.net.PortNumber;
33import org.onosproject.net.SparseAnnotations;
34import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigEvent;
36import org.onosproject.net.config.NetworkConfigListener;
37import org.onosproject.net.config.NetworkConfigRegistry;
38import org.onosproject.net.device.DefaultDeviceDescription;
39import org.onosproject.net.device.DeviceAdminService;
40import org.onosproject.net.device.DeviceDescription;
41import org.onosproject.net.device.DeviceDescriptionDiscovery;
42import org.onosproject.net.device.DeviceProvider;
43import org.onosproject.net.device.DeviceProviderRegistry;
44import org.onosproject.net.device.DeviceProviderService;
45import org.onosproject.net.device.DeviceService;
46import org.onosproject.net.provider.AbstractProvider;
47import org.onosproject.net.provider.ProviderId;
48import org.onosproject.tl1.Tl1Controller;
49import org.onosproject.tl1.Tl1Device;
50import org.onosproject.tl1.Tl1Listener;
51import org.slf4j.Logger;
52
53import java.io.IOException;
54import java.net.InetSocketAddress;
55import java.net.Socket;
56import java.net.URI;
57import java.net.URISyntaxException;
58import java.util.NoSuchElementException;
59
60import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
61import static org.slf4j.LoggerFactory.getLogger;
62
63/**
64 * Device provider for TL1 devices.
65 *
66 * Sits between ONOS provider service and the TL1 controller.
67 * Relies on network config subsystem to know about devices.
68 */
69@Component(immediate = true)
70public class Tl1DeviceProvider extends AbstractProvider implements DeviceProvider {
71 private static final String APP_NAME = "org.onosproject.tl1";
72 private static final String TL1 = "tl1";
73 private static final String PROVIDER = "org.onosproject.provider.tl1.device";
74 private static final String UNKNOWN = "unknown";
75 private static final int REACHABILITY_TIMEOUT = 2000; // in milliseconds
76
77 private final Logger log = getLogger(getClass());
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected NetworkConfigRegistry cfgRegistry;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected CoreService coreService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceProviderRegistry providerRegistry;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected DeviceAdminService deviceAdminService;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected DeviceService deviceService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected Tl1Controller controller;
96
97 private ApplicationId appId;
98 private NetworkConfigListener cfgListener = new InnerConfigListener();
99 private Tl1Listener tl1Listener = new InnerTl1Listener();
100 private DeviceProviderService providerService;
101
102 private final ConfigFactory cfgFactory =
103 new ConfigFactory<ApplicationId, Tl1ProviderConfig>(APP_SUBJECT_FACTORY,
104 Tl1ProviderConfig.class,
105 "devices",
106 true) {
107 @Override
108 public Tl1ProviderConfig createConfig() {
109 return new Tl1ProviderConfig();
110 }
111 };
112
113 @Activate
114 public void activate() {
115 appId = coreService.registerApplication(APP_NAME);
116 providerService = providerRegistry.register(this);
117 cfgRegistry.addListener(cfgListener);
118 controller.addListener(tl1Listener);
119 cfgRegistry.registerConfigFactory(cfgFactory);
120 registerDevices();
121 log.info("Started");
122 }
123
124 @Deactivate
125 public void deactivate() {
126 controller.removeListener(tl1Listener);
127 cfgRegistry.removeListener(cfgListener);
128 controller.getDeviceIds().forEach(deviceId -> {
129 controller.removeDevice(deviceId);
130 deviceAdminService.removeDevice(deviceId);
131 });
132 providerRegistry.unregister(this);
133 cfgRegistry.unregisterConfigFactory(cfgFactory);
134 providerService = null;
135 log.info("Stopped");
136 }
137
138 public Tl1DeviceProvider() {
139 super(new ProviderId(TL1, PROVIDER));
140 }
141
142 public void triggerProbe(DeviceId deviceId) {
143 // TODO
144 }
145
146 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
147 switch (newRole) {
148 case MASTER:
149 controller.connectDevice(deviceId);
150 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
151 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
152 break;
153 case STANDBY:
154 controller.disconnectDevice(deviceId);
155 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
156 break;
157 case NONE:
158 controller.disconnectDevice(deviceId);
159 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
160 break;
161 default:
162 log.error("Invalid mastership state: {}", newRole);
163 }
164 }
165
166 // Assumes device is registered in TL1 controller.
167 public boolean isReachable(DeviceId deviceId) {
168 try {
169 // First check if device is already connected.
170 // If not, try to open a socket.
171 Tl1Device device = controller.getDevice(deviceId).get();
172 if (device.isConnected()) {
173 return true;
174 }
175
176 Socket socket = new Socket();
177 socket.connect(new InetSocketAddress(device.ip().toInetAddress(), device.port()), REACHABILITY_TIMEOUT);
178 socket.close();
179 return true;
180 } catch (NoSuchElementException | IOException | IllegalArgumentException e) {
181 log.error("Cannot reach device {}", deviceId, e);
182 return false;
183 }
184 }
185
186 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
187 // TODO
188 }
189
190 // Register all devices in the core and in the TL1 controller
191 void registerDevices() {
192 Tl1ProviderConfig cfg = cfgRegistry.getConfig(appId, Tl1ProviderConfig.class);
193
194 if (cfg == null) {
195 return;
196 }
197
198 try {
199 cfg.readDevices().forEach(device -> {
200 try {
201 // Add device to TL1 controller
202 DeviceId deviceId = DeviceId.deviceId(
203 new URI(TL1, device.ip() + ":" + device.port(), null));
204
205 if (controller.addDevice(deviceId, device)) {
206 SparseAnnotations ann = DefaultAnnotations.builder()
207 .set(AnnotationKeys.PROTOCOL, TL1.toUpperCase())
208 .build();
209 // Register device in the core with default parameters and mark it as unavailable
210 DeviceDescription dd = new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH, UNKNOWN,
211 UNKNOWN, UNKNOWN, UNKNOWN, new ChassisId(), false, ann);
212 providerService.deviceConnected(deviceId, dd);
213 }
214 } catch (URISyntaxException e) {
215 log.error("Skipping device {}", device, e);
216 }
217 });
218 } catch (ConfigException e) {
219 log.error("Cannot parse network configuration", e);
220 }
221 }
222
223 /**
224 * Tries to update the device and port descriptions through the {@code DeviceDescriptionDiscovery} behaviour.
225 *
226 * @param deviceId the device
227 */
228 void updateDevice(DeviceId deviceId) {
229 Device device = deviceService.getDevice(deviceId);
230
231 if (!device.is(DeviceDescriptionDiscovery.class)) {
232 return;
233 }
234
235 try {
236 // Update device description
237 DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
238 DeviceDescription dd = discovery.discoverDeviceDetails();
239 if (dd == null) {
240 return;
241 }
242 providerService.deviceConnected(deviceId,
243 new DefaultDeviceDescription(dd, true, dd.annotations()));
244 // Update ports
245 providerService.updatePorts(deviceId, discovery.discoverPortDetails());
246 } catch (IllegalStateException | IllegalArgumentException e) {
247 log.error("Cannot update device description {}", deviceId, e);
248 }
249 }
250
251 /**
252 * Listener for network configuration events.
253 */
254 private class InnerConfigListener implements NetworkConfigListener {
255 public void event(NetworkConfigEvent event) {
256 if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
257 registerDevices();
258 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
259 // TODO: calculate delta
260 registerDevices();
261 } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
262 controller.getDeviceIds().forEach(deviceId -> {
263 controller.removeDevice(deviceId);
264 deviceAdminService.removeDevice(deviceId);
265 });
266 }
267 }
268
269 public boolean isRelevant(NetworkConfigEvent event) {
270 return event.configClass().equals(Tl1ProviderConfig.class) &&
271 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
272 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED ||
273 event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED);
274 }
275 }
276
277 /**
278 * Listener for TL1 events.
279 */
280 private class InnerTl1Listener implements Tl1Listener {
281 @Override
282 public void deviceConnected(DeviceId deviceId) {
283 updateDevice(deviceId);
284 }
285
286 @Override
287 public void deviceDisconnected(DeviceId deviceId) {
288 providerService.deviceDisconnected(deviceId);
289 }
290 }
291}