blob: 10a699735deae1a7c5bd1d341866dc9fabcf7eb5 [file] [log] [blame]
Carmelo Cascone3bb71c12016-04-06 21:30:44 -07001/*
2 * Copyright 2014-2016 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 */
16
17package org.onosproject.provider.bmv2.device.impl;
18
19import com.google.common.collect.Sets;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.onlab.packet.ChassisId;
24import org.onosproject.common.net.AbstractDeviceProvider;
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.behaviour.PortDiscovery;
36import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
40import org.onosproject.net.device.DefaultDeviceDescription;
41import org.onosproject.net.device.DeviceDescription;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.provider.ProviderId;
44import org.slf4j.Logger;
45
46import java.net.URI;
47import java.net.URISyntaxException;
48import java.util.Set;
49import java.util.concurrent.ExecutorService;
50import java.util.concurrent.Executors;
51import java.util.concurrent.TimeUnit;
52
53import static org.onlab.util.Tools.groupedThreads;
54import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.ping;
55import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
56import static org.slf4j.LoggerFactory.getLogger;
57
58/**
59 * BMv2 device provider.
60 */
61@Component(immediate = true)
62public class Bmv2DeviceProvider extends AbstractDeviceProvider {
63
64 private final Logger log = getLogger(Bmv2DeviceProvider.class);
65
66 public static final String MANUFACTURER = "p4.org";
67 public static final String HW_VERSION = "bmv2";
68 private static final String APP_NAME = "org.onosproject.bmv2";
69 private static final String UNKNOWN = "unknown";
70 public static final String SCHEME = "bmv2";
71
72 private final ExecutorService deviceDiscoveryExecutor = Executors
73 .newFixedThreadPool(5, groupedThreads("onos/bmv2", "device-discovery", log));
74
75 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
76
77 private final ConfigFactory cfgFactory =
78 new ConfigFactory<ApplicationId, Bmv2ProviderConfig>(
79 APP_SUBJECT_FACTORY, Bmv2ProviderConfig.class,
80 "devices", true) {
81 @Override
82 public Bmv2ProviderConfig createConfig() {
83 return new Bmv2ProviderConfig();
84 }
85 };
86
87 private final Set<DeviceId> activeDevices = Sets.newConcurrentHashSet();
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected NetworkConfigRegistry netCfgService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected CoreService coreService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DeviceService deviceService;
97
98 private ApplicationId appId;
99
100 /**
101 * Creates a Bmv2 device provider with the supplied identifier.
102 */
103 public Bmv2DeviceProvider() {
104 super(new ProviderId("bmv2", "org.onosproject.provider.device"));
105 }
106
107 protected static DeviceId deviceIdOf(Bmv2ProviderConfig.Bmv2DeviceInfo info) {
108 try {
109 return DeviceId.deviceId(new URI(
110 SCHEME, info.ip().toString() + ":" + info.port(), null));
111 } catch (URISyntaxException e) {
112 throw new IllegalArgumentException(
113 "Unable to build deviceID for device "
114 + info.ip().toString() + ":" + info.ip().toString(),
115 e);
116 }
117 }
118
119 @Override
120 protected void activate() {
121 appId = coreService.registerApplication(APP_NAME);
122 netCfgService.registerConfigFactory(cfgFactory);
123 netCfgService.addListener(cfgListener);
124
125 super.activate();
126 }
127
128 @Override
129 protected void deactivate() {
130 try {
131 activeDevices.stream().forEach(did -> {
132 deviceDiscoveryExecutor.execute(() -> disconnectDevice(did));
133 });
134 deviceDiscoveryExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
135 } catch (InterruptedException e) {
136 log.error("Device discovery threads did not terminate");
137 }
138 deviceDiscoveryExecutor.shutdownNow();
139 netCfgService.unregisterConfigFactory(cfgFactory);
140 netCfgService.removeListener(cfgListener);
141
142 super.deactivate();
143 }
144
145 @Override
146 public void triggerProbe(DeviceId deviceId) {
147 deviceDiscoveryExecutor.execute(() -> executeProbe(deviceId));
148 }
149
150 private void executeProbe(DeviceId did) {
151 boolean reachable = isReachable(did);
152 log.debug("Probed device: id={}, reachable={}",
153 did.toString(),
154 reachable);
155 if (reachable) {
156 connectDevice(did);
157 } else {
158 disconnectDevice(did);
159 }
160 }
161
162 @Override
163 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
164 log.debug("roleChanged() is not yet implemented");
165 // TODO: implement mastership handling
166 }
167
168 @Override
169 public boolean isReachable(DeviceId deviceId) {
170 return ping(deviceId);
171 }
172
173 @Override
174 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
175 log.debug("changePortState() is not yet implemented");
176 // TODO: implement port handling
177 }
178
179 private void connectDevice(DeviceId did) {
180 log.debug("Trying to create device on ONOS core: {}", did);
181 SparseAnnotations annotations = DefaultAnnotations.builder()
182 .set(AnnotationKeys.PROTOCOL, SCHEME)
183 .build();
184 DeviceDescription descr = new DefaultDeviceDescription(
185 did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION,
186 UNKNOWN, UNKNOWN, new ChassisId(), annotations);
187 providerService.deviceConnected(did, descr);
188 activeDevices.add(did);
189 discoverPorts(did);
190 }
191
192 private void discoverPorts(DeviceId did) {
193 Device device = deviceService.getDevice(did);
194 if (device.is(PortDiscovery.class)) {
195 PortDiscovery portConfig = device.as(PortDiscovery.class);
196 providerService.updatePorts(did, portConfig.getPorts());
197 } else {
198 log.warn("No PortDiscovery behavior for device {}", did);
199 }
200 }
201
202 private void disconnectDevice(DeviceId did) {
203 log.debug("Trying to remove device from ONOS core: {}", did);
204 providerService.deviceDisconnected(did);
205 activeDevices.remove(did);
206 }
207
208 /**
209 * Handles net-cfg events.
210 */
211 private class InternalNetworkConfigListener implements NetworkConfigListener {
212
213 @Override
214 public void event(NetworkConfigEvent event) {
215 Bmv2ProviderConfig cfg = netCfgService.getConfig(appId, Bmv2ProviderConfig.class);
216 if (cfg != null) {
217 try {
218 cfg.getDevicesInfo().stream().forEach(info -> {
219 triggerProbe(deviceIdOf(info));
220 });
221 } catch (ConfigException e) {
222 log.error("Unable to read config: " + e);
223 }
224 } else {
225 log.error("Unable to read config (was null)");
226 }
227 }
228
229 @Override
230 public boolean isRelevant(NetworkConfigEvent event) {
231 return event.configClass().equals(Bmv2ProviderConfig.class) &&
232 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
233 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
234 }
235 }
236}