func (c *statusHistoryCommand) Run(ctx *cmd.Context) error { apiclient, err := c.NewAPIClient() if err != nil { return fmt.Errorf(connectionError, c.ConnectionName(), err) } defer apiclient.Close() var statuses *params.UnitStatusHistory kind := params.HistoryKind(c.outputContent) statuses, err = apiclient.UnitStatusHistory(kind, c.unitName, c.backlogSize) if err != nil { if len(statuses.Statuses) == 0 { return errors.Trace(err) } // Display any error, but continue to print status if some was returned fmt.Fprintf(ctx.Stderr, "%v\n", err) } else if len(statuses.Statuses) == 0 { return errors.Errorf("no status history available") } table := [][]string{{"TIME", "TYPE", "STATUS", "MESSAGE"}} lengths := []int{1, 1, 1, 1} for _, v := range statuses.Statuses { fields := []string{common.FormatTime(v.Since, c.isoTime), string(v.Kind), string(v.Status), v.Info} for k, v := range fields { if len(v) > lengths[k] { lengths[k] = len(v) } } table = append(table, fields) } f := fmt.Sprintf("%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\n", lengths[0], lengths[1], lengths[2], lengths[3]) for _, v := range table { fmt.Printf(f, v[0], v[1], v[2], v[3]) } return nil }
func createFilesystemInfo(result params.FilesystemDetailsResult) (names.FilesystemTag, FilesystemInfo, error) { details := result.Result filesystemTag, err := names.ParseFilesystemTag(details.FilesystemTag) if err != nil { return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) } var info FilesystemInfo info.ProviderFilesystemId = details.Info.FilesystemId info.Size = details.Info.Size info.Status = EntityStatus{ details.Status.Status, details.Status.Info, // TODO(axw) we should support formatting as ISO time common.FormatTime(details.Status.Since, false), } if details.VolumeTag != "" { volumeId, err := idFromTag(details.VolumeTag) if err != nil { return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) } info.Volume = volumeId } if len(details.MachineAttachments) > 0 { machineAttachments := make(map[string]MachineFilesystemAttachment) for machineTag, attachment := range details.MachineAttachments { machineId, err := idFromTag(machineTag) if err != nil { return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) } machineAttachments[machineId] = MachineFilesystemAttachment{ attachment.MountPoint, attachment.ReadOnly, } } info.Attachments = &FilesystemAttachments{ Machines: machineAttachments, } } if details.Storage != nil { storageTag, storageInfo, err := createStorageInfo(*details.Storage) if err != nil { return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) } info.Storage = storageTag.Id() if storageInfo.Attachments != nil { info.Attachments.Units = storageInfo.Attachments.Units } } return filesystemTag, info, nil }
func createVolumeInfo(result params.VolumeDetailsResult) (names.VolumeTag, VolumeInfo, error) { details := result.Details if details == nil { details = volumeDetailsFromLegacy(result) } volumeTag, err := names.ParseVolumeTag(details.VolumeTag) if err != nil { return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) } var info VolumeInfo info.ProviderVolumeId = details.Info.VolumeId info.HardwareId = details.Info.HardwareId info.Size = details.Info.Size info.Persistent = details.Info.Persistent info.Status = EntityStatus{ details.Status.Status, details.Status.Info, // TODO(axw) we should support formatting as ISO time common.FormatTime(details.Status.Since, false), } if len(details.MachineAttachments) > 0 { machineAttachments := make(map[string]MachineVolumeAttachment) for machineTag, attachment := range details.MachineAttachments { machineId, err := idFromTag(machineTag) if err != nil { return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) } machineAttachments[machineId] = MachineVolumeAttachment{ attachment.DeviceName, attachment.DeviceLink, attachment.BusAddress, attachment.ReadOnly, } } info.Attachments = &VolumeAttachments{ Machines: machineAttachments, } } if details.Storage != nil { storageTag, storageInfo, err := createStorageInfo(*details.Storage) if err != nil { return names.VolumeTag{}, VolumeInfo{}, errors.Trace(err) } info.Storage = storageTag.Id() if storageInfo.Attachments != nil { info.Attachments.Units = storageInfo.Attachments.Units } } return volumeTag, info, nil }
func (sf *statusFormatter) getAgentStatusInfo(unit params.UnitStatus) statusInfoContents { info := statusInfoContents{ Err: unit.UnitAgent.Err, Current: unit.UnitAgent.Status, Message: unit.UnitAgent.Info, Version: unit.UnitAgent.Version, } if unit.UnitAgent.Since != nil { info.Since = common.FormatTime(unit.UnitAgent.Since, sf.isoTime) } return info }
func (sf *statusFormatter) getServiceStatusInfo(service params.ServiceStatus) statusInfoContents { info := statusInfoContents{ Err: service.Status.Err, Current: service.Status.Status, Message: service.Status.Info, Version: service.Status.Version, } if service.Status.Since != nil { info.Since = common.FormatTime(service.Status.Since, sf.isoTime) } return info }
func (sf *statusFormatter) getStatusInfoContents(inst params.DetailedStatus) statusInfoContents { info := statusInfoContents{ Err: inst.Err, Current: inst.Status, Message: inst.Info, Version: inst.Version, Life: inst.Life, } if inst.Since != nil { info.Since = common.FormatTime(inst.Since, sf.isoTime) } return info }
func (sf *statusFormatter) getAgentStatusInfo(unit params.UnitStatus) statusInfoContents { // TODO(perrito66) add status validation. info := statusInfoContents{ Err: unit.AgentStatus.Err, Current: status.Status(unit.AgentStatus.Status), Message: unit.AgentStatus.Info, Version: unit.AgentStatus.Version, } if unit.AgentStatus.Since != nil { info.Since = common.FormatTime(unit.AgentStatus.Since, sf.isoTime) } return info }
func (sf *statusFormatter) getServiceStatusInfo(service params.ApplicationStatus) statusInfoContents { // TODO(perrito66) add status validation. info := statusInfoContents{ Err: service.Status.Err, Current: status.Status(service.Status.Status), Message: service.Status.Info, Version: service.Status.Version, } if service.Status.Since != nil { info.Since = common.FormatTime(service.Status.Since, sf.isoTime) } return info }
func createStorageInfo(details params.StorageDetails) (names.StorageTag, StorageInfo, error) { storageTag, err := names.ParseStorageTag(details.StorageTag) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } info := StorageInfo{ Kind: details.Kind.String(), Status: EntityStatus{ details.Status.Status, details.Status.Info, // TODO(axw) we should support formatting as ISO time common.FormatTime(details.Status.Since, false), }, Persistent: details.Persistent, } if len(details.Attachments) > 0 { unitStorageAttachments := make(map[string]UnitStorageAttachment) for unitTagString, attachmentDetails := range details.Attachments { unitTag, err := names.ParseUnitTag(unitTagString) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } var machineId string if attachmentDetails.MachineTag != "" { machineTag, err := names.ParseMachineTag(attachmentDetails.MachineTag) if err != nil { return names.StorageTag{}, StorageInfo{}, errors.Trace(err) } machineId = machineTag.Id() } unitStorageAttachments[unitTag.Id()] = UnitStorageAttachment{ machineId, attachmentDetails.Location, } } info.Attachments = &StorageAttachments{unitStorageAttachments} } return storageTag, info, nil }
func createInfo(volume params.VolumeInstance) (info VolumeInfo, unit, storage string) { info.VolumeId = volume.VolumeId info.HardwareId = volume.HardwareId info.Size = volume.Size info.Persistent = volume.Persistent info.Status = EntityStatus{ volume.Status.Status, volume.Status.Info, // TODO(axw) we should support formatting as ISO time common.FormatTime(volume.Status.Since, false), } if v, err := idFromTag(volume.VolumeTag); err == nil { info.Volume = v } var err error if storage, err = idFromTag(volume.StorageTag); err != nil { storage = "unassigned" } if unit, err = idFromTag(volume.UnitTag); err != nil { unit = "unattached" } return }
func (c *statusHistoryCommand) Run(ctx *cmd.Context) error { apiclient, err := c.NewAPIClient() if err != nil { return errors.Trace(err) } defer apiclient.Close() kind := status.HistoryKind(c.outputContent) var delta *time.Duration if c.backlogSizeDays != 0 { t := time.Duration(c.backlogSizeDays*24) * time.Hour delta = &t } filterArgs := status.StatusHistoryFilter{ Size: c.backlogSize, Delta: delta, } if !c.date.IsZero() { filterArgs.Date = &c.date } var tag names.Tag switch kind { case status.KindUnit, status.KindWorkload, status.KindUnitAgent: if !names.IsValidUnit(c.entityName) { return errors.Errorf("%q is not a valid name for a %s", c.entityName, kind) } tag = names.NewUnitTag(c.entityName) default: if !names.IsValidMachine(c.entityName) { return errors.Errorf("%q is not a valid name for a %s", c.entityName, kind) } tag = names.NewMachineTag(c.entityName) } statuses, err := apiclient.StatusHistory(kind, tag, filterArgs) historyLen := len(statuses) if err != nil { if historyLen == 0 { return errors.Trace(err) } // Display any error, but continue to print status if some was returned fmt.Fprintf(ctx.Stderr, "%v\n", err) } if historyLen == 0 { return errors.Errorf("no status history available") } table := [][]string{{"TIME", "TYPE", "STATUS", "MESSAGE"}} lengths := []int{1, 1, 1, 1} statuses = statuses.SquashLogs(1) statuses = statuses.SquashLogs(2) statuses = statuses.SquashLogs(3) for _, v := range statuses { fields := []string{common.FormatTime(v.Since, c.isoTime), string(v.Kind), string(v.Status), v.Info} for k, v := range fields { if len(v) > lengths[k] { lengths[k] = len(v) } } table = append(table, fields) } f := fmt.Sprintf("%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\n", lengths[0], lengths[1], lengths[2], lengths[3]) for _, v := range table { fmt.Printf(f, v[0], v[1], v[2], v[3]) } return nil }