blob: f2d5b5e2881731305f53da9a3a5e931eed07f243 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08003 *
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.sdnip;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080017
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070018import org.onosproject.core.ApplicationId;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070019import org.onosproject.net.intent.Intent;
20import org.onosproject.net.intent.IntentService;
21import org.onosproject.net.intent.IntentState;
22import org.onosproject.net.intent.Key;
Jonathan Hart9a426f82015-09-03 15:43:13 +020023import org.onosproject.routing.IntentSynchronizationService;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070024import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
26
Ray Milkeyebc5d222015-03-18 15:45:36 -070027import java.util.HashMap;
Ray Milkeyebc5d222015-03-18 15:45:36 -070028import java.util.LinkedList;
29import java.util.List;
30import java.util.Map;
Ray Milkeyebc5d222015-03-18 15:45:36 -070031import java.util.concurrent.ConcurrentHashMap;
32import java.util.concurrent.ExecutorService;
Ray Milkeyebc5d222015-03-18 15:45:36 -070033
Jonathan Hart9a426f82015-09-03 15:43:13 +020034import static java.util.concurrent.Executors.newSingleThreadExecutor;
35import static org.onlab.util.Tools.groupedThreads;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080036
Jonathan Hart51372182014-12-03 21:32:34 -080037/**
38 * Synchronizes intents between the in-memory intent store and the
39 * IntentService.
40 */
Jonathan Hart9a426f82015-09-03 15:43:13 +020041public class IntentSynchronizer implements IntentSynchronizationService {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070042
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080043 private static final Logger log =
44 LoggerFactory.getLogger(IntentSynchronizer.class);
45
46 private final ApplicationId appId;
47 private final IntentService intentService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020048
49 private final Map<Key, Intent> intents;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080050
51 //
Luca Prete00043db2015-11-03 15:40:40 -080052 // State to deal with the Leader election and pushing Intents
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080053 //
Luca Prete00043db2015-11-03 15:40:40 -080054 private final ExecutorService intentsSynchronizerExecutor;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080055 private volatile boolean isElectedLeader = false;
56 private volatile boolean isActivatedLeader = false;
57
58 /**
59 * Class constructor.
60 *
61 * @param appId the Application ID
62 * @param intentService the intent service
Jonathan Hart9a426f82015-09-03 15:43:13 +020063 */
Luca Prete00043db2015-11-03 15:40:40 -080064 public IntentSynchronizer(ApplicationId appId, IntentService intentService) {
Jonathan Hart9a426f82015-09-03 15:43:13 +020065 this(appId, intentService,
Luca Prete00043db2015-11-03 15:40:40 -080066 newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync")));
Jonathan Hart9a426f82015-09-03 15:43:13 +020067 }
68
69 /**
70 * Class constructor.
71 *
72 * @param appId the Application ID
73 * @param intentService the intent service
74 * @param executorService executor service for synchronization thread
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080075 */
Jonathan Hart552e31f2015-02-06 11:11:59 -080076 IntentSynchronizer(ApplicationId appId, IntentService intentService,
Jonathan Hart9a426f82015-09-03 15:43:13 +020077 ExecutorService executorService) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080078 this.appId = appId;
79 this.intentService = intentService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080080
Jonathan Hart9a426f82015-09-03 15:43:13 +020081 intents = new ConcurrentHashMap<>();
Jonathan Hart552e31f2015-02-06 11:11:59 -080082
Luca Prete00043db2015-11-03 15:40:40 -080083 intentsSynchronizerExecutor = executorService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080084 }
85
86 /**
87 * Starts the synchronizer.
88 */
89 public void start() {
Jonathan Hart9a426f82015-09-03 15:43:13 +020090
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080091 }
92
93 /**
94 * Stops the synchronizer.
95 */
96 public void stop() {
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -080097 synchronized (this) {
98 // Stop the thread(s)
Luca Prete00043db2015-11-03 15:40:40 -080099 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800100
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800101 //
Luca Prete00043db2015-11-03 15:40:40 -0800102 // Withdraw all app related intents
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800103 //
104 if (!isElectedLeader) {
105 return; // Nothing to do: not the leader anymore
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800107
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800108 //
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800109 // NOTE: We don't withdraw the intents during shutdown, because
110 // it creates flux in the data plane during switchover.
111 //
112
113 /*
114 //
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800115 // Build a batch operation to withdraw all intents from this
116 // application.
117 //
Luca Prete00043db2015-11-03 15:40:40 -0800118 log.debug("Intent Synchronizer shutdown: " +
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800119 "withdrawing all intents...");
Pavlin Radoslavov248c2ae2014-12-02 09:51:25 -0800120 IntentOperations.Builder builder = IntentOperations.builder(appId);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800121 for (Intent intent : intentService.getIntents()) {
122 // Skip the intents from other applications
123 if (!intent.appId().equals(appId)) {
124 continue;
125 }
126
127 // Skip the intents that are already withdrawn
128 IntentState intentState =
129 intentService.getIntentState(intent.id());
Pavlin Radoslavovdeb8a102014-11-26 13:31:36 -0800130 if ((intentState == null) ||
131 intentState.equals(IntentState.WITHDRAWING) ||
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800132 intentState.equals(IntentState.WITHDRAWN)) {
133 continue;
134 }
135
Luca Prete00043db2015-11-03 15:40:40 -0800136 log.trace("Intent Synchronizer withdrawing intent: {}",
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800137 intent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800138 builder.addWithdrawOperation(intent.id());
139 }
Pavlin Radoslavov93ae8322014-11-24 20:54:36 -0800140 IntentOperations intentOperations = builder.build();
141 intentService.execute(intentOperations);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800142 leaderChanged(false);
143
144 peerIntents.clear();
145 routeIntents.clear();
Luca Prete00043db2015-11-03 15:40:40 -0800146 log.debug("Intent Synchronizer shutdown completed");
Pavlin Radoslavov20be3e62014-11-25 18:52:08 -0800147 */
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800148 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800149 }
150
Jonathan Hart9a426f82015-09-03 15:43:13 +0200151 @Override
152 public void submit(Intent intent) {
153 synchronized (this) {
154 intents.put(intent.key(), intent);
155 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800156 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200157 intentService.submit(intent);
158 }
159 }
160 }
161
162 @Override
163 public void withdraw(Intent intent) {
164 synchronized (this) {
165 intents.remove(intent.key(), intent);
166 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800167 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200168 intentService.withdraw(intent);
169 }
170 }
171 }
172
Jonathan Hart51372182014-12-03 21:32:34 -0800173 /**
Luca Prete00043db2015-11-03 15:40:40 -0800174 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800175 *
176 * @param isLeader true if this instance is now the leader, otherwise false
177 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800178 public void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800179 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800180
181 if (!isLeader) {
182 this.isElectedLeader = false;
183 this.isActivatedLeader = false;
184 return; // Nothing to do
185 }
186 this.isActivatedLeader = false;
187 this.isElectedLeader = true;
188
Jonathan Hart9a426f82015-09-03 15:43:13 +0200189 // Run the synchronization method off-thread
Luca Prete00043db2015-11-03 15:40:40 -0800190 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800191 }
192
Jonathan Hart9a426f82015-09-03 15:43:13 +0200193 private void synchronizeIntents() {
194 Map<Key, Intent> serviceIntents = new HashMap<>();
195 intentService.getIntents().forEach(i -> {
196 if (i.appId().equals(appId)) {
197 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800198 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200199 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800200
Jonathan Hart9a426f82015-09-03 15:43:13 +0200201 List<Intent> intentsToAdd = new LinkedList<>();
202 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800203
Jonathan Hart9a426f82015-09-03 15:43:13 +0200204 for (Intent localIntent : intents.values()) {
205 Intent serviceIntent = serviceIntents.remove(localIntent.key());
206 if (serviceIntent == null) {
207 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800208 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200209 IntentState state = intentService.getIntentState(serviceIntent.key());
210 if (!IntentUtils.equals(serviceIntent, localIntent) || state == null ||
211 state == IntentState.WITHDRAW_REQ ||
212 state == IntentState.WITHDRAWING ||
213 state == IntentState.WITHDRAWN) {
214 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800215 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700216 }
217 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200218
219 for (Intent serviceIntent : serviceIntents.values()) {
220 IntentState state = intentService.getIntentState(serviceIntent.key());
221 if (state != null && state != IntentState.WITHDRAW_REQ
222 && state != IntentState.WITHDRAWING
223 && state != IntentState.WITHDRAWN) {
224 intentsToRemove.add(serviceIntent);
225 }
226 }
227
Luca Prete00043db2015-11-03 15:40:40 -0800228 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200229 intentsToAdd.size(), intentsToRemove.size());
230
231 // Withdraw Intents
232 for (Intent intent : intentsToRemove) {
233 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800234 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200235 intent);
236 }
237 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800238 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200239 "not elected leader anymore");
240 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700241 return;
242 }
243
Jonathan Hart9a426f82015-09-03 15:43:13 +0200244 // Add Intents
245 for (Intent intent : intentsToAdd) {
246 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800247 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200248 intent);
249 }
250 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800251 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200252 "not elected leader anymore");
253 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700254 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200255 }
256
257 if (isElectedLeader) {
258 isActivatedLeader = true; // Allow push of Intents
Pingping Line28ae4c2015-03-13 11:37:03 -0700259 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200260 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700261 }
Luca Prete00043db2015-11-03 15:40:40 -0800262 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700263 }
264
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800265}