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  }