Committed by
Gerrit Code Review
Reworked the dependency on default drivers. Driver manager must NOT depend on these.
Instead, it is ok for the flow manager to have a dependency on the drivers and go fully active/dormant when the default drivers arrive/depart. Removed inclusion of the onos-drivers bundle as part of the onos-openflow app as this caused an unwanted dependency. Change-Id: I614290277d1621c8243c0c19e5d79273f2168016
Showing
6 changed files
with
61 additions
and
35 deletions
| ... | @@ -17,7 +17,7 @@ | ... | @@ -17,7 +17,7 @@ |
| 17 | package org.onosproject.net.driver; | 17 | package org.onosproject.net.driver; |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | - * Service capable of providing a set of default drivers. | 20 | + * Service representing availability of default drivers. |
| 21 | */ | 21 | */ |
| 22 | -public interface DefaultDriverProviderService extends DriverProvider { | 22 | +public interface DefaultDriverProviderService { |
| 23 | } | 23 | } | ... | ... |
| ... | @@ -31,7 +31,6 @@ import org.onosproject.net.driver.Behaviour; | ... | @@ -31,7 +31,6 @@ import org.onosproject.net.driver.Behaviour; |
| 31 | import org.onosproject.net.driver.DefaultDriverData; | 31 | import org.onosproject.net.driver.DefaultDriverData; |
| 32 | import org.onosproject.net.driver.DefaultDriverHandler; | 32 | import org.onosproject.net.driver.DefaultDriverHandler; |
| 33 | import org.onosproject.net.driver.DefaultDriverProvider; | 33 | import org.onosproject.net.driver.DefaultDriverProvider; |
| 34 | -import org.onosproject.net.driver.DefaultDriverProviderService; | ||
| 35 | import org.onosproject.net.driver.Driver; | 34 | import org.onosproject.net.driver.Driver; |
| 36 | import org.onosproject.net.driver.DriverAdminService; | 35 | import org.onosproject.net.driver.DriverAdminService; |
| 37 | import org.onosproject.net.driver.DriverHandler; | 36 | import org.onosproject.net.driver.DriverHandler; |
| ... | @@ -63,21 +62,16 @@ public class DriverManager extends DefaultDriverProvider implements DriverAdminS | ... | @@ -63,21 +62,16 @@ public class DriverManager extends DefaultDriverProvider implements DriverAdminS |
| 63 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 62 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 64 | protected DeviceService deviceService; | 63 | protected DeviceService deviceService; |
| 65 | 64 | ||
| 66 | - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 67 | - protected DefaultDriverProviderService defaultDriverService; | ||
| 68 | - | ||
| 69 | private Set<DriverProvider> providers = Sets.newConcurrentHashSet(); | 65 | private Set<DriverProvider> providers = Sets.newConcurrentHashSet(); |
| 70 | private Map<String, Driver> driverByKey = Maps.newConcurrentMap(); | 66 | private Map<String, Driver> driverByKey = Maps.newConcurrentMap(); |
| 71 | 67 | ||
| 72 | @Activate | 68 | @Activate |
| 73 | protected void activate() { | 69 | protected void activate() { |
| 74 | - registerProvider(defaultDriverService); | ||
| 75 | log.info("Started"); | 70 | log.info("Started"); |
| 76 | } | 71 | } |
| 77 | 72 | ||
| 78 | @Deactivate | 73 | @Deactivate |
| 79 | protected void deactivate() { | 74 | protected void deactivate() { |
| 80 | - unregisterProvider(defaultDriverService); | ||
| 81 | log.info("Stopped"); | 75 | log.info("Stopped"); |
| 82 | } | 76 | } |
| 83 | 77 | ... | ... |
| ... | @@ -27,7 +27,6 @@ import org.onlab.osgi.DefaultServiceDirectory; | ... | @@ -27,7 +27,6 @@ import org.onlab.osgi.DefaultServiceDirectory; |
| 27 | import org.onlab.osgi.ServiceDirectory; | 27 | import org.onlab.osgi.ServiceDirectory; |
| 28 | import org.onlab.util.ItemNotFoundException; | 28 | import org.onlab.util.ItemNotFoundException; |
| 29 | import org.onosproject.cluster.ClusterService; | 29 | import org.onosproject.cluster.ClusterService; |
| 30 | -import org.onosproject.cluster.NodeId; | ||
| 31 | import org.onosproject.mastership.MastershipEvent; | 30 | import org.onosproject.mastership.MastershipEvent; |
| 32 | import org.onosproject.mastership.MastershipListener; | 31 | import org.onosproject.mastership.MastershipListener; |
| 33 | import org.onosproject.mastership.MastershipService; | 32 | import org.onosproject.mastership.MastershipService; |
| ... | @@ -37,6 +36,7 @@ import org.onosproject.net.behaviour.PipelinerContext; | ... | @@ -37,6 +36,7 @@ import org.onosproject.net.behaviour.PipelinerContext; |
| 37 | import org.onosproject.net.device.DeviceEvent; | 36 | import org.onosproject.net.device.DeviceEvent; |
| 38 | import org.onosproject.net.device.DeviceListener; | 37 | import org.onosproject.net.device.DeviceListener; |
| 39 | import org.onosproject.net.device.DeviceService; | 38 | import org.onosproject.net.device.DeviceService; |
| 39 | +import org.onosproject.net.driver.DefaultDriverProviderService; | ||
| 40 | import org.onosproject.net.driver.DriverHandler; | 40 | import org.onosproject.net.driver.DriverHandler; |
| 41 | import org.onosproject.net.driver.DriverService; | 41 | import org.onosproject.net.driver.DriverService; |
| 42 | import org.onosproject.net.flow.FlowRuleService; | 42 | import org.onosproject.net.flow.FlowRuleService; |
| ... | @@ -56,8 +56,8 @@ import org.slf4j.LoggerFactory; | ... | @@ -56,8 +56,8 @@ import org.slf4j.LoggerFactory; |
| 56 | import java.util.Map; | 56 | import java.util.Map; |
| 57 | import java.util.Set; | 57 | import java.util.Set; |
| 58 | import java.util.concurrent.ExecutorService; | 58 | import java.util.concurrent.ExecutorService; |
| 59 | -import java.util.concurrent.Executors; | ||
| 60 | 59 | ||
| 60 | +import static java.util.concurrent.Executors.newFixedThreadPool; | ||
| 61 | import static org.onlab.util.Tools.groupedThreads; | 61 | import static org.onlab.util.Tools.groupedThreads; |
| 62 | 62 | ||
| 63 | /** | 63 | /** |
| ... | @@ -96,6 +96,12 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -96,6 +96,12 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 96 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 96 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 97 | protected FlowObjectiveStore flowObjectiveStore; | 97 | protected FlowObjectiveStore flowObjectiveStore; |
| 98 | 98 | ||
| 99 | + // Note: This must remain an optional dependency to allow re-install of default drivers. | ||
| 100 | + // Note: For now disabled until we can move to OPTIONAL_UNARY dependency | ||
| 101 | + // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC) | ||
| 102 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 103 | + protected DefaultDriverProviderService defaultDriverService; | ||
| 104 | + | ||
| 99 | private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate(); | 105 | private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate(); |
| 100 | 106 | ||
| 101 | private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap(); | 107 | private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap(); |
| ... | @@ -107,20 +113,14 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -107,20 +113,14 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 107 | 113 | ||
| 108 | protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory(); | 114 | protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory(); |
| 109 | 115 | ||
| 110 | - private NodeId localNode; | 116 | + private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap(); |
| 111 | - | ||
| 112 | - private Map<Integer, Set<PendingNext>> pendingForwards = | ||
| 113 | - Maps.newConcurrentMap(); | ||
| 114 | 117 | ||
| 115 | private ExecutorService executorService; | 118 | private ExecutorService executorService; |
| 116 | 119 | ||
| 117 | @Activate | 120 | @Activate |
| 118 | protected void activate() { | 121 | protected void activate() { |
| 119 | - executorService = Executors.newFixedThreadPool( | 122 | + executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d")); |
| 120 | - 4, groupedThreads("onos/objective-installer", "%d")); | ||
| 121 | - | ||
| 122 | flowObjectiveStore.setDelegate(delegate); | 123 | flowObjectiveStore.setDelegate(delegate); |
| 123 | - localNode = clusterService.getLocalNode().id(); | ||
| 124 | mastershipService.addListener(mastershipListener); | 124 | mastershipService.addListener(mastershipListener); |
| 125 | deviceService.addListener(deviceListener); | 125 | deviceService.addListener(deviceListener); |
| 126 | deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id())); | 126 | deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id())); |
| ... | @@ -132,10 +132,39 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -132,10 +132,39 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 132 | flowObjectiveStore.unsetDelegate(delegate); | 132 | flowObjectiveStore.unsetDelegate(delegate); |
| 133 | mastershipService.removeListener(mastershipListener); | 133 | mastershipService.removeListener(mastershipListener); |
| 134 | deviceService.removeListener(deviceListener); | 134 | deviceService.removeListener(deviceListener); |
| 135 | + executorService.shutdown(); | ||
| 136 | + pipeliners.clear(); | ||
| 137 | + driverHandlers.clear(); | ||
| 135 | log.info("Stopped"); | 138 | log.info("Stopped"); |
| 136 | } | 139 | } |
| 137 | 140 | ||
| 138 | /** | 141 | /** |
| 142 | + * Hook for binding the optional default driver providers. | ||
| 143 | + * | ||
| 144 | + * @param service arriving default driver provider service | ||
| 145 | + */ | ||
| 146 | + // Note: For now disabled until we can move to OPTIONAL_UNARY dependency | ||
| 147 | + protected void xbindDefaultDriverService(DefaultDriverProviderService service) { | ||
| 148 | + log.info("Detected default drivers... going active"); | ||
| 149 | + defaultDriverService = service; | ||
| 150 | + deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id())); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + /** | ||
| 154 | + * Hook for unbinding the optional default driver providers. | ||
| 155 | + * | ||
| 156 | + * @param service departing default driver provider service | ||
| 157 | + */ | ||
| 158 | + // Note: For now disabled until we can move to OPTIONAL_UNARY dependency | ||
| 159 | + protected void xunbindDefaultDriverService(DefaultDriverProviderService service) { | ||
| 160 | + log.info("Lost default drivers... going dormant"); | ||
| 161 | + defaultDriverService = null; | ||
| 162 | + pipeliners.clear(); | ||
| 163 | + driverHandlers.clear(); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + | ||
| 167 | + /** | ||
| 139 | * Task that passes the flow objective down to the driver. The task will | 168 | * Task that passes the flow objective down to the driver. The task will |
| 140 | * make a few attempts to find the appropriate driver, then eventually give | 169 | * make a few attempts to find the appropriate driver, then eventually give |
| 141 | * up and report an error if no suitable driver could be found. | 170 | * up and report an error if no suitable driver could be found. |
| ... | @@ -187,13 +216,10 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -187,13 +216,10 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 187 | } | 216 | } |
| 188 | 217 | ||
| 189 | @Override | 218 | @Override |
| 190 | - public void forward(DeviceId deviceId, | 219 | + public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) { |
| 191 | - ForwardingObjective forwardingObjective) { | ||
| 192 | - | ||
| 193 | if (queueObjective(deviceId, forwardingObjective)) { | 220 | if (queueObjective(deviceId, forwardingObjective)) { |
| 194 | return; | 221 | return; |
| 195 | } | 222 | } |
| 196 | - | ||
| 197 | executorService.submit(new ObjectiveInstaller(deviceId, forwardingObjective)); | 223 | executorService.submit(new ObjectiveInstaller(deviceId, forwardingObjective)); |
| 198 | } | 224 | } |
| 199 | 225 | ||
| ... | @@ -212,7 +238,7 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -212,7 +238,7 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 212 | flowObjectiveStore.getNextGroup(fwd.nextId()) == null) { | 238 | flowObjectiveStore.getNextGroup(fwd.nextId()) == null) { |
| 213 | log.trace("Queuing forwarding objective for nextId {}", fwd.nextId()); | 239 | log.trace("Queuing forwarding objective for nextId {}", fwd.nextId()); |
| 214 | if (pendingForwards.putIfAbsent(fwd.nextId(), | 240 | if (pendingForwards.putIfAbsent(fwd.nextId(), |
| 215 | - Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) { | 241 | + Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) { |
| 216 | Set<PendingNext> pending = pendingForwards.get(fwd.nextId()); | 242 | Set<PendingNext> pending = pendingForwards.get(fwd.nextId()); |
| 217 | pending.add(new PendingNext(deviceId, fwd)); | 243 | pending.add(new PendingNext(deviceId, fwd)); |
| 218 | } | 244 | } |
| ... | @@ -228,6 +254,11 @@ public class FlowObjectiveManager implements FlowObjectiveService { | ... | @@ -228,6 +254,11 @@ public class FlowObjectiveManager implements FlowObjectiveService { |
| 228 | } | 254 | } |
| 229 | 255 | ||
| 230 | private void setupPipelineHandler(DeviceId deviceId) { | 256 | private void setupPipelineHandler(DeviceId deviceId) { |
| 257 | + if (defaultDriverService == null) { | ||
| 258 | + // We're not ready to go to work yet. | ||
| 259 | + return; | ||
| 260 | + } | ||
| 261 | + | ||
| 231 | // Attempt to lookup the handler in the cache | 262 | // Attempt to lookup the handler in the cache |
| 232 | DriverHandler handler = driverHandlers.get(deviceId); | 263 | DriverHandler handler = driverHandlers.get(deviceId); |
| 233 | if (handler == null) { | 264 | if (handler == null) { | ... | ... |
| ... | @@ -78,9 +78,6 @@ public class PacketManager | ... | @@ -78,9 +78,6 @@ public class PacketManager |
| 78 | private CoreService coreService; | 78 | private CoreService coreService; |
| 79 | 79 | ||
| 80 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 80 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 81 | - private FlowObjectiveService objectiveService; | ||
| 82 | - | ||
| 83 | - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 84 | private DeviceService deviceService; | 81 | private DeviceService deviceService; |
| 85 | 82 | ||
| 86 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 83 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| ... | @@ -89,6 +86,9 @@ public class PacketManager | ... | @@ -89,6 +86,9 @@ public class PacketManager |
| 89 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 86 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 90 | private PacketStore store; | 87 | private PacketStore store; |
| 91 | 88 | ||
| 89 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 90 | + private FlowObjectiveService objectiveService; | ||
| 91 | + | ||
| 92 | private final DeviceListener deviceListener = new InternalDeviceListener(); | 92 | private final DeviceListener deviceListener = new InternalDeviceListener(); |
| 93 | 93 | ||
| 94 | private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>(); | 94 | private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>(); |
| ... | @@ -100,6 +100,7 @@ public class PacketManager | ... | @@ -100,6 +100,7 @@ public class PacketManager |
| 100 | appId = coreService.getAppId(CoreService.CORE_APP_NAME); | 100 | appId = coreService.getAppId(CoreService.CORE_APP_NAME); |
| 101 | store.setDelegate(delegate); | 101 | store.setDelegate(delegate); |
| 102 | deviceService.addListener(deviceListener); | 102 | deviceService.addListener(deviceListener); |
| 103 | + // TODO: Should we request packets for all existing devices? I believe we should. | ||
| 103 | log.info("Started"); | 104 | log.info("Started"); |
| 104 | } | 105 | } |
| 105 | 106 | ||
| ... | @@ -250,8 +251,7 @@ public class PacketManager | ... | @@ -250,8 +251,7 @@ public class PacketManager |
| 250 | /** | 251 | /** |
| 251 | * Internal callback from the packet store. | 252 | * Internal callback from the packet store. |
| 252 | */ | 253 | */ |
| 253 | - private class InternalStoreDelegate | 254 | + private class InternalStoreDelegate implements PacketStoreDelegate { |
| 254 | - implements PacketStoreDelegate { | ||
| 255 | @Override | 255 | @Override |
| 256 | public void notify(PacketEvent event) { | 256 | public void notify(PacketEvent event) { |
| 257 | localEmit(event.subject()); | 257 | localEmit(event.subject()); | ... | ... |
| ... | @@ -18,9 +18,11 @@ package org.onosproject.driver.pipeline; | ... | @@ -18,9 +18,11 @@ package org.onosproject.driver.pipeline; |
| 18 | import org.apache.felix.scr.annotations.Activate; | 18 | import org.apache.felix.scr.annotations.Activate; |
| 19 | import org.apache.felix.scr.annotations.Component; | 19 | import org.apache.felix.scr.annotations.Component; |
| 20 | import org.apache.felix.scr.annotations.Deactivate; | 20 | import org.apache.felix.scr.annotations.Deactivate; |
| 21 | +import org.apache.felix.scr.annotations.Reference; | ||
| 22 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
| 21 | import org.apache.felix.scr.annotations.Service; | 23 | import org.apache.felix.scr.annotations.Service; |
| 22 | import org.onosproject.net.driver.DefaultDriverProviderService; | 24 | import org.onosproject.net.driver.DefaultDriverProviderService; |
| 23 | -import org.onosproject.net.driver.Driver; | 25 | +import org.onosproject.net.driver.DriverAdminService; |
| 24 | import org.onosproject.net.driver.DriverProvider; | 26 | import org.onosproject.net.driver.DriverProvider; |
| 25 | import org.onosproject.net.driver.XmlDriverLoader; | 27 | import org.onosproject.net.driver.XmlDriverLoader; |
| 26 | import org.slf4j.Logger; | 28 | import org.slf4j.Logger; |
| ... | @@ -28,7 +30,6 @@ import org.slf4j.LoggerFactory; | ... | @@ -28,7 +30,6 @@ import org.slf4j.LoggerFactory; |
| 28 | 30 | ||
| 29 | import java.io.IOException; | 31 | import java.io.IOException; |
| 30 | import java.io.InputStream; | 32 | import java.io.InputStream; |
| 31 | -import java.util.Set; | ||
| 32 | 33 | ||
| 33 | /** | 34 | /** |
| 34 | * Bootstrap for built in drivers. | 35 | * Bootstrap for built in drivers. |
| ... | @@ -43,12 +44,16 @@ public class DefaultDrivers implements DefaultDriverProviderService { | ... | @@ -43,12 +44,16 @@ public class DefaultDrivers implements DefaultDriverProviderService { |
| 43 | 44 | ||
| 44 | private DriverProvider provider; | 45 | private DriverProvider provider; |
| 45 | 46 | ||
| 47 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 48 | + protected DriverAdminService driverAdminService; | ||
| 49 | + | ||
| 46 | @Activate | 50 | @Activate |
| 47 | protected void activate() { | 51 | protected void activate() { |
| 48 | ClassLoader classLoader = getClass().getClassLoader(); | 52 | ClassLoader classLoader = getClass().getClassLoader(); |
| 49 | try { | 53 | try { |
| 50 | InputStream stream = classLoader.getResourceAsStream(DRIVERS_XML); | 54 | InputStream stream = classLoader.getResourceAsStream(DRIVERS_XML); |
| 51 | provider = new XmlDriverLoader(classLoader).loadDrivers(stream); | 55 | provider = new XmlDriverLoader(classLoader).loadDrivers(stream); |
| 56 | + driverAdminService.registerProvider(provider); | ||
| 52 | } catch (IOException e) { | 57 | } catch (IOException e) { |
| 53 | log.error("Unable to load default drivers", e); | 58 | log.error("Unable to load default drivers", e); |
| 54 | } | 59 | } |
| ... | @@ -57,11 +62,8 @@ public class DefaultDrivers implements DefaultDriverProviderService { | ... | @@ -57,11 +62,8 @@ public class DefaultDrivers implements DefaultDriverProviderService { |
| 57 | 62 | ||
| 58 | @Deactivate | 63 | @Deactivate |
| 59 | protected void deactivate() { | 64 | protected void deactivate() { |
| 65 | + driverAdminService.unregisterProvider(provider); | ||
| 60 | log.info("Stopped"); | 66 | log.info("Stopped"); |
| 61 | } | 67 | } |
| 62 | 68 | ||
| 63 | - @Override | ||
| 64 | - public Set<Driver> getDrivers() { | ||
| 65 | - return provider.getDrivers(); | ||
| 66 | - } | ||
| 67 | } | 69 | } | ... | ... |
| ... | @@ -23,7 +23,6 @@ | ... | @@ -23,7 +23,6 @@ |
| 23 | <bundle>mvn:${project.groupId}/onos-of-api/${project.version}</bundle> | 23 | <bundle>mvn:${project.groupId}/onos-of-api/${project.version}</bundle> |
| 24 | <bundle>mvn:${project.groupId}/onos-of-drivers/${project.version}</bundle> | 24 | <bundle>mvn:${project.groupId}/onos-of-drivers/${project.version}</bundle> |
| 25 | <bundle>mvn:${project.groupId}/onos-of-ctl/${project.version}</bundle> | 25 | <bundle>mvn:${project.groupId}/onos-of-ctl/${project.version}</bundle> |
| 26 | - <bundle>mvn:${project.groupId}/onos-drivers/${project.version}</bundle> | ||
| 27 | 26 | ||
| 28 | <bundle>mvn:${project.groupId}/onos-lldp-provider/${project.version}</bundle> | 27 | <bundle>mvn:${project.groupId}/onos-lldp-provider/${project.version}</bundle> |
| 29 | <bundle>mvn:${project.groupId}/onos-host-provider/${project.version}</bundle> | 28 | <bundle>mvn:${project.groupId}/onos-host-provider/${project.version}</bundle> | ... | ... |
-
Please register or login to post a comment