func formatOneline(value interface{}, printf onelinePrintf) ([]byte, error) { fs, valueConverted := value.(formattedStatus) if !valueConverted { return nil, errors.Errorf("expected value of type %T, got %T", fs, value) } var out bytes.Buffer pprint := func(uName string, u unitStatus, level int) { var fmtPorts string if len(u.OpenedPorts) > 0 { fmtPorts = fmt.Sprintf(" %s", strings.Join(u.OpenedPorts, ", ")) } format := indent("\n", level*2, "- %s: %s (%v)"+fmtPorts) printf(&out, format, uName, u, level) } for _, svcName := range common.SortStringsNaturally(stringKeysFromMap(fs.Services)) { svc := fs.Services[svcName] for _, uName := range common.SortStringsNaturally(stringKeysFromMap(svc.Units)) { unit := svc.Units[uName] pprint(uName, unit, 0) recurseUnits(unit, 1, pprint) } } return out.Bytes(), nil }
// FormatMachineTabular returns a tabular summary of machine func FormatMachineTabular(value interface{}) ([]byte, error) { fs, valueConverted := value.(formattedMachineStatus) if !valueConverted { return nil, errors.Errorf("expected value of type %T, got %T", fs, value) } var out bytes.Buffer // To format things into columns. tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) p := func(values ...interface{}) { for _, v := range values { fmt.Fprintf(tw, "%s\t", v) } fmt.Fprintln(tw) } p("\n[Machines]") p("ID\tSTATE\tDNS\tINS-ID\tSERIES\tAZ") for _, name := range common.SortStringsNaturally(stringKeysFromMap(fs.Machines)) { m := fs.Machines[name] // We want to display availability zone so extract from hardware info". hw, err := instance.ParseHardware(m.Hardware) if err != nil { logger.Warningf("invalid hardware info %s for machine %v", m.Hardware, m) } az := "" if hw.AvailabilityZone != nil { az = *hw.AvailabilityZone } p(m.Id, m.AgentState, m.DNSName, m.InstanceId, m.Series, az) } tw.Flush() return out.Bytes(), nil }
// FormatSummary returns a summary of the current environment // including the following information: // - Headers: // - All subnets the environment occupies. // - All ports the environment utilizes. // - Sections: // - Machines: Displays total #, and then the # in each state. // - Units: Displays total #, and then # in each state. // - Services: Displays total #, their names, and how many of each // are exposed. func FormatSummary(value interface{}) ([]byte, error) { fs, valueConverted := value.(formattedStatus) if !valueConverted { return nil, errors.Errorf("expected value of type %T, got %T", fs, value) } f := newSummaryFormatter() stateToMachine := f.aggregateMachineStates(fs.Machines) svcExposure := f.aggregateServiceAndUnitStates(fs.Services) p := f.delimitValuesWithTabs // Print everything out p("Running on subnets:", strings.Join(f.netStrings, ", ")) p("Utilizing ports:", f.portsInColumnsOf(3)) f.tw.Flush() // Right align summary information f.tw.Init(&f.out, 0, 2, 1, ' ', tabwriter.AlignRight) p("# MACHINES:", fmt.Sprintf("(%d)", len(fs.Machines))) f.printStateToCount(stateToMachine) p(" ") p("# UNITS:", fmt.Sprintf("(%d)", f.numUnits)) f.printStateToCount(f.stateToUnit) p(" ") p("# SERVICES:", fmt.Sprintf(" (%d)", len(fs.Services))) for _, svcName := range common.SortStringsNaturally(stringKeysFromMap(svcExposure)) { s := svcExposure[svcName] p(svcName, fmt.Sprintf("%d/%d\texposed", s[true], s[true]+s[false])) } f.tw.Flush() return f.out.Bytes(), nil }
func (f *summaryFormatter) aggregateServiceAndUnitStates(services map[string]serviceStatus) map[string]map[bool]int { svcExposure := make(map[string]map[bool]int) for _, name := range common.SortStringsNaturally(stringKeysFromMap(services)) { s := services[name] // Grab unit states for _, un := range common.SortStringsNaturally(stringKeysFromMap(s.Units)) { u := s.Units[un] f.trackUnit(un, u, 0) recurseUnits(u, 1, f.trackUnit) } if _, ok := svcExposure[name]; !ok { svcExposure[name] = make(map[bool]int) } svcExposure[name][s.Exposed]++ } return svcExposure }
// recurseUnits calls the given recurseMap function on the given unit // and its subordinates (recursively defined on the given unit). func recurseUnits(u unitStatus, il int, recurseMap func(string, unitStatus, int)) { if len(u.Subordinates) == 0 { return } for _, uName := range common.SortStringsNaturally(stringKeysFromMap(u.Subordinates)) { unit := u.Subordinates[uName] recurseMap(uName, unit, il) recurseUnits(unit, il+1, recurseMap) } }
func (f *summaryFormatter) aggregateMachineStates(machines map[string]machineStatus) map[status.Status]int { stateToMachine := make(map[status.Status]int) for _, name := range common.SortStringsNaturally(stringKeysFromMap(machines)) { m := machines[name] f.resolveAndTrackIp(m.DNSName) if agentState := m.JujuStatus.Current; agentState == "" { agentState = status.StatusPending } else { stateToMachine[agentState]++ } } return stateToMachine }
// FormatTabular returns a tabular summary of machines, services, and // units. Any subordinate items are indented by two spaces beneath // their superior. func FormatTabular(value interface{}) ([]byte, error) { fs, valueConverted := value.(formattedStatus) if !valueConverted { return nil, errors.Errorf("expected value of type %T, got %T", fs, value) } var out bytes.Buffer // To format things into columns. tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) p := func(values ...interface{}) { for _, v := range values { fmt.Fprintf(tw, "%s\t", v) } fmt.Fprintln(tw) } if envStatus := fs.ModelStatus; envStatus != nil { p("[Model]") if envStatus.AvailableVersion != "" { p("UPGRADE-AVAILABLE") p(envStatus.AvailableVersion) } p() tw.Flush() } units := make(map[string]unitStatus) relations := newRelationFormatter() p("[Services]") p("NAME\tSTATUS\tEXPOSED\tCHARM") for _, svcName := range common.SortStringsNaturally(stringKeysFromMap(fs.Services)) { svc := fs.Services[svcName] for un, u := range svc.Units { units[un] = u } subs := set.NewStrings(svc.SubordinateTo...) p(svcName, svc.StatusInfo.Current, fmt.Sprintf("%t", svc.Exposed), svc.Charm) for relType, relatedUnits := range svc.Relations { for _, related := range relatedUnits { relations.add(related, svcName, relType, subs.Contains(related)) } } } if relations.len() > 0 { p() p("[Relations]") p("SERVICE1\tSERVICE2\tRELATION\tTYPE") for _, k := range relations.sorted() { r := relations.get(k) if r != nil { p(r.service1, r.service2, r.relation, r.relationType()) } } } tw.Flush() pUnit := func(name string, u unitStatus, level int) { message := u.WorkloadStatusInfo.Message agentDoing := agentDoing(u.AgentStatusInfo) if agentDoing != "" { message = fmt.Sprintf("(%s) %s", agentDoing, message) } p( indent("", level*2, name), u.WorkloadStatusInfo.Current, u.AgentStatusInfo.Current, u.AgentStatusInfo.Version, u.Machine, strings.Join(u.OpenedPorts, ","), u.PublicAddress, message, ) } header := []string{"ID", "WORKLOAD-STATE", "AGENT-STATE", "VERSION", "MACHINE", "PORTS", "PUBLIC-ADDRESS", "MESSAGE"} p("\n[Units]") p(strings.Join(header, "\t")) for _, name := range common.SortStringsNaturally(stringKeysFromMap(units)) { u := units[name] pUnit(name, u, 0) const indentationLevel = 1 recurseUnits(u, indentationLevel, pUnit) } tw.Flush() p("\n[Machines]") p("ID\tSTATE\tDNS\tINS-ID\tSERIES\tAZ") for _, name := range common.SortStringsNaturally(stringKeysFromMap(fs.Machines)) { m := fs.Machines[name] // We want to display availability zone so extract from hardware info". hw, err := instance.ParseHardware(m.Hardware) if err != nil { logger.Warningf("invalid hardware info %s for machine %v", m.Hardware, m) } az := "" if hw.AvailabilityZone != nil { az = *hw.AvailabilityZone } p(m.Id, m.AgentState, m.DNSName, m.InstanceId, m.Series, az) } tw.Flush() return out.Bytes(), nil }
// FormatTabular returns a tabular summary of machines, services, and // units. Any subordinate items are indented by two spaces beneath // their superior. func FormatTabular(value interface{}) ([]byte, error) { fs, valueConverted := value.(formattedStatus) if !valueConverted { return nil, errors.Errorf("expected value of type %T, got %T", fs, value) } var out bytes.Buffer // To format things into columns. tw := tabwriter.NewWriter(&out, 0, 1, 1, ' ', 0) p := func(values ...interface{}) { for _, v := range values { fmt.Fprintf(tw, "%s\t", v) } fmt.Fprintln(tw) } if envStatus := fs.EnvironmentStatus; envStatus != nil { p("[Environment]") if envStatus.AvailableVersion != "" { p("UPGRADE-AVAILABLE") p(envStatus.AvailableVersion) } p() tw.Flush() } units := make(map[string]unitStatus) p("[Services]") p("NAME\tSTATUS\tEXPOSED\tCHARM") for _, svcName := range common.SortStringsNaturally(stringKeysFromMap(fs.Services)) { svc := fs.Services[svcName] for un, u := range svc.Units { units[un] = u } p(svcName, svc.StatusInfo.Current, fmt.Sprintf("%t", svc.Exposed), svc.Charm) } tw.Flush() pUnit := func(name string, u unitStatus, level int) { message := u.WorkloadStatusInfo.Message agentDoing := agentDoing(u.AgentStatusInfo) if agentDoing != "" { message = fmt.Sprintf("(%s) %s", agentDoing, message) } p( indent("", level*2, name), u.WorkloadStatusInfo.Current, u.AgentStatusInfo.Current, u.AgentStatusInfo.Version, u.Machine, strings.Join(u.OpenedPorts, ","), u.PublicAddress, message, ) } // See if we have new or old data; that determines what data we can display. newStatus := false for _, u := range units { if u.AgentStatusInfo.Current != "" { newStatus = true break } } var header []string if newStatus { header = []string{"ID", "WORKLOAD-STATE", "AGENT-STATE", "VERSION", "MACHINE", "PORTS", "PUBLIC-ADDRESS", "MESSAGE"} } else { header = []string{"ID", "STATE", "VERSION", "MACHINE", "PORTS", "PUBLIC-ADDRESS"} } p("\n[Units]") p(strings.Join(header, "\t")) for _, name := range common.SortStringsNaturally(stringKeysFromMap(units)) { u := units[name] pUnit(name, u, 0) const indentationLevel = 1 recurseUnits(u, indentationLevel, pUnit) } tw.Flush() p("\n[Machines]") p("ID\tSTATE\tVERSION\tDNS\tINS-ID\tSERIES\tHARDWARE") for _, name := range common.SortStringsNaturally(stringKeysFromMap(fs.Machines)) { m := fs.Machines[name] p(m.Id, m.AgentState, m.AgentVersion, m.DNSName, m.InstanceId, m.Series, m.Hardware) } tw.Flush() return out.Bytes(), nil }
func (f *summaryFormatter) printStateToCount(m map[status.Status]int) { for _, stateToCount := range common.SortStringsNaturally(stringKeysFromMap(m)) { numInStatus := m[status.Status(stateToCount)] f.delimitValuesWithTabs(stateToCount+":", fmt.Sprintf(" %d ", numInStatus)) } }