github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/manager/stop.go (about) 1 package manager 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "sync" 8 "time" 9 10 proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 11 ) 12 13 type flusher interface { 14 Flush() error 15 } 16 17 func (m *Manager) powerOff(stopVMs bool) error { 18 m.mutex.RLock() 19 if stopVMs { 20 m.shutdownVMs() 21 } 22 defer m.mutex.RUnlock() 23 for _, vm := range m.vms { 24 if vm.State != proto.StateStopped { 25 return fmt.Errorf("%s is not shut down", vm.Address.IpAddress) 26 } 27 } 28 cmd := exec.Command("poweroff") 29 if output, err := cmd.CombinedOutput(); err != nil { 30 return fmt.Errorf("%s: %s", err, string(output)) 31 } 32 return nil 33 } 34 35 // shutdownVMs will shut down all running VMs and wait. This must be called with 36 // the read lock held, and it will unlock the lock after signalling VMs to shut 37 // down but before waiting for them to finish shutting down. 38 func (m *Manager) shutdownVMs() { 39 m.shuttingDown = true 40 var waitGroup sync.WaitGroup 41 var failCount uint 42 var failMutex sync.Mutex 43 for _, vm := range m.vms { 44 waitGroup.Add(1) 45 go func(vm *vmInfoType) { 46 defer waitGroup.Done() 47 if !vm.shutdown() { 48 failMutex.Lock() 49 failCount++ 50 failMutex.Unlock() 51 } 52 }(vm) 53 } 54 m.mutex.RUnlock() 55 waitGroup.Wait() 56 if failCount > 1 { 57 m.Logger.Printf("stopping but failed to cleanly shut down %d VMs\n", 58 failCount) 59 } else if failCount > 0 { 60 m.Logger.Println("stopping but failed to cleanly shut down 1 VM") 61 } else { 62 m.Logger.Println("stopping cleanly after shutting down VMs") 63 } 64 time.Sleep(time.Second) // Wait just a little for background work. 65 if flusher, ok := m.Logger.(flusher); ok { 66 flusher.Flush() 67 } 68 } 69 70 func (m *Manager) shutdownVMsAndExit() { 71 m.mutex.RLock() 72 m.shutdownVMs() 73 os.Exit(0) 74 } 75 76 // Returns false if the VM failed to shut down cleanly, else true. 77 func (vm *vmInfoType) shutdown() bool { 78 vm.mutex.RLock() 79 switch vm.State { 80 case proto.StateStarting, proto.StateRunning: 81 stoppedNotifier := make(chan struct{}, 1) 82 vm.stoppedNotifier = stoppedNotifier 83 vm.commandInput <- "system_powerdown" 84 vm.mutex.RUnlock() 85 timer := time.NewTimer(time.Minute) 86 select { 87 case <-stoppedNotifier: 88 if !timer.Stop() { 89 <-timer.C 90 } 91 vm.logger.Println("shut down cleanly for system shutdown") 92 case <-timer.C: 93 vm.logger.Println("shutdown timed out: killing VM") 94 vm.commandInput <- "quit" 95 return false 96 } 97 default: 98 vm.mutex.RUnlock() 99 } 100 return true 101 }