github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/controller/listcontrollers.go (about) 1 // Copyright 2015,2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller 5 6 import ( 7 "fmt" 8 "strings" 9 "sync" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "github.com/juju/gnuflag" 14 "gopkg.in/juju/names.v2" 15 16 "github.com/juju/juju/api/base" 17 "github.com/juju/juju/api/controller" 18 "github.com/juju/juju/cmd/modelcmd" 19 "github.com/juju/juju/environs/bootstrap" 20 "github.com/juju/juju/jujuclient" 21 "github.com/juju/juju/status" 22 ) 23 24 var helpControllersSummary = ` 25 Lists all controllers.`[1:] 26 27 var helpControllersDetails = ` 28 The output format may be selected with the '--format' option. In the 29 default tabular output, the current controller is marked with an asterisk. 30 31 Examples: 32 juju controllers 33 juju controllers --format json --output ~/tmp/controllers.json 34 35 See also: 36 models 37 show-controller`[1:] 38 39 // NewListControllersCommand returns a command to list registered controllers. 40 func NewListControllersCommand() cmd.Command { 41 cmd := &listControllersCommand{ 42 store: jujuclient.NewFileClientStore(), 43 } 44 return modelcmd.WrapBase(cmd) 45 } 46 47 // Info implements Command.Info 48 func (c *listControllersCommand) Info() *cmd.Info { 49 return &cmd.Info{ 50 Name: "controllers", 51 Purpose: helpControllersSummary, 52 Doc: helpControllersDetails, 53 Aliases: []string{"list-controllers"}, 54 } 55 } 56 57 // SetFlags implements Command.SetFlags. 58 func (c *listControllersCommand) SetFlags(f *gnuflag.FlagSet) { 59 c.JujuCommandBase.SetFlags(f) 60 f.BoolVar(&c.refresh, "refresh", false, "Connect to each controller to download the latest details") 61 c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{ 62 "yaml": cmd.FormatYaml, 63 "json": cmd.FormatJson, 64 "tabular": c.formatControllersListTabular, 65 }) 66 } 67 68 func (c *listControllersCommand) getAPI(controllerName string) (ControllerAccessAPI, error) { 69 if c.api != nil { 70 return c.api(controllerName), nil 71 } 72 api, err := c.NewAPIRoot(c.store, controllerName, "") 73 if err != nil { 74 return nil, errors.Annotate(err, "opening API connection") 75 } 76 return controller.NewClient(api), nil 77 } 78 79 // Run implements Command.Run 80 func (c *listControllersCommand) Run(ctx *cmd.Context) error { 81 controllers, err := c.store.AllControllers() 82 if err != nil { 83 return errors.Annotate(err, "failed to list controllers") 84 } 85 if len(controllers) == 0 && c.out.Name() == "tabular" { 86 ctx.Infof("%s", modelcmd.ErrNoControllersDefined) 87 return nil 88 } 89 if c.refresh && len(controllers) > 0 { 90 var wg sync.WaitGroup 91 wg.Add(len(controllers)) 92 for controllerName := range controllers { 93 name := controllerName 94 go func() { 95 defer wg.Done() 96 client, err := c.getAPI(name) 97 if err != nil { 98 fmt.Fprintf(ctx.GetStderr(), "error connecting to api for %q: %v\n", name, err) 99 return 100 } 101 defer client.Close() 102 if err := c.refreshControllerDetails(client, name); err != nil { 103 fmt.Fprintf(ctx.GetStderr(), "error updating cached details for %q: %v\n", name, err) 104 } 105 }() 106 } 107 wg.Wait() 108 // Reload controller details 109 controllers, err = c.store.AllControllers() 110 if err != nil { 111 return errors.Annotate(err, "failed to list controllers") 112 } 113 } 114 details, errs := c.convertControllerDetails(controllers) 115 if len(errs) > 0 { 116 fmt.Fprintln(ctx.Stderr, strings.Join(errs, "\n")) 117 } 118 currentController, err := c.store.CurrentController() 119 if errors.IsNotFound(err) { 120 currentController = "" 121 } else if err != nil { 122 return errors.Annotate(err, "getting current controller") 123 } 124 controllerSet := ControllerSet{ 125 Controllers: details, 126 CurrentController: currentController, 127 } 128 return c.out.Write(ctx, controllerSet) 129 } 130 131 func (c *listControllersCommand) refreshControllerDetails(client ControllerAccessAPI, controllerName string) error { 132 // First, get all the models the user can see, and their details. 133 var modelStatus []base.ModelStatus 134 allModels, err := client.AllModels() 135 if err != nil { 136 return err 137 } 138 var controllerModelUUID string 139 modelTags := make([]names.ModelTag, len(allModels)) 140 for i, m := range allModels { 141 modelTags[i] = names.NewModelTag(m.UUID) 142 if m.Name == bootstrap.ControllerModelName { 143 controllerModelUUID = m.UUID 144 } 145 } 146 modelStatus, err = client.ModelStatus(modelTags...) 147 if err != nil { 148 return err 149 } 150 151 c.mu.Lock() 152 defer c.mu.Unlock() 153 // Use the model information to update the cached controller details. 154 details, err := c.store.ControllerByName(controllerName) 155 if err != nil { 156 return err 157 } 158 159 modelCount := len(allModels) 160 details.ModelCount = &modelCount 161 machineCount := 0 162 for _, s := range modelStatus { 163 machineCount += s.TotalMachineCount 164 } 165 details.MachineCount = &machineCount 166 details.ActiveControllerMachineCount, details.ControllerMachineCount = controllerMachineCounts(controllerModelUUID, modelStatus) 167 return c.store.UpdateController(controllerName, *details) 168 } 169 170 func controllerMachineCounts(controllerModelUUID string, modelStatus []base.ModelStatus) (activeCount, totalCount int) { 171 for _, s := range modelStatus { 172 if s.UUID != controllerModelUUID { 173 continue 174 } 175 for _, m := range s.Machines { 176 if !m.WantsVote { 177 continue 178 } 179 totalCount++ 180 if m.Status != string(status.Down) && m.HasVote { 181 activeCount++ 182 } 183 } 184 } 185 return activeCount, totalCount 186 } 187 188 type listControllersCommand struct { 189 modelcmd.JujuCommandBase 190 191 out cmd.Output 192 store jujuclient.ClientStore 193 api func(controllerName string) ControllerAccessAPI 194 refresh bool 195 mu sync.Mutex 196 }