blob: 67f39f21f8631a3d844b1c03f2e86fee5951c084 [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.cli.net.vnet;
18
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.apache.karaf.shell.api.action.Argument;
20import org.apache.karaf.shell.api.action.Command;
Ray Milkey0068fd02018-10-11 15:45:39 -070021import org.apache.karaf.shell.api.action.Completion;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070022import org.apache.karaf.shell.api.action.lifecycle.Service;
23import org.apache.karaf.shell.api.action.Option;
Brian Stanke11f6d532016-07-05 16:17:59 -040024import org.onosproject.cli.AbstractShellCommand;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.incubator.net.virtual.NetworkId;
28import org.onosproject.incubator.net.virtual.VirtualNetworkService;
29import org.onosproject.net.intent.Intent;
30import org.onosproject.net.intent.IntentEvent;
31import org.onosproject.net.intent.IntentListener;
32import org.onosproject.net.intent.IntentService;
33import org.onosproject.net.intent.IntentState;
34import org.onosproject.net.intent.Key;
35
36import java.math.BigInteger;
37import java.util.EnumSet;
38import java.util.Objects;
39import java.util.concurrent.CountDownLatch;
40import java.util.concurrent.TimeUnit;
41
42import static com.google.common.base.Strings.isNullOrEmpty;
43import static org.onosproject.net.intent.IntentState.FAILED;
44import static org.onosproject.net.intent.IntentState.WITHDRAWN;
45
46/**
47 * Removes a virtual network intent.
48 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070049@Service
Brian Stanke11f6d532016-07-05 16:17:59 -040050@Command(scope = "onos", name = "remove-vnet-intent",
51 description = "Removes the virtual network intent")
52public class VirtualNetworkIntentRemoveCommand extends AbstractShellCommand {
53
54 @Argument(index = 0, name = "networkId", description = "Network ID",
55 required = true, multiValued = false)
Ray Milkey0068fd02018-10-11 15:45:39 -070056 @Completion(VirtualNetworkCompleter.class)
Brian Stanke11f6d532016-07-05 16:17:59 -040057 Long networkId = null;
58
59 @Argument(index = 1, name = "app",
60 description = "Application ID",
61 required = false, multiValued = false)
62 String applicationIdString = null;
63
64 @Argument(index = 2, name = "key",
65 description = "Intent Key",
66 required = false, multiValued = false)
67 String keyString = null;
68
69 @Option(name = "-p", aliases = "--purge",
70 description = "Purge the intent from the store after removal",
71 required = false, multiValued = false)
72 private boolean purgeAfterRemove = false;
73
74 @Option(name = "-s", aliases = "--sync",
75 description = "Waits for the removal before returning",
76 required = false, multiValued = false)
77 private boolean sync = false;
78
79 private static final EnumSet<IntentState> CAN_PURGE = EnumSet.of(WITHDRAWN, FAILED);
80
81 @Override
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 protected void doExecute() {
Brian Stanke11f6d532016-07-05 16:17:59 -040083 VirtualNetworkService service = get(VirtualNetworkService.class);
84 IntentService intentService = service.get(NetworkId.networkId(networkId), IntentService.class);
85 CoreService coreService = get(CoreService.class);
86
87 if (purgeAfterRemove || sync) {
88 print("Using \"sync\" to remove/purge intents - this may take a while...");
89 print("Check \"summary\" to see remove/purge progress.");
90 }
91
92 ApplicationId appId = appId();
93 if (!isNullOrEmpty(applicationIdString)) {
94 appId = coreService.getAppId(applicationIdString);
95 if (appId == null) {
96 print("Cannot find application Id %s", applicationIdString);
97 return;
98 }
99 }
100
101 if (isNullOrEmpty(keyString)) {
102 for (Intent intent : intentService.getIntents()) {
103 if (intent.appId().equals(appId)) {
104 removeIntent(intentService, intent);
105 }
106 }
107
108 } else {
109 final Key key;
110 if (keyString.startsWith("0x")) {
111 // The intent uses a LongKey
112 keyString = keyString.replaceFirst("0x", "");
113 key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
114 } else {
115 // The intent uses a StringKey
116 key = Key.of(keyString, appId);
117 }
118
119 Intent intent = intentService.getIntent(key);
120 if (intent != null) {
121 removeIntent(intentService, intent);
122 } else {
123 print("Intent not found!");
124 }
125 }
126 }
127
128 /**
129 * Removes the intent using the specified intentService.
130 *
131 * @param intentService intent service
132 * @param intent intent
133 */
134 private void removeIntent(IntentService intentService, Intent intent) {
135 IntentListener listener = null;
136 Key key = intent.key();
137 final CountDownLatch withdrawLatch, purgeLatch;
138 if (purgeAfterRemove || sync) {
139 // set up latch and listener to track uninstall progress
140 withdrawLatch = new CountDownLatch(1);
141 purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
142 listener = (IntentEvent event) -> {
143 if (Objects.equals(event.subject().key(), key)) {
144 if (event.type() == IntentEvent.Type.WITHDRAWN ||
145 event.type() == IntentEvent.Type.FAILED) {
146 withdrawLatch.countDown();
Ray Milkey74e59132018-01-17 15:24:52 -0800147 } else if (purgeLatch != null && purgeAfterRemove &&
Brian Stanke11f6d532016-07-05 16:17:59 -0400148 event.type() == IntentEvent.Type.PURGED) {
149 purgeLatch.countDown();
150 }
151 }
152 };
153 intentService.addListener(listener);
154 } else {
155 purgeLatch = null;
156 withdrawLatch = null;
157 }
158
159 // request the withdraw
160 intentService.withdraw(intent);
161
Ray Milkey74e59132018-01-17 15:24:52 -0800162 if ((purgeAfterRemove || sync) && purgeLatch != null) {
Brian Stanke11f6d532016-07-05 16:17:59 -0400163 try { // wait for withdraw event
164 withdrawLatch.await(5, TimeUnit.SECONDS);
165 } catch (InterruptedException e) {
166 print("Timed out waiting for intent {} withdraw", key);
167 }
168 if (purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
169 intentService.purge(intent);
170 if (sync) { // wait for purge event
171 /* TODO
172 Technically, the event comes before map.remove() is called.
173 If we depend on sync and purge working together, we will
174 need to address this.
175 */
176 try {
177 purgeLatch.await(5, TimeUnit.SECONDS);
178 } catch (InterruptedException e) {
179 print("Timed out waiting for intent {} purge", key);
180 }
181 }
182 }
183 }
184
185 if (listener != null) {
186 // clean up the listener
187 intentService.removeListener(listener);
188 }
189 }
190}