This is an automated email from the ASF dual-hosted git repository.

namelchev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new d28bb13b862 IGNITE-18278 Add the ability to get a view from multiple 
nodes in the control utility. (#10405)
d28bb13b862 is described below

commit d28bb13b8622913482548772e320fbd5ef17ef83
Author: Nikita Amelchev <[email protected]>
AuthorDate: Wed Nov 30 18:51:42 2022 +0300

    IGNITE-18278 Add the ability to get a view from multiple nodes in the 
control utility. (#10405)
---
 .../ignite/internal/commandline/TaskExecutor.java  |   2 +-
 .../commandline/systemview/SystemViewCommand.java  | 116 ++++++++++++-----
 .../systemview/SystemViewCommandArg.java           |  15 ++-
 .../apache/ignite/util/SystemViewCommandTest.java  | 142 +++++++++++++++++----
 .../visor/systemview/VisorSystemViewTask.java      |  33 ++++-
 .../systemview/VisorSystemViewTaskResult.java      |  29 ++---
 ...ridCommandHandlerClusterByClassTest_help.output |   8 +-
 ...andHandlerClusterByClassWithSSLTest_help.output |   8 +-
 8 files changed, 269 insertions(+), 84 deletions(-)

diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/TaskExecutor.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/TaskExecutor.java
index c798d45fc63..b4f88cb4e84 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/TaskExecutor.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/TaskExecutor.java
@@ -187,7 +187,7 @@ public class TaskExecutor {
      * @param compute instance
      * @return balanced node
      */
-    private static GridClientNode getBalancedNode(GridClientCompute compute) 
throws GridClientException {
+    public static GridClientNode getBalancedNode(GridClientCompute compute) 
throws GridClientException {
         Collection<GridClientNode> nodes = 
compute.nodes(GridClientNode::connectable);
 
         if (F.isEmpty(nodes))
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommand.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommand.java
index 92f0e27e1e3..97ced5ecc86 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommand.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommand.java
@@ -19,8 +19,8 @@ package org.apache.ignite.internal.commandline.systemview;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -28,12 +28,17 @@ import java.util.UUID;
 import java.util.stream.Collectors;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientCompute;
 import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.client.GridClientNode;
 import org.apache.ignite.internal.commandline.AbstractCommand;
 import org.apache.ignite.internal.commandline.Command;
 import org.apache.ignite.internal.commandline.CommandArgIterator;
 import org.apache.ignite.internal.commandline.CommandLogger;
 import org.apache.ignite.internal.commandline.argument.CommandArgUtils;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.VisorTaskArgument;
 import org.apache.ignite.internal.visor.systemview.VisorSystemViewTask;
 import 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType;
 import org.apache.ignite.internal.visor.systemview.VisorSystemViewTaskArg;
@@ -41,10 +46,14 @@ import 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTaskResult;
 import org.apache.ignite.spi.systemview.view.SystemView;
 
 import static java.util.Collections.nCopies;
+import static java.util.Collections.singleton;
 import static org.apache.ignite.internal.commandline.CommandList.SYSTEM_VIEW;
 import static org.apache.ignite.internal.commandline.CommandLogger.optional;
-import static 
org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.CommandLogger.or;
+import static 
org.apache.ignite.internal.commandline.TaskExecutor.getBalancedNode;
+import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.ALL_NODES;
 import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.NODE_ID;
+import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.NODE_IDS;
 import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.DATE;
 import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.NUMBER;
 import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.STRING;
@@ -60,8 +69,11 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
      */
     private VisorSystemViewTaskArg taskArg;
 
-    /** ID of the node to get the system view content from. */
-    private UUID nodeId;
+    /** ID of the nodes to get the system view content from. */
+    private Collection<UUID> nodeIds;
+
+    /** Flag to get the system view from all nodes. */
+    private boolean allNodes;
 
     /** {@inheritDoc} */
     @Override public Object execute(GridClientConfiguration clientCfg, 
IgniteLogger log) throws Exception {
@@ -69,17 +81,42 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
             VisorSystemViewTaskResult res;
 
             try (GridClient client = Command.startClient(clientCfg)) {
-                res = executeTaskByNameOnNode(
-                    client,
-                    VisorSystemViewTask.class.getName(),
-                    taskArg,
-                    nodeId,
-                    clientCfg
-                );
+                GridClientCompute compute = client.compute();
+
+                Map<UUID, GridClientNode> clusterNodes = 
compute.nodes().stream()
+                    .collect(Collectors.toMap(GridClientNode::nodeId, n -> n));
+
+                if (allNodes)
+                    nodeIds = clusterNodes.keySet();
+                else if (F.isEmpty(nodeIds))
+                    nodeIds = singleton(getBalancedNode(compute).nodeId());
+                else {
+                    for (UUID id : nodeIds) {
+                        if (!clusterNodes.containsKey(id))
+                            throw new IllegalArgumentException("Node with id=" 
+ id + " not found.");
+                    }
+                }
+
+                Collection<GridClientNode> connectable = 
F.viewReadOnly(nodeIds, clusterNodes::get,
+                    id -> clusterNodes.get(id).connectable());
+
+                if (!F.isEmpty(connectable))
+                    compute = compute.projection(connectable);
+
+                res = compute.execute(VisorSystemViewTask.class.getName(),
+                    new VisorTaskArgument<>(nodeIds, taskArg, false));
             }
 
-            if (res != null)
-                printTable(res.attributes(), res.types(), res.rows(), log);
+            if (res != null) {
+                res.rows().forEach((nodeId, rows) -> {
+                    log.info("Results from node with ID: " + nodeId);
+                    log.info("---");
+
+                    printTable(res.attributes(), res.types(), rows, log);
+
+                    log.info("---" + U.nl());
+                });
+            }
             else
                 log.info("No system view with specified name was found [name=" 
+ taskArg.systemViewName() + "]");
 
@@ -161,7 +198,8 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
 
     /** {@inheritDoc} */
     @Override public void parseArguments(CommandArgIterator argIter) {
-        nodeId = null;
+        nodeIds = null;
+        allNodes = false;
 
         String sysViewName = null;
 
@@ -170,19 +208,27 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
 
             SystemViewCommandArg cmdArg = CommandArgUtils.of(arg, 
SystemViewCommandArg.class);
 
-            if (cmdArg == NODE_ID) {
-                String nodeIdArg = argIter.nextArg(
-                    "ID of the node from which system view content should be 
obtained is expected.");
-
-                try {
-                    nodeId = UUID.fromString(nodeIdArg);
-                }
-                catch (IllegalArgumentException e) {
-                    throw new IllegalArgumentException("Failed to parse " + 
NODE_ID + " command argument." +
-                        " String representation of \"java.util.UUID\" is 
exepected. For example:" +
-                        " 123e4567-e89b-42d3-a456-556642440000", e);
-                }
+            if (cmdArg == NODE_ID || cmdArg == NODE_IDS) {
+                if (nodeIds != null)
+                    throw new IllegalArgumentException("Only one of " + 
NODE_ID + ", " + NODE_IDS + " commands is expected.");
+
+                String idsArg = argIter.nextArg(
+                    cmdArg == NODE_ID ? "ID of the node from which system view 
content should be obtained is expected." :
+                        "Comma-separated list of node IDs from which system 
view content should be obtained is expected.");
+
+                nodeIds = F.viewReadOnly(argIter.parseStringSet(idsArg), name 
-> {
+                    try {
+                        return UUID.fromString(name);
+                    }
+                    catch (IllegalArgumentException e) {
+                        throw new IllegalArgumentException("Failed to parse " 
+ (cmdArg == NODE_ID ? NODE_ID : NODE_IDS) +
+                            " command argument. String representation of 
\"java.util.UUID\" is exepected. For example:" +
+                            " 123e4567-e89b-42d3-a456-556642440000", e);
+                    }
+                });
             }
+            else if (cmdArg == ALL_NODES)
+                allNodes = true;
             else {
                 if (sysViewName != null)
                     throw new IllegalArgumentException("Multiple system view 
names are not supported.");
@@ -191,6 +237,9 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
             }
         }
 
+        if (allNodes && !F.isEmpty(nodeIds))
+            throw new IllegalArgumentException("The " + ALL_NODES + " 
parameter cannot be used with specified node IDs.");
+
         if (sysViewName == null) {
             throw new IllegalArgumentException(
                 "The name of the system view for which its content should be 
printed is expected.");
@@ -206,15 +255,20 @@ public class SystemViewCommand extends 
AbstractCommand<VisorSystemViewTaskArg> {
 
     /** {@inheritDoc} */
     @Override public void printUsage(IgniteLogger log) {
-        Map<String, String> params = new HashMap<>();
+        Map<String, String> params = new LinkedHashMap<>();
 
-        params.put("node_id", "ID of the node to get the system view from. If 
not set, random node will be chosen.");
         params.put("system_view_name", "Name of the system view which content 
should be printed." +
             " Both \"SQL\" and \"Java\" styles of system view name are 
supported" +
             " (e.g. SQL_TABLES and sql.tables will be handled similarly).");
-
-        usage(log, "Print system view content:", SYSTEM_VIEW, params, 
optional(NODE_ID, "node_id"),
-            "system_view_name");
+        params.put(NODE_ID + " node_id", "ID of the node to get the system 
view from (deprecated. Use " + NODE_IDS + " instead). " +
+            "If not set, random node will be chosen.");
+        params.put(NODE_IDS + " nodeId1,nodeId2,..",
+            "Comma-separated list of nodes IDs to get the system view from. If 
not set, random node will be chosen.");
+        params.put(ALL_NODES.argName(),
+            "Get the system view from all nodes. If not set, random node will 
be chosen.");
+
+        usage(log, "Print system view content:", SYSTEM_VIEW, params, 
"system_view_name",
+            or(optional(NODE_ID, "node_id"), optional(NODE_IDS, 
"nodeId1,nodeId2,.."), optional(ALL_NODES)));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommandArg.java
 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommandArg.java
index 1462eedca12..f712666f85b 100644
--- 
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommandArg.java
+++ 
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/systemview/SystemViewCommandArg.java
@@ -21,8 +21,19 @@ import 
org.apache.ignite.internal.commandline.argument.CommandArg;
 
 /** Represents all possible arguments for {@link SystemViewCommand}. */
 public enum SystemViewCommandArg implements CommandArg {
-    /** Id of the node to get the system view from. */
-    NODE_ID("--node-id");
+    /**
+     * Id of the node to get the system view from.
+     *
+     * @deprecated Use {@link SystemViewCommandArg#NODE_IDS} instead.
+     */
+    @Deprecated
+    NODE_ID("--node-id"),
+
+    /** Node IDs to get the system view from. */
+    NODE_IDS("--node-ids"),
+
+    /** Get the system view from all nodes. */
+    ALL_NODES("--all-nodes");
 
     /** Name of the argument. */
     private final String name;
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
index 5f621e27fe0..64b50216e8c 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/SystemViewCommandTest.java
@@ -22,8 +22,12 @@ import java.net.InetSocketAddress;
 import java.sql.Connection;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
@@ -32,6 +36,8 @@ import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgniteCache;
@@ -66,6 +72,8 @@ import 
org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDataba
 import 
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
 import org.apache.ignite.internal.processors.service.DummyService;
 import org.apache.ignite.internal.util.StripedExecutor;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.visor.systemview.VisorSystemViewTask;
 import org.apache.ignite.services.ServiceConfiguration;
@@ -85,7 +93,10 @@ import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_IN
 import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
 import static org.apache.ignite.internal.commandline.CommandList.SYSTEM_VIEW;
 import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommand.COLUMN_SEPARATOR;
+import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.ALL_NODES;
 import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.NODE_ID;
+import static 
org.apache.ignite.internal.commandline.systemview.SystemViewCommandArg.NODE_IDS;
+import static 
org.apache.ignite.internal.managers.discovery.GridDiscoveryManager.NODES_SYS_VIEW;
 import static 
org.apache.ignite.internal.managers.systemview.ScanQuerySystemView.SCAN_QRY_SYS_VIEW;
 import static 
org.apache.ignite.internal.metric.SystemViewSelfTest.TEST_PREDICATE;
 import static 
org.apache.ignite.internal.metric.SystemViewSelfTest.TEST_TRANSFORMER;
@@ -203,6 +214,21 @@ public class SystemViewCommandTest extends 
GridCommandHandlerClusterByClassAbstr
             "Multiple system view names are not supported.");
     }
 
+    /**
+     * Tests command error output in case {@link 
SystemViewCommandArg#ALL_NODES} and
+     * {@link SystemViewCommandArg#NODE_IDS} are both specified.
+     */
+    @Test
+    public void testAllNodesAndNodeIds() {
+        assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS,
+                CMD_SYS_VIEW, SVCS_VIEW, ALL_NODES.argName(), 
NODE_IDS.argName(), ignite0.localNode().id().toString()),
+            "The " + ALL_NODES.argName() + " parameter cannot be used with 
specified node IDs.");
+
+        assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS,
+                CMD_SYS_VIEW, SVCS_VIEW, ALL_NODES.argName(), 
NODE_ID.argName(), ignite0.localNode().id().toString()),
+            "The " + ALL_NODES.argName() + " parameter cannot be used with 
specified node IDs.");
+    }
+
     /**
      * Tests command error output in case {@link SystemViewCommandArg#NODE_ID} 
argument value refers to nonexistent
      * node.
@@ -1125,6 +1151,36 @@ public class SystemViewCommandTest extends 
GridCommandHandlerClusterByClassAbstr
         assertEquals(srvCnt, systemView(ignite0, SNAPSHOT_SYS_VIEW).size());
     }
 
+    /** */
+    @Test
+    public void testMultipleNodes() {
+        checkNodesResult(Collections.singleton(ignite0), NODE_IDS.argName());
+        checkNodesResult(Collections.singleton(client), NODE_IDS.argName());
+
+        checkNodesResult(F.asList(ignite0, ignite1), NODE_IDS.argName());
+        checkNodesResult(F.asList(ignite0, ignite1, client), 
NODE_IDS.argName());
+
+        checkNodesResult(F.viewReadOnly(G.allGrids(), node -> (IgniteEx)node), 
ALL_NODES.argName());
+    }
+
+    /** */
+    private void checkNodesResult(Collection<IgniteEx> nodes, String nodesArg) 
{
+        Map<UUID, List<List<String>>> map = systemView(nodes, NODES_SYS_VIEW, 
nodesArg);
+
+        assertEquals(nodes.size(), map.size());
+
+        map.forEach((nodeId, rows) -> {
+            assertEquals(ignite0.cluster().nodes().size(), rows.size());
+
+            for (List<String> row : rows) {
+                UUID rowNodeId = UUID.fromString(row.get(0));
+                boolean isLocal = Boolean.parseBoolean(row.get(8));
+
+                assertEquals(nodeId.equals(rowNodeId), isLocal);
+            }
+        });
+    }
+
     /**
      * Execute query on given node.
      *
@@ -1148,9 +1204,26 @@ public class SystemViewCommandTest extends 
GridCommandHandlerClusterByClassAbstr
      * @return Content of the requested system view.
      */
     private List<List<String>> systemView(IgniteEx node, String sysViewName) {
+        Map<UUID, List<List<String>>> map = 
systemView(Collections.singleton(node), sysViewName, NODE_ID.argName());
+
+        assertEquals(1, map.size());
+
+        return map.get(node.localNode().id());
+    }
+
+    /**
+     * Gets system view content via control utility from specified nodes. Here 
we also check if attributes names
+     * returned by the command match the real ones. And that both "SQL" and 
"Java" command names styles are supported.
+     *
+     * @param nodes Nodes to obtain system view from.
+     * @param sysViewName Name of the system view which content is required.
+     * @param nodesArg Argument to specify nodes.
+     * @return Content of the requested system view.
+     */
+    private Map<UUID, List<List<String>>> systemView(Collection<IgniteEx> 
nodes, String sysViewName, String nodesArg) {
         List<String> attrNames = new ArrayList<>();
 
-        SystemView<?> sysView = node.context().systemView().view(sysViewName);
+        SystemView<?> sysView = 
nodes.iterator().next().context().systemView().view(sysViewName);
 
         sysView.walker().visitAll(new AttributeVisitor() {
             @Override public <T> void accept(int idx, String name, Class<T> 
clazz) {
@@ -1158,28 +1231,29 @@ public class SystemViewCommandTest extends 
GridCommandHandlerClusterByClassAbstr
             }
         });
 
-        String nodeId = node.context().discovery().localNode().id().toString();
-
-        List<List<String>> rows = parseSystemViewCommandOutput(
-            executeCommand(EXIT_CODE_OK, CMD_SYS_VIEW, toSqlName(sysViewName), 
NODE_ID.argName(), nodeId));
+        int attrsCnt = sysView.walker().count();
 
-        assertEquals(attrNames, rows.get(0));
+        Map<UUID, List<List<String>>> map = null;
 
-        rows = parseSystemViewCommandOutput(
-            executeCommand(EXIT_CODE_OK, CMD_SYS_VIEW, 
toSqlName(sysViewName).toLowerCase(), NODE_ID.argName(), nodeId));
+        for (String nameArg : F.asList(toSqlName(sysViewName), 
toSqlName(sysViewName).toLowerCase(), sysViewName)) {
+            String[] args;
 
-        assertEquals(attrNames, rows.get(0));
+            if (ALL_NODES.argName().equals(nodesArg))
+                args = new String[] {CMD_SYS_VIEW, nameArg, 
ALL_NODES.argName()};
+            else {
+                String nodeIds = String.join(",", F.viewReadOnly(nodes, n -> 
n.localNode().id().toString()));
 
-        rows = parseSystemViewCommandOutput(
-            executeCommand(EXIT_CODE_OK, CMD_SYS_VIEW, sysViewName, 
NODE_ID.argName(), nodeId));
+                args = new String[] {CMD_SYS_VIEW, nameArg, nodesArg, nodeIds};
+            }
 
-        assertEquals(attrNames, rows.remove(0));
+            map = parseSystemViewCommandOutput(executeCommand(EXIT_CODE_OK, 
args));
 
-        int attrsCnt = sysView.walker().count();
+            map.values().forEach(rows -> rows.forEach(row -> 
assertEquals(attrsCnt, row.size())));
 
-        rows.forEach(row -> assertEquals(attrsCnt, row.size()));
+            map.values().forEach(rows -> assertEquals(attrNames, 
rows.remove(0)));
+        }
 
-        return rows;
+        return map;
     }
 
     /**
@@ -1188,22 +1262,46 @@ public class SystemViewCommandTest extends 
GridCommandHandlerClusterByClassAbstr
      * @param out Command output to parse.
      * @return System view values.
      */
-    private List<List<String>> parseSystemViewCommandOutput(String out) {
+    private Map<UUID, List<List<String>>> parseSystemViewCommandOutput(String 
out) {
         String outStart = 
"--------------------------------------------------------------------------------";
 
         String outEnd = "Command [" + SYSTEM_VIEW.toCommandName() + "] 
finished with code: " + EXIT_CODE_OK;
 
-        List<String> rows = Arrays.asList(out.substring(
+        String[] rows = out.substring(
             out.indexOf(outStart) + outStart.length() + 1,
             out.indexOf(outEnd) - 1
-        ).split(U.nl()));
+        ).split(U.nl());
+
+        Pattern nodePtrn = Pattern.compile("Results from node with ID: (.*)");
+        String tableDelim = "---";
 
-        return rows.stream().map(row ->
-            Arrays.stream(row.split(quote(COLUMN_SEPARATOR)))
+        Map<UUID, List<List<String>>> res = new HashMap<>();
+
+        UUID currNodeId = null;
+
+        for (String rowStr : rows) {
+            Matcher nodeMatcher = nodePtrn.matcher(rowStr);
+
+            if (nodeMatcher.matches()) {
+                currNodeId = UUID.fromString(nodeMatcher.group(1));
+
+                continue;
+            }
+
+            if (tableDelim.equals(rowStr) || rowStr.isEmpty())
+                continue;
+
+            assertNotNull("Expected node ID: " + out, currNodeId);
+
+            List<String> row = 
Arrays.stream(rowStr.split(quote(COLUMN_SEPARATOR)))
                 .map(String::trim)
                 .filter(str -> !str.isEmpty())
-                .collect(Collectors.toList()))
-            .collect(Collectors.toList());
+                .collect(Collectors.toList());
+
+            res.computeIfAbsent(currNodeId, id -> new ArrayList<>()).add(row);
+        }
+
+        return res;
     }
 
     /**
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTask.java
index 6f6e0154553..b4b86691a40 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTask.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTask.java
@@ -21,20 +21,24 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.UUID;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.compute.ComputeJobResult;
 import org.apache.ignite.internal.managers.systemview.GridSystemViewManager;
 import org.apache.ignite.internal.processors.task.GridInternal;
 import org.apache.ignite.internal.processors.task.GridVisorManagementTask;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.visor.VisorJob;
-import org.apache.ignite.internal.visor.VisorOneNodeTask;
+import org.apache.ignite.internal.visor.VisorMultiNodeTask;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.spi.systemview.view.SystemView;
 import org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker;
 import 
org.apache.ignite.spi.systemview.view.SystemViewRowAttributeWalker.AttributeWithValueVisitor;
 import org.jetbrains.annotations.Nullable;
 
+import static java.util.Collections.singletonMap;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.toSqlName;
 import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.DATE;
 import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.NUMBER;
@@ -43,7 +47,8 @@ import static 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.Si
 /** Reperesents visor task for obtaining system view content. */
 @GridInternal
 @GridVisorManagementTask
-public class VisorSystemViewTask extends 
VisorOneNodeTask<VisorSystemViewTaskArg, VisorSystemViewTaskResult> {
+public class VisorSystemViewTask extends 
VisorMultiNodeTask<VisorSystemViewTaskArg, VisorSystemViewTaskResult,
+    VisorSystemViewTaskResult> {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -52,6 +57,28 @@ public class VisorSystemViewTask extends 
VisorOneNodeTask<VisorSystemViewTaskArg
         return new VisorSystemViewJob(arg, false);
     }
 
+    /** {@inheritDoc} */
+    @Override protected @Nullable VisorSystemViewTaskResult 
reduce0(List<ComputeJobResult> results)
+        throws IgniteException {
+        VisorSystemViewTaskResult res = null;
+
+        Map<UUID, List<List<?>>> merged = new TreeMap<>();
+
+        for (ComputeJobResult r : results) {
+            if (r.getException() != null)
+                throw new IgniteException("Failed to execute job [nodeId=" + 
r.getNode().id() + ']', r.getException());
+
+            res = r.getData();
+
+            if (res == null)
+                return null;
+
+            merged.putAll(res.rows());
+        }
+
+        return new VisorSystemViewTaskResult(res.attributes(), res.types(), 
merged);
+    }
+
     /** */
     private static class VisorSystemViewJob extends 
VisorJob<VisorSystemViewTaskArg, VisorSystemViewTaskResult> {
         /** */
@@ -149,7 +176,7 @@ public class VisorSystemViewTask extends 
VisorOneNodeTask<VisorSystemViewTaskArg
                 rows.add(attrVals);
             }
 
-            return new VisorSystemViewTaskResult(attrNames, attrTypes, rows);
+            return new VisorSystemViewTaskResult(attrNames, attrTypes, 
singletonMap(ignite.localNode().id(), rows));
         }
 
         /**
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTaskResult.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTaskResult.java
index 313ccf45538..91522d1099b 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTaskResult.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/visor/systemview/VisorSystemViewTaskResult.java
@@ -20,8 +20,9 @@ package org.apache.ignite.internal.visor.systemview;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import 
org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType;
@@ -31,8 +32,8 @@ public class VisorSystemViewTaskResult extends 
IgniteDataTransferObject {
     /** */
     private static final long serialVersionUID = 0L;
 
-    /** Attribute values for each row of the system view. */
-    private List<List<?>> rows;
+    /** Attribute values for each row of the system view per node ID. */
+    private Map<UUID, List<List<?>>> rows;
 
     /** Names of the system view attributes. */
     private List<String> attrs;
@@ -48,9 +49,9 @@ public class VisorSystemViewTaskResult extends 
IgniteDataTransferObject {
     /**
      * @param attrs Names of system view attributes.
      * @param types Types of the system view attributes.
-     * @param rows Attribute values for each row of the system view.
+     * @param rows Attribute values for each row of the system view per node 
ID.
      */
-    public VisorSystemViewTaskResult(List<String> attrs, List<SimpleType> 
types, List<List<?>> rows) {
+    public VisorSystemViewTaskResult(List<String> attrs, List<SimpleType> 
types, Map<UUID, List<List<?>>> rows) {
         this.attrs = attrs;
         this.types = types;
         this.rows = rows;
@@ -61,8 +62,8 @@ public class VisorSystemViewTaskResult extends 
IgniteDataTransferObject {
         return attrs;
     }
 
-    /** @return Attribute values for each row of the system view. */
-    public List<List<?>> rows() {
+    /** @return Attribute values for each row of the system view per node ID. 
*/
+    public Map<UUID, List<List<?>>> rows() {
         return rows;
     }
 
@@ -77,10 +78,7 @@ public class VisorSystemViewTaskResult extends 
IgniteDataTransferObject {
 
         U.writeCollection(out, types);
 
-        out.writeInt(rows.size());
-
-        for (List<?> row : rows)
-            U.writeCollection(out, row);
+        U.writeMap(out, rows);
     }
 
     /** {@inheritDoc} */
@@ -89,13 +87,6 @@ public class VisorSystemViewTaskResult extends 
IgniteDataTransferObject {
 
         types = U.readList(in);
 
-        int rowsCnt = in.readInt();
-
-        List<List<?>> rows = new ArrayList<>(rowsCnt);
-
-        for (int i = 0; i < rowsCnt; i++)
-            rows.add(U.readList(in));
-
-        this.rows = rows;
+        rows = U.readTreeMap(in);
     }
 }
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index c1b37a48c3c..2b9eba75a01 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -252,11 +252,13 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin'
     control.(sh|bat) --property set --name <property_name> --val 
<property_value>
 
   Print system view content:
-    control.(sh|bat) --system-view [--node-id node_id] system_view_name
+    control.(sh|bat) --system-view system_view_name [--node-id 
node_id]|[--node-ids nodeId1,nodeId2,..]|[--all-nodes]
 
     Parameters:
-      system_view_name  - Name of the system view which content should be 
printed. Both "SQL" and "Java" styles of system view name are supported (e.g. 
SQL_TABLES and sql.tables will be handled similarly).
-      node_id           - ID of the node to get the system view from. If not 
set, random node will be chosen.
+      system_view_name               - Name of the system view which content 
should be printed. Both "SQL" and "Java" styles of system view name are 
supported (e.g. SQL_TABLES and sql.tables will be handled similarly).
+      --node-id node_id              - ID of the node to get the system view 
from (deprecated. Use --node-ids instead). If not set, random node will be 
chosen.
+      --node-ids nodeId1,nodeId2,..  - Comma-separated list of nodes IDs to 
get the system view from. If not set, random node will be chosen.
+      --all-nodes                    - Get the system view from all nodes. If 
not set, random node will be chosen.
 
   Print metric value:
     control.(sh|bat) --metric [--node-id node_id] name
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index c1b37a48c3c..2b9eba75a01 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -252,11 +252,13 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin'
     control.(sh|bat) --property set --name <property_name> --val 
<property_value>
 
   Print system view content:
-    control.(sh|bat) --system-view [--node-id node_id] system_view_name
+    control.(sh|bat) --system-view system_view_name [--node-id 
node_id]|[--node-ids nodeId1,nodeId2,..]|[--all-nodes]
 
     Parameters:
-      system_view_name  - Name of the system view which content should be 
printed. Both "SQL" and "Java" styles of system view name are supported (e.g. 
SQL_TABLES and sql.tables will be handled similarly).
-      node_id           - ID of the node to get the system view from. If not 
set, random node will be chosen.
+      system_view_name               - Name of the system view which content 
should be printed. Both "SQL" and "Java" styles of system view name are 
supported (e.g. SQL_TABLES and sql.tables will be handled similarly).
+      --node-id node_id              - ID of the node to get the system view 
from (deprecated. Use --node-ids instead). If not set, random node will be 
chosen.
+      --node-ids nodeId1,nodeId2,..  - Comma-separated list of nodes IDs to 
get the system view from. If not set, random node will be chosen.
+      --all-nodes                    - Get the system view from all nodes. If 
not set, random node will be chosen.
 
   Print metric value:
     control.(sh|bat) --metric [--node-id node_id] name


Reply via email to