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