blob: 0b593bd99402e6965bf9afd7218aeae524657917 [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.config.impl;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.onlab.packet.ChassisId;
import org.onosproject.net.Device.Type;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.basics.BasicDeviceConfig;
import org.onosproject.net.config.inject.DeviceInjectionConfig;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.provider.ProviderId;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
import static org.slf4j.LoggerFactory.getLogger;
// TODO In theory just @Component should be sufficient,
// but won't work without @Service. Need investigation.
/**
* Component to monitor DeviceInjectionConfig changes.
*/
@Beta
@Component(immediate = true, service = DeviceInjectionConfigMonitor.class)
public class DeviceInjectionConfigMonitor {
private final Logger log = getLogger(getClass());
private final ProviderId pid = new ProviderId("inject", "org.onosproject.inject");
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected NetworkConfigService netcfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceProviderRegistry deviceProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected NetworkConfigRegistry netcfgRegistry;
private final List<ConfigFactory<?, ?>> factories = ImmutableList.of(
new ConfigFactory<DeviceId, DeviceInjectionConfig>(DEVICE_SUBJECT_FACTORY,
DeviceInjectionConfig.class,
DeviceInjectionConfig.CONFIG_KEY) {
@Override
public DeviceInjectionConfig createConfig() {
return new DeviceInjectionConfig();
}
});
private final InternalConfigListener listener = new InternalConfigListener();
private ExecutorService worker;
private InternalDeviceProvider deviceProvider = new InternalDeviceProvider();
private DeviceProviderService providerService;
@Activate
public void activate() {
worker = newSingleThreadExecutor(groupedThreads("onos/inject",
"worker",
log));
providerService = deviceProviderRegistry.register(deviceProvider);
netcfgService.addListener(listener);
factories.forEach(netcfgRegistry::registerConfigFactory);
log.info("Started");
}
@Deactivate
public void deactivate() {
netcfgService.removeListener(listener);
deviceProviderRegistry.unregister(deviceProvider);
worker.shutdown();
try {
worker.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted.", e);
Thread.currentThread().interrupt();
}
factories.forEach(netcfgRegistry::unregisterConfigFactory);
log.info("Stopped");
}
private void removeDevice(DeviceId did) {
providerService.deviceDisconnected(did);
}
private void injectDevice(DeviceId did) {
Optional<BasicDeviceConfig> basic =
Optional.ofNullable(netcfgService.getConfig(did, BasicDeviceConfig.class));
Optional<DeviceDescriptionDiscovery> discovery = basic
.map(BasicDeviceConfig::driver)
.map(driverService::getDriver)
.filter(drvr -> drvr.hasBehaviour(DeviceDescriptionDiscovery.class))
.map(drvr -> drvr.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(drvr, did)),
DeviceDescriptionDiscovery.class));
if (discovery.isPresent()) {
providerService.deviceConnected(did,
discovery.get().discoverDeviceDetails());
providerService.updatePorts(did,
discovery.get().discoverPortDetails());
} else {
String unk = "UNKNOWN";
DefaultDeviceDescription desc = new DefaultDeviceDescription(
did.uri(),
basic.map(BasicDeviceConfig::type).orElse(Type.SWITCH),
basic.map(BasicDeviceConfig::manufacturer).orElse(unk),
basic.map(BasicDeviceConfig::hwVersion).orElse(unk),
basic.map(BasicDeviceConfig::swVersion).orElse(unk),
basic.map(BasicDeviceConfig::serial).orElse(unk),
new ChassisId(),
true);
providerService.deviceConnected(did, desc);
Optional<DeviceInjectionConfig> inject =
Optional.ofNullable(netcfgService.getConfig(did, DeviceInjectionConfig.class));
String ports = inject.map(DeviceInjectionConfig::ports).orElse("0");
int numPorts = Integer.parseInt(ports);
List<PortDescription> portDescs = new ArrayList<>(numPorts);
for (int i = 1; i <= numPorts; ++i) {
// TODO inject port details if something like BasicPortConfig was created
PortNumber number = portNumber(i);
boolean isEnabled = true;
portDescs.add(DefaultPortDescription.builder().withPortNumber(number) .isEnabled(isEnabled).build());
}
providerService.updatePorts(did, portDescs);
}
}
final class InternalDeviceProvider implements DeviceProvider {
@Override
public ProviderId id() {
return pid;
}
@Override
public void triggerProbe(DeviceId deviceId) {
worker.execute(() -> injectDevice(deviceId));
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
providerService.receivedRoleReply(deviceId, newRole, newRole);
}
@Override
public boolean isReachable(DeviceId deviceId) {
return true;
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber,
boolean enable) {
// TODO handle request to change port state from controller
}
}
/**
* Listens for Config updates.
*/
final class InternalConfigListener
implements NetworkConfigListener {
/**
* Relevant {@link NetworkConfigEvent} type.
*/
private final Set<NetworkConfigEvent.Type> relevant
= ImmutableSet.copyOf(EnumSet.of(
NetworkConfigEvent.Type.CONFIG_ADDED,
NetworkConfigEvent.Type.CONFIG_UPDATED,
NetworkConfigEvent.Type.CONFIG_REMOVED));
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return event.configClass() == DeviceInjectionConfig.class &&
relevant.contains(event.type());
}
@Override
public void event(NetworkConfigEvent event) {
worker.execute(() -> processEvent(event));
}
/**
* Process Config add/update/remove event.
* <p>
* Note: will be executed in the worker thread.
*
* @param event add/update/remove event
*/
protected void processEvent(NetworkConfigEvent event) {
DeviceId did = (DeviceId) event.subject();
if (!did.uri().getScheme().equals(pid.scheme())) {
log.warn("Attempt to inject unexpected scheme {}", did);
return;
}
log.debug("{} to {}: {}", event.type(), did, event);
// TODO if there's exist one probably better to follow master
// if (deviceService.getRole(did) != MastershipRole.MASTER) {
// log.debug("Not the master, ignoring. {}", event);
// return;
// }
switch (event.type()) {
case CONFIG_ADDED:
case CONFIG_UPDATED:
injectDevice(did);
break;
case CONFIG_REMOVED:
removeDevice(did);
break;
case CONFIG_REGISTERED:
case CONFIG_UNREGISTERED:
default:
log.warn("Ignoring unexpected event: {}", event);
break;
}
}
}
}