func printMachine(w output.Wrapper, m machineStatus) { // 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 } w.Print(m.Id) w.PrintStatus(m.JujuStatus.Current) w.Println(m.DNSName, m.InstanceId, m.Series, az) for _, name := range utils.SortStringsNaturally(stringKeysFromMap(m.Containers)) { printMachine(w, m.Containers[name]) } }
// FormatTabular writes a tabular summary of machines, applications, and // units. Any subordinate items are indented by two spaces beneath // their superior. func FormatTabular(writer io.Writer, forceColor bool, value interface{}) error { const maxVersionWidth = 15 const ellipsis = "..." const truncatedWidth = maxVersionWidth - len(ellipsis) fs, valueConverted := value.(formattedStatus) if !valueConverted { return errors.Errorf("expected value of type %T, got %T", fs, value) } // To format things into columns. tw := output.TabWriter(writer) if forceColor { tw.SetColorCapable(forceColor) } w := output.Wrapper{tw} p := w.Println outputHeaders := func(values ...interface{}) { p() p(values...) } cloudRegion := fs.Model.Cloud if fs.Model.CloudRegion != "" { cloudRegion += "/" + fs.Model.CloudRegion } header := []interface{}{"MODEL", "CONTROLLER", "CLOUD/REGION", "VERSION"} values := []interface{}{fs.Model.Name, fs.Model.Controller, cloudRegion, fs.Model.Version} message := getModelMessage(fs.Model) if message != "" { header = append(header, "NOTES") values = append(values, message) } // The first set of headers don't use outputHeaders because it adds the blank line. p(header...) p(values...) units := make(map[string]unitStatus) metering := false relations := newRelationFormatter() outputHeaders("APP", "VERSION", "STATUS", "SCALE", "CHARM", "STORE", "REV", "OS", "NOTES") tw.SetColumnAlignRight(3) tw.SetColumnAlignRight(6) for _, appName := range utils.SortStringsNaturally(stringKeysFromMap(fs.Applications)) { app := fs.Applications[appName] version := app.Version // Don't let a long version push out the version column. if len(version) > maxVersionWidth { version = version[:truncatedWidth] + ellipsis } // Notes may well contain other things later. notes := "" if app.Exposed { notes = "exposed" } w.Print(appName, version) w.PrintStatus(app.StatusInfo.Current) scale, warn := fs.applicationScale(appName) if warn { w.PrintColor(output.WarningHighlight, scale) } else { w.Print(scale) } p(app.CharmName, app.CharmOrigin, app.CharmRev, app.OS, notes) for un, u := range app.Units { units[un] = u if u.MeterStatus != nil { metering = true } } // Ensure that we pick a consistent name for peer relations. sortedRelTypes := make([]string, 0, len(app.Relations)) for relType := range app.Relations { sortedRelTypes = append(sortedRelTypes, relType) } sort.Strings(sortedRelTypes) subs := set.NewStrings(app.SubordinateTo...) for _, relType := range sortedRelTypes { for _, related := range app.Relations[relType] { relations.add(related, appName, relType, subs.Contains(related)) } } } pUnit := func(name string, u unitStatus, level int) { message := u.WorkloadStatusInfo.Message agentDoing := agentDoing(u.JujuStatusInfo) if agentDoing != "" { message = fmt.Sprintf("(%s) %s", agentDoing, message) } w.Print(indent("", level*2, name)) w.PrintStatus(u.WorkloadStatusInfo.Current) w.PrintStatus(u.JujuStatusInfo.Current) p( u.Machine, u.PublicAddress, strings.Join(u.OpenedPorts, ","), message, ) } outputHeaders("UNIT", "WORKLOAD", "AGENT", "MACHINE", "PUBLIC-ADDRESS", "PORTS", "MESSAGE") for _, name := range utils.SortStringsNaturally(stringKeysFromMap(units)) { u := units[name] pUnit(name, u, 0) const indentationLevel = 1 recurseUnits(u, indentationLevel, pUnit) } if metering { outputHeaders("METER", "STATUS", "MESSAGE") for _, name := range utils.SortStringsNaturally(stringKeysFromMap(units)) { u := units[name] if u.MeterStatus != nil { p(name, u.MeterStatus.Color, u.MeterStatus.Message) } } } p() printMachines(tw, fs.Machines) if relations.len() > 0 { outputHeaders("RELATION", "PROVIDES", "CONSUMES", "TYPE") for _, k := range relations.sorted() { r := relations.get(k) if r != nil { p(r.relation, r.application1, r.application2, r.relationType()) } } } tw.Flush() return nil }