github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/manager/memory.go (about)

     1  package manager
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  	"os/exec"
     7  	"strconv"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/meminfo"
    10  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    11  )
    12  
    13  var (
    14  	errorInsufficientAvailableMemory = errors.New(
    15  		"insufficient available memory")
    16  	errorInsufficientUnallocatedMemory = errors.New(
    17  		"insufficient unallocated memory")
    18  	errorUnableToAllocatedMemory = errors.New("unable to allocate memory")
    19  )
    20  
    21  func checkAvailableMemory(memoryInMiB uint64) error {
    22  	if memInfo, err := meminfo.GetMemInfo(); err != nil {
    23  		return err
    24  	} else {
    25  		if memoryInMiB >= memInfo.Available>>20 {
    26  			return errorInsufficientAvailableMemory
    27  		}
    28  		return nil
    29  	}
    30  }
    31  
    32  func getVmInfoMemoryInMiB(vmInfo proto.VmInfo) uint64 {
    33  	var memoryTotal uint64
    34  	for _, volume := range vmInfo.Volumes {
    35  		if volume.Type == proto.VolumeTypeMemory {
    36  			memoryTotal += volume.Size
    37  		}
    38  	}
    39  	memoryInMiB := memoryTotal >> 20
    40  	if memoryInMiB<<20 < memoryTotal {
    41  		memoryInMiB += 1
    42  	}
    43  	return vmInfo.MemoryInMiB + memoryInMiB
    44  }
    45  
    46  func tryAllocateMemory(memoryInMiB uint64) <-chan error {
    47  	channel := make(chan error, 1)
    48  	executable, err := os.Executable()
    49  	if err != nil {
    50  		channel <- err
    51  		return channel
    52  	}
    53  	cmd := exec.Command(executable, "-testMemoryAvailable",
    54  		strconv.FormatUint(memoryInMiB, 10))
    55  	go func() {
    56  		if err := cmd.Run(); err != nil {
    57  			if _, ok := err.(*exec.ExitError); ok {
    58  				channel <- errorUnableToAllocatedMemory
    59  			} else {
    60  				channel <- err
    61  			}
    62  		} else {
    63  			channel <- nil
    64  		}
    65  	}()
    66  	return channel
    67  }
    68  
    69  // This will grab the Manager lock and the lock for each VM.
    70  func (m *Manager) getUnallocatedMemoryInMiB() uint64 {
    71  	m.mutex.RLock()
    72  	defer m.mutex.RUnlock()
    73  	return m.getUnallocatedMemoryInMiBWithLock(nil)
    74  }
    75  
    76  // This will grab the lock for each VM, except a specified VM which should
    77  // already be locked.
    78  func (m *Manager) getUnallocatedMemoryInMiBWithLock(locked *vmInfoType) uint64 {
    79  	unallocated := int64(m.memTotalInMiB)
    80  	for _, vm := range m.vms {
    81  		unallocated -= int64(vm.getMemoryInMiB(vm != locked))
    82  	}
    83  	if unallocated < 0 {
    84  		return 0
    85  	}
    86  	return uint64(unallocated)
    87  }
    88  
    89  // This will grab the lock for each VM, except a specified VM which should
    90  // already be locked.
    91  func (m *Manager) checkSufficientMemoryWithLock(memoryInMiB uint64,
    92  	locked *vmInfoType) error {
    93  	if memoryInMiB > m.getUnallocatedMemoryInMiBWithLock(locked) {
    94  		return errorInsufficientUnallocatedMemory
    95  	}
    96  	return checkAvailableMemory(memoryInMiB)
    97  }
    98  
    99  func (vm *vmInfoType) getMemoryInMiB(grabLock bool) uint64 {
   100  	if grabLock {
   101  		vm.mutex.RLock()
   102  		defer vm.mutex.RUnlock()
   103  	}
   104  	return getVmInfoMemoryInMiB(vm.VmInfo)
   105  }