Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 1 | /* |
Ray Milkey | 34c9590 | 2015-04-15 09:47:53 -0700 | [diff] [blame] | 2 | * Copyright 2014-2015 Open Networking Laboratory |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 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'Connor | abafb50 | 2014-12-02 22:26:20 -0800 | [diff] [blame] | 16 | package org.onosproject.sdnip; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 17 | |
Pingping Lin | c07781f | 2015-10-30 00:44:41 -0700 | [diff] [blame] | 18 | import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| 19 | import static org.onlab.util.Tools.groupedThreads; |
| 20 | |
| 21 | import java.util.HashMap; |
| 22 | import java.util.LinkedList; |
| 23 | import java.util.List; |
| 24 | import java.util.Map; |
| 25 | import java.util.Map.Entry; |
| 26 | import java.util.concurrent.ConcurrentHashMap; |
| 27 | import java.util.concurrent.ExecutorService; |
| 28 | |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 29 | import org.onosproject.core.ApplicationId; |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 30 | import org.onosproject.net.intent.Intent; |
| 31 | import org.onosproject.net.intent.IntentService; |
| 32 | import org.onosproject.net.intent.IntentState; |
Luca Prete | 86ac7d1 | 2015-12-02 23:36:49 -0800 | [diff] [blame] | 33 | import org.onosproject.net.intent.IntentUtils; |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 34 | import org.onosproject.net.intent.Key; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 35 | import org.onosproject.routing.IntentSynchronizationService; |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 36 | import org.slf4j.Logger; |
| 37 | import org.slf4j.LoggerFactory; |
| 38 | |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 39 | /** |
| 40 | * Synchronizes intents between the in-memory intent store and the |
| 41 | * IntentService. |
| 42 | */ |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 43 | public class IntentSynchronizer implements IntentSynchronizationService { |
Pavlin Radoslavov | 2aa1f32 | 2015-03-11 17:59:44 -0700 | [diff] [blame] | 44 | |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 45 | private static final Logger log = |
| 46 | LoggerFactory.getLogger(IntentSynchronizer.class); |
| 47 | |
| 48 | private final ApplicationId appId; |
| 49 | private final IntentService intentService; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 50 | |
| 51 | private final Map<Key, Intent> intents; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 52 | |
| 53 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 54 | // State to deal with the Leader election and pushing Intents |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 55 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 56 | private final ExecutorService intentsSynchronizerExecutor; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 57 | private volatile boolean isElectedLeader = false; |
| 58 | private volatile boolean isActivatedLeader = false; |
| 59 | |
| 60 | /** |
| 61 | * Class constructor. |
| 62 | * |
| 63 | * @param appId the Application ID |
| 64 | * @param intentService the intent service |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 65 | */ |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 66 | public IntentSynchronizer(ApplicationId appId, IntentService intentService) { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 67 | this(appId, intentService, |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 68 | newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync"))); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Class constructor. |
| 73 | * |
| 74 | * @param appId the Application ID |
| 75 | * @param intentService the intent service |
| 76 | * @param executorService executor service for synchronization thread |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 77 | */ |
Luca Prete | 3401a3f | 2015-11-19 11:43:01 -0800 | [diff] [blame] | 78 | public IntentSynchronizer(ApplicationId appId, IntentService intentService, |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 79 | ExecutorService executorService) { |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 80 | this.appId = appId; |
| 81 | this.intentService = intentService; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 82 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 83 | intents = new ConcurrentHashMap<>(); |
Jonathan Hart | 552e31f | 2015-02-06 11:11:59 -0800 | [diff] [blame] | 84 | |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 85 | intentsSynchronizerExecutor = executorService; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Starts the synchronizer. |
| 90 | */ |
| 91 | public void start() { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 92 | |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Stops the synchronizer. |
| 97 | */ |
| 98 | public void stop() { |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 99 | synchronized (this) { |
| 100 | // Stop the thread(s) |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 101 | intentsSynchronizerExecutor.shutdownNow(); |
Pingping Lin | c07781f | 2015-10-30 00:44:41 -0700 | [diff] [blame] | 102 | log.info("Intents Synchronizer Executor shutdown completed"); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 103 | |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 104 | } |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 105 | } |
| 106 | |
Pingping Lin | c07781f | 2015-10-30 00:44:41 -0700 | [diff] [blame] | 107 | /** |
| 108 | * Withdraws all intents. |
| 109 | */ |
| 110 | public void removeIntents() { |
| 111 | if (!isElectedLeader) { |
| 112 | // only leader will withdraw intents |
| 113 | return; |
| 114 | } |
| 115 | |
| 116 | log.debug("Intent Synchronizer shutdown: withdrawing all intents..."); |
| 117 | |
| 118 | for (Entry<Key, Intent> entry : intents.entrySet()) { |
| 119 | intentService.withdraw(entry.getValue()); |
| 120 | log.debug("Intent Synchronizer withdrawing intent: {}", |
| 121 | entry.getValue()); |
| 122 | } |
| 123 | |
| 124 | intents.clear(); |
| 125 | log.info("Tried to clean all intents"); |
| 126 | } |
| 127 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 128 | @Override |
| 129 | public void submit(Intent intent) { |
| 130 | synchronized (this) { |
| 131 | intents.put(intent.key(), intent); |
| 132 | if (isElectedLeader && isActivatedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 133 | log.trace("Submitting intent: {}", intent); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 134 | intentService.submit(intent); |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | @Override |
| 140 | public void withdraw(Intent intent) { |
| 141 | synchronized (this) { |
| 142 | intents.remove(intent.key(), intent); |
| 143 | if (isElectedLeader && isActivatedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 144 | log.trace("Withdrawing intent: {}", intent); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 145 | intentService.withdraw(intent); |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 150 | /** |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 151 | * Signals the synchronizer that the leadership has changed. |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 152 | * |
| 153 | * @param isLeader true if this instance is now the leader, otherwise false |
| 154 | */ |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 155 | public void leaderChanged(boolean isLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 156 | log.debug("Leader changed: {}", isLeader); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 157 | |
| 158 | if (!isLeader) { |
| 159 | this.isElectedLeader = false; |
| 160 | this.isActivatedLeader = false; |
| 161 | return; // Nothing to do |
| 162 | } |
| 163 | this.isActivatedLeader = false; |
| 164 | this.isElectedLeader = true; |
| 165 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 166 | // Run the synchronization method off-thread |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 167 | intentsSynchronizerExecutor.execute(this::synchronizeIntents); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 168 | } |
| 169 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 170 | private void synchronizeIntents() { |
| 171 | Map<Key, Intent> serviceIntents = new HashMap<>(); |
| 172 | intentService.getIntents().forEach(i -> { |
| 173 | if (i.appId().equals(appId)) { |
| 174 | serviceIntents.put(i.key(), i); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 175 | } |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 176 | }); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 177 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 178 | List<Intent> intentsToAdd = new LinkedList<>(); |
| 179 | List<Intent> intentsToRemove = new LinkedList<>(); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 180 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 181 | for (Intent localIntent : intents.values()) { |
| 182 | Intent serviceIntent = serviceIntents.remove(localIntent.key()); |
| 183 | if (serviceIntent == null) { |
| 184 | intentsToAdd.add(localIntent); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 185 | } else { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 186 | IntentState state = intentService.getIntentState(serviceIntent.key()); |
Ray Milkey | 4fd3ceb | 2015-12-10 14:43:08 -0800 | [diff] [blame^] | 187 | if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null || |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 188 | state == IntentState.WITHDRAW_REQ || |
| 189 | state == IntentState.WITHDRAWING || |
| 190 | state == IntentState.WITHDRAWN) { |
| 191 | intentsToAdd.add(localIntent); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 192 | } |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 193 | } |
| 194 | } |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 195 | |
| 196 | for (Intent serviceIntent : serviceIntents.values()) { |
| 197 | IntentState state = intentService.getIntentState(serviceIntent.key()); |
| 198 | if (state != null && state != IntentState.WITHDRAW_REQ |
| 199 | && state != IntentState.WITHDRAWING |
| 200 | && state != IntentState.WITHDRAWN) { |
| 201 | intentsToRemove.add(serviceIntent); |
| 202 | } |
| 203 | } |
| 204 | |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 205 | log.debug("Intent Synchronizer: submitting {}, withdrawing {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 206 | intentsToAdd.size(), intentsToRemove.size()); |
| 207 | |
| 208 | // Withdraw Intents |
| 209 | for (Intent intent : intentsToRemove) { |
| 210 | intentService.withdraw(intent); |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 211 | log.trace("Intent Synchronizer: withdrawing intent: {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 212 | intent); |
| 213 | } |
| 214 | if (!isElectedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 215 | log.debug("Intent Synchronizer: cannot withdraw intents: " + |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 216 | "not elected leader anymore"); |
| 217 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 218 | return; |
| 219 | } |
| 220 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 221 | // Add Intents |
| 222 | for (Intent intent : intentsToAdd) { |
| 223 | intentService.submit(intent); |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 224 | log.trace("Intent Synchronizer: submitting intent: {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 225 | intent); |
| 226 | } |
| 227 | if (!isElectedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 228 | log.debug("Intent Synchronizer: cannot submit intents: " + |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 229 | "not elected leader anymore"); |
| 230 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 231 | return; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | if (isElectedLeader) { |
| 235 | isActivatedLeader = true; // Allow push of Intents |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 236 | } else { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 237 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 238 | } |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame] | 239 | log.debug("Intent synchronization completed"); |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 240 | } |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 241 | } |