[ONOS-6463] general device Provider
Change-Id: Ibc045bffe14c24068adc7f0adc96366d0f1807a0
diff --git a/providers/general/BUCK b/providers/general/BUCK
new file mode 100644
index 0000000..5d4973e
--- /dev/null
+++ b/providers/general/BUCK
@@ -0,0 +1,15 @@
+BUNDLES = [
+ '//providers/general/device:onos-providers-general-device',
+]
+
+onos_app (
+ app_name = "org.onosproject.generaldeviceprovider",
+ title = 'General Device Provider',
+ category = 'Provider',
+ url = 'http://onosproject.org',
+ included_bundles = BUNDLES,
+ description = 'General device southbound providers.',
+ required_apps = [],
+)
+
+
diff --git a/providers/general/app/app.xml b/providers/general/app/app.xml
new file mode 100644
index 0000000..eeaba0c
--- /dev/null
+++ b/providers/general/app/app.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<app name="org.onosproject.generaldeviceprovider" origin="ON.Lab" version="${project.version}"
+ category="Provider" url="https://wiki.onosproject.org/" title="General Device Provider"
+ featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+ features="${project.artifactId}">
+ <description>${project.description}</description>
+
+ <artifact>mvn:${project.groupId}/onos-general-provider-device/${project.version}</artifact>
+
+</app>
diff --git a/providers/general/app/features.xml b/providers/general/app/features.xml
new file mode 100644
index 0000000..f147e61
--- /dev/null
+++ b/providers/general/app/features.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+ <feature name="${project.artifactId}" version="${project.version}"
+ description="${project.description}">
+ <feature>onos-api</feature>
+
+ <bundle>mvn:${project.groupId}/onos-general-provider-device/${project.version}</bundle>
+ </feature>
+</features>
+
diff --git a/providers/general/app/pom.xml b/providers/general/app/pom.xml
new file mode 100644
index 0000000..da2bf99
--- /dev/null
+++ b/providers/general/app/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-general-providers</artifactId>
+ <version>1.11.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-general-provider-app</artifactId>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <onos.app.name>org.onosproject.generaldriverprovider</onos.app.name>
+ <onos.app.title>General Device Provider</onos.app.title>
+ <onos.app.category>Provider</onos.app.category>
+ <onos.app.requires>
+ </onos.app.requires>
+ <onos.app.url>
+ </onos.app.url>
+ </properties>
+
+ <description>General Device Provider that uses drivers as means of device discovery</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-general-provider-device</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- Add other dependencies here as more bundles are added to the app -->
+
+ </dependencies>
+
+</project>
diff --git a/providers/general/device/BUCK b/providers/general/device/BUCK
new file mode 100644
index 0000000..770f6ca
--- /dev/null
+++ b/providers/general/device/BUCK
@@ -0,0 +1,12 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
diff --git a/providers/general/device/pom.xml b/providers/general/device/pom.xml
new file mode 100644
index 0000000..1c7bb14
--- /dev/null
+++ b/providers/general/device/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-general-providers</artifactId>
+ <version>1.11.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-general-provider-device</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS general device provider that uses drivers as a mean of
+ discovery and handshake. </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>5.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/DeviceInfoConfig.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/DeviceInfoConfig.java
new file mode 100644
index 0000000..f8660af
--- /dev/null
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/DeviceInfoConfig.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.general.device.api;
+
+import com.google.common.annotations.Beta;
+
+import java.util.Map;
+
+/**
+ * Protocol specific configuration for the general device provider.
+ */
+@Beta
+public final class DeviceInfoConfig {
+
+ private final Map<String, String> configValues;
+ private final String deviceKeyId;
+
+
+ public DeviceInfoConfig(Map<String, String> configValues, String deviceKeyId) {
+ this.configValues = configValues;
+ this.deviceKeyId = deviceKeyId;
+ }
+
+ /**
+ * Gets the configValues contained in the json sent via net-cfg.
+ *
+ * @return configValues in key-value pairs.
+ */
+ public Map<String, String> configValues() {
+ return configValues;
+ }
+
+ /**
+ * Gets the device key id of the device.
+ * This key should be pre-existing in ONOS.
+ *
+ * @return device key id
+ */
+ public String deviceKeyId() {
+ return deviceKeyId;
+ }
+
+}
\ No newline at end of file
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/GeneralProviderDeviceConfig.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/GeneralProviderDeviceConfig.java
new file mode 100644
index 0000000..de3d8d9
--- /dev/null
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/GeneralProviderDeviceConfig.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.general.device.api;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.annotations.Beta;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Configuration for General device provider.
+ */
+@Beta
+public class GeneralProviderDeviceConfig extends Config<DeviceId> {
+
+ private static final String DEVICEKEYID = "deviceKeyId";
+
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ /**
+ * Gets the information of all protocols associated to the device.
+ *
+ * @return map of protocol name and relative information
+ */
+ public Map<String, DeviceInfoConfig> protocolsInfo() {
+ return getProtocolInfoMap();
+ }
+
+ private Map<String, DeviceInfoConfig> getProtocolInfoMap() {
+ Map<String, DeviceInfoConfig> deviceMap = new HashMap<>();
+ node.fieldNames().forEachRemaining(name -> {
+
+ Map<String, String> configMap = new HashMap<>();
+ JsonNode protocol = node.get(name);
+ protocol.fieldNames().forEachRemaining(info -> configMap.put(info, protocol.get(info).asText()));
+
+ String deviceKeyId = "";
+ if (protocol.has(DEVICEKEYID)) {
+ deviceKeyId = protocol.get(DEVICEKEYID).asText("");
+ }
+
+ deviceMap.put(name, new DeviceInfoConfig(configMap, deviceKeyId));
+ });
+ return deviceMap;
+ }
+
+}
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/package-info.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/package-info.java
new file mode 100644
index 0000000..fb23954
--- /dev/null
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/**
+ * APIs for General Provider that uses drivers as a means of infrastructure device discovery.
+ */
+package org.onosproject.provider.general.device.api;
\ No newline at end of file
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
new file mode 100644
index 0000000..8909591
--- /dev/null
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.general.device.impl;
+
+import com.google.common.annotations.Beta;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.ChassisId;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.core.CoreService;
+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.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.BasicDeviceConfig;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceHandshaker;
+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.PortDescription;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.device.PortStatisticsDiscovery;
+import org.onosproject.net.driver.Behaviour;
+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.DriverService;
+import org.onosproject.net.key.DeviceKeyAdminService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.provider.general.device.api.GeneralProviderDeviceConfig;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.concurrent.Executors.newScheduledThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.device.DeviceEvent.Type;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses drivers to detect device and do initial handshake
+ * and channel establishment with devices. Any other provider specific operation
+ * is also delegated to the DeviceHandshaker driver.
+ */
+@Beta
+@Component(immediate = true)
+public class GeneralDeviceProvider extends AbstractProvider
+ implements DeviceProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceKeyAdminService deviceKeyAdminService;
+
+ protected static final String APP_NAME = "org.onosproject.generaldeviceprovider";
+ protected static final String URI_SCHEME = "device";
+ protected static final String CFG_SCHEME = "generalprovider";
+ private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.general.provider.device";
+ private static final int CORE_POOL_SIZE = 10;
+ private static final String UNKNOWN = "unknown";
+ private static final int PORT_STATS_PERIOD_SECONDS = 10;
+
+
+ protected ScheduledExecutorService connectionExecutor
+ = newScheduledThreadPool(CORE_POOL_SIZE,
+ groupedThreads("onos/generaldeviceprovider-device",
+ "connection-executor-%d", log));
+ protected ScheduledExecutorService portStatsExecutor
+ = newScheduledThreadPool(CORE_POOL_SIZE,
+ groupedThreads("onos/generaldeviceprovider-port-stats",
+ "port-stats-executor-%d", log));
+
+ protected DeviceProviderService providerService;
+ private InternalDeviceListener deviceListener = new InternalDeviceListener();
+
+ protected final ConfigFactory factory =
+ new ConfigFactory<DeviceId, GeneralProviderDeviceConfig>(
+ SubjectFactories.DEVICE_SUBJECT_FACTORY,
+ GeneralProviderDeviceConfig.class, CFG_SCHEME) {
+ @Override
+ public GeneralProviderDeviceConfig createConfig() {
+ return new GeneralProviderDeviceConfig();
+ }
+ };
+
+ protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
+
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ coreService.registerApplication(APP_NAME);
+ cfgService.registerConfigFactory(factory);
+ cfgService.addListener(cfgListener);
+ deviceService.addListener(deviceListener);
+ //This will fail if ONOS has CFG and drivers which depend on this provider
+ // are activated, failing due to not finding the driver.
+ cfgService.getSubjects(DeviceId.class, GeneralProviderDeviceConfig.class)
+ .forEach(did -> connectionExecutor.execute(() -> connectDevice(did)));
+ log.info("Started");
+ }
+
+
+ @Deactivate
+ public void deactivate() {
+ portStatsExecutor.shutdown();
+ cfgService.removeListener(cfgListener);
+ //Not Removing the device so they can still be used from other driver providers
+ //cfgService.getSubjects(DeviceId.class, GeneralProviderDeviceConfig.class)
+ // .forEach(did -> connectionExecutor.execute(() -> disconnectDevice(did)));
+ connectionExecutor.shutdown();
+ deviceService.removeListener(deviceListener);
+ providerRegistry.unregister(this);
+ providerService = null;
+ cfgService.unregisterConfigFactory(factory);
+ log.info("Stopped");
+ }
+
+ public GeneralDeviceProvider() {
+ super(new ProviderId(URI_SCHEME, DEVICE_PROVIDER_PACKAGE));
+ }
+
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ //TODO Really don't see the point of this in non OF Context,
+ // for now testing reachability, can be moved to no-op
+ log.debug("Triggering probe equals testing reachability on device {}", deviceId);
+ isReachable(deviceId);
+ }
+
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ log.debug("Received role {} for device {}", newRole, deviceId);
+ CompletableFuture<MastershipRole> roleReply = getHandshaker(deviceId).roleChanged(newRole);
+ roleReply.thenAcceptAsync(mastership -> providerService.receivedRoleReply(deviceId, newRole, mastership));
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ log.debug("Testing rechability for device {}", deviceId);
+ CompletableFuture<Boolean> reachable = getHandshaker(deviceId).isReachable();
+ try {
+ return reachable.get(10, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ log.error("Device {} is not reachable", deviceId, e);
+ return false;
+ }
+ }
+
+ @Override
+ public void changePortState(DeviceId deviceId, PortNumber portNumber,
+ boolean enable) {
+ if (deviceService.getDevice(deviceId).is(PortAdmin.class)) {
+
+ PortAdmin portAdmin = getPortAdmin(deviceId);
+ CompletableFuture<Boolean> modified;
+ if (enable) {
+ modified = portAdmin.enable(portNumber);
+ } else {
+ modified = portAdmin.disable(portNumber);
+ }
+ modified.thenAcceptAsync(result -> {
+ if (!result) {
+ log.warn("Your device {} port {} status can't be changed to {}",
+ deviceId, portNumber, enable);
+ }
+ });
+
+ } else {
+ log.warn("Device {} does not support PortAdmin behaviour", deviceId);
+ }
+ }
+
+ private DeviceHandshaker getHandshaker(DeviceId deviceId) {
+ Driver driver = getDriver(deviceId);
+ return getBehaviour(driver, DeviceHandshaker.class,
+ new DefaultDriverData(driver, deviceId));
+ }
+
+ private PortAdmin getPortAdmin(DeviceId deviceId) {
+ Driver driver = getDriver(deviceId);
+ return getBehaviour(driver, PortAdmin.class,
+ new DefaultDriverData(driver, deviceId));
+
+ }
+
+ private Driver getDriver(DeviceId deviceId) {
+ Driver driver;
+ try {
+ driver = driverService.getDriver(deviceId);
+ } catch (ItemNotFoundException e) {
+ log.debug("Falling back to configuration to fetch driver " +
+ "for device {}", deviceId);
+ driver = driverService.getDriver(
+ cfgService.getConfig(deviceId, BasicDeviceConfig.class).driver());
+ }
+ return driver;
+ }
+
+ //needed since the device manager will not return the driver through implementation()
+ // method since the device is not pushed to the core so for the connectDevice
+ // we need to work around that in order to test before calling
+ // store.createOrUpdateDevice
+ private <T extends Behaviour> T getBehaviour(Driver driver, Class<T> type,
+ DriverData data) {
+ if (driver.hasBehaviour(type)) {
+ DefaultDriverHandler handler = new DefaultDriverHandler(data);
+ return driver.createBehaviour(handler, type);
+ } else {
+ return null;
+ }
+ }
+
+ //Connects a general device
+ private void connectDevice(DeviceId deviceId) {
+ //retrieve the configuration
+ GeneralProviderDeviceConfig providerConfig =
+ cfgService.getConfig(deviceId, GeneralProviderDeviceConfig.class);
+ BasicDeviceConfig basicDeviceConfig =
+ cfgService.getConfig(deviceId, BasicDeviceConfig.class);
+
+ if (providerConfig == null || basicDeviceConfig == null) {
+ log.error("Configuration is NULL: basic config {}, general provider " +
+ "config {}", basicDeviceConfig, providerConfig);
+ } else {
+ log.info("Connecting to device {}", deviceId);
+
+ Driver driver = driverService.getDriver(basicDeviceConfig.driver());
+ DriverData driverData = new DefaultDriverData(driver, deviceId);
+
+ DeviceHandshaker handshaker =
+ getBehaviour(driver, DeviceHandshaker.class, driverData);
+
+ if (handshaker != null) {
+
+ //Storing deviceKeyId and all other config values
+ // as data in the driver with protocol_<info>
+ // name as the key. e.g protocol_ip
+ providerConfig.protocolsInfo()
+ .forEach((protocol, deviceInfoConfig) -> {
+ deviceInfoConfig.configValues()
+ .forEach((k, v) -> driverData.set(protocol + "_" + k, v));
+ driverData.set(protocol + "_key", deviceInfoConfig.deviceKeyId());
+ });
+
+ //Connecting to the device
+ CompletableFuture<Boolean> connected = handshaker.connect();
+
+ connected.thenAcceptAsync(result -> {
+ if (result) {
+
+ //Populated with the default values obtained by the driver
+ ChassisId cid = new ChassisId();
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PROTOCOL,
+ providerConfig.protocolsInfo().keySet().toString())
+ .build();
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
+ driver.manufacturer(), driver.hwVersion(),
+ driver.swVersion(), UNKNOWN,
+ cid, false, annotations);
+ //Empty list of ports
+ List<PortDescription> ports = new ArrayList<>();
+
+ if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
+ DeviceDescriptionDiscovery deviceDiscovery = driver
+ .createBehaviour(driverData, DeviceDescriptionDiscovery.class);
+
+ DeviceDescription newdescription = deviceDiscovery.discoverDeviceDetails();
+ if (newdescription != null) {
+ description = newdescription;
+ }
+ ports = deviceDiscovery.discoverPortDetails();
+ }
+ providerService.deviceConnected(deviceId, description);
+ providerService.updatePorts(deviceId, ports);
+
+ } else {
+ log.warn("Can't connect to device {}", deviceId);
+ }
+ });
+ } else {
+ log.error("Device {}, with driver {} does not support DeviceHandshaker " +
+ "behaviour, {}", deviceId, driver.name(), driver.behaviours());
+ }
+ }
+ }
+
+ private void disconnectDevice(DeviceId deviceId) {
+ log.info("Disconnecting for device {}", deviceId);
+ DeviceHandshaker handshaker = getHandshaker(deviceId);
+ if (handshaker != null) {
+ CompletableFuture<Boolean> disconnect = handshaker.disconnect();
+
+ disconnect.thenAcceptAsync(result -> {
+ if (result) {
+ log.info("Disconnected device {}", deviceId);
+ providerService.deviceDisconnected(deviceId);
+ } else {
+ log.warn("Device {} was unable to disconnect", deviceId);
+ }
+ });
+ } else {
+ //gracefully ignoring.
+ log.info("No DeviceHandshaker for device {}", deviceId);
+ }
+ }
+
+ //Needed to catch the exception in the executors since are not rethrown otherwise.
+ private Runnable exceptionSafe(Runnable runnable) {
+ return () -> {
+ try {
+ runnable.run();
+ } catch (Exception e) {
+ log.error("Unhandled Exception", e);
+ }
+ };
+ }
+
+ private void updatePortStatistics(DeviceId deviceId) {
+ Collection<PortStatistics> statistics = deviceService.getDevice(deviceId)
+ .as(PortStatisticsDiscovery.class)
+ .discoverPortStatistics();
+ providerService.updatePortStatistics(deviceId, statistics);
+ }
+
+ /**
+ * Listener for configuration events.
+ */
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ DeviceId deviceId = (DeviceId) event.subject();
+ //Assuming that the deviceId comes with uri 'device:'
+ if (!deviceId.uri().getScheme().equals(URI_SCHEME)) {
+ // not under my scheme, skipping
+ log.debug("{} is not my scheme, skipping", deviceId);
+ return;
+ }
+ if (deviceService.getDevice(deviceId) == null || !deviceService.isAvailable(deviceId)) {
+ connectionExecutor.submit(exceptionSafe(() -> connectDevice(deviceId)));
+ } else {
+ log.info("Device {} is already connected to ONOS and is available", deviceId);
+ }
+ }
+
+ @Override
+ public boolean isRelevant(NetworkConfigEvent event) {
+ return event.configClass().equals(GeneralProviderDeviceConfig.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) {
+ Type type = event.type();
+
+ if (type.equals((Type.DEVICE_ADDED))) {
+
+ //For now this is scheduled periodically, when streaming API will
+ // be available we check and base it on the streaming API (e.g. gNMI)
+ if (deviceService.getDevice(event.subject().id()).
+ is(PortStatisticsDiscovery.class)) {
+ portStatsExecutor.scheduleAtFixedRate(exceptionSafe(() ->
+ updatePortStatistics(event.subject().id())),
+ 0, PORT_STATS_PERIOD_SECONDS, TimeUnit.SECONDS);
+ updatePortStatistics(event.subject().id());
+ }
+
+ } else if (type.equals(Type.DEVICE_REMOVED)) {
+ connectionExecutor.submit(exceptionSafe(() ->
+ disconnectDevice(event.subject().id())));
+ }
+ }
+
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ return URI_SCHEME.toUpperCase()
+ .equals(event.subject().id().uri().toString());
+ }
+ }
+}
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/package-info.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/package-info.java
new file mode 100644
index 0000000..aa4b93db
--- /dev/null
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/**
+ * Provider that uses drivers as a means of infrastructure device discovery.
+ */
+package org.onosproject.provider.general.device.impl;
\ No newline at end of file
diff --git a/providers/general/device/src/test/resources/device.json b/providers/general/device/src/test/resources/device.json
new file mode 100644
index 0000000..f11a264
--- /dev/null
+++ b/providers/general/device/src/test/resources/device.json
@@ -0,0 +1,6 @@
+{
+ "ip":"1.1.1.1",
+ "port":830,
+ "username":"test",
+ "password":"test"
+}
diff --git a/providers/general/device/src/test/resources/deviceSshKey.json b/providers/general/device/src/test/resources/deviceSshKey.json
new file mode 100644
index 0000000..c799935
--- /dev/null
+++ b/providers/general/device/src/test/resources/deviceSshKey.json
@@ -0,0 +1,7 @@
+{
+ "ip":"1.1.1.1",
+ "port":830,
+ "username":"test",
+ "password":"test",
+ "sshkey":"test"
+}
diff --git a/providers/general/pom.xml b/providers/general/pom.xml
new file mode 100644
index 0000000..f09a78c
--- /dev/null
+++ b/providers/general/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2017-present Open Networking Laboratory
+ ~
+ ~ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-providers</artifactId>
+ <version>1.11.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-general-providers</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS general device provider</description>
+
+ <modules>
+ <module>device</module>
+ <module>app</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>