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  }