github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/juju/status.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  
    10  	"launchpad.net/gnuflag"
    11  
    12  	"github.com/juju/juju/cmd"
    13  	"github.com/juju/juju/cmd/envcmd"
    14  	"github.com/juju/juju/environs/network"
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/juju"
    17  	"github.com/juju/juju/state/api"
    18  	"github.com/juju/juju/state/api/params"
    19  	"github.com/juju/juju/state/apiserver/client"
    20  )
    21  
    22  type StatusCommand struct {
    23  	envcmd.EnvCommandBase
    24  	out      cmd.Output
    25  	patterns []string
    26  }
    27  
    28  var statusDoc = `
    29  This command will report on the runtime state of various system entities.
    30  
    31  Service or unit names may be specified to filter the status to only those
    32  services and units that match, along with the related machines, services
    33  and units. If a subordinate unit is matched, then its principal unit will
    34  be displayed. If a principal unit is matched, then all of its subordinates
    35  will be displayed.
    36  
    37  Wildcards ('*') may be specified in service/unit names to match any sequence
    38  of characters. For example, 'nova-*' will match any service whose name begins
    39  with 'nova-': 'nova-compute', 'nova-volume', etc.
    40  `
    41  
    42  func (c *StatusCommand) Info() *cmd.Info {
    43  	return &cmd.Info{
    44  		Name:    "status",
    45  		Args:    "[pattern ...]",
    46  		Purpose: "output status information about an environment",
    47  		Doc:     statusDoc,
    48  		Aliases: []string{"stat"},
    49  	}
    50  }
    51  
    52  func (c *StatusCommand) SetFlags(f *gnuflag.FlagSet) {
    53  	c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
    54  		"yaml": cmd.FormatYaml,
    55  		"json": cmd.FormatJson,
    56  	})
    57  }
    58  
    59  func (c *StatusCommand) Init(args []string) error {
    60  	c.patterns = args
    61  	return nil
    62  }
    63  
    64  var connectionError = `Unable to connect to environment %q.
    65  Please check your credentials or use 'juju bootstrap' to create a new environment.
    66  
    67  Error details:
    68  %v
    69  `
    70  
    71  func (c *StatusCommand) Run(ctx *cmd.Context) error {
    72  	// Just verify the pattern validity client side, do not use the matcher
    73  	_, err := client.NewUnitMatcher(c.patterns)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	apiclient, err := juju.NewAPIClientFromName(c.EnvName)
    78  	if err != nil {
    79  		return fmt.Errorf(connectionError, c.EnvName, err)
    80  	}
    81  	defer apiclient.Close()
    82  
    83  	status, err := apiclient.Status(c.patterns)
    84  	// Display any error, but continue to print status if some was returned
    85  	if err != nil {
    86  		fmt.Fprintf(ctx.Stderr, "%v\n", err)
    87  	}
    88  	result := formatStatus(status)
    89  	return c.out.Write(ctx, result)
    90  }
    91  
    92  type formattedStatus struct {
    93  	Environment string                   `json:"environment"`
    94  	Machines    map[string]machineStatus `json:"machines"`
    95  	Services    map[string]serviceStatus `json:"services"`
    96  	Networks    map[string]networkStatus `json:"networks,omitempty" yaml:",omitempty"`
    97  }
    98  
    99  type errorStatus struct {
   100  	StatusError string `json:"status-error" yaml:"status-error"`
   101  }
   102  
   103  type machineStatus struct {
   104  	Err            error                    `json:"-" yaml:",omitempty"`
   105  	AgentState     params.Status            `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`
   106  	AgentStateInfo string                   `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`
   107  	AgentVersion   string                   `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`
   108  	DNSName        string                   `json:"dns-name,omitempty" yaml:"dns-name,omitempty"`
   109  	InstanceId     instance.Id              `json:"instance-id,omitempty" yaml:"instance-id,omitempty"`
   110  	InstanceState  string                   `json:"instance-state,omitempty" yaml:"instance-state,omitempty"`
   111  	Life           string                   `json:"life,omitempty" yaml:"life,omitempty"`
   112  	Series         string                   `json:"series,omitempty" yaml:"series,omitempty"`
   113  	Id             string                   `json:"-" yaml:"-"`
   114  	Containers     map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"`
   115  	Hardware       string                   `json:"hardware,omitempty" yaml:"hardware,omitempty"`
   116  	HAStatus       string                   `json:"state-server-member-status,omitempty" yaml:"state-server-member-status,omitempty"`
   117  }
   118  
   119  // A goyaml bug means we can't declare these types
   120  // locally to the GetYAML methods.
   121  type machineStatusNoMarshal machineStatus
   122  
   123  func (s machineStatus) MarshalJSON() ([]byte, error) {
   124  	if s.Err != nil {
   125  		return json.Marshal(errorStatus{s.Err.Error()})
   126  	}
   127  	return json.Marshal(machineStatusNoMarshal(s))
   128  }
   129  
   130  func (s machineStatus) GetYAML() (tag string, value interface{}) {
   131  	if s.Err != nil {
   132  		return "", errorStatus{s.Err.Error()}
   133  	}
   134  	// TODO(rog) rename mNoMethods to noMethods (and also in
   135  	// the other GetYAML methods) when people are using the non-buggy
   136  	// goyaml version.
   137  	type mNoMethods machineStatus
   138  	return "", mNoMethods(s)
   139  }
   140  
   141  type serviceStatus struct {
   142  	Err           error                 `json:"-" yaml:",omitempty"`
   143  	Charm         string                `json:"charm" yaml:"charm"`
   144  	CanUpgradeTo  string                `json:"can-upgrade-to,omitempty" yaml:"can-upgrade-to,omitempty"`
   145  	Exposed       bool                  `json:"exposed" yaml:"exposed"`
   146  	Life          string                `json:"life,omitempty" yaml:"life,omitempty"`
   147  	Relations     map[string][]string   `json:"relations,omitempty" yaml:"relations,omitempty"`
   148  	Networks      map[string][]string   `json:"networks,omitempty" yaml:"networks,omitempty"`
   149  	SubordinateTo []string              `json:"subordinate-to,omitempty" yaml:"subordinate-to,omitempty"`
   150  	Units         map[string]unitStatus `json:"units,omitempty" yaml:"units,omitempty"`
   151  }
   152  
   153  type serviceStatusNoMarshal serviceStatus
   154  
   155  func (s serviceStatus) MarshalJSON() ([]byte, error) {
   156  	if s.Err != nil {
   157  		return json.Marshal(errorStatus{s.Err.Error()})
   158  	}
   159  	type sNoMethods serviceStatus
   160  	return json.Marshal(sNoMethods(s))
   161  }
   162  
   163  func (s serviceStatus) GetYAML() (tag string, value interface{}) {
   164  	if s.Err != nil {
   165  		return "", errorStatus{s.Err.Error()}
   166  	}
   167  	type sNoMethods serviceStatus
   168  	return "", sNoMethods(s)
   169  }
   170  
   171  type unitStatus struct {
   172  	Err            error                 `json:"-" yaml:",omitempty"`
   173  	Charm          string                `json:"upgrading-from,omitempty" yaml:"upgrading-from,omitempty"`
   174  	AgentState     params.Status         `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`
   175  	AgentStateInfo string                `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`
   176  	AgentVersion   string                `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`
   177  	Life           string                `json:"life,omitempty" yaml:"life,omitempty"`
   178  	Machine        string                `json:"machine,omitempty" yaml:"machine,omitempty"`
   179  	OpenedPorts    []string              `json:"open-ports,omitempty" yaml:"open-ports,omitempty"`
   180  	PublicAddress  string                `json:"public-address,omitempty" yaml:"public-address,omitempty"`
   181  	Subordinates   map[string]unitStatus `json:"subordinates,omitempty" yaml:"subordinates,omitempty"`
   182  }
   183  
   184  type unitStatusNoMarshal unitStatus
   185  
   186  func (s unitStatus) MarshalJSON() ([]byte, error) {
   187  	if s.Err != nil {
   188  		return json.Marshal(errorStatus{s.Err.Error()})
   189  	}
   190  	return json.Marshal(unitStatusNoMarshal(s))
   191  }
   192  
   193  func (s unitStatus) GetYAML() (tag string, value interface{}) {
   194  	if s.Err != nil {
   195  		return "", errorStatus{s.Err.Error()}
   196  	}
   197  	type uNoMethods unitStatus
   198  	return "", unitStatusNoMarshal(s)
   199  }
   200  
   201  type networkStatus struct {
   202  	Err        error      `json:"-" yaml:",omitempty"`
   203  	ProviderId network.Id `json:"provider-id" yaml:"provider-id"`
   204  	CIDR       string     `json:"cidr,omitempty" yaml:"cidr,omitempty"`
   205  	VLANTag    int        `json:"vlan-tag,omitempty" yaml:"vlan-tag,omitempty"`
   206  }
   207  
   208  type networkStatusNoMarshal networkStatus
   209  
   210  func (n networkStatus) MarshalJSON() ([]byte, error) {
   211  	if n.Err != nil {
   212  		return json.Marshal(errorStatus{n.Err.Error()})
   213  	}
   214  	type nNoMethods networkStatus
   215  	return json.Marshal(nNoMethods(n))
   216  }
   217  
   218  func (n networkStatus) GetYAML() (tag string, value interface{}) {
   219  	if n.Err != nil {
   220  		return "", errorStatus{n.Err.Error()}
   221  	}
   222  	type nNoMethods networkStatus
   223  	return "", nNoMethods(n)
   224  }
   225  
   226  func formatStatus(status *api.Status) formattedStatus {
   227  	if status == nil {
   228  		return formattedStatus{}
   229  	}
   230  	out := formattedStatus{
   231  		Environment: status.EnvironmentName,
   232  		Machines:    make(map[string]machineStatus),
   233  		Services:    make(map[string]serviceStatus),
   234  	}
   235  	for k, m := range status.Machines {
   236  		out.Machines[k] = formatMachine(m)
   237  	}
   238  	for k, s := range status.Services {
   239  		out.Services[k] = formatService(s)
   240  	}
   241  	for k, n := range status.Networks {
   242  		if out.Networks == nil {
   243  			out.Networks = make(map[string]networkStatus)
   244  		}
   245  		out.Networks[k] = formatNetwork(n)
   246  	}
   247  	return out
   248  }
   249  
   250  func formatMachine(machine api.MachineStatus) machineStatus {
   251  	out := machineStatus{
   252  		Err:            machine.Err,
   253  		AgentState:     machine.AgentState,
   254  		AgentStateInfo: machine.AgentStateInfo,
   255  		AgentVersion:   machine.AgentVersion,
   256  		DNSName:        machine.DNSName,
   257  		InstanceId:     machine.InstanceId,
   258  		InstanceState:  machine.InstanceState,
   259  		Life:           machine.Life,
   260  		Series:         machine.Series,
   261  		Id:             machine.Id,
   262  		Containers:     make(map[string]machineStatus),
   263  		Hardware:       machine.Hardware,
   264  	}
   265  	for k, m := range machine.Containers {
   266  		out.Containers[k] = formatMachine(m)
   267  	}
   268  
   269  	for _, job := range machine.Jobs {
   270  		if job == params.JobManageEnviron {
   271  			out.HAStatus = makeHAStatus(machine.HasVote, machine.WantsVote)
   272  			break
   273  		}
   274  	}
   275  	return out
   276  }
   277  
   278  func makeHAStatus(hasVote, wantsVote bool) string {
   279  	var s string
   280  	switch {
   281  	case hasVote && wantsVote:
   282  		s = "has-vote"
   283  	case hasVote && !wantsVote:
   284  		s = "removing-vote"
   285  	case !hasVote && wantsVote:
   286  		s = "adding-vote"
   287  	case !hasVote && !wantsVote:
   288  		s = "no-vote"
   289  	}
   290  	return s
   291  }
   292  
   293  func formatService(service api.ServiceStatus) serviceStatus {
   294  	out := serviceStatus{
   295  		Err:           service.Err,
   296  		Charm:         service.Charm,
   297  		Exposed:       service.Exposed,
   298  		Life:          service.Life,
   299  		Relations:     service.Relations,
   300  		Networks:      make(map[string][]string),
   301  		CanUpgradeTo:  service.CanUpgradeTo,
   302  		SubordinateTo: service.SubordinateTo,
   303  		Units:         make(map[string]unitStatus),
   304  	}
   305  	if len(service.Networks.Enabled) > 0 {
   306  		out.Networks["enabled"] = service.Networks.Enabled
   307  	}
   308  	if len(service.Networks.Disabled) > 0 {
   309  		out.Networks["disabled"] = service.Networks.Disabled
   310  	}
   311  	for k, m := range service.Units {
   312  		out.Units[k] = formatUnit(m)
   313  	}
   314  	return out
   315  }
   316  
   317  func formatUnit(unit api.UnitStatus) unitStatus {
   318  	out := unitStatus{
   319  		Err:            unit.Err,
   320  		AgentState:     unit.AgentState,
   321  		AgentStateInfo: unit.AgentStateInfo,
   322  		AgentVersion:   unit.AgentVersion,
   323  		Life:           unit.Life,
   324  		Machine:        unit.Machine,
   325  		OpenedPorts:    unit.OpenedPorts,
   326  		PublicAddress:  unit.PublicAddress,
   327  		Charm:          unit.Charm,
   328  		Subordinates:   make(map[string]unitStatus),
   329  	}
   330  	for k, m := range unit.Subordinates {
   331  		out.Subordinates[k] = formatUnit(m)
   332  	}
   333  	return out
   334  }
   335  
   336  func formatNetwork(network api.NetworkStatus) networkStatus {
   337  	return networkStatus{
   338  		Err:        network.Err,
   339  		ProviderId: network.ProviderId,
   340  		CIDR:       network.CIDR,
   341  		VLANTag:    network.VLANTag,
   342  	}
   343  }