github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/modelmanager/modelmanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelmanager 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/api/base" 12 "github.com/juju/juju/api/common" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/permission" 17 ) 18 19 var logger = loggo.GetLogger("juju.api.modelmanager") 20 21 // Client provides methods that the Juju client command uses to interact 22 // with models stored in the Juju Server. 23 type Client struct { 24 base.ClientFacade 25 facade base.FacadeCaller 26 *common.ModelStatusAPI 27 } 28 29 // NewClient creates a new `Client` based on an existing authenticated API 30 // connection. 31 func NewClient(st base.APICallCloser) *Client { 32 frontend, backend := base.NewClientFacade(st, "ModelManager") 33 return &Client{ 34 ClientFacade: frontend, 35 facade: backend, 36 ModelStatusAPI: common.NewModelStatusAPI(backend), 37 } 38 } 39 40 // Close closes the api connection. 41 func (c *Client) Close() error { 42 return c.ClientFacade.Close() 43 } 44 45 // CreateModel creates a new model using the model config, 46 // cloud region and credential specified in the args. 47 func (c *Client) CreateModel( 48 name, owner, cloud, cloudRegion string, 49 cloudCredential names.CloudCredentialTag, 50 config map[string]interface{}, 51 ) (base.ModelInfo, error) { 52 var result base.ModelInfo 53 if !names.IsValidUser(owner) { 54 return result, errors.Errorf("invalid owner name %q", owner) 55 } 56 var cloudTag string 57 if cloud != "" { 58 if !names.IsValidCloud(cloud) { 59 return result, errors.Errorf("invalid cloud name %q", cloud) 60 } 61 cloudTag = names.NewCloudTag(cloud).String() 62 } 63 var cloudCredentialTag string 64 if cloudCredential != (names.CloudCredentialTag{}) { 65 cloudCredentialTag = cloudCredential.String() 66 } 67 createArgs := params.ModelCreateArgs{ 68 Name: name, 69 OwnerTag: names.NewUserTag(owner).String(), 70 Config: config, 71 CloudTag: cloudTag, 72 CloudRegion: cloudRegion, 73 CloudCredentialTag: cloudCredentialTag, 74 } 75 var modelInfo params.ModelInfo 76 err := c.facade.FacadeCall("CreateModel", createArgs, &modelInfo) 77 if err != nil { 78 return result, errors.Trace(err) 79 } 80 return convertParamsModelInfo(modelInfo) 81 } 82 83 func convertParamsModelInfo(modelInfo params.ModelInfo) (base.ModelInfo, error) { 84 cloud, err := names.ParseCloudTag(modelInfo.CloudTag) 85 if err != nil { 86 return base.ModelInfo{}, err 87 } 88 var credential string 89 if modelInfo.CloudCredentialTag != "" { 90 credTag, err := names.ParseCloudCredentialTag(modelInfo.CloudCredentialTag) 91 if err != nil { 92 return base.ModelInfo{}, err 93 } 94 credential = credTag.Id() 95 } 96 ownerTag, err := names.ParseUserTag(modelInfo.OwnerTag) 97 if err != nil { 98 return base.ModelInfo{}, err 99 } 100 result := base.ModelInfo{ 101 Name: modelInfo.Name, 102 UUID: modelInfo.UUID, 103 ControllerUUID: modelInfo.ControllerUUID, 104 ProviderType: modelInfo.ProviderType, 105 DefaultSeries: modelInfo.DefaultSeries, 106 Cloud: cloud.Id(), 107 CloudRegion: modelInfo.CloudRegion, 108 CloudCredential: credential, 109 Owner: ownerTag.Id(), 110 Life: string(modelInfo.Life), 111 } 112 result.Status = base.Status{ 113 Status: modelInfo.Status.Status, 114 Info: modelInfo.Status.Info, 115 Data: make(map[string]interface{}), 116 Since: modelInfo.Status.Since, 117 } 118 for k, v := range modelInfo.Status.Data { 119 result.Status.Data[k] = v 120 } 121 result.Users = make([]base.UserInfo, len(modelInfo.Users)) 122 for i, u := range modelInfo.Users { 123 result.Users[i] = base.UserInfo{ 124 UserName: u.UserName, 125 DisplayName: u.DisplayName, 126 Access: string(u.Access), 127 LastConnection: u.LastConnection, 128 } 129 } 130 result.Machines = make([]base.Machine, len(modelInfo.Machines)) 131 for i, m := range modelInfo.Machines { 132 machine := base.Machine{ 133 Id: m.Id, 134 InstanceId: m.InstanceId, 135 HasVote: m.HasVote, 136 WantsVote: m.WantsVote, 137 Status: m.Status, 138 } 139 if m.Hardware != nil { 140 machine.Hardware = &instance.HardwareCharacteristics{ 141 Arch: m.Hardware.Arch, 142 Mem: m.Hardware.Mem, 143 RootDisk: m.Hardware.RootDisk, 144 CpuCores: m.Hardware.Cores, 145 CpuPower: m.Hardware.CpuPower, 146 Tags: m.Hardware.Tags, 147 AvailabilityZone: m.Hardware.AvailabilityZone, 148 } 149 } 150 result.Machines[i] = machine 151 } 152 return result, nil 153 } 154 155 // ListModels returns the models that the specified user 156 // has access to in the current server. Only that controller owner 157 // can list models for any user (at this stage). Other users 158 // can only ask about their own models. 159 func (c *Client) ListModels(user string) ([]base.UserModel, error) { 160 var models params.UserModelList 161 if !names.IsValidUser(user) { 162 return nil, errors.Errorf("invalid user name %q", user) 163 } 164 entity := params.Entity{names.NewUserTag(user).String()} 165 err := c.facade.FacadeCall("ListModels", entity, &models) 166 if err != nil { 167 return nil, errors.Trace(err) 168 } 169 result := make([]base.UserModel, len(models.UserModels)) 170 for i, model := range models.UserModels { 171 owner, err := names.ParseUserTag(model.OwnerTag) 172 if err != nil { 173 return nil, errors.Annotatef(err, "OwnerTag %q at position %d", model.OwnerTag, i) 174 } 175 result[i] = base.UserModel{ 176 Name: model.Name, 177 UUID: model.UUID, 178 Owner: owner.Id(), 179 LastConnection: model.LastConnection, 180 } 181 } 182 return result, nil 183 } 184 185 func (c *Client) ModelInfo(tags []names.ModelTag) ([]params.ModelInfoResult, error) { 186 entities := params.Entities{ 187 Entities: make([]params.Entity, len(tags)), 188 } 189 for i, tag := range tags { 190 entities.Entities[i].Tag = tag.String() 191 } 192 var results params.ModelInfoResults 193 err := c.facade.FacadeCall("ModelInfo", entities, &results) 194 if err != nil { 195 return nil, errors.Trace(err) 196 } 197 if len(results.Results) != len(tags) { 198 return nil, errors.Errorf("expected %d result(s), got %d", len(tags), len(results.Results)) 199 } 200 return results.Results, nil 201 } 202 203 // DumpModel returns the serialized database agnostic model representation. 204 func (c *Client) DumpModel(model names.ModelTag) (map[string]interface{}, error) { 205 var results params.MapResults 206 entities := params.Entities{ 207 Entities: []params.Entity{{Tag: model.String()}}, 208 } 209 210 err := c.facade.FacadeCall("DumpModels", entities, &results) 211 if err != nil { 212 return nil, errors.Trace(err) 213 } 214 if count := len(results.Results); count != 1 { 215 return nil, errors.Errorf("unexpected result count: %d", count) 216 } 217 result := results.Results[0] 218 if result.Error != nil { 219 return nil, result.Error 220 } 221 return result.Result, nil 222 } 223 224 // DumpModelDB returns all relevant mongo documents for the model. 225 func (c *Client) DumpModelDB(model names.ModelTag) (map[string]interface{}, error) { 226 var results params.MapResults 227 entities := params.Entities{ 228 Entities: []params.Entity{{Tag: model.String()}}, 229 } 230 231 err := c.facade.FacadeCall("DumpModelsDB", entities, &results) 232 if err != nil { 233 return nil, errors.Trace(err) 234 } 235 if count := len(results.Results); count != 1 { 236 return nil, errors.Errorf("unexpected result count: %d", count) 237 } 238 result := results.Results[0] 239 if result.Error != nil { 240 return nil, result.Error 241 } 242 return result.Result, nil 243 } 244 245 // DestroyModel puts the specified model into a "dying" state, which will 246 // cause the model's resources to be cleaned up, after which the model will 247 // be removed. 248 func (c *Client) DestroyModel(tag names.ModelTag) error { 249 var results params.ErrorResults 250 entities := params.Entities{ 251 Entities: []params.Entity{{Tag: tag.String()}}, 252 } 253 if err := c.facade.FacadeCall("DestroyModels", entities, &results); err != nil { 254 return errors.Trace(err) 255 } 256 if n := len(results.Results); n != 1 { 257 return errors.Errorf("expected 1 result, got %d", n) 258 } 259 if err := results.Results[0].Error; err != nil { 260 return errors.Trace(err) 261 } 262 return nil 263 } 264 265 // GrantModel grants a user access to the specified models. 266 func (c *Client) GrantModel(user, access string, modelUUIDs ...string) error { 267 return c.modifyModelUser(params.GrantModelAccess, user, access, modelUUIDs) 268 } 269 270 // RevokeModel revokes a user's access to the specified models. 271 func (c *Client) RevokeModel(user, access string, modelUUIDs ...string) error { 272 return c.modifyModelUser(params.RevokeModelAccess, user, access, modelUUIDs) 273 } 274 275 func (c *Client) modifyModelUser(action params.ModelAction, user, access string, modelUUIDs []string) error { 276 var args params.ModifyModelAccessRequest 277 278 if !names.IsValidUser(user) { 279 return errors.Errorf("invalid username: %q", user) 280 } 281 userTag := names.NewUserTag(user) 282 283 modelAccess := permission.Access(access) 284 if err := permission.ValidateModelAccess(modelAccess); err != nil { 285 return errors.Trace(err) 286 } 287 for _, model := range modelUUIDs { 288 if !names.IsValidModel(model) { 289 return errors.Errorf("invalid model: %q", model) 290 } 291 modelTag := names.NewModelTag(model) 292 args.Changes = append(args.Changes, params.ModifyModelAccess{ 293 UserTag: userTag.String(), 294 Action: action, 295 Access: params.UserAccessPermission(modelAccess), 296 ModelTag: modelTag.String(), 297 }) 298 } 299 300 var result params.ErrorResults 301 err := c.facade.FacadeCall("ModifyModelAccess", args, &result) 302 if err != nil { 303 return errors.Trace(err) 304 } 305 if len(result.Results) != len(args.Changes) { 306 return errors.Errorf("expected %d results, got %d", len(args.Changes), len(result.Results)) 307 } 308 309 for i, r := range result.Results { 310 if r.Error != nil && r.Error.Code == params.CodeAlreadyExists { 311 logger.Warningf("model %q is already shared with %q", modelUUIDs[i], userTag.Id()) 312 result.Results[i].Error = nil 313 } 314 } 315 return result.Combine() 316 } 317 318 // ModelDefaults returns the default values for various sources used when 319 // creating a new model. 320 func (c *Client) ModelDefaults() (config.ModelDefaultAttributes, error) { 321 result := params.ModelDefaultsResult{} 322 err := c.facade.FacadeCall("ModelDefaults", nil, &result) 323 if err != nil { 324 return nil, errors.Trace(err) 325 } 326 values := make(config.ModelDefaultAttributes) 327 for name, val := range result.Config { 328 setting := config.AttributeDefaultValues{ 329 Default: val.Default, 330 Controller: val.Controller, 331 } 332 for _, region := range val.Regions { 333 setting.Regions = append(setting.Regions, config.RegionDefaultValue{ 334 Name: region.RegionName, 335 Value: region.Value}) 336 } 337 values[name] = setting 338 } 339 return values, nil 340 } 341 342 // SetModelDefaults updates the specified default model config values. 343 func (c *Client) SetModelDefaults(cloud, region string, config map[string]interface{}) error { 344 var cloudTag string 345 if cloud != "" { 346 cloudTag = names.NewCloudTag(cloud).String() 347 } 348 args := params.SetModelDefaults{ 349 Config: []params.ModelDefaultValues{{ 350 Config: config, 351 CloudTag: cloudTag, 352 CloudRegion: region, 353 }}, 354 } 355 var result params.ErrorResults 356 err := c.facade.FacadeCall("SetModelDefaults", args, &result) 357 if err != nil { 358 return err 359 } 360 return result.OneError() 361 } 362 363 // UnsetModelDefaults removes the specified default model config values. 364 func (c *Client) UnsetModelDefaults(cloud, region string, keys ...string) error { 365 var cloudTag string 366 if cloud != "" { 367 cloudTag = names.NewCloudTag(cloud).String() 368 } 369 args := params.UnsetModelDefaults{ 370 Keys: []params.ModelUnsetKeys{{ 371 Keys: keys, 372 CloudTag: cloudTag, 373 CloudRegion: region, 374 }}, 375 } 376 var result params.ErrorResults 377 err := c.facade.FacadeCall("UnsetModelDefaults", args, &result) 378 if err != nil { 379 return err 380 } 381 return result.OneError() 382 }