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 }