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 }