github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/command/node_status.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  )
     8  
     9  type NodeStatusCommand struct {
    10  	Meta
    11  }
    12  
    13  func (c *NodeStatusCommand) Help() string {
    14  	helpText := `
    15  Usage: nomad node-status [options] <node>
    16  
    17    Display status information about a given node. The list of nodes
    18    returned includes only nodes which jobs may be scheduled to, and
    19    includes status and other high-level information.
    20  
    21    If a node ID is passed, information for that specific node will
    22    be displayed. If no node ID's are passed, then a short-hand
    23    list of all nodes will be displayed.
    24  
    25  General Options:
    26  
    27    ` + generalOptionsUsage() + `
    28  
    29  Node Status Options:
    30  
    31    -short
    32      Display short output. Used only when a single node is being
    33      queried, and drops verbose output about node allocations.
    34  
    35    -verbose
    36      Display full information.
    37  `
    38  	return strings.TrimSpace(helpText)
    39  }
    40  
    41  func (c *NodeStatusCommand) Synopsis() string {
    42  	return "Display status information about nodes"
    43  }
    44  
    45  func (c *NodeStatusCommand) Run(args []string) int {
    46  	var short, verbose bool
    47  
    48  	flags := c.Meta.FlagSet("node-status", FlagSetClient)
    49  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    50  	flags.BoolVar(&short, "short", false, "")
    51  	flags.BoolVar(&verbose, "verbose", false, "")
    52  
    53  	if err := flags.Parse(args); err != nil {
    54  		return 1
    55  	}
    56  
    57  	// Check that we got either a single node or none
    58  	args = flags.Args()
    59  	if len(args) > 1 {
    60  		c.Ui.Error(c.Help())
    61  		return 1
    62  	}
    63  
    64  	// Truncate the id unless full length is requested
    65  	length := shortId
    66  	if verbose {
    67  		length = fullId
    68  	}
    69  
    70  	// Get the HTTP client
    71  	client, err := c.Meta.Client()
    72  	if err != nil {
    73  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
    74  		return 1
    75  	}
    76  
    77  	// Use list mode if no node name was provided
    78  	if len(args) == 0 {
    79  		// Query the node info
    80  		nodes, _, err := client.Nodes().List(nil)
    81  		if err != nil {
    82  			c.Ui.Error(fmt.Sprintf("Error querying node status: %s", err))
    83  			return 1
    84  		}
    85  
    86  		// Return nothing if no nodes found
    87  		if len(nodes) == 0 {
    88  			return 0
    89  		}
    90  
    91  		// Format the nodes list
    92  		out := make([]string, len(nodes)+1)
    93  		out[0] = "ID|Datacenter|Name|Class|Drain|Status"
    94  		for i, node := range nodes {
    95  			out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%v|%s",
    96  				limit(node.ID, length),
    97  				node.Datacenter,
    98  				node.Name,
    99  				node.NodeClass,
   100  				node.Drain,
   101  				node.Status)
   102  		}
   103  
   104  		// Dump the output
   105  		c.Ui.Output(formatList(out))
   106  		return 0
   107  	}
   108  
   109  	// Query the specific node
   110  	nodeID := args[0]
   111  	node, _, err := client.Nodes().Info(nodeID, nil)
   112  	if err != nil {
   113  		if len(nodeID) == 1 {
   114  			c.Ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
   115  			return 1
   116  		}
   117  		if len(nodeID)%2 == 1 {
   118  			// Identifiers must be of even length, so we strip off the last byte
   119  			// to provide a consistent user experience.
   120  			nodeID = nodeID[:len(nodeID)-1]
   121  		}
   122  
   123  		// Exact lookup failed, try with prefix based search
   124  		nodes, _, err := client.Nodes().PrefixList(nodeID)
   125  		if err != nil {
   126  			c.Ui.Error(fmt.Sprintf("Error querying node info: %s", err))
   127  			return 1
   128  		}
   129  		// Return error if no nodes are found
   130  		if len(nodes) == 0 {
   131  			c.Ui.Error(fmt.Sprintf("No node(s) with prefix %q found", nodeID))
   132  			return 1
   133  		}
   134  		if len(nodes) > 1 {
   135  			// Format the nodes list that matches the prefix so that the user
   136  			// can create a more specific request
   137  			out := make([]string, len(nodes)+1)
   138  			out[0] = "ID|Datacenter|Name|Class|Drain|Status"
   139  			for i, node := range nodes {
   140  				out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%v|%s",
   141  					limit(node.ID, length),
   142  					node.Datacenter,
   143  					node.Name,
   144  					node.NodeClass,
   145  					node.Drain,
   146  					node.Status)
   147  			}
   148  			// Dump the output
   149  			c.Ui.Output(fmt.Sprintf("Prefix matched multiple nodes\n\n%s", formatList(out)))
   150  			return 0
   151  		}
   152  		// Prefix lookup matched a single node
   153  		node, _, err = client.Nodes().Info(nodes[0].ID, nil)
   154  		if err != nil {
   155  			c.Ui.Error(fmt.Sprintf("Error querying node info: %s", err))
   156  			return 1
   157  		}
   158  	}
   159  
   160  	m := node.Attributes
   161  	keys := make([]string, len(m))
   162  	for k := range m {
   163  		keys = append(keys, k)
   164  	}
   165  	sort.Strings(keys)
   166  
   167  	var attributes []string
   168  	for _, k := range keys {
   169  		if k != "" {
   170  			attributes = append(attributes, fmt.Sprintf("%s:%s", k, m[k]))
   171  		}
   172  	}
   173  
   174  	// Format the output
   175  	basic := []string{
   176  		fmt.Sprintf("ID|%s", limit(node.ID, length)),
   177  		fmt.Sprintf("Name|%s", node.Name),
   178  		fmt.Sprintf("Class|%s", node.NodeClass),
   179  		fmt.Sprintf("Datacenter|%s", node.Datacenter),
   180  		fmt.Sprintf("Drain|%v", node.Drain),
   181  		fmt.Sprintf("Status|%s", node.Status),
   182  		fmt.Sprintf("Attributes|%s", strings.Join(attributes, ", ")),
   183  	}
   184  
   185  	var allocs []string
   186  	if !short {
   187  		// Query the node allocations
   188  		nodeAllocs, _, err := client.Nodes().Allocations(node.ID, nil)
   189  		if err != nil {
   190  			c.Ui.Error(fmt.Sprintf("Error querying node allocations: %s", err))
   191  			return 1
   192  		}
   193  
   194  		// Format the allocations
   195  		allocs = make([]string, len(nodeAllocs)+1)
   196  		allocs[0] = "ID|Eval ID|Job ID|Task Group|Desired Status|Client Status"
   197  		for i, alloc := range nodeAllocs {
   198  			allocs[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s",
   199  				limit(alloc.ID, length),
   200  				limit(alloc.EvalID, length),
   201  				alloc.JobID,
   202  				alloc.TaskGroup,
   203  				alloc.DesiredStatus,
   204  				alloc.ClientStatus)
   205  		}
   206  	}
   207  
   208  	// Dump the output
   209  	c.Ui.Output(formatKV(basic))
   210  	if !short {
   211  		c.Ui.Output("\n==> Allocations")
   212  		c.Ui.Output(formatList(allocs))
   213  	}
   214  	return 0
   215  }