[ONOS-4045]Adding mastership service to NetconfProvider
Change-Id: Id39cbef54a079ab6e080a9d3f60770c4bea90b3f
diff --git a/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java b/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java
index 9d2be56..6025e07 100644
--- a/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java
+++ b/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java
@@ -23,9 +23,12 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.config.basics.ConfigException;
+import org.onosproject.mastership.MastershipService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
@@ -39,21 +42,27 @@
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
+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.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.NetconfDeviceInfo;
import org.onosproject.netconf.NetconfDeviceListener;
import org.onosproject.netconf.NetconfException;
import org.slf4j.Logger;
import java.io.IOException;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -67,6 +76,8 @@
@Component(immediate = true)
public class NetconfDeviceProvider extends AbstractProvider
implements DeviceProvider {
+
+ public static final String ACTIVE = "active";
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -82,21 +93,36 @@
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DriverService driverService;
+ protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
+ protected DeviceKeyAdminService deviceKeyAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
private static final String APP_NAME = "org.onosproject.netconf";
private 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 NETCONF = "netconf";
+ private static final String PORT = "port";
+ //FIXME eventually a property
+ private static final int ISREACHABLE_TIMEOUT = 2000;
private final ExecutorService executor =
- Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider", "device-installer-%d", log));
+ Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
+ "device-installer-%d", log));
private DeviceProviderService providerService;
private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
+ private InternalDeviceListener deviceListener = new InternalDeviceListener();
+ private NodeId localNodeId;
private final ConfigFactory factory =
new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
@@ -108,31 +134,40 @@
return new NetconfProviderConfig();
}
};
- private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
+ private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
private ApplicationId appId;
+ private boolean active;
@Activate
public void activate() {
+ active = true;
providerService = providerRegistry.register(this);
appId = coreService.registerApplication(APP_NAME);
cfgService.registerConfigFactory(factory);
- cfgService.addListener(cfgLister);
+ cfgService.addListener(cfgListener);
controller.addDeviceListener(innerNodeListener);
+ deviceService.addListener(deviceListener);
executor.execute(NetconfDeviceProvider.this::connectDevices);
+ localNodeId = clusterService.getLocalNode().id();
log.info("Started");
}
@Deactivate
public void deactivate() {
+ deviceService.removeListener(deviceListener);
+ active = false;
+ controller.getNetconfDevices().forEach(id -> {
+ deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
+ controller.disconnectDevice(id, true);
+ });
controller.removeDeviceListener(innerNodeListener);
- controller.getNetconfDevices().forEach(id ->
- controller.removeDevice(controller.getDevicesMap().get(id)
- .getDeviceInfo()));
+ deviceService.removeListener(deviceListener);
providerRegistry.unregister(this);
providerService = null;
cfgService.unregisterConfigFactory(factory);
+ executor.shutdown();
log.info("Stopped");
}
@@ -148,52 +183,87 @@
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
- // TODO: This will be implemented later.
+ if (active) {
+ switch (newRole) {
+ case MASTER:
+ initiateConnection(deviceId, newRole);
+ log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
+ break;
+ case STANDBY:
+ controller.disconnectDevice(deviceId, false);
+ 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) {
- NetconfDevice netconfDevice = controller.getNetconfDevice(deviceId);
- if (netconfDevice == null) {
- log.debug("Requested device id: {} is not associated to any " +
- "NETCONF Device", deviceId.toString());
- return false;
+ //FIXME this is a workaround util device state is shared
+ // between controller instances.
+ Device device = deviceService.getDevice(deviceId);
+ String ip;
+ int port;
+ Socket socket = null;
+ if (device != null) {
+ ip = device.annotations().value(IPADDRESS);
+ port = Integer.parseInt(device.annotations().value(PORT));
+ } else {
+ String[] info = deviceId.toString().split(":");
+ if (info.length == 3) {
+ ip = info[1];
+ port = Integer.parseInt(info[2]);
+ } else {
+ ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
+ && !el.equals(info[info.length - 1]))
+ .reduce((t, u) -> t + ":" + u)
+ .get();
+ log.debug("ip v6 {}", ip);
+ port = Integer.parseInt(info[info.length - 1]);
+ }
}
- return netconfDevice.isActive();
+ //test connection to device opening a socket to it.
+ try {
+ socket = new Socket(ip, port);
+ log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected() && !socket.isClosed());
+ return socket.isConnected() && !socket.isClosed();
+ } catch (IOException e) {
+ log.info("Device {} is not reachable", deviceId);
+ return false;
+ } finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ log.debug("Test Socket failed {} ", deviceId);
+ return false;
+ }
+ }
+ }
}
private class InnerNetconfDeviceListener implements NetconfDeviceListener {
- private static final String IPADDRESS = "ipaddress";
- protected static final String ISNULL = "NetconfDeviceInfo is null";
@Override
- public void deviceAdded(NetconfDeviceInfo nodeId) {
- Preconditions.checkNotNull(nodeId, ISNULL);
- DeviceId deviceId = nodeId.getDeviceId();
- //Netconf configuration object
- ChassisId cid = new ChassisId();
- String ipAddress = nodeId.ip().toString();
- SparseAnnotations annotations = DefaultAnnotations.builder()
- .set(IPADDRESS, ipAddress)
- .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
- .build();
- DeviceDescription deviceDescription = new DefaultDeviceDescription(
- deviceId.uri(),
- Device.Type.SWITCH,
- UNKNOWN, UNKNOWN,
- UNKNOWN, UNKNOWN,
- cid,
- annotations);
- providerService.deviceConnected(deviceId, deviceDescription);
+ public void deviceAdded(DeviceId deviceId) {
+ //no-op
+ log.debug("Netconf device {} added to Netconf subController", deviceId);
}
@Override
- public void deviceRemoved(NetconfDeviceInfo nodeId) {
- Preconditions.checkNotNull(nodeId, ISNULL);
- DeviceId deviceId = nodeId.getDeviceId();
+ public void deviceRemoved(DeviceId deviceId) {
+ Preconditions.checkNotNull(deviceId, ISNULL);
+ log.debug("Netconf device {} removed from Netconf subController", deviceId);
providerService.deviceDisconnected(deviceId);
-
}
}
@@ -201,41 +271,85 @@
NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
if (cfg != null) {
try {
- cfg.getDevicesAddresses().stream()
- .forEach(addr -> {
- try {
- NetconfDeviceInfo netconf = new NetconfDeviceInfo(addr.name(),
- addr.password(),
- addr.ip(),
- addr.port());
- controller.connectDevice(netconf);
- Device device = deviceService.getDevice(netconf.getDeviceId());
- if (device.is(PortDiscovery.class)) {
- PortDiscovery portConfig = device.as(PortDiscovery.class);
- if (portConfig != null) {
- providerService.updatePorts(netconf.getDeviceId(),
- portConfig.getPorts());
- }
- } else {
- log.warn("No portGetter behaviour for device {}", netconf.getDeviceId());
- }
+ cfg.getDevicesAddresses().stream().forEach(addr -> {
+ DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
+ Preconditions.checkNotNull(deviceId, ISNULL);
+ //Netconf configuration object
+ ChassisId cid = new ChassisId();
+ String ipAddress = addr.ip().toString();
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set(IPADDRESS, ipAddress)
+ .set(PORT, String.valueOf(addr.port()))
+ .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
+ .build();
+ DeviceDescription deviceDescription = new DefaultDeviceDescription(
+ deviceId.uri(),
+ Device.Type.SWITCH,
+ UNKNOWN, UNKNOWN,
+ UNKNOWN, UNKNOWN,
+ cid,
+ annotations);
+ deviceKeyAdminService.addKey(
+ DeviceKey.createDeviceKeyUsingUsernamePassword(
+ DeviceKeyId.deviceKeyId(deviceId.toString()),
+ null, addr.name(), addr.password()));
+ providerService.deviceConnected(deviceId, deviceDescription);
- } catch (IOException e) {
- throw new RuntimeException(
- new NetconfException(
- "Can't connect to NETCONF " +
- "device on " + addr.ip() +
- ":" + addr.port(), e));
- }
- }
- );
+ });
} catch (ConfigException e) {
log.error("Cannot read config error " + e);
}
}
}
+ private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
+ try {
+ if (isReachable(deviceId)) {
+ controller.connectDevice(deviceId);
+ providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
+ } else {
+ return;
+ }
+ } catch (Exception e) {
+ if (deviceService.getDevice(deviceId) != null) {
+ providerService.deviceDisconnected(deviceId);
+ }
+ deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
+ throw new RuntimeException(new NetconfException(
+ "Can't connect to NETCONF " + "device on " + deviceId + ":" + deviceId, e));
+
+ }
+ }
+
+ private void discoverPorts(DeviceId deviceId) {
+ Device device = deviceService.getDevice(deviceId);
+ if (device.is(PortDiscovery.class)) {
+ PortDiscovery portConfig = device.as(PortDiscovery.class);
+ providerService.updatePorts(deviceId,
+ portConfig.getPorts());
+ } else {
+ log.warn("No portGetter behaviour for device {}", deviceId);
+ }
+ }
+
+ /**
+ * Return the DeviceId about the device containing the URI.
+ *
+ * @return DeviceId
+ */
+ public DeviceId getDeviceId(String ip, int port) {
+ try {
+ return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException("Unable to build deviceID for device "
+ + ip + ":" + port, e);
+ }
+ }
+
+ /**
+ * Listener for configuration events.
+ */
private class InternalNetworkConfigListener implements NetworkConfigListener {
@@ -246,10 +360,35 @@
@Override
public boolean isRelevant(NetworkConfigEvent event) {
- //TODO refactor
return event.configClass().equals(NetconfProviderConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
}
+
+ /**
+ * Listener for core device events.
+ */
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
+ executor.execute(() -> discoverPorts(event.subject().id()));
+ } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
+ log.debug("removing device {}", event.subject().id());
+ deviceService.getDevice(event.subject().id()).annotations().keys();
+ controller.disconnectDevice(event.subject().id(), true);
+ }
+ }
+
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ if (mastershipService.getMasterFor(event.subject().id()) == null) {
+ return true;
+ }
+ return event.subject().annotations().value(AnnotationKeys.PROTOCOL)
+ .equals(SCHEME_NAME.toUpperCase()) &&
+ mastershipService.getMasterFor(event.subject().id()).equals(localNodeId);
+ }
+ }
}