github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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  	"launchpad.net/juju-core/cmd"
    13  	"launchpad.net/juju-core/instance"
    14  	"launchpad.net/juju-core/juju"
    15  	"launchpad.net/juju-core/state/api"
    16  	"launchpad.net/juju-core/state/api/params"
    17  	"launchpad.net/juju-core/state/statecmd"
    18  )
    19  
    20  type StatusCommand struct {
    21  	cmd.EnvCommandBase
    22  	out      cmd.Output
    23  	patterns []string
    24  }
    25  
    26  var statusDoc = `
    27  This command will report on the runtime state of various system entities.
    28  
    29  Service or unit names may be specified to filter the status to only those
    30  services and units that match, along with the related machines, services
    31  and units. If a subordinate unit is matched, then its principal unit will
    32  be displayed. If a principal unit is matched, then all of its subordinates
    33  will be displayed.
    34  
    35  Wildcards ('*') may be specified in service/unit names to match any sequence
    36  of characters. For example, 'nova-*' will match any service whose name begins
    37  with 'nova-': 'nova-compute', 'nova-volume', etc.
    38  `
    39  
    40  func (c *StatusCommand) Info() *cmd.Info {
    41  	return &cmd.Info{
    42  		Name:    "status",
    43  		Args:    "[pattern ...]",
    44  		Purpose: "output status information about an environment",
    45  		Doc:     statusDoc,
    46  		Aliases: []string{"stat"},
    47  	}
    48  }
    49  
    50  func (c *StatusCommand) SetFlags(f *gnuflag.FlagSet) {
    51  	c.EnvCommandBase.SetFlags(f)
    52  	c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{
    53  		"yaml": cmd.FormatYaml,
    54  		"json": cmd.FormatJson,
    55  	})
    56  }
    57  
    58  func (c *StatusCommand) Init(args []string) error {
    59  	c.patterns = args
    60  	return nil
    61  }
    62  
    63  var connectionError = `Unable to connect to environment "%s".
    64  Please check your credentials or use 'juju bootstrap' to create a new environment.
    65  
    66  Error details:
    67  %v
    68  `
    69  
    70  func (c *StatusCommand) getStatus1dot16() (*api.Status, error) {
    71  	conn, err := juju.NewConnFromName(c.EnvName)
    72  	if err != nil {
    73  		return nil, fmt.Errorf(connectionError, c.EnvName, err)
    74  	}
    75  	defer conn.Close()
    76  
    77  	return statecmd.Status(conn, c.patterns)
    78  }
    79  
    80  func (c *StatusCommand) Run(ctx *cmd.Context) error {
    81  	// Just verify the pattern validity client side, do not use the matcher
    82  	_, err := statecmd.NewUnitMatcher(c.patterns)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	apiclient, err := juju.NewAPIClientFromName(c.EnvName)
    87  	if err != nil {
    88  		return fmt.Errorf(connectionError, c.EnvName, err)
    89  	}
    90  	defer apiclient.Close()
    91  
    92  	status, err := apiclient.Status(c.patterns)
    93  	if params.IsCodeNotImplemented(err) {
    94  		logger.Infof("Status not supported by the API server, " +
    95  			"falling back to 1.16 compatibility mode " +
    96  			"(direct DB access)")
    97  		status, err = c.getStatus1dot16()
    98  	}
    99  	// Display any error, but continue to print status if some was returned
   100  	if err != nil {
   101  		fmt.Fprintf(ctx.Stderr, "%v\n", err)
   102  	}
   103  	result := formatStatus(status)
   104  	return c.out.Write(ctx, result)
   105  }
   106  
   107  type formattedStatus struct {
   108  	Environment string                   `json:"environment"`
   109  	Machines    map[string]machineStatus `json:"machines"`
   110  	Services    map[string]serviceStatus `json:"services"`
   111  }
   112  
   113  type errorStatus struct {
   114  	StatusError string `json:"status-error" yaml:"status-error"`
   115  }
   116  
   117  type machineStatus struct {
   118  	Err            error                    `json:"-" yaml:",omitempty"`
   119  	AgentState     params.Status            `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`
   120  	AgentStateInfo string                   `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`
   121  	AgentVersion   string                   `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`
   122  	DNSName        string                   `json:"dns-name,omitempty" yaml:"dns-name,omitempty"`
   123  	InstanceId     instance.Id              `json:"instance-id,omitempty" yaml:"instance-id,omitempty"`
   124  	InstanceState  string                   `json:"instance-state,omitempty" yaml:"instance-state,omitempty"`
   125  	Life           string                   `json:"life,omitempty" yaml:"life,omitempty"`
   126  	Series         string                   `json:"series,omitempty" yaml:"series,omitempty"`
   127  	Id             string                   `json:"-" yaml:"-"`
   128  	Containers     map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"`
   129  	Hardware       string                   `json:"hardware,omitempty" yaml:"hardware,omitempty"`
   130  }
   131  
   132  // A goyaml bug means we can't declare these types
   133  // locally to the GetYAML methods.
   134  type machineStatusNoMarshal machineStatus
   135  
   136  func (s machineStatus) MarshalJSON() ([]byte, error) {
   137  	if s.Err != nil {
   138  		return json.Marshal(errorStatus{s.Err.Error()})
   139  	}
   140  	return json.Marshal(machineStatusNoMarshal(s))
   141  }
   142  
   143  func (s machineStatus) GetYAML() (tag string, value interface{}) {
   144  	if s.Err != nil {
   145  		return "", errorStatus{s.Err.Error()}
   146  	}
   147  	// TODO(rog) rename mNoMethods to noMethods (and also in
   148  	// the other GetYAML methods) when people are using the non-buggy
   149  	// goyaml version.
   150  	type mNoMethods machineStatus
   151  	return "", mNoMethods(s)
   152  }
   153  
   154  type serviceStatus struct {
   155  	Err           error                 `json:"-" yaml:",omitempty"`
   156  	Charm         string                `json:"charm" yaml:"charm"`
   157  	CanUpgradeTo  string                `json:"can-upgrade-to,omitempty" yaml:"can-upgrade-to,omitempty"`
   158  	Exposed       bool                  `json:"exposed" yaml:"exposed"`
   159  	Life          string                `json:"life,omitempty" yaml:"life,omitempty"`
   160  	Relations     map[string][]string   `json:"relations,omitempty" yaml:"relations,omitempty"`
   161  	SubordinateTo []string              `json:"subordinate-to,omitempty" yaml:"subordinate-to,omitempty"`
   162  	Units         map[string]unitStatus `json:"units,omitempty" yaml:"units,omitempty"`
   163  }
   164  
   165  type serviceStatusNoMarshal serviceStatus
   166  
   167  func (s serviceStatus) MarshalJSON() ([]byte, error) {
   168  	if s.Err != nil {
   169  		return json.Marshal(errorStatus{s.Err.Error()})
   170  	}
   171  	type sNoMethods serviceStatus
   172  	return json.Marshal(sNoMethods(s))
   173  }
   174  
   175  func (s serviceStatus) GetYAML() (tag string, value interface{}) {
   176  	if s.Err != nil {
   177  		return "", errorStatus{s.Err.Error()}
   178  	}
   179  	type sNoMethods serviceStatus
   180  	return "", sNoMethods(s)
   181  }
   182  
   183  type unitStatus struct {
   184  	Err            error                 `json:"-" yaml:",omitempty"`
   185  	Charm          string                `json:"upgrading-from,omitempty" yaml:"upgrading-from,omitempty"`
   186  	AgentState     params.Status         `json:"agent-state,omitempty" yaml:"agent-state,omitempty"`
   187  	AgentStateInfo string                `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"`
   188  	AgentVersion   string                `json:"agent-version,omitempty" yaml:"agent-version,omitempty"`
   189  	Life           string                `json:"life,omitempty" yaml:"life,omitempty"`
   190  	Machine        string                `json:"machine,omitempty" yaml:"machine,omitempty"`
   191  	OpenedPorts    []string              `json:"open-ports,omitempty" yaml:"open-ports,omitempty"`
   192  	PublicAddress  string                `json:"public-address,omitempty" yaml:"public-address,omitempty"`
   193  	Subordinates   map[string]unitStatus `json:"subordinates,omitempty" yaml:"subordinates,omitempty"`
   194  }
   195  
   196  type unitStatusNoMarshal unitStatus
   197  
   198  func (s unitStatus) MarshalJSON() ([]byte, error) {
   199  	if s.Err != nil {
   200  		return json.Marshal(errorStatus{s.Err.Error()})
   201  	}
   202  	return json.Marshal(unitStatusNoMarshal(s))
   203  }
   204  
   205  func (s unitStatus) GetYAML() (tag string, value interface{}) {
   206  	if s.Err != nil {
   207  		return "", errorStatus{s.Err.Error()}
   208  	}
   209  	type uNoMethods unitStatus
   210  	return "", unitStatusNoMarshal(s)
   211  }
   212  
   213  func formatStatus(status *api.Status) formattedStatus {
   214  	if status == nil {
   215  		return formattedStatus{}
   216  	}
   217  	out := formattedStatus{
   218  		Environment: status.EnvironmentName,
   219  		Machines:    make(map[string]machineStatus),
   220  		Services:    make(map[string]serviceStatus),
   221  	}
   222  	for k, m := range status.Machines {
   223  		out.Machines[k] = formatMachine(m)
   224  	}
   225  	for k, s := range status.Services {
   226  		out.Services[k] = formatService(s)
   227  	}
   228  	return out
   229  }
   230  
   231  func formatMachine(machine api.MachineStatus) machineStatus {
   232  	out := machineStatus{
   233  		Err:            machine.Err,
   234  		AgentState:     machine.AgentState,
   235  		AgentStateInfo: machine.AgentStateInfo,
   236  		AgentVersion:   machine.AgentVersion,
   237  		DNSName:        machine.DNSName,
   238  		InstanceId:     machine.InstanceId,
   239  		InstanceState:  machine.InstanceState,
   240  		Life:           machine.Life,
   241  		Series:         machine.Series,
   242  		Id:             machine.Id,
   243  		Containers:     make(map[string]machineStatus),
   244  		Hardware:       machine.Hardware,
   245  	}
   246  	for k, m := range machine.Containers {
   247  		out.Containers[k] = formatMachine(m)
   248  	}
   249  	return out
   250  }
   251  
   252  func formatService(service api.ServiceStatus) serviceStatus {
   253  	out := serviceStatus{
   254  		Err:           service.Err,
   255  		Charm:         service.Charm,
   256  		Exposed:       service.Exposed,
   257  		Life:          service.Life,
   258  		Relations:     service.Relations,
   259  		CanUpgradeTo:  service.CanUpgradeTo,
   260  		SubordinateTo: service.SubordinateTo,
   261  		Units:         make(map[string]unitStatus),
   262  	}
   263  	for k, m := range service.Units {
   264  		out.Units[k] = formatUnit(m)
   265  	}
   266  	return out
   267  }
   268  
   269  func formatUnit(unit api.UnitStatus) unitStatus {
   270  	out := unitStatus{
   271  		Err:            unit.Err,
   272  		AgentState:     unit.AgentState,
   273  		AgentStateInfo: unit.AgentStateInfo,
   274  		AgentVersion:   unit.AgentVersion,
   275  		Life:           unit.Life,
   276  		Machine:        unit.Machine,
   277  		OpenedPorts:    unit.OpenedPorts,
   278  		PublicAddress:  unit.PublicAddress,
   279  		Charm:          unit.Charm,
   280  		Subordinates:   make(map[string]unitStatus),
   281  	}
   282  	for k, m := range unit.Subordinates {
   283  		out.Subordinates[k] = formatUnit(m)
   284  	}
   285  	return out
   286  }