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