Brian O'Connor

Using replace instead install when there is already an intent

Fixes the problem of flows being left on the data plane

Change-Id: Iec3db8b460123f2744a57d8c08d14c8effe9ec34
...@@ -50,7 +50,6 @@ public interface IntentInstaller<T extends Intent> { ...@@ -50,7 +50,6 @@ public interface IntentInstaller<T extends Intent> {
50 * @return flow rule operations to complete the replace 50 * @return flow rule operations to complete the replace
51 * @throws IntentException if issues are encountered while uninstalling the intent 51 * @throws IntentException if issues are encountered while uninstalling the intent
52 */ 52 */
53 - @Deprecated
54 List<FlowRuleBatchOperation> replace(T oldIntent, T newIntent); 53 List<FlowRuleBatchOperation> replace(T oldIntent, T newIntent);
55 54
56 } 55 }
......
...@@ -49,7 +49,7 @@ class InstallCoordinating implements IntentUpdate { ...@@ -49,7 +49,7 @@ class InstallCoordinating implements IntentUpdate {
49 try { 49 try {
50 //FIXME we orphan flow rules that are currently on the data plane 50 //FIXME we orphan flow rules that are currently on the data plane
51 // ... should either reuse them or remove them 51 // ... should either reuse them or remove them
52 - FlowRuleOperations flowRules = intentManager.coordinate(pending); 52 + FlowRuleOperations flowRules = intentManager.coordinate(current, pending);
53 return Optional.of(new Installing(intentManager, pending, flowRules)); 53 return Optional.of(new Installing(intentManager, pending, flowRules));
54 } catch (IntentException e) { 54 } catch (IntentException e) {
55 log.warn("Unable to generate a FlowRuleOperations from intent {} due to:", pending.intent().id(), e); 55 log.warn("Unable to generate a FlowRuleOperations from intent {} due to:", pending.intent().id(), e);
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
16 package org.onosproject.net.intent.impl; 16 package org.onosproject.net.intent.impl;
17 17
18 import org.onosproject.net.flow.FlowRuleOperations; 18 import org.onosproject.net.flow.FlowRuleOperations;
19 -import org.onosproject.net.intent.Intent;
20 import org.onosproject.net.intent.IntentData; 19 import org.onosproject.net.intent.IntentData;
21 import org.onosproject.net.intent.IntentException; 20 import org.onosproject.net.intent.IntentException;
22 import org.slf4j.Logger; 21 import org.slf4j.Logger;
...@@ -49,9 +48,6 @@ class Installing implements IntentUpdate { ...@@ -49,9 +48,6 @@ class Installing implements IntentUpdate {
49 public Optional<IntentUpdate> execute() { 48 public Optional<IntentUpdate> execute() {
50 try { 49 try {
51 intentManager.flowRuleService.apply(flowRules); // FIXME we need to provide a context 50 intentManager.flowRuleService.apply(flowRules); // FIXME we need to provide a context
52 - for (Intent installable: pending.installables()) {
53 - intentManager.trackerService.addTrackedResources(pending.key(), installable.resources());
54 - }
55 return Optional.of(new Installed(pending)); 51 return Optional.of(new Installed(pending));
56 // What kinds of exceptions are thrown by FlowRuleService.apply()? 52 // What kinds of exceptions are thrown by FlowRuleService.apply()?
57 // Is IntentException a correct exception abstraction? 53 // Is IntentException a correct exception abstraction?
......
...@@ -66,8 +66,10 @@ import java.util.concurrent.Future; ...@@ -66,8 +66,10 @@ import java.util.concurrent.Future;
66 import java.util.stream.Collectors; 66 import java.util.stream.Collectors;
67 67
68 import static com.google.common.base.Preconditions.checkNotNull; 68 import static com.google.common.base.Preconditions.checkNotNull;
69 +import static com.google.common.base.Preconditions.checkState;
69 import static java.util.concurrent.Executors.newFixedThreadPool; 70 import static java.util.concurrent.Executors.newFixedThreadPool;
70 import static java.util.concurrent.Executors.newSingleThreadExecutor; 71 import static java.util.concurrent.Executors.newSingleThreadExecutor;
72 +import static org.onlab.util.Tools.isNullOrEmpty;
71 import static org.onlab.util.Tools.namedThreads; 73 import static org.onlab.util.Tools.namedThreads;
72 import static org.onosproject.net.intent.IntentState.*; 74 import static org.onosproject.net.intent.IntentState.*;
73 import static org.slf4j.LoggerFactory.getLogger; 75 import static org.slf4j.LoggerFactory.getLogger;
...@@ -282,13 +284,42 @@ public class IntentManager ...@@ -282,13 +284,42 @@ public class IntentManager
282 284
283 //TODO javadoc 285 //TODO javadoc
284 //FIXME 286 //FIXME
285 - FlowRuleOperations coordinate(IntentData pending) { 287 + FlowRuleOperations coordinate(IntentData current, IntentData pending) {
286 - List<Intent> installables = pending.installables(); 288 + List<Intent> oldInstallables = (current != null) ? current.installables() : null;
287 - List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(installables.size()); 289 + List<Intent> newInstallables = pending.installables();
288 - for (Intent installable : installables) { 290 +
289 - registerSubclassInstallerIfNeeded(installable); 291 + checkState(isNullOrEmpty(oldInstallables) ||
292 + oldInstallables.size() == newInstallables.size(),
293 + "Old and New Intent must have equivalent installable intents.");
294 +
295 + List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
296 + for (int i = 0; i < newInstallables.size(); i++) {
297 + Intent newInstallable = newInstallables.get(i);
298 + registerSubclassInstallerIfNeeded(newInstallable);
290 //TODO consider migrating installers to FlowRuleOperations 299 //TODO consider migrating installers to FlowRuleOperations
291 - plans.add(getInstaller(installable).install(installable)); 300 + /* FIXME
301 + - we need to do another pass on this method about that doesn't
302 + require the length of installables to be equal, and also doesn't
303 + depend on ordering
304 + - we should also reconsider when to start/stop tracking resources
305 + */
306 + if (isNullOrEmpty(oldInstallables)) {
307 + plans.add(getInstaller(newInstallable).install(newInstallable));
308 + } else {
309 + Intent oldInstallable = oldInstallables.get(i);
310 + checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
311 + "Installable Intent type mismatch.");
312 + trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
313 + plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
314 + }
315 + trackerService.addTrackedResources(pending.key(), newInstallable.resources());
316 +// } catch (IntentException e) {
317 +// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
318 +// //FIXME... we failed. need to uninstall (if same) or revert (if different)
319 +// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
320 +// exception = e;
321 +// batches = uninstallIntent(oldIntent, oldInstallables);
322 +// }
292 } 323 }
293 324
294 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out 325 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
...@@ -321,6 +352,7 @@ public class IntentManager ...@@ -321,6 +352,7 @@ public class IntentManager
321 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>(); 352 List<List<FlowRuleBatchOperation>> plans = new ArrayList<>();
322 for (Intent installable : installables) { 353 for (Intent installable : installables) {
323 plans.add(getInstaller(installable).uninstall(installable)); 354 plans.add(getInstaller(installable).uninstall(installable));
355 + trackerService.removeTrackedResources(pending.key(), installable.resources());
324 } 356 }
325 357
326 return merge(plans).build(new FlowRuleOperationsContext() { 358 return merge(plans).build(new FlowRuleOperationsContext() {
...@@ -494,7 +526,7 @@ public class IntentManager ...@@ -494,7 +526,7 @@ public class IntentManager
494 case INSTALL_REQ: 526 case INSTALL_REQ:
495 return new InstallRequest(this, intentData, Optional.ofNullable(current)); 527 return new InstallRequest(this, intentData, Optional.ofNullable(current));
496 case WITHDRAW_REQ: 528 case WITHDRAW_REQ:
497 - if (current == null) { 529 + if (current == null || isNullOrEmpty(current.installables())) {
498 return new Withdrawn(current, WITHDRAWN); 530 return new Withdrawn(current, WITHDRAWN);
499 } else { 531 } else {
500 return new WithdrawRequest(this, intentData, current); 532 return new WithdrawRequest(this, intentData, current);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onosproject.net.intent.impl; 16 package org.onosproject.net.intent.impl;
17 17
18 import com.google.common.collect.HashMultimap; 18 import com.google.common.collect.HashMultimap;
19 +import com.google.common.collect.Lists;
19 import com.google.common.collect.SetMultimap; 20 import com.google.common.collect.SetMultimap;
20 import org.apache.felix.scr.annotations.Activate; 21 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component; 22 import org.apache.felix.scr.annotations.Component;
...@@ -112,10 +113,13 @@ public class LinkCollectionIntentInstaller ...@@ -112,10 +113,13 @@ public class LinkCollectionIntentInstaller
112 } 113 }
113 114
114 @Override 115 @Override
115 - public List<FlowRuleBatchOperation> replace(LinkCollectionIntent intent, 116 + public List<FlowRuleBatchOperation> replace(LinkCollectionIntent oldIntent,
116 LinkCollectionIntent newIntent) { 117 LinkCollectionIntent newIntent) {
117 - // FIXME: implement 118 + // FIXME: implement this in a more intelligent/less brute force way
118 - return null; 119 + List<FlowRuleBatchOperation> batches = Lists.newArrayList();
120 + batches.addAll(uninstall(oldIntent));
121 + batches.addAll(install(newIntent));
122 + return batches;
119 } 123 }
120 124
121 /** 125 /**
......
...@@ -47,6 +47,7 @@ class WithdrawCoordinating implements IntentUpdate { ...@@ -47,6 +47,7 @@ class WithdrawCoordinating implements IntentUpdate {
47 @Override 47 @Override
48 public Optional<IntentUpdate> execute() { 48 public Optional<IntentUpdate> execute() {
49 try { 49 try {
50 + // Note: current.installables() are not null or empty due to createIntentUpdate check
50 FlowRuleOperations flowRules = intentManager.uninstallCoordinate(current, pending); 51 FlowRuleOperations flowRules = intentManager.uninstallCoordinate(current, pending);
51 pending.setInstallables(current.installables()); 52 pending.setInstallables(current.installables());
52 return Optional.of(new Withdrawing(intentManager, pending, flowRules)); 53 return Optional.of(new Withdrawing(intentManager, pending, flowRules));
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
16 package org.onosproject.net.intent.impl; 16 package org.onosproject.net.intent.impl;
17 17
18 import org.onosproject.net.flow.FlowRuleOperations; 18 import org.onosproject.net.flow.FlowRuleOperations;
19 -import org.onosproject.net.intent.Intent;
20 import org.onosproject.net.intent.IntentData; 19 import org.onosproject.net.intent.IntentData;
21 20
22 import java.util.Optional; 21 import java.util.Optional;
...@@ -43,9 +42,6 @@ class Withdrawing implements IntentUpdate { ...@@ -43,9 +42,6 @@ class Withdrawing implements IntentUpdate {
43 @Override 42 @Override
44 public Optional<IntentUpdate> execute() { 43 public Optional<IntentUpdate> execute() {
45 intentManager.flowRuleService.apply(flowRules); 44 intentManager.flowRuleService.apply(flowRules);
46 - for (Intent installable: pending.installables()) {
47 - intentManager.trackerService.removeTrackedResources(pending.key(), installable.resources());
48 - }
49 return Optional.of(new Withdrawn(pending)); 45 return Optional.of(new Withdrawn(pending));
50 } 46 }
51 } 47 }
......
...@@ -34,6 +34,7 @@ import java.nio.file.SimpleFileVisitor; ...@@ -34,6 +34,7 @@ import java.nio.file.SimpleFileVisitor;
34 import java.nio.file.StandardCopyOption; 34 import java.nio.file.StandardCopyOption;
35 import java.nio.file.attribute.BasicFileAttributes; 35 import java.nio.file.attribute.BasicFileAttributes;
36 import java.util.ArrayList; 36 import java.util.ArrayList;
37 +import java.util.Collection;
37 import java.util.List; 38 import java.util.List;
38 import java.util.concurrent.ThreadFactory; 39 import java.util.concurrent.ThreadFactory;
39 40
...@@ -84,6 +85,16 @@ public abstract class Tools { ...@@ -84,6 +85,16 @@ public abstract class Tools {
84 } 85 }
85 86
86 /** 87 /**
88 + * Returns true if the collection is null or is empty.
89 + *
90 + * @param collection collection to test
91 + * @return true if null or empty; false otherwise
92 + */
93 + public static boolean isNullOrEmpty(Collection collection) {
94 + return collection == null || collection.isEmpty();
95 + }
96 +
97 + /**
87 * Converts a string from hex to long. 98 * Converts a string from hex to long.
88 * 99 *
89 * @param string hex number in string form; sans 0x 100 * @param string hex number in string form; sans 0x
......