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 | |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 18 | import org.onosproject.core.ApplicationId; |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 19 | import org.onosproject.net.intent.Intent; |
| 20 | import org.onosproject.net.intent.IntentService; |
| 21 | import org.onosproject.net.intent.IntentState; |
| 22 | import org.onosproject.net.intent.Key; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 23 | import org.onosproject.routing.IntentSynchronizationService; |
Jonathan Hart | 96c5a4a | 2015-07-31 14:23:33 -0700 | [diff] [blame] | 24 | import org.slf4j.Logger; |
| 25 | import org.slf4j.LoggerFactory; |
| 26 | |
Ray Milkey | ebc5d22 | 2015-03-18 15:45:36 -0700 | [diff] [blame] | 27 | import java.util.HashMap; |
Ray Milkey | ebc5d22 | 2015-03-18 15:45:36 -0700 | [diff] [blame] | 28 | import java.util.LinkedList; |
| 29 | import java.util.List; |
| 30 | import java.util.Map; |
Ray Milkey | ebc5d22 | 2015-03-18 15:45:36 -0700 | [diff] [blame] | 31 | import java.util.concurrent.ConcurrentHashMap; |
| 32 | import java.util.concurrent.ExecutorService; |
Ray Milkey | ebc5d22 | 2015-03-18 15:45:36 -0700 | [diff] [blame] | 33 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 34 | import static java.util.concurrent.Executors.newSingleThreadExecutor; |
| 35 | import static org.onlab.util.Tools.groupedThreads; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 36 | |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 37 | /** |
| 38 | * Synchronizes intents between the in-memory intent store and the |
| 39 | * IntentService. |
| 40 | */ |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 41 | public class IntentSynchronizer implements IntentSynchronizationService { |
Pavlin Radoslavov | 2aa1f32 | 2015-03-11 17:59:44 -0700 | [diff] [blame] | 42 | |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 43 | private static final Logger log = |
| 44 | LoggerFactory.getLogger(IntentSynchronizer.class); |
| 45 | |
| 46 | private final ApplicationId appId; |
| 47 | private final IntentService intentService; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 48 | |
| 49 | private final Map<Key, Intent> intents; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 50 | |
| 51 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 52 | // State to deal with the Leader election and pushing Intents |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 53 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 54 | private final ExecutorService intentsSynchronizerExecutor; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 55 | 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 Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 63 | */ |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 64 | public IntentSynchronizer(ApplicationId appId, IntentService intentService) { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 65 | this(appId, intentService, |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 66 | newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync"))); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 67 | } |
| 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 Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 75 | */ |
Jonathan Hart | 552e31f | 2015-02-06 11:11:59 -0800 | [diff] [blame] | 76 | IntentSynchronizer(ApplicationId appId, IntentService intentService, |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 77 | ExecutorService executorService) { |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 78 | this.appId = appId; |
| 79 | this.intentService = intentService; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 80 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 81 | intents = new ConcurrentHashMap<>(); |
Jonathan Hart | 552e31f | 2015-02-06 11:11:59 -0800 | [diff] [blame] | 82 | |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 83 | intentsSynchronizerExecutor = executorService; |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Starts the synchronizer. |
| 88 | */ |
| 89 | public void start() { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 90 | |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | /** |
| 94 | * Stops the synchronizer. |
| 95 | */ |
| 96 | public void stop() { |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 97 | synchronized (this) { |
| 98 | // Stop the thread(s) |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 99 | intentsSynchronizerExecutor.shutdownNow(); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 100 | |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 101 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 102 | // Withdraw all app related intents |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 103 | // |
| 104 | if (!isElectedLeader) { |
| 105 | return; // Nothing to do: not the leader anymore |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 106 | } |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 107 | |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 108 | // |
Pavlin Radoslavov | 20be3e6 | 2014-11-25 18:52:08 -0800 | [diff] [blame] | 109 | // 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 Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 115 | // Build a batch operation to withdraw all intents from this |
| 116 | // application. |
| 117 | // |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 118 | log.debug("Intent Synchronizer shutdown: " + |
Pavlin Radoslavov | 93ae832 | 2014-11-24 20:54:36 -0800 | [diff] [blame] | 119 | "withdrawing all intents..."); |
Pavlin Radoslavov | 248c2ae | 2014-12-02 09:51:25 -0800 | [diff] [blame] | 120 | IntentOperations.Builder builder = IntentOperations.builder(appId); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 121 | 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 Radoslavov | deb8a10 | 2014-11-26 13:31:36 -0800 | [diff] [blame] | 130 | if ((intentState == null) || |
| 131 | intentState.equals(IntentState.WITHDRAWING) || |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 132 | intentState.equals(IntentState.WITHDRAWN)) { |
| 133 | continue; |
| 134 | } |
| 135 | |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 136 | log.trace("Intent Synchronizer withdrawing intent: {}", |
Pavlin Radoslavov | 93ae832 | 2014-11-24 20:54:36 -0800 | [diff] [blame] | 137 | intent); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 138 | builder.addWithdrawOperation(intent.id()); |
| 139 | } |
Pavlin Radoslavov | 93ae832 | 2014-11-24 20:54:36 -0800 | [diff] [blame] | 140 | IntentOperations intentOperations = builder.build(); |
| 141 | intentService.execute(intentOperations); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 142 | leaderChanged(false); |
| 143 | |
| 144 | peerIntents.clear(); |
| 145 | routeIntents.clear(); |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 146 | log.debug("Intent Synchronizer shutdown completed"); |
Pavlin Radoslavov | 20be3e6 | 2014-11-25 18:52:08 -0800 | [diff] [blame] | 147 | */ |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 148 | } |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 149 | } |
| 150 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 151 | @Override |
| 152 | public void submit(Intent intent) { |
| 153 | synchronized (this) { |
| 154 | intents.put(intent.key(), intent); |
| 155 | if (isElectedLeader && isActivatedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 156 | log.trace("Submitting intent: {}", intent); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 157 | 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 Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 167 | log.trace("Withdrawing intent: {}", intent); |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 168 | intentService.withdraw(intent); |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 173 | /** |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 174 | * Signals the synchronizer that the leadership has changed. |
Jonathan Hart | 5137218 | 2014-12-03 21:32:34 -0800 | [diff] [blame] | 175 | * |
| 176 | * @param isLeader true if this instance is now the leader, otherwise false |
| 177 | */ |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 178 | public void leaderChanged(boolean isLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 179 | log.debug("Leader changed: {}", isLeader); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 180 | |
| 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 Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 189 | // Run the synchronization method off-thread |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 190 | intentsSynchronizerExecutor.execute(this::synchronizeIntents); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 191 | } |
| 192 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 193 | 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 Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 198 | } |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 199 | }); |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 200 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 201 | List<Intent> intentsToAdd = new LinkedList<>(); |
| 202 | List<Intent> intentsToRemove = new LinkedList<>(); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 203 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 204 | for (Intent localIntent : intents.values()) { |
| 205 | Intent serviceIntent = serviceIntents.remove(localIntent.key()); |
| 206 | if (serviceIntent == null) { |
| 207 | intentsToAdd.add(localIntent); |
Pavlin Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 208 | } else { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 209 | 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 Radoslavov | a7243cc | 2014-11-22 21:38:02 -0800 | [diff] [blame] | 215 | } |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 216 | } |
| 217 | } |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 218 | |
| 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 Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 228 | log.debug("Intent Synchronizer: submitting {}, withdrawing {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 229 | intentsToAdd.size(), intentsToRemove.size()); |
| 230 | |
| 231 | // Withdraw Intents |
| 232 | for (Intent intent : intentsToRemove) { |
| 233 | intentService.withdraw(intent); |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 234 | log.trace("Intent Synchronizer: withdrawing intent: {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 235 | intent); |
| 236 | } |
| 237 | if (!isElectedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 238 | log.debug("Intent Synchronizer: cannot withdraw intents: " + |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 239 | "not elected leader anymore"); |
| 240 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 241 | return; |
| 242 | } |
| 243 | |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 244 | // Add Intents |
| 245 | for (Intent intent : intentsToAdd) { |
| 246 | intentService.submit(intent); |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 247 | log.trace("Intent Synchronizer: submitting intent: {}", |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 248 | intent); |
| 249 | } |
| 250 | if (!isElectedLeader) { |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 251 | log.debug("Intent Synchronizer: cannot submit intents: " + |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 252 | "not elected leader anymore"); |
| 253 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 254 | return; |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | if (isElectedLeader) { |
| 258 | isActivatedLeader = true; // Allow push of Intents |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 259 | } else { |
Jonathan Hart | 9a426f8 | 2015-09-03 15:43:13 +0200 | [diff] [blame] | 260 | isActivatedLeader = false; |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 261 | } |
Luca Prete | 00043db | 2015-11-03 15:40:40 -0800 | [diff] [blame^] | 262 | log.debug("Intent synchronization completed"); |
Pingping Lin | e28ae4c | 2015-03-13 11:37:03 -0700 | [diff] [blame] | 263 | } |
| 264 | |
Pavlin Radoslavov | a071b1e | 2014-11-17 13:37:57 -0800 | [diff] [blame] | 265 | } |