blob: 20c8b34c9c1148347e1625278f6bd587986876e2 [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;
tomf5c9d922014-10-03 15:22:03 -070026import org.apache.karaf.shell.commands.Command;
Brian O'Connorfe0f4b12014-10-30 21:19:02 -070027import org.apache.karaf.shell.commands.Option;
Carolina Fernandezfa56d142016-11-14 01:13:26 +010028import org.onlab.util.StringFilter;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080029import org.onlab.util.Tools;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.cli.AbstractShellCommand;
DongRyeol Cha4df49482018-04-02 17:22:47 +090031import org.onosproject.cluster.NodeId;
Luca Prete5db2e872016-12-07 18:31:00 -080032import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.FilteredConnectPoint;
34import org.onosproject.net.flow.TrafficSelector;
35import org.onosproject.net.flow.criteria.Criterion;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.intent.ConnectivityIntent;
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -080037import org.onosproject.net.intent.HostToHostIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentService;
40import org.onosproject.net.intent.IntentState;
41import org.onosproject.net.intent.LinkCollectionIntent;
DongRyeol Cha4df49482018-04-02 17:22:47 +090042import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.intent.MultiPointToSinglePointIntent;
Rimon Ashkenazyf0699702016-01-17 19:28:49 +020044import org.onosproject.net.intent.OpticalCircuitIntent;
45import org.onosproject.net.intent.OpticalConnectivityIntent;
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +020046import org.onosproject.net.intent.OpticalOduIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.intent.PathIntent;
48import org.onosproject.net.intent.PointToPointIntent;
49import org.onosproject.net.intent.SinglePointToMultiPointIntent;
DongRyeol Cha4df49482018-04-02 17:22:47 +090050import org.onosproject.net.intent.WorkPartitionService;
Thomas Vachuska6ce73042014-10-21 10:01:49 -070051
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080052import static com.google.common.base.MoreObjects.firstNonNull;
53import static java.lang.String.format;
54import static org.apache.commons.lang3.text.WordUtils.uncapitalize;
55
Luca Prete5db2e872016-12-07 18:31:00 -080056import java.util.ArrayList;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080057import java.util.HashMap;
Luca Prete5db2e872016-12-07 18:31:00 -080058import java.util.List;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080059import java.util.Map;
Luca Prete5db2e872016-12-07 18:31:00 -080060import java.util.Set;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -080061import java.util.stream.Collectors;
tomf5c9d922014-10-03 15:22:03 -070062
63/**
64 * Lists the inventory of intents and their states.
65 */
66@Command(scope = "onos", name = "intents",
67 description = "Lists the inventory of intents and their states")
68public class IntentsListCommand extends AbstractShellCommand {
69
Luca Prete5db2e872016-12-07 18:31:00 -080070 // Color codes and style
71 private static final String BOLD = "\u001B[1m";
72 private static final String RESET = "\u001B[0m";
73
74 // Messages and string formatter
75 private static final String APP_ID = BOLD + "Application Id:" + RESET + " %s";
76
DongRyeol Cha4df49482018-04-02 17:22:47 +090077 private static final String LEADER_ID = BOLD + "Leader Id:" + RESET + " %s";
78
Luca Prete5db2e872016-12-07 18:31:00 -080079 private static final String COMMON_SELECTOR = BOLD + "Common ingress " +
80 "selector:" + RESET + " %s";
81
82 private static final String CP = BOLD + "Connect Point:" + RESET + " %s";
83
84 private static final String CONSTRAINTS = BOLD + "Constraints:" + RESET + " %s";
85
86 private static final String DST = BOLD + "Destination " + RESET;
87
88 private static final String EGRESS = BOLD + "Egress ";
89
90 private static final String FILTERED_CPS = "connect points and individual selectors" + RESET;
91
92 private static final String HOST = "host:" + RESET + " %s";
93
94 private static final String ID = BOLD + "Id:" + RESET + " %s";
95
96 private static final String INHERITED = "Inherited";
97
98 private static final String INGRESS = BOLD + "Ingress ";
99
100 private static final String INDENTATION = " -> ";
101
102 private static final String INSTALLABLE = BOLD + "Installable:" + RESET + " %s";
103
104 private static final String KEY = BOLD + "Key:" + RESET + " %s";
105
DongRyeol Cha4df49482018-04-02 17:22:47 +0900106 private static final String NONE = "None";
107
Luca Prete5db2e872016-12-07 18:31:00 -0800108 private static final String RESOURCES = BOLD + "Resources:" + RESET + " %s";
109
110 private static final String SELECTOR = BOLD + "Selector:" + RESET + " %s";
111
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800112 private static final String SEPARATOR = StringUtils.repeat("-", 172);
Luca Prete5db2e872016-12-07 18:31:00 -0800113
114 private static final String SPACE = " ";
115
116 private static final String SRC = BOLD + "Source ";
117
118 private static final String STATE = BOLD + "State:" + RESET + " %s";
119
120 private static final String TREATMENT = BOLD + "Treatment:" + RESET + " %s";
121
122 private static final String TYPE = BOLD + "Intent type:" + RESET + " %s";
123
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800124 /**
125 * {@value #SUMMARY_TITLES}.
126 */
Luca Prete5db2e872016-12-07 18:31:00 -0800127 private static final String SUMMARY_TITLES =
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800128 BOLD + format(
Ray Milkey42b62032018-02-01 11:15:31 -0800129 "%n%1s%21s%14s%14s%14s%14s%14s%14s%14s%14s%14s%14s",
Luca Prete5db2e872016-12-07 18:31:00 -0800130 "Intent type",
131 "Total",
132 "Installed",
133 "Withdrawn",
134 "Failed",
135 "InstallReq",
136 "Compiling",
137 "Installing",
138 "Recompiling",
139 "WithdrawReq",
140 "Withdrawing",
141 "UnknownState") +
142 RESET;
143
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800144 @Option(name = "-i", aliases = "--installable",
145 description = "Output Installable Intents",
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700146 required = false, multiValued = false)
147 private boolean showInstallable = false;
148
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800149 @Option(name = "-s", aliases = "--summary",
150 description = "Intents summary",
151 required = false, multiValued = false)
152 private boolean intentsSummary = false;
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700153
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800154 @Option(name = "-m", aliases = "--mini-summary",
155 description = "Intents mini summary",
156 required = false, multiValued = false)
157 private boolean miniSummary = false;
158
Jonathan Hart34f1e382015-02-24 16:52:23 -0800159 @Option(name = "-p", aliases = "--pending",
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100160 description = "Show information about pending intents",
Jonathan Hart34f1e382015-02-24 16:52:23 -0800161 required = false, multiValued = false)
162 private boolean pending = false;
163
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100164 @Option(name = "-d", aliases = "--details",
165 description = "Show details for intents, filtered by ID",
166 required = false, multiValued = true)
167 private List<String> intentIds = new ArrayList<>();
168
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100169 @Option(name = "-f", aliases = "--filter",
Yuta HIGUCHIfadb9a32017-01-13 09:33:10 -0800170 description = "Filter intents by specific keyword",
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100171 required = false, multiValued = true)
172 private List<String> filter = new ArrayList<>();
173
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100174 @Option(name = "-r", aliases = "--remove",
175 description = "Remove and purge intents by specific keyword",
176 required = false, multiValued = false)
177 private String remove = null;
178
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100179 private StringFilter contentFilter;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800180 private IntentService service;
DongRyeol Cha4df49482018-04-02 17:22:47 +0900181 private WorkPartitionService workPartitionService;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100182
tomf5c9d922014-10-03 15:22:03 -0700183 @Override
184 protected void execute() {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800185 service = get(IntentService.class);
DongRyeol Cha4df49482018-04-02 17:22:47 +0900186 workPartitionService = get(WorkPartitionService.class);
187
188 if (workPartitionService == null) {
189 return;
190 }
191
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100192 contentFilter = new StringFilter(filter, StringFilter.Strategy.AND);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800193
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800194 Iterable<Intent> intents;
195 if (pending) {
196 intents = service.getPending();
197 } else {
198 intents = service.getIntents();
199 }
200
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100201 // Remove intents
202 if (remove != null && !remove.isEmpty()) {
203 filter.add(remove);
204 contentFilter = new StringFilter(filter, StringFilter.Strategy.AND);
205 IntentRemoveCommand intentRemoveCmd = new IntentRemoveCommand();
206 if (!remove.isEmpty()) {
207 intentRemoveCmd.purgeIntentsInteractive(filterIntents(service));
208 }
209 return;
210 }
211
212 // Show detailed intents
213 if (!intentIds.isEmpty()) {
214 IntentDetailsCommand intentDetailsCmd = new IntentDetailsCommand();
215 intentDetailsCmd.detailIntents(intentIds);
216 return;
217 }
218
219 // Show brief intents
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800220 if (intentsSummary || miniSummary) {
221 Map<String, IntentSummary> summarized = summarize(intents);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800222 if (outputJson()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800223 ObjectNode summaries = mapper().createObjectNode();
224 summarized.forEach((n, s) -> summaries.set(uncapitalize(n), s.json(mapper())));
225 print("%s", summaries);
226 } else if (miniSummary) {
227 StringBuilder builder = new StringBuilder();
228 builder.append(summarized.remove("All").miniSummary());
229 summarized.values().forEach(s -> builder.append(s.miniSummary()));
230 print("%s", builder.toString());
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800231 } else {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800232 StringBuilder builder = new StringBuilder();
233 builder.append(SUMMARY_TITLES);
234 builder.append('\n').append(SEPARATOR);
235 builder.append(summarized.remove("All").summary());
236 summarized.values().forEach(s -> builder.append(s.summary()));
237 print("%s", builder.toString());
Ray Milkey740c8a32015-03-17 13:41:03 -0700238 }
Jonathan Hart34f1e382015-02-24 16:52:23 -0800239 return;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800240 }
241
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100242 // JSON or default output
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700243 if (outputJson()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800244 print("%s", json(intents));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700245 } else {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800246 for (Intent intent : intents) {
247 IntentState state = service.getIntentState(intent.key());
248 StringBuilder intentFormat = fullFormat(intent, state);
249 StringBuilder detailsIntentFormat = detailsFormat(intent, state);
250 String formatted = intentFormat.append(detailsIntentFormat).toString();
251 if (contentFilter.filter(formatted)) {
252 print("%s\n", formatted);
253 }
254 }
tomf5c9d922014-10-03 15:22:03 -0700255 }
256 }
257
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800258 /**
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100259 * Filter a given list of intents based on the existing content filter.
260 *
261 * @param service IntentService object
262 * @return further filtered list of intents
263 */
264 private List<Intent> filterIntents(IntentService service) {
265 return filterIntents(service.getIntents());
266 }
267
268 /**
269 * Filter a given list of intents based on the existing content filter.
270 *
271 * @param intents Iterable of intents
272 * @return further filtered list of intents
273 */
274 private List<Intent> filterIntents(Iterable<Intent> intents) {
275 return Tools.stream(intents)
276 .filter(i -> contentFilter.filter(i)).collect(Collectors.toList());
277 }
278
279 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800280 * Internal local class to keep track of a single type Intent summary.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800281 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800282 private class IntentSummary {
283 private final String intentType;
284 private int total = 0;
285 private int installReq = 0;
286 private int compiling = 0;
287 private int installing = 0;
288 private int installed = 0;
289 private int recompiling = 0;
290 private int withdrawReq = 0;
291 private int withdrawing = 0;
292 private int withdrawn = 0;
293 private int failed = 0;
294 private int unknownState = 0;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800295
296 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800297 * Creates empty {@link IntentSummary} for specified {@code intentType}.
298 *
299 * @param intentType the string describing the Intent type
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800300 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800301 IntentSummary(String intentType) {
302 this.intentType = intentType;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800303 }
304
305 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800306 * Creates {@link IntentSummary} initialized with given {@code intent}.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800307 *
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800308 * @param intent to initialize with
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800309 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800310 IntentSummary(Intent intent) {
311 // remove "Intent" from intentType label
312 this(intentType(intent));
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100313 if (contentFilter.filter(intent)) {
314 update(service.getIntentState(intent.key()));
315 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800316 }
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800317
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800318 // for identity element, when reducing
319 IntentSummary() {
320 this.intentType = null;
321 }
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800322
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800323 /**
324 * Updates the Intent Summary.
325 *
326 * @param intentState the state of the intent
327 */
328 void update(IntentState intentState) {
329 total++;
330 switch (intentState) {
331 case INSTALL_REQ:
332 installReq++;
333 break;
334 case COMPILING:
335 compiling++;
336 break;
337 case INSTALLING:
338 installing++;
339 break;
340 case INSTALLED:
341 installed++;
342 break;
343 case RECOMPILING:
344 recompiling++;
345 break;
346 case WITHDRAW_REQ:
347 withdrawReq++;
348 break;
349 case WITHDRAWING:
350 withdrawing++;
351 break;
352 case WITHDRAWN:
353 withdrawn++;
354 break;
355 case FAILED:
356 failed++;
357 break;
358 default:
359 unknownState++;
360 break;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800361 }
362 }
363
364 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800365 * Prints the Intent Summary.
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800366 *
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800367 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800368 StringBuilder summary() {
Luca Prete5db2e872016-12-07 18:31:00 -0800369 StringBuilder builder = new StringBuilder();
Luca Prete5db2e872016-12-07 18:31:00 -0800370
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800371 builder.append(format(
Ray Milkey42b62032018-02-01 11:15:31 -0800372 "%n%1s%s%14d%14d%14d%14d%14d%14d%14d%14d%14d%14d",
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800373 BOLD + intentType + RESET,
374 Strings.padStart(String.valueOf(total),
375 (32 - intentType.length()),
376 ' '),
377 installed,
378 withdrawn,
379 failed,
380 installReq,
381 compiling,
382 installing,
383 recompiling,
384 withdrawReq,
385 withdrawing,
386 unknownState));
387 builder.append('\n').append(SEPARATOR);
388
389 return builder;
390 }
391
392 StringBuilder miniSummary() {
393 StringBuilder builder = new StringBuilder();
394 builder.append(BOLD).append(intentType).append(RESET)
395 .append(" (").append(total).append(')').append('\n');
396 builder.append('\t')
397 .append("installed: ").append(installed).append(' ')
398 .append("withdrawn: ").append(withdrawn).append(' ')
399 .append("failed: ").append(failed)
400 .append('\n');
401 builder.append('\t')
402 .append("compiling: ").append(compiling).append(' ')
403 .append("installing: ").append(installing).append(' ')
404 .append("recompiling: ").append(recompiling).append(' ')
405 .append("withdrawing: ").append(withdrawing)
406 .append('\n');
407 builder.append('\t')
408 .append("installReq: ").append(installReq).append(' ')
409 .append("withdrawReq: ").append(withdrawReq).append(' ')
410 .append("unknownState: ").append(unknownState)
411 .append('\n')
412 .append('\n');
413 return builder;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800414 }
415
416 /**
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800417 * Gets the JSON representation of the Intent Summary.
418 *
419 * @param mapper the object mapper
420 * @return the JSON representation of the Intent Summary
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800421 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800422 JsonNode json(ObjectMapper mapper) {
423 ObjectNode result = mapper.createObjectNode()
424 .put("total", total)
425 .put("installed", installed)
426 .put("failed", failed)
427 .put("installReq", installReq)
428 .put("installing", installing)
429 .put("compiling", compiling)
430 .put("recompiling", recompiling)
431 .put("withdrawReq", withdrawReq)
432 .put("withdrawing", withdrawing)
433 .put("withdrawn", withdrawn)
434 .put("unknownState", unknownState);
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800435
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800436 return result;
Pavlin Radoslavov708e8202014-11-14 17:18:37 -0800437 }
438 }
439
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800440 /**
441 * Merges 2 {@link IntentSummary} together.
442 *
443 * @param a element to merge
444 * @param b element to merge
445 * @return merged {@link IntentSummary}
Luca Prete5db2e872016-12-07 18:31:00 -0800446 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800447 IntentSummary merge(IntentSummary a, IntentSummary b) {
448 IntentSummary m = new IntentSummary(firstNonNull(a.intentType, b.intentType));
449 m.total = a.total + b.total;
450 m.installReq = a.installReq + b.installReq;
451 m.compiling = a.compiling + b.compiling;
452 m.installing = a.installing + b.installing;
453 m.installed = a.installed + b.installed;
454 m.recompiling = a.recompiling + b.recompiling;
455 m.withdrawing = a.withdrawing + b.withdrawing;
456 m.withdrawReq = a.withdrawReq + b.withdrawReq;
457 m.withdrawn = a.withdrawn + b.withdrawn;
458 m.failed = a.failed + b.failed;
459 m.unknownState = a.unknownState + b.unknownState;
460 return m;
461 }
462
463 /**
464 * Returns IntentType string.
465 *
466 * @param intent input
467 * @return IntentType string
468 */
469 private static String intentType(Intent intent) {
470 return intent.getClass().getSimpleName().replace("Intent", "");
471 }
472
473 /**
474 * Build summary of intents per intent type.
475 *
476 * @param intents to summarize
477 * @return summaries per Intent type
478 */
479 private Map<String, IntentSummary> summarize(Iterable<Intent> intents) {
480 Map<String, List<Intent>> perIntent = Tools.stream(intents)
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100481 .collect(Collectors.groupingBy(IntentsListCommand::intentType));
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800482
483 List<IntentSummary> collect = perIntent.values().stream()
484 .map(il ->
485 il.stream()
486 .map(IntentSummary::new)
487 .reduce(new IntentSummary(), this::merge)
488 ).collect(Collectors.toList());
489
490 Map<String, IntentSummary> summaries = new HashMap<>();
491
492 // individual
493 collect.forEach(is -> summaries.put(is.intentType, is));
494
495 // all summarised
496 summaries.put("All", collect.stream()
497 .reduce(new IntentSummary("All"), this::merge));
498 return summaries;
499 }
500
501 /**
502 * Returns detailed information text about a specific intent.
503 *
504 * @param intent to print
505 * @param state of intent
506 * @return detailed information or "" if {@code state} was null
507 */
508 private StringBuilder detailsFormat(Intent intent, IntentState state) {
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100509 StringBuilder builder = new StringBuilder();
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800510 if (state == null) {
511 return builder;
512 }
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800513 if (!intent.resources().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800514 builder.append('\n').append(format(RESOURCES, intent.resources()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700515 }
516 if (intent instanceof ConnectivityIntent) {
517 ConnectivityIntent ci = (ConnectivityIntent) intent;
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700518 if (!ci.selector().criteria().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800519 builder.append('\n').append(format(COMMON_SELECTOR, formatSelector(ci.selector())));
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700520 }
Ray Milkey42507352015-03-20 15:16:10 -0700521 if (!ci.treatment().allInstructions().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800522 builder.append('\n').append(format(TREATMENT, ci.treatment().allInstructions()));
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700523 }
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800524 if (ci.constraints() != null && !ci.constraints().isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800525 builder.append('\n').append(format(CONSTRAINTS, ci.constraints()));
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800526 }
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700527 }
528
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -0800529 if (intent instanceof HostToHostIntent) {
530 HostToHostIntent pi = (HostToHostIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800531 builder.append('\n').append(format(SRC + HOST, pi.one()));
532 builder.append('\n').append(format(DST + HOST, pi.two()));
Pavlin Radoslavovaab51a32014-12-08 11:07:38 -0800533 } else if (intent instanceof PointToPointIntent) {
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700534 PointToPointIntent pi = (PointToPointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800535 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredIngressPoint()), INGRESS));
536 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredEgressPoint()), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700537 } else if (intent instanceof MultiPointToSinglePointIntent) {
538 MultiPointToSinglePointIntent pi = (MultiPointToSinglePointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800539 builder.append('\n').append(formatFilteredCps(pi.filteredIngressPoints(), INGRESS));
540 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredEgressPoint()), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700541 } else if (intent instanceof SinglePointToMultiPointIntent) {
542 SinglePointToMultiPointIntent pi = (SinglePointToMultiPointIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800543 builder.append('\n').append(formatFilteredCps(Sets.newHashSet(pi.filteredIngressPoint()), INGRESS));
544 builder.append('\n').append(formatFilteredCps(pi.filteredEgressPoints(), EGRESS));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700545 } else if (intent instanceof PathIntent) {
546 PathIntent pi = (PathIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800547 builder.append(format("path=%s, cost=%f", pi.path().links(), pi.path().cost()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700548 } else if (intent instanceof LinkCollectionIntent) {
549 LinkCollectionIntent li = (LinkCollectionIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800550 builder.append('\n').append(format("links=%s", li.links()));
551 builder.append('\n').append(format(CP, li.egressPoints()));
Rimon Ashkenazyf0699702016-01-17 19:28:49 +0200552 } else if (intent instanceof OpticalCircuitIntent) {
553 OpticalCircuitIntent ci = (OpticalCircuitIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800554 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200555 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
556 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Rimon Ashkenazyf0699702016-01-17 19:28:49 +0200557 } else if (intent instanceof OpticalConnectivityIntent) {
558 OpticalConnectivityIntent ci = (OpticalConnectivityIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800559 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200560 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
561 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Marc De Leenheeradfeffd2017-06-22 16:05:34 -0700562 builder.append('\n').append(format("ochSignal=%s", ci.ochSignal()));
Rimon Ashkenazy27438ff2016-03-22 15:57:45 +0200563 } else if (intent instanceof OpticalOduIntent) {
564 OpticalOduIntent ci = (OpticalOduIntent) intent;
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800565 builder.append('\n').append(format("src=%s, dst=%s", ci.getSrc(), ci.getDst()));
Andrea Campanella449e63c2017-04-20 18:25:14 +0200566 builder.append('\n').append(format("signal type=%s", ci.getSignalType()));
567 builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700568 }
Thomas Vachuska10d4abc2014-10-21 12:47:26 -0700569
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -0800570 List<Intent> installable = service.getInstallableIntents(intent.key())
571 .stream().filter(i -> contentFilter.filter(i))
572 .collect(Collectors.toList());
Brian O'Connorfe0f4b12014-10-30 21:19:02 -0700573 if (showInstallable && installable != null && !installable.isEmpty()) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800574 builder.append('\n').append(format(INSTALLABLE, installable));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100575 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800576 return builder;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100577 }
578
Luca Prete5db2e872016-12-07 18:31:00 -0800579 /*
580 * Prints out a formatted string, given a list of connect points.
581 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800582 private StringBuilder formatFilteredCps(Set<FilteredConnectPoint> fCps, String prefix) {
Luca Prete5db2e872016-12-07 18:31:00 -0800583 StringBuilder builder = new StringBuilder();
584 builder.append(prefix);
585 builder.append(FILTERED_CPS);
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800586 fCps.forEach(fCp -> builder.append('\n').append(formatFilteredCp(fCp)));
Luca Prete5db2e872016-12-07 18:31:00 -0800587
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800588 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800589 }
590
591 /*
592 * Prints out a formatted string, given a filtered connect point.
593 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800594 private StringBuilder formatFilteredCp(FilteredConnectPoint fCp) {
Luca Prete5db2e872016-12-07 18:31:00 -0800595 ConnectPoint connectPoint = fCp.connectPoint();
596 TrafficSelector selector = fCp.trafficSelector();
597 StringBuilder builder = new StringBuilder();
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800598 builder.append(INDENTATION).append(format(CP, connectPoint));
599 builder.append(SPACE).append(format(SELECTOR, formatSelector(selector)));
Luca Prete5db2e872016-12-07 18:31:00 -0800600
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800601 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800602 }
603
604 /*
605 * Prints out a formatted string, given a traffic selector
606 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800607 private StringBuilder formatSelector(TrafficSelector ts) {
Luca Prete5db2e872016-12-07 18:31:00 -0800608 StringBuilder builder = new StringBuilder();
609 List<Criterion> criteria = Lists.newArrayList(ts.criteria());
610
611 if (criteria == null || criteria.isEmpty()) {
612 builder.append(INHERITED);
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800613 return builder;
Luca Prete5db2e872016-12-07 18:31:00 -0800614 }
615
616 criteria.forEach(c -> {
617 builder.append(c.toString());
618 if (criteria.indexOf(c) < criteria.size() - 1) {
619 builder.append(", ");
620 }
621 });
622
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800623 return builder;
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100624 }
625
Luca Prete5db2e872016-12-07 18:31:00 -0800626 /*
627 * Prints information about the intent state, given an intent.
628 */
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800629 private StringBuilder fullFormat(Intent intent, IntentState state) {
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100630 StringBuilder builder = new StringBuilder();
DongRyeol Cha4df49482018-04-02 17:22:47 +0900631 NodeId nodeId = workPartitionService.getLeader(intent.key(), Key::hash);
632
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800633 builder.append(format(ID, intent.id()));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100634 if (state != null) {
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800635 builder.append('\n').append(format(STATE, state));
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100636 }
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800637 builder.append('\n').append(format(KEY, intent.key()));
638 builder.append('\n').append(format(TYPE, intent.getClass().getSimpleName()));
639 builder.append('\n').append(format(APP_ID, intent.appId().name()));
DongRyeol Cha4df49482018-04-02 17:22:47 +0900640 builder.append('\n').append(nodeId == null ? NONE : format(LEADER_ID, nodeId.id()));
Luca Prete5db2e872016-12-07 18:31:00 -0800641
Yuta HIGUCHIf0e09cd2017-01-27 10:52:36 -0800642 return builder;
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700643 }
644
Luca Prete5db2e872016-12-07 18:31:00 -0800645 /*
646 * Produces a JSON array from the intents specified.
647 */
648 private JsonNode json(Iterable<Intent> intents) {
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700649 ObjectMapper mapper = new ObjectMapper();
650 ArrayNode result = mapper.createArrayNode();
Carolina Fernandez0b1449d2016-12-04 13:30:36 +0100651 Tools.stream(intents)
Carolina Fernandezfa56d142016-11-14 01:13:26 +0100652 .filter(intent -> contentFilter.filter(jsonForEntity(intent, Intent.class).toString()))
653 .forEach(intent -> result.add(jsonForEntity(intent, Intent.class)));
Thomas Vachuska6ce73042014-10-21 10:01:49 -0700654 return result;
655 }
tomf5c9d922014-10-03 15:22:03 -0700656}