github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/command/alloc_status.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/nomad/api"
    10  )
    11  
    12  type AllocStatusCommand struct {
    13  	Meta
    14  }
    15  
    16  func (c *AllocStatusCommand) Help() string {
    17  	helpText := `
    18  Usage: nomad alloc-status [options] <allocation>
    19  
    20    Display information about existing allocations and its tasks. This command can
    21    be used to inspect the current status of all allocation, including its running
    22    status, metadata, and verbose failure messages reported by internal
    23    subsystems.
    24  
    25  General Options:
    26  
    27    ` + generalOptionsUsage() + `
    28  
    29  
    30    -short
    31      Display short output. Shows only the most recent task event.
    32  
    33    -verbose
    34      Show full information.
    35  `
    36  
    37  	return strings.TrimSpace(helpText)
    38  }
    39  
    40  func (c *AllocStatusCommand) Synopsis() string {
    41  	return "Display allocation status information and metadata"
    42  }
    43  
    44  func (c *AllocStatusCommand) Run(args []string) int {
    45  	var short, verbose bool
    46  
    47  	flags := c.Meta.FlagSet("alloc-status", FlagSetClient)
    48  	flags.Usage = func() { c.Ui.Output(c.Help()) }
    49  	flags.BoolVar(&short, "short", false, "")
    50  	flags.BoolVar(&verbose, "verbose", false, "")
    51  
    52  	if err := flags.Parse(args); err != nil {
    53  		return 1
    54  	}
    55  
    56  	// Check that we got exactly one allocation ID
    57  	args = flags.Args()
    58  	if len(args) != 1 {
    59  		c.Ui.Error(c.Help())
    60  		return 1
    61  	}
    62  	allocID := args[0]
    63  
    64  	// Get the HTTP client
    65  	client, err := c.Meta.Client()
    66  	if err != nil {
    67  		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
    68  		return 1
    69  	}
    70  
    71  	// Truncate the id unless full length is requested
    72  	length := shortId
    73  	if verbose {
    74  		length = fullId
    75  	}
    76  
    77  	// Query the allocation info
    78  	alloc, _, err := client.Allocations().Info(allocID, nil)
    79  	if err != nil {
    80  		if len(allocID) == 1 {
    81  			c.Ui.Error(fmt.Sprintf("Identifier must contain at least two characters."))
    82  			return 1
    83  		}
    84  		if len(allocID)%2 == 1 {
    85  			// Identifiers must be of even length, so we strip off the last byte
    86  			// to provide a consistent user experience.
    87  			allocID = allocID[:len(allocID)-1]
    88  		}
    89  
    90  		allocs, _, err := client.Allocations().PrefixList(allocID)
    91  		if err != nil {
    92  			c.Ui.Error(fmt.Sprintf("Error querying allocation: %v", err))
    93  			return 1
    94  		}
    95  		if len(allocs) == 0 {
    96  			c.Ui.Error(fmt.Sprintf("No allocation(s) with prefix or id %q found", allocID))
    97  			return 1
    98  		}
    99  		if len(allocs) > 1 {
   100  			// Format the allocs
   101  			out := make([]string, len(allocs)+1)
   102  			out[0] = "ID|Eval ID|Job ID|Task Group|Desired Status|Client Status"
   103  			for i, alloc := range allocs {
   104  				out[i+1] = fmt.Sprintf("%s|%s|%s|%s|%s|%s",
   105  					limit(alloc.ID, length),
   106  					limit(alloc.EvalID, length),
   107  					alloc.JobID,
   108  					alloc.TaskGroup,
   109  					alloc.DesiredStatus,
   110  					alloc.ClientStatus,
   111  				)
   112  			}
   113  			c.Ui.Output(fmt.Sprintf("Prefix matched multiple allocations\n\n%s", formatList(out)))
   114  			return 0
   115  		}
   116  		// Prefix lookup matched a single allocation
   117  		alloc, _, err = client.Allocations().Info(allocs[0].ID, nil)
   118  		if err != nil {
   119  			c.Ui.Error(fmt.Sprintf("Error querying allocation: %s", err))
   120  			return 1
   121  		}
   122  	}
   123  
   124  	// Format the allocation data
   125  	basic := []string{
   126  		fmt.Sprintf("ID|%s", limit(alloc.ID, length)),
   127  		fmt.Sprintf("Eval ID|%s", limit(alloc.EvalID, length)),
   128  		fmt.Sprintf("Name|%s", alloc.Name),
   129  		fmt.Sprintf("Node ID|%s", limit(alloc.NodeID, length)),
   130  		fmt.Sprintf("Job ID|%s", alloc.JobID),
   131  		fmt.Sprintf("Client Status|%s", alloc.ClientStatus),
   132  		fmt.Sprintf("Evaluated Nodes|%d", alloc.Metrics.NodesEvaluated),
   133  		fmt.Sprintf("Filtered Nodes|%d", alloc.Metrics.NodesFiltered),
   134  		fmt.Sprintf("Exhausted Nodes|%d", alloc.Metrics.NodesExhausted),
   135  		fmt.Sprintf("Allocation Time|%s", alloc.Metrics.AllocationTime),
   136  		fmt.Sprintf("Failures|%d", alloc.Metrics.CoalescedFailures),
   137  	}
   138  	c.Ui.Output(formatKV(basic))
   139  
   140  	// Print the state of each task.
   141  	if short {
   142  		c.shortTaskStatus(alloc)
   143  	} else {
   144  		c.taskStatus(alloc)
   145  	}
   146  
   147  	// Format the detailed status
   148  	c.Ui.Output("\n==> Status")
   149  	dumpAllocStatus(c.Ui, alloc, length)
   150  
   151  	return 0
   152  }
   153  
   154  // shortTaskStatus prints out the current state of each task.
   155  func (c *AllocStatusCommand) shortTaskStatus(alloc *api.Allocation) {
   156  	tasks := make([]string, 0, len(alloc.TaskStates)+1)
   157  	tasks = append(tasks, "Name|State|Last Event|Time")
   158  	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
   159  		fmt.Println(task)
   160  		state := alloc.TaskStates[task]
   161  		lastState := state.State
   162  		var lastEvent, lastTime string
   163  
   164  		l := len(state.Events)
   165  		if l != 0 {
   166  			last := state.Events[l-1]
   167  			lastEvent = last.Type
   168  			lastTime = c.formatUnixNanoTime(last.Time)
   169  		}
   170  
   171  		tasks = append(tasks, fmt.Sprintf("%s|%s|%s|%s",
   172  			task, lastState, lastEvent, lastTime))
   173  	}
   174  
   175  	c.Ui.Output("\n==> Tasks")
   176  	c.Ui.Output(formatList(tasks))
   177  }
   178  
   179  // taskStatus prints out the most recent events for each task.
   180  func (c *AllocStatusCommand) taskStatus(alloc *api.Allocation) {
   181  	for task := range c.sortedTaskStateIterator(alloc.TaskStates) {
   182  		state := alloc.TaskStates[task]
   183  		events := make([]string, len(state.Events)+1)
   184  		events[0] = "Time|Type|Description"
   185  
   186  		size := len(state.Events)
   187  		for i, event := range state.Events {
   188  			formatedTime := c.formatUnixNanoTime(event.Time)
   189  
   190  			// Build up the description based on the event type.
   191  			var desc string
   192  			switch event.Type {
   193  			case api.TaskDriverFailure:
   194  				desc = event.DriverError
   195  			case api.TaskKilled:
   196  				desc = event.KillError
   197  			case api.TaskTerminated:
   198  				var parts []string
   199  				parts = append(parts, fmt.Sprintf("Exit Code: %d", event.ExitCode))
   200  
   201  				if event.Signal != 0 {
   202  					parts = append(parts, fmt.Sprintf("Signal: %d", event.Signal))
   203  				}
   204  
   205  				if event.Message != "" {
   206  					parts = append(parts, fmt.Sprintf("Exit Message: %q", event.Message))
   207  				}
   208  				desc = strings.Join(parts, ", ")
   209  			}
   210  
   211  			// Reverse order so we are sorted by time
   212  			events[size-i] = fmt.Sprintf("%s|%s|%s", formatedTime, event.Type, desc)
   213  		}
   214  
   215  		c.Ui.Output(fmt.Sprintf("\n==> Task %q is %q\nRecent Events:", task, state.State))
   216  		c.Ui.Output(formatList(events))
   217  	}
   218  }
   219  
   220  // formatUnixNanoTime is a helper for formating time for output.
   221  func (c *AllocStatusCommand) formatUnixNanoTime(nano int64) string {
   222  	t := time.Unix(0, nano)
   223  	return formatTime(t)
   224  }
   225  
   226  // sortedTaskStateIterator is a helper that takes the task state map and returns a
   227  // channel that returns the keys in a sorted order.
   228  func (c *AllocStatusCommand) sortedTaskStateIterator(m map[string]*api.TaskState) <-chan string {
   229  	output := make(chan string, len(m))
   230  	keys := make([]string, len(m))
   231  	i := 0
   232  	for k := range m {
   233  		keys[i] = k
   234  		i++
   235  	}
   236  	sort.Strings(keys)
   237  
   238  	for _, key := range keys {
   239  		output <- key
   240  	}
   241  
   242  	close(output)
   243  	return output
   244  }