github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/status/formatter.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package status
     5  
     6  import (
     7  	"github.com/juju/juju/apiserver/params"
     8  	"github.com/juju/juju/cmd/juju/common"
     9  	"github.com/juju/juju/state/multiwatcher"
    10  	"github.com/juju/juju/status"
    11  )
    12  
    13  type statusFormatter struct {
    14  	status    *params.FullStatus
    15  	relations map[int]params.RelationStatus
    16  	isoTime   bool
    17  }
    18  
    19  // NewStatusFormatter takes stored model information (params.FullStatus) and populates
    20  // the statusFormatter struct used in various status formatting methods
    21  func NewStatusFormatter(status *params.FullStatus, isoTime bool) *statusFormatter {
    22  	sf := statusFormatter{
    23  		status:    status,
    24  		relations: make(map[int]params.RelationStatus),
    25  		isoTime:   isoTime,
    26  	}
    27  	for _, relation := range status.Relations {
    28  		sf.relations[relation.Id] = relation
    29  	}
    30  	return &sf
    31  }
    32  
    33  func (sf *statusFormatter) format() formattedStatus {
    34  	if sf.status == nil {
    35  		return formattedStatus{}
    36  	}
    37  	out := formattedStatus{
    38  		Model:    sf.status.ModelName,
    39  		Machines: make(map[string]machineStatus),
    40  		Services: make(map[string]serviceStatus),
    41  	}
    42  	if sf.status.AvailableVersion != "" {
    43  		out.ModelStatus = &modelStatus{
    44  			AvailableVersion: sf.status.AvailableVersion,
    45  		}
    46  	}
    47  
    48  	for k, m := range sf.status.Machines {
    49  		out.Machines[k] = sf.formatMachine(m)
    50  	}
    51  	for sn, s := range sf.status.Services {
    52  		out.Services[sn] = sf.formatService(sn, s)
    53  	}
    54  	return out
    55  }
    56  
    57  // MachineFormat takes stored model information (params.FullStatus) and formats machine status info.
    58  func (sf *statusFormatter) MachineFormat(machineId []string) formattedMachineStatus {
    59  	if sf.status == nil {
    60  		return formattedMachineStatus{}
    61  	}
    62  	out := formattedMachineStatus{
    63  		Model:    sf.status.ModelName,
    64  		Machines: make(map[string]machineStatus),
    65  	}
    66  	for k, m := range sf.status.Machines {
    67  		if len(machineId) != 0 {
    68  			for i := 0; i < len(machineId); i++ {
    69  				if m.Id == machineId[i] {
    70  					out.Machines[k] = sf.formatMachine(m)
    71  				}
    72  			}
    73  		} else {
    74  			out.Machines[k] = sf.formatMachine(m)
    75  		}
    76  	}
    77  	return out
    78  }
    79  
    80  func (sf *statusFormatter) formatMachine(machine params.MachineStatus) machineStatus {
    81  	var out machineStatus
    82  
    83  	out = machineStatus{
    84  		JujuStatus:    sf.getStatusInfoContents(machine.AgentStatus),
    85  		DNSName:       machine.DNSName,
    86  		InstanceId:    machine.InstanceId,
    87  		MachineStatus: sf.getStatusInfoContents(machine.InstanceStatus),
    88  		Series:        machine.Series,
    89  		Id:            machine.Id,
    90  		Containers:    make(map[string]machineStatus),
    91  		Hardware:      machine.Hardware,
    92  	}
    93  
    94  	for k, m := range machine.Containers {
    95  		out.Containers[k] = sf.formatMachine(m)
    96  	}
    97  
    98  	for _, job := range machine.Jobs {
    99  		if job == multiwatcher.JobManageModel {
   100  			out.HAStatus = makeHAStatus(machine.HasVote, machine.WantsVote)
   101  			break
   102  		}
   103  	}
   104  	return out
   105  }
   106  
   107  func (sf *statusFormatter) formatService(name string, service params.ServiceStatus) serviceStatus {
   108  	out := serviceStatus{
   109  		Err:           service.Err,
   110  		Charm:         service.Charm,
   111  		Exposed:       service.Exposed,
   112  		Life:          service.Life,
   113  		Relations:     service.Relations,
   114  		CanUpgradeTo:  service.CanUpgradeTo,
   115  		SubordinateTo: service.SubordinateTo,
   116  		Units:         make(map[string]unitStatus),
   117  		StatusInfo:    sf.getServiceStatusInfo(service),
   118  	}
   119  	for k, m := range service.Units {
   120  		out.Units[k] = sf.formatUnit(unitFormatInfo{
   121  			unit:          m,
   122  			unitName:      k,
   123  			serviceName:   name,
   124  			meterStatuses: service.MeterStatuses,
   125  		})
   126  	}
   127  	return out
   128  }
   129  
   130  func (sf *statusFormatter) getServiceStatusInfo(service params.ServiceStatus) statusInfoContents {
   131  	info := statusInfoContents{
   132  		Err:     service.Status.Err,
   133  		Current: service.Status.Status,
   134  		Message: service.Status.Info,
   135  		Version: service.Status.Version,
   136  	}
   137  	if service.Status.Since != nil {
   138  		info.Since = common.FormatTime(service.Status.Since, sf.isoTime)
   139  	}
   140  	return info
   141  }
   142  
   143  type unitFormatInfo struct {
   144  	unit          params.UnitStatus
   145  	unitName      string
   146  	serviceName   string
   147  	meterStatuses map[string]params.MeterStatus
   148  }
   149  
   150  func (sf *statusFormatter) formatUnit(info unitFormatInfo) unitStatus {
   151  	// TODO(Wallyworld) - this should be server side but we still need to support older servers.
   152  	sf.updateUnitStatusInfo(&info.unit, info.serviceName)
   153  
   154  	out := unitStatus{
   155  		WorkloadStatusInfo: sf.getWorkloadStatusInfo(info.unit),
   156  		JujuStatusInfo:     sf.getAgentStatusInfo(info.unit),
   157  		Machine:            info.unit.Machine,
   158  		OpenedPorts:        info.unit.OpenedPorts,
   159  		PublicAddress:      info.unit.PublicAddress,
   160  		Charm:              info.unit.Charm,
   161  		Subordinates:       make(map[string]unitStatus),
   162  	}
   163  
   164  	if ms, ok := info.meterStatuses[info.unitName]; ok {
   165  		out.MeterStatus = &meterStatus{
   166  			Color:   ms.Color,
   167  			Message: ms.Message,
   168  		}
   169  	}
   170  
   171  	for k, m := range info.unit.Subordinates {
   172  		out.Subordinates[k] = sf.formatUnit(unitFormatInfo{
   173  			unit:          m,
   174  			unitName:      k,
   175  			serviceName:   info.serviceName,
   176  			meterStatuses: info.meterStatuses,
   177  		})
   178  	}
   179  	return out
   180  }
   181  
   182  func (sf *statusFormatter) getStatusInfoContents(inst params.DetailedStatus) statusInfoContents {
   183  	info := statusInfoContents{
   184  		Err:     inst.Err,
   185  		Current: inst.Status,
   186  		Message: inst.Info,
   187  		Version: inst.Version,
   188  		Life:    inst.Life,
   189  	}
   190  	if inst.Since != nil {
   191  		info.Since = common.FormatTime(inst.Since, sf.isoTime)
   192  	}
   193  	return info
   194  }
   195  
   196  func (sf *statusFormatter) getWorkloadStatusInfo(unit params.UnitStatus) statusInfoContents {
   197  	info := statusInfoContents{
   198  		Err:     unit.WorkloadStatus.Err,
   199  		Current: unit.WorkloadStatus.Status,
   200  		Message: unit.WorkloadStatus.Info,
   201  		Version: unit.WorkloadStatus.Version,
   202  	}
   203  	if unit.WorkloadStatus.Since != nil {
   204  		info.Since = common.FormatTime(unit.WorkloadStatus.Since, sf.isoTime)
   205  	}
   206  	return info
   207  }
   208  
   209  func (sf *statusFormatter) getAgentStatusInfo(unit params.UnitStatus) statusInfoContents {
   210  	info := statusInfoContents{
   211  		Err:     unit.AgentStatus.Err,
   212  		Current: unit.AgentStatus.Status,
   213  		Message: unit.AgentStatus.Info,
   214  		Version: unit.AgentStatus.Version,
   215  	}
   216  	if unit.AgentStatus.Since != nil {
   217  		info.Since = common.FormatTime(unit.AgentStatus.Since, sf.isoTime)
   218  	}
   219  	return info
   220  }
   221  
   222  func (sf *statusFormatter) updateUnitStatusInfo(unit *params.UnitStatus, serviceName string) {
   223  	if unit.WorkloadStatus.Status == status.StatusError {
   224  		if relation, ok := sf.relations[getRelationIdFromData(unit)]; ok {
   225  			// Append the details of the other endpoint on to the status info string.
   226  			if ep, ok := findOtherEndpoint(relation.Endpoints, serviceName); ok {
   227  				unit.WorkloadStatus.Info = unit.WorkloadStatus.Info + " for " + ep.String()
   228  			}
   229  		}
   230  	}
   231  }
   232  
   233  func makeHAStatus(hasVote, wantsVote bool) string {
   234  	var s string
   235  	switch {
   236  	case hasVote && wantsVote:
   237  		s = "has-vote"
   238  	case hasVote && !wantsVote:
   239  		s = "removing-vote"
   240  	case !hasVote && wantsVote:
   241  		s = "adding-vote"
   242  	case !hasVote && !wantsVote:
   243  		s = "no-vote"
   244  	}
   245  	return s
   246  }
   247  
   248  func getRelationIdFromData(unit *params.UnitStatus) int {
   249  	if relationId_, ok := unit.WorkloadStatus.Data["relation-id"]; ok {
   250  		if relationId, ok := relationId_.(float64); ok {
   251  			return int(relationId)
   252  		} else {
   253  			logger.Infof("relation-id found status data but was unexpected "+
   254  				"type: %q. Status output may be lacking some detail.", relationId_)
   255  		}
   256  	}
   257  	return -1
   258  }
   259  
   260  // findOtherEndpoint searches the provided endpoints for an endpoint
   261  // that *doesn't* match serviceName. The returned bool indicates if
   262  // such an endpoint was found.
   263  func findOtherEndpoint(endpoints []params.EndpointStatus, serviceName string) (params.EndpointStatus, bool) {
   264  	for _, endpoint := range endpoints {
   265  		if endpoint.ServiceName != serviceName {
   266  			return endpoint, true
   267  		}
   268  	}
   269  	return params.EndpointStatus{}, false
   270  }