github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/machine.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 11 apiservererrors "github.com/juju/juju/apiserver/errors" 12 "github.com/juju/juju/core/instance" 13 "github.com/juju/juju/core/model" 14 "github.com/juju/juju/core/status" 15 "github.com/juju/juju/rpc/params" 16 "github.com/juju/juju/state" 17 ) 18 19 // StateJobs translates a slice of multiwatcher jobs to their equivalents in state. 20 func StateJobs(jobs []model.MachineJob) ([]state.MachineJob, error) { 21 newJobs := make([]state.MachineJob, len(jobs)) 22 for i, job := range jobs { 23 newJob, err := machineJobFromParams(job) 24 if err != nil { 25 return nil, err 26 } 27 newJobs[i] = newJob 28 } 29 return newJobs, nil 30 } 31 32 // machineJobFromParams returns the job corresponding to model.MachineJob. 33 func machineJobFromParams(job model.MachineJob) (state.MachineJob, error) { 34 switch job { 35 case model.JobHostUnits: 36 return state.JobHostUnits, nil 37 case model.JobManageModel: 38 return state.JobManageModel, nil 39 default: 40 return -1, errors.Errorf("invalid machine job %q", job) 41 } 42 } 43 44 type origStateInterface interface { 45 Machine(string) (*state.Machine, error) 46 } 47 48 type stateInterface interface { 49 Machine(string) (Machine, error) 50 } 51 52 type stateShim struct { 53 origStateInterface 54 } 55 56 func (st *stateShim) Machine(id string) (Machine, error) { 57 return st.origStateInterface.Machine(id) 58 } 59 60 type ControllerNode interface { 61 Id() string 62 HasVote() bool 63 WantsVote() bool 64 } 65 66 type Machine interface { 67 Id() string 68 InstanceId() (instance.Id, error) 69 InstanceNames() (instance.Id, string, error) 70 Status() (status.StatusInfo, error) 71 ContainerType() instance.ContainerType 72 HardwareCharacteristics() (*instance.HardwareCharacteristics, error) 73 Life() state.Life 74 ForceDestroy(time.Duration) error 75 Destroy() error 76 IsManager() bool 77 IsLockedForSeriesUpgrade() (bool, error) 78 } 79 80 func DestroyMachines(st origStateInterface, force bool, maxWait time.Duration, ids ...string) error { 81 return destroyMachines(&stateShim{st}, force, maxWait, ids...) 82 } 83 84 func destroyMachines(st stateInterface, force bool, maxWait time.Duration, ids ...string) error { 85 var errs []error 86 for _, id := range ids { 87 machine, err := st.Machine(id) 88 switch { 89 case errors.IsNotFound(err): 90 err = errors.Errorf("machine %s does not exist", id) 91 case err != nil: 92 case force: 93 err = machine.ForceDestroy(maxWait) 94 case machine.Life() != state.Alive: 95 continue 96 default: 97 err = machine.Destroy() 98 } 99 if err != nil { 100 errs = append(errs, err) 101 } 102 } 103 return apiservererrors.DestroyErr("machines", ids, errs) 104 } 105 106 // ModelMachineInfo returns information about machine hardware for 107 // alive top level machines (not containers). 108 func ModelMachineInfo(st ModelManagerBackend) (machineInfo []params.ModelMachineInfo, _ error) { 109 machines, err := st.AllMachines() 110 if err != nil { 111 return nil, errors.Trace(err) 112 } 113 controllerNodes, err := st.ControllerNodes() 114 if err != nil { 115 return nil, errors.Trace(err) 116 } 117 hasVote := make(map[string]bool) 118 wantsVote := make(map[string]bool) 119 for _, n := range controllerNodes { 120 hasVote[n.Id()] = n.HasVote() 121 wantsVote[n.Id()] = n.WantsVote() 122 } 123 var primaryID string 124 primaryHA, err := st.HAPrimaryMachine() 125 if err != nil { 126 // We do not want to return any errors here as they are all 127 // non-fatal for this call since we can still 128 // get machine info even if we could not get HA Primary determined. 129 // Also on some non-HA setups, i.e. where mongo was not run with --replSet, 130 // this call will return an error. 131 logger.Warningf("could not determine if there is a primary HA machine: %v", err) 132 } 133 if len(controllerNodes) > 1 { 134 primaryID = primaryHA.Id() 135 } 136 137 for _, m := range machines { 138 if m.Life() != state.Alive { 139 continue 140 } 141 var aStatus string 142 // This is suboptimal as if there are many machines, 143 // we are making many calls into the DB for each machine. 144 var statusMessage string 145 statusInfo, err := m.Status() 146 if err == nil { 147 aStatus = string(statusInfo.Status) 148 statusMessage = statusInfo.Message 149 } else { 150 aStatus = err.Error() 151 } 152 mInfo := params.ModelMachineInfo{ 153 Id: m.Id(), 154 HasVote: hasVote[m.Id()], 155 WantsVote: wantsVote[m.Id()], 156 Status: aStatus, 157 Message: statusMessage, 158 } 159 if primaryID != "" { 160 if isPrimary := primaryID == m.Id(); isPrimary { 161 mInfo.HAPrimary = &isPrimary 162 } 163 } 164 instId, displayName, err := m.InstanceNames() 165 switch { 166 case err == nil: 167 mInfo.InstanceId = string(instId) 168 mInfo.DisplayName = displayName 169 case errors.IsNotProvisioned(err): 170 // ok, but no instance ID to get. 171 default: 172 return nil, errors.Trace(err) 173 } 174 if m.ContainerType() != "" && m.ContainerType() != instance.NONE { 175 machineInfo = append(machineInfo, mInfo) 176 continue 177 } 178 // Only include cores for physical machines. 179 hw, err := m.HardwareCharacteristics() 180 if err != nil && !errors.IsNotFound(err) { 181 return nil, errors.Trace(err) 182 } 183 if hw != nil && hw.String() != "" { 184 hwParams := ¶ms.MachineHardware{ 185 Cores: hw.CpuCores, 186 Arch: hw.Arch, 187 Mem: hw.Mem, 188 RootDisk: hw.RootDisk, 189 CpuPower: hw.CpuPower, 190 Tags: hw.Tags, 191 AvailabilityZone: hw.AvailabilityZone, 192 VirtType: hw.VirtType, 193 } 194 mInfo.Hardware = hwParams 195 } 196 machineInfo = append(machineInfo, mInfo) 197 } 198 return machineInfo, nil 199 }