github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/storage/filesystem.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage 5 6 import ( 7 "fmt" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/cmd/juju/common" 15 ) 16 17 // FilesystemCommandBase is a helper base structure for filesystem commands. 18 type FilesystemCommandBase struct { 19 StorageCommandBase 20 } 21 22 // FilesystemInfo defines the serialization behaviour for storage filesystem. 23 type FilesystemInfo struct { 24 // from params.Filesystem. This is provider-supplied unique filesystem id. 25 ProviderFilesystemId string `yaml:"provider-id,omitempty" json:"provider-id,omitempty"` 26 27 // Volume is the ID of the volume that the filesystem is backed by, if any. 28 Volume string `yaml:"volume,omitempty" json:"volume,omitempty"` 29 30 // Storage is the ID of the storage instance that the filesystem is 31 // assigned to, if any. 32 Storage string `yaml:"storage,omitempty" json:"storage,omitempty"` 33 34 // Attachments is the set of entities attached to the filesystem. 35 Attachments *FilesystemAttachments 36 37 // Pool is the name of the storage pool that the filesystem came from. 38 Pool string `yaml:"pool,omitempty" json:"pool,omitempty"` 39 40 // from params.FilesystemInfo 41 Size uint64 `yaml:"size" json:"size"` 42 43 // Life is the lifecycle state of the filesystem. 44 Life string `yaml:"life,omitempty" json:"life,omitempty"` 45 46 // from params.FilesystemInfo. 47 Status EntityStatus `yaml:"status,omitempty" json:"status,omitempty"` 48 } 49 50 type FilesystemAttachments struct { 51 Machines map[string]FilesystemAttachment `yaml:"machines,omitempty" json:"machines,omitempty"` 52 Containers map[string]FilesystemAttachment `yaml:"containers,omitempty" json:"containers,omitempty"` 53 Units map[string]UnitStorageAttachment `yaml:"units,omitempty" json:"units,omitempty"` 54 } 55 56 type FilesystemAttachment struct { 57 MountPoint string `yaml:"mount-point" json:"mount-point"` 58 ReadOnly bool `yaml:"read-only" json:"read-only"` 59 Life string `yaml:"life,omitempty" json:"life,omitempty"` 60 } 61 62 // generateListFilesystemOutput returns a map filesystem IDs to filesystem info 63 func generateListFilesystemsOutput(ctx *cmd.Context, api StorageListAPI, ids []string) (map[string]FilesystemInfo, error) { 64 65 results, err := api.ListFilesystems(ids) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 70 // filter out valid output, if any 71 var valid []params.FilesystemDetails 72 for _, result := range results { 73 if result.Error == nil { 74 valid = append(valid, result.Result...) 75 continue 76 } 77 // display individual error 78 fmt.Fprintf(ctx.Stderr, "%v\n", result.Error) 79 } 80 if len(valid) == 0 { 81 return nil, nil 82 } 83 return convertToFilesystemInfo(valid) 84 } 85 86 // convertToFilesystemInfo returns a map of filesystem IDs to filesystem info. 87 func convertToFilesystemInfo(all []params.FilesystemDetails) (map[string]FilesystemInfo, error) { 88 result := make(map[string]FilesystemInfo) 89 for _, one := range all { 90 filesystemTag, info, err := createFilesystemInfo(one) 91 if err != nil { 92 return nil, errors.Trace(err) 93 } 94 result[filesystemTag.Id()] = info 95 } 96 return result, nil 97 } 98 99 func createFilesystemInfo(details params.FilesystemDetails) (names.FilesystemTag, FilesystemInfo, error) { 100 filesystemTag, err := names.ParseFilesystemTag(details.FilesystemTag) 101 if err != nil { 102 return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) 103 } 104 105 var info FilesystemInfo 106 info.ProviderFilesystemId = details.Info.FilesystemId 107 info.Pool = details.Info.Pool 108 info.Size = details.Info.Size 109 info.Life = string(details.Life) 110 info.Status = EntityStatus{ 111 details.Status.Status, 112 details.Status.Info, 113 // TODO(axw) we should support formatting as ISO time 114 common.FormatTime(details.Status.Since, false), 115 } 116 117 if details.VolumeTag != "" { 118 volumeId, err := idFromTag(details.VolumeTag) 119 if err != nil { 120 return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) 121 } 122 info.Volume = volumeId 123 } 124 125 attachmentsFromDetails := func( 126 in map[string]params.FilesystemAttachmentDetails, 127 out map[string]FilesystemAttachment, 128 ) error { 129 for tag, attachment := range in { 130 id, err := idFromTag(tag) 131 if err != nil { 132 return errors.Trace(err) 133 } 134 out[id] = FilesystemAttachment{ 135 attachment.MountPoint, 136 attachment.ReadOnly, 137 string(attachment.Life), 138 } 139 } 140 return nil 141 } 142 143 if len(details.MachineAttachments) > 0 { 144 machineAttachments := make(map[string]FilesystemAttachment) 145 if err := attachmentsFromDetails(details.MachineAttachments, machineAttachments); err != nil { 146 return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) 147 } 148 info.Attachments = &FilesystemAttachments{ 149 Machines: machineAttachments, 150 } 151 } 152 if len(details.UnitAttachments) > 0 { 153 unitAttachments := make(map[string]FilesystemAttachment) 154 if err := attachmentsFromDetails(details.UnitAttachments, unitAttachments); err != nil { 155 return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) 156 } 157 if info.Attachments == nil { 158 info.Attachments = &FilesystemAttachments{} 159 } 160 info.Attachments.Containers = unitAttachments 161 } 162 163 if details.Storage != nil { 164 storageTag, storageInfo, err := createStorageInfo(*details.Storage) 165 if err != nil { 166 return names.FilesystemTag{}, FilesystemInfo{}, errors.Trace(err) 167 } 168 info.Storage = storageTag.Id() 169 if storageInfo.Attachments != nil { 170 if info.Attachments == nil { 171 info.Attachments = &FilesystemAttachments{} 172 } 173 info.Attachments.Units = storageInfo.Attachments.Units 174 } 175 } 176 177 return filesystemTag, info, nil 178 }