blob: da4a4b75f3a188b07cca5f3f0ef05f72ad346070 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor66630c82014-10-02 21:08:19 -070017
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
Brian O'Connor66630c82014-10-02 21:08:19 -070022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -080025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Brian O'Connor66630c82014-10-02 21:08:19 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.core.IdGenerator;
33import org.onosproject.event.AbstractListenerRegistry;
34import org.onosproject.event.EventDeliveryService;
35import org.onosproject.net.flow.CompletedBatchOperation;
36import org.onosproject.net.flow.FlowRuleBatchOperation;
37import org.onosproject.net.flow.FlowRuleService;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentBatchDelegate;
40import org.onosproject.net.intent.IntentBatchService;
41import org.onosproject.net.intent.IntentCompiler;
42import org.onosproject.net.intent.IntentEvent;
43import org.onosproject.net.intent.IntentException;
44import org.onosproject.net.intent.IntentExtensionService;
45import org.onosproject.net.intent.IntentId;
46import org.onosproject.net.intent.IntentInstaller;
47import org.onosproject.net.intent.IntentListener;
48import org.onosproject.net.intent.IntentOperation;
49import org.onosproject.net.intent.IntentOperations;
50import org.onosproject.net.intent.IntentService;
51import org.onosproject.net.intent.IntentState;
52import org.onosproject.net.intent.IntentStore;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080053import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.intent.IntentStoreDelegate;
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -080055import org.osgi.service.component.ComponentContext;
Brian O'Connor66630c82014-10-02 21:08:19 -070056import org.slf4j.Logger;
57
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080058import java.util.ArrayList;
59import java.util.Collections;
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -080060import java.util.Dictionary;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080061import java.util.EnumSet;
62import java.util.List;
63import java.util.Map;
64import java.util.concurrent.ConcurrentHashMap;
65import java.util.concurrent.ConcurrentMap;
66import java.util.concurrent.ExecutionException;
67import java.util.concurrent.ExecutorService;
68import java.util.concurrent.Future;
69import java.util.concurrent.TimeUnit;
70import java.util.concurrent.TimeoutException;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070071
72import static com.google.common.base.Preconditions.checkArgument;
73import static com.google.common.base.Preconditions.checkNotNull;
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -080074import static com.google.common.base.Strings.isNullOrEmpty;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080075import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorabafb502014-12-02 22:26:20 -080076import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070077import static org.onlab.util.Tools.namedThreads;
78import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070079
80/**
81 * An implementation of Intent Manager.
82 */
83@Component(immediate = true)
84@Service
85public class IntentManager
86 implements IntentService, IntentExtensionService {
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -080087
88 private static final int DEFAULT_TIMEOUT_PER_INTENT_OP_MSEC = 500; // ms
89
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080090 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070091
92 public static final String INTENT_NULL = "Intent cannot be null";
93 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
94
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080095 private static final int NUM_THREADS = 12;
96
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080097 private static final EnumSet<IntentState> RECOMPILE
98 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
99 private static final EnumSet<IntentState> NON_PARKED_OR_FAILED
100 = EnumSet.complementOf(EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAW_REQ, WITHDRAWN));
101
102
Brian O'Connor66630c82014-10-02 21:08:19 -0700103 // Collections for compiler, installer, and listener are ONOS instance local
104 private final ConcurrentMap<Class<? extends Intent>,
105 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700106 private final ConcurrentMap<Class<? extends Intent>,
107 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700108
109 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700110 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700111
Brian O'Connor520c0522014-11-23 23:50:47 -0800112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected IntentStore store;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700119 protected IntentBatchService batchService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700122 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700125 protected EventDeliveryService eventDispatcher;
126
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected FlowRuleService flowRuleService;
129
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -0800130 @Property(name = "timeoutPerIntentOpMsec", intValue = DEFAULT_TIMEOUT_PER_INTENT_OP_MSEC,
131 label = "Configure Timeout per Intent Operation; " +
132 "default is 500 msec")
133 private int timeoutPerIntentOpMsec = DEFAULT_TIMEOUT_PER_INTENT_OP_MSEC;
Brian O'Connor520c0522014-11-23 23:50:47 -0800134
135 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800136
137 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
138 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
139 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
140 private IdGenerator idGenerator;
141
Brian O'Connor66630c82014-10-02 21:08:19 -0700142 @Activate
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -0800143 public void activate(ComponentContext context) {
144 readComponentConfiguration(context);
145
Brian O'Connor66630c82014-10-02 21:08:19 -0700146 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700147 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700148 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700149 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800150 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800151 idGenerator = coreService.getIdGenerator("intent-ids");
152 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700153 log.info("Started");
154 }
155
156 @Deactivate
157 public void deactivate() {
158 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700159 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700160 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700161 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700162 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800163 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700164 log.info("Stopped");
165 }
166
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -0800167 @Modified
168 public void modified(ComponentContext context) {
169 readComponentConfiguration(context);
170 }
171
172 /**
173 * Extracts properties from the component configuration context.
174 *
175 * @param context the component context
176 */
177 private void readComponentConfiguration(ComponentContext context) {
178 if (context == null) {
179 return; // Nothing to do
180 }
181 Dictionary<?, ?> properties = context.getProperties();
182
183 Integer timeoutPerIntentOpMsecConfigured =
184 getIntegerProperty(properties, "timeoutPerIntentOpMsec");
185 if (timeoutPerIntentOpMsecConfigured == null) {
186 log.info("Timeout per Intent Operation is not configured, " +
187 "using current value of {} ms", timeoutPerIntentOpMsec);
188 } else {
189 timeoutPerIntentOpMsec = timeoutPerIntentOpMsecConfigured;
190 log.info("Configured. Timeout per Intent Operation is " +
191 "configured to {} ms", timeoutPerIntentOpMsec);
192 }
193 }
194
195 /**
196 * Get Integer property from the propertyName
197 * Return null if propertyName is not found.
198 *
199 * @param properties properties to be looked up
200 * @param propertyName the name of the property to look up
201 * @return value when the propertyName is defined or return null
202 */
203 private static Integer getIntegerProperty(Dictionary<?, ?> properties,
204 String propertyName) {
205 Integer value = null;
206 try {
207 String s = (String) properties.get(propertyName);
208 value = isNullOrEmpty(s) ? null : Integer.parseInt(s.trim());
209 } catch (NumberFormatException | ClassCastException e) {
210 value = null;
211 }
212 return value;
213 }
214
Brian O'Connor66630c82014-10-02 21:08:19 -0700215 @Override
216 public void submit(Intent intent) {
217 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800218 execute(IntentOperations.builder(intent.appId())
219 .addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700220 }
221
222 @Override
223 public void withdraw(Intent intent) {
224 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800225 execute(IntentOperations.builder(intent.appId())
226 .addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700227 }
228
Brian O'Connor66630c82014-10-02 21:08:19 -0700229 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700230 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700231 checkNotNull(oldIntentId, INTENT_ID_NULL);
232 checkNotNull(newIntent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800233 execute(IntentOperations.builder(newIntent.appId())
Ray Milkeye97ede92014-11-20 10:43:12 -0800234 .addReplaceOperation(oldIntentId, newIntent)
235 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700236 }
237
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700238 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700239 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800240 if (operations.operations().isEmpty()) {
241 return;
242 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700243 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700244 }
245
246 @Override
247 public Iterable<Intent> getIntents() {
248 return store.getIntents();
249 }
250
251 @Override
252 public long getIntentCount() {
253 return store.getIntentCount();
254 }
255
256 @Override
257 public Intent getIntent(IntentId id) {
258 checkNotNull(id, INTENT_ID_NULL);
259 return store.getIntent(id);
260 }
261
262 @Override
263 public IntentState getIntentState(IntentId id) {
264 checkNotNull(id, INTENT_ID_NULL);
265 return store.getIntentState(id);
266 }
267
268 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700269 public List<Intent> getInstallableIntents(IntentId intentId) {
270 checkNotNull(intentId, INTENT_ID_NULL);
271 return store.getInstallableIntents(intentId);
272 }
273
274 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700275 public void addListener(IntentListener listener) {
276 listenerRegistry.addListener(listener);
277 }
278
279 @Override
280 public void removeListener(IntentListener listener) {
281 listenerRegistry.removeListener(listener);
282 }
283
284 @Override
285 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
286 compilers.put(cls, compiler);
287 }
288
289 @Override
290 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
291 compilers.remove(cls);
292 }
293
294 @Override
295 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
296 return ImmutableMap.copyOf(compilers);
297 }
298
299 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700300 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700301 installers.put(cls, installer);
302 }
303
304 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700305 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700306 installers.remove(cls);
307 }
308
309 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700310 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700311 return ImmutableMap.copyOf(installers);
312 }
313
314 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700315 * Returns the corresponding intent compiler to the specified intent.
316 *
317 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700318 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700319 * @return intent compiler corresponding to the specified intent
320 */
321 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
322 @SuppressWarnings("unchecked")
323 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
324 if (compiler == null) {
325 throw new IntentException("no compiler for class " + intent.getClass());
326 }
327 return compiler;
328 }
329
330 /**
331 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700332 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700333 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700334 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700335 * @return intent installer corresponding to the specified installable intent
336 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700337 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700338 @SuppressWarnings("unchecked")
339 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
340 if (installer == null) {
341 throw new IntentException("no installer for class " + intent.getClass());
342 }
343 return installer;
344 }
345
346 /**
tom85258ee2014-10-07 00:10:02 -0700347 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700348 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700349 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700350 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700351 private void executeCompilingPhase(IntentUpdate update) {
352 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700353 // Indicate that the intent is entering the compiling phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800354 update.setInflightState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700355
356 try {
357 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700358 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700359
360 // If all went well, associate the resulting list of installable
361 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700362 update.setInstallables(installables);
Brian O'Connora44dda52014-12-01 17:42:41 -0800363 } catch (PathNotFoundException e) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800364 log.debug("Path not found for intent {}", intent);
Ray Milkey93508c22014-12-02 11:35:56 -0800365 update.setInflightState(intent, FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700366 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700367 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700368
tom85258ee2014-10-07 00:10:02 -0700369 // If compilation failed, mark the intent as failed.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800370 update.setInflightState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700371 }
372 }
373
Brian O'Connorcb900f42014-10-07 21:55:33 -0700374 /**
375 * Compiles an intent recursively.
376 *
377 * @param intent intent
378 * @return result of compilation
379 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700380 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700381 if (intent.isInstallable()) {
382 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700383 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700384
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700385 registerSubclassCompilerIfNeeded(intent);
386 List<Intent> previous = update.oldInstallables();
387 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700388 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700389 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
390 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700391 }
tom85258ee2014-10-07 00:10:02 -0700392 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700393 }
394
395 /**
tom85258ee2014-10-07 00:10:02 -0700396 * Installs all installable intents associated with the specified top-level
397 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700398 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700399 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700400 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700401 private void executeInstallingPhase(IntentUpdate update) {
402 if (update.newInstallables() == null) {
403 //no failed intents allowed past this point...
404 return;
tom85258ee2014-10-07 00:10:02 -0700405 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700406 // Indicate that the intent is entering the installing phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800407 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700408
409 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
410 for (Intent installable : update.newInstallables()) {
411 registerSubclassInstallerIfNeeded(installable);
412 trackerService.addTrackedResources(update.newIntent().id(),
413 installable.resources());
414 try {
415 batches.addAll(getInstaller(installable).install(installable));
Ray Milkey93508c22014-12-02 11:35:56 -0800416 } catch (Exception e) { // TODO this should be IntentException
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700417 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800418 trackerService.removeTrackedResources(update.newIntent().id(),
419 installable.resources());
Ray Milkey93508c22014-12-02 11:35:56 -0800420 //TODO we failed; intent should be recompiled
421 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700422 }
423 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800424 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700425 }
426
427 /**
428 * Uninstalls the specified intent by uninstalling all of its associated
429 * installable derivatives.
430 *
431 * @param update intent update
432 */
433 private void executeWithdrawingPhase(IntentUpdate update) {
434 if (!update.oldIntent().equals(update.newIntent())) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800435 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700436 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800437 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700438 }
439
440 /**
441 * Uninstalls all installable intents associated with the given intent.
442 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800443 * @param intent intent
444 * @param installables installable intents
445 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700446 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800447 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
448 if (installables == null) {
449 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700450 }
451 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800452 for (Intent installable : installables) {
453 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700454 installable.resources());
455 try {
456 batches.addAll(getInstaller(installable).uninstall(installable));
457 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800458 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700459 // TODO: this should never happen. but what if it does?
460 }
461 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800462 return batches;
tom85258ee2014-10-07 00:10:02 -0700463 }
464
465 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700466 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700467 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700468 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700469 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700470 private void executeReplacementPhase(IntentUpdate update) {
471 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
472 "Old and New Intent must have equivalent installable intents.");
473 if (!update.oldIntent().equals(update.newIntent())) {
474 // only set the old intent's state if it is different
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800475 update.setInflightState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700476 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800477 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700478
479 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
480 for (int i = 0; i < update.oldInstallables().size(); i++) {
481 Intent oldInstallable = update.oldInstallables().get(i);
482 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800483 //FIXME revisit this
484// if (oldInstallable.equals(newInstallable)) {
485// continue;
486// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700487 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
488 "Installable Intent type mismatch.");
489 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
490 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
491 try {
492 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
493 } catch (IntentException e) {
494 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
495 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800496 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800497 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connor427a1762014-11-19 18:40:32 -0800498 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700499 }
500 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800501 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700502 }
503
504 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700505 * Registers an intent compiler of the specified intent if an intent compiler
506 * for the intent is not registered. This method traverses the class hierarchy of
507 * the intent. Once an intent compiler for a parent type is found, this method
508 * registers the found intent compiler.
509 *
510 * @param intent intent
511 */
512 private void registerSubclassCompilerIfNeeded(Intent intent) {
513 if (!compilers.containsKey(intent.getClass())) {
514 Class<?> cls = intent.getClass();
515 while (cls != Object.class) {
516 // As long as we're within the Intent class descendants
517 if (Intent.class.isAssignableFrom(cls)) {
518 IntentCompiler<?> compiler = compilers.get(cls);
519 if (compiler != null) {
520 compilers.put(intent.getClass(), compiler);
521 return;
522 }
523 }
524 cls = cls.getSuperclass();
525 }
526 }
527 }
528
529 /**
530 * Registers an intent installer of the specified intent if an intent installer
531 * for the intent is not registered. This method traverses the class hierarchy of
532 * the intent. Once an intent installer for a parent type is found, this method
533 * registers the found intent installer.
534 *
535 * @param intent intent
536 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700537 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700538 if (!installers.containsKey(intent.getClass())) {
539 Class<?> cls = intent.getClass();
540 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700541 // As long as we're within the Intent class descendants
542 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700543 IntentInstaller<?> installer = installers.get(cls);
544 if (installer != null) {
545 installers.put(intent.getClass(), installer);
546 return;
547 }
548 }
549 cls = cls.getSuperclass();
550 }
551 }
552 }
553
Brian O'Connor66630c82014-10-02 21:08:19 -0700554 // Store delegate to re-post events emitted from the store.
555 private class InternalStoreDelegate implements IntentStoreDelegate {
556 @Override
557 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700558 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700559 }
560 }
561
Brian O'Connor72a034c2014-11-26 18:24:23 -0800562 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
563 boolean compileAllFailed) {
564 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
565 // Attempt recompilation of the specified intents first.
566 for (IntentId id : intentIds) {
567 Intent intent = store.getIntent(id);
568 if (intent == null) {
569 continue;
570 }
571 IntentOperations.Builder builder = batches.get(intent.appId());
572 if (builder == null) {
573 builder = IntentOperations.builder(intent.appId());
574 batches.put(intent.appId(), builder);
575 }
576 builder.addUpdateOperation(id);
577 }
578
579 if (compileAllFailed) {
580 // If required, compile all currently failed intents.
581 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800582 IntentState state = getIntentState(intent.id());
583 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800584 IntentOperations.Builder builder = batches.get(intent.appId());
585 if (builder == null) {
586 builder = IntentOperations.builder(intent.appId());
587 batches.put(intent.appId(), builder);
588 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800589 if (state == WITHDRAW_REQ) {
590 builder.addWithdrawOperation(intent.id());
591 } else {
592 builder.addUpdateOperation(intent.id());
593 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800594 }
595 }
596 }
597
598 for (ApplicationId appId : batches.keySet()) {
599 if (batchService.isLocalLeader(appId)) {
600 execute(batches.get(appId).build());
601 }
602 }
603 }
604
tom95329eb2014-10-06 08:40:06 -0700605 // Topology change delegate
606 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
607 @Override
tom85258ee2014-10-07 00:10:02 -0700608 public void triggerCompile(Iterable<IntentId> intentIds,
609 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800610 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700611 }
tom95329eb2014-10-06 08:40:06 -0700612 }
tom85258ee2014-10-07 00:10:02 -0700613
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800614 // TODO move this inside IntentUpdate?
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700615 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800616 * TODO. rename this...
617 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700618 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800619 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700620
Brian O'Connor427a1762014-11-19 18:40:32 -0800621 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700622 if (update.newIntent() != null) {
623 executeCompilingPhase(update);
624 }
625
626 if (update.oldInstallables() != null && update.newInstallables() != null) {
627 executeReplacementPhase(update);
628 } else if (update.newInstallables() != null) {
629 executeInstallingPhase(update);
630 } else if (update.oldInstallables() != null) {
631 executeWithdrawingPhase(update);
632 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800633 if (update.oldIntent() != null &&
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800634 !update.oldIntent().equals(update.newIntent())) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800635 // removing failed intent
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800636 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700637 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800638// if (update.newIntent() != null) {
639// // TODO assert that next state is failed
640// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700641 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700642 }
643
644 // TODO comments...
645 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700646 private final Intent oldIntent;
647 private final Intent newIntent;
648 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
649
650 private final List<Intent> oldInstallables;
651 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800652 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
653 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700654
655 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700656 switch (op.type()) {
657 case SUBMIT:
658 newIntent = op.intent();
659 oldIntent = null;
660 break;
661 case WITHDRAW:
662 newIntent = null;
663 oldIntent = store.getIntent(op.intentId());
664 break;
665 case REPLACE:
666 newIntent = op.intent();
667 oldIntent = store.getIntent(op.intentId());
668 break;
669 case UPDATE:
670 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800671 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700672 break;
673 default:
674 oldIntent = null;
675 newIntent = null;
676 break;
677 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700678 // fetch the old intent's installables from the store
679 if (oldIntent != null) {
680 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700681 } else {
682 oldInstallables = null;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800683 if (newIntent == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800684 log.info("Ignoring {} for missing Intent {}", op.type(), op.intentId());
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800685 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700686 }
687 }
688
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800689 void init(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800690 if (newIntent != null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800691 // TODO consider only "creating" intent if it does not exist
692 // Note: We need to set state to INSTALL_REQ regardless.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800693 batchWrite.createIntent(newIntent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800694 } else if (oldIntent != null && !oldIntent.equals(newIntent)) {
695 batchWrite.setState(oldIntent, WITHDRAW_REQ);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800696 }
697 }
698
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700699 Intent oldIntent() {
700 return oldIntent;
701 }
702
703 Intent newIntent() {
704 return newIntent;
705 }
706
707 List<Intent> oldInstallables() {
708 return oldInstallables;
709 }
710
711 List<Intent> newInstallables() {
712 return newInstallables;
713 }
714
715 void setInstallables(List<Intent> installables) {
716 newInstallables = installables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700717 }
718
Brian O'Connor427a1762014-11-19 18:40:32 -0800719 boolean isComplete() {
720 return currentBatch >= batches.size();
721 }
722
723 FlowRuleBatchOperation currentBatch() {
724 return !isComplete() ? batches.get(currentBatch) : null;
725 }
726
Ray Milkey93508c22014-12-02 11:35:56 -0800727 void batchSuccess() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800728 // move on to next Batch
Ray Milkey93508c22014-12-02 11:35:56 -0800729 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800730 }
731
732 void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800733 // the current batch has failed, so recompile
734 // remove the current batch and all remaining
735 for (int i = currentBatch; i < batches.size(); i++) {
736 batches.remove(i);
737 }
738 if (oldIntent != null) {
739 executeWithdrawingPhase(this); // remove the old intent
740 }
741 if (newIntent != null) {
742 setInflightState(newIntent, FAILED);
743 batches.addAll(uninstallIntent(newIntent, newInstallables()));
744 }
745
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800746 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800747 }
748
alshabiba9819bf2014-11-30 18:15:52 -0800749 private void finalizeStates(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800750 // events to be triggered on successful write
Brian O'Connor427a1762014-11-19 18:40:32 -0800751 for (Intent intent : stateMap.keySet()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800752 switch (getInflightState(intent)) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800753 case INSTALLING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800754 batchWrite.setState(intent, INSTALLED);
755 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Brian O'Connor427a1762014-11-19 18:40:32 -0800756 break;
757 case WITHDRAWING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800758 batchWrite.setState(intent, WITHDRAWN);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800759 batchWrite.removeInstalledIntents(intent.id());
760 batchWrite.removeIntent(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800761 break;
762 case FAILED:
Ray Milkey93508c22014-12-02 11:35:56 -0800763 batchWrite.setState(intent, FAILED);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800764 batchWrite.removeInstalledIntents(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800765 break;
766
767 // FALLTHROUGH to default from here
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800768 case INSTALL_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800769 case COMPILING:
770 case RECOMPILING:
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800771 case WITHDRAW_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800772 case WITHDRAWN:
773 case INSTALLED:
774 default:
775 //FIXME clean this up (we shouldn't ever get here)
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800776 log.warn("Bad state: {} for {}", getInflightState(intent), intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800777 break;
778 }
779 }
780 }
781
Brian O'Connor427a1762014-11-19 18:40:32 -0800782 void addBatches(List<FlowRuleBatchOperation> batches) {
783 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700784 }
785
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800786 IntentState getInflightState(Intent intent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700787 return stateMap.get(intent);
788 }
789
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800790 // set transient state during intent update process
791 void setInflightState(Intent intent, IntentState newState) {
792 // This method should be called for
793 // transition to non-parking or Failed only
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800794 if (!NON_PARKED_OR_FAILED.contains(newState)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800795 log.error("Unexpected transition to {}", newState);
796 }
797
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700798 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800799 log.debug("intent id: {}, old state: {}, new state: {}",
Ray Milkeye97ede92014-11-20 10:43:12 -0800800 intent.id(), oldState, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700801
802 stateMap.put(intent, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700803 }
804 }
805
Brian O'Connorcb900f42014-10-07 21:55:33 -0700806 private class IntentInstallMonitor implements Runnable {
807
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800808 // TODO make this configurable
Brian O'Connor427a1762014-11-19 18:40:32 -0800809 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700810
Brian O'Connor427a1762014-11-19 18:40:32 -0800811 private final IntentOperations ops;
812 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
813
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800814 // future holding current FlowRuleBatch installation result
Brian O'Connor427a1762014-11-19 18:40:32 -0800815 private Future<CompletedBatchOperation> future;
816 private long startTime = System.currentTimeMillis();
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800817 private long endTime;
Brian O'Connor427a1762014-11-19 18:40:32 -0800818 private int installAttempt;
819
820 public IntentInstallMonitor(IntentOperations ops) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700821 this.ops = ops;
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800822 resetTimeoutLimit();
823 }
824
825 private void resetTimeoutLimit() {
826 // FIXME compute reasonable timeouts
827 this.endTime = System.currentTimeMillis()
Pavlin Radoslavov589fe3f2015-02-25 14:41:04 -0800828 + ops.operations().size() * timeoutPerIntentOpMsec;
Brian O'Connor427a1762014-11-19 18:40:32 -0800829 }
830
831 private void buildIntentUpdates() {
Sho SHIMIZU30f45fb2014-12-03 18:13:26 -0800832 BatchWrite batchWrite = BatchWrite.newInstance();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800833
834 // create context and record new request to store
Brian O'Connor427a1762014-11-19 18:40:32 -0800835 for (IntentOperation op : ops.operations()) {
836 IntentUpdate update = new IntentUpdate(op);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800837 update.init(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800838 intentUpdates.add(update);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800839 }
840
841 if (!batchWrite.isEmpty()) {
842 store.batchWrite(batchWrite);
843 }
844
845 // start processing each Intents
846 for (IntentUpdate update : intentUpdates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800847 processIntentUpdate(update);
848 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700849 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700850 }
851
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700852 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800853 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700854 *
855 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700856 */
857 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800858 //TODO test this. (also, maybe save this batch)
859 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
860 for (IntentUpdate update : intentUpdates) {
861 if (!update.isComplete()) {
862 batch.addAll(update.currentBatch());
863 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700864 }
Ray Milkey93508c22014-12-02 11:35:56 -0800865 if (batch.size() > 0) {
866 //FIXME apply batch might throw an exception
867 return flowRuleService.applyBatch(batch);
868 } else {
869 // there are no flow rule batches; finalize the intent update
Sho SHIMIZU30f45fb2014-12-03 18:13:26 -0800870 BatchWrite batchWrite = BatchWrite.newInstance();
Brian O'Connor427a1762014-11-19 18:40:32 -0800871 for (IntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800872 update.finalizeStates(batchWrite);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800873 }
874 if (!batchWrite.isEmpty()) {
875 store.batchWrite(batchWrite);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700876 }
Ray Milkey93508c22014-12-02 11:35:56 -0800877 return null;
878 }
879 }
880
881 private void updateBatches(CompletedBatchOperation completed) {
882 if (completed.isSuccess()) {
883 for (IntentUpdate update : intentUpdates) {
884 update.batchSuccess();
885 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700886 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800887 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800888 log.debug("Failed items: {}", completed.failedItems());
889 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800890
891 for (Long id : completed.failedIds()) {
892 IntentId targetId = IntentId.valueOf(id);
893 for (IntentUpdate update : intentUpdates) {
894 List<Intent> installables = Lists.newArrayList(update.newInstallables());
Ray Milkey93508c22014-12-02 11:35:56 -0800895 if (update.oldInstallables() != null) {
896 installables.addAll(update.oldInstallables());
897 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800898 for (Intent intent : installables) {
899 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800900 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800901 break;
902 }
903 }
904 }
905 // don't increment the non-failed items, as they have been reverted.
906 }
907 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700908 }
909
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800910 private void abandonShip() {
911 // the batch has failed
912 // TODO: maybe we should do more?
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800913 log.error("Walk the plank, matey...");
Ray Milkey93508c22014-12-02 11:35:56 -0800914 future = null;
915 batchService.removeIntentOperations(ops);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800916 }
917
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700918 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700919 * Iterate through the pending futures, and remove them when they have completed.
920 */
921 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700922 if (future == null) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800923 // we are done if the future is null
924 return;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700925 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700926 try {
927 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800928 updateBatches(completed);
929 future = applyNextBatch();
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800930 } catch (TimeoutException | InterruptedException te) {
931 log.trace("Installation of intents are still pending: {}", ops);
932 } catch (ExecutionException e) {
933 log.warn("Execution of batch failed: {}", ops, e);
934 abandonShip();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700935 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700936 }
937
Brian O'Connor427a1762014-11-19 18:40:32 -0800938 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800939 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800940 if (future.cancel(true)) { // cancel success; batch is reverted
941 // reset the timer
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800942 resetTimeoutLimit();
Ray Milkey93508c22014-12-02 11:35:56 -0800943 installAttempt++;
944 if (installAttempt == MAX_ATTEMPTS) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800945 log.warn("Install request timed out: {}", ops);
946 for (IntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800947 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800948 }
Ray Milkey93508c22014-12-02 11:35:56 -0800949 } else if (installAttempt > MAX_ATTEMPTS) {
950 abandonShip();
951 return;
Brian O'Connor427a1762014-11-19 18:40:32 -0800952 } // else just resubmit the work
953 future = applyNextBatch();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800954 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800955 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800956 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800957 // FIXME
958 // cancel failed... batch is broken; shouldn't happen!
959 // we could manually reverse everything
960 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -0800961 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800962 }
963 }
964
965 boolean isComplete() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800966 return future == null;
967 }
968
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700969 @Override
970 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800971 try {
972 if (intentUpdates.isEmpty()) {
973 // this should only be called on the first iteration
974 // note: this a "expensive", so it is not done in the constructor
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800975
976 // - creates per Intent installation context (IntentUpdate)
977 // - write Intents to store
978 // - process (compile, install, etc.) each Intents
979 // - generate FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800980 buildIntentUpdates();
981 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800982
983 // - peek if current FlowRuleBatch is complete
984 // -- If complete OK:
985 // step each IntentUpdate forward
986 // If phase left: generate next FlowRuleBatch
987 // If no more phase: write parking states
988 // -- If complete FAIL:
989 // Intent which failed: transition Intent to FAILED
990 // Other Intents: resubmit same FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800991 processFutures();
992 if (isComplete()) {
993 // there are no outstanding batches; we are done
994 batchService.removeIntentOperations(ops);
995 } else if (endTime < System.currentTimeMillis()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800996 // - cancel current FlowRuleBatch and resubmit again
Brian O'Connor427a1762014-11-19 18:40:32 -0800997 retry();
998 } else {
999 // we are not done yet, yield the thread by resubmitting ourselves
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001000 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -08001001 }
1002 } catch (Exception e) {
1003 log.error("Error submitting batches:", e);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001004 // FIXME incomplete Intents should be cleaned up
1005 // (transition to FAILED, etc.)
Ray Milkey93508c22014-12-02 11:35:56 -08001006 abandonShip();
Brian O'Connorcb900f42014-10-07 21:55:33 -07001007 }
1008 }
1009 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001010
1011 private class InternalBatchDelegate implements IntentBatchDelegate {
1012 @Override
1013 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -08001014 log.info("Execute {} operation(s).", operations.operations().size());
1015 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001016 //FIXME: perhaps we want to track this task so that we can cancel it.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -08001017 executor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -07001018 }
1019
1020 @Override
1021 public void cancel(IntentOperations operations) {
1022 //FIXME: implement this
1023 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
1024 }
1025 }
Ray Milkeye97ede92014-11-20 10:43:12 -08001026
Brian O'Connor66630c82014-10-02 21:08:19 -07001027}