blob: e40bf4e0da5740129e739b84519b958d26feea75 [file] [log] [blame]
Brian Stanke11f6d532016-07-05 16:17:59 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Brian Stanke11f6d532016-07-05 16:17:59 -04003 *
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 */
16
17package org.onosproject.incubator.net.virtual.impl;
18
Yoonseon Han096cea02017-05-15 15:10:41 -070019import org.onlab.util.Tools;
yoonseonc6a69272017-01-12 18:22:20 -080020import org.onosproject.incubator.net.virtual.NetworkId;
Brian Stanke11f6d532016-07-05 16:17:59 -040021import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
Yoonseon Han9e043792017-05-03 15:43:33 -070022import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
Brian Stanke11f6d532016-07-05 16:17:59 -040023import org.onosproject.incubator.net.virtual.VirtualNetworkService;
24import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
25import org.onosproject.incubator.net.virtual.VirtualPort;
yoonseon214963b2016-11-21 15:41:07 -080026import org.onosproject.incubator.net.virtual.VnetService;
yoonseonc6a69272017-01-12 18:22:20 -080027import org.onosproject.incubator.net.virtual.event.AbstractVirtualListenerManager;
Yoonseon Han096cea02017-05-15 15:10:41 -070028import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualFinalIntentProcessPhase;
29import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallCoordinator;
30import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentAccumulator;
31import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentCompilerRegistry;
32import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentInstallerRegistry;
33import org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase;
34import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentProcessor;
35import org.onosproject.incubator.net.virtual.impl.intent.VirtualIntentSkipped;
Brian Stanke11f6d532016-07-05 16:17:59 -040036import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.Port;
39import org.onosproject.net.PortNumber;
Yoonseon Han096cea02017-05-15 15:10:41 -070040import org.onosproject.net.group.GroupService;
Brian Stanke11f6d532016-07-05 16:17:59 -040041import org.onosproject.net.intent.Intent;
Yoonseon Han096cea02017-05-15 15:10:41 -070042import org.onosproject.net.intent.IntentBatchDelegate;
Brian Stanke11f6d532016-07-05 16:17:59 -040043import org.onosproject.net.intent.IntentData;
44import org.onosproject.net.intent.IntentEvent;
45import org.onosproject.net.intent.IntentListener;
Yoonseon Han096cea02017-05-15 15:10:41 -070046import org.onosproject.net.intent.IntentStoreDelegate;
Brian Stanke11f6d532016-07-05 16:17:59 -040047import org.onosproject.net.intent.IntentService;
48import org.onosproject.net.intent.IntentState;
49import org.onosproject.net.intent.Key;
Yoonseon Han096cea02017-05-15 15:10:41 -070050import org.onosproject.net.resource.ResourceConsumer;
Brian Stanke11f6d532016-07-05 16:17:59 -040051import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
Yoonseon Han096cea02017-05-15 15:10:41 -070054import java.util.Collection;
Brian Stanke11f6d532016-07-05 16:17:59 -040055import java.util.List;
Yoonseon Han096cea02017-05-15 15:10:41 -070056import java.util.Objects;
Brian Stanke11f6d532016-07-05 16:17:59 -040057import java.util.Optional;
Yoonseon Han096cea02017-05-15 15:10:41 -070058import java.util.concurrent.CompletableFuture;
59import java.util.concurrent.ExecutorService;
60import java.util.stream.Collectors;
Brian Stanke11f6d532016-07-05 16:17:59 -040061
62import static com.google.common.base.Preconditions.*;
Yoonseon Han096cea02017-05-15 15:10:41 -070063import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
64import static org.onlab.util.BoundedThreadPool.newSingleThreadExecutor;
65import static org.onlab.util.Tools.groupedThreads;
66import static org.onosproject.incubator.net.virtual.impl.intent.phase.VirtualIntentProcessPhase.newInitialPhase;
67import static org.onosproject.net.intent.IntentState.FAILED;
Brian Stanke11f6d532016-07-05 16:17:59 -040068
69/**
Brian Stankefb61df42016-07-25 11:47:51 -040070 * Intent service implementation built on the virtual network service.
Brian Stanke11f6d532016-07-05 16:17:59 -040071 */
yoonseon214963b2016-11-21 15:41:07 -080072public class VirtualNetworkIntentManager
yoonseonc6a69272017-01-12 18:22:20 -080073 extends AbstractVirtualListenerManager<IntentEvent, IntentListener>
Brian Stanke11f6d532016-07-05 16:17:59 -040074 implements IntentService, VnetService {
75
76 private final Logger log = LoggerFactory.getLogger(getClass());
77
Yoonseon Han096cea02017-05-15 15:10:41 -070078 private static final int DEFAULT_NUM_THREADS = 12;
79 private int numThreads = DEFAULT_NUM_THREADS;
80
Brian Stanke11f6d532016-07-05 16:17:59 -040081 private static final String NETWORK_ID_NULL = "Network ID cannot be null";
82 private static final String DEVICE_NULL = "Device cannot be null";
83 private static final String INTENT_NULL = "Intent cannot be null";
84 private static final String KEY_NULL = "Key cannot be null";
85 private static final String APP_ID_NULL = "Intent app identifier cannot be null";
86 private static final String INTENT_KEY_NULL = "Intent key cannot be null";
87 private static final String CP_NULL = "Connect Point cannot be null";
88
Yoonseon Han096cea02017-05-15 15:10:41 -070089 //FIXME: Tracker service for vnet.
90
91 //ONOS core services
92 protected VirtualNetworkStore virtualNetworkStore;
Yoonseon Han9e043792017-05-03 15:43:33 -070093 protected VirtualNetworkIntentStore intentStore;
Yoonseon Han096cea02017-05-15 15:10:41 -070094
95 //Virtual network services
96 protected GroupService groupService;
97
98 private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
99 private final InternalIntentProcessor processor = new InternalIntentProcessor();
100 private final IntentStoreDelegate delegate = new InternalStoreDelegate();
101 private final VirtualIntentCompilerRegistry compilerRegistry =
102 VirtualIntentCompilerRegistry.getInstance();
103 private final VirtualIntentInstallerRegistry installerRegistry =
104 VirtualIntentInstallerRegistry.getInstance();
105 private final VirtualIntentAccumulator accumulator =
106 new VirtualIntentAccumulator(batchDelegate);
107
108 private VirtualIntentInstallCoordinator installCoordinator;
109 private ExecutorService batchExecutor;
110 private ExecutorService workerExecutor;
Brian Stanke11f6d532016-07-05 16:17:59 -0400111
Brian Stanke11f6d532016-07-05 16:17:59 -0400112 /**
113 * Creates a new VirtualNetworkIntentService object.
114 *
115 * @param virtualNetworkManager virtual network manager service
yoonseonc6a69272017-01-12 18:22:20 -0800116 * @param networkId a virtual network identifier
Brian Stanke11f6d532016-07-05 16:17:59 -0400117 */
yoonseon214963b2016-11-21 15:41:07 -0800118 public VirtualNetworkIntentManager(VirtualNetworkService virtualNetworkManager,
yoonseonc6a69272017-01-12 18:22:20 -0800119 NetworkId networkId) {
120
Yoonseon Hanb14461b2017-03-07 14:08:01 +0900121 super(virtualNetworkManager, networkId, IntentEvent.class);
yoonseonc6a69272017-01-12 18:22:20 -0800122
Yoonseon Han096cea02017-05-15 15:10:41 -0700123 this.virtualNetworkStore = serviceDirectory.get(VirtualNetworkStore.class);
Yoonseon Han9e043792017-05-03 15:43:33 -0700124 this.intentStore = serviceDirectory.get(VirtualNetworkIntentStore.class);
Yoonseon Han096cea02017-05-15 15:10:41 -0700125
126 this.groupService = manager.get(networkId, GroupService.class);
127
128 intentStore.setDelegate(networkId, delegate);
129 batchExecutor = newSingleThreadExecutor(groupedThreads("onos/intent", "batch", log));
130 workerExecutor = newFixedThreadPool(numThreads, groupedThreads("onos/intent", "worker-%d", log));
131
132 installCoordinator = new VirtualIntentInstallCoordinator(networkId, installerRegistry, intentStore);
133 log.info("Started");
134
Brian Stanke11f6d532016-07-05 16:17:59 -0400135 }
136
137 @Override
138 public void submit(Intent intent) {
139 checkNotNull(intent, INTENT_NULL);
140 checkState(intent instanceof VirtualNetworkIntent, "Only VirtualNetworkIntent is supported.");
141 checkArgument(validateIntent((VirtualNetworkIntent) intent), "Invalid Intent");
142
Yoonseon Han096cea02017-05-15 15:10:41 -0700143 IntentData data = IntentData.submit(intent);
144 intentStore.addPending(networkId, data);
Brian Stanke11f6d532016-07-05 16:17:59 -0400145 }
146
147 /**
148 * Returns true if the virtual network intent is valid.
149 *
150 * @param intent virtual network intent
151 * @return true if intent is valid
152 */
153 private boolean validateIntent(VirtualNetworkIntent intent) {
154 checkNotNull(intent, INTENT_NULL);
155 checkNotNull(intent.networkId(), NETWORK_ID_NULL);
156 checkNotNull(intent.appId(), APP_ID_NULL);
157 checkNotNull(intent.key(), INTENT_KEY_NULL);
158 ConnectPoint ingressPoint = intent.ingressPoint();
159 ConnectPoint egressPoint = intent.egressPoint();
160
161 return (validateConnectPoint(ingressPoint) && validateConnectPoint(egressPoint));
162 }
163
164 /**
165 * Returns true if the connect point is valid.
166 *
167 * @param connectPoint connect point
168 * @return true if connect point is valid
169 */
170 private boolean validateConnectPoint(ConnectPoint connectPoint) {
171 checkNotNull(connectPoint, CP_NULL);
172 Port port = getPort(connectPoint.deviceId(), connectPoint.port());
Yoonseon Han9e043792017-05-03 15:43:33 -0700173 return port != null;
Brian Stanke11f6d532016-07-05 16:17:59 -0400174 }
175
176 /**
177 * Returns the virtual port for the given device identifier and port number.
178 *
179 * @param deviceId virtual device identifier
180 * @param portNumber virtual port number
181 * @return virtual port
182 */
183 private Port getPort(DeviceId deviceId, PortNumber portNumber) {
184 checkNotNull(deviceId, DEVICE_NULL);
185
yoonseonc6a69272017-01-12 18:22:20 -0800186 Optional<VirtualPort> foundPort = manager.getVirtualPorts(this.networkId(), deviceId)
Brian Stanke11f6d532016-07-05 16:17:59 -0400187 .stream()
188 .filter(port -> port.number().equals(portNumber))
189 .findFirst();
190 if (foundPort.isPresent()) {
191 return foundPort.get();
192 }
193 return null;
194 }
195
196 @Override
197 public void withdraw(Intent intent) {
198 checkNotNull(intent, INTENT_NULL);
Yoonseon Han096cea02017-05-15 15:10:41 -0700199 IntentData data = IntentData.withdraw(intent);
200 intentStore.addPending(networkId, data);
Brian Stanke11f6d532016-07-05 16:17:59 -0400201 }
202
203 @Override
204 public void purge(Intent intent) {
205 checkNotNull(intent, INTENT_NULL);
Brian Stanke11f6d532016-07-05 16:17:59 -0400206
Yoonseon Han096cea02017-05-15 15:10:41 -0700207 IntentData data = IntentData.purge(intent);
208 intentStore.addPending(networkId, data);
209
210 // remove associated group if there is one
211 // FIXME: Remove P2P intent for vnets
Brian Stanke11f6d532016-07-05 16:17:59 -0400212 }
213
214 @Override
215 public Intent getIntent(Key key) {
216 checkNotNull(key, KEY_NULL);
Yoonseon Han9e043792017-05-03 15:43:33 -0700217 return intentStore.getIntent(networkId, key);
Brian Stanke11f6d532016-07-05 16:17:59 -0400218 }
219
220 @Override
221 public Iterable<Intent> getIntents() {
Yoonseon Han9e043792017-05-03 15:43:33 -0700222 return intentStore.getIntents(networkId);
Brian Stanke11f6d532016-07-05 16:17:59 -0400223 }
224
225 @Override
Brian O'Connor38224302016-08-02 22:03:01 -0700226 public void addPending(IntentData intentData) {
Yoonseon Han096cea02017-05-15 15:10:41 -0700227 checkNotNull(intentData, INTENT_NULL);
228 //TODO we might consider further checking / assertions
229 intentStore.addPending(networkId, intentData);
Brian O'Connor38224302016-08-02 22:03:01 -0700230 }
231
232 @Override
Brian Stanke11f6d532016-07-05 16:17:59 -0400233 public Iterable<IntentData> getIntentData() {
Yoonseon Han9e043792017-05-03 15:43:33 -0700234 return intentStore.getIntentData(networkId, false, 0);
Brian Stanke11f6d532016-07-05 16:17:59 -0400235 }
236
237 @Override
238 public long getIntentCount() {
Yoonseon Han096cea02017-05-15 15:10:41 -0700239 return intentStore.getIntentCount(networkId);
Brian Stanke11f6d532016-07-05 16:17:59 -0400240 }
241
242 @Override
243 public IntentState getIntentState(Key intentKey) {
244 checkNotNull(intentKey, KEY_NULL);
Yoonseon Han096cea02017-05-15 15:10:41 -0700245 return intentStore.getIntentState(networkId, intentKey);
Brian Stanke11f6d532016-07-05 16:17:59 -0400246 }
247
248 @Override
249 public List<Intent> getInstallableIntents(Key intentKey) {
Yoonseon Han096cea02017-05-15 15:10:41 -0700250 return intentStore.getInstallableIntents(networkId, intentKey);
Brian Stanke11f6d532016-07-05 16:17:59 -0400251 }
252
253 @Override
254 public boolean isLocal(Key intentKey) {
Yoonseon Han096cea02017-05-15 15:10:41 -0700255 return intentStore.isMaster(networkId, intentKey);
Brian Stanke11f6d532016-07-05 16:17:59 -0400256 }
257
258 @Override
259 public Iterable<Intent> getPending() {
Yoonseon Han096cea02017-05-15 15:10:41 -0700260 return intentStore.getPending(networkId);
261 }
262
263 // Store delegate to re-post events emitted from the store.
264 private class InternalStoreDelegate implements IntentStoreDelegate {
265 @Override
266 public void notify(IntentEvent event) {
267 post(event);
268 switch (event.type()) {
269 case WITHDRAWN:
270 //FIXME: release resources
271 break;
272 default:
273 break;
274 }
275 }
276
277 @Override
278 public void process(IntentData data) {
279 accumulator.add(data);
280 }
281
282 @Override
283 public void onUpdate(IntentData intentData) {
284 //FIXME: track intent
285 }
286
287 private void releaseResources(Intent intent) {
288 // If a resource group is set on the intent, the resource consumer is
289 // set equal to it. Otherwise it's set to the intent key
290 ResourceConsumer resourceConsumer =
291 intent.resourceGroup() != null ? intent.resourceGroup() : intent.key();
292
293 // By default the resource doesn't get released
294 boolean removeResource = false;
295
296 if (intent.resourceGroup() == null) {
297 // If the intent doesn't have a resource group, it means the
298 // resource was registered using the intent key, so it can be
299 // released
300 removeResource = true;
301 } else {
302 // When a resource group is set, we make sure there are no other
303 // intents using the same resource group, before deleting the
304 // related resources.
305 Long remainingIntents =
306 Tools.stream(intentStore.getIntents(networkId))
307 .filter(i -> {
308 return i.resourceGroup() != null
309 && i.resourceGroup().equals(intent.resourceGroup());
310 })
311 .count();
312 if (remainingIntents == 0) {
313 removeResource = true;
314 }
315 }
316
317 if (removeResource) {
318 // Release resources allocated to withdrawn intent
319 // FIXME: confirm resources are released
320 }
321 }
322 }
323
324 private class InternalBatchDelegate implements IntentBatchDelegate {
325 @Override
326 public void execute(Collection<IntentData> operations) {
327 log.debug("Execute {} operation(s).", operations.size());
328 log.trace("Execute operations: {}", operations);
329
330 // batchExecutor is single-threaded, so only one batch is in flight at a time
331 CompletableFuture.runAsync(() -> {
332 // process intent until the phase reaches one of the final phases
333 List<CompletableFuture<IntentData>> futures = operations.stream()
334 .map(data -> {
335 log.debug("Start processing of {} {}@{}", data.request(), data.key(), data.version());
336 return data;
337 })
338 .map(x -> CompletableFuture.completedFuture(x)
339 .thenApply(VirtualNetworkIntentManager.this::createInitialPhase)
340 .thenApplyAsync(VirtualIntentProcessPhase::process, workerExecutor)
341 .thenApply(VirtualFinalIntentProcessPhase::data)
342 .exceptionally(e -> {
343 // When the future fails, we update the Intent to simulate the failure of
344 // the installation/withdrawal phase and we save in the current map. In
345 // the next round the CleanUp Thread will pick this Intent again.
346 log.warn("Future failed", e);
347 log.warn("Intent {} - state {} - request {}",
348 x.key(), x.state(), x.request());
349 switch (x.state()) {
350 case INSTALL_REQ:
351 case INSTALLING:
352 case WITHDRAW_REQ:
353 case WITHDRAWING:
354 // TODO should we swtich based on current
355 IntentData current = intentStore.getIntentData(networkId, x.key());
356 return IntentData.nextState(current, FAILED);
357 default:
358 return null;
359 }
360 }))
361 .collect(Collectors.toList());
362
363 // write multiple data to store in order
364 intentStore.batchWrite(networkId, Tools.allOf(futures).join().stream()
365 .filter(Objects::nonNull)
366 .collect(Collectors.toList()));
367 }, batchExecutor).exceptionally(e -> {
368 log.error("Error submitting batches:", e);
369 // FIXME incomplete Intents should be cleaned up
370 // (transition to FAILED, etc.)
371
372 // the batch has failed
373 // TODO: maybe we should do more?
374 log.error("Walk the plank, matey...");
375 return null;
376 }).thenRun(accumulator::ready);
377
378 }
379 }
380
381 private VirtualIntentProcessPhase createInitialPhase(IntentData data) {
382 IntentData pending = intentStore.getPendingData(networkId, data.key());
383 if (pending == null || pending.version().isNewerThan(data.version())) {
384 /*
385 If the pending map is null, then this intent was compiled by a
386 previous batch iteration, so we can skip it.
387 If the pending map has a newer request, it will get compiled as
388 part of the next batch, so we can skip it.
389 */
390 return VirtualIntentSkipped.getPhase();
391 }
392 IntentData current = intentStore.getIntentData(networkId, data.key());
393 return newInitialPhase(networkId, processor, data, current);
394 }
395
396 private class InternalIntentProcessor implements VirtualIntentProcessor {
397 @Override
398 public List<Intent> compile(NetworkId networkId,
399 Intent intent,
400 List<Intent> previousInstallables) {
401 return compilerRegistry.compile(networkId, intent, previousInstallables);
402 }
403
404 @Override
405 public void apply(NetworkId networkId,
406 Optional<IntentData> toUninstall,
407 Optional<IntentData> toInstall) {
408
409 installCoordinator.installIntents(toUninstall, toInstall);
410 }
Brian Stanke11f6d532016-07-05 16:17:59 -0400411 }
Brian Stanke11f6d532016-07-05 16:17:59 -0400412}