Carmelo Cascone

ONOS-3972 Implemented BMv2 device provider

BMv2 devices can now be discovered (along with port informations) using
net-cfg (see tools/test/config/bmv2-cfg.json)

Change-Id: Ief2ca790dceb4d047b75aae6c5e1a89a05215e5f
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2014-2016 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-drivers-bmv2</feature>
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle>
<bundle>mvn:org.apache.thrift/libthrift/0.9.2</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle>
</feature>
</features>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014-2016 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/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-app</artifactId>
<packaging>bundle</packaging>
<description>ONOS BMv2 southbound providers</description>
<properties>
<onos.app.name>org.onosproject.bmv2</onos.app.name>
<onos.app.title>BMv2 Provider</onos.app.title>
<onos.app.category>Provider</onos.app.category>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-provider-device</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014-2016 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/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-provider-device</artifactId>
<packaging>bundle</packaging>
<description>ONOS BMv2 device provider</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-drivers-bmv2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-common</artifactId>
<version>1.6.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
/*
* Copyright 2014-2016 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.bmv2.device.impl;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
import org.onosproject.common.net.AbstractDeviceProvider;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.config.basics.ConfigException;
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.PortDiscovery;
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.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.ping;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
import static org.slf4j.LoggerFactory.getLogger;
/**
* BMv2 device provider.
*/
@Component(immediate = true)
public class Bmv2DeviceProvider extends AbstractDeviceProvider {
private final Logger log = getLogger(Bmv2DeviceProvider.class);
public static final String MANUFACTURER = "p4.org";
public static final String HW_VERSION = "bmv2";
private static final String APP_NAME = "org.onosproject.bmv2";
private static final String UNKNOWN = "unknown";
public static final String SCHEME = "bmv2";
private final ExecutorService deviceDiscoveryExecutor = Executors
.newFixedThreadPool(5, groupedThreads("onos/bmv2", "device-discovery", log));
private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
private final ConfigFactory cfgFactory =
new ConfigFactory<ApplicationId, Bmv2ProviderConfig>(
APP_SUBJECT_FACTORY, Bmv2ProviderConfig.class,
"devices", true) {
@Override
public Bmv2ProviderConfig createConfig() {
return new Bmv2ProviderConfig();
}
};
private final Set<DeviceId> activeDevices = Sets.newConcurrentHashSet();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry netCfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
private ApplicationId appId;
/**
* Creates a Bmv2 device provider with the supplied identifier.
*/
public Bmv2DeviceProvider() {
super(new ProviderId("bmv2", "org.onosproject.provider.device"));
}
protected static DeviceId deviceIdOf(Bmv2ProviderConfig.Bmv2DeviceInfo info) {
try {
return DeviceId.deviceId(new URI(
SCHEME, info.ip().toString() + ":" + info.port(), null));
} catch (URISyntaxException e) {
throw new IllegalArgumentException(
"Unable to build deviceID for device "
+ info.ip().toString() + ":" + info.ip().toString(),
e);
}
}
@Override
protected void activate() {
appId = coreService.registerApplication(APP_NAME);
netCfgService.registerConfigFactory(cfgFactory);
netCfgService.addListener(cfgListener);
super.activate();
}
@Override
protected void deactivate() {
try {
activeDevices.stream().forEach(did -> {
deviceDiscoveryExecutor.execute(() -> disconnectDevice(did));
});
deviceDiscoveryExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error("Device discovery threads did not terminate");
}
deviceDiscoveryExecutor.shutdownNow();
netCfgService.unregisterConfigFactory(cfgFactory);
netCfgService.removeListener(cfgListener);
super.deactivate();
}
@Override
public void triggerProbe(DeviceId deviceId) {
deviceDiscoveryExecutor.execute(() -> executeProbe(deviceId));
}
private void executeProbe(DeviceId did) {
boolean reachable = isReachable(did);
log.debug("Probed device: id={}, reachable={}",
did.toString(),
reachable);
if (reachable) {
connectDevice(did);
} else {
disconnectDevice(did);
}
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
log.debug("roleChanged() is not yet implemented");
// TODO: implement mastership handling
}
@Override
public boolean isReachable(DeviceId deviceId) {
return ping(deviceId);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
log.debug("changePortState() is not yet implemented");
// TODO: implement port handling
}
private void connectDevice(DeviceId did) {
log.debug("Trying to create device on ONOS core: {}", did);
SparseAnnotations annotations = DefaultAnnotations.builder()
.set(AnnotationKeys.PROTOCOL, SCHEME)
.build();
DeviceDescription descr = new DefaultDeviceDescription(
did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION,
UNKNOWN, UNKNOWN, new ChassisId(), annotations);
providerService.deviceConnected(did, descr);
activeDevices.add(did);
discoverPorts(did);
}
private void discoverPorts(DeviceId did) {
Device device = deviceService.getDevice(did);
if (device.is(PortDiscovery.class)) {
PortDiscovery portConfig = device.as(PortDiscovery.class);
providerService.updatePorts(did, portConfig.getPorts());
} else {
log.warn("No PortDiscovery behavior for device {}", did);
}
}
private void disconnectDevice(DeviceId did) {
log.debug("Trying to remove device from ONOS core: {}", did);
providerService.deviceDisconnected(did);
activeDevices.remove(did);
}
/**
* Handles net-cfg events.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
Bmv2ProviderConfig cfg = netCfgService.getConfig(appId, Bmv2ProviderConfig.class);
if (cfg != null) {
try {
cfg.getDevicesInfo().stream().forEach(info -> {
triggerProbe(deviceIdOf(info));
});
} catch (ConfigException e) {
log.error("Unable to read config: " + e);
}
} else {
log.error("Unable to read config (was null)");
}
}
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return event.configClass().equals(Bmv2ProviderConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
}
}
/*
* Copyright 2014-2016 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.bmv2.device.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.annotations.Beta;
import com.google.common.collect.Sets;
import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.net.config.Config;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Configuration decoder for Bmv2 provider.
*/
@Beta
public class Bmv2ProviderConfig extends Config<ApplicationId> {
public static final String CONFIG_VALUE_ERROR = "Error parsing config value";
private static final String IP = "ip";
private static final int DEFAULT_THRIFT_PORT = 9090;
private static final String PORT = "port";
/**
* Retrieves a set of Bmv2DeviceInfo containing all the device
* configuration pertaining to the Bmv2 device provider.
*
* @return set of device configurations.
* @throws ConfigException if configuration can't be read
*/
public Set<Bmv2DeviceInfo> getDevicesInfo() throws ConfigException {
Set<Bmv2DeviceInfo> deviceInfos = Sets.newHashSet();
try {
for (JsonNode node : array) {
String ip = node.path(IP).asText();
IpAddress ipAddr = ip.isEmpty() ? null : IpAddress.valueOf(ip);
int port = node.path(PORT).asInt(DEFAULT_THRIFT_PORT);
deviceInfos.add(new Bmv2DeviceInfo(ipAddr, port));
}
} catch (IllegalArgumentException e) {
throw new ConfigException(CONFIG_VALUE_ERROR, e);
}
return deviceInfos;
}
/**
* Contains information about a Bmv2 device retrieved from the net-cfg
* subsystem.
*/
public static class Bmv2DeviceInfo {
private final IpAddress ip;
private final int port;
/**
* Build an information object containing the given device specifics.
*
* @param ip ip
* @param port port
*/
public Bmv2DeviceInfo(IpAddress ip, int port) {
// TODO use generalized host string instead of IP address
this.ip = checkNotNull(ip, "ip cannot be null");
this.port = checkNotNull(port, "port cannot be null");
}
/**
* Returns IpAddress of the device.
*
* @return ip
*/
public IpAddress ip() {
return ip;
}
/**
* Returns port of the device.
*
* @return port
*/
public int port() {
return port;
}
}
}
/*
* Copyright 2014-2016 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 use BMv2 Thrift RPCs as a mean of infrastructure device discovery.
*/
package org.onosproject.provider.bmv2.device.impl;
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014-2016 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/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-providers</artifactId>
<packaging>pom</packaging>
<description>ONOS BMv2 providers</description>
<modules>
<module>app</module>
<module>device</module>
</modules>
</project>
\ No newline at end of file
......@@ -45,6 +45,7 @@
<module>lldpcommon</module>
<module>lldp</module>
<module>netcfglinks</module>
<module>bmv2</module>
</modules>
<dependencies>
......
{
"apps":{
"org.onosproject.bmv2":{
"devices":[{
"ip":"192.168.57.100",
"port":9090
}]
}
}
}
\ No newline at end of file