blob: d09a352fc4a44a67c8b48c81a48a4d0ae6951319 [file] [log] [blame]
/*
* Copyright 2015-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.provider.netconf.device.impl;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.tuple.Triple;
import com.google.common.util.concurrent.Striped;
import org.onlab.packet.ChassisId;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.CoreService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.behaviour.PortAdmin;
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.basics.SubjectFactories;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
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.PortStatistics;
import org.onosproject.net.device.PortStatisticsDiscovery;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverData;
import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.key.DeviceKey;
import org.onosproject.net.key.DeviceKeyAdminService;
import org.onosproject.net.key.DeviceKeyId;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfDeviceListener;
import org.onosproject.netconf.NetconfException;
import org.onosproject.netconf.config.NetconfDeviceConfig;
import org.osgi.service.component.ComponentContext;
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.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.function.Supplier;
import static java.util.concurrent.Executors.newScheduledThreadPool;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.netconf.NetconfDeviceInfo.extractIpPortPath;
import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES;
import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES_DEFAULT;
import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS;
import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS_DEFAULT;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which uses an NETCONF controller to detect device.
*/
@Component(immediate = true,
property = {
POLL_FREQUENCY_SECONDS + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
MAX_RETRIES + ":Integer=" + MAX_RETRIES_DEFAULT,
})
public class NetconfDeviceProvider extends AbstractProvider
implements DeviceProvider {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected NetconfController controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected NetworkConfigRegistry cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceKeyAdminService deviceKeyAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected ComponentConfigService componentConfigService;
protected static final String APP_NAME = "org.onosproject.netconf";
protected static final String SCHEME_NAME = "netconf";
private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
private static final String UNKNOWN = "unknown";
protected static final String ISNULL = "NetconfDeviceInfo is null";
private static final String IPADDRESS = "ipaddress";
private static final String PORT = "port";
private static final String PATH = "path";
private static final int CORE_POOL_SIZE = 10;
/**
* Configure poll frequency for port status and statistics; default is 30 sec.
*/
private int pollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
/**
* Configure maximum allowed number of retries for obtaining list of ports; default is 5 times.
*/
private int maxRetries = MAX_RETRIES_DEFAULT;
protected ExecutorService connectionExecutor = Executors.newFixedThreadPool(CORE_POOL_SIZE,
groupedThreads("onos/netconfDeviceProviderConnection",
"connection-executor-%d", log));
protected ScheduledExecutorService pollingExecutor = newScheduledThreadPool(CORE_POOL_SIZE,
groupedThreads("onos/netconfDeviceProviderPoll",
"polling-executor-%d", log));
protected DeviceProviderService providerService;
private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
protected ScheduledFuture<?> scheduledTask;
private final Striped<Lock> deviceLocks = Striped.lock(30);
protected final ConfigFactory factory =
// TODO consider moving Config registration to NETCONF ctl bundle
new ConfigFactory<DeviceId, NetconfDeviceConfig>(
SubjectFactories.DEVICE_SUBJECT_FACTORY,
NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
@Override
public NetconfDeviceConfig createConfig() {
return new NetconfDeviceConfig();
}
};
protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
private InternalDeviceListener deviceListener = new InternalDeviceListener();
private boolean active;
private ForkJoinPool scheduledTaskPool = new ForkJoinPool(CORE_POOL_SIZE);
@Activate
public void activate(ComponentContext context) {
active = true;
componentConfigService.registerProperties(getClass());
providerService = providerRegistry.register(this);
coreService.registerApplication(APP_NAME);
cfgService.registerConfigFactory(factory);
cfgService.addListener(cfgListener);
controller.addDeviceListener(innerNodeListener);
deviceService.addListener(deviceListener);
pollingExecutor.execute(NetconfDeviceProvider.this::connectDevices);
scheduledTask = schedulePolling();
modified(context);
log.info("Started");
}
@Deactivate
public void deactivate() {
cfgService.removeListener(cfgListener);
componentConfigService.unregisterProperties(getClass(), false);
deviceService.removeListener(deviceListener);
active = false;
controller.getNetconfDevices().forEach(id -> {
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
controller.disconnectDevice(id, true);
});
controller.removeDeviceListener(innerNodeListener);
providerRegistry.unregister(this);
providerService = null;
retriedPortDiscoveryMap.clear();
cfgService.unregisterConfigFactory(factory);
scheduledTask.cancel(true);
connectionExecutor.shutdown();
pollingExecutor.shutdown();
log.info("Stopped");
}
@Modified
public void modified(ComponentContext context) {
if (context != null) {
Dictionary<?, ?> properties = context.getProperties();
int newPollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
POLL_FREQUENCY_SECONDS_DEFAULT);
if (newPollFrequency != pollFrequency) {
pollFrequency = newPollFrequency;
if (scheduledTask != null) {
scheduledTask.cancel(false);
}
scheduledTask = schedulePolling();
log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
}
maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
log.info("Configured. Number of retries is configured to {} times", maxRetries);
}
}
public NetconfDeviceProvider() {
super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
}
@Override
public void triggerProbe(DeviceId deviceId) {
//Not implemented, unused in netconf cases.
log.debug("Probing {} not implemented, not useful for NETCONF", deviceId);
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
log.debug("Request role change {}, {}", deviceId, newRole);
if (active) {
switch (newRole) {
case MASTER:
if (controller.getNetconfDevice(deviceId) == null) {
connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
() -> initiateConnection(deviceId), deviceId).run()));
log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
}
break;
case STANDBY:
//TODO this issue a warning on the first election/connection
controller.disconnectDevice(deviceId, false);
withDeviceLock(
() -> initiateConnection(deviceId, newRole), deviceId).run();
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
//else no-op
break;
case NONE:
controller.disconnectDevice(deviceId, false);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
break;
default:
log.error("Unimplemented Mastership state : {}", newRole);
}
}
}
@Override
public boolean isReachable(DeviceId deviceId) {
boolean sessionExists =
Optional.ofNullable(controller.getDevicesMap().get(deviceId))
.map(NetconfDevice::isActive)
.orElse(false);
if (sessionExists) {
return true;
}
//FIXME this is a workaround util device state is shared
// between controller instances.
Device device = deviceService.getDevice(deviceId);
String ip;
int port;
if (device != null) {
ip = device.annotations().value(IPADDRESS);
port = Integer.parseInt(device.annotations().value(PORT));
} else {
Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
ip = info.getLeft();
port = info.getMiddle();
}
// FIXME just opening TCP session probably is not the appropriate
// method to test reachability.
//test connection to device opening a socket to it.
log.debug("Testing reachability for {}:{}", ip, port);
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress(ip, port), 1000);
log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
boolean isConnected = socket.isConnected() && !socket.isClosed();
socket.close();
return isConnected;
} catch (IOException e) {
log.info("Device {} is not reachable", deviceId);
log.debug(" error details", e);
return false;
}
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Device device = deviceService.getDevice(deviceId);
if (device == null) {
log.error("Device {} is not present in the store", deviceId);
return;
}
if (!mastershipService.isLocalMaster(deviceId)) {
log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
return;
}
if (!device.is(PortAdmin.class)) {
log.warn("Device {} does not support Port Admin", deviceId);
return;
}
PortAdmin portAdmin = device.as(PortAdmin.class);
CompletableFuture<Boolean> modified;
if (enable) {
modified = portAdmin.enable(portNumber);
} else {
modified = portAdmin.disable(portNumber);
}
modified.thenAccept(result -> {
if (result) {
Port port = deviceService.getPort(deviceId, portNumber);
//rebuilding port description with admin state changed.
providerService.portStatusChanged(deviceId,
DefaultPortDescription.builder()
.withPortNumber(portNumber)
.isEnabled(enable)
.isRemoved(false)
.type(port.type())
.portSpeed(port.portSpeed())
.annotations((SparseAnnotations) port.annotations())
.build());
} else {
log.warn("Your device {} port {} status can't be changed to {}",
deviceId, portNumber, enable);
}
});
}
@Override
public void triggerDisconnect(DeviceId deviceId) {
log.debug("Forcing disconnect for device {}", deviceId);
controller.disconnectDevice(deviceId, true);
}
private ScheduledFuture schedulePolling() {
return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
pollFrequency / 10,
pollFrequency, TimeUnit.SECONDS);
}
private Runnable exceptionSafe(Runnable runnable) {
return () -> {
try {
runnable.run();
} catch (Exception e) {
log.error("Unhandled Exception", e);
}
};
}
//Connecting devices with initial config
private void connectDevices() {
Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
deviceSubjects.parallelStream().forEach(deviceId -> {
connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
});
}
//updating keys and device info
private void checkAndUpdateDevices() {
Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
try {
scheduledTaskPool.submit(() -> {
deviceSubjects.parallelStream().forEach(deviceId -> {
log.debug("check and update {}", deviceId);
NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
discoverOrUpdatePorts(deviceId);
});
}).get();
} catch (ExecutionException e) {
log.error("Can't update the devices due to {}", e.getMessage());
} catch (InterruptedException | CancellationException e) {
log.info("Update device is cancelled due to {}", e.getMessage());
}
}
//Saving device keys in the store
private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
if (sshKey.equals("")) {
deviceKeyAdminService.addKey(
DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
} else {
deviceKeyAdminService.addKey(
DeviceKey.createDeviceKeyUsingSshKey(
DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
}
}
//running an election and applying the role to a given device
private void runElectionFor(DeviceId deviceId) {
//Triggering an election for the deviceId thus only master will connect
if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
// not under my scheme, skipping
log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
return;
}
connectionExecutor.submit(exceptionSafe(() -> {
CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
try {
roleChanged(deviceId, role.get());
} catch (InterruptedException | ExecutionException e) {
log.error("Can't get role for {} ", deviceId, e);
}
}));
}
//initiating the SSh connection the a given device.
private void initiateConnection(DeviceId deviceId) {
if (!isReachable(deviceId)) {
log.warn("Can't connect to device {}", deviceId);
return;
}
try {
NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
if (deviceNetconf != null) {
//storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
//getting the device description
DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
//connecting device to ONOS
log.debug("Connected NETCONF device {}, on {}:{} with username {}",
deviceId, config.ip(), config.port(),
(config.path().isPresent() ? "/" + config.path().get() : ""),
config.username());
providerService.deviceConnected(deviceId, deviceDescription);
} else {
mastershipService.relinquishMastership(deviceId);
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
log.error("Can't connect to NETCONF device {}", deviceId);
}
} catch (Exception e) {
mastershipService.relinquishMastership(deviceId);
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
throw new IllegalStateException(new NetconfException(
"Can't connect to NETCONF device " + deviceId, e));
}
}
private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
Driver driver = driverService.getDriver(deviceId);
if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
final DriverData data = new DefaultDriverData(driver, deviceId);
final DriverHandler handler = new DefaultDriverHandler(data);
//creating the behaviour because the core has yet no notion of device.
DeviceDescriptionDiscovery deviceDescriptionDiscovery =
driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
} else {
return existingOrEmptyDescription(deviceId, config);
}
}
private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
if (newDescription == null) {
return existingOrEmptyDescription;
}
//merging and returning
return new DefaultDeviceDescription(newDescription, true,
DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
existingOrEmptyDescription.annotations()));
}
private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
Device device = deviceService.getDevice(deviceId);
if (deviceService.getDevice(deviceId) != null) {
//getting the previous description
return new DefaultDeviceDescription(device.id().uri(), device.type(),
device.manufacturer(), device.hwVersion(),
device.swVersion(), device.serialNumber(),
device.chassisId(), (SparseAnnotations) device.annotations());
}
ChassisId cid = new ChassisId();
String ipAddress = config.ip().toString();
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
.set(IPADDRESS, ipAddress)
.set(PORT, String.valueOf(config.port()))
.set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
if (config.path().isPresent()) {
annotations.set(PATH, config.path().get());
}
return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
}
/**
* Will appropriately create connections with the device.
* For Master role: will create secure transport and proxy sessions.
* For Standby role: will create only proxy session and disconnect secure transport session.
* For none role: will disconnect all sessions.
*
* @param deviceId device id
* @param newRole new role
*/
private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
try {
if (isReachable(deviceId)) {
NetconfDevice device = null;
if (newRole.equals(MastershipRole.MASTER)) {
device = controller.connectDevice(deviceId, true);
} else if (newRole.equals(MastershipRole.STANDBY)) {
device = controller.connectDevice(deviceId, false);
}
if (device != null) {
providerService.receivedRoleReply(deviceId, newRole, newRole);
} else {
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
}
}
} catch (Exception e) {
if (deviceService.getDevice(deviceId) != null) {
providerService.deviceDisconnected(deviceId);
}
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
throw new IllegalStateException(new NetconfException(
"Can't connect to NETCONF device " + deviceId, e));
}
}
private void discoverOrUpdatePorts(DeviceId deviceId) {
retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
//TODO this does not enable port discovery if port changes.
Device device = deviceService.getDevice(deviceId);
if (device == null) {
log.debug("Cant' reach device {}, not updating ports", deviceId);
return;
}
if (deviceService.getPorts(deviceId).isEmpty()
&& count != null && count.getAndIncrement() < maxRetries) {
if (device.is(DeviceDescriptionDiscovery.class)) {
providerService.updatePorts(deviceId,
device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
} else {
log.warn("No DeviceDescirption behaviour for device {}", deviceId);
}
}
updatePortStatistics(device);
}
private void updatePortStatistics(Device device) {
if (device.is(PortStatisticsDiscovery.class)) {
PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
if (portStatistics != null) {
providerService.updatePortStatistics(device.id(),
portStatistics);
}
} else {
log.debug("No port statistics getter behaviour for device {}",
device.id());
}
}
private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
final Lock lock = deviceLocks.get(deviceId);
lock.lock();
try {
return task.get();
} finally {
lock.unlock();
}
}
private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
// Wrapper of withDeviceLock(Supplier, ...) for void tasks.
return () -> withDeviceLock(() -> {
task.run();
return null;
}, deviceId);
}
/**
* Listener for configuration events.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
connectionExecutor.execute(exceptionSafe(() ->
runElectionFor((DeviceId) event.config().get().subject())));
} else {
log.warn("Incorrect or absent Class for Netconf Configuration");
}
}
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return (event.configClass().equals(NetconfDeviceConfig.class)) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
}
/**
* Listener for Netconf Controller Events.
*/
private class InnerNetconfDeviceListener implements NetconfDeviceListener {
@Override
public void deviceAdded(DeviceId deviceId) {
//no-op
log.debug("Netconf device {} added to Netconf controller", deviceId);
}
@Override
public void deviceRemoved(DeviceId deviceId) {
Preconditions.checkNotNull(deviceId, ISNULL);
if (deviceService.getDevice(deviceId) != null) {
providerService.deviceDisconnected(deviceId);
retriedPortDiscoveryMap.remove(deviceId);
log.debug("Netconf device {} removed from Netconf controller", deviceId);
} else {
log.warn("Netconf device {} does not exist in the store, " +
"it may already have been removed", deviceId);
}
}
}
/**
* Listener for core device events.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
}
@Override
public boolean isRelevant(DeviceEvent event) {
if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
return false;
}
return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
(SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
mastershipService.isLocalMaster(event.subject().id());
}
}
}