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]) } }
// formatControllersTabular returns a tabular summary of controller/model items // sorted by controller name alphabetically. func formatControllersTabular(writer io.Writer, set ControllerSet, promptRefresh bool) error { tw := output.TabWriter(writer) w := output.Wrapper{tw} if promptRefresh && len(set.Controllers) > 0 { fmt.Fprintln(writer, "Use --refresh to see the latest information.") fmt.Fprintln(writer) } w.Println("CONTROLLER", "MODEL", "USER", "ACCESS", "CLOUD/REGION", "MODELS", "MACHINES", "HA", "VERSION") tw.SetColumnAlignRight(5) tw.SetColumnAlignRight(6) tw.SetColumnAlignRight(7) names := []string{} for name := range set.Controllers { names = append(names, name) } sort.Strings(names) for _, name := range names { c := set.Controllers[name] modelName := noValueDisplay if c.ModelName != "" { modelName = c.ModelName } userName := noValueDisplay access := noValueDisplay if c.User != "" { userName = c.User access = notKnownDisplay if c.Access != "" { access = c.Access } } if name == set.CurrentController { name += "*" w.PrintColor(output.CurrentHighlight, name) } else { w.Print(name) } cloudRegion := c.Cloud if c.CloudRegion != "" { cloudRegion += "/" + c.CloudRegion } agentVersion := c.AgentVersion staleVersion := false if agentVersion == "" { agentVersion = notKnownDisplay } else { agentVersionNum, err := version.Parse(agentVersion) staleVersion = err == nil && jujuversion.Current.Compare(agentVersionNum) > 0 } machineCount := noValueDisplay if c.MachineCount != nil && *c.MachineCount > 0 { machineCount = fmt.Sprintf("%d", *c.MachineCount) } modelCount := noValueDisplay if c.ModelCount != nil && *c.ModelCount > 0 { modelCount = fmt.Sprintf("%d", *c.ModelCount) } w.Print(modelName, userName, access, cloudRegion, modelCount, machineCount) controllerMachineInfo, warn := controllerMachineStatus(c.ControllerMachines) if warn { w.PrintColor(output.WarningHighlight, controllerMachineInfo) } else { w.Print(controllerMachineInfo) } if staleVersion { w.PrintColor(output.WarningHighlight, agentVersion) } else { w.Print(agentVersion) } w.Println() } tw.Flush() return nil }
// formatTabular takes an interface{} to adhere to the cmd.Formatter interface func (c *modelsCommand) formatTabular(writer io.Writer, value interface{}) error { modelSet, ok := value.(ModelSet) if !ok { return errors.Errorf("expected value of type %T, got %T", modelSet, value) } // We need the tag of the user for which we're listing models, // and for the logged-in user. We use these below when formatting // the model display names. loggedInUser := names.NewUserTag(c.loggedInUser) userForLastConn := loggedInUser var userForListing names.UserTag if c.user != "" { userForListing = names.NewUserTag(c.user) userForLastConn = userForListing } tw := output.TabWriter(writer) w := output.Wrapper{tw} w.Println("Controller: " + c.ControllerName()) w.Println() w.Print("Model") if c.listUUID { w.Print("UUID") } // Only owners, or users with write access or above get to see machines and cores. haveMachineInfo := false for _, m := range modelSet.Models { if haveMachineInfo = len(m.Machines) > 0; haveMachineInfo { break } } if haveMachineInfo { w.Println("Owner", "Status", "Machines", "Cores", "Access", "Last connection") offset := 0 if c.listUUID { offset++ } tw.SetColumnAlignRight(3 + offset) tw.SetColumnAlignRight(4 + offset) } else { w.Println("Owner", "Status", "Access", "Last connection") } for _, model := range modelSet.Models { owner := names.NewUserTag(model.Owner) name := common.OwnerQualifiedModelName(model.Name, owner, userForListing) if jujuclient.JoinOwnerModelName(owner, model.Name) == modelSet.CurrentModelQualified { name += "*" w.PrintColor(output.CurrentHighlight, name) } else { w.Print(name) } if c.listUUID { w.Print(model.UUID) } lastConnection := model.Users[userForLastConn.Id()].LastConnection if lastConnection == "" { lastConnection = "never connected" } userForAccess := loggedInUser if c.user != "" { userForAccess = names.NewUserTag(c.user) } access := model.Users[userForAccess.Id()].Access w.Print(model.Owner, model.Status.Current) if haveMachineInfo { machineInfo := fmt.Sprintf("%d", len(model.Machines)) cores := uint64(0) for _, m := range model.Machines { cores += m.Cores } coresInfo := "-" if cores > 0 { coresInfo = fmt.Sprintf("%d", cores) } w.Print(machineInfo, coresInfo) } w.Println(access, lastConnection) } tw.Flush() return nil }
// 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 }