github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/highavailability/highavailability.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package highavailability
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  func init() {
    18  	common.RegisterStandardFacade("HighAvailability", 1, NewHighAvailabilityAPI)
    19  }
    20  
    21  // HighAvailability defines the methods on the highavailability API end point.
    22  type HighAvailability interface {
    23  	EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error)
    24  }
    25  
    26  // HighAvailabilityAPI implements the HighAvailability interface and is the concrete
    27  // implementation of the api end point.
    28  type HighAvailabilityAPI struct {
    29  	state      *state.State
    30  	resources  *common.Resources
    31  	authorizer common.Authorizer
    32  }
    33  
    34  var _ HighAvailability = (*HighAvailabilityAPI)(nil)
    35  
    36  // NewHighAvailabilityAPI creates a new server-side highavailability API end point.
    37  func NewHighAvailabilityAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*HighAvailabilityAPI, error) {
    38  	// Only clients and environment managers can access the high availability service.
    39  	if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() {
    40  		return nil, common.ErrPerm
    41  	}
    42  	return &HighAvailabilityAPI{
    43  		state:      st,
    44  		resources:  resources,
    45  		authorizer: authorizer,
    46  	}, nil
    47  }
    48  
    49  func (api *HighAvailabilityAPI) EnsureAvailability(args params.StateServersSpecs) (params.StateServersChangeResults, error) {
    50  	results := params.StateServersChangeResults{Results: make([]params.StateServersChangeResult, len(args.Specs))}
    51  	for i, stateServersSpec := range args.Specs {
    52  		result, err := EnsureAvailabilitySingle(api.state, stateServersSpec)
    53  		results.Results[i].Result = result
    54  		results.Results[i].Error = common.ServerError(err)
    55  	}
    56  	return results, nil
    57  }
    58  
    59  // Convert machine ids to tags.
    60  func machineIdsToTags(ids ...string) []string {
    61  	var result []string
    62  	for _, id := range ids {
    63  		result = append(result, names.NewMachineTag(id).String())
    64  	}
    65  	return result
    66  }
    67  
    68  // Generate a StateServersChanges structure.
    69  func stateServersChanges(change state.StateServersChanges) params.StateServersChanges {
    70  	return params.StateServersChanges{
    71  		Added:      machineIdsToTags(change.Added...),
    72  		Maintained: machineIdsToTags(change.Maintained...),
    73  		Removed:    machineIdsToTags(change.Removed...),
    74  		Promoted:   machineIdsToTags(change.Promoted...),
    75  		Demoted:    machineIdsToTags(change.Demoted...),
    76  	}
    77  }
    78  
    79  // EnsureAvailabilitySingle applies a single StateServersSpec specification to the current environment.
    80  // Exported so it can be called by the legacy client API in the client package.
    81  func EnsureAvailabilitySingle(st *state.State, spec params.StateServersSpec) (params.StateServersChanges, error) {
    82  	// Check if changes are allowed and the command may proceed.
    83  	blockChecker := common.NewBlockChecker(st)
    84  	if err := blockChecker.ChangeAllowed(); err != nil {
    85  		return params.StateServersChanges{}, errors.Trace(err)
    86  	}
    87  	// Validate the environment tag if present.
    88  	if spec.EnvironTag != "" {
    89  		tag, err := names.ParseEnvironTag(spec.EnvironTag)
    90  		if err != nil {
    91  			return params.StateServersChanges{}, errors.Errorf("invalid environment tag: %v", err)
    92  		}
    93  		if _, err := st.FindEntity(tag); err != nil {
    94  			return params.StateServersChanges{}, err
    95  		}
    96  	}
    97  
    98  	series := spec.Series
    99  	if series == "" {
   100  		ssi, err := st.StateServerInfo()
   101  		if err != nil {
   102  			return params.StateServersChanges{}, err
   103  		}
   104  
   105  		// We should always have at least one voting machine
   106  		// If we *really* wanted we could just pick whatever series is
   107  		// in the majority, but really, if we always copy the value of
   108  		// the first one, then they'll stay in sync.
   109  		if len(ssi.VotingMachineIds) == 0 {
   110  			// Better than a panic()?
   111  			return params.StateServersChanges{}, fmt.Errorf("internal error, failed to find any voting machines")
   112  		}
   113  		templateMachine, err := st.Machine(ssi.VotingMachineIds[0])
   114  		if err != nil {
   115  			return params.StateServersChanges{}, err
   116  		}
   117  		series = templateMachine.Series()
   118  	}
   119  	changes, err := st.EnsureAvailability(spec.NumStateServers, spec.Constraints, series, spec.Placement)
   120  	if err != nil {
   121  		return params.StateServersChanges{}, err
   122  	}
   123  	return stateServersChanges(changes), nil
   124  }