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
1 +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 +<!--
3 + ~ Copyright 2014-2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
18 + <feature name="${project.artifactId}" version="${project.version}"
19 + description="${project.description}">
20 + <feature>onos-drivers-bmv2</feature>
21 + <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
22 + <bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle>
23 + <bundle>mvn:org.apache.thrift/libthrift/0.9.2</bundle>
24 + <bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle>
25 + </feature>
26 +</features>
27 +
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014-2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21 + <parent>
22 + <artifactId>onos-bmv2-providers</artifactId>
23 + <groupId>org.onosproject</groupId>
24 + <version>1.6.0-SNAPSHOT</version>
25 + </parent>
26 +
27 + <modelVersion>4.0.0</modelVersion>
28 +
29 + <artifactId>onos-bmv2-app</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>ONOS BMv2 southbound providers</description>
33 +
34 + <properties>
35 + <onos.app.name>org.onosproject.bmv2</onos.app.name>
36 + <onos.app.title>BMv2 Provider</onos.app.title>
37 + <onos.app.category>Provider</onos.app.category>
38 + </properties>
39 +
40 + <dependencies>
41 + <dependency>
42 + <groupId>org.onosproject</groupId>
43 + <artifactId>onos-bmv2-provider-device</artifactId>
44 + <version>${project.version}</version>
45 + </dependency>
46 + </dependencies>
47 +
48 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014-2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21 + <parent>
22 + <artifactId>onos-bmv2-providers</artifactId>
23 + <groupId>org.onosproject</groupId>
24 + <version>1.6.0-SNAPSHOT</version>
25 + </parent>
26 + <modelVersion>4.0.0</modelVersion>
27 +
28 + <artifactId>onos-bmv2-provider-device</artifactId>
29 + <packaging>bundle</packaging>
30 +
31 + <description>ONOS BMv2 device provider</description>
32 +
33 + <dependencies>
34 + <dependency>
35 + <groupId>org.onosproject</groupId>
36 + <artifactId>onos-drivers-bmv2</artifactId>
37 + <version>${project.version}</version>
38 + </dependency>
39 + <dependency>
40 + <groupId>org.onosproject</groupId>
41 + <artifactId>onos-core-common</artifactId>
42 + <version>1.6.0-SNAPSHOT</version>
43 + </dependency>
44 + </dependencies>
45 +</project>
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2014-2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.provider.bmv2.device.impl;
18 +
19 +import com.google.common.collect.Sets;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.onlab.packet.ChassisId;
24 +import org.onosproject.common.net.AbstractDeviceProvider;
25 +import org.onosproject.core.ApplicationId;
26 +import org.onosproject.core.CoreService;
27 +import org.onosproject.incubator.net.config.basics.ConfigException;
28 +import org.onosproject.net.AnnotationKeys;
29 +import org.onosproject.net.DefaultAnnotations;
30 +import org.onosproject.net.Device;
31 +import org.onosproject.net.DeviceId;
32 +import org.onosproject.net.MastershipRole;
33 +import org.onosproject.net.PortNumber;
34 +import org.onosproject.net.SparseAnnotations;
35 +import org.onosproject.net.behaviour.PortDiscovery;
36 +import org.onosproject.net.config.ConfigFactory;
37 +import org.onosproject.net.config.NetworkConfigEvent;
38 +import org.onosproject.net.config.NetworkConfigListener;
39 +import org.onosproject.net.config.NetworkConfigRegistry;
40 +import org.onosproject.net.device.DefaultDeviceDescription;
41 +import org.onosproject.net.device.DeviceDescription;
42 +import org.onosproject.net.device.DeviceService;
43 +import org.onosproject.net.provider.ProviderId;
44 +import org.slf4j.Logger;
45 +
46 +import java.net.URI;
47 +import java.net.URISyntaxException;
48 +import java.util.Set;
49 +import java.util.concurrent.ExecutorService;
50 +import java.util.concurrent.Executors;
51 +import java.util.concurrent.TimeUnit;
52 +
53 +import static org.onlab.util.Tools.groupedThreads;
54 +import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.ping;
55 +import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
56 +import static org.slf4j.LoggerFactory.getLogger;
57 +
58 +/**
59 + * BMv2 device provider.
60 + */
61 +@Component(immediate = true)
62 +public class Bmv2DeviceProvider extends AbstractDeviceProvider {
63 +
64 + private final Logger log = getLogger(Bmv2DeviceProvider.class);
65 +
66 + public static final String MANUFACTURER = "p4.org";
67 + public static final String HW_VERSION = "bmv2";
68 + private static final String APP_NAME = "org.onosproject.bmv2";
69 + private static final String UNKNOWN = "unknown";
70 + public static final String SCHEME = "bmv2";
71 +
72 + private final ExecutorService deviceDiscoveryExecutor = Executors
73 + .newFixedThreadPool(5, groupedThreads("onos/bmv2", "device-discovery", log));
74 +
75 + private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
76 +
77 + private final ConfigFactory cfgFactory =
78 + new ConfigFactory<ApplicationId, Bmv2ProviderConfig>(
79 + APP_SUBJECT_FACTORY, Bmv2ProviderConfig.class,
80 + "devices", true) {
81 + @Override
82 + public Bmv2ProviderConfig createConfig() {
83 + return new Bmv2ProviderConfig();
84 + }
85 + };
86 +
87 + private final Set<DeviceId> activeDevices = Sets.newConcurrentHashSet();
88 +
89 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 + protected NetworkConfigRegistry netCfgService;
91 +
92 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 + protected CoreService coreService;
94 +
95 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 + protected DeviceService deviceService;
97 +
98 + private ApplicationId appId;
99 +
100 + /**
101 + * Creates a Bmv2 device provider with the supplied identifier.
102 + */
103 + public Bmv2DeviceProvider() {
104 + super(new ProviderId("bmv2", "org.onosproject.provider.device"));
105 + }
106 +
107 + protected static DeviceId deviceIdOf(Bmv2ProviderConfig.Bmv2DeviceInfo info) {
108 + try {
109 + return DeviceId.deviceId(new URI(
110 + SCHEME, info.ip().toString() + ":" + info.port(), null));
111 + } catch (URISyntaxException e) {
112 + throw new IllegalArgumentException(
113 + "Unable to build deviceID for device "
114 + + info.ip().toString() + ":" + info.ip().toString(),
115 + e);
116 + }
117 + }
118 +
119 + @Override
120 + protected void activate() {
121 + appId = coreService.registerApplication(APP_NAME);
122 + netCfgService.registerConfigFactory(cfgFactory);
123 + netCfgService.addListener(cfgListener);
124 +
125 + super.activate();
126 + }
127 +
128 + @Override
129 + protected void deactivate() {
130 + try {
131 + activeDevices.stream().forEach(did -> {
132 + deviceDiscoveryExecutor.execute(() -> disconnectDevice(did));
133 + });
134 + deviceDiscoveryExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
135 + } catch (InterruptedException e) {
136 + log.error("Device discovery threads did not terminate");
137 + }
138 + deviceDiscoveryExecutor.shutdownNow();
139 + netCfgService.unregisterConfigFactory(cfgFactory);
140 + netCfgService.removeListener(cfgListener);
141 +
142 + super.deactivate();
143 + }
144 +
145 + @Override
146 + public void triggerProbe(DeviceId deviceId) {
147 + deviceDiscoveryExecutor.execute(() -> executeProbe(deviceId));
148 + }
149 +
150 + private void executeProbe(DeviceId did) {
151 + boolean reachable = isReachable(did);
152 + log.debug("Probed device: id={}, reachable={}",
153 + did.toString(),
154 + reachable);
155 + if (reachable) {
156 + connectDevice(did);
157 + } else {
158 + disconnectDevice(did);
159 + }
160 + }
161 +
162 + @Override
163 + public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
164 + log.debug("roleChanged() is not yet implemented");
165 + // TODO: implement mastership handling
166 + }
167 +
168 + @Override
169 + public boolean isReachable(DeviceId deviceId) {
170 + return ping(deviceId);
171 + }
172 +
173 + @Override
174 + public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
175 + log.debug("changePortState() is not yet implemented");
176 + // TODO: implement port handling
177 + }
178 +
179 + private void connectDevice(DeviceId did) {
180 + log.debug("Trying to create device on ONOS core: {}", did);
181 + SparseAnnotations annotations = DefaultAnnotations.builder()
182 + .set(AnnotationKeys.PROTOCOL, SCHEME)
183 + .build();
184 + DeviceDescription descr = new DefaultDeviceDescription(
185 + did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION,
186 + UNKNOWN, UNKNOWN, new ChassisId(), annotations);
187 + providerService.deviceConnected(did, descr);
188 + activeDevices.add(did);
189 + discoverPorts(did);
190 + }
191 +
192 + private void discoverPorts(DeviceId did) {
193 + Device device = deviceService.getDevice(did);
194 + if (device.is(PortDiscovery.class)) {
195 + PortDiscovery portConfig = device.as(PortDiscovery.class);
196 + providerService.updatePorts(did, portConfig.getPorts());
197 + } else {
198 + log.warn("No PortDiscovery behavior for device {}", did);
199 + }
200 + }
201 +
202 + private void disconnectDevice(DeviceId did) {
203 + log.debug("Trying to remove device from ONOS core: {}", did);
204 + providerService.deviceDisconnected(did);
205 + activeDevices.remove(did);
206 + }
207 +
208 + /**
209 + * Handles net-cfg events.
210 + */
211 + private class InternalNetworkConfigListener implements NetworkConfigListener {
212 +
213 + @Override
214 + public void event(NetworkConfigEvent event) {
215 + Bmv2ProviderConfig cfg = netCfgService.getConfig(appId, Bmv2ProviderConfig.class);
216 + if (cfg != null) {
217 + try {
218 + cfg.getDevicesInfo().stream().forEach(info -> {
219 + triggerProbe(deviceIdOf(info));
220 + });
221 + } catch (ConfigException e) {
222 + log.error("Unable to read config: " + e);
223 + }
224 + } else {
225 + log.error("Unable to read config (was null)");
226 + }
227 + }
228 +
229 + @Override
230 + public boolean isRelevant(NetworkConfigEvent event) {
231 + return event.configClass().equals(Bmv2ProviderConfig.class) &&
232 + (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
233 + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
234 + }
235 + }
236 +}
1 +/*
2 + * Copyright 2014-2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.provider.bmv2.device.impl;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.google.common.annotations.Beta;
21 +import com.google.common.collect.Sets;
22 +import org.onlab.packet.IpAddress;
23 +import org.onosproject.core.ApplicationId;
24 +import org.onosproject.incubator.net.config.basics.ConfigException;
25 +import org.onosproject.net.config.Config;
26 +
27 +import java.util.Set;
28 +
29 +import static com.google.common.base.Preconditions.checkNotNull;
30 +
31 +/**
32 + * Configuration decoder for Bmv2 provider.
33 + */
34 +@Beta
35 +public class Bmv2ProviderConfig extends Config<ApplicationId> {
36 + public static final String CONFIG_VALUE_ERROR = "Error parsing config value";
37 + private static final String IP = "ip";
38 + private static final int DEFAULT_THRIFT_PORT = 9090;
39 + private static final String PORT = "port";
40 +
41 + /**
42 + * Retrieves a set of Bmv2DeviceInfo containing all the device
43 + * configuration pertaining to the Bmv2 device provider.
44 + *
45 + * @return set of device configurations.
46 + * @throws ConfigException if configuration can't be read
47 + */
48 + public Set<Bmv2DeviceInfo> getDevicesInfo() throws ConfigException {
49 + Set<Bmv2DeviceInfo> deviceInfos = Sets.newHashSet();
50 +
51 + try {
52 + for (JsonNode node : array) {
53 + String ip = node.path(IP).asText();
54 + IpAddress ipAddr = ip.isEmpty() ? null : IpAddress.valueOf(ip);
55 + int port = node.path(PORT).asInt(DEFAULT_THRIFT_PORT);
56 + deviceInfos.add(new Bmv2DeviceInfo(ipAddr, port));
57 +
58 + }
59 + } catch (IllegalArgumentException e) {
60 + throw new ConfigException(CONFIG_VALUE_ERROR, e);
61 + }
62 +
63 + return deviceInfos;
64 + }
65 +
66 + /**
67 + * Contains information about a Bmv2 device retrieved from the net-cfg
68 + * subsystem.
69 + */
70 + public static class Bmv2DeviceInfo {
71 + private final IpAddress ip;
72 + private final int port;
73 +
74 + /**
75 + * Build an information object containing the given device specifics.
76 + *
77 + * @param ip ip
78 + * @param port port
79 + */
80 + public Bmv2DeviceInfo(IpAddress ip, int port) {
81 + // TODO use generalized host string instead of IP address
82 + this.ip = checkNotNull(ip, "ip cannot be null");
83 + this.port = checkNotNull(port, "port cannot be null");
84 + }
85 +
86 + /**
87 + * Returns IpAddress of the device.
88 + *
89 + * @return ip
90 + */
91 + public IpAddress ip() {
92 + return ip;
93 + }
94 +
95 + /**
96 + * Returns port of the device.
97 + *
98 + * @return port
99 + */
100 + public int port() {
101 + return port;
102 + }
103 + }
104 +
105 +}
1 +/*
2 + * Copyright 2014-2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Provider that use BMv2 Thrift RPCs as a mean of infrastructure device discovery.
19 + */
20 +package org.onosproject.provider.bmv2.device.impl;
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014-2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +
18 +<project xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21 + <parent>
22 + <artifactId>onos-providers</artifactId>
23 + <groupId>org.onosproject</groupId>
24 + <version>1.6.0-SNAPSHOT</version>
25 + </parent>
26 +
27 + <modelVersion>4.0.0</modelVersion>
28 +
29 + <artifactId>onos-bmv2-providers</artifactId>
30 + <packaging>pom</packaging>
31 +
32 + <description>ONOS BMv2 providers</description>
33 +
34 + <modules>
35 + <module>app</module>
36 + <module>device</module>
37 + </modules>
38 +
39 +</project>
...\ No newline at end of file ...\ No newline at end of file
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
45 <module>lldpcommon</module> 45 <module>lldpcommon</module>
46 <module>lldp</module> 46 <module>lldp</module>
47 <module>netcfglinks</module> 47 <module>netcfglinks</module>
48 + <module>bmv2</module>
48 </modules> 49 </modules>
49 50
50 <dependencies> 51 <dependencies>
......
1 +{
2 + "apps":{
3 + "org.onosproject.bmv2":{
4 + "devices":[{
5 + "ip":"192.168.57.100",
6 + "port":9090
7 + }]
8 + }
9 + }
10 +}
...\ No newline at end of file ...\ No newline at end of file