Carmelo Cascone
Committed by Gerrit Code Review

Simplified Bmv2 device context service and context handling in demo apps

Change-Id: I2a13ed673902d0616732d43c841f50b1ad38cd4c
...@@ -56,9 +56,11 @@ import org.onosproject.net.topology.TopologyVertex; ...@@ -56,9 +56,11 @@ import org.onosproject.net.topology.TopologyVertex;
56 import org.slf4j.Logger; 56 import org.slf4j.Logger;
57 57
58 import java.util.Collection; 58 import java.util.Collection;
59 +import java.util.Collections;
59 import java.util.List; 60 import java.util.List;
60 import java.util.Map; 61 import java.util.Map;
61 import java.util.Set; 62 import java.util.Set;
63 +import java.util.concurrent.ConcurrentMap;
62 import java.util.concurrent.ExecutorService; 64 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.Executors; 65 import java.util.concurrent.Executors;
64 import java.util.concurrent.TimeUnit; 66 import java.util.concurrent.TimeUnit;
...@@ -135,10 +137,13 @@ public abstract class AbstractUpgradableFabricApp { ...@@ -135,10 +137,13 @@ public abstract class AbstractUpgradableFabricApp {
135 private Set<DeviceId> spineSwitches; 137 private Set<DeviceId> spineSwitches;
136 138
137 private Map<DeviceId, List<FlowRule>> deviceFlowRules; 139 private Map<DeviceId, List<FlowRule>> deviceFlowRules;
138 - private Map<DeviceId, Boolean> rulesInstalled; 140 + private Map<DeviceId, Boolean> contextFlags;
141 + private Map<DeviceId, Boolean> ruleFlags;
142 +
143 + private ConcurrentMap<DeviceId, Boolean> deployLocks = Maps.newConcurrentMap();
139 144
140 /** 145 /**
141 - * Creates a new Bmv2 Fabric Component. 146 + * Creates a new BMv2 fabric app.
142 * 147 *
143 * @param appName app name 148 * @param appName app name
144 * @param configurationName a common name for the P4 program / BMv2 configuration used by this app 149 * @param configurationName a common name for the P4 program / BMv2 configuration used by this app
...@@ -212,7 +217,8 @@ public abstract class AbstractUpgradableFabricApp { ...@@ -212,7 +217,8 @@ public abstract class AbstractUpgradableFabricApp {
212 leafSwitches = Sets.newHashSet(); 217 leafSwitches = Sets.newHashSet();
213 spineSwitches = Sets.newHashSet(); 218 spineSwitches = Sets.newHashSet();
214 deviceFlowRules = Maps.newConcurrentMap(); 219 deviceFlowRules = Maps.newConcurrentMap();
215 - rulesInstalled = Maps.newConcurrentMap(); 220 + ruleFlags = Maps.newConcurrentMap();
221 + contextFlags = Maps.newConcurrentMap();
216 } 222 }
217 223
218 // Start flow rules generator... 224 // Start flow rules generator...
...@@ -266,41 +272,19 @@ public abstract class AbstractUpgradableFabricApp { ...@@ -266,41 +272,19 @@ public abstract class AbstractUpgradableFabricApp {
266 272
267 private void deployRoutine() { 273 private void deployRoutine() {
268 if (otherAppFound && otherApp.appActive) { 274 if (otherAppFound && otherApp.appActive) {
269 - log.info("Starting update routine..."); 275 + log.info("Deactivating other app...");
270 - updateRoutine();
271 appService.deactivate(otherApp.appId); 276 appService.deactivate(otherApp.appId);
272 - } else { 277 + try {
273 - Stream.concat(leafSwitches.stream(), spineSwitches.stream()) 278 + Thread.sleep(CLEANUP_SLEEP);
274 - .map(deviceService::getDevice) 279 + } catch (InterruptedException e) {
275 - .forEach(device -> spawnTask(() -> deployDevice(device))); 280 + log.warn("Cleanup sleep interrupted!");
281 + Thread.interrupted();
282 + }
276 } 283 }
277 - }
278 284
279 - private void updateRoutine() {
280 Stream.concat(leafSwitches.stream(), spineSwitches.stream()) 285 Stream.concat(leafSwitches.stream(), spineSwitches.stream())
281 - .forEach(did -> spawnTask(() -> { 286 + .map(deviceService::getDevice)
282 - cleanUpDevice(did); 287 + .forEach(device -> spawnTask(() -> deployDevice(device)));
283 - try {
284 - Thread.sleep(CLEANUP_SLEEP);
285 - } catch (InterruptedException e) {
286 - log.warn("Cleanup sleep interrupted!");
287 - Thread.interrupted();
288 - }
289 - deployDevice(deviceService.getDevice(did));
290 - }));
291 - }
292 -
293 - private void cleanUpDevice(DeviceId deviceId) {
294 - List<FlowRule> flowRulesToRemove = Lists.newArrayList();
295 - flowRuleService.getFlowEntries(deviceId).forEach(fe -> {
296 - if (fe.appId() == otherApp.appId.id()) {
297 - flowRulesToRemove.add(fe);
298 - }
299 - });
300 - if (flowRulesToRemove.size() > 0) {
301 - log.info("Cleaning {} old flow rules from {}...", flowRulesToRemove.size(), deviceId);
302 - removeFlowRules(flowRulesToRemove);
303 - }
304 } 288 }
305 289
306 /** 290 /**
...@@ -309,36 +293,35 @@ public abstract class AbstractUpgradableFabricApp { ...@@ -309,36 +293,35 @@ public abstract class AbstractUpgradableFabricApp {
309 * @param device a device 293 * @param device a device
310 */ 294 */
311 public void deployDevice(Device device) { 295 public void deployDevice(Device device) {
312 - // Serialize executions per device ID using a concurrent map. 296 +
313 - rulesInstalled.compute(device.id(), (did, deployed) -> { 297 + DeviceId deviceId = device.id();
314 - Bmv2DeviceContext deviceContext = bmv2ContextService.getContext(device.id()); 298 +
315 - if (deviceContext == null) { 299 + // Synchronize executions over the same device.
316 - log.error("Unable to get context for device {}", device.id()); 300 + deployLocks.putIfAbsent(deviceId, new Boolean(true));
317 - return deployed; 301 + synchronized (deployLocks.get(deviceId)) {
318 - } else if (!deviceContext.equals(bmv2Context)) { 302 +
319 - log.info("Swapping configuration to {} on device {}...", configurationName, device.id()); 303 + // Set context if not already done.
320 - bmv2ContextService.triggerConfigurationSwap(device.id(), bmv2Context); 304 + if (!contextFlags.getOrDefault(deviceId, false)) {
321 - return deployed; 305 + log.info("Setting context to {} for {}...", configurationName, deviceId);
306 + bmv2ContextService.setContext(deviceId, bmv2Context);
307 + contextFlags.put(device.id(), true);
308 + }
309 +
310 + // Initialize device.
311 + if (!initDevice(deviceId)) {
312 + log.warn("Failed to initialize device {}", deviceId);
322 } 313 }
323 314
324 - List<FlowRule> rules = deviceFlowRules.get(device.id()); 315 + // Install rules.
325 - if (initDevice(device.id())) { 316 + if (!ruleFlags.getOrDefault(deviceId, false)) {
326 - if (deployed == null && rules != null && rules.size() > 0) { 317 + List<FlowRule> rules = deviceFlowRules.getOrDefault(deviceId, Collections.emptyList());
327 - log.info("Installing rules for {}...", did); 318 + if (rules.size() > 0) {
319 + log.info("Installing rules for {}...", deviceId);
328 installFlowRules(rules); 320 installFlowRules(rules);
329 - return true; 321 + ruleFlags.put(deviceId, true);
330 - }
331 - } else {
332 - log.warn("Filed to initialize device {}", device.id());
333 - if (deployed != null && rules != null && rules.size() > 0) {
334 - log.info("Removing rules for {}...", did);
335 - removeFlowRules(rules);
336 - return null;
337 } 322 }
338 } 323 }
339 - 324 + }
340 - return deployed;
341 - });
342 } 325 }
343 326
344 private void spawnTask(Runnable task) { 327 private void spawnTask(Runnable task) {
......
...@@ -164,7 +164,7 @@ public interface Bmv2DeviceAgent { ...@@ -164,7 +164,7 @@ public interface Bmv2DeviceAgent {
164 * @param jsonString a string value 164 * @param jsonString a string value
165 * @throws Bmv2RuntimeException if any error occurs 165 * @throws Bmv2RuntimeException if any error occurs
166 */ 166 */
167 - void loadNewJsonConfig(String jsonString) throws Bmv2RuntimeException; 167 + void uploadNewJsonConfig(String jsonString) throws Bmv2RuntimeException;
168 168
169 /** 169 /**
170 * Triggers a configuration swap on the device. 170 * Triggers a configuration swap on the device.
......
...@@ -25,12 +25,8 @@ import org.onosproject.net.DeviceId; ...@@ -25,12 +25,8 @@ import org.onosproject.net.DeviceId;
25 */ 25 */
26 public interface Bmv2DeviceContextService { 26 public interface Bmv2DeviceContextService {
27 27
28 - // TODO: handle the potential configuration states (e.g. RUNNING, SWAP_REQUESTED, etc.)
29 -
30 /** 28 /**
31 - * Returns the context of a given device. The context returned is the last one for which a configuration swap was 29 + * Returns the context of the given device, null if no context has been previously set.
32 - * triggered, hence there's no guarantees that the device is enforcing the returned context's configuration at the
33 - * time of the call.
34 * 30 *
35 * @param deviceId a device ID 31 * @param deviceId a device ID
36 * @return a BMv2 device context 32 * @return a BMv2 device context
...@@ -38,12 +34,12 @@ public interface Bmv2DeviceContextService { ...@@ -38,12 +34,12 @@ public interface Bmv2DeviceContextService {
38 Bmv2DeviceContext getContext(DeviceId deviceId); 34 Bmv2DeviceContext getContext(DeviceId deviceId);
39 35
40 /** 36 /**
41 - * Triggers a configuration swap on a given device. 37 + * Sets the context for the given device.
42 * 38 *
43 * @param deviceId a device ID 39 * @param deviceId a device ID
44 * @param context a BMv2 device context 40 * @param context a BMv2 device context
45 */ 41 */
46 - void triggerConfigurationSwap(DeviceId deviceId, Bmv2DeviceContext context); 42 + void setContext(DeviceId deviceId, Bmv2DeviceContext context);
47 43
48 /** 44 /**
49 * Binds the given interpreter with the given class loader so that other ONOS instances in the cluster can properly 45 * Binds the given interpreter with the given class loader so that other ONOS instances in the cluster can properly
...@@ -55,14 +51,9 @@ public interface Bmv2DeviceContextService { ...@@ -55,14 +51,9 @@ public interface Bmv2DeviceContextService {
55 void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader); 51 void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader);
56 52
57 /** 53 /**
58 - * Notifies this service that a given device has been updated, meaning a potential context change. 54 + * Returns a default context.
59 - * It returns true if the device configuration is the same as the last for which a swap was triggered, false
60 - * otherwise. In the last case, the service will asynchronously trigger a swap to the last
61 - * configuration stored by this service. If no swap has already been triggered then a default configuration will be
62 - * applied.
63 * 55 *
64 - * @param deviceId a device ID 56 + * @return a BMv2 device context
65 - * @return a boolean value
66 */ 57 */
67 - boolean notifyDeviceChange(DeviceId deviceId); 58 + Bmv2DeviceContext defaultContext();
68 } 59 }
......
...@@ -397,7 +397,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent { ...@@ -397,7 +397,7 @@ public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent {
397 } 397 }
398 398
399 @Override 399 @Override
400 - public void loadNewJsonConfig(String jsonString) throws Bmv2RuntimeException { 400 + public void uploadNewJsonConfig(String jsonString) throws Bmv2RuntimeException {
401 401
402 log.debug("Loading new JSON config on device... > deviceId={}, jsonStringLength={}", 402 log.debug("Loading new JSON config on device... > deviceId={}, jsonStringLength={}",
403 deviceId, jsonString.length()); 403 deviceId, jsonString.length());
......
...@@ -185,9 +185,11 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -185,9 +185,11 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
185 (!Objects.equals(thisDescription, lastDescription) || 185 (!Objects.equals(thisDescription, lastDescription) ||
186 !Objects.equals(thisDescription.annotations(), lastDescription.annotations())); 186 !Objects.equals(thisDescription.annotations(), lastDescription.annotations()));
187 if (descriptionChanged || !deviceService.isAvailable(did)) { 187 if (descriptionChanged || !deviceService.isAvailable(did)) {
188 - // Device description changed or device not available in the core. 188 + if (contextService.getContext(did) == null) {
189 - if (contextService.notifyDeviceChange(did)) { 189 + // Device is a first timer.
190 - // Configuration is the expected one, we can proceed notifying the core. 190 + log.info("Setting DEFAULT context for {}", did);
191 + contextService.setContext(did, contextService.defaultContext());
192 + } else {
191 resetDeviceState(did); 193 resetDeviceState(did);
192 initPortCounters(did); 194 initPortCounters(did);
193 providerService.deviceConnected(did, thisDescription); 195 providerService.deviceConnected(did, thisDescription);
...@@ -295,7 +297,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -295,7 +297,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
295 if (cfg != null) { 297 if (cfg != null) {
296 try { 298 try {
297 cfg.getDevicesInfo().stream().forEach(info -> { 299 cfg.getDevicesInfo().stream().forEach(info -> {
298 - // TODO: require also bmv2 internal device id from net-cfg (now is default 0) 300 + // FIXME: require also bmv2 internal device id from net-cfg (now is default 0)
299 Bmv2Device bmv2Device = new Bmv2Device(info.ip().toString(), info.port(), 0); 301 Bmv2Device bmv2Device = new Bmv2Device(info.ip().toString(), info.port(), 0);
300 triggerProbe(bmv2Device.asDeviceId()); 302 triggerProbe(bmv2Device.asDeviceId());
301 }); 303 });
......