blob: c15ea3ce8e71e77d962f5686e0e0bf444b227c79 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
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 */
Jonathan Hart6af92be2016-01-05 20:52:25 -080016
Jonathan Hart470ed4f2017-01-31 16:52:28 -080017package org.onosproject.intentsync;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080018
Jonathan Hart365335e2015-12-10 11:09:53 -080019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipEvent;
27import org.onosproject.cluster.LeadershipEventListener;
28import org.onosproject.cluster.LeadershipService;
Madan Jampani620f70d2016-01-30 22:22:47 -080029import org.onosproject.cluster.NodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.intent.Intent;
33import org.onosproject.net.intent.IntentService;
34import org.onosproject.net.intent.IntentState;
Jonathan Hart6af92be2016-01-05 20:52:25 -080035import org.onosproject.net.intent.IntentUtils;
Jonathan Hart365335e2015-12-10 11:09:53 -080036import org.onosproject.net.intent.Key;
Jonathan Hart365335e2015-12-10 11:09:53 -080037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
Pingping Linc07781f2015-10-30 00:44:41 -070039
40import java.util.HashMap;
41import java.util.LinkedList;
42import java.util.List;
43import java.util.Map;
44import java.util.Map.Entry;
45import java.util.concurrent.ConcurrentHashMap;
46import java.util.concurrent.ExecutorService;
47
Jonathan Hart365335e2015-12-10 11:09:53 -080048import static java.util.concurrent.Executors.newSingleThreadExecutor;
49import static org.onlab.util.Tools.groupedThreads;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070050
Jonathan Hart51372182014-12-03 21:32:34 -080051/**
Jonathan Hart365335e2015-12-10 11:09:53 -080052 * Synchronizes intents between an in-memory intent store and the IntentService.
Jonathan Hart51372182014-12-03 21:32:34 -080053 */
Jonathan Hart365335e2015-12-10 11:09:53 -080054@Service
Jonathan Hart470ed4f2017-01-31 16:52:28 -080055@Component(immediate = true)
Jonathan Hart365335e2015-12-10 11:09:53 -080056public class IntentSynchronizer implements IntentSynchronizationService,
57 IntentSynchronizationAdminService {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070058
Jonathan Hart365335e2015-12-10 11:09:53 -080059 private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080060
Jonathan Hart365335e2015-12-10 11:09:53 -080061 private static final String APP_NAME = "org.onosproject.intentsynchronizer";
Jonathan Hart9a426f82015-09-03 15:43:13 +020062
Jonathan Hart365335e2015-12-10 11:09:53 -080063 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 protected CoreService coreService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080065
Jonathan Hart365335e2015-12-10 11:09:53 -080066 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected LeadershipService leadershipService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected ClusterService clusterService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected IntentService intentService;
74
Madan Jampani620f70d2016-01-30 22:22:47 -080075 private NodeId localNodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080076 private ApplicationId appId;
77
78 private final InternalLeadershipListener leadershipEventListener =
79 new InternalLeadershipListener();
80
81 private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
82
83 private ExecutorService intentsSynchronizerExecutor;
84
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080085 private volatile boolean isElectedLeader = false;
86 private volatile boolean isActivatedLeader = false;
87
Jonathan Hart365335e2015-12-10 11:09:53 -080088 @Activate
89 public void activate() {
Madan Jampani620f70d2016-01-30 22:22:47 -080090 this.localNodeId = clusterService.getLocalNode().id();
Jonathan Hart365335e2015-12-10 11:09:53 -080091 this.appId = coreService.registerApplication(APP_NAME);
Jonathan Harta2eb9ff2016-04-13 21:27:06 -070092 intentsSynchronizerExecutor = createExecutor();
Jonathan Hart365335e2015-12-10 11:09:53 -080093
94 leadershipService.addListener(leadershipEventListener);
95 leadershipService.runForLeadership(appId.name());
96
97 log.info("Started");
Jonathan Hart9a426f82015-09-03 15:43:13 +020098 }
99
Jonathan Hart365335e2015-12-10 11:09:53 -0800100 @Deactivate
101 public void deactivate() {
102 leadershipService.withdraw(appId.name());
103 leadershipService.removeListener(leadershipEventListener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800104
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800105 synchronized (this) {
Luca Prete00043db2015-11-03 15:40:40 -0800106 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800108
109 log.info("Stopped");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800110 }
111
Pingping Linc07781f2015-10-30 00:44:41 -0700112 /**
Jonathan Hart365335e2015-12-10 11:09:53 -0800113 * Creates an executor that will be used for synchronization tasks.
114 * <p>
115 * Can be overridden to change the type of executor used.
116 * </p>
117 *
118 * @return executor service
Pingping Linc07781f2015-10-30 00:44:41 -0700119 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800120 protected ExecutorService createExecutor() {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700121 return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync", log));
Jonathan Hart365335e2015-12-10 11:09:53 -0800122 }
123
124 @Override
Pingping Linc07781f2015-10-30 00:44:41 -0700125 public void removeIntents() {
126 if (!isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800127 // Only leader will withdraw intents
Pingping Linc07781f2015-10-30 00:44:41 -0700128 return;
129 }
130
131 log.debug("Intent Synchronizer shutdown: withdrawing all intents...");
132
133 for (Entry<Key, Intent> entry : intents.entrySet()) {
134 intentService.withdraw(entry.getValue());
135 log.debug("Intent Synchronizer withdrawing intent: {}",
136 entry.getValue());
137 }
138
139 intents.clear();
140 log.info("Tried to clean all intents");
141 }
142
Jonathan Hart9a426f82015-09-03 15:43:13 +0200143 @Override
Luca Prete2705d662016-04-29 15:30:23 -0700144 public void removeIntentsByAppId(ApplicationId appId) {
145 if (!isElectedLeader) {
146 // Only leader will withdraw intents
147 return;
148 }
149
150 log.debug("Withdrawing intents for app {}...",
151 appId);
152
153 intents.entrySet()
154 .stream()
155 .filter(intent -> intent.getValue().appId().equals(appId))
156 .forEach(intent -> {
157 log.debug("Intent Synchronizer withdrawing intent: {}",
158 intent);
159 intentService.withdraw(intent.getValue());
160 intents.remove(intent.getKey(), intent.getValue());
161 log.info("Tried to clean intents for app: {}", appId);
162 });
163 }
164
165 @Override
Jonathan Hart9a426f82015-09-03 15:43:13 +0200166 public void submit(Intent intent) {
167 synchronized (this) {
168 intents.put(intent.key(), intent);
169 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800170 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200171 intentService.submit(intent);
172 }
173 }
174 }
175
176 @Override
177 public void withdraw(Intent intent) {
178 synchronized (this) {
179 intents.remove(intent.key(), intent);
180 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800181 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200182 intentService.withdraw(intent);
183 }
184 }
185 }
186
Jonathan Hart51372182014-12-03 21:32:34 -0800187 /**
Luca Prete00043db2015-11-03 15:40:40 -0800188 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800189 *
190 * @param isLeader true if this instance is now the leader, otherwise false
191 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800192 private void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800193 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800194
195 if (!isLeader) {
196 this.isElectedLeader = false;
197 this.isActivatedLeader = false;
Jonathan Hart365335e2015-12-10 11:09:53 -0800198 // Nothing to do
199 return;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800200 }
201 this.isActivatedLeader = false;
202 this.isElectedLeader = true;
203
Jonathan Hart365335e2015-12-10 11:09:53 -0800204 // Run the synchronization task
Luca Prete00043db2015-11-03 15:40:40 -0800205 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800206 }
207
Jonathan Hart9a426f82015-09-03 15:43:13 +0200208 private void synchronizeIntents() {
209 Map<Key, Intent> serviceIntents = new HashMap<>();
210 intentService.getIntents().forEach(i -> {
211 if (i.appId().equals(appId)) {
212 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800213 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200214 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800215
Jonathan Hart9a426f82015-09-03 15:43:13 +0200216 List<Intent> intentsToAdd = new LinkedList<>();
217 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800218
Jonathan Hart9a426f82015-09-03 15:43:13 +0200219 for (Intent localIntent : intents.values()) {
220 Intent serviceIntent = serviceIntents.remove(localIntent.key());
221 if (serviceIntent == null) {
222 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800223 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200224 IntentState state = intentService.getIntentState(serviceIntent.key());
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800225 if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null ||
Jonathan Hart9a426f82015-09-03 15:43:13 +0200226 state == IntentState.WITHDRAW_REQ ||
227 state == IntentState.WITHDRAWING ||
228 state == IntentState.WITHDRAWN) {
229 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800230 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700231 }
232 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200233
234 for (Intent serviceIntent : serviceIntents.values()) {
235 IntentState state = intentService.getIntentState(serviceIntent.key());
236 if (state != null && state != IntentState.WITHDRAW_REQ
237 && state != IntentState.WITHDRAWING
238 && state != IntentState.WITHDRAWN) {
239 intentsToRemove.add(serviceIntent);
240 }
241 }
242
Luca Prete00043db2015-11-03 15:40:40 -0800243 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200244 intentsToAdd.size(), intentsToRemove.size());
245
246 // Withdraw Intents
247 for (Intent intent : intentsToRemove) {
248 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800249 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200250 intent);
251 }
252 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800253 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200254 "not elected leader anymore");
255 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700256 return;
257 }
258
Jonathan Hart9a426f82015-09-03 15:43:13 +0200259 // Add Intents
260 for (Intent intent : intentsToAdd) {
261 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800262 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200263 intent);
264 }
265 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800266 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200267 "not elected leader anymore");
268 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700269 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200270 }
271
272 if (isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800273 // Allow push of Intents
274 isActivatedLeader = true;
Pingping Line28ae4c2015-03-13 11:37:03 -0700275 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200276 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700277 }
Luca Prete00043db2015-11-03 15:40:40 -0800278 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700279 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800280
281 @Override
282 public void modifyPrimary(boolean isPrimary) {
283 leaderChanged(isPrimary);
284 }
285
286 /**
287 * A listener for leadership events.
288 */
289 private class InternalLeadershipListener implements LeadershipEventListener {
290
291 @Override
Madan Jampani620f70d2016-01-30 22:22:47 -0800292 public boolean isRelevant(LeadershipEvent event) {
293 return event.subject().topic().equals(appId.name());
294 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800295
Madan Jampani620f70d2016-01-30 22:22:47 -0800296 @Override
297 public void event(LeadershipEvent event) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800298 switch (event.type()) {
Madan Jampani620f70d2016-01-30 22:22:47 -0800299 case LEADER_CHANGED:
300 case LEADER_AND_CANDIDATES_CHANGED:
301 if (localNodeId.equals(event.subject().leaderNodeId())) {
302 log.info("IntentSynchronizer gained leadership");
303 leaderChanged(true);
304 } else {
305 log.info("IntentSynchronizer leader changed. New leader is {}", event.subject().leaderNodeId());
306 leaderChanged(false);
307 }
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800308 break;
Jonathan Hart365335e2015-12-10 11:09:53 -0800309 default:
310 break;
311 }
312 }
313 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800314}