blob: a98ea390b9cfb3f9c02f23bb3a63509aa03595ad [file] [log] [blame]
Yuta HIGUCHI76767fa2017-03-10 14:51:30 -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.net.config.impl;
17
18import static java.util.concurrent.Executors.newSingleThreadExecutor;
19import static org.onlab.util.Tools.groupedThreads;
20import static org.onosproject.net.PortNumber.portNumber;
21import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
22import static org.slf4j.LoggerFactory.getLogger;
23
24import java.util.ArrayList;
25import java.util.EnumSet;
26import java.util.List;
27import java.util.Optional;
28import java.util.Set;
29import java.util.concurrent.ExecutorService;
30import java.util.concurrent.TimeUnit;
31
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Reference;
36import org.apache.felix.scr.annotations.ReferenceCardinality;
37import org.apache.felix.scr.annotations.Service;
38import org.onlab.packet.ChassisId;
39import org.onosproject.net.Device.Type;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.MastershipRole;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.config.ConfigFactory;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
47import org.onosproject.net.config.NetworkConfigService;
48import org.onosproject.net.config.basics.BasicDeviceConfig;
49import org.onosproject.net.config.inject.DeviceInjectionConfig;
50import org.onosproject.net.device.DefaultDeviceDescription;
51import org.onosproject.net.device.DefaultPortDescription;
52import org.onosproject.net.device.DeviceDescriptionDiscovery;
53import org.onosproject.net.device.DeviceProvider;
54import org.onosproject.net.device.DeviceProviderRegistry;
55import org.onosproject.net.device.DeviceProviderService;
56import org.onosproject.net.device.DeviceService;
57import org.onosproject.net.device.PortDescription;
58import org.onosproject.net.driver.DefaultDriverData;
59import org.onosproject.net.driver.DefaultDriverHandler;
60import org.onosproject.net.driver.DriverService;
61import org.onosproject.net.provider.ProviderId;
62import org.slf4j.Logger;
63
64import com.google.common.annotations.Beta;
65import com.google.common.collect.ImmutableList;
66import com.google.common.collect.ImmutableSet;
67
68// TODO In theory just @Component should be sufficient,
69// but won't work without @Service. Need investigation.
70/**
71 * Component to monitor DeviceInjectionConfig changes.
72 */
73@Beta
74@Service(value = DeviceInjectionConfigMonitor.class)
75@Component(immediate = true)
76public class DeviceInjectionConfigMonitor {
77
78 private final Logger log = getLogger(getClass());
79
80 private final ProviderId pid = new ProviderId("inject", "org.onosproject.inject");
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected NetworkConfigService netcfgService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceService deviceService;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected DeviceProviderRegistry deviceProviderRegistry;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected DriverService driverService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected NetworkConfigRegistry netcfgRegistry;
96
97 private final List<ConfigFactory<?, ?>> factories = ImmutableList.of(
98 new ConfigFactory<DeviceId, DeviceInjectionConfig>(DEVICE_SUBJECT_FACTORY,
99 DeviceInjectionConfig.class,
100 DeviceInjectionConfig.CONFIG_KEY) {
101 @Override
102 public DeviceInjectionConfig createConfig() {
103 return new DeviceInjectionConfig();
104 }
105 });
106
107
108 private final InternalConfigListener listener = new InternalConfigListener();
109
110 private ExecutorService worker;
111
112 private InternalDeviceProvider deviceProvider = new InternalDeviceProvider();
113
114 private DeviceProviderService providerService;
115
116
117 @Activate
118 public void activate() {
119 worker = newSingleThreadExecutor(groupedThreads("onos/inject",
120 "worker",
121 log));
122 providerService = deviceProviderRegistry.register(deviceProvider);
123
124 netcfgService.addListener(listener);
125
126 factories.forEach(netcfgRegistry::registerConfigFactory);
127
128 log.info("Started");
129 }
130
131 @Deactivate
132 public void deactivate() {
133 netcfgService.removeListener(listener);
134
135 deviceProviderRegistry.unregister(deviceProvider);
136
137 worker.shutdown();
138 try {
139 worker.awaitTermination(5, TimeUnit.SECONDS);
140 } catch (InterruptedException e) {
141 log.warn("Interrupted.", e);
142 Thread.currentThread().interrupt();
143 }
144 factories.forEach(netcfgRegistry::unregisterConfigFactory);
145
146 log.info("Stopped");
147 }
148
149 private void removeDevice(DeviceId did) {
150 providerService.deviceDisconnected(did);
151 }
152
153 private void injectDevice(DeviceId did) {
154 Optional<BasicDeviceConfig> basic =
155 Optional.ofNullable(netcfgService.getConfig(did, BasicDeviceConfig.class));
156 Optional<DeviceDescriptionDiscovery> discovery = basic
157 .map(BasicDeviceConfig::driver)
158 .map(driverService::getDriver)
159 .filter(drvr -> drvr.hasBehaviour(DeviceDescriptionDiscovery.class))
160 .map(drvr -> drvr.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(drvr, did)),
161 DeviceDescriptionDiscovery.class));
162
163 if (discovery.isPresent()) {
164 providerService.deviceConnected(did,
165 discovery.get().discoverDeviceDetails());
166 providerService.updatePorts(did,
167 discovery.get().discoverPortDetails());
168 } else {
169
170 String unk = "UNKNOWN";
171 DefaultDeviceDescription desc = new DefaultDeviceDescription(
172 did.uri(),
173 basic.map(BasicDeviceConfig::type).orElse(Type.SWITCH),
174 basic.map(BasicDeviceConfig::manufacturer).orElse(unk),
175 basic.map(BasicDeviceConfig::hwVersion).orElse(unk),
176 basic.map(BasicDeviceConfig::swVersion).orElse(unk),
177 basic.map(BasicDeviceConfig::serial).orElse(unk),
178 new ChassisId(),
179 true);
180 providerService.deviceConnected(did, desc);
181
182 Optional<DeviceInjectionConfig> inject =
183 Optional.ofNullable(netcfgService.getConfig(did, DeviceInjectionConfig.class));
184
185 String ports = inject.map(DeviceInjectionConfig::ports).orElse("0");
186 int numPorts = Integer.parseInt(ports);
187 List<PortDescription> portDescs = new ArrayList<>(numPorts);
188 for (int i = 1; i <= numPorts; ++i) {
189 // TODO inject port details if something like BasicPortConfig was created
190 PortNumber number = portNumber(i);
191 boolean isEnabled = true;
192 portDescs.add(new DefaultPortDescription(number, isEnabled));
193 }
194 providerService.updatePorts(did, portDescs);
195 }
196 }
197
198 final class InternalDeviceProvider implements DeviceProvider {
199 @Override
200 public ProviderId id() {
201 return pid;
202 }
203
204 @Override
205 public void triggerProbe(DeviceId deviceId) {
206 worker.execute(() -> injectDevice(deviceId));
207 }
208
209 @Override
210 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
211 providerService.receivedRoleReply(deviceId, newRole, newRole);
212 }
213
214 @Override
215 public boolean isReachable(DeviceId deviceId) {
216 return true;
217 }
218
219 @Override
220 public void changePortState(DeviceId deviceId, PortNumber portNumber,
221 boolean enable) {
222 // TODO handle request to change port state from controller
223 }
224 }
225
226 /**
227 * Listens for Config updates.
228 */
229 final class InternalConfigListener
230 implements NetworkConfigListener {
231
232 /**
233 * Relevant {@link NetworkConfigEvent} type.
234 */
235 private final Set<NetworkConfigEvent.Type> relevant
236 = ImmutableSet.copyOf(EnumSet.of(
237 NetworkConfigEvent.Type.CONFIG_ADDED,
238 NetworkConfigEvent.Type.CONFIG_UPDATED,
239 NetworkConfigEvent.Type.CONFIG_REMOVED));
240
241 @Override
242 public boolean isRelevant(NetworkConfigEvent event) {
243 return event.configClass() == DeviceInjectionConfig.class &&
244 relevant.contains(event.type());
245 }
246
247 @Override
248 public void event(NetworkConfigEvent event) {
249 worker.execute(() -> processEvent(event));
250 }
251
252 /**
253 * Process Config add/update/remove event.
254 * <p>
255 * Note: will be executed in the worker thread.
256 *
257 * @param event add/update/remove event
258 */
259 protected void processEvent(NetworkConfigEvent event) {
260
261 DeviceId did = (DeviceId) event.subject();
262 if (!did.uri().getScheme().equals(pid.scheme())) {
263 log.warn("Attempt to inject unexpected scheme {}", did);
264 return;
265 }
266 log.debug("{} to {}: {}", event.type(), did, event);
267
268 // TODO if there's exist one probably better to follow master
269// if (deviceService.getRole(did) != MastershipRole.MASTER) {
270// log.debug("Not the master, ignoring. {}", event);
271// return;
272// }
273
274 switch (event.type()) {
275 case CONFIG_ADDED:
276 case CONFIG_UPDATED:
277 injectDevice(did);
278 break;
279 case CONFIG_REMOVED:
280 removeDevice(did);
281 break;
282
283 case CONFIG_REGISTERED:
284 case CONFIG_UNREGISTERED:
285 default:
286 log.warn("Ignoring unexpected event: {}", event);
287 break;
288 }
289 }
290 }
291
292}