blob: 1f33976b8126a49774f594cbc79565ed963bfbc1 [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.onosproject.cluster.ClusterService;
20import org.onosproject.cluster.LeadershipEvent;
21import org.onosproject.cluster.LeadershipEventListener;
22import org.onosproject.cluster.LeadershipService;
Madan Jampani620f70d2016-01-30 22:22:47 -080023import org.onosproject.cluster.NodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080024import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.intent.Intent;
27import org.onosproject.net.intent.IntentService;
28import org.onosproject.net.intent.IntentState;
Jonathan Hart6af92be2016-01-05 20:52:25 -080029import org.onosproject.net.intent.IntentUtils;
Jonathan Hart365335e2015-12-10 11:09:53 -080030import org.onosproject.net.intent.Key;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070031import org.osgi.service.component.annotations.Activate;
32import org.osgi.service.component.annotations.Component;
33import org.osgi.service.component.annotations.Deactivate;
34import org.osgi.service.component.annotations.Reference;
35import org.osgi.service.component.annotations.ReferenceCardinality;
Jonathan Hart365335e2015-12-10 11:09:53 -080036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
Pingping Linc07781f2015-10-30 00:44:41 -070038
39import java.util.HashMap;
40import java.util.LinkedList;
41import java.util.List;
42import java.util.Map;
43import java.util.Map.Entry;
44import java.util.concurrent.ConcurrentHashMap;
45import java.util.concurrent.ExecutorService;
46
Jonathan Hart365335e2015-12-10 11:09:53 -080047import static java.util.concurrent.Executors.newSingleThreadExecutor;
48import static org.onlab.util.Tools.groupedThreads;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070049
Jonathan Hart51372182014-12-03 21:32:34 -080050/**
Jonathan Hart365335e2015-12-10 11:09:53 -080051 * Synchronizes intents between an in-memory intent store and the IntentService.
Jonathan Hart51372182014-12-03 21:32:34 -080052 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070053@Component(immediate = true, service = { IntentSynchronizationService.class,
54 IntentSynchronizationAdminService.class })
Jonathan Hart365335e2015-12-10 11:09:53 -080055public class IntentSynchronizer implements IntentSynchronizationService,
56 IntentSynchronizationAdminService {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070057
Jonathan Hart365335e2015-12-10 11:09:53 -080058 private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080059
Jonathan Hart365335e2015-12-10 11:09:53 -080060 private static final String APP_NAME = "org.onosproject.intentsynchronizer";
Jonathan Hart9a426f82015-09-03 15:43:13 +020061
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart365335e2015-12-10 11:09:53 -080063 protected CoreService coreService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080064
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart365335e2015-12-10 11:09:53 -080066 protected LeadershipService leadershipService;
67
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart365335e2015-12-10 11:09:53 -080069 protected ClusterService clusterService;
70
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart365335e2015-12-10 11:09:53 -080072 protected IntentService intentService;
73
Madan Jampani620f70d2016-01-30 22:22:47 -080074 private NodeId localNodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080075 private ApplicationId appId;
76
77 private final InternalLeadershipListener leadershipEventListener =
78 new InternalLeadershipListener();
79
80 private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
81
82 private ExecutorService intentsSynchronizerExecutor;
83
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080084 private volatile boolean isElectedLeader = false;
85 private volatile boolean isActivatedLeader = false;
86
Jonathan Hart365335e2015-12-10 11:09:53 -080087 @Activate
88 public void activate() {
Madan Jampani620f70d2016-01-30 22:22:47 -080089 this.localNodeId = clusterService.getLocalNode().id();
Jonathan Hart365335e2015-12-10 11:09:53 -080090 this.appId = coreService.registerApplication(APP_NAME);
Jonathan Harta2eb9ff2016-04-13 21:27:06 -070091 intentsSynchronizerExecutor = createExecutor();
Jonathan Hart365335e2015-12-10 11:09:53 -080092
93 leadershipService.addListener(leadershipEventListener);
94 leadershipService.runForLeadership(appId.name());
95
96 log.info("Started");
Jonathan Hart9a426f82015-09-03 15:43:13 +020097 }
98
Jonathan Hart365335e2015-12-10 11:09:53 -080099 @Deactivate
100 public void deactivate() {
101 leadershipService.withdraw(appId.name());
102 leadershipService.removeListener(leadershipEventListener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800103
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800104 synchronized (this) {
Luca Prete00043db2015-11-03 15:40:40 -0800105 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800106 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800107
108 log.info("Stopped");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800109 }
110
Pingping Linc07781f2015-10-30 00:44:41 -0700111 /**
Jonathan Hart365335e2015-12-10 11:09:53 -0800112 * Creates an executor that will be used for synchronization tasks.
113 * <p>
114 * Can be overridden to change the type of executor used.
115 * </p>
116 *
117 * @return executor service
Pingping Linc07781f2015-10-30 00:44:41 -0700118 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800119 protected ExecutorService createExecutor() {
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700120 return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync", log));
Jonathan Hart365335e2015-12-10 11:09:53 -0800121 }
122
123 @Override
Pingping Linc07781f2015-10-30 00:44:41 -0700124 public void removeIntents() {
125 if (!isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800126 // Only leader will withdraw intents
Pingping Linc07781f2015-10-30 00:44:41 -0700127 return;
128 }
129
130 log.debug("Intent Synchronizer shutdown: withdrawing all intents...");
131
132 for (Entry<Key, Intent> entry : intents.entrySet()) {
133 intentService.withdraw(entry.getValue());
134 log.debug("Intent Synchronizer withdrawing intent: {}",
135 entry.getValue());
136 }
137
138 intents.clear();
139 log.info("Tried to clean all intents");
140 }
141
Jonathan Hart9a426f82015-09-03 15:43:13 +0200142 @Override
Luca Prete2705d662016-04-29 15:30:23 -0700143 public void removeIntentsByAppId(ApplicationId appId) {
144 if (!isElectedLeader) {
145 // Only leader will withdraw intents
146 return;
147 }
148
149 log.debug("Withdrawing intents for app {}...",
150 appId);
151
152 intents.entrySet()
153 .stream()
154 .filter(intent -> intent.getValue().appId().equals(appId))
155 .forEach(intent -> {
156 log.debug("Intent Synchronizer withdrawing intent: {}",
157 intent);
158 intentService.withdraw(intent.getValue());
159 intents.remove(intent.getKey(), intent.getValue());
160 log.info("Tried to clean intents for app: {}", appId);
161 });
162 }
163
164 @Override
Jonathan Hart9a426f82015-09-03 15:43:13 +0200165 public void submit(Intent intent) {
166 synchronized (this) {
167 intents.put(intent.key(), intent);
168 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800169 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200170 intentService.submit(intent);
171 }
172 }
173 }
174
175 @Override
176 public void withdraw(Intent intent) {
177 synchronized (this) {
178 intents.remove(intent.key(), intent);
179 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800180 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200181 intentService.withdraw(intent);
182 }
183 }
184 }
185
Jonathan Hart51372182014-12-03 21:32:34 -0800186 /**
Luca Prete00043db2015-11-03 15:40:40 -0800187 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800188 *
189 * @param isLeader true if this instance is now the leader, otherwise false
190 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800191 private void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800192 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800193
194 if (!isLeader) {
195 this.isElectedLeader = false;
196 this.isActivatedLeader = false;
Jonathan Hart365335e2015-12-10 11:09:53 -0800197 // Nothing to do
198 return;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800199 }
200 this.isActivatedLeader = false;
201 this.isElectedLeader = true;
202
Jonathan Hart365335e2015-12-10 11:09:53 -0800203 // Run the synchronization task
Luca Prete00043db2015-11-03 15:40:40 -0800204 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800205 }
206
Jonathan Hart9a426f82015-09-03 15:43:13 +0200207 private void synchronizeIntents() {
208 Map<Key, Intent> serviceIntents = new HashMap<>();
209 intentService.getIntents().forEach(i -> {
210 if (i.appId().equals(appId)) {
211 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800212 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200213 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800214
Jonathan Hart9a426f82015-09-03 15:43:13 +0200215 List<Intent> intentsToAdd = new LinkedList<>();
216 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800217
Jonathan Hart9a426f82015-09-03 15:43:13 +0200218 for (Intent localIntent : intents.values()) {
219 Intent serviceIntent = serviceIntents.remove(localIntent.key());
220 if (serviceIntent == null) {
221 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800222 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200223 IntentState state = intentService.getIntentState(serviceIntent.key());
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800224 if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null ||
Jonathan Hart9a426f82015-09-03 15:43:13 +0200225 state == IntentState.WITHDRAW_REQ ||
226 state == IntentState.WITHDRAWING ||
227 state == IntentState.WITHDRAWN) {
228 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800229 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700230 }
231 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200232
233 for (Intent serviceIntent : serviceIntents.values()) {
234 IntentState state = intentService.getIntentState(serviceIntent.key());
235 if (state != null && state != IntentState.WITHDRAW_REQ
236 && state != IntentState.WITHDRAWING
237 && state != IntentState.WITHDRAWN) {
238 intentsToRemove.add(serviceIntent);
239 }
240 }
241
Luca Prete00043db2015-11-03 15:40:40 -0800242 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200243 intentsToAdd.size(), intentsToRemove.size());
244
245 // Withdraw Intents
246 for (Intent intent : intentsToRemove) {
247 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800248 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200249 intent);
250 }
251 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800252 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200253 "not elected leader anymore");
254 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700255 return;
256 }
257
Jonathan Hart9a426f82015-09-03 15:43:13 +0200258 // Add Intents
259 for (Intent intent : intentsToAdd) {
260 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800261 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200262 intent);
263 }
264 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800265 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200266 "not elected leader anymore");
267 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700268 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200269 }
270
271 if (isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800272 // Allow push of Intents
273 isActivatedLeader = true;
Pingping Line28ae4c2015-03-13 11:37:03 -0700274 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200275 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700276 }
Luca Prete00043db2015-11-03 15:40:40 -0800277 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700278 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800279
280 @Override
281 public void modifyPrimary(boolean isPrimary) {
282 leaderChanged(isPrimary);
283 }
284
285 /**
286 * A listener for leadership events.
287 */
288 private class InternalLeadershipListener implements LeadershipEventListener {
289
290 @Override
Madan Jampani620f70d2016-01-30 22:22:47 -0800291 public boolean isRelevant(LeadershipEvent event) {
292 return event.subject().topic().equals(appId.name());
293 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800294
Madan Jampani620f70d2016-01-30 22:22:47 -0800295 @Override
296 public void event(LeadershipEvent event) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800297 switch (event.type()) {
Madan Jampani620f70d2016-01-30 22:22:47 -0800298 case LEADER_CHANGED:
299 case LEADER_AND_CANDIDATES_CHANGED:
300 if (localNodeId.equals(event.subject().leaderNodeId())) {
301 log.info("IntentSynchronizer gained leadership");
302 leaderChanged(true);
303 } else {
304 log.info("IntentSynchronizer leader changed. New leader is {}", event.subject().leaderNodeId());
305 leaderChanged(false);
306 }
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800307 break;
Jonathan Hart365335e2015-12-10 11:09:53 -0800308 default:
309 break;
310 }
311 }
312 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800313}