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 }