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 := &params.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  }