github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/controller/controller.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The controller package defines an API end point for functions dealing 5 // with controllers as a whole. 6 package controller 7 8 import ( 9 "sort" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 "github.com/juju/utils/set" 15 16 "github.com/juju/juju/apiserver/common" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/core/migration" 19 "github.com/juju/juju/state" 20 ) 21 22 var logger = loggo.GetLogger("juju.apiserver.controller") 23 24 func init() { 25 common.RegisterStandardFacade("Controller", 2, NewControllerAPI) 26 } 27 28 // Controller defines the methods on the controller API end point. 29 type Controller interface { 30 AllModels() (params.UserModelList, error) 31 DestroyController(args params.DestroyControllerArgs) error 32 ModelConfig() (params.ModelConfigResults, error) 33 ListBlockedModels() (params.ModelBlockInfoList, error) 34 RemoveBlocks(args params.RemoveBlocksArgs) error 35 WatchAllModels() (params.AllWatcherId, error) 36 ModelStatus(req params.Entities) (params.ModelStatusResults, error) 37 InitiateModelMigration(params.InitiateModelMigrationArgs) (params.InitiateModelMigrationResults, error) 38 } 39 40 // ControllerAPI implements the environment manager interface and is 41 // the concrete implementation of the api end point. 42 type ControllerAPI struct { 43 state *state.State 44 authorizer common.Authorizer 45 apiUser names.UserTag 46 resources *common.Resources 47 } 48 49 var _ Controller = (*ControllerAPI)(nil) 50 51 // NewControllerAPI creates a new api server endpoint for managing 52 // environments. 53 func NewControllerAPI( 54 st *state.State, 55 resources *common.Resources, 56 authorizer common.Authorizer, 57 ) (*ControllerAPI, error) { 58 if !authorizer.AuthClient() { 59 return nil, errors.Trace(common.ErrPerm) 60 } 61 62 // Since we know this is a user tag (because AuthClient is true), 63 // we just do the type assertion to the UserTag. 64 apiUser, _ := authorizer.GetAuthTag().(names.UserTag) 65 isAdmin, err := st.IsControllerAdministrator(apiUser) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 // The entire end point is only accessible to controller administrators. 70 if !isAdmin { 71 return nil, errors.Trace(common.ErrPerm) 72 } 73 74 return &ControllerAPI{ 75 state: st, 76 authorizer: authorizer, 77 apiUser: apiUser, 78 resources: resources, 79 }, nil 80 } 81 82 // AllModels allows controller administrators to get the list of all the 83 // environments in the controller. 84 func (s *ControllerAPI) AllModels() (params.UserModelList, error) { 85 result := params.UserModelList{} 86 87 // Get all the environments that the authenticated user can see, and 88 // supplement that with the other environments that exist that the user 89 // cannot see. The reason we do this is to get the LastConnection time for 90 // the environments that the user is able to see, so we have consistent 91 // output when listing with or without --all when an admin user. 92 environments, err := s.state.ModelsForUser(s.apiUser) 93 if err != nil { 94 return result, errors.Trace(err) 95 } 96 visibleEnvironments := set.NewStrings() 97 for _, env := range environments { 98 lastConn, err := env.LastConnection() 99 if err != nil && !state.IsNeverConnectedError(err) { 100 return result, errors.Trace(err) 101 } 102 visibleEnvironments.Add(env.UUID()) 103 result.UserModels = append(result.UserModels, params.UserModel{ 104 Model: params.Model{ 105 Name: env.Name(), 106 UUID: env.UUID(), 107 OwnerTag: env.Owner().String(), 108 }, 109 LastConnection: &lastConn, 110 }) 111 } 112 113 allEnvs, err := s.state.AllModels() 114 if err != nil { 115 return result, errors.Trace(err) 116 } 117 118 for _, env := range allEnvs { 119 if !visibleEnvironments.Contains(env.UUID()) { 120 result.UserModels = append(result.UserModels, params.UserModel{ 121 Model: params.Model{ 122 Name: env.Name(), 123 UUID: env.UUID(), 124 OwnerTag: env.Owner().String(), 125 }, 126 // No LastConnection as this user hasn't. 127 }) 128 } 129 } 130 131 // Sort the resulting sequence by environment name, then owner. 132 sort.Sort(orderedUserModels(result.UserModels)) 133 134 return result, nil 135 } 136 137 // ListBlockedModels returns a list of all environments on the controller 138 // which have a block in place. The resulting slice is sorted by environment 139 // name, then owner. Callers must be controller administrators to retrieve the 140 // list. 141 func (s *ControllerAPI) ListBlockedModels() (params.ModelBlockInfoList, error) { 142 results := params.ModelBlockInfoList{} 143 144 blocks, err := s.state.AllBlocksForController() 145 if err != nil { 146 return results, errors.Trace(err) 147 } 148 149 envBlocks := make(map[string][]string) 150 for _, block := range blocks { 151 uuid := block.ModelUUID() 152 types, ok := envBlocks[uuid] 153 if !ok { 154 types = []string{block.Type().String()} 155 } else { 156 types = append(types, block.Type().String()) 157 } 158 envBlocks[uuid] = types 159 } 160 161 for uuid, blocks := range envBlocks { 162 envInfo, err := s.state.GetModel(names.NewModelTag(uuid)) 163 if err != nil { 164 logger.Debugf("Unable to get name for model: %s", uuid) 165 continue 166 } 167 results.Models = append(results.Models, params.ModelBlockInfo{ 168 UUID: envInfo.UUID(), 169 Name: envInfo.Name(), 170 OwnerTag: envInfo.Owner().String(), 171 Blocks: blocks, 172 }) 173 } 174 175 // Sort the resulting sequence by environment name, then owner. 176 sort.Sort(orderedBlockInfo(results.Models)) 177 178 return results, nil 179 } 180 181 // ModelConfig returns the environment config for the controller 182 // environment. For information on the current environment, use 183 // client.ModelGet 184 func (s *ControllerAPI) ModelConfig() (params.ModelConfigResults, error) { 185 result := params.ModelConfigResults{} 186 187 controllerEnv, err := s.state.ControllerModel() 188 if err != nil { 189 return result, errors.Trace(err) 190 } 191 192 config, err := controllerEnv.Config() 193 if err != nil { 194 return result, errors.Trace(err) 195 } 196 197 result.Config = config.AllAttrs() 198 return result, nil 199 } 200 201 // RemoveBlocks removes all the blocks in the controller. 202 func (s *ControllerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error { 203 if !args.All { 204 return errors.New("not supported") 205 } 206 return errors.Trace(s.state.RemoveAllBlocksForController()) 207 } 208 209 // WatchAllModels starts watching events for all models in the 210 // controller. The returned AllWatcherId should be used with Next on the 211 // AllModelWatcher endpoint to receive deltas. 212 func (c *ControllerAPI) WatchAllModels() (params.AllWatcherId, error) { 213 w := c.state.WatchAllModels() 214 return params.AllWatcherId{ 215 AllWatcherId: c.resources.Register(w), 216 }, nil 217 } 218 219 type orderedBlockInfo []params.ModelBlockInfo 220 221 func (o orderedBlockInfo) Len() int { 222 return len(o) 223 } 224 225 func (o orderedBlockInfo) Less(i, j int) bool { 226 if o[i].Name < o[j].Name { 227 return true 228 } 229 if o[i].Name > o[j].Name { 230 return false 231 } 232 233 if o[i].OwnerTag < o[j].OwnerTag { 234 return true 235 } 236 if o[i].OwnerTag > o[j].OwnerTag { 237 return false 238 } 239 240 // Unreachable based on the rules of there not being duplicate 241 // environments of the same name for the same owner, but return false 242 // instead of panicing. 243 return false 244 } 245 246 // ModelStatus returns a summary of the environment. 247 func (c *ControllerAPI) ModelStatus(req params.Entities) (params.ModelStatusResults, error) { 248 envs := req.Entities 249 results := params.ModelStatusResults{} 250 status := make([]params.ModelStatus, len(envs)) 251 for i, env := range envs { 252 envStatus, err := c.environStatus(env.Tag) 253 if err != nil { 254 return results, errors.Trace(err) 255 } 256 status[i] = envStatus 257 } 258 results.Results = status 259 return results, nil 260 } 261 262 // InitiateModelMigration attempts to begin the migration of one or 263 // more models to other controllers. 264 func (c *ControllerAPI) InitiateModelMigration(reqArgs params.InitiateModelMigrationArgs) ( 265 params.InitiateModelMigrationResults, error, 266 ) { 267 out := params.InitiateModelMigrationResults{ 268 Results: make([]params.InitiateModelMigrationResult, len(reqArgs.Specs)), 269 } 270 for i, spec := range reqArgs.Specs { 271 result := &out.Results[i] 272 result.ModelTag = spec.ModelTag 273 id, err := c.initiateOneModelMigration(spec) 274 if err != nil { 275 result.Error = common.ServerError(err) 276 } else { 277 result.Id = id 278 } 279 } 280 return out, nil 281 } 282 283 func (c *ControllerAPI) initiateOneModelMigration(spec params.ModelMigrationSpec) (string, error) { 284 modelTag, err := names.ParseModelTag(spec.ModelTag) 285 if err != nil { 286 return "", errors.Annotate(err, "model tag") 287 } 288 289 // Ensure the model exists. 290 if _, err := c.state.GetModel(modelTag); err != nil { 291 return "", errors.Annotate(err, "unable to read model") 292 } 293 294 // Get State for model. 295 hostedState, err := c.state.ForModel(modelTag) 296 if err != nil { 297 return "", errors.Trace(err) 298 } 299 defer hostedState.Close() 300 301 // Start the migration. 302 targetInfo := spec.TargetInfo 303 304 controllerTag, err := names.ParseModelTag(targetInfo.ControllerTag) 305 if err != nil { 306 return "", errors.Annotate(err, "controller tag") 307 } 308 authTag, err := names.ParseUserTag(targetInfo.AuthTag) 309 if err != nil { 310 return "", errors.Annotate(err, "auth tag") 311 } 312 313 args := state.ModelMigrationSpec{ 314 InitiatedBy: c.apiUser, 315 TargetInfo: migration.TargetInfo{ 316 ControllerTag: controllerTag, 317 Addrs: targetInfo.Addrs, 318 CACert: targetInfo.CACert, 319 AuthTag: authTag, 320 Password: targetInfo.Password, 321 }, 322 } 323 mig, err := hostedState.CreateModelMigration(args) 324 if err != nil { 325 return "", errors.Trace(err) 326 } 327 return mig.Id(), nil 328 } 329 330 func (c *ControllerAPI) environStatus(tag string) (params.ModelStatus, error) { 331 var status params.ModelStatus 332 modelTag, err := names.ParseModelTag(tag) 333 if err != nil { 334 return status, errors.Trace(err) 335 } 336 st, err := c.state.ForModel(modelTag) 337 if err != nil { 338 return status, errors.Trace(err) 339 } 340 defer st.Close() 341 342 machines, err := st.AllMachines() 343 if err != nil { 344 return status, errors.Trace(err) 345 } 346 347 var hostedMachines []*state.Machine 348 for _, m := range machines { 349 if !m.IsManager() { 350 hostedMachines = append(hostedMachines, m) 351 } 352 } 353 354 services, err := st.AllServices() 355 if err != nil { 356 return status, errors.Trace(err) 357 } 358 359 env, err := st.Model() 360 if err != nil { 361 return status, errors.Trace(err) 362 } 363 if err != nil { 364 return status, errors.Trace(err) 365 } 366 367 return params.ModelStatus{ 368 ModelTag: tag, 369 OwnerTag: env.Owner().String(), 370 Life: params.Life(env.Life().String()), 371 HostedMachineCount: len(hostedMachines), 372 ServiceCount: len(services), 373 }, nil 374 } 375 376 func (o orderedBlockInfo) Swap(i, j int) { 377 o[i], o[j] = o[j], o[i] 378 } 379 380 type orderedUserModels []params.UserModel 381 382 func (o orderedUserModels) Len() int { 383 return len(o) 384 } 385 386 func (o orderedUserModels) Less(i, j int) bool { 387 if o[i].Name < o[j].Name { 388 return true 389 } 390 if o[i].Name > o[j].Name { 391 return false 392 } 393 394 if o[i].OwnerTag < o[j].OwnerTag { 395 return true 396 } 397 if o[i].OwnerTag > o[j].OwnerTag { 398 return false 399 } 400 401 // Unreachable based on the rules of there not being duplicate 402 // environments of the same name for the same owner, but return false 403 // instead of panicing. 404 return false 405 } 406 407 func (o orderedUserModels) Swap(i, j int) { 408 o[i], o[j] = o[j], o[i] 409 }