blob: 78ebbf361379c4f7d49ea23f516eb17ea2148c0f [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;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.apache.felix.scr.annotations.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.core.IdGenerator;
31import org.onosproject.event.AbstractListenerRegistry;
32import org.onosproject.event.EventDeliveryService;
33import org.onosproject.net.flow.CompletedBatchOperation;
34import org.onosproject.net.flow.FlowRuleBatchOperation;
35import org.onosproject.net.flow.FlowRuleService;
36import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentBatchDelegate;
38import org.onosproject.net.intent.IntentBatchService;
39import org.onosproject.net.intent.IntentCompiler;
40import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentException;
42import org.onosproject.net.intent.IntentExtensionService;
43import org.onosproject.net.intent.IntentId;
44import org.onosproject.net.intent.IntentInstaller;
45import org.onosproject.net.intent.IntentListener;
46import org.onosproject.net.intent.IntentOperation;
47import org.onosproject.net.intent.IntentOperations;
48import org.onosproject.net.intent.IntentService;
49import org.onosproject.net.intent.IntentState;
50import org.onosproject.net.intent.IntentStore;
Sho SHIMIZU64ae11c2014-12-03 15:17:47 -080051import org.onosproject.net.intent.BatchWrite;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.IntentStoreDelegate;
Brian O'Connor66630c82014-10-02 21:08:19 -070053import org.slf4j.Logger;
54
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -080055import java.time.Duration;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080056import java.util.ArrayList;
57import java.util.Collections;
58import java.util.EnumSet;
59import java.util.List;
60import java.util.Map;
61import java.util.concurrent.ConcurrentHashMap;
62import java.util.concurrent.ConcurrentMap;
63import java.util.concurrent.ExecutionException;
64import java.util.concurrent.ExecutorService;
65import java.util.concurrent.Future;
66import java.util.concurrent.TimeUnit;
67import java.util.concurrent.TimeoutException;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070068
69import static com.google.common.base.Preconditions.checkArgument;
70import static com.google.common.base.Preconditions.checkNotNull;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080071import static java.util.concurrent.Executors.newFixedThreadPool;
Brian O'Connorabafb502014-12-02 22:26:20 -080072import static org.onosproject.net.intent.IntentState.*;
Brian O'Connorfa81eae2014-10-30 13:20:05 -070073import static org.onlab.util.Tools.namedThreads;
74import static org.slf4j.LoggerFactory.getLogger;
Brian O'Connor66630c82014-10-02 21:08:19 -070075
76/**
77 * An implementation of Intent Manager.
78 */
79@Component(immediate = true)
80@Service
81public class IntentManager
82 implements IntentService, IntentExtensionService {
Sho SHIMIZU8b5051d2014-11-05 11:24:13 -080083 private static final Logger log = getLogger(IntentManager.class);
Brian O'Connor66630c82014-10-02 21:08:19 -070084
85 public static final String INTENT_NULL = "Intent cannot be null";
86 public static final String INTENT_ID_NULL = "Intent ID cannot be null";
87
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -080088 private static final int NUM_THREADS = 12;
89
Brian O'Connor7a71d5d2014-12-02 00:12:27 -080090 private static final EnumSet<IntentState> RECOMPILE
91 = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
92 private static final EnumSet<IntentState> NON_PARKED_OR_FAILED
93 = EnumSet.complementOf(EnumSet.of(INSTALL_REQ, INSTALLED, WITHDRAW_REQ, WITHDRAWN));
94
95
Brian O'Connor66630c82014-10-02 21:08:19 -070096 // Collections for compiler, installer, and listener are ONOS instance local
97 private final ConcurrentMap<Class<? extends Intent>,
98 IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
Thomas Vachuskab97cf282014-10-20 23:31:12 -070099 private final ConcurrentMap<Class<? extends Intent>,
100 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700101
102 private final AbstractListenerRegistry<IntentEvent, IntentListener>
tom95329eb2014-10-06 08:40:06 -0700103 listenerRegistry = new AbstractListenerRegistry<>();
Brian O'Connor66630c82014-10-02 21:08:19 -0700104
Brian O'Connor520c0522014-11-23 23:50:47 -0800105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected CoreService coreService;
Brian O'Connor66630c82014-10-02 21:08:19 -0700107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected IntentStore store;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700112 protected IntentBatchService batchService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
tom85258ee2014-10-07 00:10:02 -0700115 protected ObjectiveTrackerService trackerService;
tom95329eb2014-10-06 08:40:06 -0700116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Brian O'Connor66630c82014-10-02 21:08:19 -0700118 protected EventDeliveryService eventDispatcher;
119
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected FlowRuleService flowRuleService;
122
Brian O'Connor520c0522014-11-23 23:50:47 -0800123
124 private ExecutorService executor;
Brian O'Connor520c0522014-11-23 23:50:47 -0800125
126 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
127 private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
128 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
129 private IdGenerator idGenerator;
130
Brian O'Connor66630c82014-10-02 21:08:19 -0700131 @Activate
132 public void activate() {
133 store.setDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700134 trackerService.setDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700135 batchService.setDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700136 eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
Yuta HIGUCHI7a9fddd2014-12-03 15:30:25 -0800137 executor = newFixedThreadPool(NUM_THREADS, namedThreads("onos-intent-%d"));
Brian O'Connor520c0522014-11-23 23:50:47 -0800138 idGenerator = coreService.getIdGenerator("intent-ids");
139 Intent.bindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700140 log.info("Started");
141 }
142
143 @Deactivate
144 public void deactivate() {
145 store.unsetDelegate(delegate);
tom95329eb2014-10-06 08:40:06 -0700146 trackerService.unsetDelegate(topoDelegate);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700147 batchService.unsetDelegate(batchDelegate);
Brian O'Connor66630c82014-10-02 21:08:19 -0700148 eventDispatcher.removeSink(IntentEvent.class);
Brian O'Connorcb900f42014-10-07 21:55:33 -0700149 executor.shutdown();
Brian O'Connor520c0522014-11-23 23:50:47 -0800150 Intent.unbindIdGenerator(idGenerator);
Brian O'Connor66630c82014-10-02 21:08:19 -0700151 log.info("Stopped");
152 }
153
154 @Override
155 public void submit(Intent intent) {
156 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800157 execute(IntentOperations.builder(intent.appId())
158 .addSubmitOperation(intent).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700159 }
160
161 @Override
162 public void withdraw(Intent intent) {
163 checkNotNull(intent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800164 execute(IntentOperations.builder(intent.appId())
165 .addWithdrawOperation(intent.id()).build());
Brian O'Connor66630c82014-10-02 21:08:19 -0700166 }
167
Brian O'Connor66630c82014-10-02 21:08:19 -0700168 @Override
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700169 public void replace(IntentId oldIntentId, Intent newIntent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700170 checkNotNull(oldIntentId, INTENT_ID_NULL);
171 checkNotNull(newIntent, INTENT_NULL);
Brian O'Connor72a034c2014-11-26 18:24:23 -0800172 execute(IntentOperations.builder(newIntent.appId())
Ray Milkeye97ede92014-11-20 10:43:12 -0800173 .addReplaceOperation(oldIntentId, newIntent)
174 .build());
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700175 }
176
Thomas Vachuska83e090e2014-10-22 14:25:35 -0700177 @Override
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700178 public void execute(IntentOperations operations) {
Brian O'Connore2ff25a2014-11-18 19:25:43 -0800179 if (operations.operations().isEmpty()) {
180 return;
181 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700182 batchService.addIntentOperations(operations);
Brian O'Connor66630c82014-10-02 21:08:19 -0700183 }
184
185 @Override
186 public Iterable<Intent> getIntents() {
187 return store.getIntents();
188 }
189
190 @Override
191 public long getIntentCount() {
192 return store.getIntentCount();
193 }
194
195 @Override
196 public Intent getIntent(IntentId id) {
197 checkNotNull(id, INTENT_ID_NULL);
198 return store.getIntent(id);
199 }
200
201 @Override
202 public IntentState getIntentState(IntentId id) {
203 checkNotNull(id, INTENT_ID_NULL);
204 return store.getIntentState(id);
205 }
206
207 @Override
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700208 public List<Intent> getInstallableIntents(IntentId intentId) {
209 checkNotNull(intentId, INTENT_ID_NULL);
210 return store.getInstallableIntents(intentId);
211 }
212
213 @Override
Brian O'Connor66630c82014-10-02 21:08:19 -0700214 public void addListener(IntentListener listener) {
215 listenerRegistry.addListener(listener);
216 }
217
218 @Override
219 public void removeListener(IntentListener listener) {
220 listenerRegistry.removeListener(listener);
221 }
222
223 @Override
224 public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
225 compilers.put(cls, compiler);
226 }
227
228 @Override
229 public <T extends Intent> void unregisterCompiler(Class<T> cls) {
230 compilers.remove(cls);
231 }
232
233 @Override
234 public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
235 return ImmutableMap.copyOf(compilers);
236 }
237
238 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700239 public <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700240 installers.put(cls, installer);
241 }
242
243 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700244 public <T extends Intent> void unregisterInstaller(Class<T> cls) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700245 installers.remove(cls);
246 }
247
248 @Override
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700249 public Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
Brian O'Connor66630c82014-10-02 21:08:19 -0700250 return ImmutableMap.copyOf(installers);
251 }
252
253 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700254 * Returns the corresponding intent compiler to the specified intent.
255 *
256 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700257 * @param <T> the type of intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700258 * @return intent compiler corresponding to the specified intent
259 */
260 private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
261 @SuppressWarnings("unchecked")
262 IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
263 if (compiler == null) {
264 throw new IntentException("no compiler for class " + intent.getClass());
265 }
266 return compiler;
267 }
268
269 /**
270 * Returns the corresponding intent installer to the specified installable intent.
tom95329eb2014-10-06 08:40:06 -0700271 *
Brian O'Connor66630c82014-10-02 21:08:19 -0700272 * @param intent intent
tom95329eb2014-10-06 08:40:06 -0700273 * @param <T> the type of installable intent
Brian O'Connor66630c82014-10-02 21:08:19 -0700274 * @return intent installer corresponding to the specified installable intent
275 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700276 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700277 @SuppressWarnings("unchecked")
278 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
279 if (installer == null) {
280 throw new IntentException("no installer for class " + intent.getClass());
281 }
282 return installer;
283 }
284
285 /**
tom85258ee2014-10-07 00:10:02 -0700286 * Compiles the specified intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700287 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700288 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700289 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700290 private void executeCompilingPhase(IntentUpdate update) {
291 Intent intent = update.newIntent();
tom85258ee2014-10-07 00:10:02 -0700292 // Indicate that the intent is entering the compiling phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800293 update.setInflightState(intent, COMPILING);
tom85258ee2014-10-07 00:10:02 -0700294
295 try {
296 // Compile the intent into installable derivatives.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700297 List<Intent> installables = compileIntent(intent, update);
tom85258ee2014-10-07 00:10:02 -0700298
299 // If all went well, associate the resulting list of installable
300 // intents with the top-level intent and proceed to install.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700301 update.setInstallables(installables);
Brian O'Connora44dda52014-12-01 17:42:41 -0800302 } catch (PathNotFoundException e) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800303 log.debug("Path not found for intent {}", intent);
Ray Milkey93508c22014-12-02 11:35:56 -0800304 update.setInflightState(intent, FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700305 } catch (IntentException e) {
Jonathan Hart11096402014-10-20 17:31:49 -0700306 log.warn("Unable to compile intent {} due to:", intent.id(), e);
tom53945d52014-10-07 11:01:36 -0700307
tom85258ee2014-10-07 00:10:02 -0700308 // If compilation failed, mark the intent as failed.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800309 update.setInflightState(intent, FAILED);
tom85258ee2014-10-07 00:10:02 -0700310 }
311 }
312
Brian O'Connorcb900f42014-10-07 21:55:33 -0700313 /**
314 * Compiles an intent recursively.
315 *
316 * @param intent intent
317 * @return result of compilation
318 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700319 private List<Intent> compileIntent(Intent intent, IntentUpdate update) {
Thomas Vachuska4926c1b2014-10-21 00:44:10 -0700320 if (intent.isInstallable()) {
321 return ImmutableList.of(intent);
Brian O'Connor66630c82014-10-02 21:08:19 -0700322 }
Brian O'Connorcb900f42014-10-07 21:55:33 -0700323
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700324 registerSubclassCompilerIfNeeded(intent);
325 List<Intent> previous = update.oldInstallables();
326 // FIXME: get previous resources
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700327 List<Intent> installable = new ArrayList<>();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700328 for (Intent compiled : getCompiler(intent).compile(intent, previous, null)) {
329 installable.addAll(compileIntent(compiled, update));
Brian O'Connorcb900f42014-10-07 21:55:33 -0700330 }
tom85258ee2014-10-07 00:10:02 -0700331 return installable;
Brian O'Connor66630c82014-10-02 21:08:19 -0700332 }
333
334 /**
tom85258ee2014-10-07 00:10:02 -0700335 * Installs all installable intents associated with the specified top-level
336 * intent.
Brian O'Connor66630c82014-10-02 21:08:19 -0700337 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700338 * @param update intent update
Brian O'Connor66630c82014-10-02 21:08:19 -0700339 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700340 private void executeInstallingPhase(IntentUpdate update) {
341 if (update.newInstallables() == null) {
342 //no failed intents allowed past this point...
343 return;
tom85258ee2014-10-07 00:10:02 -0700344 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700345 // Indicate that the intent is entering the installing phase.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800346 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700347
348 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
349 for (Intent installable : update.newInstallables()) {
350 registerSubclassInstallerIfNeeded(installable);
351 trackerService.addTrackedResources(update.newIntent().id(),
352 installable.resources());
353 try {
354 batches.addAll(getInstaller(installable).install(installable));
Ray Milkey93508c22014-12-02 11:35:56 -0800355 } catch (Exception e) { // TODO this should be IntentException
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700356 log.warn("Unable to install intent {} due to:", update.newIntent().id(), e);
Brian O'Connor427a1762014-11-19 18:40:32 -0800357 trackerService.removeTrackedResources(update.newIntent().id(),
358 installable.resources());
Ray Milkey93508c22014-12-02 11:35:56 -0800359 //TODO we failed; intent should be recompiled
360 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700361 }
362 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800363 update.addBatches(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700364 }
365
366 /**
367 * Uninstalls the specified intent by uninstalling all of its associated
368 * installable derivatives.
369 *
370 * @param update intent update
371 */
372 private void executeWithdrawingPhase(IntentUpdate update) {
373 if (!update.oldIntent().equals(update.newIntent())) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800374 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700375 } // else newIntent is FAILED
Brian O'Connor427a1762014-11-19 18:40:32 -0800376 update.addBatches(uninstallIntent(update.oldIntent(), update.oldInstallables()));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700377 }
378
379 /**
380 * Uninstalls all installable intents associated with the given intent.
381 *
Brian O'Connor427a1762014-11-19 18:40:32 -0800382 * @param intent intent
383 * @param installables installable intents
384 * @return list of batches to uninstall intent
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700385 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800386 private List<FlowRuleBatchOperation> uninstallIntent(Intent intent, List<Intent> installables) {
387 if (installables == null) {
388 return Collections.emptyList();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700389 }
390 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
Brian O'Connor427a1762014-11-19 18:40:32 -0800391 for (Intent installable : installables) {
392 trackerService.removeTrackedResources(intent.id(),
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700393 installable.resources());
394 try {
395 batches.addAll(getInstaller(installable).uninstall(installable));
396 } catch (IntentException e) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800397 log.warn("Unable to uninstall intent {} due to:", intent.id(), e);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700398 // TODO: this should never happen. but what if it does?
399 }
400 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800401 return batches;
tom85258ee2014-10-07 00:10:02 -0700402 }
403
404 /**
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700405 * Withdraws the old intent and installs the new intent as one operation.
tom85258ee2014-10-07 00:10:02 -0700406 *
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700407 * @param update intent update
tom85258ee2014-10-07 00:10:02 -0700408 */
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700409 private void executeReplacementPhase(IntentUpdate update) {
410 checkArgument(update.oldInstallables().size() == update.newInstallables().size(),
411 "Old and New Intent must have equivalent installable intents.");
412 if (!update.oldIntent().equals(update.newIntent())) {
413 // only set the old intent's state if it is different
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800414 update.setInflightState(update.oldIntent(), WITHDRAWING);
tom53945d52014-10-07 11:01:36 -0700415 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800416 update.setInflightState(update.newIntent(), INSTALLING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700417
418 List<FlowRuleBatchOperation> batches = Lists.newArrayList();
419 for (int i = 0; i < update.oldInstallables().size(); i++) {
420 Intent oldInstallable = update.oldInstallables().get(i);
421 Intent newInstallable = update.newInstallables().get(i);
Brian O'Connor427a1762014-11-19 18:40:32 -0800422 //FIXME revisit this
423// if (oldInstallable.equals(newInstallable)) {
424// continue;
425// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700426 checkArgument(oldInstallable.getClass().equals(newInstallable.getClass()),
427 "Installable Intent type mismatch.");
428 trackerService.removeTrackedResources(update.oldIntent().id(), oldInstallable.resources());
429 trackerService.addTrackedResources(update.newIntent().id(), newInstallable.resources());
430 try {
431 batches.addAll(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
432 } catch (IntentException e) {
433 log.warn("Unable to update intent {} due to:", update.oldIntent().id(), e);
434 //FIXME... we failed. need to uninstall (if same) or revert (if different)
Brian O'Connor427a1762014-11-19 18:40:32 -0800435 trackerService.removeTrackedResources(update.newIntent().id(), newInstallable.resources());
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800436 update.setInflightState(update.newIntent(), FAILED);
Brian O'Connor427a1762014-11-19 18:40:32 -0800437 batches = uninstallIntent(update.oldIntent(), update.oldInstallables());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700438 }
439 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800440 update.addBatches(batches);
tom53945d52014-10-07 11:01:36 -0700441 }
442
443 /**
Brian O'Connor66630c82014-10-02 21:08:19 -0700444 * Registers an intent compiler of the specified intent if an intent compiler
445 * for the intent is not registered. This method traverses the class hierarchy of
446 * the intent. Once an intent compiler for a parent type is found, this method
447 * registers the found intent compiler.
448 *
449 * @param intent intent
450 */
451 private void registerSubclassCompilerIfNeeded(Intent intent) {
452 if (!compilers.containsKey(intent.getClass())) {
453 Class<?> cls = intent.getClass();
454 while (cls != Object.class) {
455 // As long as we're within the Intent class descendants
456 if (Intent.class.isAssignableFrom(cls)) {
457 IntentCompiler<?> compiler = compilers.get(cls);
458 if (compiler != null) {
459 compilers.put(intent.getClass(), compiler);
460 return;
461 }
462 }
463 cls = cls.getSuperclass();
464 }
465 }
466 }
467
468 /**
469 * Registers an intent installer of the specified intent if an intent installer
470 * for the intent is not registered. This method traverses the class hierarchy of
471 * the intent. Once an intent installer for a parent type is found, this method
472 * registers the found intent installer.
473 *
474 * @param intent intent
475 */
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700476 private void registerSubclassInstallerIfNeeded(Intent intent) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700477 if (!installers.containsKey(intent.getClass())) {
478 Class<?> cls = intent.getClass();
479 while (cls != Object.class) {
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700480 // As long as we're within the Intent class descendants
481 if (Intent.class.isAssignableFrom(cls)) {
Brian O'Connor66630c82014-10-02 21:08:19 -0700482 IntentInstaller<?> installer = installers.get(cls);
483 if (installer != null) {
484 installers.put(intent.getClass(), installer);
485 return;
486 }
487 }
488 cls = cls.getSuperclass();
489 }
490 }
491 }
492
Brian O'Connor66630c82014-10-02 21:08:19 -0700493 // Store delegate to re-post events emitted from the store.
494 private class InternalStoreDelegate implements IntentStoreDelegate {
495 @Override
496 public void notify(IntentEvent event) {
tom85258ee2014-10-07 00:10:02 -0700497 eventDispatcher.post(event);
Brian O'Connor66630c82014-10-02 21:08:19 -0700498 }
499 }
500
Brian O'Connor72a034c2014-11-26 18:24:23 -0800501 private void buildAndSubmitBatches(Iterable<IntentId> intentIds,
502 boolean compileAllFailed) {
503 Map<ApplicationId, IntentOperations.Builder> batches = Maps.newHashMap();
504 // Attempt recompilation of the specified intents first.
505 for (IntentId id : intentIds) {
506 Intent intent = store.getIntent(id);
507 if (intent == null) {
508 continue;
509 }
510 IntentOperations.Builder builder = batches.get(intent.appId());
511 if (builder == null) {
512 builder = IntentOperations.builder(intent.appId());
513 batches.put(intent.appId(), builder);
514 }
515 builder.addUpdateOperation(id);
516 }
517
518 if (compileAllFailed) {
519 // If required, compile all currently failed intents.
520 for (Intent intent : getIntents()) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800521 IntentState state = getIntentState(intent.id());
522 if (RECOMPILE.contains(state)) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800523 IntentOperations.Builder builder = batches.get(intent.appId());
524 if (builder == null) {
525 builder = IntentOperations.builder(intent.appId());
526 batches.put(intent.appId(), builder);
527 }
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800528 if (state == WITHDRAW_REQ) {
529 builder.addWithdrawOperation(intent.id());
530 } else {
531 builder.addUpdateOperation(intent.id());
532 }
Brian O'Connor72a034c2014-11-26 18:24:23 -0800533 }
534 }
535 }
536
537 for (ApplicationId appId : batches.keySet()) {
538 if (batchService.isLocalLeader(appId)) {
539 execute(batches.get(appId).build());
540 }
541 }
542 }
543
tom95329eb2014-10-06 08:40:06 -0700544 // Topology change delegate
545 private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
546 @Override
tom85258ee2014-10-07 00:10:02 -0700547 public void triggerCompile(Iterable<IntentId> intentIds,
548 boolean compileAllFailed) {
Brian O'Connor72a034c2014-11-26 18:24:23 -0800549 buildAndSubmitBatches(intentIds, compileAllFailed);
tom95329eb2014-10-06 08:40:06 -0700550 }
tom95329eb2014-10-06 08:40:06 -0700551 }
tom85258ee2014-10-07 00:10:02 -0700552
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800553 // TODO move this inside IntentUpdate?
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700554 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800555 * TODO. rename this...
556 * @param update intent update
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700557 */
Brian O'Connor427a1762014-11-19 18:40:32 -0800558 private void processIntentUpdate(IntentUpdate update) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700559
Brian O'Connor427a1762014-11-19 18:40:32 -0800560 // check to see if the intent needs to be compiled or recompiled
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700561 if (update.newIntent() != null) {
562 executeCompilingPhase(update);
563 }
564
565 if (update.oldInstallables() != null && update.newInstallables() != null) {
566 executeReplacementPhase(update);
567 } else if (update.newInstallables() != null) {
568 executeInstallingPhase(update);
569 } else if (update.oldInstallables() != null) {
570 executeWithdrawingPhase(update);
571 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800572 if (update.oldIntent() != null &&
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800573 !update.oldIntent().equals(update.newIntent())) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800574 // removing failed intent
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800575 update.setInflightState(update.oldIntent(), WITHDRAWING);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700576 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800577// if (update.newIntent() != null) {
578// // TODO assert that next state is failed
579// }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700580 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700581 }
582
583 // TODO comments...
584 private class IntentUpdate {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700585 private final Intent oldIntent;
586 private final Intent newIntent;
587 private final Map<Intent, IntentState> stateMap = Maps.newHashMap();
588
589 private final List<Intent> oldInstallables;
590 private List<Intent> newInstallables;
Brian O'Connor427a1762014-11-19 18:40:32 -0800591 private final List<FlowRuleBatchOperation> batches = Lists.newLinkedList();
592 private int currentBatch = 0; // TODO: maybe replace with an iterator
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700593
594 IntentUpdate(IntentOperation op) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700595 switch (op.type()) {
596 case SUBMIT:
597 newIntent = op.intent();
598 oldIntent = null;
599 break;
600 case WITHDRAW:
601 newIntent = null;
602 oldIntent = store.getIntent(op.intentId());
603 break;
604 case REPLACE:
605 newIntent = op.intent();
606 oldIntent = store.getIntent(op.intentId());
607 break;
608 case UPDATE:
609 oldIntent = store.getIntent(op.intentId());
Brian O'Connor427a1762014-11-19 18:40:32 -0800610 newIntent = oldIntent;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700611 break;
612 default:
613 oldIntent = null;
614 newIntent = null;
615 break;
616 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700617 // fetch the old intent's installables from the store
618 if (oldIntent != null) {
619 oldInstallables = store.getInstallableIntents(oldIntent.id());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700620 } else {
621 oldInstallables = null;
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800622 if (newIntent == null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800623 log.info("Ignoring {} for missing Intent {}", op.type(), op.intentId());
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800624 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700625 }
626 }
627
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800628 void init(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800629 if (newIntent != null) {
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800630 // TODO consider only "creating" intent if it does not exist
631 // Note: We need to set state to INSTALL_REQ regardless.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800632 batchWrite.createIntent(newIntent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800633 } else if (oldIntent != null && !oldIntent.equals(newIntent)) {
634 batchWrite.setState(oldIntent, WITHDRAW_REQ);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800635 }
636 }
637
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700638 Intent oldIntent() {
639 return oldIntent;
640 }
641
642 Intent newIntent() {
643 return newIntent;
644 }
645
646 List<Intent> oldInstallables() {
647 return oldInstallables;
648 }
649
650 List<Intent> newInstallables() {
651 return newInstallables;
652 }
653
654 void setInstallables(List<Intent> installables) {
655 newInstallables = installables;
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700656 }
657
Brian O'Connor427a1762014-11-19 18:40:32 -0800658 boolean isComplete() {
659 return currentBatch >= batches.size();
660 }
661
662 FlowRuleBatchOperation currentBatch() {
663 return !isComplete() ? batches.get(currentBatch) : null;
664 }
665
Ray Milkey93508c22014-12-02 11:35:56 -0800666 void batchSuccess() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800667 // move on to next Batch
Ray Milkey93508c22014-12-02 11:35:56 -0800668 currentBatch++;
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800669 }
670
671 void batchFailed() {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800672 // the current batch has failed, so recompile
673 // remove the current batch and all remaining
674 for (int i = currentBatch; i < batches.size(); i++) {
675 batches.remove(i);
676 }
677 if (oldIntent != null) {
678 executeWithdrawingPhase(this); // remove the old intent
679 }
680 if (newIntent != null) {
681 setInflightState(newIntent, FAILED);
682 batches.addAll(uninstallIntent(newIntent, newInstallables()));
683 }
684
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800685 // TODO we might want to try to recompile the new intent
Brian O'Connor427a1762014-11-19 18:40:32 -0800686 }
687
alshabiba9819bf2014-11-30 18:15:52 -0800688 private void finalizeStates(BatchWrite batchWrite) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800689 // events to be triggered on successful write
Brian O'Connor427a1762014-11-19 18:40:32 -0800690 for (Intent intent : stateMap.keySet()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800691 switch (getInflightState(intent)) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800692 case INSTALLING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800693 batchWrite.setState(intent, INSTALLED);
694 batchWrite.setInstallableIntents(newIntent.id(), newInstallables);
Brian O'Connor427a1762014-11-19 18:40:32 -0800695 break;
696 case WITHDRAWING:
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800697 batchWrite.setState(intent, WITHDRAWN);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800698 batchWrite.removeInstalledIntents(intent.id());
699 batchWrite.removeIntent(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800700 break;
701 case FAILED:
Ray Milkey93508c22014-12-02 11:35:56 -0800702 batchWrite.setState(intent, FAILED);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800703 batchWrite.removeInstalledIntents(intent.id());
Brian O'Connor427a1762014-11-19 18:40:32 -0800704 break;
705
706 // FALLTHROUGH to default from here
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800707 case INSTALL_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800708 case COMPILING:
709 case RECOMPILING:
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800710 case WITHDRAW_REQ:
Brian O'Connor427a1762014-11-19 18:40:32 -0800711 case WITHDRAWN:
712 case INSTALLED:
713 default:
714 //FIXME clean this up (we shouldn't ever get here)
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800715 log.warn("Bad state: {} for {}", getInflightState(intent), intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800716 break;
717 }
718 }
719 }
720
Brian O'Connor427a1762014-11-19 18:40:32 -0800721 void addBatches(List<FlowRuleBatchOperation> batches) {
722 this.batches.addAll(batches);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700723 }
724
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800725 IntentState getInflightState(Intent intent) {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700726 return stateMap.get(intent);
727 }
728
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800729 // set transient state during intent update process
730 void setInflightState(Intent intent, IntentState newState) {
731 // This method should be called for
732 // transition to non-parking or Failed only
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800733 if (!NON_PARKED_OR_FAILED.contains(newState)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800734 log.error("Unexpected transition to {}", newState);
735 }
736
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700737 IntentState oldState = stateMap.get(intent);
Brian O'Connor427a1762014-11-19 18:40:32 -0800738 log.debug("intent id: {}, old state: {}, new state: {}",
Ray Milkeye97ede92014-11-20 10:43:12 -0800739 intent.id(), oldState, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700740
741 stateMap.put(intent, newState);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700742 }
743 }
744
Brian O'Connorcb900f42014-10-07 21:55:33 -0700745 private class IntentInstallMonitor implements Runnable {
746
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800747 // TODO make this configurable through a configuration file using @Property mechanism
748 // These fields needs to be moved to the enclosing class and configurable through a configuration file
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800749 private static final int TIMEOUT_PER_OP = 500; // ms
Brian O'Connor427a1762014-11-19 18:40:32 -0800750 private static final int MAX_ATTEMPTS = 3;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700751
Brian O'Connor427a1762014-11-19 18:40:32 -0800752 private final IntentOperations ops;
753 private final List<IntentUpdate> intentUpdates = Lists.newArrayList();
754
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800755 private final Duration timeoutPerOperation;
756 private final int maxAttempts;
757
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800758 // future holding current FlowRuleBatch installation result
Brian O'Connor427a1762014-11-19 18:40:32 -0800759 private Future<CompletedBatchOperation> future;
760 private long startTime = System.currentTimeMillis();
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800761 private long endTime;
Brian O'Connor427a1762014-11-19 18:40:32 -0800762 private int installAttempt;
763
764 public IntentInstallMonitor(IntentOperations ops) {
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800765 this(ops, Duration.ofMillis(TIMEOUT_PER_OP), MAX_ATTEMPTS);
766 }
767
768 public IntentInstallMonitor(IntentOperations ops, Duration timeoutPerOperation, int maxAttempts) {
Sho SHIMIZUfcf91622014-12-11 12:51:24 -0800769 this.ops = checkNotNull(ops);
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800770 this.timeoutPerOperation = checkNotNull(timeoutPerOperation);
771 checkArgument(maxAttempts > 0, "maxAttempts must be larger than 0, but %s", maxAttempts);
772 this.maxAttempts = maxAttempts;
773
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800774 resetTimeoutLimit();
775 }
776
777 private void resetTimeoutLimit() {
778 // FIXME compute reasonable timeouts
779 this.endTime = System.currentTimeMillis()
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800780 + ops.operations().size() * timeoutPerOperation.toMillis();
Brian O'Connor427a1762014-11-19 18:40:32 -0800781 }
782
783 private void buildIntentUpdates() {
Sho SHIMIZU30f45fb2014-12-03 18:13:26 -0800784 BatchWrite batchWrite = BatchWrite.newInstance();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800785
786 // create context and record new request to store
Brian O'Connor427a1762014-11-19 18:40:32 -0800787 for (IntentOperation op : ops.operations()) {
788 IntentUpdate update = new IntentUpdate(op);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800789 update.init(batchWrite);
Brian O'Connor427a1762014-11-19 18:40:32 -0800790 intentUpdates.add(update);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800791 }
792
793 if (!batchWrite.isEmpty()) {
794 store.batchWrite(batchWrite);
795 }
796
797 // start processing each Intents
798 for (IntentUpdate update : intentUpdates) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800799 processIntentUpdate(update);
800 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700801 future = applyNextBatch();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700802 }
803
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700804 /**
Brian O'Connor427a1762014-11-19 18:40:32 -0800805 * Builds and applies the next batch, and returns the future.
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700806 *
807 * @return Future for next batch
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700808 */
809 private Future<CompletedBatchOperation> applyNextBatch() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800810 //TODO test this. (also, maybe save this batch)
811 FlowRuleBatchOperation batch = new FlowRuleBatchOperation(Collections.emptyList());
812 for (IntentUpdate update : intentUpdates) {
813 if (!update.isComplete()) {
814 batch.addAll(update.currentBatch());
815 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700816 }
Ray Milkey93508c22014-12-02 11:35:56 -0800817 if (batch.size() > 0) {
818 //FIXME apply batch might throw an exception
819 return flowRuleService.applyBatch(batch);
820 } else {
821 // there are no flow rule batches; finalize the intent update
Sho SHIMIZU30f45fb2014-12-03 18:13:26 -0800822 BatchWrite batchWrite = BatchWrite.newInstance();
Brian O'Connor427a1762014-11-19 18:40:32 -0800823 for (IntentUpdate update : intentUpdates) {
Ray Milkey93508c22014-12-02 11:35:56 -0800824 update.finalizeStates(batchWrite);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800825 }
826 if (!batchWrite.isEmpty()) {
827 store.batchWrite(batchWrite);
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700828 }
Ray Milkey93508c22014-12-02 11:35:56 -0800829 return null;
830 }
831 }
832
833 private void updateBatches(CompletedBatchOperation completed) {
834 if (completed.isSuccess()) {
835 for (IntentUpdate update : intentUpdates) {
836 update.batchSuccess();
837 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700838 } else {
Brian O'Connor427a1762014-11-19 18:40:32 -0800839 // entire batch has been reverted...
Ray Milkey93508c22014-12-02 11:35:56 -0800840 log.debug("Failed items: {}", completed.failedItems());
841 log.debug("Failed ids: {}", completed.failedIds());
Brian O'Connor427a1762014-11-19 18:40:32 -0800842
843 for (Long id : completed.failedIds()) {
844 IntentId targetId = IntentId.valueOf(id);
845 for (IntentUpdate update : intentUpdates) {
846 List<Intent> installables = Lists.newArrayList(update.newInstallables());
Ray Milkey93508c22014-12-02 11:35:56 -0800847 if (update.oldInstallables() != null) {
848 installables.addAll(update.oldInstallables());
849 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800850 for (Intent intent : installables) {
851 if (intent.id().equals(targetId)) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800852 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800853 break;
854 }
855 }
856 }
857 // don't increment the non-failed items, as they have been reverted.
858 }
859 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700860 }
861
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800862 private void abandonShip() {
863 // the batch has failed
864 // TODO: maybe we should do more?
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800865 log.error("Walk the plank, matey...");
Ray Milkey93508c22014-12-02 11:35:56 -0800866 future = null;
867 batchService.removeIntentOperations(ops);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800868 }
869
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700870 /**
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700871 * Iterate through the pending futures, and remove them when they have completed.
872 */
873 private void processFutures() {
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700874 if (future == null) {
Brian O'Connor5d55ed42014-12-01 18:27:47 -0800875 // we are done if the future is null
876 return;
Brian O'Connorcb900f42014-10-07 21:55:33 -0700877 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700878 try {
879 CompletedBatchOperation completed = future.get(100, TimeUnit.NANOSECONDS);
Brian O'Connor427a1762014-11-19 18:40:32 -0800880 updateBatches(completed);
881 future = applyNextBatch();
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800882 } catch (TimeoutException | InterruptedException te) {
883 log.trace("Installation of intents are still pending: {}", ops);
884 } catch (ExecutionException e) {
885 log.warn("Execution of batch failed: {}", ops, e);
886 abandonShip();
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700887 }
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700888 }
889
Brian O'Connor427a1762014-11-19 18:40:32 -0800890 private void retry() {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800891 log.debug("Execution timed out, retrying.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800892 if (future.cancel(true)) { // cancel success; batch is reverted
893 // reset the timer
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800894 resetTimeoutLimit();
Ray Milkey93508c22014-12-02 11:35:56 -0800895 installAttempt++;
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800896 if (installAttempt == maxAttempts) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800897 log.warn("Install request timed out: {}", ops);
898 for (IntentUpdate update : intentUpdates) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800899 update.batchFailed();
Brian O'Connor427a1762014-11-19 18:40:32 -0800900 }
Sho SHIMIZU8cd9fb82014-12-11 13:19:18 -0800901 } else if (installAttempt > maxAttempts) {
Ray Milkey93508c22014-12-02 11:35:56 -0800902 abandonShip();
903 return;
Brian O'Connor427a1762014-11-19 18:40:32 -0800904 } // else just resubmit the work
905 future = applyNextBatch();
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800906 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800907 } else {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800908 log.error("Cancelling FlowRuleBatch failed.");
Brian O'Connor427a1762014-11-19 18:40:32 -0800909 // FIXME
910 // cancel failed... batch is broken; shouldn't happen!
911 // we could manually reverse everything
912 // ... or just core dump and send email to Ali
Ray Milkey93508c22014-12-02 11:35:56 -0800913 abandonShip();
Brian O'Connor427a1762014-11-19 18:40:32 -0800914 }
915 }
916
917 boolean isComplete() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800918 return future == null;
919 }
920
Brian O'Connorf2dbde52014-10-10 16:20:24 -0700921 @Override
922 public void run() {
Brian O'Connor427a1762014-11-19 18:40:32 -0800923 try {
924 if (intentUpdates.isEmpty()) {
925 // this should only be called on the first iteration
926 // note: this a "expensive", so it is not done in the constructor
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800927
928 // - creates per Intent installation context (IntentUpdate)
929 // - write Intents to store
930 // - process (compile, install, etc.) each Intents
931 // - generate FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800932 buildIntentUpdates();
933 }
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800934
935 // - peek if current FlowRuleBatch is complete
936 // -- If complete OK:
937 // step each IntentUpdate forward
938 // If phase left: generate next FlowRuleBatch
939 // If no more phase: write parking states
940 // -- If complete FAIL:
941 // Intent which failed: transition Intent to FAILED
942 // Other Intents: resubmit same FlowRuleBatch for this phase
Brian O'Connor427a1762014-11-19 18:40:32 -0800943 processFutures();
944 if (isComplete()) {
945 // there are no outstanding batches; we are done
946 batchService.removeIntentOperations(ops);
947 } else if (endTime < System.currentTimeMillis()) {
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800948 // - cancel current FlowRuleBatch and resubmit again
Brian O'Connor427a1762014-11-19 18:40:32 -0800949 retry();
950 } else {
951 // we are not done yet, yield the thread by resubmitting ourselves
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800952 executor.submit(this);
Brian O'Connor427a1762014-11-19 18:40:32 -0800953 }
954 } catch (Exception e) {
955 log.error("Error submitting batches:", e);
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800956 // FIXME incomplete Intents should be cleaned up
957 // (transition to FAILED, etc.)
Ray Milkey93508c22014-12-02 11:35:56 -0800958 abandonShip();
Brian O'Connorcb900f42014-10-07 21:55:33 -0700959 }
960 }
961 }
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700962
963 private class InternalBatchDelegate implements IntentBatchDelegate {
964 @Override
965 public void execute(IntentOperations operations) {
Yuta HIGUCHIfe4367a2014-11-24 19:19:09 -0800966 log.info("Execute {} operation(s).", operations.operations().size());
967 log.debug("Execute operations: {}", operations.operations());
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700968 //FIXME: perhaps we want to track this task so that we can cancel it.
Yuta HIGUCHIc2bf3d82014-11-28 18:50:41 -0800969 executor.execute(new IntentInstallMonitor(operations));
Brian O'Connorfa81eae2014-10-30 13:20:05 -0700970 }
971
972 @Override
973 public void cancel(IntentOperations operations) {
974 //FIXME: implement this
975 log.warn("NOT IMPLEMENTED -- Cancel operations: {}", operations);
976 }
977 }
Ray Milkeye97ede92014-11-20 10:43:12 -0800978
Brian O'Connor66630c82014-10-02 21:08:19 -0700979}