github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/common/model.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for infos. 3 4 package common 5 6 import ( 7 "reflect" 8 "time" 9 10 "github.com/juju/errors" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/core/model" 15 "github.com/juju/juju/core/status" 16 "github.com/juju/juju/jujuclient" 17 ) 18 19 // ModelInfo contains information about a model. 20 type ModelInfo struct { 21 // Name is a fully qualified model name, i.e. having the format $owner/$model. 22 Name string `json:"name" yaml:"name"` 23 24 // ShortName is un-qualified model name. 25 ShortName string `json:"short-name" yaml:"short-name"` 26 UUID string `json:"model-uuid" yaml:"model-uuid"` 27 Type model.ModelType `json:"model-type" yaml:"model-type"` 28 ControllerUUID string `json:"controller-uuid" yaml:"controller-uuid"` 29 ControllerName string `json:"controller-name" yaml:"controller-name"` 30 IsController bool `json:"is-controller" yaml:"is-controller"` 31 Owner string `json:"owner" yaml:"owner"` 32 Cloud string `json:"cloud" yaml:"cloud"` 33 CloudRegion string `json:"region,omitempty" yaml:"region,omitempty"` 34 ProviderType string `json:"type,omitempty" yaml:"type,omitempty"` 35 Life string `json:"life" yaml:"life"` 36 Status *ModelStatus `json:"status,omitempty" yaml:"status,omitempty"` 37 Users map[string]ModelUserInfo `json:"users,omitempty" yaml:"users,omitempty"` 38 Machines map[string]ModelMachineInfo `json:"machines,omitempty" yaml:"machines,omitempty"` 39 SLA string `json:"sla,omitempty" yaml:"sla,omitempty"` 40 SLAOwner string `json:"sla-owner,omitempty" yaml:"sla-owner,omitempty"` 41 AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` 42 Credential *ModelCredential `json:"credential,omitempty" yaml:"credential,omitempty"` 43 } 44 45 // ModelMachineInfo contains information about a machine in a model. 46 // We currently only care about showing core count, but might 47 // in the future care about memory, disks, containers etc. 48 type ModelMachineInfo struct { 49 Cores uint64 `json:"cores" yaml:"cores"` 50 } 51 52 // ModelStatus contains the current status of a model. 53 type ModelStatus struct { 54 Current status.Status `json:"current,omitempty" yaml:"current,omitempty"` 55 Message string `json:"message,omitempty" yaml:"message,omitempty"` 56 Since string `json:"since,omitempty" yaml:"since,omitempty"` 57 Migration string `json:"migration,omitempty" yaml:"migration,omitempty"` 58 MigrationStart string `json:"migration-start,omitempty" yaml:"migration-start,omitempty"` 59 MigrationEnd string `json:"migration-end,omitempty" yaml:"migration-end,omitempty"` 60 } 61 62 // ModelUserInfo defines the serialization behaviour of the model user 63 // information. 64 type ModelUserInfo struct { 65 DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"` 66 Access string `yaml:"access" json:"access"` 67 LastConnection string `yaml:"last-connection" json:"last-connection"` 68 } 69 70 // FriendlyDuration renders a time pointer that we get from the API as 71 // a friendly string. 72 func FriendlyDuration(when *time.Time, now time.Time) string { 73 if when == nil { 74 return "" 75 } 76 return UserFriendlyDuration(*when, now) 77 } 78 79 // ModelCredential contains model credential basic details. 80 type ModelCredential struct { 81 Name string `json:"name" yaml:"name"` 82 Owner string `json:"owner" yaml:"owner"` 83 Cloud string `json:"cloud" yaml:"cloud"` 84 } 85 86 // ModelInfoFromParams translates a params.ModelInfo to ModelInfo. 87 func ModelInfoFromParams(info params.ModelInfo, now time.Time) (ModelInfo, error) { 88 ownerTag, err := names.ParseUserTag(info.OwnerTag) 89 if err != nil { 90 return ModelInfo{}, errors.Trace(err) 91 } 92 cloudTag, err := names.ParseCloudTag(info.CloudTag) 93 if err != nil { 94 return ModelInfo{}, errors.Trace(err) 95 } 96 modelInfo := ModelInfo{ 97 ShortName: info.Name, 98 Name: jujuclient.JoinOwnerModelName(ownerTag, info.Name), 99 Type: model.ModelType(info.Type), 100 UUID: info.UUID, 101 ControllerUUID: info.ControllerUUID, 102 IsController: info.IsController, 103 Owner: ownerTag.Id(), 104 Life: string(info.Life), 105 Cloud: cloudTag.Id(), 106 CloudRegion: info.CloudRegion, 107 } 108 if info.AgentVersion != nil { 109 modelInfo.AgentVersion = info.AgentVersion.String() 110 } 111 // Although this may be more performance intensive, we have to use reflection 112 // since structs containing map[string]interface {} cannot be compared, i.e 113 // cannot use simple '==' here. 114 if !reflect.DeepEqual(info.Status, params.EntityStatus{}) { 115 modelInfo.Status = &ModelStatus{ 116 Current: info.Status.Status, 117 Message: info.Status.Info, 118 Since: FriendlyDuration(info.Status.Since, now), 119 } 120 } 121 if info.Migration != nil { 122 status := modelInfo.Status 123 if status == nil { 124 status = &ModelStatus{} 125 modelInfo.Status = status 126 } 127 status.Migration = info.Migration.Status 128 status.MigrationStart = FriendlyDuration(info.Migration.Start, now) 129 status.MigrationEnd = FriendlyDuration(info.Migration.End, now) 130 } 131 132 if info.ProviderType != "" { 133 modelInfo.ProviderType = info.ProviderType 134 135 } 136 if len(info.Users) != 0 { 137 modelInfo.Users = ModelUserInfoFromParams(info.Users, now) 138 } 139 if len(info.Machines) != 0 { 140 modelInfo.Machines = ModelMachineInfoFromParams(info.Machines) 141 } 142 if info.SLA != nil { 143 modelInfo.SLA = ModelSLAFromParams(info.SLA) 144 modelInfo.SLAOwner = ModelSLAOwnerFromParams(info.SLA) 145 } 146 147 if info.CloudCredentialTag != "" { 148 credTag, err := names.ParseCloudCredentialTag(info.CloudCredentialTag) 149 if err != nil { 150 return ModelInfo{}, errors.Trace(err) 151 } 152 modelInfo.Credential = &ModelCredential{ 153 Name: credTag.Name(), 154 Owner: credTag.Owner().Id(), 155 Cloud: credTag.Cloud().Id(), 156 } 157 } 158 159 return modelInfo, nil 160 } 161 162 // ModelMachineInfoFromParams translates []params.ModelMachineInfo to a map of 163 // machine ids to ModelMachineInfo. 164 func ModelMachineInfoFromParams(machines []params.ModelMachineInfo) map[string]ModelMachineInfo { 165 output := make(map[string]ModelMachineInfo, len(machines)) 166 for _, info := range machines { 167 mInfo := ModelMachineInfo{} 168 if info.Hardware != nil && info.Hardware.Cores != nil { 169 mInfo.Cores = *info.Hardware.Cores 170 } 171 output[info.Id] = mInfo 172 } 173 return output 174 } 175 176 // ModelUserInfoFromParams translates []params.ModelUserInfo to a map of 177 // user names to ModelUserInfo. 178 func ModelUserInfoFromParams(users []params.ModelUserInfo, now time.Time) map[string]ModelUserInfo { 179 output := make(map[string]ModelUserInfo) 180 for _, info := range users { 181 outInfo := ModelUserInfo{ 182 DisplayName: info.DisplayName, 183 Access: string(info.Access), 184 } 185 if info.LastConnection != nil { 186 outInfo.LastConnection = UserFriendlyDuration(*info.LastConnection, now) 187 } else { 188 outInfo.LastConnection = "never connected" 189 } 190 output[names.NewUserTag(info.UserName).Id()] = outInfo 191 } 192 return output 193 } 194 195 func ModelSLAFromParams(sla *params.ModelSLAInfo) string { 196 if sla == nil { 197 return "" 198 } 199 return sla.Level 200 } 201 202 func ModelSLAOwnerFromParams(sla *params.ModelSLAInfo) string { 203 if sla == nil { 204 return "" 205 } 206 return sla.Owner 207 } 208 209 // OwnerQualifiedModelName returns the model name qualified with the 210 // model owner if the owner is not the same as the given canonical 211 // user name. If the owner is a local user, we omit the domain. 212 func OwnerQualifiedModelName(modelName string, owner, user names.UserTag) string { 213 if owner.Id() == user.Id() { 214 return modelName 215 } 216 return jujuclient.JoinOwnerModelName(owner, modelName) 217 }