blob: 0f0c448f43d09a223933a72e6fd73638ded4b15d [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;
tomf5c9d922014-10-03 15:22:03 -070017
Luca Prete5db2e872016-12-07 18:31:00 -080018import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.ObjectNode;
22import com.google.common.base.Strings;
23import com.google.common.collect.Lists;
24import com.google.common.collect.Sets;
25import org.apache.commons.lang.StringUtils;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070026import org.apache.karaf.shell.api.action.Command;
27import org.apache.karaf.shell.api.action.lifecycle.Service;
28import org.apache.karaf.shell.api.action.Option;
Carolina Fernandezfa56d142016-11-14 01:13:26 +010029import org.onlab.util.StringFilter;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080030import org.onlab.util.Tools;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.cli.AbstractShellCommand;
DongRyeol Cha4df49482018-04-02 17:22:47 +090032import org.onosproject.cluster.NodeId;
Luca Prete5db2e872016-12-07 18:31:00 -080033import org.onosproject.net.ConnectPoint;
34import org.onosproject.net.FilteredConnectPoint;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.criteria.Criterion;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.intent.ConnectivityIntent;
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -080038import org.onosproject.net.intent.HostToHostIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.intent.Intent;
40import org.onosproject.net.intent.IntentService;
41import org.onosproject.net.intent.IntentState;
42import org.onosproject.net.intent.LinkCollectionIntent;
DongRyeol Cha4df49482018-04-02 17:22:47 +090043import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.intent.MultiPointToSinglePointIntent;
Rimon Ashkenazyf0699702016-01-17 19:28:49 +020045import org.onosproject.net.intent.OpticalCircuitIntent;
46import org.onosproject.net.intent.OpticalConnectivityIntent;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020047import org.onosproject.net.intent.OpticalOduIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.intent.PathIntent;
49import org.onosproject.net.intent.PointToPointIntent;
50import org.onosproject.net.intent.SinglePointToMultiPointIntent;
DongRyeol Cha4df49482018-04-02 17:22:47 +090051import org.onosproject.net.intent.WorkPartitionService;
Thomas Vachuska6ce73042014-10-21 10:01:49 -070052
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080053import static com.google.common.base.MoreObjects.firstNonNull;
54import static java.lang.String.format;
55import static org.apache.commons.lang3.text.WordUtils.uncapitalize;
56
Luca Prete5db2e872016-12-07 18:31:00 -080057import java.util.ArrayList;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080058import java.util.HashMap;
Luca Prete5db2e872016-12-07 18:31:00 -080059import java.util.List;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080060import java.util.Map;
Luca Prete5db2e872016-12-07 18:31:00 -080061import java.util.Set;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080062import java.util.stream.Collectors;
tomf5c9d922014-10-03 15:22:03 -070063
64/**
65 * Lists the inventory of intents and their states.
66 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070067@Service
tomf5c9d922014-10-03 15:22:03 -070068@Command(scope = "onos", name = "intents",
69 description = "Lists the inventory of intents and their states")
70public class IntentsListCommand extends AbstractShellCommand {
71
Luca Prete5db2e872016-12-07 18:31:00 -080072 // Color codes and style
73 private static final String BOLD = "\u001B[1m";
74 private static final String RESET = "\u001B[0m";
75
76 // Messages and string formatter
77 private static final String APP_ID = BOLD + "Application Id:" + RESET + " %s";
78
DongRyeol Cha4df49482018-04-02 17:22:47 +090079 private static final String LEADER_ID = BOLD + "Leader Id:" + RESET + " %s";
80
Luca Prete5db2e872016-12-07 18:31:00 -080081 private static final String COMMON_SELECTOR = BOLD + "Common ingress " +
82 "selector:" + RESET + " %s";
83
84 private static final String CP = BOLD + "Connect Point:" + RESET + " %s";
85
86 private static final String CONSTRAINTS = BOLD + "Constraints:" + RESET + " %s";
87
88 private static final String DST = BOLD + "Destination " + RESET;
89
90 private static final String EGRESS = BOLD + "Egress ";
91
92 private static final String FILTERED_CPS = "connect points and individual selectors" + RESET;
93
94 private static final String HOST = "host:" + RESET + " %s";
95
96 private static final String ID = BOLD + "Id:" + RESET + " %s";
97
98 private static final String INHERITED = "Inherited";
99
100 private static final String INGRESS = BOLD + "Ingress ";
101
102 private static final String INDENTATION = " -> ";
103
104 private static final String INSTALLABLE = BOLD + "Installable:" + RESET + " %s";
105
106 private static final String KEY = BOLD + "Key:" + RESET + " %s";
107
DongRyeol Cha4df49482018-04-02 17:22:47 +0900108 private static final String NONE = "None";
109
Luca Prete5db2e872016-12-07 18:31:00 -0800110 private static final String RESOURCES = BOLD + "Resources:" + RESET + " %s";
111
112 private static final String SELECTOR = BOLD + "Selector:" + RESET + " %s";
113
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800114 private static final String SEPARATOR = StringUtils.repeat("-", 172);
Luca Prete5db2e872016-12-07 18:31:00 -0800115
116 private static final String SPACE = " ";
117
118 private static final String SRC = BOLD + "Source ";
119
120 private static final String STATE = BOLD + "State:" + RESET + " %s";
121
122 private static final String TREATMENT = BOLD + "Treatment:" + RESET + " %s";
123
124 private static final String TYPE = BOLD + "Intent type:" + RESET + " %s";
125
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800126 /**
127 * {@value #SUMMARY_TITLES}.
128 */
Luca Prete5db2e872016-12-07 18:31:00 -0800129 private static final String SUMMARY_TITLES =
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800130 BOLD + format(
Ray Milkey42b62032018-02-01 11:15:31 -0800131 "%n%1s%21s%14s%14s%14s%14s%14s%14s%14s%14s%14s%14s",
Luca Prete5db2e872016-12-07 18:31:00 -0800132 "Intent type",
133 "Total",
134 "Installed",
135 "Withdrawn",
136 "Failed",
137 "InstallReq",
138 "Compiling",
139 "Installing",
140 "Recompiling",
141 "WithdrawReq",
142 "Withdrawing",
143 "UnknownState") +
144 RESET;
145
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800146 @Option(name = "-i", aliases = "--installable",
147 description = "Output Installable Intents",
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700148 required = false, multiValued = false)
149 private boolean showInstallable = false;
150
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800151 @Option(name = "-s", aliases = "--summary",
152 description = "Intents summary",
153 required = false, multiValued = false)
154 private boolean intentsSummary = false;
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700155
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800156 @Option(name = "-m", aliases = "--mini-summary",
157 description = "Intents mini summary",
158 required = false, multiValued = false)
159 private boolean miniSummary = false;
160
Jonathan Hart34f1e382015-02-24 16:52:23 -0800161 @Option(name = "-p", aliases = "--pending",
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100162 description = "Show information about pending intents",
Jonathan Hart34f1e382015-02-24 16:52:23 -0800163 required = false, multiValued = false)
164 private boolean pending = false;
165
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100166 @Option(name = "-d", aliases = "--details",
167 description = "Show details for intents, filtered by ID",
168 required = false, multiValued = true)
169 private List<String> intentIds = new ArrayList<>();
170
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100171 @Option(name = "-f", aliases = "--filter",
Yuta HIGUCHIfadb9a32017-01-13 09:33:10 -0800172 description = "Filter intents by specific keyword",
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100173 required = false, multiValued = true)
174 private List<String> filter = new ArrayList<>();
175
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100176 @Option(name = "-r", aliases = "--remove",
177 description = "Remove and purge intents by specific keyword",
178 required = false, multiValued = false)
179 private String remove = null;
180
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100181 private StringFilter contentFilter;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800182 private IntentService service;
DongRyeol Cha4df49482018-04-02 17:22:47 +0900183 private WorkPartitionService workPartitionService;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100184
tomf5c9d922014-10-03 15:22:03 -0700185 @Override
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700186 protected void doExecute() {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800187 service = get(IntentService.class);
DongRyeol Cha4df49482018-04-02 17:22:47 +0900188 workPartitionService = get(WorkPartitionService.class);
189
190 if (workPartitionService == null) {
191 return;
192 }
193
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100194 contentFilter = new StringFilter(filter, StringFilter.Strategy.AND);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800195
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800196 Iterable<Intent> intents;
197 if (pending) {
198 intents = service.getPending();
199 } else {
200 intents = service.getIntents();
201 }
202
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100203 // Remove intents
204 if (remove != null && !remove.isEmpty()) {
205 filter.add(remove);
206 contentFilter = new StringFilter(filter, StringFilter.Strategy.AND);
207 IntentRemoveCommand intentRemoveCmd = new IntentRemoveCommand();
208 if (!remove.isEmpty()) {
209 intentRemoveCmd.purgeIntentsInteractive(filterIntents(service));
210 }
211 return;
212 }
213
214 // Show detailed intents
215 if (!intentIds.isEmpty()) {
216 IntentDetailsCommand intentDetailsCmd = new IntentDetailsCommand();
217 intentDetailsCmd.detailIntents(intentIds);
218 return;
219 }
220
221 // Show brief intents
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800222 if (intentsSummary || miniSummary) {
223 Map<String, IntentSummary> summarized = summarize(intents);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800224 if (outputJson()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800225 ObjectNode summaries = mapper().createObjectNode();
226 summarized.forEach((n, s) -> summaries.set(uncapitalize(n), s.json(mapper())));
227 print("%s", summaries);
228 } else if (miniSummary) {
229 StringBuilder builder = new StringBuilder();
230 builder.append(summarized.remove("All").miniSummary());
231 summarized.values().forEach(s -> builder.append(s.miniSummary()));
232 print("%s", builder.toString());
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800233 } else {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800234 StringBuilder builder = new StringBuilder();
235 builder.append(SUMMARY_TITLES);
236 builder.append('\n').append(SEPARATOR);
237 builder.append(summarized.remove("All").summary());
238 summarized.values().forEach(s -> builder.append(s.summary()));
239 print("%s", builder.toString());
Ray Milkey740c8a32015-03-17 13:41:03 -0700240 }
Jonathan Hart34f1e382015-02-24 16:52:23 -0800241 return;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800242 }
243
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100244 // JSON or default output
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700245 if (outputJson()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800246 print("%s", json(intents));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700247 } else {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800248 for (Intent intent : intents) {
249 IntentState state = service.getIntentState(intent.key());
250 StringBuilder intentFormat = fullFormat(intent, state);
251 StringBuilder detailsIntentFormat = detailsFormat(intent, state);
252 String formatted = intentFormat.append(detailsIntentFormat).toString();
253 if (contentFilter.filter(formatted)) {
254 print("%s\n", formatted);
255 }
256 }
tomf5c9d922014-10-03 15:22:03 -0700257 }
258 }
259
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800260 /**
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100261 * Filter a given list of intents based on the existing content filter.
262 *
263 * @param service IntentService object
264 * @return further filtered list of intents
265 */
266 private List<Intent> filterIntents(IntentService service) {
267 return filterIntents(service.getIntents());
268 }
269
270 /**
271 * Filter a given list of intents based on the existing content filter.
272 *
273 * @param intents Iterable of intents
274 * @return further filtered list of intents
275 */
276 private List<Intent> filterIntents(Iterable<Intent> intents) {
277 return Tools.stream(intents)
278 .filter(i -> contentFilter.filter(i)).collect(Collectors.toList());
279 }
280
281 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800282 * Internal local class to keep track of a single type Intent summary.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800283 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800284 private class IntentSummary {
285 private final String intentType;
286 private int total = 0;
287 private int installReq = 0;
288 private int compiling = 0;
289 private int installing = 0;
290 private int installed = 0;
291 private int recompiling = 0;
292 private int withdrawReq = 0;
293 private int withdrawing = 0;
294 private int withdrawn = 0;
295 private int failed = 0;
296 private int unknownState = 0;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800297
298 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800299 * Creates empty {@link IntentSummary} for specified {@code intentType}.
300 *
301 * @param intentType the string describing the Intent type
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800302 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800303 IntentSummary(String intentType) {
304 this.intentType = intentType;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800305 }
306
307 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800308 * Creates {@link IntentSummary} initialized with given {@code intent}.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800309 *
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800310 * @param intent to initialize with
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800311 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800312 IntentSummary(Intent intent) {
313 // remove "Intent" from intentType label
314 this(intentType(intent));
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100315 if (contentFilter.filter(intent)) {
316 update(service.getIntentState(intent.key()));
317 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800318 }
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800319
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800320 // for identity element, when reducing
321 IntentSummary() {
322 this.intentType = null;
323 }
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800324
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800325 /**
326 * Updates the Intent Summary.
327 *
328 * @param intentState the state of the intent
329 */
330 void update(IntentState intentState) {
331 total++;
332 switch (intentState) {
333 case INSTALL_REQ:
334 installReq++;
335 break;
336 case COMPILING:
337 compiling++;
338 break;
339 case INSTALLING:
340 installing++;
341 break;
342 case INSTALLED:
343 installed++;
344 break;
345 case RECOMPILING:
346 recompiling++;
347 break;
348 case WITHDRAW_REQ:
349 withdrawReq++;
350 break;
351 case WITHDRAWING:
352 withdrawing++;
353 break;
354 case WITHDRAWN:
355 withdrawn++;
356 break;
357 case FAILED:
358 failed++;
359 break;
360 default:
361 unknownState++;
362 break;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800363 }
364 }
365
366 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800367 * Prints the Intent Summary.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800368 *
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800369 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800370 StringBuilder summary() {
Luca Prete5db2e872016-12-07 18:31:00 -0800371 StringBuilder builder = new StringBuilder();
Luca Prete5db2e872016-12-07 18:31:00 -0800372
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800373 builder.append(format(
Ray Milkey42b62032018-02-01 11:15:31 -0800374 "%n%1s%s%14d%14d%14d%14d%14d%14d%14d%14d%14d%14d",
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800375 BOLD + intentType + RESET,
376 Strings.padStart(String.valueOf(total),
377 (32 - intentType.length()),
378 ' '),
379 installed,
380 withdrawn,
381 failed,
382 installReq,
383 compiling,
384 installing,
385 recompiling,
386 withdrawReq,
387 withdrawing,
388 unknownState));
389 builder.append('\n').append(SEPARATOR);
390
391 return builder;
392 }
393
394 StringBuilder miniSummary() {
395 StringBuilder builder = new StringBuilder();
396 builder.append(BOLD).append(intentType).append(RESET)
397 .append(" (").append(total).append(')').append('\n');
398 builder.append('\t')
399 .append("installed: ").append(installed).append(' ')
400 .append("withdrawn: ").append(withdrawn).append(' ')
401 .append("failed: ").append(failed)
402 .append('\n');
403 builder.append('\t')
404 .append("compiling: ").append(compiling).append(' ')
405 .append("installing: ").append(installing).append(' ')
406 .append("recompiling: ").append(recompiling).append(' ')
407 .append("withdrawing: ").append(withdrawing)
408 .append('\n');
409 builder.append('\t')
410 .append("installReq: ").append(installReq).append(' ')
411 .append("withdrawReq: ").append(withdrawReq).append(' ')
412 .append("unknownState: ").append(unknownState)
413 .append('\n')
414 .append('\n');
415 return builder;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800416 }
417
418 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800419 * Gets the JSON representation of the Intent Summary.
420 *
421 * @param mapper the object mapper
422 * @return the JSON representation of the Intent Summary
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800423 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800424 JsonNode json(ObjectMapper mapper) {
425 ObjectNode result = mapper.createObjectNode()
426 .put("total", total)
427 .put("installed", installed)
428 .put("failed", failed)
429 .put("installReq", installReq)
430 .put("installing", installing)
431 .put("compiling", compiling)
432 .put("recompiling", recompiling)
433 .put("withdrawReq", withdrawReq)
434 .put("withdrawing", withdrawing)
435 .put("withdrawn", withdrawn)
436 .put("unknownState", unknownState);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800437
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800438 return result;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800439 }
440 }
441
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800442 /**
443 * Merges 2 {@link IntentSummary} together.
444 *
445 * @param a element to merge
446 * @param b element to merge
447 * @return merged {@link IntentSummary}
Luca Prete5db2e872016-12-07 18:31:00 -0800448 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800449 IntentSummary merge(IntentSummary a, IntentSummary b) {
450 IntentSummary m = new IntentSummary(firstNonNull(a.intentType, b.intentType));
451 m.total = a.total + b.total;
452 m.installReq = a.installReq + b.installReq;
453 m.compiling = a.compiling + b.compiling;
454 m.installing = a.installing + b.installing;
455 m.installed = a.installed + b.installed;
456 m.recompiling = a.recompiling + b.recompiling;
457 m.withdrawing = a.withdrawing + b.withdrawing;
458 m.withdrawReq = a.withdrawReq + b.withdrawReq;
459 m.withdrawn = a.withdrawn + b.withdrawn;
460 m.failed = a.failed + b.failed;
461 m.unknownState = a.unknownState + b.unknownState;
462 return m;
463 }
464
465 /**
466 * Returns IntentType string.
467 *
468 * @param intent input
469 * @return IntentType string
470 */
471 private static String intentType(Intent intent) {
472 return intent.getClass().getSimpleName().replace("Intent", "");
473 }
474
475 /**
476 * Build summary of intents per intent type.
477 *
478 * @param intents to summarize
479 * @return summaries per Intent type
480 */
481 private Map<String, IntentSummary> summarize(Iterable<Intent> intents) {
482 Map<String, List<Intent>> perIntent = Tools.stream(intents)
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100483 .collect(Collectors.groupingBy(IntentsListCommand::intentType));
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800484
485 List<IntentSummary> collect = perIntent.values().stream()
486 .map(il ->
487 il.stream()
488 .map(IntentSummary::new)
489 .reduce(new IntentSummary(), this::merge)
490 ).collect(Collectors.toList());
491
492 Map<String, IntentSummary> summaries = new HashMap<>();
493
494 // individual
495 collect.forEach(is -> summaries.put(is.intentType, is));
496
497 // all summarised
498 summaries.put("All", collect.stream()
499 .reduce(new IntentSummary("All"), this::merge));
500 return summaries;
501 }
502
503 /**
504 * Returns detailed information text about a specific intent.
505 *
506 * @param intent to print
507 * @param state of intent
508 * @return detailed information or "" if {@code state} was null
509 */
510 private StringBuilder detailsFormat(Intent intent, IntentState state) {
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100511 StringBuilder builder = new StringBuilder();
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800512 if (state == null) {
513 return builder;
514 }
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800515 if (!intent.resources().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800516 builder.append('\n').append(format(RESOURCES, intent.resources()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700517 }
518 if (intent instanceof ConnectivityIntent) {
519 ConnectivityIntent ci = (ConnectivityIntent) intent;
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700520 if (!ci.selector().criteria().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800521 builder.append('\n').append(format(COMMON_SELECTOR, formatSelector(ci.selector())));
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700522 }
Ray Milkey42507352015-03-20 15:16:10 -0700523 if (!ci.treatment().allInstructions().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800524 builder.append('\n').append(format(TREATMENT, ci.treatment().allInstructions()));
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700525 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800526 if (ci.constraints() != null && !ci.constraints().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800527 builder.append('\n').append(format(CONSTRAINTS, ci.constraints()));
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800528 }
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700529 }
530
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -0800531 if (intent instanceof HostToHostIntent) {
532 HostToHostIntent pi = (HostToHostIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800533 builder.append('\n').append(format(SRC + HOST, pi.one()));
534 builder.append('\n').append(format(DST + HOST, pi.two()));
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -0800535 } else if (intent instanceof PointToPointIntent) {
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700536 PointToPointIntent pi = (PointToPointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800537 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredIngressPoint()), INGRESS));
538 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredEgressPoint()), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700539 } else if (intent instanceof MultiPointToSinglePointIntent) {
540 MultiPointToSinglePointIntent pi = (MultiPointToSinglePointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800541 builder.append('\n').append(formatFilteredCps(pi.filteredIngressPoints(), INGRESS));
542 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredEgressPoint()), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700543 } else if (intent instanceof SinglePointToMultiPointIntent) {
544 SinglePointToMultiPointIntent pi = (SinglePointToMultiPointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800545 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredIngressPoint()), INGRESS));
546 builder.append('\n').append(formatFilteredCps(pi.filteredEgressPoints(), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700547 } else if (intent instanceof PathIntent) {
548 PathIntent pi = (PathIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800549 builder.append(format("path=%s, cost=%f", pi.path().links(), pi.path().cost()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700550 } else if (intent instanceof LinkCollectionIntent) {
551 LinkCollectionIntent li = (LinkCollectionIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800552 builder.append('\n').append(format("links=%s", li.links()));
553 builder.append('\n').append(format(CP, li.egressPoints()));
Rimon Ashkenazyf0699702016-01-17 19:28:49 +0200554 } else if (intent instanceof OpticalCircuitIntent) {
555 OpticalCircuitIntent ci = (OpticalCircuitIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800556 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200557 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
558 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Rimon Ashkenazyf0699702016-01-17 19:28:49 +0200559 } else if (intent instanceof OpticalConnectivityIntent) {
560 OpticalConnectivityIntent ci = (OpticalConnectivityIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800561 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200562 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
563 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Marc De Leenheeradfeffd2017-06-22 16:05:34 -0700564 builder.append('\n').append(format("ochSignal=%s", ci.ochSignal()));
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200565 } else if (intent instanceof OpticalOduIntent) {
566 OpticalOduIntent ci = (OpticalOduIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800567 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200568 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
569 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700570 }
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700571
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -0800572 List<Intent> installable = service.getInstallableIntents(intent.key())
573 .stream().filter(i -> contentFilter.filter(i))
574 .collect(Collectors.toList());
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700575 if (showInstallable && installable != null && !installable.isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800576 builder.append('\n').append(format(INSTALLABLE, installable));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100577 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800578 return builder;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100579 }
580
Luca Prete5db2e872016-12-07 18:31:00 -0800581 /*
582 * Prints out a formatted string, given a list of connect points.
583 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800584 private StringBuilder formatFilteredCps(Set<FilteredConnectPoint> fCps, String prefix) {
Luca Prete5db2e872016-12-07 18:31:00 -0800585 StringBuilder builder = new StringBuilder();
586 builder.append(prefix);
587 builder.append(FILTERED_CPS);
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800588 fCps.forEach(fCp -> builder.append('\n').append(formatFilteredCp(fCp)));
Luca Prete5db2e872016-12-07 18:31:00 -0800589
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800590 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800591 }
592
593 /*
594 * Prints out a formatted string, given a filtered connect point.
595 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800596 private StringBuilder formatFilteredCp(FilteredConnectPoint fCp) {
Luca Prete5db2e872016-12-07 18:31:00 -0800597 ConnectPoint connectPoint = fCp.connectPoint();
598 TrafficSelector selector = fCp.trafficSelector();
599 StringBuilder builder = new StringBuilder();
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800600 builder.append(INDENTATION).append(format(CP, connectPoint));
601 builder.append(SPACE).append(format(SELECTOR, formatSelector(selector)));
Luca Prete5db2e872016-12-07 18:31:00 -0800602
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800603 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800604 }
605
606 /*
607 * Prints out a formatted string, given a traffic selector
608 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800609 private StringBuilder formatSelector(TrafficSelector ts) {
Luca Prete5db2e872016-12-07 18:31:00 -0800610 StringBuilder builder = new StringBuilder();
611 List<Criterion> criteria = Lists.newArrayList(ts.criteria());
612
613 if (criteria == null || criteria.isEmpty()) {
614 builder.append(INHERITED);
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800615 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800616 }
617
618 criteria.forEach(c -> {
619 builder.append(c.toString());
620 if (criteria.indexOf(c) < criteria.size() - 1) {
621 builder.append(", ");
622 }
623 });
624
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800625 return builder;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100626 }
627
Luca Prete5db2e872016-12-07 18:31:00 -0800628 /*
629 * Prints information about the intent state, given an intent.
630 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800631 private StringBuilder fullFormat(Intent intent, IntentState state) {
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100632 StringBuilder builder = new StringBuilder();
DongRyeol Cha4df49482018-04-02 17:22:47 +0900633 NodeId nodeId = workPartitionService.getLeader(intent.key(), Key::hash);
634
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800635 builder.append(format(ID, intent.id()));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100636 if (state != null) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800637 builder.append('\n').append(format(STATE, state));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100638 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800639 builder.append('\n').append(format(KEY, intent.key()));
640 builder.append('\n').append(format(TYPE, intent.getClass().getSimpleName()));
641 builder.append('\n').append(format(APP_ID, intent.appId().name()));
DongRyeol Cha4df49482018-04-02 17:22:47 +0900642 builder.append('\n').append(nodeId == null ? NONE : format(LEADER_ID, nodeId.id()));
Luca Prete5db2e872016-12-07 18:31:00 -0800643
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800644 return builder;
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700645 }
646
Luca Prete5db2e872016-12-07 18:31:00 -0800647 /*
648 * Produces a JSON array from the intents specified.
649 */
650 private JsonNode json(Iterable<Intent> intents) {
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700651 ObjectMapper mapper = new ObjectMapper();
652 ArrayNode result = mapper.createArrayNode();
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100653 Tools.stream(intents)
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100654 .filter(intent -> contentFilter.filter(jsonForEntity(intent, Intent.class).toString()))
655 .forEach(intent -> result.add(jsonForEntity(intent, Intent.class)));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700656 return result;
657 }
tomf5c9d922014-10-03 15:22:03 -0700658}