github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/hypervisor/manager/start.go (about)

     1  package manager
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"errors"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    15  	"github.com/Cloud-Foundations/Dominator/lib/json"
    16  	"github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger"
    17  	"github.com/Cloud-Foundations/Dominator/lib/meminfo"
    18  	"github.com/Cloud-Foundations/Dominator/lib/objectserver/cachingreader"
    19  	"github.com/Cloud-Foundations/Dominator/lib/rpcclientpool"
    20  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    21  	"github.com/Cloud-Foundations/tricorder/go/tricorder/messages"
    22  	trimsg "github.com/Cloud-Foundations/tricorder/go/tricorder/messages"
    23  )
    24  
    25  const (
    26  	dirPerms = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP |
    27  		syscall.S_IROTH | syscall.S_IXOTH
    28  	privateFilePerms  = syscall.S_IRUSR | syscall.S_IWUSR
    29  	publicFilePerms   = privateFilePerms | syscall.S_IRGRP | syscall.S_IROTH
    30  	productSerialFile = "/sys/class/dmi/id/product_serial"
    31  )
    32  
    33  func newManager(startOptions StartOptions) (*Manager, error) {
    34  	memInfo, err := meminfo.GetMemInfo()
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	rootCookie := make([]byte, 32)
    39  	if _, err := rand.Read(rootCookie); err != nil {
    40  		return nil, err
    41  	}
    42  	manager := &Manager{
    43  		StartOptions:  startOptions,
    44  		rootCookie:    rootCookie,
    45  		memTotalInMiB: memInfo.Total >> 20,
    46  		notifiers:     make(map[<-chan proto.Update]chan<- proto.Update),
    47  		numCPU:        runtime.NumCPU(),
    48  		serialNumber:  readProductSerial(),
    49  		vms:           make(map[string]*vmInfoType),
    50  	}
    51  	err = fsutil.CopyToFile(manager.GetRootCookiePath(), privateFilePerms,
    52  		bytes.NewReader(rootCookie), 0)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	if err := manager.setupVolumes(startOptions); err != nil {
    57  		return nil, err
    58  	}
    59  	if err := manager.loadSubnets(); err != nil {
    60  		return nil, err
    61  	}
    62  	if err := manager.loadAddressPool(); err != nil {
    63  		return nil, err
    64  	}
    65  	dirname := filepath.Join(manager.StateDir, "VMs")
    66  	dir, err := os.Open(dirname)
    67  	if err != nil {
    68  		if os.IsNotExist(err) {
    69  			if err := os.Mkdir(dirname, dirPerms); err != nil {
    70  				return nil, errors.New(
    71  					"error making: " + dirname + ": " + err.Error())
    72  			}
    73  			dir, err = os.Open(dirname)
    74  		}
    75  	}
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	defer dir.Close()
    80  	names, err := dir.Readdirnames(-1)
    81  	if err != nil {
    82  		return nil, errors.New(
    83  			"error reading directory: " + dirname + ": " + err.Error())
    84  	}
    85  	for _, ipAddr := range names {
    86  		vmDirname := filepath.Join(dirname, ipAddr)
    87  		filename := filepath.Join(vmDirname, "info.json")
    88  		var vmInfo vmInfoType
    89  		if err := json.ReadFromFile(filename, &vmInfo); err != nil {
    90  			return nil, err
    91  		}
    92  		vmInfo.Address.Shrink()
    93  		vmInfo.manager = manager
    94  		vmInfo.dirname = vmDirname
    95  		vmInfo.ipAddress = ipAddr
    96  		vmInfo.ownerUsers = make(map[string]struct{}, len(vmInfo.OwnerUsers))
    97  		for _, username := range vmInfo.OwnerUsers {
    98  			vmInfo.ownerUsers[username] = struct{}{}
    99  		}
   100  		vmInfo.logger = prefixlogger.New(ipAddr+": ", manager.Logger)
   101  		vmInfo.metadataChannels = make(map[chan<- string]struct{})
   102  		manager.vms[ipAddr] = &vmInfo
   103  		if _, err := vmInfo.startManaging(0, false, false); err != nil {
   104  			manager.Logger.Println(err)
   105  			if ipAddr == "0.0.0.0" {
   106  				delete(manager.vms, ipAddr)
   107  				vmInfo.destroy()
   108  			}
   109  		}
   110  	}
   111  	// Check address pool for used addresses with no VM.
   112  	freeIPs := make(map[string]struct{}, len(manager.addressPool.Free))
   113  	for _, addr := range manager.addressPool.Free {
   114  		freeIPs[addr.IpAddress.String()] = struct{}{}
   115  	}
   116  	secondaryIPs := make(map[string]struct{})
   117  	for _, vm := range manager.vms {
   118  		for _, addr := range vm.SecondaryAddresses {
   119  			secondaryIPs[addr.IpAddress.String()] = struct{}{}
   120  		}
   121  	}
   122  	for _, addr := range manager.addressPool.Registered {
   123  		ipAddr := addr.IpAddress.String()
   124  		if _, ok := freeIPs[ipAddr]; ok {
   125  			continue
   126  		}
   127  		if _, ok := manager.vms[ipAddr]; ok {
   128  			continue
   129  		}
   130  		if _, ok := secondaryIPs[ipAddr]; ok {
   131  			continue
   132  		}
   133  		manager.Logger.Printf("%s shown as used but no corresponding VM\n",
   134  			ipAddr)
   135  	}
   136  	if startOptions.ObjectCacheBytes >= 1<<20 {
   137  		dirname := filepath.Join(filepath.Dir(manager.volumeDirectories[0]),
   138  			"objectcache")
   139  		if err := os.MkdirAll(dirname, dirPerms); err != nil {
   140  			return nil, err
   141  		}
   142  		objSrv, err := cachingreader.NewObjectServer(dirname,
   143  			startOptions.ObjectCacheBytes, startOptions.ImageServerAddress,
   144  			startOptions.Logger)
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  		manager.objectCache = objSrv
   149  	}
   150  	go manager.loopCheckHealthStatus()
   151  	return manager, nil
   152  }
   153  
   154  func readProductSerial() string {
   155  	if file, err := os.Open(productSerialFile); err != nil {
   156  		return ""
   157  	} else {
   158  		defer file.Close()
   159  		buffer := make([]byte, 256)
   160  		if nRead, err := file.Read(buffer); err != nil {
   161  			return ""
   162  		} else if nRead < 1 {
   163  			return ""
   164  		} else {
   165  			serial := strings.TrimSpace(string(buffer[:nRead]))
   166  			if serial == "System Serial Number" {
   167  				serial = ""
   168  			}
   169  			return serial
   170  		}
   171  	}
   172  }
   173  
   174  func (m *Manager) loopCheckHealthStatus() {
   175  	cr := rpcclientpool.New("tcp", ":6910", true, "")
   176  	for ; ; time.Sleep(time.Second * 10) {
   177  		healthStatus := m.checkHealthStatus(cr)
   178  		m.mutex.Lock()
   179  		if m.healthStatus != healthStatus {
   180  			m.healthStatus = healthStatus
   181  			m.sendUpdateWithLock(proto.Update{})
   182  		}
   183  		m.mutex.Unlock()
   184  	}
   185  }
   186  
   187  func (m *Manager) checkHealthStatus(cr *rpcclientpool.ClientResource) string {
   188  	client, err := cr.Get(nil)
   189  	if err != nil {
   190  		m.Logger.Printf("error getting health-agent client: %s", err)
   191  		return "bad health-agent"
   192  	}
   193  	defer client.Put()
   194  	var metric messages.Metric
   195  	err = client.Call("MetricsServer.GetMetric", "/sys/storage/health", &metric)
   196  	if err != nil {
   197  		if strings.Contains(err.Error(), trimsg.ErrMetricNotFound.Error()) {
   198  			return ""
   199  		}
   200  		m.Logger.Printf("error getting health-agent metrics: %s", err)
   201  		client.Close()
   202  		return "failed getting health metrics"
   203  	}
   204  	if healthStatus, ok := metric.Value.(string); !ok {
   205  		m.Logger.Println("list metric is not string")
   206  		return "bad health metric type"
   207  	} else if healthStatus == "good" {
   208  		return "healthy"
   209  	} else {
   210  		return healthStatus
   211  	}
   212  }