blob: b602dfe277a5e1695c1c3ab9155d76085058a40e [file] [log] [blame]
Thomas Vachuska7d693f52014-10-21 19:17:57 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
Thomas Vachuska7d693f52014-10-21 19:17:57 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska7d693f52014-10-21 19:17:57 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska7d693f52014-10-21 19:17:57 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.cli.net;
Brian O'Connora4cab072014-10-03 18:46:39 -070017
Carolina Fernandez0b1449d2016-12-04 13:30:36 +010018import com.google.common.collect.ImmutableList;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.apache.karaf.shell.api.action.Argument;
20import org.apache.karaf.shell.api.action.Command;
21import org.apache.karaf.shell.api.action.lifecycle.Service;
22import org.apache.karaf.shell.api.action.Option;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.cli.AbstractShellCommand;
Ray Milkey02479862015-02-17 17:02:19 -080024import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.net.intent.Intent;
suibin zhangbaa163f2015-08-06 09:22:24 -070027import org.onosproject.net.intent.IntentEvent;
Carolina Fernandez0b1449d2016-12-04 13:30:36 +010028import org.onosproject.net.intent.IntentListener;
29import org.onosproject.net.intent.IntentService;
30import org.onosproject.net.intent.IntentState;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080031import org.onosproject.net.intent.Key;
Brian O'Connora4cab072014-10-03 18:46:39 -070032
Carolina Fernandez0b1449d2016-12-04 13:30:36 +010033import java.io.BufferedReader;
34import java.io.IOException;
35import java.io.InputStreamReader;
Thomas Vachuska4926c1b2014-10-21 00:44:10 -070036import java.math.BigInteger;
suibin zhangbaa163f2015-08-06 09:22:24 -070037import java.util.EnumSet;
Brian O'Connor8016f342015-02-24 17:00:39 -080038import java.util.Objects;
39import java.util.concurrent.CountDownLatch;
40import java.util.concurrent.TimeUnit;
41
Thomas Vachuska33937c42015-07-15 17:40:32 -070042import static com.google.common.base.Strings.isNullOrEmpty;
Brian O'Connor8016f342015-02-24 17:00:39 -080043import static org.onosproject.net.intent.IntentState.FAILED;
44import static org.onosproject.net.intent.IntentState.WITHDRAWN;
Thomas Vachuska4926c1b2014-10-21 00:44:10 -070045
Brian O'Connora4cab072014-10-03 18:46:39 -070046/**
Ray Milkey02479862015-02-17 17:02:19 -080047 * Removes an intent.
Brian O'Connora4cab072014-10-03 18:46:39 -070048 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070049@Service
tom6db1f0a2014-10-07 09:12:29 -070050@Command(scope = "onos", name = "remove-intent",
Thomas Vachuska33937c42015-07-15 17:40:32 -070051 description = "Removes the specified intent")
tom6db1f0a2014-10-07 09:12:29 -070052public class IntentRemoveCommand extends AbstractShellCommand {
Brian O'Connora4cab072014-10-03 18:46:39 -070053
Ray Milkey02479862015-02-17 17:02:19 -080054 @Argument(index = 0, name = "app",
Thomas Vachuska33937c42015-07-15 17:40:32 -070055 description = "Application ID",
56 required = false, multiValued = false)
Ray Milkey02479862015-02-17 17:02:19 -080057 String applicationIdString = null;
58
Brian O'Connor422b8752015-06-04 16:16:49 -070059 @Argument(index = 1, name = "key",
Thomas Vachuska33937c42015-07-15 17:40:32 -070060 description = "Intent Key",
61 required = false, multiValued = false)
Brian O'Connor422b8752015-06-04 16:16:49 -070062 String keyString = null;
Brian O'Connora4cab072014-10-03 18:46:39 -070063
Brian O'Connor8016f342015-02-24 17:00:39 -080064 @Option(name = "-p", aliases = "--purge",
65 description = "Purge the intent from the store after removal",
66 required = false, multiValued = false)
67 private boolean purgeAfterRemove = false;
68
69 @Option(name = "-s", aliases = "--sync",
70 description = "Waits for the removal before returning",
71 required = false, multiValued = false)
72 private boolean sync = false;
73
suibin zhangbaa163f2015-08-06 09:22:24 -070074 private static final EnumSet<IntentState> CAN_PURGE = EnumSet.of(WITHDRAWN, FAILED);
75
Brian O'Connora4cab072014-10-03 18:46:39 -070076 @Override
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077 protected void doExecute() {
Ray Milkey02479862015-02-17 17:02:19 -080078 IntentService intentService = get(IntentService.class);
Carolina Fernandez0b1449d2016-12-04 13:30:36 +010079 removeIntent(intentService.getIntents(),
80 applicationIdString, keyString,
81 purgeAfterRemove, sync);
82 }
Ray Milkey02479862015-02-17 17:02:19 -080083
Carolina Fernandez0b1449d2016-12-04 13:30:36 +010084 /**
85 * Purges the intents passed as argument.
86 *
87 * @param intents list of intents to purge
88 */
89 private void purgeIntents(Iterable<Intent> intents) {
90 IntentService intentService = get(IntentService.class);
91 this.purgeAfterRemove = true;
92 removeIntentsByAppId(intentService, intents, null);
93 }
94
95 /**
96 * Purges the intents passed as argument after confirmation is provided
97 * for each of them.
98 * If no explicit confirmation is provided, the intent is not purged.
99 *
100 * @param intents list of intents to purge
101 */
102 public void purgeIntentsInteractive(Iterable<Intent> intents) {
103 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
104 intents.forEach(intent -> {
105 System.out.print(String.format("Id=%s, Key=%s, AppId=%s. Remove? [y/N]: ",
106 intent.id(), intent.key(), intent.appId().name()));
107 String response;
108 try {
109 response = br.readLine();
110 response = response.trim().replace("\n", "");
Jon Halla3fcf672017-03-28 16:53:22 -0700111 if ("y".equals(response)) {
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100112 this.purgeIntents(ImmutableList.of(intent));
113 }
114 } catch (IOException e) {
115 response = "";
116 }
117 print(response);
118 });
119 }
120
121 /**
122 * Removes the intents passed as argument, assuming these
123 * belong to the application's ID provided (if any) and
124 * contain a key string.
125 *
126 * If an application ID is provided, it will be used to further
127 * filter the intents to be removed.
128 *
129 * @param intents list of intents to remove
130 * @param applicationIdString application ID to filter intents
131 * @param keyString string to filter intents
132 * @param purgeAfterRemove states whether the intents should be also purged
133 * @param sync states whether the cli should wait for the operation to finish
134 * before returning
135 */
136 private void removeIntent(Iterable<Intent> intents,
137 String applicationIdString, String keyString,
138 boolean purgeAfterRemove, boolean sync) {
139 IntentService intentService = get(IntentService.class);
140 CoreService coreService = get(CoreService.class);
141 this.applicationIdString = applicationIdString;
142 this.keyString = keyString;
143 this.purgeAfterRemove = purgeAfterRemove;
144 this.sync = sync;
suibin zhang5e897b72015-08-06 10:02:18 -0700145 if (purgeAfterRemove || sync) {
146 print("Using \"sync\" to remove/purge intents - this may take a while...");
147 print("Check \"summary\" to see remove/purge progress.");
suibin zhangbaa163f2015-08-06 09:22:24 -0700148 }
149
Ray Milkey02479862015-02-17 17:02:19 -0800150 ApplicationId appId = appId();
Thomas Vachuska33937c42015-07-15 17:40:32 -0700151 if (!isNullOrEmpty(applicationIdString)) {
Ray Milkey02479862015-02-17 17:02:19 -0800152 appId = coreService.getAppId(applicationIdString);
153 if (appId == null) {
154 print("Cannot find application Id %s", applicationIdString);
155 return;
156 }
157 }
Brian O'Connora4cab072014-10-03 18:46:39 -0700158
Thomas Vachuska33937c42015-07-15 17:40:32 -0700159 if (isNullOrEmpty(keyString)) {
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100160 removeIntentsByAppId(intentService, intents, appId);
Brian O'Connor8016f342015-02-24 17:00:39 -0800161
Thomas Vachuska33937c42015-07-15 17:40:32 -0700162 } else {
163 final Key key;
164 if (keyString.startsWith("0x")) {
165 // The intent uses a LongKey
166 keyString = keyString.replaceFirst("0x", "");
167 key = Key.of(new BigInteger(keyString, 16).longValue(), appId);
168 } else {
169 // The intent uses a StringKey
170 key = Key.of(keyString, appId);
171 }
Brian O'Connor8016f342015-02-24 17:00:39 -0800172
Thomas Vachuska33937c42015-07-15 17:40:32 -0700173 Intent intent = intentService.getIntent(key);
174 if (intent != null) {
175 removeIntent(intentService, intent);
176 }
177 }
178 }
179
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100180 /**
181 * Removes the intents passed as argument.
182 *
183 * If an application ID is provided, it will be used to further
184 * filter the intents to be removed.
185 *
186 * @param intentService IntentService object
187 * @param intents intents to remove
188 * @param appId application ID to filter intents
189 */
190 private void removeIntentsByAppId(IntentService intentService,
191 Iterable<Intent> intents,
192 ApplicationId appId) {
193 for (Intent intent : intents) {
194 if (appId == null || intent.appId().equals(appId)) {
195 removeIntent(intentService, intent);
196 }
197 }
198 }
199
200 /**
201 * Removes the intent passed as argument.
202 *
203 * @param intentService IntentService object
204 * @param intent intent to remove
205 */
Thomas Vachuska33937c42015-07-15 17:40:32 -0700206 private void removeIntent(IntentService intentService, Intent intent) {
207 IntentListener listener = null;
208 Key key = intent.key();
209 final CountDownLatch withdrawLatch, purgeLatch;
210 if (purgeAfterRemove || sync) {
211 // set up latch and listener to track uninstall progress
212 withdrawLatch = new CountDownLatch(1);
213 purgeLatch = purgeAfterRemove ? new CountDownLatch(1) : null;
214 listener = (IntentEvent event) -> {
215 if (Objects.equals(event.subject().key(), key)) {
216 if (event.type() == IntentEvent.Type.WITHDRAWN ||
217 event.type() == IntentEvent.Type.FAILED) {
218 withdrawLatch.countDown();
Ray Milkey74e59132018-01-17 15:24:52 -0800219 } else if (purgeLatch != null && purgeAfterRemove &&
Thomas Vachuska33937c42015-07-15 17:40:32 -0700220 event.type() == IntentEvent.Type.PURGED) {
221 purgeLatch.countDown();
222 }
Brian O'Connor8016f342015-02-24 17:00:39 -0800223 }
Thomas Vachuska33937c42015-07-15 17:40:32 -0700224 };
225 intentService.addListener(listener);
226 } else {
227 purgeLatch = null;
228 withdrawLatch = null;
229 }
230
231 // request the withdraw
232 intentService.withdraw(intent);
233
Ray Milkey74e59132018-01-17 15:24:52 -0800234 if (withdrawLatch != null && (purgeAfterRemove || sync)) {
Thomas Vachuska33937c42015-07-15 17:40:32 -0700235 try { // wait for withdraw event
236 withdrawLatch.await(5, TimeUnit.SECONDS);
237 } catch (InterruptedException e) {
238 print("Timed out waiting for intent {} withdraw", key);
239 }
Ray Milkey74e59132018-01-17 15:24:52 -0800240 if (purgeLatch != null && purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
Thomas Vachuska33937c42015-07-15 17:40:32 -0700241 intentService.purge(intent);
suibin zhangbaa163f2015-08-06 09:22:24 -0700242 if (sync) { // wait for purge event
Ray Milkey8c6d00e2015-03-13 14:14:34 -0700243 /* TODO
244 Technically, the event comes before map.remove() is called.
245 If we depend on sync and purge working together, we will
246 need to address this.
247 */
suibin zhangbaa163f2015-08-06 09:22:24 -0700248 try {
249 purgeLatch.await(5, TimeUnit.SECONDS);
250 } catch (InterruptedException e) {
251 print("Timed out waiting for intent {} purge", key);
252 }
Brian O'Connor8016f342015-02-24 17:00:39 -0800253 }
254 }
Thomas Vachuska33937c42015-07-15 17:40:32 -0700255 }
Brian O'Connor8016f342015-02-24 17:00:39 -0800256
Thomas Vachuska33937c42015-07-15 17:40:32 -0700257 if (listener != null) {
258 // clean up the listener
259 intentService.removeListener(listener);
Ray Milkey8c6d00e2015-03-13 14:14:34 -0700260 }
Brian O'Connora4cab072014-10-03 18:46:39 -0700261 }
262}