github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/fleetmanager/hypervisors/ipmi.go (about)

     1  package hypervisors
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"net"
     7  	"os/exec"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/lib/net/util"
    12  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    13  )
    14  
    15  const powerOff = "Power is off"
    16  
    17  var (
    18  	myIP    net.IP
    19  	wolConn *net.UDPConn
    20  )
    21  
    22  func (m *Manager) powerOnMachine(hostname string,
    23  	authInfo *srpc.AuthInformation) error {
    24  	h, err := m.getLockedHypervisor(hostname, false)
    25  	if err != nil {
    26  		return err
    27  	}
    28  	defer h.mutex.RUnlock()
    29  	if err := h.checkAuth(authInfo); err != nil {
    30  		return err
    31  	}
    32  	var ipmiHostname string
    33  	if len(h.machine.IPMI.HostIpAddress) > 0 {
    34  		ipmiHostname = h.machine.IPMI.HostIpAddress.String()
    35  	} else if h.machine.IPMI.Hostname != "" {
    36  		ipmiHostname = h.machine.IPMI.Hostname
    37  	} else if sentWakeOnLan, err := m.wakeOnLan(h); err != nil {
    38  		return err
    39  	} else if sentWakeOnLan {
    40  		return nil
    41  	} else {
    42  		return fmt.Errorf("no IPMI address for: %s", hostname)
    43  	}
    44  	cmd := exec.Command("ipmitool", "-f", m.ipmiPasswordFile,
    45  		"-H", ipmiHostname, "-I", "lanplus", "-U", m.ipmiUsername,
    46  		"chassis", "power", "on")
    47  	if output, err := cmd.CombinedOutput(); err != nil {
    48  		return fmt.Errorf("%s: %s", err, string(output))
    49  	}
    50  	return nil
    51  }
    52  
    53  func (m *Manager) wakeOnLan(h *hypervisorType) (bool, error) {
    54  	if len(h.machine.HostMacAddress) < 1 {
    55  		return false, nil
    56  	}
    57  	routeTable, err := util.GetRouteTable()
    58  	if err != nil {
    59  		return false, err
    60  	}
    61  	var routeEntry *util.RouteEntry
    62  	for _, route := range routeTable.RouteEntries {
    63  		if route.Flags&util.RouteFlagUp == 0 {
    64  			continue
    65  		}
    66  		if route.Flags&util.RouteFlagGateway != 0 {
    67  			continue
    68  		}
    69  		if h.machine.HostIpAddress.Mask(route.Mask).Equal(route.BaseAddr) {
    70  			routeEntry = route
    71  			break
    72  		}
    73  	}
    74  	if routeEntry == nil {
    75  		return false, nil
    76  	}
    77  	if wolConn == nil {
    78  		myIP, err = util.GetMyIP()
    79  		if err != nil {
    80  			return false, err
    81  		}
    82  		wolConn, err = net.ListenUDP("udp", &net.UDPAddr{IP: myIP})
    83  		if err != nil {
    84  			return false, err
    85  		}
    86  	}
    87  	packet := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
    88  	for count := 0; count < 16; count++ {
    89  		packet = append(packet, h.machine.HostMacAddress...)
    90  	}
    91  	remoteAddr := &net.UDPAddr{IP: routeEntry.BroadcastAddr, Port: 9}
    92  	if _, err := wolConn.WriteToUDP(packet, remoteAddr); err != nil {
    93  		return false, err
    94  	}
    95  	return true, nil
    96  }
    97  
    98  func (m *Manager) probeUnreachable(h *hypervisorType) probeStatus {
    99  	if m.ipmiPasswordFile == "" || m.ipmiUsername == "" {
   100  		return probeStatusUnreachable
   101  	}
   102  	var ipmiHostname string
   103  	if len(h.machine.IPMI.HostIpAddress) > 0 {
   104  		ipmiHostname = h.machine.IPMI.HostIpAddress.String()
   105  	} else if h.machine.IPMI.Hostname != "" {
   106  		ipmiHostname = h.machine.IPMI.Hostname
   107  	} else {
   108  		return probeStatusUnreachable
   109  	}
   110  	h.mutex.RLock()
   111  	previousProbeStatus := h.probeStatus
   112  	h.mutex.RUnlock()
   113  	mimimumProbeInterval := time.Second * time.Duration(30+rand.Intn(30))
   114  	if previousProbeStatus == probeStatusOff &&
   115  		time.Until(h.lastIpmiProbe.Add(mimimumProbeInterval)) > 0 {
   116  		return probeStatusOff
   117  	}
   118  	cmd := exec.Command("ipmitool", "-f", m.ipmiPasswordFile,
   119  		"-H", ipmiHostname, "-I", "lanplus", "-U", m.ipmiUsername,
   120  		"chassis", "power", "status")
   121  	h.lastIpmiProbe = time.Now()
   122  	if output, err := cmd.Output(); err != nil {
   123  		if previousProbeStatus == probeStatusOff {
   124  			return probeStatusOff
   125  		} else {
   126  			return probeStatusUnreachable
   127  		}
   128  	} else if strings.Contains(string(output), powerOff) {
   129  		return probeStatusOff
   130  	}
   131  	return probeStatusUnreachable
   132  }