ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [38/50] [abbrv] incubator-ignite git commit: # ignite-63
Date Fri, 23 Jan 2015 09:02:46 GMT
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/kill/VisorKillCommand.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/kill/VisorKillCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/kill/VisorKillCommand.scala
new file mode 100644
index 0000000..48a9cfe
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/kill/VisorKillCommand.scala
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands.kill
+
+import org.apache.ignite.internal.GridNodeAttributes
+import GridNodeAttributes._
+
+import org.apache.ignite._
+import org.apache.ignite.cluster.ClusterNode
+
+import java.util.{Collections, UUID}
+
+import org.apache.ignite.visor.VisorTag
+import org.gridgain.visor._
+import org.apache.ignite.visor.commands.VisorConsoleCommand
+import visor.visor._
+
+import scala.collection.JavaConversions._
+import scala.language.{implicitConversions, reflectiveCalls}
+import scala.util.control.Breaks._
+
+/**
+ * ==Overview==
+ * Contains Visor command `kill` implementation.
+ *
+ * ==Help==
+ * {{{
+ * +--------------------------------+
+ * | kill | Kills or restarts node. |
+ * +--------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     kill
+ *     kill "-in|-ih"
+ *     kill "{-r|-k} {-id8=<node-id8>|-id=<node-id>}"
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -in
+ *         Run command in interactive mode with ability to
+ *         choose a node to kill or restart.
+ *         Note that either '-in' or '-ih' can be specified.
+ *
+ *         This mode is used by default.
+ *     -ih
+ *         Run command in interactive mode with ability to
+ *         choose a host where to kill or restart nodes.
+ *         Note that either '-in' or '-ih' can be specified.
+ *     -r
+ *         Restart node mode.
+ *         Note that either '-r' or '-k' can be specified.
+ *         If no parameters provided - command starts in interactive mode.
+ *     -k
+ *         Kill (stop) node mode.
+ *         Note that either '-r' or '-k' can be specified.
+ *         If no parameters provided - command starts in interactive mode.
+ *     -id8=<node-id8>
+ *         ID8 of the node to kill or restart.
+ *         Note that either '-id8' or '-id' can be specified.
+ *         If no parameters provided - command starts in interactive mode.
+ *     -id=<node-id>
+ *         ID of the node to kill or restart.
+ *         Note that either '-id8' or '-id' can be specified.
+ *         If no parameters provided - command starts in interactive mode.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     kill
+ *         Starts command in interactive mode.
+ *     kill "-id8=12345678 -r"
+ *         Restart node with '12345678' ID8.
+ *     kill "-k"
+ *         Kill (stop) all nodes.
+ * }}}
+ */
+class VisorKillCommand {
+    /**
+     * Prints error message and advise.
+     *
+     * @param errMsgs Error messages.
+     */
+    private def scold(errMsgs: Any*) {
+        assert(errMsgs != null)
+
+        nl()
+
+        warn(errMsgs: _*)
+        warn("Type 'help kill' to see how to use this command.")
+    }
+
+    /**
+     * ===Command===
+     * Stops or restarts a JVM indicated by the node ID.
+     *
+     * ===Examples===
+     * <ex>kill "-id8=12345678 -r"<ex>
+     * Restarts the specified node.
+     *
+     * <ex>kill "-k"</ex>
+     * Stops all nodes.
+     *
+     * @param args Command arguments.
+     */
+    def kill(args: String) = breakable {
+        if (!isConnected)
+            adviseToConnect()
+        else {
+            val argLst = parseArgs(args)
+
+            val iNodes = hasArgFlag("in", argLst)
+            val iHosts = hasArgFlag("ih", argLst)
+
+            if (iNodes && iHosts)
+                scold("Only one of '-in' or '-ih' can be specified.").^^
+            else if (iNodes)
+                interactiveNodes().^^
+            else if (iHosts)
+                interactiveHosts().^^
+
+            val id8 = argValue("id8", argLst)
+            val id = argValue("id", argLst)
+            val restart = hasArgFlag("r", argLst)
+            val kill = hasArgFlag("k", argLst)
+
+            var node: ClusterNode = null
+
+            if (kill && restart)
+                scold("Only one of '-k' or '-r' can be specified.")
+            else if (!kill && !restart)
+                scold("Invalid command arguments: " + args)
+            else if (id8.isDefined && id.isDefined)
+                scold("Only one of -id8 or -id is allowed.")
+            else {
+                if (id8.isDefined) {
+                    val ns = nodeById8(id8.get)
+
+                    if (ns.isEmpty)
+                        scold("Unknown 'id8' value: " + id8.get).^^
+                    else if (ns.size != 1) {
+                        scold("'id8' resolves to more than one node (use full 'id' instead) : " + args).^^
+                    }
+                    else
+                        node = ns.head
+                }
+                else if (id.isDefined)
+                    try {
+                        node = grid.node(java.util.UUID.fromString(id.get))
+
+                        if (node == null)
+                            scold("'id' does not match any node : " + args).^^
+                    }
+                    catch {
+                        case e: IllegalArgumentException => scold("Invalid node 'id' in args: " + args).^^
+                    }
+
+                if (node == null && (id.isDefined || id8.isDefined))
+                    scold("Node with given ID cannot be found.").^^
+
+                try
+                    // In case of the restart - check that target node supports it.
+                    if (restart && node != null && node.attribute[String](ATTR_RESTART_ENABLED) != "true")
+                        scold("Node doesn't support restart: " + nid8(node)).^^
+                catch {
+                    case e: IgniteCheckedException => scold("Failed to restart the node. " + e.getMessage).^^
+                }
+
+                val op = if (restart) "restart" else "kill"
+
+                try
+                    killOrRestart(if (node == null) grid.nodes().map(_.id()) else Collections.singleton(node.id()), restart)
+                catch {
+                    case _: IgniteCheckedException => scold("Failed to " + op + " due to system error.").^^
+                }
+            }
+        }
+    }
+
+    /**
+     * ===Command===
+     * Run command in interactive mode.
+     *
+     * ===Examples===
+     * <ex>kill</ex>
+     * Starts command in interactive mode.
+     */
+    def kill() {
+        kill("-in")
+    }
+
+    /**
+     * Kills or restarts nodes in provided projection.
+     *
+     * @param nodes Projection.
+     * @param restart Restart flag.
+     */
+    private def killOrRestart(nodes: Iterable[UUID], restart: Boolean) {
+        assert(nodes != null)
+
+        if (nodes.isEmpty)
+            warn("Topology is empty.").^^
+
+        val op = if (restart) "restart" else "kill"
+
+        if (nodes.size() == grid.nodes().size())
+            ask("Are you sure you want to " + op + " ALL nodes? (y/n) [n]: ", "n") match {
+                case "y" | "Y" =>  ask("You are about to " + op + " ALL nodes. " +
+                    "Are you 100% sure? (y/n) [n]: ", "n") match {
+                        case "y" | "Y" => ()
+                        case "n" | "N" => break()
+                        case x => nl(); warn("Invalid answer: " + x); break()
+                    }
+                case "n" | "N" => break()
+                case x => nl(); warn("Invalid answer: " + x); break()
+            }
+        else if (nodes.size() > 1)
+            ask("Are you sure you want to " + op + " several nodes? (y/n) [n]: ", "n") match {
+                case "y" | "Y" => ()
+                case "n" | "N" => break()
+                case x => nl(); warn("Invalid answer: " + x); break()
+            }
+        else
+            ask("Are you sure you want to " + op + " this node? (y/n) [n]: ", "n") match {
+                case "y" | "Y" => ()
+                case "n" | "N" => break()
+                case x => nl(); warn("Invalid answer: " + x); break()
+            }
+
+        if (restart)
+            grid.restartNodes(nodes)
+        else
+            grid.stopNodes(nodes)
+    }
+
+    /**
+     * Runs interactive mode of the command (choosing node).
+     */
+    private def interactiveNodes() {
+        askForNode("Select node from:") match {
+            case Some(id) => ask("Do you want to [k]ill or [r]estart? (k/r) [r]: ", "r") match {
+                case "k" | "K" => killOrRestart(Seq(id), false)
+                case "r" | "R" => killOrRestart(Seq(id), true)
+                case x => nl(); warn("Invalid answer: " + x)
+            }
+            case None => ()
+        }
+    }
+
+    /**
+     * Runs interactive mode of the command (choosing host).
+     */
+    private def interactiveHosts() {
+        askForHost("Select host from:") match {
+            case Some(p) => ask("Do you want to [k]ill or [r]estart? (k/r) [r]: ", "r") match {
+                case "k" | "K" => killOrRestart(p.nodes().map(_.id), false)
+                case "r" | "R" => killOrRestart(p.nodes().map(_.id), false)
+                case x => nl(); warn("Invalid answer: " + x)
+            }
+            case None => ()
+        }
+    }
+}
+
+/**
+ * Companion object that does initialization of the command.
+ */
+object VisorKillCommand {
+    // Adds command's help to visor.
+    addHelp(
+        name = "kill",
+        shortInfo = "Kills or restarts node.",
+        spec = List(
+            "kill",
+            "kill -in|-ih",
+            "kill {-r|-k} {-id8=<node-id8>|-id=<node-id>}"
+        ),
+        args = List(
+            "-in" -> List(
+                "Run command in interactive mode with ability to",
+                "choose a node to kill or restart.",
+                "Note that either '-in' or '-ih' can be specified.",
+                " ",
+                "This mode is used by default."
+            ),
+            "-ih" -> List(
+                "Run command in interactive mode with ability to",
+                "choose a host where to kill or restart nodes.",
+                "Note that either '-in' or '-ih' can be specified."
+            ),
+            "-r" -> List(
+                "Restart node mode.",
+                "Note that either '-r' or '-k' can be specified.",
+                "If no parameters provided - command starts in interactive mode."
+            ),
+            "-k" -> List(
+                "Kill (stop) node mode.",
+                "Note that either '-r' or '-k' can be specified.",
+                "If no parameters provided - command starts in interactive mode."
+            ),
+            "-id8=<node-id8>" -> List(
+                "ID8 of the node to kill or restart.",
+                "Note that either '-id8' or '-id' can be specified and " +
+                    "you can also use '@n0' ... '@nn' variables as shortcut to <node-id8>.",
+                "If no parameters provided - command starts in interactive mode."
+            ),
+            "-id=<node-id>" -> List(
+                "ID of the node to kill or restart.",
+                "Note that either '-id8' or '-id' can be specified.",
+                "If no parameters provided - command starts in interactive mode."
+            )
+        ),
+        examples = List(
+            "kill" ->
+                "Starts command in interactive mode.",
+            "kill -id8=12345678 -r" ->
+                "Restart node with id8.",
+            "kill -id8=@n0 -r" ->
+                "Restart specified node with id8 taken from 'n0' memory variable.",
+            "kill -k" ->
+                "Kill (stop) all nodes."
+        ),
+        ref = VisorConsoleCommand(cmd.kill, cmd.kill)
+    )
+
+    /** Singleton command. */
+    private val cmd = new VisorKillCommand
+
+    /**
+     * Singleton.
+     */
+    def apply() = cmd
+
+    /**
+     * Implicit converter from visor to commands "pimp".
+     *
+     * @param vs Visor tagging trait.
+     */
+    implicit def fromKill2Visor(vs: VisorTag) = cmd
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/Packet.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/Packet.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/Packet.scala
new file mode 100644
index 0000000..e070322
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/Packet.scala
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands
+
+/**
+ * ==Overview==
+ * Contains Visor command `node` implementation.
+ *
+ * ==Help==
+ * {{{
+ * +--------------------------------+
+ * | node | Prints node statistics. |
+ * +--------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     node "{-id8=<node-id8>|-id=<node-id>} {-a}"
+ *     node
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -id8=<node-id8>
+ *         ID8 of node. Either '-id8' or '-id' can be specified.
+ *         If neither specified - command starts in interactive mode.
+ *     -id=<node-id>
+ *         Full ID of node. Either '-id8' or '-id' can  be specified.
+ *         If neither specified - command starts in interactive mode.
+ *     -a
+ *         Print extended information.
+ *         By default - only abbreviated statistics is printed.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     node
+ *         Starts command in interactive mode.
+ *     node "-id8=12345678"
+ *         Prints statistics for specified node.
+ *     node "-id8=12345678 -a"
+ *         Prints full statistics for specified node.
+ * }}}
+ */
+package object node

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/VisorNodeCommand.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/VisorNodeCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/VisorNodeCommand.scala
new file mode 100644
index 0000000..69c1b4f
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/node/VisorNodeCommand.scala
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands.node
+
+import org.apache.ignite.internal.GridNodeAttributes
+import org.apache.ignite.internal.util.GridUtils
+import org.apache.ignite.internal.util.typedef.internal.U
+import GridNodeAttributes._
+import org.apache.ignite.internal.util.lang.{GridFunc => F}
+import org.apache.ignite.internal.util.typedef.X
+
+import org.apache.ignite.cluster.ClusterNode
+import org.apache.ignite.visor.{VisorTag, visor}
+import org.jetbrains.annotations._
+
+import java.util.UUID
+
+import org.gridgain.visor._
+import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
+import visor._
+
+import scala.collection.JavaConversions._
+import scala.language.{implicitConversions, reflectiveCalls}
+import scala.util.control.Breaks._
+
+/**
+ * ==Overview==
+ * Contains Visor command `node` implementation.
+ *
+ * ==Help==
+ * {{{
+ * +--------------------------------+
+ * | node | Prints node statistics. |
+ * +--------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     node "{-id8=<node-id8>|-id=<node-id>} {-a}"
+ *     node
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -id8=<node-id8>
+ *         ID8 of node. Either '-id8' or '-id' can be specified.
+ *         If neither specified - command starts in interactive mode.
+ *     -id=<node-id>
+ *         Full ID of node. Either '-id8' or '-id' can  be specified.
+ *         If neither specified - command starts in interactive mode.
+ *     -a
+ *         Print extended information.
+ *         By default - only abbreviated statistics is printed.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     node
+ *         Starts command in interactive mode.
+ *     node "-id8=12345678"
+ *         Prints statistics for specified node.
+ *     node "-id8=12345678 -a"
+ *         Prints full statistics for specified node.
+ * }}}
+ */
+class VisorNodeCommand {
+    /**
+     * Prints error message and advise.
+     *
+     * @param errMsgs Error messages.
+     */
+    private def scold(errMsgs: Any*) {
+        assert(errMsgs != null)
+
+        warn(errMsgs: _*)
+        warn("Type 'help node' to see how to use this command.")
+    }
+
+    /**
+     * ===Command===
+     * Run command in interactive mode.
+     *
+     * ===Examples===
+     * <ex>node</ex>
+     * Starts command in interactive mode.
+     */
+    def node() {
+        if (!isConnected)
+            adviseToConnect()
+        else
+            askForNode("Select node from:") match {
+                case Some(id) => ask("Detailed statistics (y/n) [n]: ", "n") match {
+                    case "n" | "N" => nl(); node("-id=" + id)
+                    case "y" | "Y" => nl(); node("-a -id=" + id)
+                    case x => nl(); warn("Invalid answer: " + x)
+                }
+                case None => ()
+            }
+    }
+
+    /**
+     * ===Command===
+     * Prints full node information.
+     *
+     * ===Examples===
+     * <ex>node "-id8=12345678"</ex>
+     * Prints information for specified node.
+     *
+     * <ex>node "-id8=12345678 -all"</ex>
+     * Prints full information for specified node.
+     *
+     * @param args Command arguments.
+     */
+    def node(@Nullable args: String) = breakable {
+        if (!isConnected)
+            adviseToConnect()
+        else
+            try {
+                val argLst = parseArgs(args)
+
+                if (argLst.isEmpty)
+                    warn("Missing arguments.").^^
+                else {
+                    val id8 = argValue("id8", argLst)
+                    val id = argValue("id", argLst)
+                    val all = hasArgFlag("a", argLst)
+
+                    var node: ClusterNode = null
+
+                    if (id8.isDefined) {
+                        val ns = nodeById8(id8.get)
+
+                        if (ns.size != 1)
+                            warn("Unknown (invalid) node ID8: " + id8.get).^^
+                        else
+                            node = ns.head
+                    }
+                    else if (id.isDefined)
+                        try
+                            node = grid.node(UUID.fromString(id.get))
+                        catch {
+                            case e: IllegalArgumentException => warn("Invalid node ID: " + id.get).^^
+                        }
+                    else
+                        warn("Invalid arguments: " + args).^^
+
+                    if (node != null) {
+                        val t = VisorTextTable()
+
+                        t.autoBorder = false
+
+                        t.maxCellWidth = 60
+
+                        t += ("ID", node.id)
+                        t += ("ID8", nid8(node))
+                        t += ("Order", node.order)
+
+                        (0 /: node.addresses())((b, a) => { t += ("Address (" + b + ")", a); b + 1 })
+
+                        val m = node.metrics
+
+                        val gridName: String = node.attribute(ATTR_GRID_NAME)
+
+                        val ver = GridUtils.productVersion(node)
+                        val verStr = ver.major() + "." + ver.minor() + "." + ver.maintenance() +
+                            (if (F.isEmpty(ver.stage())) "" else "-" + ver.stage())
+
+                        if (all) {
+                            t += ("OS info", "" +
+                                node.attribute("os.name") + " " +
+                                node.attribute("os.arch") + " " +
+                                node.attribute("os.version")
+                            )
+                            t += ("OS user", node.attribute(ATTR_USER_NAME))
+                            t += ("Deployment mode", node.attribute(ATTR_DEPLOYMENT_MODE))
+                            t += ("Language runtime", node.attribute(ATTR_LANG_RUNTIME))
+                            t += ("Ignite version", verStr)
+                            t += ("JRE information", node.attribute(ATTR_JIT_NAME))
+                            t += ("Non-loopback IPs", node.attribute(ATTR_IPS))
+                            t += ("Enabled MACs", node.attribute(ATTR_MACS))
+                            t += ("Grid name", safe(gridName, "<default>"))
+                            t += ("JVM start time", formatDateTime(m.getStartTime))
+                            t += ("Node start time", formatDateTime(m.getNodeStartTime))
+                            t += ("Up time", X.timeSpan2HMSM(m.getUpTime))
+                            t += ("CPUs", formatNumber(m.getTotalCpus))
+                            t += ("Last metric update", formatDateTime(m.getLastUpdateTime))
+                            t += ("Maximum active jobs", formatNumber(m.getMaximumActiveJobs))
+                            t += ("Current active jobs", formatNumber(m.getCurrentActiveJobs))
+                            t += ("Average active jobs", formatDouble(m.getAverageActiveJobs))
+                            t += ("Maximum waiting jobs", formatNumber(m.getMaximumWaitingJobs))
+                            t += ("Current waiting jobs", formatNumber(m.getCurrentWaitingJobs))
+                            t += ("Average waiting jobs", formatDouble(m.getAverageWaitingJobs))
+                            t += ("Maximum rejected jobs", formatNumber(m.getMaximumRejectedJobs))
+                            t += ("Current rejected jobs", formatNumber(m.getCurrentRejectedJobs))
+                            t += ("Average rejected jobs", formatDouble(m.getAverageRejectedJobs))
+                            t += ("Maximum cancelled jobs", formatNumber(m.getMaximumCancelledJobs))
+                            t += ("Current cancelled jobs", formatNumber(m.getCurrentCancelledJobs))
+                            t += ("Average cancelled jobs", formatDouble(m.getAverageCancelledJobs))
+                            t += ("Total rejected jobs", formatNumber(m.getTotalRejectedJobs))
+                            t += ("Total executed jobs", formatNumber(m.getTotalExecutedJobs))
+                            t += ("Total cancelled jobs", formatNumber(m.getTotalCancelledJobs))
+                            t += ("Maximum job wait time", formatNumber(m.getMaximumJobWaitTime) + "ms")
+                            t += ("Current job wait time", formatNumber(m.getCurrentJobWaitTime) + "ms")
+                            t += ("Average job wait time", formatDouble(m.getAverageJobWaitTime) + "ms")
+                            t += ("Maximum job execute time", formatNumber(m.getMaximumJobExecuteTime) + "ms")
+                            t += ("Curent job execute time", formatNumber(m.getCurrentJobExecuteTime) + "ms")
+                            t += ("Average job execute time", formatDouble(m.getAverageJobExecuteTime) + "ms")
+                            t += ("Total busy time", formatNumber(m.getTotalBusyTime) + "ms")
+                            t += ("Busy time %", formatDouble(m.getBusyTimePercentage * 100) + "%")
+                            t += ("Current CPU load %", formatDouble(m.getCurrentCpuLoad * 100) + "%")
+                            t += ("Average CPU load %", formatDouble(m.getAverageCpuLoad * 100) + "%")
+                            t += ("Heap memory initialized", formatMemory(m.getHeapMemoryInitialized))
+                            t += ("Heap memory used", formatMemory(m.getHeapMemoryUsed))
+                            t += ("Heap memory committed", formatMemory(m.getHeapMemoryCommitted))
+                            t += ("Heap memory maximum", formatMemory(m.getHeapMemoryMaximum))
+                            t += ("Non-heap memory initialized", formatMemory(m.getNonHeapMemoryInitialized))
+                            t += ("Non-heap memory used", formatMemory(m.getNonHeapMemoryUsed))
+                            t += ("Non-heap memory committed", formatMemory(m.getNonHeapMemoryCommitted))
+                            t += ("Non-heap memory maximum", formatMemory(m.getNonHeapMemoryMaximum))
+                            t += ("Current thread count", formatNumber(m.getCurrentThreadCount))
+                            t += ("Maximum thread count", formatNumber(m.getMaximumThreadCount))
+                            t += ("Total started thread count", formatNumber(m.getTotalStartedThreadCount))
+                            t += ("Current daemon thread count", formatNumber(m.getCurrentDaemonThreadCount))
+                        }
+                        else {
+                            t += ("OS info", "" +
+                                node.attribute("os.name") + " " +
+                                node.attribute("os.arch") + " " +
+                                node.attribute("os.version")
+                            )
+                            t += ("OS user", node.attribute(ATTR_USER_NAME))
+                            t += ("Deployment mode", node.attribute(ATTR_DEPLOYMENT_MODE))
+                            t += ("Language runtime", node.attribute(ATTR_LANG_RUNTIME))
+                            t += ("Ignite version", verStr)
+                            t += ("JRE information", node.attribute(ATTR_JIT_NAME))
+                            t += ("Grid name", safe(gridName, "<default>"))
+                            t += ("JVM start time", formatDateTime(m.getStartTime))
+                            t += ("Node start time", formatDateTime(m.getNodeStartTime))
+                            t += ("Up time", X.timeSpan2HMSM(m.getUpTime))
+                            t += ("Last metric update", formatDateTime(m.getLastUpdateTime))
+                            t += ("CPUs", formatNumber(m.getTotalCpus))
+                            t += ("Thread count", formatNumber(m.getCurrentThreadCount))
+                            t += ("Cur/avg active jobs", formatNumber(m.getCurrentActiveJobs) +
+                                "/" + formatDouble(m.getAverageActiveJobs))
+                            t += ("Cur/avg waiting jobs", formatNumber(m.getCurrentWaitingJobs) +
+                                "/" + formatDouble(m.getAverageWaitingJobs))
+                            t += ("Cur/avg rejected jobs", formatNumber(m.getCurrentRejectedJobs) +
+                                "/" + formatDouble(m.getAverageRejectedJobs))
+                            t += ("Cur/avg cancelled jobs", formatNumber(m.getCurrentCancelledJobs) +
+                                "/" + formatDouble(m.getAverageCancelledJobs))
+                            t += ("Cur/avg job wait time", formatNumber(m.getCurrentJobWaitTime) +
+                                "/" + formatDouble(m.getAverageJobWaitTime) + "ms")
+                            t += ("Cur/avg job execute time", formatNumber(m.getCurrentJobExecuteTime) +
+                                "/" + formatDouble(m.getAverageJobExecuteTime) + "ms")
+                            t += ("Cur/avg CPU load %", formatDouble(m.getCurrentCpuLoad * 100) +
+                                "/" + formatDouble(m.getAverageCpuLoad * 100) + "%")
+                            t += ("Heap memory used/max", formatMemory(m.getHeapMemoryUsed) +
+                                "/" +  formatMemory(m.getHeapMemoryMaximum))
+                        }
+
+                        println("Time of the snapshot: " + formatDateTime(System.currentTimeMillis))
+
+                        t.render()
+
+                        if (!all)
+                            println("\nUse \"-a\" flag to see detailed statistics.")
+                    }
+                }
+            }
+            catch {
+                case e: Exception => scold(e.getMessage)
+            }
+    }
+}
+
+/**
+ * Companion object that does initialization of the command.
+ */
+object VisorNodeCommand {
+    // Adds command's help to visor.
+    addHelp(
+        name = "node",
+        shortInfo = "Prints node statistics.",
+        spec = List(
+            "node {-id8=<node-id8>|-id=<node-id>} {-a}",
+            "node"
+        ),
+        args = List(
+            "-id8=<node-id8>" -> List(
+                "Note that either '-id8' or '-id' can be specified and " +
+                    "you can also use '@n0' ... '@nn' variables as shortcut to <node-id8>.",
+                "If neither specified - command starts in interactive mode."
+            ),
+            "-id=<node-id>" -> List(
+                "Full ID of node. Either '-id8' or '-id' can  be specified.",
+                "If neither specified - command starts in interactive mode."
+            ),
+            "-a" -> List(
+                "Print extended information.",
+                "By default - only abbreviated statistics is printed."
+            )
+        ),
+        examples = List(
+            "node" ->
+                "Starts command in interactive mode.",
+            "node -id8=12345678" ->
+                "Prints statistics for specified node.",
+            "node -id8=@n0 -a" ->
+                "Prints full statistics for specified node with id8 taken from 'n0' memory variable."
+        ),
+        ref = VisorConsoleCommand(cmd.node, cmd.node)
+    )
+
+    /** Singleton command. */
+    private val cmd = new VisorNodeCommand
+
+    /**
+     * Singleton.
+     */
+    def apply() = cmd
+
+    /**
+     * Implicit converter from visor to commands "pimp".
+     *
+     * @param vs Visor tagging trait.
+     */
+    implicit def fromNode2Visor(vs: VisorTag) = cmd
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/Packet.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/Packet.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/Packet.scala
new file mode 100644
index 0000000..eb824e8
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/Packet.scala
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands
+
+/**
+ * ==Command==
+ * Visor 'ping' command implementation.
+ *
+ * ==Help==
+ * {{{
+ * +--------------------+
+ * | ping | Pings node. |
+ * +--------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     ping {"id81 id82 ... id8k"}
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     id8k
+ *         ID8 of the node to ping.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     ping "12345678"
+ *         Pings node with '12345678' ID8.
+ *     ping
+ *         Pings all nodes in the topology.
+ * }}}
+ */
+package object ping

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/VisorPingCommand.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/VisorPingCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/VisorPingCommand.scala
new file mode 100644
index 0000000..a31c654
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/ping/VisorPingCommand.scala
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands.ping
+
+import org.apache.ignite.cluster.ClusterNode
+
+import java.util.concurrent._
+
+import org.apache.ignite.visor.{VisorTag, visor}
+import org.gridgain.visor._
+import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
+import visor._
+
+import scala.collection.JavaConversions._
+import scala.language.{implicitConversions, reflectiveCalls}
+import scala.util.control.Breaks._
+
+/**
+ * Ping result container.
+ */
+private class Result {
+    /** Total pings count. */
+    var total = 0
+
+    /** Successful pings count. */
+    var oks = 0
+
+    /** Failed pings count */
+    var fails = 0
+
+    /** Failed nodes. */
+    val failedNodes = collection.mutable.Set.empty[ClusterNode]
+}
+
+/**
+ * Thread that pings one node.
+ */
+private case class Pinger(n: ClusterNode, res: Result) extends Runnable {
+    assert(n != null)
+    assert(res != null)
+
+    override def run() {
+        val ok = grid.pingNode(n.id())
+
+        res.synchronized {
+            res.total += 1
+
+            if (ok)
+                res.oks += 1
+            else {
+                res.fails += 1
+                res.failedNodes += n
+            }
+        }
+    }
+}
+
+/**
+ * ==Command==
+ * Visor 'ping' command implementation.
+ *
+ * ==Help==
+ * {{{
+ * +--------------------+
+ * | ping | Pings node. |
+ * +--------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     ping {"id81 id82 ... id8k"}
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     id8k
+ *         ID8 of the node to ping.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     ping "12345678"
+ *         Pings node with '12345678' ID8.
+ *     ping
+ *         Pings all nodes in the topology.
+ * }}}
+ */
+class VisorPingCommand {
+    /**
+     * Prints error message and advise.
+     *
+     * @param errMsgs Error messages.
+     */
+    private def scold(errMsgs: Any*) {
+        assert(errMsgs != null)
+
+        warn(errMsgs: _*)
+        warn("Type 'help ping' to see how to use this command.")
+    }
+
+    /**
+     * ===Command===
+     * Pings node(s) by its ID8.
+     *
+     * ===Examples===
+     * <ex>ping "12345678 56781234"</ex>
+     * Pings nodes with '12345678' and '56781234' ID8s.
+     *
+     * @param args List of node ID8s. If empty or null - pings all nodes in the topology.
+     */
+    def ping(args: String) = breakable {
+        if (!isConnected)
+            adviseToConnect()
+        else {
+            val argLst = parseArgs(args)
+
+            val res = new Result()
+
+            var pings = List.empty[Pinger]
+
+            if (argLst.isEmpty)
+                pings ++= grid.nodes().map(Pinger(_, res))
+            else {
+                for (id8 <- argLst) {
+                    if (id8._1 != null || id8._2 == null)
+                        scold("Invalid ID8: " + argName(id8))
+                    else {
+                        val ns = nodeById8(id8._2)
+
+                        if (ns.size() != 1)
+                            scold("Unknown ID8: " + argName(id8))
+                        else
+                            pings +:= Pinger(ns.head, res)
+                    }
+                }
+            }
+
+            if (pings.isEmpty)
+                scold("Topology is empty.")
+            else {
+                try
+                    pings.map(pool.submit(_)).foreach(_.get)
+                catch {
+                    case _: RejectedExecutionException => scold("Ping failed due to system error.").^^
+                }
+
+                val t = VisorTextTable()
+
+                // No synchronization on 'res' is needed since all threads
+                // are finished and joined.
+                t += ("Total pings", res.total)
+                t += ("Successful pings", res.oks + " (" + formatInt(100 * res.oks / res.total) + "%)")
+                t += ("Failed pings", res.fails + " (" + formatInt(100 * res.fails / res.total) + "%)")
+
+                if (res.failedNodes.nonEmpty)
+                    t += ("Failed nodes", res.failedNodes.map(n => nodeId8Addr(n.id)))
+
+                t.render()
+            }
+        }
+    }
+
+    /**
+     * ===Command===
+     * Pings all nodes in the topology.
+     *
+     * ===Examples===
+     * <ex>ping</ex>
+     * Pings all nodes in the topology.
+     */
+    def ping() {
+        ping("")
+    }
+}
+
+/**
+ * Companion object that does initialization of the command.
+ */
+object VisorPingCommand {
+    // Adds command's help to visor.
+    addHelp(
+        name = "ping",
+        shortInfo = "Pings node.",
+        spec = List("ping <id81> <id82> ... <id8k>"),
+        args = List(
+            ("<id8k>",
+                "ID8 of the node to ping. Note you can also use '@n0' ... '@nn' variables as shortcut to <id8k>.")
+        ),
+        examples = List(
+            "ping 12345678" ->
+                "Pings node with '12345678' ID8.",
+            "ping @n0" ->
+                "Pings node with 'specified node with ID8 taken from 'n0' memory variable.",
+            "ping" ->
+                "Pings all nodes in the topology."
+        ),
+        ref = VisorConsoleCommand(cmd.ping, cmd.ping)
+    )
+
+    /** Singleton command. */
+    private val cmd = new VisorPingCommand
+
+    /**
+     * Singleton.
+     */
+    def apply() = cmd
+
+    /**
+     * Implicit converter from visor to commands "pimp".
+     *
+     * @param vs Visor tagging trait.
+     */
+    implicit def fromPing2Visor(vs: VisorTag) = cmd
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/Packet.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/Packet.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/Packet.scala
new file mode 100644
index 0000000..e1cde80
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/Packet.scala
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands
+
+/**
+ * ==Overview==
+ * Contains Visor command `start` implementation.
+ *
+ * ==Help==
+ * {{{
+ * +-----------------------------------------------------+
+ * | start | Starts one or more nodes on remote host(s). |
+ * |       | Uses SSH protocol to execute commands.      |
+ * +-----------------------------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     start "-f=<path> {-m=<num>} {-r}"
+ *     start "-h=<hostname> {-p=<num>} {-u=<username>} {-pw=<password>} {-k=<path>}
+ *         {-n=<num>} {-g=<path>} {-c=<path>} {-s=<path>} {-m=<num>} {-r}"
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -f=<path>
+ *         Path to INI file that contains topology specification.
+ *     -h=<hostname>
+ *         Hostname where to start nodes.
+ *
+ *         Can define several hosts if their IPs are sequential.
+ *         Example of range is 192.168.1.100~150,
+ *         which means all IPs from 192.168.1.100 to 192.168.1.150 inclusively.
+ *     -p=<num>
+ *         Port number (default is 22).
+ *     -u=<username>
+ *         Username (if not defined, current local username will be used).
+ *     -pw=<password>
+ *         Password (if not defined, private key file must be defined).
+ *     -k=<path>
+ *         Path to private key file. Define if key authentication is used.
+ *     -n=<num>
+ *         Expected number of nodes on the host.
+ *         If some nodes are started already, then only remaining nodes will be started.
+ *         If current count of nodes is equal to this number and '-r' flag is not set, then nothing will happen.
+ *     -g=<path>
+ *         Path to GridGain installation folder.
+ *         If not defined, GRIDGAIN_HOME environment variable must be set on remote hosts.
+ *     -c=<path>
+ *         Path to configuration file (relative to GridGain home).
+ *         If not provided, default GridGain configuration is used.
+ *     -s=<path>
+ *         Path to start script (relative to GridGain home).
+ *         Default is "bin/ggstart.sh" for Unix or
+ *         "bin\ggstart.bat" for Windows.
+ *     -m=<num>
+ *         Defines maximum number of nodes that can be started in parallel on one host.
+ *         This actually means number of parallel SSH connections to each SSH server.
+ *         Default is 5.
+ *     -r
+ *         Indicates that existing nodes on the host will be restarted.
+ *         By default, if flag is not present, existing nodes will be left as is.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     start "-h=10.1.1.10 -u=uname -pw=passwd -n=3"
+ *         Starts three nodes with default configuration (password authentication).
+ *     start "-h=192.168.1.100~104 -u=uname -k=/home/uname/.ssh/is_rsa -n=5"
+ *         Starts 25 nodes on 5 hosts (5 nodes per host) with default configuration (key-based authentication).
+ *     start "-f=start-nodes.ini -r"
+ *         Starts topology defined in 'start-nodes.ini' file. Existing nodes are stopped.
+ * }}}
+ */
+package object start

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/VisorStartCommand.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/VisorStartCommand.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/VisorStartCommand.scala
new file mode 100644
index 0000000..e9c5d21
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/start/VisorStartCommand.scala
@@ -0,0 +1,430 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands.start
+
+import org.apache.ignite._
+
+import java.io._
+import java.util.concurrent._
+
+import org.apache.ignite.internal.util.GridUtils
+import org.apache.ignite.internal.util.typedef.internal.U
+import org.apache.ignite.visor.VisorTag
+import org.gridgain.visor._
+import org.apache.ignite.visor.commands.{VisorConsoleCommand, VisorTextTable}
+import visor.visor._
+
+import scala.collection.JavaConversions._
+import scala.language.{implicitConversions, reflectiveCalls}
+import scala.util.control.Breaks._
+
+/**
+ * Node start attempt result.
+ */
+private case class Result(
+    host: String,
+    ok: Boolean,
+    errMsg: String = null
+) {
+    assert(host != null)
+}
+
+/**
+ * ==Overview==
+ * Contains Visor command `start` implementation.
+ *
+ * ==Help==
+ * {{{
+ * +-----------------------------------------------------+
+ * | start | Starts one or more nodes on remote host(s). |
+ * |       | Uses SSH protocol to execute commands.      |
+ * +-----------------------------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     start "-f=<path> {-m=<num>} {-r}"
+ *     start "-h=<hostname> {-p=<num>} {-u=<username>} {-pw=<password>} {-k=<path>}
+ *         {-n=<num>} {-g=<path>} {-c=<path>} {-s=<path>} {-m=<num>} {-r}"
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -f=<path>
+ *         Path to INI file that contains topology specification.
+ *     -h=<hostname>
+ *         Hostname where to start nodes.
+ *
+ *         Can define several hosts if their IPs are sequential.
+ *         Example of range is 192.168.1.100~150,
+ *         which means all IPs from 192.168.1.100 to 192.168.1.150 inclusively.
+ *     -p=<num>
+ *         Port number (default is 22).
+ *     -u=<username>
+ *         Username (if not defined, current local username will be used).
+ *     -pw=<password>
+ *         Password (if not defined, private key file must be defined).
+ *     -k=<path>
+ *         Path to private key file. Define if key authentication is used.
+ *     -n=<num>
+ *         Expected number of nodes on the host.
+ *         If some nodes are started already, then only remaining nodes will be started.
+ *         If current count of nodes is equal to this number and '-r' flag is not set, then nothing will happen.
+ *     -g=<path>
+ *         Path to GridGain installation folder.
+ *         If not defined, GRIDGAIN_HOME environment variable must be set on remote hosts.
+ *     -c=<path>
+ *         Path to configuration file (relative to GridGain home).
+ *         If not provided, default GridGain configuration is used.
+ *     -s=<path>
+ *         Path to start script (relative to GridGain home).
+ *         Default is "bin/ggstart.sh" for Unix or
+ *         "bin\ggstart.bat" for Windows.
+ *     -m=<num>
+ *         Defines maximum number of nodes that can be started in parallel on one host.
+ *         This actually means number of parallel SSH connections to each SSH server.
+ *         Default is 5.
+ *     -t=<num>
+ *         Defines connection timeout in milliseconds.
+ *         Default is 2000.
+ *     -r
+ *         Indicates that existing nodes on the host will be restarted.
+ *         By default, if flag is not present, existing nodes will be left as is.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     start "-h=10.1.1.10 -u=uname -pw=passwd -n=3"
+ *         Starts three nodes with default configuration (password authentication).
+ *     start "-h=192.168.1.100~104 -u=uname -k=/home/uname/.ssh/is_rsa -n=5"
+ *         Starts 25 nodes on 5 hosts (5 nodes per host) with default configuration (key-based authentication).
+ *     start "-f=start-nodes.ini -r"
+ *         Starts topology defined in 'start-nodes.ini' file. Existing nodes are stopped.
+ * }}}
+ */
+class VisorStartCommand {
+    /** Default maximum number of parallel connections. */
+    private final val DFLT_MAX_CONN = 5
+
+    /** Default connection timeout. */
+    private final val DFLT_TIMEOUT = 2000
+
+    /**
+     * Prints error message and advise.
+     *
+     * @param errMsgs Error messages.
+     */
+    private def scold(errMsgs: Any*) {
+        assert(errMsgs != null)
+
+        nl()
+
+        warn(errMsgs: _*)
+        warn("Type 'help start' to see how to use this command.")
+    }
+
+    /**
+     * Catch point for missing arguments case.
+     */
+    def start() {
+        scold("Missing arguments.")
+    }
+
+    /**
+     * ===Command===
+     * Starts or restart one or more nodes on remote host.
+     * Uses SSH protocol to execute commands.
+     *
+     * ===Examples===
+     * <ex>start "-h=uname:passwd@host#3"</ex>
+     * Starts three nodes with default configuration (password authentication).
+     *
+     * <ex>start "-h=uname@host#3 -k=ssh-key.pem"</ex>
+     * Starts three nodes with default configuration (key authentication).
+     *
+     * <ex>start "-f=hosts.txt -c=config/spring.xml"</ex>
+     * Reads `hosts.txt` file and starts nodes with provided configuration.
+     *
+     * @param args Command arguments.
+     */
+    def start(args: String) = breakable {
+        assert(args != null)
+
+        if (!isConnected)
+            adviseToConnect()
+        else {
+            val argLst = parseArgs(args)
+
+            val fileOpt = argValue("f", argLst)
+            val hostOpt = argValue("h", argLst)
+            val portOpt = argValue("p", argLst)
+            val unameOpt = argValue("u", argLst)
+            val passwdOpt = argValue("pw", argLst)
+            val keyOpt = argValue("k", argLst)
+            val nodesOpt = argValue("n", argLst)
+            val ggHomeOpt = argValue("g", argLst)
+            val cfgOpt = argValue("c", argLst)
+            val scriptOpt = argValue("s", argLst)
+            val maxConnOpt = argValue("m", argLst)
+            val timeoutOpt = argValue("t", argLst)
+            val restart = hasArgFlag("r", argLst)
+
+            val maxConn = maxConnOpt match {
+                case None => DFLT_MAX_CONN
+                case Some(mc) =>
+                    try {
+                        mc.toInt
+                    }
+                    catch {
+                        case e: NumberFormatException =>
+                            scold("Invalid maximum number of parallel connections: " + maxConnOpt.get).^^
+
+                        0 // Never happens.
+                    }
+            }
+
+            if (maxConn <= 0)
+                scold("Invalid maximum number of parallel connections: " + maxConn).^^
+
+            val timeout = timeoutOpt match {
+                case None => DFLT_TIMEOUT
+                case Some(to) =>
+                    try {
+                        to.toInt
+                    }
+                    catch {
+                        case e: NumberFormatException =>
+                            scold("Invalid timeout: " + to).^^
+
+                        0 // Never happens.
+                    }
+            }
+
+            if (timeout <= 0)
+                scold("Invalid connection timeout: " + timeout).^^
+
+            var res = Seq.empty[Result]
+
+            if (fileOpt.isDefined) {
+                val file = new File(fileOpt.get)
+
+                if (!file.exists())
+                    scold("File not found: " + file.getAbsolutePath).^^
+
+                if (file.isDirectory)
+                    scold("File is a directory: " + file.getAbsolutePath).^^
+
+                try
+                    res = grid.startNodes(file, restart, timeout, maxConn).map(t => {
+                        Result(t.get1, t.get2, t.get3)
+                    }).toSeq
+                catch {
+                    case e: IgniteCheckedException => scold(e.getMessage).^^
+                    case _: RejectedExecutionException => scold("Failed due to system error.").^^
+                }
+            }
+            else {
+                if (hostOpt.isEmpty)
+                    scold("Hostname is required.").^^
+
+                val port: java.lang.Integer =
+                    try {
+                        if (portOpt.isDefined) portOpt.get.toInt else null.asInstanceOf[java.lang.Integer]
+                    }
+                    catch {
+                        case e: NumberFormatException => scold("Invalid port number: " + portOpt.get).^^
+
+                        0 // Never happens.
+                    }
+
+                if (port != null && port <= 0)
+                    scold("Invalid port number: " + port).^^
+
+                val keyFile = if (keyOpt.isDefined) new File(keyOpt.get) else null
+
+                if (keyFile != null && (!keyFile.exists || !keyFile.isFile))
+                    scold("File not found: " + keyFile.getAbsolutePath).^^
+
+                val nodes: java.lang.Integer =
+                    try {
+                        if (nodesOpt.isDefined) nodesOpt.get.toInt else null.asInstanceOf[java.lang.Integer]
+                    }
+                    catch {
+                        case e: NumberFormatException => scold("Invalid number of nodes: " + nodesOpt.get).^^
+
+                        0 // Never happens.
+                    }
+
+                if (nodes != null && nodes <= 0)
+                    scold("Invalid number of nodes: " + nodes).^^
+
+                val params: Map[String, AnyRef] = Map(
+                    "host" -> hostOpt.get,
+                    "port" -> port,
+                    "uname" -> unameOpt.orNull,
+                    "passwd" -> passwdOpt.orNull,
+                    "key" -> keyFile,
+                    "nodes" -> nodes,
+                    "ggHome" -> ggHomeOpt.orNull,
+                    "cfg" -> cfgOpt.orNull,
+                    "script" -> scriptOpt.orNull
+                )
+
+                try
+                    res = grid.startNodes(asJavaCollection(Seq(params)), null, restart, timeout, maxConn).
+                        map(t => Result(t.get1, t.get2, t.get3)).toSeq
+                catch {
+                    case e: IgniteCheckedException => scold(e.getMessage).^^
+                    case _: RejectedExecutionException => scold("Failed due to system error.").^^
+                }
+            }
+
+            val resT = VisorTextTable()
+
+            resT += ("Successful start attempts", res.count(_.ok))
+            resT += ("Failed start attempts", res.count(!_.ok))
+
+            resT.render()
+
+            if (res.exists(!_.ok)) {
+                nl()
+
+                println("Errors:")
+
+                val errT = VisorTextTable()
+
+                errT.maxCellWidth = 70
+
+                errT #= ("Host", "Error")
+
+                res.filter(!_.ok) foreach (r => { errT += (r.host, r.errMsg.replace("\t", " ").split(GridUtils.nl()).toSeq) })
+
+                errT.render()
+            }
+
+            nl()
+
+            println("Type 'top' to see current topology.")
+
+            nl()
+
+            println("NOTE:")
+            println("    - Successful start attempt DOES NOT mean that node actually started.")
+            println("    - For large topologies (> 100s nodes) it can take over 10 minutes for all nodes to start.")
+            println("    - See individual node log for details.")
+        }
+    }
+}
+
+/**
+ * Companion object that does initialization of the command.
+ */
+object VisorStartCommand {
+    addHelp(
+        name = "start",
+        shortInfo = "Starts or restarts nodes on remote hosts.",
+        longInfo = List(
+            "Starts one or more nodes on remote host(s).",
+            "Uses SSH protocol to execute commands."
+        ),
+        spec = List(
+            "start -f=<path> {-m=<num>} {-r}",
+            "start -h=<hostname> {-p=<num>} {-u=<username>} {-pw=<password>} {-k=<path>}",
+                "{-n=<num>} {-g=<path>} {-c=<path>} {-s=<path>} {-m=<num>} {-r}"
+        ),
+        args = List(
+            "-f=<path>" -> List(
+                "Path to INI file that contains topology specification."
+            ),
+            "-h=<hostname>" -> List(
+                "Hostname where to start nodes.",
+                " ",
+                "Can define several hosts if their IPs are sequential.",
+                "Example of range is 192.168.1.100~150,",
+                "which means all IPs from 192.168.1.100 to 192.168.1.150 inclusively."
+            ),
+            "-p=<num>" -> List(
+                "Port number (default is 22)."
+            ),
+            "-u=<username>" -> List(
+                "Username (if not defined, current local username will be used)."
+            ),
+            "-pw=<password>" -> List(
+                "Password (if not defined, private key file must be defined)."
+            ),
+            "-k=<path>" -> List(
+                "Path to private key file. Define if key authentication is used."
+            ),
+            "-n=<num>" -> List(
+                "Expected number of nodes on the host.",
+                "If some nodes are started already, then only remaining nodes will be started.",
+                "If current count of nodes is equal to this number and '-r' flag is not set, then nothing will happen."
+            ),
+            "-g=<path>" -> List(
+                "Path to GridGain installation folder.",
+                "If not defined, GRIDGAIN_HOME environment variable must be set on remote hosts."
+            ),
+            "-c=<path>" -> List(
+                "Path to configuration file (relative to GridGain home).",
+                "If not provided, default GridGain configuration is used."
+            ),
+            "-s=<path>" -> List(
+                "Path to start script (relative to GridGain home).",
+                "Default is \"bin/ggstart.sh\" for Unix or",
+                "\"bin\\ggstart.bat\" for Windows."
+            ),
+            "-m=<num>" -> List(
+                "Defines maximum number of nodes that can be started in parallel on one host.",
+                "This actually means number of parallel SSH connections to each SSH server.",
+                "Default is 5."
+            ),
+            "-t=<num>" -> List(
+                "Defines connection timeout in milliseconds.",
+                "Default is 2000."
+            ),
+            "-r" -> List(
+                "Indicates that existing nodes on the host will be restarted.",
+                "By default, if flag is not present, existing nodes will be left as is."
+            )
+        ),
+        examples = List(
+            "start -h=10.1.1.10 -u=uname -pw=passwd -n=3" ->
+                "Starts three nodes with default configuration (password authentication).",
+            "start -h=192.168.1.100~104 -u=uname -k=/home/uname/.ssh/is_rsa -n=5" ->
+                "Starts 25 nodes on 5 hosts (5 nodes per host) with default configuration (key-based authentication).",
+            "start -f=start-nodes.ini -r" ->
+                "Starts topology defined in 'start-nodes.ini' file. Existing nodes are stopped."
+        ),
+        ref = VisorConsoleCommand(cmd.start, cmd.start)
+    )
+
+    /** Singleton command. */
+    private val cmd = new VisorStartCommand
+
+    /**
+     * Singleton.
+     */
+    def apply() = cmd
+
+    /**
+     * Implicit converter from visor to commands "pimp".
+     *
+     * @param vs Visor tagging trait.
+     */
+    implicit def fromStart2Visor(vs: VisorTag) = cmd
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/e1c3c8ce/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/tasks/Packet.scala
----------------------------------------------------------------------
diff --git a/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/tasks/Packet.scala b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/tasks/Packet.scala
new file mode 100644
index 0000000..de5b9b0
--- /dev/null
+++ b/modules/visor-console/src/main/scala/org/apache/ignite/visor/commands/tasks/Packet.scala
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.visor.commands
+
+/**
+ * ==Overview==
+ * Visor 'tasks' command implementation.
+ *
+ * ==Help==
+ * {{{
+ * +---------------------------------------------------------------------------------------+
+ * | tasks | Prints statistics about tasks and executions.                                 |
+ * |       |                                                                               |
+ * |       | Note that this command depends on GridGain events.                            |
+ * |       |                                                                               |
+ * |       | GridGain events can be individually enabled and disabled and disabled events  |
+ * |       | can affect the results produced by this command. Note also that configuration |
+ * |       | of Event Storage SPI that is responsible for temporary storage of generated   |
+ * |       | events on each node can also affect the functionality of this command.        |
+ * |       |                                                                               |
+ * |       | By default - all events are enabled and GridGain stores last 10,000 local     |
+ * |       | events on each node. Both of these defaults can be changed in configuration.  |
+ * +---------------------------------------------------------------------------------------+
+ * }}}
+ *
+ * ====Specification====
+ * {{{
+ *     tasks
+ *     tasks "-l {-t=<num>s|m|h|d} {-r}"
+ *     tasks "-s=<substring> {-t=<num>s|m|h|d} {-r}"
+ *     tasks "-g {-t=<num>s|m|h|d} {-r}"
+ *     tasks "-h {-t=<num>s|m|h|d} {-r}"
+ *     tasks "-n=<task-name> {-r}"
+ *     tasks "-e=<exec-id>"
+ * }}}
+ *
+ * ====Arguments====
+ * {{{
+ *     -l
+ *         List all tasks and executions for a given time period.
+ *         Executions sorted chronologically (see '-r'), and tasks alphabetically
+ *         See '-t=<num>s|m|h|d' on how to specify the time limit.
+ *         Default time period is one hour, i.e '-t=1h'
+ *
+ *         This is a default mode when command is called without parameters.
+ *     -s=<substring>
+ *         List all tasks and executions for a given task name substring.
+ *         Executions sorted chronologically (see '-r'), and tasks alphabetically
+ *         See '-t=<num>s|m|h|d' on how to specify the time limit.
+ *         Default time period is one hour, i.e '-t=1h'
+ *     -g
+ *         List all tasks grouped by nodes for a given time period.
+ *         Tasks sorted alphabetically
+ *         See '-t=<num>s|m|h|d' on how to specify the time limit.
+ *         Default time period is one hour, i.e '-t=1h'
+ *     -h
+ *         List all tasks grouped by hosts for a given time period.
+ *         Tasks sorted alphabetically
+ *         See '-t=<num>s|m|h|d' on how to specify the time limit.
+ *         Default time period is one hour, i.e '-t=1h'
+ *     -t=<num>s|m|h|d
+ *         Defines time frame for '-l' parameter:
+ *            =<num>s Last <num> seconds.
+ *            =<num>m Last <num> minutes.
+ *            =<num>h Last <num> hours.
+ *            =<num>d Last <num> days.
+ *     -r
+ *         Reverse sorting of executions.
+ *     -n=<task-name>
+ *         Prints aggregated statistic for named task.
+ *     -e=<exec-id>
+ *         Prints aggregated statistic for given task execution.
+ * }}}
+ *
+ * ====Examples====
+ * {{{
+ *     tasks "-l"
+ *         Prints list of all tasks and executions for the last hour (default).
+ *     tasks
+ *         Prints list of all tasks and executions for the last hour (default).
+ *     tasks "-l -t=5m"
+ *         Prints list of tasks and executions that started during last 5 minutes.
+ *     tasks "-s=Task"
+ *         Prints list of all tasks and executions that have 'Task' in task name.
+ *     tasks "-g"
+ *         Prints list of tasks grouped by nodes.
+ *     tasks "-g -t=5m"
+ *         Prints list of tasks that started during last 5 minutes grouped by nodes.
+ *     tasks "-h"
+ *         Prints list of tasks grouped by hosts.
+ *     tasks "-h -t=5m"
+ *         Prints list of tasks that started during last 5 minutes grouped by hosts.
+ *     tasks "-n=GridTask"
+ *         Prints summary for task named 'GridTask'.
+ *     tasks "-e=7D5CB773-225C-4165-8162-3BB67337894B"
+ *         Traces task execution with ID '7D5CB773-225C-4165-8162-3BB67337894B'.
+ *     tasks "-e=@e1"
+ *         Traces task execution with ID taken from 'e1' memory variable.
+ * }}}
+ */
+package object tasks


Mime
View raw message