blob: 3b47256d87daf9c0940d043b857bbd5c18321d3f [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present 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 */
Jonathan Hart6af92be2016-01-05 20:52:25 -080016
17package org.onosproject.routing.impl;
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;
37import org.onosproject.routing.IntentSynchronizationAdminService;
38import org.onosproject.routing.IntentSynchronizationService;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
Pingping Linc07781f2015-10-30 00:44:41 -070041
42import java.util.HashMap;
43import java.util.LinkedList;
44import java.util.List;
45import java.util.Map;
46import java.util.Map.Entry;
47import java.util.concurrent.ConcurrentHashMap;
48import java.util.concurrent.ExecutorService;
49
Jonathan Hart365335e2015-12-10 11:09:53 -080050import static java.util.concurrent.Executors.newSingleThreadExecutor;
51import static org.onlab.util.Tools.groupedThreads;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070052
Jonathan Hart51372182014-12-03 21:32:34 -080053/**
Jonathan Hart365335e2015-12-10 11:09:53 -080054 * Synchronizes intents between an in-memory intent store and the IntentService.
Jonathan Hart51372182014-12-03 21:32:34 -080055 */
Jonathan Hart365335e2015-12-10 11:09:53 -080056@Service
Jonathan Hart6af92be2016-01-05 20:52:25 -080057@Component(immediate = false)
Jonathan Hart365335e2015-12-10 11:09:53 -080058public class IntentSynchronizer implements IntentSynchronizationService,
59 IntentSynchronizationAdminService {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070060
Jonathan Hart365335e2015-12-10 11:09:53 -080061 private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080062
Jonathan Hart365335e2015-12-10 11:09:53 -080063 private static final String APP_NAME = "org.onosproject.intentsynchronizer";
Jonathan Hart9a426f82015-09-03 15:43:13 +020064
Jonathan Hart365335e2015-12-10 11:09:53 -080065 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected CoreService coreService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080067
Jonathan Hart365335e2015-12-10 11:09:53 -080068 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected LeadershipService leadershipService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected ClusterService clusterService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected IntentService intentService;
76
Madan Jampani620f70d2016-01-30 22:22:47 -080077 private NodeId localNodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080078 private ApplicationId appId;
79
80 private final InternalLeadershipListener leadershipEventListener =
81 new InternalLeadershipListener();
82
83 private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
84
85 private ExecutorService intentsSynchronizerExecutor;
86
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080087 private volatile boolean isElectedLeader = false;
88 private volatile boolean isActivatedLeader = false;
89
Jonathan Hart365335e2015-12-10 11:09:53 -080090 @Activate
91 public void activate() {
Madan Jampani620f70d2016-01-30 22:22:47 -080092 this.localNodeId = clusterService.getLocalNode().id();
Jonathan Hart365335e2015-12-10 11:09:53 -080093 this.appId = coreService.registerApplication(APP_NAME);
Jonathan Harta2eb9ff2016-04-13 21:27:06 -070094 intentsSynchronizerExecutor = createExecutor();
Jonathan Hart365335e2015-12-10 11:09:53 -080095
96 leadershipService.addListener(leadershipEventListener);
97 leadershipService.runForLeadership(appId.name());
98
99 log.info("Started");
Jonathan Hart9a426f82015-09-03 15:43:13 +0200100 }
101
Jonathan Hart365335e2015-12-10 11:09:53 -0800102 @Deactivate
103 public void deactivate() {
104 leadershipService.withdraw(appId.name());
105 leadershipService.removeListener(leadershipEventListener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 synchronized (this) {
Luca Prete00043db2015-11-03 15:40:40 -0800108 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800109 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800110
111 log.info("Stopped");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800112 }
113
Pingping Linc07781f2015-10-30 00:44:41 -0700114 /**
Jonathan Hart365335e2015-12-10 11:09:53 -0800115 * Creates an executor that will be used for synchronization tasks.
116 * <p>
117 * Can be overridden to change the type of executor used.
118 * </p>
119 *
120 * @return executor service
Pingping Linc07781f2015-10-30 00:44:41 -0700121 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800122 protected ExecutorService createExecutor() {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700123 return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync", log));
Jonathan Hart365335e2015-12-10 11:09:53 -0800124 }
125
126 @Override
Pingping Linc07781f2015-10-30 00:44:41 -0700127 public void removeIntents() {
128 if (!isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800129 // Only leader will withdraw intents
Pingping Linc07781f2015-10-30 00:44:41 -0700130 return;
131 }
132
133 log.debug("Intent Synchronizer shutdown: withdrawing all intents...");
134
135 for (Entry<Key, Intent> entry : intents.entrySet()) {
136 intentService.withdraw(entry.getValue());
137 log.debug("Intent Synchronizer withdrawing intent: {}",
138 entry.getValue());
139 }
140
141 intents.clear();
142 log.info("Tried to clean all intents");
143 }
144
Jonathan Hart9a426f82015-09-03 15:43:13 +0200145 @Override
Luca Prete2705d662016-04-29 15:30:23 -0700146 public void removeIntentsByAppId(ApplicationId appId) {
147 if (!isElectedLeader) {
148 // Only leader will withdraw intents
149 return;
150 }
151
152 log.debug("Withdrawing intents for app {}...",
153 appId);
154
155 intents.entrySet()
156 .stream()
157 .filter(intent -> intent.getValue().appId().equals(appId))
158 .forEach(intent -> {
159 log.debug("Intent Synchronizer withdrawing intent: {}",
160 intent);
161 intentService.withdraw(intent.getValue());
162 intents.remove(intent.getKey(), intent.getValue());
163 log.info("Tried to clean intents for app: {}", appId);
164 });
165 }
166
167 @Override
Jonathan Hart9a426f82015-09-03 15:43:13 +0200168 public void submit(Intent intent) {
169 synchronized (this) {
170 intents.put(intent.key(), intent);
171 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800172 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200173 intentService.submit(intent);
174 }
175 }
176 }
177
178 @Override
179 public void withdraw(Intent intent) {
180 synchronized (this) {
181 intents.remove(intent.key(), intent);
182 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800183 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200184 intentService.withdraw(intent);
185 }
186 }
187 }
188
Jonathan Hart51372182014-12-03 21:32:34 -0800189 /**
Luca Prete00043db2015-11-03 15:40:40 -0800190 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800191 *
192 * @param isLeader true if this instance is now the leader, otherwise false
193 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800194 private void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800195 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800196
197 if (!isLeader) {
198 this.isElectedLeader = false;
199 this.isActivatedLeader = false;
Jonathan Hart365335e2015-12-10 11:09:53 -0800200 // Nothing to do
201 return;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800202 }
203 this.isActivatedLeader = false;
204 this.isElectedLeader = true;
205
Jonathan Hart365335e2015-12-10 11:09:53 -0800206 // Run the synchronization task
Luca Prete00043db2015-11-03 15:40:40 -0800207 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800208 }
209
Jonathan Hart9a426f82015-09-03 15:43:13 +0200210 private void synchronizeIntents() {
211 Map<Key, Intent> serviceIntents = new HashMap<>();
212 intentService.getIntents().forEach(i -> {
213 if (i.appId().equals(appId)) {
214 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800215 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200216 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800217
Jonathan Hart9a426f82015-09-03 15:43:13 +0200218 List<Intent> intentsToAdd = new LinkedList<>();
219 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800220
Jonathan Hart9a426f82015-09-03 15:43:13 +0200221 for (Intent localIntent : intents.values()) {
222 Intent serviceIntent = serviceIntents.remove(localIntent.key());
223 if (serviceIntent == null) {
224 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800225 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200226 IntentState state = intentService.getIntentState(serviceIntent.key());
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800227 if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null ||
Jonathan Hart9a426f82015-09-03 15:43:13 +0200228 state == IntentState.WITHDRAW_REQ ||
229 state == IntentState.WITHDRAWING ||
230 state == IntentState.WITHDRAWN) {
231 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800232 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700233 }
234 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200235
236 for (Intent serviceIntent : serviceIntents.values()) {
237 IntentState state = intentService.getIntentState(serviceIntent.key());
238 if (state != null && state != IntentState.WITHDRAW_REQ
239 && state != IntentState.WITHDRAWING
240 && state != IntentState.WITHDRAWN) {
241 intentsToRemove.add(serviceIntent);
242 }
243 }
244
Luca Prete00043db2015-11-03 15:40:40 -0800245 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200246 intentsToAdd.size(), intentsToRemove.size());
247
248 // Withdraw Intents
249 for (Intent intent : intentsToRemove) {
250 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800251 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200252 intent);
253 }
254 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800255 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200256 "not elected leader anymore");
257 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700258 return;
259 }
260
Jonathan Hart9a426f82015-09-03 15:43:13 +0200261 // Add Intents
262 for (Intent intent : intentsToAdd) {
263 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800264 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200265 intent);
266 }
267 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800268 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200269 "not elected leader anymore");
270 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700271 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200272 }
273
274 if (isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800275 // Allow push of Intents
276 isActivatedLeader = true;
Pingping Line28ae4c2015-03-13 11:37:03 -0700277 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200278 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700279 }
Luca Prete00043db2015-11-03 15:40:40 -0800280 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700281 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800282
283 @Override
284 public void modifyPrimary(boolean isPrimary) {
285 leaderChanged(isPrimary);
286 }
287
288 /**
289 * A listener for leadership events.
290 */
291 private class InternalLeadershipListener implements LeadershipEventListener {
292
293 @Override
Madan Jampani620f70d2016-01-30 22:22:47 -0800294 public boolean isRelevant(LeadershipEvent event) {
295 return event.subject().topic().equals(appId.name());
296 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800297
Madan Jampani620f70d2016-01-30 22:22:47 -0800298 @Override
299 public void event(LeadershipEvent event) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800300 switch (event.type()) {
Madan Jampani620f70d2016-01-30 22:22:47 -0800301 case LEADER_CHANGED:
302 case LEADER_AND_CANDIDATES_CHANGED:
303 if (localNodeId.equals(event.subject().leaderNodeId())) {
304 log.info("IntentSynchronizer gained leadership");
305 leaderChanged(true);
306 } else {
307 log.info("IntentSynchronizer leader changed. New leader is {}", event.subject().leaderNodeId());
308 leaderChanged(false);
309 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800310 default:
311 break;
312 }
313 }
314 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800315}