github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/hypervisor/manager/vm.go (about) 1 package manager 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "syscall" 15 "time" 16 17 domlib "github.com/Cloud-Foundations/Dominator/dom/lib" 18 hyperclient "github.com/Cloud-Foundations/Dominator/hypervisor/client" 19 imclient "github.com/Cloud-Foundations/Dominator/imageserver/client" 20 "github.com/Cloud-Foundations/Dominator/lib/errors" 21 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 22 "github.com/Cloud-Foundations/Dominator/lib/filesystem/scanner" 23 "github.com/Cloud-Foundations/Dominator/lib/filesystem/util" 24 "github.com/Cloud-Foundations/Dominator/lib/filter" 25 "github.com/Cloud-Foundations/Dominator/lib/format" 26 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 27 "github.com/Cloud-Foundations/Dominator/lib/hash" 28 "github.com/Cloud-Foundations/Dominator/lib/image" 29 "github.com/Cloud-Foundations/Dominator/lib/json" 30 "github.com/Cloud-Foundations/Dominator/lib/log" 31 "github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger" 32 "github.com/Cloud-Foundations/Dominator/lib/mbr" 33 libnet "github.com/Cloud-Foundations/Dominator/lib/net" 34 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 35 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 36 objclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 37 "github.com/Cloud-Foundations/Dominator/lib/rsync" 38 "github.com/Cloud-Foundations/Dominator/lib/srpc" 39 "github.com/Cloud-Foundations/Dominator/lib/tags" 40 "github.com/Cloud-Foundations/Dominator/lib/verstr" 41 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 42 proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 43 subproto "github.com/Cloud-Foundations/Dominator/proto/sub" 44 sublib "github.com/Cloud-Foundations/Dominator/sub/lib" 45 ) 46 47 const ( 48 bootlogFilename = "bootlog" 49 serialSockFilename = "serial0.sock" 50 ) 51 52 var ( 53 errorNoAccessToResource = errors.New("no access to resource") 54 ) 55 56 func computeSize(minimumFreeBytes, roundupPower, size uint64) uint64 { 57 minBytes := size + size>>3 // 12% extra for good luck. 58 minBytes += minimumFreeBytes 59 if roundupPower < 24 { 60 roundupPower = 24 // 16 MiB. 61 } 62 imageUnits := minBytes >> roundupPower 63 if imageUnits<<roundupPower < minBytes { 64 imageUnits++ 65 } 66 return imageUnits << roundupPower 67 } 68 69 func copyData(filename string, reader io.Reader, length uint64) error { 70 file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 71 fsutil.PrivateFilePerms) 72 if err != nil { 73 return err 74 } 75 defer file.Close() 76 if err := setVolumeSize(filename, length); err != nil { 77 return err 78 } 79 if reader == nil { 80 return nil 81 } 82 _, err = io.CopyN(file, reader, int64(length)) 83 return err 84 } 85 86 func createTapDevice(bridge string) (*os.File, error) { 87 tapFile, tapName, err := libnet.CreateTapDevice() 88 if err != nil { 89 return nil, fmt.Errorf("error creating tap device: %s", err) 90 } 91 doAutoClose := true 92 defer func() { 93 if doAutoClose { 94 tapFile.Close() 95 } 96 }() 97 cmd := exec.Command("ip", "link", "set", tapName, "up") 98 if output, err := cmd.CombinedOutput(); err != nil { 99 return nil, fmt.Errorf("error upping: %s: %s", err, output) 100 } 101 cmd = exec.Command("ip", "link", "set", tapName, "master", bridge) 102 if output, err := cmd.CombinedOutput(); err != nil { 103 return nil, fmt.Errorf("error attaching: %s: %s", err, output) 104 } 105 doAutoClose = false 106 return tapFile, nil 107 } 108 109 func deleteFilesNotInImage(imgFS, vmFS *filesystem.FileSystem, 110 rootDir string, logger log.DebugLogger) error { 111 var totalBytes uint64 112 imgHashToInodesTable := imgFS.HashToInodesTable() 113 imgComputedFiles := make(map[string]struct{}) 114 imgFS.ForEachFile(func(name string, inodeNumber uint64, 115 inode filesystem.GenericInode) error { 116 if _, ok := inode.(*filesystem.ComputedRegularInode); ok { 117 imgComputedFiles[name] = struct{}{} 118 } 119 return nil 120 }) 121 for filename, inum := range vmFS.FilenameToInodeTable() { 122 if inode, ok := vmFS.InodeTable[inum].(*filesystem.RegularInode); ok { 123 if inode.Size < 1 { 124 continue 125 } 126 if _, isComputed := imgComputedFiles[filename]; isComputed { 127 continue 128 } 129 if _, inImage := imgHashToInodesTable[inode.Hash]; inImage { 130 continue 131 } 132 pathname := filepath.Join(rootDir, filename) 133 if err := os.Remove(pathname); err != nil { 134 return err 135 } 136 logger.Debugf(1, "pre-delete: %s\n", pathname) 137 totalBytes += inode.Size 138 } 139 } 140 logger.Debugf(0, "pre-delete: totalBytes: %s\n", 141 format.FormatBytes(totalBytes)) 142 return nil 143 } 144 145 func extractKernel(volume proto.LocalVolume, extension string, 146 objectsGetter objectserver.ObjectsGetter, fs *filesystem.FileSystem, 147 bootInfo *util.BootInfoType) error { 148 dirent := bootInfo.KernelImageDirent 149 if dirent == nil { 150 return errors.New("no kernel image found") 151 } 152 inode, ok := dirent.Inode().(*filesystem.RegularInode) 153 if !ok { 154 return errors.New("kernel image is not a regular file") 155 } 156 inode.Size = 0 157 filename := filepath.Join(volume.DirectoryToCleanup, "kernel"+extension) 158 _, err := objectserver.LinkObject(filename, objectsGetter, inode.Hash) 159 if err != nil { 160 return err 161 } 162 dirent = bootInfo.InitrdImageDirent 163 if dirent != nil { 164 inode, ok := dirent.Inode().(*filesystem.RegularInode) 165 if !ok { 166 return errors.New("initrd image is not a regular file") 167 } 168 inode.Size = 0 169 filename := filepath.Join(volume.DirectoryToCleanup, 170 "initrd"+extension) 171 _, err = objectserver.LinkObject(filename, objectsGetter, 172 inode.Hash) 173 if err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 func maybeDrainAll(conn *srpc.Conn, request proto.CreateVmRequest) error { 181 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 182 return err 183 } 184 if err := maybeDrainUserData(conn, request); err != nil { 185 return err 186 } 187 return nil 188 } 189 190 func maybeDrainImage(imageReader io.Reader, imageDataSize uint64) error { 191 if imageDataSize > 0 { // Drain data. 192 _, err := io.CopyN(ioutil.Discard, imageReader, int64(imageDataSize)) 193 return err 194 } 195 return nil 196 } 197 198 func maybeDrainUserData(conn *srpc.Conn, request proto.CreateVmRequest) error { 199 if request.UserDataSize > 0 { // Drain data. 200 _, err := io.CopyN(ioutil.Discard, conn, int64(request.UserDataSize)) 201 return err 202 } 203 return nil 204 } 205 206 func readData(firstByte byte, moreBytes <-chan byte) []byte { 207 buffer := make([]byte, 1, len(moreBytes)+1) 208 buffer[0] = firstByte 209 for { 210 select { 211 case char, ok := <-moreBytes: 212 if !ok { 213 return buffer 214 } 215 buffer = append(buffer, char) 216 default: 217 return buffer 218 } 219 } 220 } 221 222 func readOne(objectsDir string, hashVal hash.Hash, length uint64, 223 reader io.Reader) error { 224 filename := filepath.Join(objectsDir, objectcache.HashToFilename(hashVal)) 225 dirname := filepath.Dir(filename) 226 if err := os.MkdirAll(dirname, dirPerms); err != nil { 227 return err 228 } 229 return fsutil.CopyToFile(filename, fsutil.PrivateFilePerms, reader, length) 230 } 231 232 func setVolumeSize(filename string, size uint64) error { 233 if err := os.Truncate(filename, int64(size)); err != nil { 234 return err 235 } 236 return fsutil.Fallocate(filename, size) 237 } 238 239 func (m *Manager) acknowledgeVm(ipAddr net.IP, 240 authInfo *srpc.AuthInformation) error { 241 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 242 if err != nil { 243 return err 244 } 245 defer vm.mutex.Unlock() 246 vm.destroyTimer.Stop() 247 return nil 248 } 249 250 func (m *Manager) addVmVolumes(ipAddr net.IP, authInfo *srpc.AuthInformation, 251 volumeSizes []uint64) error { 252 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 253 if err != nil { 254 return err 255 } 256 defer vm.mutex.Unlock() 257 if vm.State != proto.StateStopped { 258 return errors.New("VM is not stopped") 259 } 260 volumes := make([]proto.Volume, 0, len(volumeSizes)) 261 for _, size := range volumeSizes { 262 volumes = append(volumes, proto.Volume{Size: size}) 263 } 264 volumeDirectories, err := vm.manager.getVolumeDirectories(0, volumes, 265 vm.SpreadVolumes) 266 if err != nil { 267 return err 268 } 269 volumeLocations := make([]proto.LocalVolume, 0, len(volumes)) 270 defer func() { 271 for _, volumeLocation := range volumeLocations { 272 os.Remove(volumeLocation.Filename) 273 os.Remove(volumeLocation.DirectoryToCleanup) 274 } 275 }() 276 for index, volumeDirectory := range volumeDirectories { 277 dirname := filepath.Join(volumeDirectory, vm.ipAddress) 278 filename := filepath.Join(dirname, 279 fmt.Sprintf("secondary-volume.%d", len(vm.Volumes)-1+index)) 280 volumeLocation := proto.LocalVolume{ 281 DirectoryToCleanup: dirname, 282 Filename: filename, 283 } 284 if err := os.MkdirAll(dirname, dirPerms); err != nil { 285 return err 286 } 287 cFlags := os.O_CREATE | os.O_EXCL | os.O_RDWR 288 file, err := os.OpenFile(filename, cFlags, privateFilePerms) 289 if err != nil { 290 return err 291 } else { 292 file.Close() 293 } 294 if err := setVolumeSize(filename, volumeSizes[index]); err != nil { 295 return err 296 } 297 volumeLocations = append(volumeLocations, volumeLocation) 298 } 299 vm.VolumeLocations = append(vm.VolumeLocations, volumeLocations...) 300 volumeLocations = nil // Prevent cleanup. Thunderbirds are Go! 301 vm.Volumes = append(vm.Volumes, volumes...) 302 vm.writeAndSendInfo() 303 return nil 304 } 305 306 func (m *Manager) allocateVm(req proto.CreateVmRequest, 307 authInfo *srpc.AuthInformation) (*vmInfoType, error) { 308 if err := req.ConsoleType.CheckValid(); err != nil { 309 return nil, err 310 } 311 if req.MemoryInMiB < 1 { 312 return nil, errors.New("no memory specified") 313 } 314 if req.MilliCPUs < 1 { 315 return nil, errors.New("no CPUs specified") 316 } 317 subnetIDs := map[string]struct{}{req.SubnetId: {}} 318 for _, subnetId := range req.SecondarySubnetIDs { 319 if subnetId == "" { 320 return nil, 321 errors.New("cannot give unspecified secondary subnet ID") 322 } 323 if _, ok := subnetIDs[subnetId]; ok { 324 return nil, 325 fmt.Errorf("subnet: %s specified multiple times", subnetId) 326 } 327 subnetIDs[subnetId] = struct{}{} 328 } 329 address, subnetId, err := m.getFreeAddress(req.Address.IpAddress, 330 req.SubnetId, authInfo) 331 if err != nil { 332 return nil, err 333 } 334 addressesToFree := []proto.Address{address} 335 defer func() { 336 for _, address := range addressesToFree { 337 err := m.releaseAddressInPool(address) 338 if err != nil { 339 m.Logger.Println(err) 340 } 341 } 342 }() 343 var secondaryAddresses []proto.Address 344 for index, subnetId := range req.SecondarySubnetIDs { 345 var reqIpAddr net.IP 346 if index < len(req.SecondaryAddresses) { 347 reqIpAddr = req.SecondaryAddresses[index].IpAddress 348 } 349 secondaryAddress, _, err := m.getFreeAddress(reqIpAddr, subnetId, 350 authInfo) 351 if err != nil { 352 return nil, err 353 } 354 secondaryAddresses = append(secondaryAddresses, secondaryAddress) 355 addressesToFree = append(addressesToFree, secondaryAddress) 356 } 357 m.mutex.Lock() 358 defer m.mutex.Unlock() 359 if err := m.checkSufficientCPUWithLock(req.MilliCPUs); err != nil { 360 return nil, err 361 } 362 if err := m.checkSufficientMemoryWithLock(req.MemoryInMiB); err != nil { 363 return nil, err 364 } 365 var ipAddress string 366 if len(address.IpAddress) < 1 { 367 ipAddress = "0.0.0.0" 368 } else { 369 ipAddress = address.IpAddress.String() 370 } 371 vm := &vmInfoType{ 372 LocalVmInfo: proto.LocalVmInfo{ 373 VmInfo: proto.VmInfo{ 374 Address: address, 375 ConsoleType: req.ConsoleType, 376 DestroyProtection: req.DestroyProtection, 377 DisableVirtIO: req.DisableVirtIO, 378 Hostname: req.Hostname, 379 ImageName: req.ImageName, 380 ImageURL: req.ImageURL, 381 MemoryInMiB: req.MemoryInMiB, 382 MilliCPUs: req.MilliCPUs, 383 OwnerGroups: req.OwnerGroups, 384 SpreadVolumes: req.SpreadVolumes, 385 SecondaryAddresses: secondaryAddresses, 386 SecondarySubnetIDs: req.SecondarySubnetIDs, 387 State: proto.StateStarting, 388 SubnetId: subnetId, 389 Tags: req.Tags, 390 }, 391 }, 392 manager: m, 393 dirname: filepath.Join(m.StateDir, "VMs", ipAddress), 394 ipAddress: ipAddress, 395 logger: prefixlogger.New(ipAddress+": ", m.Logger), 396 metadataChannels: make(map[chan<- string]struct{}), 397 } 398 m.vms[ipAddress] = vm 399 addressesToFree = nil 400 return vm, nil 401 } 402 403 func (m *Manager) becomePrimaryVmOwner(ipAddr net.IP, 404 authInfo *srpc.AuthInformation) error { 405 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 406 if err != nil { 407 return err 408 } 409 defer vm.mutex.Unlock() 410 if vm.OwnerUsers[0] == authInfo.Username { 411 return errors.New("you already are the primary owner") 412 } 413 ownerUsers := make([]string, 1, len(vm.OwnerUsers[0])) 414 ownerUsers[0] = authInfo.Username 415 for _, user := range vm.OwnerUsers { 416 if user != authInfo.Username { 417 ownerUsers = append(ownerUsers, user) 418 } 419 } 420 vm.OwnerUsers = ownerUsers 421 vm.ownerUsers = make(map[string]struct{}, len(ownerUsers)) 422 for _, user := range ownerUsers { 423 vm.ownerUsers[user] = struct{}{} 424 } 425 vm.writeAndSendInfo() 426 return nil 427 } 428 429 func (m *Manager) changeVmConsoleType(ipAddr net.IP, 430 authInfo *srpc.AuthInformation, consoleType proto.ConsoleType) error { 431 if err := consoleType.CheckValid(); err != nil { 432 return err 433 } 434 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 435 if err != nil { 436 return err 437 } 438 defer vm.mutex.Unlock() 439 if vm.State != proto.StateStopped { 440 return errors.New("VM is not stopped") 441 } 442 vm.ConsoleType = consoleType 443 vm.writeAndSendInfo() 444 return nil 445 } 446 447 func (m *Manager) changeVmDestroyProtection(ipAddr net.IP, 448 authInfo *srpc.AuthInformation, destroyProtection bool) error { 449 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 450 if err != nil { 451 return err 452 } 453 defer vm.mutex.Unlock() 454 vm.DestroyProtection = destroyProtection 455 vm.writeAndSendInfo() 456 return nil 457 } 458 459 func (m *Manager) changeVmOwnerUsers(ipAddr net.IP, 460 authInfo *srpc.AuthInformation, extraUsers []string) error { 461 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 462 if err != nil { 463 return err 464 } 465 defer vm.mutex.Unlock() 466 ownerUsers := make([]string, 1, len(extraUsers)+1) 467 ownerUsers[0] = vm.OwnerUsers[0] 468 for _, user := range extraUsers { 469 ownerUsers = append(ownerUsers, user) 470 } 471 vm.OwnerUsers = ownerUsers 472 vm.ownerUsers = make(map[string]struct{}, len(ownerUsers)) 473 for _, user := range ownerUsers { 474 vm.ownerUsers[user] = struct{}{} 475 } 476 vm.writeAndSendInfo() 477 return nil 478 } 479 480 func (m *Manager) changeVmSize(ipAddr net.IP, authInfo *srpc.AuthInformation, 481 memoryInMiB uint64, milliCPUs uint) error { 482 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 483 if err != nil { 484 return err 485 } 486 defer vm.mutex.Unlock() 487 if vm.State != proto.StateStopped { 488 return errors.New("VM is not stopped") 489 } 490 changed := false 491 if memoryInMiB > 0 { 492 if memoryInMiB < vm.MemoryInMiB { 493 vm.MemoryInMiB = memoryInMiB 494 changed = true 495 } else if memoryInMiB > vm.MemoryInMiB { 496 m.mutex.Lock() 497 err := m.checkSufficientMemoryWithLock(memoryInMiB - vm.MemoryInMiB) 498 if err == nil { 499 vm.MemoryInMiB = memoryInMiB 500 changed = true 501 } 502 m.mutex.Unlock() 503 if err != nil { 504 return err 505 } 506 } 507 } 508 if milliCPUs > 0 { 509 if milliCPUs < vm.MilliCPUs { 510 vm.MilliCPUs = milliCPUs 511 changed = true 512 } else if milliCPUs > vm.MilliCPUs { 513 m.mutex.Lock() 514 err := m.checkSufficientCPUWithLock(milliCPUs - vm.MilliCPUs) 515 if err == nil { 516 vm.MilliCPUs = milliCPUs 517 changed = true 518 } 519 m.mutex.Unlock() 520 if err != nil { 521 return err 522 } 523 } 524 } 525 if changed { 526 vm.writeAndSendInfo() 527 } 528 return nil 529 } 530 531 func (m *Manager) changeVmTags(ipAddr net.IP, authInfo *srpc.AuthInformation, 532 tgs tags.Tags) error { 533 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 534 if err != nil { 535 return err 536 } 537 defer vm.mutex.Unlock() 538 vm.Tags = tgs 539 vm.writeAndSendInfo() 540 return nil 541 } 542 543 func (m *Manager) checkVmHasHealthAgent(ipAddr net.IP) (bool, error) { 544 vm, err := m.getVmAndLock(ipAddr, false) 545 if err != nil { 546 return false, err 547 } 548 defer vm.mutex.RUnlock() 549 if vm.State != proto.StateRunning { 550 return false, nil 551 } 552 return vm.hasHealthAgent, nil 553 } 554 555 func (m *Manager) commitImportedVm(ipAddr net.IP, 556 authInfo *srpc.AuthInformation) error { 557 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 558 if err != nil { 559 return err 560 } 561 defer vm.mutex.Unlock() 562 if !vm.Uncommitted { 563 return fmt.Errorf("%s is already committed", ipAddr) 564 } 565 if err := m.registerAddress(vm.Address); err != nil { 566 return err 567 } 568 vm.Uncommitted = false 569 vm.writeAndSendInfo() 570 return nil 571 } 572 573 func (m *Manager) connectToVmConsole(ipAddr net.IP, 574 authInfo *srpc.AuthInformation) (net.Conn, error) { 575 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 576 if err != nil { 577 return nil, err 578 } 579 defer vm.mutex.Unlock() 580 if vm.State != proto.StateRunning { 581 return nil, errors.New("VM is not running") 582 } 583 if vm.ConsoleType != proto.ConsoleVNC { 584 return nil, errors.New("VNC console is not enabled") 585 } 586 console, err := net.Dial("unix", filepath.Join(vm.dirname, "vnc")) 587 if err != nil { 588 return nil, err 589 } 590 return console, nil 591 } 592 593 func (m *Manager) connectToVmSerialPort(ipAddr net.IP, 594 authInfo *srpc.AuthInformation, 595 portNumber uint) (chan<- byte, <-chan byte, error) { 596 if portNumber > 0 { 597 return nil, nil, errors.New("only one serial port is supported") 598 } 599 input := make(chan byte, 256) 600 output := make(chan byte, 16<<10) 601 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 602 if err != nil { 603 return nil, nil, err 604 } 605 defer vm.mutex.Unlock() 606 if vm.State != proto.StateRunning { 607 return nil, nil, errors.New("VM is not running") 608 } 609 serialInput := vm.serialInput 610 if serialInput == nil { 611 return nil, nil, errors.New("no serial input device for VM") 612 } 613 if vm.serialOutput != nil { 614 return nil, nil, errors.New("VM already has a serial port connection") 615 } 616 vm.serialOutput = output 617 go func(input <-chan byte, output chan<- byte) { 618 for char := range input { 619 buffer := readData(char, input) 620 if _, err := serialInput.Write(buffer); err != nil { 621 vm.logger.Printf("error writing to serial port: %s\n", err) 622 break 623 } 624 } 625 vm.logger.Debugln(0, "input channel for console closed") 626 vm.mutex.Lock() 627 if vm.serialOutput != nil { 628 close(vm.serialOutput) 629 vm.serialOutput = nil 630 } 631 vm.mutex.Unlock() 632 }(input, output) 633 return input, output, nil 634 } 635 636 func (m *Manager) copyVm(conn *srpc.Conn, request proto.CopyVmRequest) error { 637 m.Logger.Debugf(1, "CopyVm(%s) starting\n", conn.Username()) 638 hypervisor, err := srpc.DialHTTP("tcp", request.SourceHypervisor, 0) 639 if err != nil { 640 return err 641 } 642 defer hypervisor.Close() 643 defer func() { 644 req := proto.DiscardVmAccessTokenRequest{ 645 AccessToken: request.AccessToken, 646 IpAddress: request.IpAddress} 647 var reply proto.DiscardVmAccessTokenResponse 648 hypervisor.RequestReply("Hypervisor.DiscardVmAccessToken", 649 req, &reply) 650 }() 651 getInfoRequest := proto.GetVmInfoRequest{request.IpAddress} 652 var getInfoReply proto.GetVmInfoResponse 653 err = hypervisor.RequestReply("Hypervisor.GetVmInfo", getInfoRequest, 654 &getInfoReply) 655 if err != nil { 656 return err 657 } 658 switch getInfoReply.VmInfo.State { 659 case proto.StateStopped, proto.StateRunning: 660 default: 661 return errors.New("VM is not stopped or running") 662 } 663 accessToken := request.AccessToken 664 ownerUsers := make([]string, 1, len(request.OwnerUsers)+1) 665 ownerUsers[0] = conn.Username() 666 if ownerUsers[0] == "" { 667 return errors.New("no authentication data") 668 } 669 ownerUsers = append(ownerUsers, request.OwnerUsers...) 670 vmInfo := request.VmInfo 671 vmInfo.Address = proto.Address{} 672 vmInfo.SecondaryAddresses = nil 673 vmInfo.Uncommitted = false 674 vmInfo.Volumes = getInfoReply.VmInfo.Volumes 675 vm, err := m.allocateVm(proto.CreateVmRequest{VmInfo: vmInfo}, 676 conn.GetAuthInformation()) 677 if err != nil { 678 return err 679 } 680 vm.OwnerUsers = ownerUsers 681 vm.ownerUsers = make(map[string]struct{}, len(ownerUsers)) 682 for _, username := range ownerUsers { 683 vm.ownerUsers[username] = struct{}{} 684 } 685 vm.Volumes = vmInfo.Volumes 686 if err := <-tryAllocateMemory(vmInfo.MemoryInMiB); err != nil { 687 return err 688 } 689 var secondaryVolumes []proto.Volume 690 for index, volume := range vmInfo.Volumes { 691 if index > 0 { 692 secondaryVolumes = append(secondaryVolumes, volume) 693 } 694 } 695 err = vm.setupVolumes(vmInfo.Volumes[0].Size, secondaryVolumes, 696 vmInfo.SpreadVolumes) 697 if err != nil { 698 return err 699 } 700 defer func() { // Evaluate vm at return time, not defer time. 701 if vm == nil { 702 return 703 } 704 vm.cleanup() 705 }() 706 vm.ownerUsers = make(map[string]struct{}, len(vm.OwnerUsers)) 707 for _, username := range vm.OwnerUsers { 708 vm.ownerUsers[username] = struct{}{} 709 } 710 if err := os.MkdirAll(vm.dirname, dirPerms); err != nil { 711 return err 712 } 713 // Begin copying over the volumes. 714 err = sendVmCopyMessage(conn, "initial volume(s) copy") 715 if err != nil { 716 return err 717 } 718 err = vm.migrateVmVolumes(hypervisor, request.IpAddress, accessToken) 719 if err != nil { 720 return err 721 } 722 if getInfoReply.VmInfo.State != proto.StateStopped { 723 err = sendVmCopyMessage(conn, "stopping VM") 724 if err != nil { 725 return err 726 } 727 err := hyperclient.StopVm(hypervisor, request.IpAddress, 728 request.AccessToken) 729 if err != nil { 730 return err 731 } 732 defer hyperclient.StartVm(hypervisor, request.IpAddress, accessToken) 733 err = sendVmCopyMessage(conn, "update volume(s)") 734 if err != nil { 735 return err 736 } 737 err = vm.migrateVmVolumes(hypervisor, request.IpAddress, accessToken) 738 if err != nil { 739 return err 740 } 741 } 742 err = migratevmUserData(hypervisor, 743 filepath.Join(vm.dirname, "user-data.raw"), 744 request.IpAddress, accessToken) 745 if err != nil { 746 return err 747 } 748 vm.setState(proto.StateStopped) 749 vm.destroyTimer = time.AfterFunc(time.Second*15, vm.autoDestroy) 750 response := proto.CopyVmResponse{ 751 Final: true, 752 IpAddress: vm.Address.IpAddress, 753 } 754 if err := conn.Encode(response); err != nil { 755 return err 756 } 757 vm = nil // Cancel cleanup. 758 m.Logger.Debugln(1, "CopyVm() finished") 759 return nil 760 } 761 762 func (m *Manager) createVm(conn *srpc.Conn) error { 763 764 sendError := func(conn *srpc.Conn, err error) error { 765 return conn.Encode(proto.CreateVmResponse{Error: err.Error()}) 766 } 767 768 var ipAddressToSend net.IP 769 sendUpdate := func(conn *srpc.Conn, message string) error { 770 response := proto.CreateVmResponse{ 771 IpAddress: ipAddressToSend, 772 ProgressMessage: message, 773 } 774 if err := conn.Encode(response); err != nil { 775 return err 776 } 777 return conn.Flush() 778 } 779 780 m.Logger.Debugf(1, "CreateVm(%s) starting\n", conn.Username()) 781 var request proto.CreateVmRequest 782 if err := conn.Decode(&request); err != nil { 783 return err 784 } 785 ownerUsers := make([]string, 1, len(request.OwnerUsers)+1) 786 ownerUsers[0] = conn.Username() 787 if ownerUsers[0] == "" { 788 return sendError(conn, errors.New("no authentication data")) 789 } 790 ownerUsers = append(ownerUsers, request.OwnerUsers...) 791 vm, err := m.allocateVm(request, conn.GetAuthInformation()) 792 if err != nil { 793 if err := maybeDrainAll(conn, request); err != nil { 794 return err 795 } 796 return sendError(conn, err) 797 } 798 defer func() { 799 vm.cleanup() // Evaluate vm at return time, not defer time. 800 }() 801 memoryError := tryAllocateMemory(request.MemoryInMiB) 802 vm.OwnerUsers = ownerUsers 803 vm.ownerUsers = make(map[string]struct{}, len(ownerUsers)) 804 for _, username := range ownerUsers { 805 vm.ownerUsers[username] = struct{}{} 806 } 807 if err := os.MkdirAll(vm.dirname, dirPerms); err != nil { 808 if err := maybeDrainAll(conn, request); err != nil { 809 return err 810 } 811 return sendError(conn, err) 812 } 813 if request.ImageName != "" { 814 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 815 return err 816 } 817 if err := sendUpdate(conn, "getting image"); err != nil { 818 return err 819 } 820 client, img, imageName, err := m.getImage(request.ImageName, 821 request.ImageTimeout) 822 if err != nil { 823 return sendError(conn, err) 824 } 825 defer client.Close() 826 fs := img.FileSystem 827 vm.ImageName = imageName 828 size := computeSize(request.MinimumFreeBytes, request.RoundupPower, 829 fs.EstimateUsage(0)) 830 err = vm.setupVolumes(size, request.SecondaryVolumes, 831 request.SpreadVolumes) 832 if err != nil { 833 return sendError(conn, err) 834 } 835 err = sendUpdate(conn, "unpacking image: "+imageName) 836 if err != nil { 837 return err 838 } 839 writeRawOptions := util.WriteRawOptions{ 840 InitialImageName: imageName, 841 MinimumFreeBytes: request.MinimumFreeBytes, 842 RootLabel: vm.rootLabel(), 843 RoundupPower: request.RoundupPower, 844 } 845 err = m.writeRaw(vm.VolumeLocations[0], "", client, fs, writeRawOptions, 846 request.SkipBootloader) 847 if err != nil { 848 return sendError(conn, err) 849 } 850 m.Logger.Debugln(1, "finished writing volume") 851 if fi, err := os.Stat(vm.VolumeLocations[0].Filename); err != nil { 852 return sendError(conn, err) 853 } else { 854 vm.Volumes = []proto.Volume{{Size: uint64(fi.Size())}} 855 } 856 } else if request.ImageDataSize > 0 { 857 err := vm.copyRootVolume(request, conn, request.ImageDataSize) 858 if err != nil { 859 return err 860 } 861 } else if request.ImageURL != "" { 862 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 863 return err 864 } 865 httpResponse, err := http.Get(request.ImageURL) 866 if err != nil { 867 return sendError(conn, err) 868 } 869 defer httpResponse.Body.Close() 870 if httpResponse.StatusCode != http.StatusOK { 871 return sendError(conn, errors.New(httpResponse.Status)) 872 } 873 if httpResponse.ContentLength < 0 { 874 return sendError(conn, 875 errors.New("ContentLength from: "+request.ImageURL)) 876 } 877 err = vm.copyRootVolume(request, httpResponse.Body, 878 uint64(httpResponse.ContentLength)) 879 if err != nil { 880 return sendError(conn, err) 881 } 882 } else if request.MinimumFreeBytes > 0 { // Create empty root volume. 883 err = vm.copyRootVolume(request, nil, request.MinimumFreeBytes) 884 if err != nil { 885 return sendError(conn, err) 886 } 887 } else { 888 return sendError(conn, errors.New("no image specified")) 889 } 890 if request.UserDataSize > 0 { 891 filename := filepath.Join(vm.dirname, "user-data.raw") 892 if err := copyData(filename, conn, request.UserDataSize); err != nil { 893 return sendError(conn, err) 894 } 895 } 896 if len(request.SecondaryVolumes) > 0 { 897 err := sendUpdate(conn, "creating secondary volumes") 898 if err != nil { 899 return err 900 } 901 for index, volume := range request.SecondaryVolumes { 902 fname := vm.VolumeLocations[index+1].Filename 903 var dataReader io.Reader 904 if request.SecondaryVolumesData { 905 dataReader = conn 906 } 907 if err := copyData(fname, dataReader, volume.Size); err != nil { 908 return sendError(conn, err) 909 } 910 vm.Volumes = append(vm.Volumes, volume) 911 } 912 } 913 if len(memoryError) < 1 { 914 msg := "waiting for test memory allocation" 915 sendUpdate(conn, msg) 916 vm.logger.Debugln(0, msg) 917 } 918 if err := <-memoryError; err != nil { 919 return sendError(conn, err) 920 } 921 if vm.ipAddress == "" { 922 ipAddressToSend = net.ParseIP(vm.ipAddress) 923 if err := sendUpdate(conn, "starting VM"); err != nil { 924 return err 925 } 926 } else { 927 ipAddressToSend = net.ParseIP(vm.ipAddress) 928 if err := sendUpdate(conn, "starting VM "+vm.ipAddress); err != nil { 929 return err 930 } 931 } 932 dhcpTimedOut, err := vm.startManaging(request.DhcpTimeout, 933 request.EnableNetboot, false) 934 if err != nil { 935 return sendError(conn, err) 936 } 937 vm.destroyTimer = time.AfterFunc(time.Second*15, vm.autoDestroy) 938 response := proto.CreateVmResponse{ 939 DhcpTimedOut: dhcpTimedOut, 940 Final: true, 941 IpAddress: net.ParseIP(vm.ipAddress), 942 } 943 if err := conn.Encode(response); err != nil { 944 return err 945 } 946 vm = nil // Cancel cleanup. 947 m.Logger.Debugln(1, "CreateVm() finished") 948 return nil 949 } 950 951 func (m *Manager) deleteVmVolume(ipAddr net.IP, authInfo *srpc.AuthInformation, 952 accessToken []byte, volumeIndex uint) error { 953 if volumeIndex < 1 { 954 return errors.New("cannot delete root volume") 955 } 956 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, accessToken) 957 if err != nil { 958 return err 959 } 960 defer vm.mutex.Unlock() 961 if volumeIndex >= uint(len(vm.VolumeLocations)) { 962 return errors.New("volume index too large") 963 } 964 if vm.State != proto.StateStopped { 965 return errors.New("VM is not stopped") 966 } 967 if err := os.Remove(vm.VolumeLocations[volumeIndex].Filename); err != nil { 968 return err 969 } 970 os.Remove(vm.VolumeLocations[volumeIndex].DirectoryToCleanup) 971 volumeLocations := make([]proto.LocalVolume, 0, len(vm.VolumeLocations)-1) 972 volumes := make([]proto.Volume, 0, len(vm.VolumeLocations)-1) 973 for index, volume := range vm.VolumeLocations { 974 if uint(index) != volumeIndex { 975 volumeLocations = append(volumeLocations, volume) 976 volumes = append(volumes, vm.Volumes[index]) 977 } 978 } 979 vm.VolumeLocations = volumeLocations 980 vm.Volumes = volumes 981 vm.writeAndSendInfo() 982 return nil 983 } 984 985 func (m *Manager) destroyVm(ipAddr net.IP, authInfo *srpc.AuthInformation, 986 accessToken []byte) error { 987 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, accessToken) 988 if err != nil { 989 return err 990 } 991 defer vm.mutex.Unlock() 992 switch vm.State { 993 case proto.StateStarting: 994 return errors.New("VM is starting") 995 case proto.StateRunning: 996 if vm.DestroyProtection { 997 return errors.New("cannot destroy running VM when protected") 998 } 999 vm.setState(proto.StateDestroying) 1000 vm.commandChannel <- "quit" 1001 case proto.StateStopping: 1002 return errors.New("VM is stopping") 1003 case proto.StateStopped, proto.StateFailedToStart, proto.StateMigrating, 1004 proto.StateExporting: 1005 vm.delete() 1006 case proto.StateDestroying: 1007 return errors.New("VM is already destroying") 1008 default: 1009 return errors.New("unknown state: " + vm.State.String()) 1010 } 1011 return nil 1012 } 1013 1014 func (m *Manager) discardVmAccessToken(ipAddr net.IP, 1015 authInfo *srpc.AuthInformation, accessToken []byte) error { 1016 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, accessToken) 1017 if err != nil { 1018 return err 1019 } 1020 defer vm.mutex.Unlock() 1021 for index := range vm.accessToken { // Scrub token. 1022 vm.accessToken[index] = 0 1023 } 1024 vm.accessToken = nil 1025 return nil 1026 } 1027 1028 func (m *Manager) discardVmOldImage(ipAddr net.IP, 1029 authInfo *srpc.AuthInformation) error { 1030 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 1031 if err != nil { 1032 return err 1033 } 1034 defer vm.mutex.Unlock() 1035 return os.Remove(vm.VolumeLocations[0].Filename + ".old") 1036 } 1037 1038 func (m *Manager) discardVmOldUserData(ipAddr net.IP, 1039 authInfo *srpc.AuthInformation) error { 1040 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 1041 if err != nil { 1042 return err 1043 } 1044 defer vm.mutex.Unlock() 1045 return os.Remove(filepath.Join(vm.dirname, "user-data.old")) 1046 } 1047 1048 func (m *Manager) discardVmSnapshot(ipAddr net.IP, 1049 authInfo *srpc.AuthInformation) error { 1050 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 1051 if err != nil { 1052 return err 1053 } 1054 defer vm.mutex.Unlock() 1055 return vm.discardSnapshot() 1056 } 1057 1058 func (m *Manager) exportLocalVm(authInfo *srpc.AuthInformation, 1059 request proto.ExportLocalVmRequest) (*proto.ExportLocalVmInfo, error) { 1060 if !bytes.Equal(m.rootCookie, request.VerificationCookie) { 1061 return nil, fmt.Errorf("bad verification cookie: you are not root") 1062 } 1063 vm, err := m.getVmLockAndAuth(request.IpAddress, true, authInfo, nil) 1064 if err != nil { 1065 return nil, err 1066 } 1067 defer vm.mutex.Unlock() 1068 if vm.State != proto.StateStopped { 1069 return nil, errors.New("VM is not stopped") 1070 } 1071 bridges, _, err := vm.getBridgesAndOptions(false) 1072 if err != nil { 1073 return nil, err 1074 } 1075 vm.setState(proto.StateExporting) 1076 vmInfo := proto.ExportLocalVmInfo{ 1077 Bridges: bridges, 1078 LocalVmInfo: vm.LocalVmInfo, 1079 } 1080 return &vmInfo, nil 1081 } 1082 1083 func (m *Manager) getImage(searchName string, imageTimeout time.Duration) ( 1084 *srpc.Client, *image.Image, string, error) { 1085 client, err := srpc.DialHTTP("tcp", m.ImageServerAddress, 0) 1086 if err != nil { 1087 return nil, nil, "", 1088 fmt.Errorf("error connecting to image server: %s: %s", 1089 m.ImageServerAddress, err) 1090 } 1091 doClose := true 1092 defer func() { 1093 if doClose { 1094 client.Close() 1095 } 1096 }() 1097 if isDir, err := imclient.CheckDirectory(client, searchName); err != nil { 1098 return nil, nil, "", err 1099 } else if isDir { 1100 imageName, err := imclient.FindLatestImage(client, searchName, false) 1101 if err != nil { 1102 return nil, nil, "", err 1103 } 1104 if imageName == "" { 1105 return nil, nil, "", 1106 errors.New("no images in directory: " + searchName) 1107 } 1108 img, err := imclient.GetImage(client, imageName) 1109 if err != nil { 1110 return nil, nil, "", err 1111 } 1112 img.FileSystem.RebuildInodePointers() 1113 doClose = false 1114 return client, img, imageName, nil 1115 } 1116 img, err := imclient.GetImageWithTimeout(client, searchName, imageTimeout) 1117 if err != nil { 1118 return nil, nil, "", err 1119 } 1120 if img == nil { 1121 return nil, nil, "", errors.New("timeout getting image") 1122 } 1123 if err := img.FileSystem.RebuildInodePointers(); err != nil { 1124 return nil, nil, "", err 1125 } 1126 doClose = false 1127 return client, img, searchName, nil 1128 } 1129 1130 func (m *Manager) getNumVMs() (uint, uint) { 1131 m.mutex.RLock() 1132 defer m.mutex.RUnlock() 1133 var numRunning, numStopped uint 1134 for _, vm := range m.vms { 1135 if vm.State == proto.StateRunning { 1136 numRunning++ 1137 } else { 1138 numStopped++ 1139 } 1140 } 1141 return numRunning, numStopped 1142 } 1143 1144 func (m *Manager) getVmAccessToken(ipAddr net.IP, 1145 authInfo *srpc.AuthInformation, lifetime time.Duration) ([]byte, error) { 1146 if lifetime < time.Minute { 1147 return nil, errors.New("lifetime is less than 1 minute") 1148 } 1149 if lifetime > time.Hour*24 { 1150 return nil, errors.New("lifetime is greater than 1 day") 1151 } 1152 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 1153 if err != nil { 1154 return nil, err 1155 } 1156 defer vm.mutex.Unlock() 1157 if vm.accessToken != nil { 1158 return nil, errors.New("someone else has the access token") 1159 } 1160 vm.accessToken = nil 1161 token := make([]byte, 32) 1162 if _, err := rand.Read(token); err != nil { 1163 return nil, err 1164 } 1165 vm.accessToken = token 1166 cleanupNotifier := make(chan struct{}, 1) 1167 vm.accessTokenCleanupNotifier = cleanupNotifier 1168 go func() { 1169 timer := time.NewTimer(lifetime) 1170 select { 1171 case <-timer.C: 1172 case <-cleanupNotifier: 1173 } 1174 vm.mutex.Lock() 1175 defer vm.mutex.Unlock() 1176 for index := 0; index < len(vm.accessToken); index++ { 1177 vm.accessToken[index] = 0 // Scrub sensitive data. 1178 } 1179 vm.accessToken = nil 1180 }() 1181 return token, nil 1182 } 1183 1184 func (m *Manager) getVmAndLock(ipAddr net.IP, write bool) (*vmInfoType, error) { 1185 ipStr := ipAddr.String() 1186 m.mutex.RLock() 1187 if vm := m.vms[ipStr]; vm == nil { 1188 m.mutex.RUnlock() 1189 return nil, fmt.Errorf("no VM with IP address: %s found", ipStr) 1190 } else { 1191 if write { 1192 vm.mutex.Lock() 1193 } else { 1194 vm.mutex.RLock() 1195 } 1196 m.mutex.RUnlock() 1197 return vm, nil 1198 } 1199 } 1200 1201 func (m *Manager) getVmLockAndAuth(ipAddr net.IP, write bool, 1202 authInfo *srpc.AuthInformation, accessToken []byte) (*vmInfoType, error) { 1203 vm, err := m.getVmAndLock(ipAddr, write) 1204 if err != nil { 1205 return nil, err 1206 } 1207 if err := vm.checkAuth(authInfo, accessToken); err != nil { 1208 if write { 1209 vm.mutex.Unlock() 1210 } else { 1211 vm.mutex.RUnlock() 1212 } 1213 return nil, err 1214 } 1215 return vm, nil 1216 } 1217 1218 func (m *Manager) getVmBootLog(ipAddr net.IP) (io.ReadCloser, error) { 1219 vm, err := m.getVmAndLock(ipAddr, false) 1220 if err != nil { 1221 return nil, err 1222 } 1223 filename := filepath.Join(vm.dirname, "bootlog") 1224 vm.mutex.RUnlock() 1225 return os.Open(filename) 1226 } 1227 1228 func (m *Manager) getVmInfo(ipAddr net.IP) (proto.VmInfo, error) { 1229 vm, err := m.getVmAndLock(ipAddr, false) 1230 if err != nil { 1231 return proto.VmInfo{}, err 1232 } 1233 defer vm.mutex.RUnlock() 1234 return vm.VmInfo, nil 1235 } 1236 1237 func (m *Manager) getVmUserData(ipAddr net.IP, authInfo *srpc.AuthInformation, 1238 accessToken []byte) (io.ReadCloser, uint64, error) { 1239 vm, err := m.getVmLockAndAuth(ipAddr, false, authInfo, accessToken) 1240 if err != nil { 1241 return nil, 0, err 1242 } 1243 filename := filepath.Join(vm.dirname, "user-data.raw") 1244 vm.mutex.RUnlock() 1245 if file, err := os.Open(filename); err != nil { 1246 return nil, 0, err 1247 } else if fi, err := file.Stat(); err != nil { 1248 return nil, 0, err 1249 } else { 1250 return file, uint64(fi.Size()), nil 1251 } 1252 } 1253 1254 func (m *Manager) getVmVolume(conn *srpc.Conn) error { 1255 var request proto.GetVmVolumeRequest 1256 if err := conn.Decode(&request); err != nil { 1257 return err 1258 } 1259 vm, err := m.getVmLockAndAuth(request.IpAddress, false, 1260 conn.GetAuthInformation(), request.AccessToken) 1261 if err != nil { 1262 return conn.Encode(proto.GetVmVolumeResponse{Error: err.Error()}) 1263 } 1264 defer vm.mutex.RUnlock() 1265 if request.VolumeIndex == 0 && vm.getActiveKernelPath() != "" { 1266 return conn.Encode(proto.GetVmVolumeResponse{ 1267 Error: "cannot get root volume with separate kernel"}) 1268 } 1269 if request.VolumeIndex >= uint(len(vm.VolumeLocations)) { 1270 return conn.Encode(proto.GetVmVolumeResponse{ 1271 Error: "index too large"}) 1272 } 1273 file, err := os.Open(vm.VolumeLocations[request.VolumeIndex].Filename) 1274 if err != nil { 1275 return conn.Encode(proto.GetVmVolumeResponse{Error: err.Error()}) 1276 } 1277 defer file.Close() 1278 if err := conn.Encode(proto.GetVmVolumeResponse{}); err != nil { 1279 return err 1280 } 1281 if err := conn.Flush(); err != nil { 1282 return err 1283 } 1284 return rsync.ServeBlocks(conn, conn, conn, file, 1285 vm.Volumes[request.VolumeIndex].Size) 1286 } 1287 1288 func (m *Manager) importLocalVm(authInfo *srpc.AuthInformation, 1289 request proto.ImportLocalVmRequest) error { 1290 requestedIpAddrs := make(map[string]struct{}, 1291 1+len(request.SecondaryAddresses)) 1292 requestedMacAddrs := make(map[string]struct{}, 1293 1+len(request.SecondaryAddresses)) 1294 requestedIpAddrs[request.Address.IpAddress.String()] = struct{}{} 1295 requestedMacAddrs[request.Address.MacAddress] = struct{}{} 1296 for _, addr := range request.SecondaryAddresses { 1297 ipAddr := addr.IpAddress.String() 1298 if _, ok := requestedIpAddrs[ipAddr]; ok { 1299 return fmt.Errorf("duplicate address: %s", ipAddr) 1300 } 1301 requestedIpAddrs[ipAddr] = struct{}{} 1302 if _, ok := requestedMacAddrs[addr.MacAddress]; ok { 1303 return fmt.Errorf("duplicate address: %s", addr.MacAddress) 1304 } 1305 requestedIpAddrs[addr.MacAddress] = struct{}{} 1306 } 1307 if !bytes.Equal(m.rootCookie, request.VerificationCookie) { 1308 return fmt.Errorf("bad verification cookie: you are not root") 1309 } 1310 request.VmInfo.OwnerUsers = []string{authInfo.Username} 1311 request.VmInfo.Uncommitted = true 1312 volumeDirectories := make(map[string]struct{}, len(m.volumeDirectories)) 1313 for _, dirname := range m.volumeDirectories { 1314 volumeDirectories[dirname] = struct{}{} 1315 } 1316 volumes := make([]proto.Volume, 0, len(request.VolumeFilenames)) 1317 for index, filename := range request.VolumeFilenames { 1318 dirname := filepath.Dir(filepath.Dir(filepath.Dir(filename))) 1319 if _, ok := volumeDirectories[dirname]; !ok { 1320 return fmt.Errorf("%s not in a volume directory", filename) 1321 } 1322 if fi, err := os.Lstat(filename); err != nil { 1323 return err 1324 } else if fi.Mode()&os.ModeType != 0 { 1325 return fmt.Errorf("%s is not a regular file", filename) 1326 } else { 1327 var volumeFormat proto.VolumeFormat 1328 if index < len(request.VmInfo.Volumes) { 1329 volumeFormat = request.VmInfo.Volumes[index].Format 1330 } 1331 volumes = append(volumes, proto.Volume{ 1332 Size: uint64(fi.Size()), 1333 Format: volumeFormat, 1334 }) 1335 } 1336 } 1337 request.Volumes = volumes 1338 if err := <-tryAllocateMemory(request.MemoryInMiB); err != nil { 1339 return err 1340 } 1341 ipAddress := request.Address.IpAddress.String() 1342 vm := &vmInfoType{ 1343 LocalVmInfo: proto.LocalVmInfo{ 1344 VmInfo: request.VmInfo, 1345 }, 1346 manager: m, 1347 dirname: filepath.Join(m.StateDir, "VMs", ipAddress), 1348 ipAddress: ipAddress, 1349 ownerUsers: map[string]struct{}{authInfo.Username: {}}, 1350 logger: prefixlogger.New(ipAddress+": ", m.Logger), 1351 metadataChannels: make(map[chan<- string]struct{}), 1352 } 1353 vm.VmInfo.State = proto.StateStarting 1354 m.mutex.Lock() 1355 defer m.mutex.Unlock() 1356 if _, ok := m.vms[ipAddress]; ok { 1357 return fmt.Errorf("%s already exists", ipAddress) 1358 } 1359 for _, poolAddress := range m.addressPool.Registered { 1360 ipAddr := poolAddress.IpAddress.String() 1361 if _, ok := requestedIpAddrs[ipAddr]; ok { 1362 return fmt.Errorf("%s is in address pool", ipAddr) 1363 } 1364 if _, ok := requestedMacAddrs[poolAddress.MacAddress]; ok { 1365 return fmt.Errorf("%s is in address pool", poolAddress.MacAddress) 1366 } 1367 } 1368 subnetId := m.getMatchingSubnet(request.Address.IpAddress) 1369 if subnetId == "" { 1370 return fmt.Errorf("no matching subnet for: %s\n", ipAddress) 1371 } 1372 vm.VmInfo.SubnetId = subnetId 1373 vm.VmInfo.SecondarySubnetIDs = nil 1374 for _, addr := range request.SecondaryAddresses { 1375 subnetId := m.getMatchingSubnet(addr.IpAddress) 1376 if subnetId == "" { 1377 return fmt.Errorf("no matching subnet for: %s\n", addr.IpAddress) 1378 } 1379 vm.VmInfo.SecondarySubnetIDs = append(vm.VmInfo.SecondarySubnetIDs, 1380 subnetId) 1381 } 1382 defer func() { 1383 if vm == nil { 1384 return 1385 } 1386 delete(m.vms, vm.ipAddress) 1387 m.sendVmInfo(vm.ipAddress, nil) 1388 os.RemoveAll(vm.dirname) 1389 for _, volume := range vm.VolumeLocations { 1390 os.RemoveAll(volume.DirectoryToCleanup) 1391 } 1392 }() 1393 if err := os.MkdirAll(vm.dirname, dirPerms); err != nil { 1394 return err 1395 } 1396 for index, sourceFilename := range request.VolumeFilenames { 1397 dirname := filepath.Join(filepath.Dir(filepath.Dir( 1398 filepath.Dir(sourceFilename))), 1399 ipAddress) 1400 if err := os.MkdirAll(dirname, dirPerms); err != nil { 1401 return err 1402 } 1403 var destFilename string 1404 if index == 0 { 1405 destFilename = filepath.Join(dirname, "root") 1406 } else { 1407 destFilename = filepath.Join(dirname, 1408 fmt.Sprintf("secondary-volume.%d", index-1)) 1409 } 1410 if err := os.Link(sourceFilename, destFilename); err != nil { 1411 return err 1412 } 1413 vm.VolumeLocations = append(vm.VolumeLocations, proto.LocalVolume{ 1414 dirname, destFilename}) 1415 } 1416 m.vms[ipAddress] = vm 1417 if _, err := vm.startManaging(0, false, true); err != nil { 1418 return err 1419 } 1420 vm = nil // Cancel cleanup. 1421 return nil 1422 } 1423 1424 func (m *Manager) listVMs(request proto.ListVMsRequest) []string { 1425 m.mutex.RLock() 1426 ipAddrs := make([]string, 0, len(m.vms)) 1427 for ipAddr, vm := range m.vms { 1428 if request.IgnoreStateMask&(1<<vm.State) != 0 { 1429 continue 1430 } 1431 include := true 1432 if len(request.OwnerUsers) > 0 { 1433 include = false 1434 for _, ownerUser := range request.OwnerUsers { 1435 if _, ok := vm.ownerUsers[ownerUser]; ok { 1436 include = true 1437 break 1438 } 1439 } 1440 } 1441 if include { 1442 ipAddrs = append(ipAddrs, ipAddr) 1443 } 1444 } 1445 m.mutex.RUnlock() 1446 if request.Sort { 1447 verstr.Sort(ipAddrs) 1448 } 1449 return ipAddrs 1450 } 1451 1452 func (m *Manager) migrateVm(conn *srpc.Conn) error { 1453 var request proto.MigrateVmRequest 1454 if err := conn.Decode(&request); err != nil { 1455 return err 1456 } 1457 hypervisor, err := srpc.DialHTTP("tcp", request.SourceHypervisor, 0) 1458 if err != nil { 1459 return err 1460 } 1461 defer hypervisor.Close() 1462 defer func() { 1463 req := proto.DiscardVmAccessTokenRequest{ 1464 AccessToken: request.AccessToken, 1465 IpAddress: request.IpAddress} 1466 var reply proto.DiscardVmAccessTokenResponse 1467 hypervisor.RequestReply("Hypervisor.DiscardVmAccessToken", 1468 req, &reply) 1469 }() 1470 ipAddress := request.IpAddress.String() 1471 m.mutex.RLock() 1472 _, ok := m.vms[ipAddress] 1473 subnetId := m.getMatchingSubnet(request.IpAddress) 1474 m.mutex.RUnlock() 1475 if ok { 1476 return errors.New("cannot migrate to the same hypervisor") 1477 } 1478 if subnetId == "" { 1479 return fmt.Errorf("no matching subnet for: %s\n", request.IpAddress) 1480 } 1481 getInfoRequest := proto.GetVmInfoRequest{request.IpAddress} 1482 var getInfoReply proto.GetVmInfoResponse 1483 err = hypervisor.RequestReply("Hypervisor.GetVmInfo", getInfoRequest, 1484 &getInfoReply) 1485 if err != nil { 1486 return err 1487 } 1488 accessToken := request.AccessToken 1489 vmInfo := getInfoReply.VmInfo 1490 if subnetId != vmInfo.SubnetId { 1491 return fmt.Errorf("subnet ID changing from: %s to: %s", 1492 vmInfo.SubnetId, subnetId) 1493 } 1494 if !request.IpAddress.Equal(vmInfo.Address.IpAddress) { 1495 return fmt.Errorf("inconsistent IP address: %s", 1496 vmInfo.Address.IpAddress) 1497 } 1498 if err := m.migrateVmChecks(vmInfo); err != nil { 1499 return err 1500 } 1501 volumeDirectories, err := m.getVolumeDirectories(vmInfo.Volumes[0].Size, 1502 vmInfo.Volumes[1:], vmInfo.SpreadVolumes) 1503 if err != nil { 1504 return err 1505 } 1506 vm := &vmInfoType{ 1507 LocalVmInfo: proto.LocalVmInfo{ 1508 VmInfo: vmInfo, 1509 VolumeLocations: make([]proto.LocalVolume, 0, 1510 len(volumeDirectories)), 1511 }, 1512 manager: m, 1513 dirname: filepath.Join(m.StateDir, "VMs", ipAddress), 1514 doNotWriteOrSend: true, 1515 ipAddress: ipAddress, 1516 logger: prefixlogger.New(ipAddress+": ", m.Logger), 1517 metadataChannels: make(map[chan<- string]struct{}), 1518 } 1519 vm.Uncommitted = true 1520 defer func() { // Evaluate vm at return time, not defer time. 1521 if vm == nil { 1522 return 1523 } 1524 vm.cleanup() 1525 hyperclient.PrepareVmForMigration(hypervisor, request.IpAddress, 1526 accessToken, false) 1527 if vmInfo.State == proto.StateRunning { 1528 hyperclient.StartVm(hypervisor, request.IpAddress, accessToken) 1529 } 1530 }() 1531 vm.ownerUsers = make(map[string]struct{}, len(vm.OwnerUsers)) 1532 for _, username := range vm.OwnerUsers { 1533 vm.ownerUsers[username] = struct{}{} 1534 } 1535 if err := os.MkdirAll(vm.dirname, dirPerms); err != nil { 1536 return err 1537 } 1538 for index, _dirname := range volumeDirectories { 1539 dirname := filepath.Join(_dirname, ipAddress) 1540 if err := os.MkdirAll(dirname, dirPerms); err != nil { 1541 return err 1542 } 1543 var filename string 1544 if index == 0 { 1545 filename = filepath.Join(dirname, "root") 1546 } else { 1547 filename = filepath.Join(dirname, 1548 fmt.Sprintf("secondary-volume.%d", index-1)) 1549 } 1550 vm.VolumeLocations = append(vm.VolumeLocations, proto.LocalVolume{ 1551 DirectoryToCleanup: dirname, 1552 Filename: filename, 1553 }) 1554 } 1555 if vmInfo.State == proto.StateStopped { 1556 err := hyperclient.PrepareVmForMigration(hypervisor, request.IpAddress, 1557 request.AccessToken, true) 1558 if err != nil { 1559 return err 1560 } 1561 } 1562 // Begin copying over the volumes. 1563 err = sendVmMigrationMessage(conn, "initial volume(s) copy") 1564 if err != nil { 1565 return err 1566 } 1567 err = vm.migrateVmVolumes(hypervisor, vm.Address.IpAddress, accessToken) 1568 if err != nil { 1569 return err 1570 } 1571 if vmInfo.State != proto.StateStopped { 1572 err = sendVmMigrationMessage(conn, "stopping VM") 1573 if err != nil { 1574 return err 1575 } 1576 err := hyperclient.StopVm(hypervisor, request.IpAddress, 1577 request.AccessToken) 1578 if err != nil { 1579 return err 1580 } 1581 err = hyperclient.PrepareVmForMigration(hypervisor, request.IpAddress, 1582 request.AccessToken, true) 1583 if err != nil { 1584 return err 1585 } 1586 err = sendVmMigrationMessage(conn, "update volume(s)") 1587 if err != nil { 1588 return err 1589 } 1590 err = vm.migrateVmVolumes(hypervisor, vm.Address.IpAddress, accessToken) 1591 if err != nil { 1592 return err 1593 } 1594 } 1595 err = migratevmUserData(hypervisor, 1596 filepath.Join(vm.dirname, "user-data.raw"), 1597 request.IpAddress, accessToken) 1598 if err != nil { 1599 return err 1600 } 1601 if err := sendVmMigrationMessage(conn, "starting VM"); err != nil { 1602 return err 1603 } 1604 vm.State = proto.StateStarting 1605 m.mutex.Lock() 1606 m.vms[ipAddress] = vm 1607 m.mutex.Unlock() 1608 dhcpTimedOut, err := vm.startManaging(request.DhcpTimeout, false, false) 1609 if err != nil { 1610 return err 1611 } 1612 if dhcpTimedOut { 1613 return fmt.Errorf("DHCP timed out") 1614 } 1615 err = conn.Encode(proto.MigrateVmResponse{RequestCommit: true}) 1616 if err != nil { 1617 return err 1618 } 1619 if err := conn.Flush(); err != nil { 1620 return err 1621 } 1622 var reply proto.MigrateVmResponseResponse 1623 if err := conn.Decode(&reply); err != nil { 1624 return err 1625 } 1626 if !reply.Commit { 1627 return fmt.Errorf("VM migration abandoned") 1628 } 1629 if err := m.registerAddress(vm.Address); err != nil { 1630 return err 1631 } 1632 for _, address := range vm.SecondaryAddresses { 1633 if err := m.registerAddress(address); err != nil { 1634 return err 1635 } 1636 } 1637 vm.doNotWriteOrSend = false 1638 vm.Uncommitted = false 1639 vm.writeAndSendInfo() 1640 err = hyperclient.DestroyVm(hypervisor, request.IpAddress, accessToken) 1641 if err != nil { 1642 m.Logger.Printf("error cleaning up old migrated VM: %s\n", ipAddress) 1643 } 1644 vm = nil // Cancel cleanup. 1645 return nil 1646 } 1647 1648 func sendVmCopyMessage(conn *srpc.Conn, message string) error { 1649 request := proto.CopyVmResponse{ProgressMessage: message} 1650 if err := conn.Encode(request); err != nil { 1651 return err 1652 } 1653 return conn.Flush() 1654 } 1655 1656 func sendVmMigrationMessage(conn *srpc.Conn, message string) error { 1657 request := proto.MigrateVmResponse{ProgressMessage: message} 1658 if err := conn.Encode(request); err != nil { 1659 return err 1660 } 1661 return conn.Flush() 1662 } 1663 1664 func sendVmPatchImageMessage(conn *srpc.Conn, message string) error { 1665 request := proto.PatchVmImageResponse{ProgressMessage: message} 1666 if err := conn.Encode(request); err != nil { 1667 return err 1668 } 1669 return conn.Flush() 1670 } 1671 1672 func (m *Manager) migrateVmChecks(vmInfo proto.VmInfo) error { 1673 switch vmInfo.State { 1674 case proto.StateStopped: 1675 case proto.StateRunning: 1676 default: 1677 return fmt.Errorf("VM state: %s is not stopped/running", vmInfo.State) 1678 } 1679 m.mutex.RLock() 1680 defer m.mutex.RUnlock() 1681 for index, address := range vmInfo.SecondaryAddresses { 1682 subnetId := m.getMatchingSubnet(address.IpAddress) 1683 if subnetId == "" { 1684 return fmt.Errorf("no matching subnet for: %s\n", address.IpAddress) 1685 } 1686 if subnetId != vmInfo.SecondarySubnetIDs[index] { 1687 return fmt.Errorf("subnet ID changing from: %s to: %s", 1688 vmInfo.SecondarySubnetIDs[index], subnetId) 1689 } 1690 } 1691 if err := m.checkSufficientCPUWithLock(vmInfo.MilliCPUs); err != nil { 1692 return err 1693 } 1694 if err := m.checkSufficientMemoryWithLock(vmInfo.MemoryInMiB); err != nil { 1695 return err 1696 } 1697 if err := <-tryAllocateMemory(vmInfo.MemoryInMiB); err != nil { 1698 return err 1699 } 1700 return nil 1701 } 1702 1703 func migratevmUserData(hypervisor *srpc.Client, filename string, 1704 ipAddr net.IP, accessToken []byte) error { 1705 conn, err := hypervisor.Call("Hypervisor.GetVmUserData") 1706 if err != nil { 1707 return err 1708 } 1709 defer conn.Close() 1710 request := proto.GetVmUserDataRequest{ 1711 AccessToken: accessToken, 1712 IpAddress: ipAddr, 1713 } 1714 if err := conn.Encode(request); err != nil { 1715 return fmt.Errorf("error encoding request: %s", err) 1716 } 1717 if err := conn.Flush(); err != nil { 1718 return err 1719 } 1720 var reply proto.GetVmUserDataResponse 1721 if err := conn.Decode(&reply); err != nil { 1722 return err 1723 } 1724 if err := errors.New(reply.Error); err != nil { 1725 return err 1726 } 1727 if reply.Length < 1 { 1728 return nil 1729 } 1730 writer, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 1731 privateFilePerms) 1732 if err != nil { 1733 io.CopyN(ioutil.Discard, conn, int64(reply.Length)) 1734 return err 1735 } 1736 defer writer.Close() 1737 if _, err := io.CopyN(writer, conn, int64(reply.Length)); err != nil { 1738 return err 1739 } 1740 return nil 1741 } 1742 1743 func (vm *vmInfoType) migrateVmVolumes(hypervisor *srpc.Client, 1744 sourceIpAddr net.IP, accessToken []byte) error { 1745 for index, volume := range vm.VolumeLocations { 1746 _, err := migrateVmVolume(hypervisor, volume.Filename, uint(index), 1747 vm.Volumes[index].Size, sourceIpAddr, accessToken) 1748 if err != nil { 1749 return err 1750 } 1751 } 1752 return nil 1753 } 1754 1755 func migrateVmVolume(hypervisor *srpc.Client, filename string, 1756 volumeIndex uint, size uint64, ipAddr net.IP, accessToken []byte) ( 1757 *rsync.Stats, error) { 1758 var initialFileSize uint64 1759 reader, err := os.OpenFile(filename, os.O_RDONLY, 0) 1760 if err != nil { 1761 if !os.IsNotExist(err) { 1762 return nil, err 1763 } 1764 } else { 1765 defer reader.Close() 1766 if fi, err := reader.Stat(); err != nil { 1767 return nil, err 1768 } else { 1769 initialFileSize = uint64(fi.Size()) 1770 if initialFileSize > size { 1771 return nil, errors.New("file larger than volume") 1772 } 1773 } 1774 } 1775 writer, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 1776 privateFilePerms) 1777 if err != nil { 1778 return nil, err 1779 } 1780 defer writer.Close() 1781 request := proto.GetVmVolumeRequest{ 1782 AccessToken: accessToken, 1783 IpAddress: ipAddr, 1784 VolumeIndex: volumeIndex, 1785 } 1786 conn, err := hypervisor.Call("Hypervisor.GetVmVolume") 1787 if err != nil { 1788 if reader == nil { 1789 os.Remove(filename) 1790 } 1791 return nil, err 1792 } 1793 defer conn.Close() 1794 if err := conn.Encode(request); err != nil { 1795 return nil, fmt.Errorf("error encoding request: %s", err) 1796 } 1797 if err := conn.Flush(); err != nil { 1798 return nil, err 1799 } 1800 var response proto.GetVmVolumeResponse 1801 if err := conn.Decode(&response); err != nil { 1802 return nil, err 1803 } 1804 if err := errors.New(response.Error); err != nil { 1805 return nil, err 1806 } 1807 stats, err := rsync.GetBlocks(conn, conn, conn, reader, writer, size, 1808 initialFileSize) 1809 return &stats, err 1810 } 1811 1812 func (m *Manager) notifyVmMetadataRequest(ipAddr net.IP, path string) { 1813 addr := ipAddr.String() 1814 m.mutex.RLock() 1815 vm, ok := m.vms[addr] 1816 m.mutex.RUnlock() 1817 if !ok { 1818 return 1819 } 1820 vm.mutex.Lock() 1821 defer vm.mutex.Unlock() 1822 for ch := range vm.metadataChannels { 1823 select { 1824 case ch <- path: 1825 default: 1826 } 1827 } 1828 } 1829 1830 func (m *Manager) patchVmImage(conn *srpc.Conn, 1831 request proto.PatchVmImageRequest) error { 1832 client, img, imageName, err := m.getImage(request.ImageName, 1833 request.ImageTimeout) 1834 if err != nil { 1835 return nil 1836 } 1837 if img.Filter == nil { 1838 return fmt.Errorf("%s contains no filter", imageName) 1839 } 1840 img.FileSystem.InodeToFilenamesTable() 1841 img.FileSystem.FilenameToInodeTable() 1842 hashToInodesTable := img.FileSystem.HashToInodesTable() 1843 img.FileSystem.BuildEntryMap() 1844 var objectsGetter objectserver.ObjectsGetter 1845 vm, err := m.getVmLockAndAuth(request.IpAddress, true, 1846 conn.GetAuthInformation(), nil) 1847 if err != nil { 1848 return err 1849 } 1850 restart := vm.State == proto.StateRunning 1851 defer func() { 1852 vm.updating = false 1853 vm.mutex.Unlock() 1854 }() 1855 if m.objectCache == nil { 1856 objectClient := objclient.AttachObjectClient(client) 1857 defer objectClient.Close() 1858 objectsGetter = objectClient 1859 } else if restart { 1860 hashes := make([]hash.Hash, 0, len(hashToInodesTable)) 1861 for hashVal := range hashToInodesTable { 1862 hashes = append(hashes, hashVal) 1863 } 1864 if err := sendVmPatchImageMessage(conn, "prefetching"); err != nil { 1865 return err 1866 } 1867 if err := m.objectCache.FetchObjects(hashes); err != nil { 1868 return err 1869 } 1870 objectsGetter = m.objectCache 1871 } else { 1872 objectsGetter = m.objectCache 1873 } 1874 switch vm.State { 1875 case proto.StateStopped: 1876 case proto.StateRunning: 1877 if len(vm.Address.IpAddress) < 1 { 1878 return errors.New("cannot stop VM with externally managed lease") 1879 } 1880 default: 1881 return errors.New("VM is not running or stopped") 1882 } 1883 if vm.updating { 1884 return errors.New("cannot update already updating VM") 1885 } 1886 vm.updating = true 1887 bootInfo, err := util.GetBootInfo(img.FileSystem, vm.rootLabel(), 1888 "net.ifnames=0") 1889 if err != nil { 1890 return err 1891 } 1892 if vm.State == proto.StateRunning { 1893 if err := sendVmPatchImageMessage(conn, "stopping VM"); err != nil { 1894 return err 1895 } 1896 stoppedNotifier := make(chan struct{}, 1) 1897 vm.stoppedNotifier = stoppedNotifier 1898 vm.setState(proto.StateStopping) 1899 vm.commandChannel <- "system_powerdown" 1900 time.AfterFunc(time.Second*15, vm.kill) 1901 vm.mutex.Unlock() 1902 <-stoppedNotifier 1903 vm.mutex.Lock() 1904 if vm.State != proto.StateStopped { 1905 restart = false 1906 return errors.New("VM is not stopped after stop attempt") 1907 } 1908 } 1909 rootFilename := vm.VolumeLocations[0].Filename 1910 tmpRootFilename := rootFilename + ".new" 1911 if err := sendVmPatchImageMessage(conn, "copying root"); err != nil { 1912 return err 1913 } 1914 err = fsutil.CopyFile(tmpRootFilename, rootFilename, 1915 fsutil.PrivateFilePerms) 1916 if err != nil { 1917 return err 1918 } 1919 defer os.Remove(tmpRootFilename) 1920 rootDir, err := ioutil.TempDir(vm.dirname, "root") 1921 if err != nil { 1922 return err 1923 } 1924 defer os.Remove(rootDir) 1925 loopDevice, err := fsutil.LoopbackSetup(tmpRootFilename) 1926 if err != nil { 1927 return err 1928 } 1929 defer fsutil.LoopbackDelete(loopDevice) 1930 vm.logger.Debugf(0, "mounting: %s onto: %s\n", loopDevice, rootDir) 1931 err = wsyscall.Mount(loopDevice+"p1", rootDir, "ext4", 0, "") 1932 if err != nil { 1933 return err 1934 } 1935 defer syscall.Unmount(rootDir, 0) 1936 if err := sendVmPatchImageMessage(conn, "scanning root"); err != nil { 1937 return err 1938 } 1939 fs, err := scanner.ScanFileSystem(rootDir, nil, img.Filter, nil, nil, nil) 1940 if err != nil { 1941 return err 1942 } 1943 if err := fs.FileSystem.RebuildInodePointers(); err != nil { 1944 return err 1945 } 1946 fs.FileSystem.BuildEntryMap() 1947 initrdFilename := vm.getInitrdPath() 1948 tmpInitrdFilename := initrdFilename + ".new" 1949 defer os.Remove(tmpInitrdFilename) 1950 kernelFilename := vm.getKernelPath() 1951 tmpKernelFilename := kernelFilename + ".new" 1952 defer os.Remove(tmpKernelFilename) 1953 writeBootloaderConfig := false 1954 if _, err := os.Stat(vm.getKernelPath()); err == nil { // No bootloader. 1955 err := extractKernel(vm.VolumeLocations[0], ".new", objectsGetter, 1956 img.FileSystem, bootInfo) 1957 if err != nil { 1958 return err 1959 } 1960 } else { // Requires a bootloader. 1961 writeBootloaderConfig = true 1962 } 1963 subObj := domlib.Sub{FileSystem: &fs.FileSystem} 1964 fetchMap, _ := domlib.BuildMissingLists(subObj, img, false, true, 1965 vm.logger) 1966 objectsToFetch := objectcache.ObjectMapToCache(fetchMap) 1967 objectsDir := filepath.Join(rootDir, ".subd", "objects") 1968 defer os.RemoveAll(objectsDir) 1969 startTime := time.Now() 1970 objectsReader, err := objectsGetter.GetObjects(objectsToFetch) 1971 if err != nil { 1972 return err 1973 } 1974 defer objectsReader.Close() 1975 err = sendVmPatchImageMessage(conn, "pre-deleting unneeded files") 1976 if err != nil { 1977 return err 1978 } 1979 err = deleteFilesNotInImage(img.FileSystem, &fs.FileSystem, rootDir, 1980 vm.logger) 1981 if err != nil { 1982 return err 1983 } 1984 msg := fmt.Sprintf("fetching(%s) %d objects", 1985 imageName, len(objectsToFetch)) 1986 if err := sendVmPatchImageMessage(conn, msg); err != nil { 1987 return err 1988 } 1989 vm.logger.Debugln(0, msg) 1990 for _, hashVal := range objectsToFetch { 1991 length, reader, err := objectsReader.NextObject() 1992 if err != nil { 1993 vm.logger.Println(err) 1994 return err 1995 } 1996 err = readOne(objectsDir, hashVal, length, reader) 1997 reader.Close() 1998 if err != nil { 1999 vm.logger.Println(err) 2000 return err 2001 } 2002 } 2003 msg = fmt.Sprintf("fetched(%s) %d objects in %s", 2004 imageName, len(objectsToFetch), format.Duration(time.Since(startTime))) 2005 if err := sendVmPatchImageMessage(conn, msg); err != nil { 2006 return err 2007 } 2008 vm.logger.Debugln(0, msg) 2009 subObj.ObjectCache = append(subObj.ObjectCache, objectsToFetch...) 2010 var subRequest subproto.UpdateRequest 2011 if domlib.BuildUpdateRequest(subObj, img, &subRequest, false, true, 2012 vm.logger) { 2013 return errors.New("failed building update: missing computed files") 2014 } 2015 subRequest.ImageName = imageName 2016 subRequest.Triggers = nil 2017 if err := sendVmPatchImageMessage(conn, "starting update"); err != nil { 2018 return err 2019 } 2020 vm.logger.Debugf(0, "update(%s) starting\n", imageName) 2021 startTime = time.Now() 2022 _, _, err = sublib.Update(subRequest, rootDir, objectsDir, nil, nil, nil, 2023 vm.logger) 2024 if err != nil { 2025 return err 2026 } 2027 if writeBootloaderConfig { 2028 err := bootInfo.WriteBootloaderConfig(rootDir, vm.logger) 2029 if err != nil { 2030 return err 2031 } 2032 } 2033 oldRootFilename := rootFilename + ".old" 2034 if err := os.Rename(rootFilename, oldRootFilename); err != nil { 2035 return err 2036 } 2037 if err := os.Rename(tmpRootFilename, rootFilename); err != nil { 2038 os.Rename(oldRootFilename, rootFilename) 2039 return err 2040 } 2041 os.Rename(initrdFilename, initrdFilename+".old") 2042 os.Rename(tmpInitrdFilename, initrdFilename) 2043 os.Rename(kernelFilename, kernelFilename+".old") 2044 os.Rename(tmpKernelFilename, kernelFilename) 2045 vm.ImageName = imageName 2046 vm.writeAndSendInfo() 2047 if restart && vm.State == proto.StateStopped { 2048 vm.setState(proto.StateStarting) 2049 sendVmPatchImageMessage(conn, "starting VM") 2050 vm.mutex.Unlock() 2051 _, err := vm.startManaging(-1, false, false) 2052 vm.mutex.Lock() 2053 if err != nil { 2054 return err 2055 } 2056 } 2057 return nil 2058 } 2059 2060 func (m *Manager) prepareVmForMigration(ipAddr net.IP, 2061 authInfoP *srpc.AuthInformation, accessToken []byte, enable bool) error { 2062 authInfo := *authInfoP 2063 authInfo.HaveMethodAccess = false // Require VM ownership or token. 2064 vm, err := m.getVmLockAndAuth(ipAddr, true, &authInfo, accessToken) 2065 if err != nil { 2066 return nil 2067 } 2068 defer vm.mutex.Unlock() 2069 if enable { 2070 if vm.Uncommitted { 2071 return errors.New("VM is uncommitted") 2072 } 2073 if vm.State != proto.StateStopped { 2074 return errors.New("VM is not stopped") 2075 } 2076 // Block reallocation of addresses until VM is destroyed, then release 2077 // claims on addresses. 2078 vm.Uncommitted = true 2079 vm.setState(proto.StateMigrating) 2080 if err := m.unregisterAddress(vm.Address, true); err != nil { 2081 vm.Uncommitted = false 2082 vm.setState(proto.StateStopped) 2083 return err 2084 } 2085 for _, address := range vm.SecondaryAddresses { 2086 if err := m.unregisterAddress(address, true); err != nil { 2087 vm.logger.Printf("error unregistering address: %s\n", 2088 address.IpAddress) 2089 vm.Uncommitted = false 2090 vm.setState(proto.StateStopped) 2091 return err 2092 } 2093 } 2094 } else { 2095 if vm.State != proto.StateMigrating { 2096 return errors.New("VM is not migrating") 2097 } 2098 // Reclaim addresses and then allow reallocation if VM is later 2099 // destroyed. 2100 if err := m.registerAddress(vm.Address); err != nil { 2101 vm.setState(proto.StateStopped) 2102 return err 2103 } 2104 for _, address := range vm.SecondaryAddresses { 2105 if err := m.registerAddress(address); err != nil { 2106 vm.logger.Printf("error registering address: %s\n", 2107 address.IpAddress) 2108 vm.setState(proto.StateStopped) 2109 return err 2110 } 2111 } 2112 vm.Uncommitted = false 2113 vm.setState(proto.StateStopped) 2114 } 2115 return nil 2116 } 2117 2118 func (m *Manager) registerVmMetadataNotifier(ipAddr net.IP, 2119 authInfo *srpc.AuthInformation, pathChannel chan<- string) error { 2120 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2121 if err != nil { 2122 return err 2123 } 2124 defer vm.mutex.Unlock() 2125 vm.metadataChannels[pathChannel] = struct{}{} 2126 return nil 2127 } 2128 2129 func (m *Manager) replaceVmImage(conn *srpc.Conn, 2130 authInfo *srpc.AuthInformation) error { 2131 2132 sendError := func(conn *srpc.Conn, err error) error { 2133 return conn.Encode(proto.ReplaceVmImageResponse{Error: err.Error()}) 2134 } 2135 2136 sendUpdate := func(conn *srpc.Conn, message string) error { 2137 response := proto.ReplaceVmImageResponse{ProgressMessage: message} 2138 if err := conn.Encode(response); err != nil { 2139 return err 2140 } 2141 return conn.Flush() 2142 } 2143 2144 var request proto.ReplaceVmImageRequest 2145 if err := conn.Decode(&request); err != nil { 2146 return err 2147 } 2148 vm, err := m.getVmLockAndAuth(request.IpAddress, true, authInfo, nil) 2149 if err != nil { 2150 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 2151 return err 2152 } 2153 return sendError(conn, err) 2154 } 2155 restart := vm.State == proto.StateRunning 2156 defer func() { 2157 vm.updating = false 2158 vm.mutex.Unlock() 2159 }() 2160 switch vm.State { 2161 case proto.StateStopped: 2162 case proto.StateRunning: 2163 if len(vm.Address.IpAddress) < 1 { 2164 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 2165 return err 2166 } 2167 return errors.New("cannot stop VM with externally managed lease") 2168 } 2169 default: 2170 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 2171 return err 2172 } 2173 return sendError(conn, errors.New("VM is not running or stopped")) 2174 } 2175 if vm.updating { 2176 return errors.New("cannot update already updating VM") 2177 } 2178 vm.updating = true 2179 initrdFilename := vm.getInitrdPath() 2180 tmpInitrdFilename := initrdFilename + ".new" 2181 defer os.Remove(tmpInitrdFilename) 2182 kernelFilename := vm.getKernelPath() 2183 tmpKernelFilename := kernelFilename + ".new" 2184 defer os.Remove(tmpKernelFilename) 2185 tmpRootFilename := vm.VolumeLocations[0].Filename + ".new" 2186 defer os.Remove(tmpRootFilename) 2187 var newSize uint64 2188 if request.ImageName != "" { 2189 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 2190 return err 2191 } 2192 if err := sendUpdate(conn, "getting image"); err != nil { 2193 return err 2194 } 2195 client, img, imageName, err := m.getImage(request.ImageName, 2196 request.ImageTimeout) 2197 if err != nil { 2198 return sendError(conn, err) 2199 } 2200 defer client.Close() 2201 request.ImageName = imageName 2202 err = sendUpdate(conn, "unpacking image: "+imageName) 2203 if err != nil { 2204 return err 2205 } 2206 writeRawOptions := util.WriteRawOptions{ 2207 InitialImageName: imageName, 2208 MinimumFreeBytes: request.MinimumFreeBytes, 2209 RootLabel: vm.rootLabel(), 2210 RoundupPower: request.RoundupPower, 2211 } 2212 err = m.writeRaw(vm.VolumeLocations[0], ".new", client, img.FileSystem, 2213 writeRawOptions, request.SkipBootloader) 2214 if err != nil { 2215 return sendError(conn, err) 2216 } 2217 if fi, err := os.Stat(tmpRootFilename); err != nil { 2218 return sendError(conn, err) 2219 } else { 2220 newSize = uint64(fi.Size()) 2221 } 2222 } else if request.ImageDataSize > 0 { 2223 err := copyData(tmpRootFilename, conn, request.ImageDataSize) 2224 if err != nil { 2225 return err 2226 } 2227 newSize = computeSize(request.MinimumFreeBytes, request.RoundupPower, 2228 request.ImageDataSize) 2229 if err := setVolumeSize(tmpRootFilename, newSize); err != nil { 2230 return sendError(conn, err) 2231 } 2232 } else if request.ImageURL != "" { 2233 if err := maybeDrainImage(conn, request.ImageDataSize); err != nil { 2234 return err 2235 } 2236 httpResponse, err := http.Get(request.ImageURL) 2237 if err != nil { 2238 return sendError(conn, err) 2239 } 2240 defer httpResponse.Body.Close() 2241 if httpResponse.StatusCode != http.StatusOK { 2242 return sendError(conn, errors.New(httpResponse.Status)) 2243 } 2244 if httpResponse.ContentLength < 0 { 2245 return sendError(conn, 2246 errors.New("ContentLength from: "+request.ImageURL)) 2247 } 2248 err = copyData(tmpRootFilename, httpResponse.Body, 2249 uint64(httpResponse.ContentLength)) 2250 if err != nil { 2251 return sendError(conn, err) 2252 } 2253 newSize = computeSize(request.MinimumFreeBytes, request.RoundupPower, 2254 uint64(httpResponse.ContentLength)) 2255 if err := setVolumeSize(tmpRootFilename, newSize); err != nil { 2256 return sendError(conn, err) 2257 } 2258 } else { 2259 return sendError(conn, errors.New("no image specified")) 2260 } 2261 if vm.State == proto.StateRunning { 2262 if err := sendUpdate(conn, "stopping VM"); err != nil { 2263 return err 2264 } 2265 stoppedNotifier := make(chan struct{}, 1) 2266 vm.stoppedNotifier = stoppedNotifier 2267 vm.setState(proto.StateStopping) 2268 vm.commandChannel <- "system_powerdown" 2269 time.AfterFunc(time.Second*15, vm.kill) 2270 vm.mutex.Unlock() 2271 <-stoppedNotifier 2272 vm.mutex.Lock() 2273 if vm.State != proto.StateStopped { 2274 restart = false 2275 return sendError(conn, 2276 errors.New("VM is not stopped after stop attempt")) 2277 } 2278 } 2279 rootFilename := vm.VolumeLocations[0].Filename 2280 oldRootFilename := vm.VolumeLocations[0].Filename + ".old" 2281 if err := os.Rename(rootFilename, oldRootFilename); err != nil { 2282 return sendError(conn, err) 2283 } 2284 if err := os.Rename(tmpRootFilename, rootFilename); err != nil { 2285 os.Rename(oldRootFilename, rootFilename) 2286 return sendError(conn, err) 2287 } 2288 os.Rename(initrdFilename, initrdFilename+".old") 2289 os.Rename(tmpInitrdFilename, initrdFilename) 2290 os.Rename(kernelFilename, kernelFilename+".old") 2291 os.Rename(tmpKernelFilename, kernelFilename) 2292 if request.ImageName != "" { 2293 vm.ImageName = request.ImageName 2294 } 2295 vm.Volumes[0].Size = newSize 2296 vm.writeAndSendInfo() 2297 if restart && vm.State == proto.StateStopped { 2298 vm.setState(proto.StateStarting) 2299 sendUpdate(conn, "starting VM") 2300 vm.mutex.Unlock() 2301 _, err := vm.startManaging(-1, false, false) 2302 vm.mutex.Lock() 2303 if err != nil { 2304 sendError(conn, err) 2305 } 2306 } 2307 response := proto.ReplaceVmImageResponse{ 2308 Final: true, 2309 } 2310 if err := conn.Encode(response); err != nil { 2311 return err 2312 } 2313 return nil 2314 } 2315 2316 func (m *Manager) replaceVmUserData(ipAddr net.IP, reader io.Reader, 2317 size uint64, authInfo *srpc.AuthInformation) error { 2318 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2319 if err != nil { 2320 return err 2321 } 2322 defer vm.mutex.Unlock() 2323 filename := filepath.Join(vm.dirname, "user-data.raw") 2324 oldFilename := filename + ".old" 2325 newFilename := filename + ".new" 2326 err = fsutil.CopyToFile(newFilename, privateFilePerms, reader, size) 2327 if err != nil { 2328 return err 2329 } 2330 defer os.Remove(newFilename) 2331 if err := os.Rename(filename, oldFilename); err != nil { 2332 return err 2333 } 2334 if err := os.Rename(newFilename, filename); err != nil { 2335 os.Rename(oldFilename, filename) 2336 return err 2337 } 2338 return nil 2339 } 2340 2341 func (m *Manager) restoreVmFromSnapshot(ipAddr net.IP, 2342 authInfo *srpc.AuthInformation, forceIfNotStopped bool) error { 2343 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2344 if err != nil { 2345 return err 2346 } 2347 defer vm.mutex.Unlock() 2348 if vm.State != proto.StateStopped { 2349 if !forceIfNotStopped { 2350 return errors.New("VM is not stopped") 2351 } 2352 } 2353 for _, volume := range vm.VolumeLocations { 2354 snapshotFilename := volume.Filename + ".snapshot" 2355 if err := os.Rename(snapshotFilename, volume.Filename); err != nil { 2356 if !os.IsNotExist(err) { 2357 return err 2358 } 2359 } 2360 } 2361 return nil 2362 } 2363 2364 func (m *Manager) restoreVmImage(ipAddr net.IP, 2365 authInfo *srpc.AuthInformation) error { 2366 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2367 if err != nil { 2368 return err 2369 } 2370 defer vm.mutex.Unlock() 2371 if vm.State != proto.StateStopped { 2372 return errors.New("VM is not stopped") 2373 } 2374 rootFilename := vm.VolumeLocations[0].Filename 2375 oldRootFilename := vm.VolumeLocations[0].Filename + ".old" 2376 fi, err := os.Stat(oldRootFilename) 2377 if err != nil { 2378 return err 2379 } 2380 if err := os.Rename(oldRootFilename, rootFilename); err != nil { 2381 return err 2382 } 2383 initrdFilename := vm.getInitrdPath() 2384 os.Rename(initrdFilename+".old", initrdFilename) 2385 kernelFilename := vm.getKernelPath() 2386 os.Rename(kernelFilename+".old", kernelFilename) 2387 vm.Volumes[0].Size = uint64(fi.Size()) 2388 vm.writeAndSendInfo() 2389 return nil 2390 } 2391 2392 func (m *Manager) restoreVmUserData(ipAddr net.IP, 2393 authInfo *srpc.AuthInformation) error { 2394 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2395 if err != nil { 2396 return err 2397 } 2398 defer vm.mutex.Unlock() 2399 filename := filepath.Join(vm.dirname, "user-data.raw") 2400 oldFilename := filename + ".old" 2401 return os.Rename(oldFilename, filename) 2402 } 2403 2404 func (m *Manager) scanVmRoot(ipAddr net.IP, authInfo *srpc.AuthInformation, 2405 scanFilter *filter.Filter) (*filesystem.FileSystem, error) { 2406 vm, err := m.getVmLockAndAuth(ipAddr, false, authInfo, nil) 2407 if err != nil { 2408 return nil, err 2409 } 2410 defer vm.mutex.RUnlock() 2411 return vm.scanVmRoot(scanFilter) 2412 } 2413 2414 func (vm *vmInfoType) scanVmRoot(scanFilter *filter.Filter) ( 2415 *filesystem.FileSystem, error) { 2416 if vm.State != proto.StateStopped { 2417 return nil, errors.New("VM is not stopped") 2418 } 2419 rootDir, err := ioutil.TempDir(vm.dirname, "root") 2420 if err != nil { 2421 return nil, err 2422 } 2423 defer os.Remove(rootDir) 2424 loopDevice, err := fsutil.LoopbackSetup(vm.VolumeLocations[0].Filename) 2425 if err != nil { 2426 return nil, err 2427 } 2428 defer fsutil.LoopbackDelete(loopDevice) 2429 blockDevice := loopDevice + "p1" 2430 vm.logger.Debugf(0, "mounting: %s onto: %s\n", blockDevice, rootDir) 2431 err = wsyscall.Mount(blockDevice, rootDir, "ext4", 0, "") 2432 if err != nil { 2433 return nil, fmt.Errorf("error mounting: %s: %s", blockDevice, err) 2434 } 2435 defer syscall.Unmount(rootDir, 0) 2436 sfs, err := scanner.ScanFileSystem(rootDir, nil, scanFilter, nil, nil, nil) 2437 if err != nil { 2438 return nil, err 2439 } 2440 return &sfs.FileSystem, nil 2441 } 2442 2443 func (m *Manager) sendVmInfo(ipAddress string, vm *proto.VmInfo) { 2444 if ipAddress != "0.0.0.0" { 2445 if vm == nil { // GOB cannot encode a nil value in a map. 2446 vm = new(proto.VmInfo) 2447 } 2448 m.sendUpdateWithLock(proto.Update{ 2449 HaveVMs: true, 2450 VMs: map[string]*proto.VmInfo{ipAddress: vm}, 2451 }) 2452 } 2453 } 2454 2455 func (m *Manager) snapshotVm(ipAddr net.IP, authInfo *srpc.AuthInformation, 2456 forceIfNotStopped, snapshotRootOnly bool) error { 2457 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, nil) 2458 if err != nil { 2459 return err 2460 } 2461 defer vm.mutex.Unlock() 2462 // TODO(rgooch): First check for sufficient free space. 2463 if vm.State != proto.StateStopped { 2464 if !forceIfNotStopped { 2465 return errors.New("VM is not stopped") 2466 } 2467 } 2468 if err := vm.discardSnapshot(); err != nil { 2469 return err 2470 } 2471 doCleanup := true 2472 defer func() { 2473 if doCleanup { 2474 vm.discardSnapshot() 2475 } 2476 }() 2477 for index, volume := range vm.VolumeLocations { 2478 snapshotFilename := volume.Filename + ".snapshot" 2479 if index == 0 || !snapshotRootOnly { 2480 err := fsutil.CopyFile(snapshotFilename, volume.Filename, 2481 privateFilePerms) 2482 if err != nil { 2483 return err 2484 } 2485 } 2486 } 2487 doCleanup = false 2488 return nil 2489 } 2490 2491 func (m *Manager) startVm(ipAddr net.IP, authInfo *srpc.AuthInformation, 2492 accessToken []byte, dhcpTimeout time.Duration) (bool, error) { 2493 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, accessToken) 2494 if err != nil { 2495 return false, err 2496 } 2497 doUnlock := true 2498 defer func() { 2499 if doUnlock { 2500 vm.mutex.Unlock() 2501 } 2502 }() 2503 if err := checkAvailableMemory(vm.MemoryInMiB); err != nil { 2504 return false, err 2505 } 2506 switch vm.State { 2507 case proto.StateStarting: 2508 return false, errors.New("VM is already starting") 2509 case proto.StateRunning: 2510 return false, errors.New("VM is running") 2511 case proto.StateStopping: 2512 return false, errors.New("VM is stopping") 2513 case proto.StateStopped, proto.StateFailedToStart, proto.StateExporting: 2514 vm.setState(proto.StateStarting) 2515 vm.mutex.Unlock() 2516 doUnlock = false 2517 return vm.startManaging(dhcpTimeout, false, false) 2518 case proto.StateDestroying: 2519 return false, errors.New("VM is destroying") 2520 case proto.StateMigrating: 2521 return false, errors.New("VM is migrating") 2522 default: 2523 return false, errors.New("unknown state: " + vm.State.String()) 2524 } 2525 } 2526 2527 func (m *Manager) stopVm(ipAddr net.IP, authInfo *srpc.AuthInformation, 2528 accessToken []byte) error { 2529 vm, err := m.getVmLockAndAuth(ipAddr, true, authInfo, accessToken) 2530 if err != nil { 2531 return err 2532 } 2533 doUnlock := true 2534 defer func() { 2535 if doUnlock { 2536 vm.mutex.Unlock() 2537 } 2538 }() 2539 switch vm.State { 2540 case proto.StateStarting: 2541 return errors.New("VM is starting") 2542 case proto.StateRunning: 2543 if len(vm.Address.IpAddress) < 1 { 2544 return errors.New("cannot stop VM with externally managed lease") 2545 } 2546 stoppedNotifier := make(chan struct{}, 1) 2547 vm.stoppedNotifier = stoppedNotifier 2548 vm.setState(proto.StateStopping) 2549 vm.commandChannel <- "system_powerdown" 2550 time.AfterFunc(time.Second*15, vm.kill) 2551 vm.mutex.Unlock() 2552 doUnlock = false 2553 <-stoppedNotifier 2554 case proto.StateFailedToStart: 2555 vm.setState(proto.StateStopped) 2556 case proto.StateStopping: 2557 return errors.New("VM is stopping") 2558 case proto.StateStopped: 2559 return errors.New("VM is already stopped") 2560 case proto.StateDestroying: 2561 return errors.New("VM is destroying") 2562 case proto.StateMigrating: 2563 return errors.New("VM is migrating") 2564 case proto.StateExporting: 2565 return errors.New("VM is exporting") 2566 default: 2567 return errors.New("unknown state: " + vm.State.String()) 2568 } 2569 return nil 2570 } 2571 2572 func (m *Manager) unregisterVmMetadataNotifier(ipAddr net.IP, 2573 pathChannel chan<- string) error { 2574 vm, err := m.getVmAndLock(ipAddr, true) 2575 if err != nil { 2576 return err 2577 } 2578 defer vm.mutex.Unlock() 2579 delete(vm.metadataChannels, pathChannel) 2580 return nil 2581 } 2582 2583 func (m *Manager) writeRaw(volume proto.LocalVolume, extension string, 2584 client *srpc.Client, fs *filesystem.FileSystem, 2585 writeRawOptions util.WriteRawOptions, skipBootloader bool) error { 2586 var objectsGetter objectserver.ObjectsGetter 2587 if m.objectCache == nil { 2588 objectClient := objclient.AttachObjectClient(client) 2589 defer objectClient.Close() 2590 objectsGetter = objectClient 2591 } else { 2592 objectsGetter = m.objectCache 2593 } 2594 writeRawOptions.AllocateBlocks = true 2595 if skipBootloader { 2596 bootInfo, err := util.GetBootInfo(fs, writeRawOptions.RootLabel, "") 2597 if err != nil { 2598 return err 2599 } 2600 err = extractKernel(volume, extension, objectsGetter, fs, bootInfo) 2601 if err != nil { 2602 return err 2603 } 2604 } else { 2605 writeRawOptions.InstallBootloader = true 2606 } 2607 writeRawOptions.WriteFstab = true 2608 return util.WriteRawWithOptions(fs, objectsGetter, 2609 volume.Filename+extension, privateFilePerms, mbr.TABLE_TYPE_MSDOS, 2610 writeRawOptions, m.Logger) 2611 } 2612 2613 func (vm *vmInfoType) autoDestroy() { 2614 vm.logger.Println("VM was not acknowledged, destroying") 2615 authInfo := &srpc.AuthInformation{HaveMethodAccess: true} 2616 err := vm.manager.destroyVm(vm.Address.IpAddress, authInfo, nil) 2617 if err != nil { 2618 vm.logger.Println(err) 2619 } 2620 } 2621 2622 func (vm *vmInfoType) changeIpAddress(ipAddress string) error { 2623 dirname := filepath.Join(vm.manager.StateDir, "VMs", ipAddress) 2624 if err := os.Rename(vm.dirname, dirname); err != nil { 2625 return err 2626 } 2627 vm.dirname = dirname 2628 for index, volume := range vm.VolumeLocations { 2629 parent := filepath.Dir(volume.DirectoryToCleanup) 2630 dirname := filepath.Join(parent, ipAddress) 2631 if err := os.Rename(volume.DirectoryToCleanup, dirname); err != nil { 2632 return err 2633 } 2634 vm.VolumeLocations[index] = proto.LocalVolume{ 2635 DirectoryToCleanup: dirname, 2636 Filename: filepath.Join(dirname, 2637 filepath.Base(volume.Filename)), 2638 } 2639 } 2640 vm.logger.Printf("changing to new address: %s\n", ipAddress) 2641 vm.logger = prefixlogger.New(ipAddress+": ", vm.manager.Logger) 2642 vm.writeInfo() 2643 vm.manager.mutex.Lock() 2644 defer vm.manager.mutex.Unlock() 2645 delete(vm.manager.vms, vm.ipAddress) 2646 vm.ipAddress = ipAddress 2647 vm.manager.vms[vm.ipAddress] = vm 2648 vm.manager.sendUpdateWithLock(proto.Update{ 2649 HaveVMs: true, 2650 VMs: map[string]*proto.VmInfo{ipAddress: &vm.VmInfo}, 2651 }) 2652 return nil 2653 } 2654 2655 func (vm *vmInfoType) checkAuth(authInfo *srpc.AuthInformation, 2656 accessToken []byte) error { 2657 if authInfo.HaveMethodAccess { 2658 return nil 2659 } 2660 if _, ok := vm.ownerUsers[authInfo.Username]; ok { 2661 return nil 2662 } 2663 if len(vm.accessToken) >= 32 && bytes.Equal(vm.accessToken, accessToken) { 2664 return nil 2665 } 2666 for _, ownerGroup := range vm.OwnerGroups { 2667 if _, ok := authInfo.GroupList[ownerGroup]; ok { 2668 return nil 2669 } 2670 } 2671 return errorNoAccessToResource 2672 } 2673 2674 func (vm *vmInfoType) cleanup() { 2675 if vm == nil { 2676 return 2677 } 2678 select { 2679 case vm.commandChannel <- "quit": 2680 default: 2681 } 2682 m := vm.manager 2683 m.mutex.Lock() 2684 delete(m.vms, vm.ipAddress) 2685 if !vm.doNotWriteOrSend { 2686 m.sendVmInfo(vm.ipAddress, nil) 2687 } 2688 if !vm.Uncommitted { 2689 if err := m.releaseAddressInPoolWithLock(vm.Address); err != nil { 2690 m.Logger.Println(err) 2691 } 2692 for _, address := range vm.SecondaryAddresses { 2693 if err := m.releaseAddressInPoolWithLock(address); err != nil { 2694 m.Logger.Println(err) 2695 } 2696 } 2697 } 2698 os.RemoveAll(vm.dirname) 2699 for _, volume := range vm.VolumeLocations { 2700 os.RemoveAll(volume.DirectoryToCleanup) 2701 } 2702 m.mutex.Unlock() 2703 } 2704 2705 func (vm *vmInfoType) copyRootVolume(request proto.CreateVmRequest, 2706 reader io.Reader, dataSize uint64) error { 2707 err := vm.setupVolumes(dataSize, request.SecondaryVolumes, 2708 request.SpreadVolumes) 2709 if err != nil { 2710 return err 2711 } 2712 err = copyData(vm.VolumeLocations[0].Filename, reader, dataSize) 2713 if err != nil { 2714 return err 2715 } 2716 vm.Volumes = []proto.Volume{{Size: dataSize}} 2717 return nil 2718 } 2719 2720 func (vm *vmInfoType) delete() { 2721 select { 2722 case vm.accessTokenCleanupNotifier <- struct{}{}: 2723 default: 2724 } 2725 for ch := range vm.metadataChannels { 2726 close(ch) 2727 } 2728 vm.manager.DhcpServer.RemoveLease(vm.Address.IpAddress) 2729 for _, address := range vm.SecondaryAddresses { 2730 vm.manager.DhcpServer.RemoveLease(address.IpAddress) 2731 } 2732 vm.manager.mutex.Lock() 2733 delete(vm.manager.vms, vm.ipAddress) 2734 vm.manager.sendVmInfo(vm.ipAddress, nil) 2735 var err error 2736 if vm.State == proto.StateExporting { 2737 err = vm.manager.unregisterAddress(vm.Address, false) 2738 for _, address := range vm.SecondaryAddresses { 2739 err := vm.manager.unregisterAddress(address, false) 2740 if err != nil { 2741 vm.manager.Logger.Println(err) 2742 } 2743 } 2744 } else if !vm.Uncommitted { 2745 err = vm.manager.releaseAddressInPoolWithLock(vm.Address) 2746 for _, address := range vm.SecondaryAddresses { 2747 err := vm.manager.releaseAddressInPoolWithLock(address) 2748 if err != nil { 2749 vm.manager.Logger.Println(err) 2750 } 2751 } 2752 } 2753 vm.manager.mutex.Unlock() 2754 if err != nil { 2755 vm.manager.Logger.Println(err) 2756 } 2757 for _, volume := range vm.VolumeLocations { 2758 os.Remove(volume.Filename) 2759 if volume.DirectoryToCleanup != "" { 2760 os.RemoveAll(volume.DirectoryToCleanup) 2761 } 2762 } 2763 os.RemoveAll(vm.dirname) 2764 } 2765 2766 func (vm *vmInfoType) destroy() { 2767 select { 2768 case vm.commandChannel <- "quit": 2769 default: 2770 } 2771 vm.delete() 2772 } 2773 2774 func (vm *vmInfoType) discardSnapshot() error { 2775 for _, volume := range vm.VolumeLocations { 2776 if err := os.Remove(volume.Filename + ".snapshot"); err != nil { 2777 if !os.IsNotExist(err) { 2778 return err 2779 } 2780 } 2781 } 2782 return nil 2783 } 2784 2785 func (vm *vmInfoType) getActiveInitrdPath() string { 2786 initrdPath := vm.getInitrdPath() 2787 if _, err := os.Stat(initrdPath); err == nil { 2788 return initrdPath 2789 } 2790 return "" 2791 } 2792 2793 func (vm *vmInfoType) getActiveKernelPath() string { 2794 kernelPath := vm.getKernelPath() 2795 if _, err := os.Stat(kernelPath); err == nil { 2796 return kernelPath 2797 } 2798 return "" 2799 } 2800 2801 func (vm *vmInfoType) getInitrdPath() string { 2802 return filepath.Join(vm.VolumeLocations[0].DirectoryToCleanup, "initrd") 2803 } 2804 2805 func (vm *vmInfoType) getKernelPath() string { 2806 return filepath.Join(vm.VolumeLocations[0].DirectoryToCleanup, "kernel") 2807 } 2808 2809 func (vm *vmInfoType) kill() { 2810 vm.mutex.RLock() 2811 defer vm.mutex.RUnlock() 2812 if vm.State == proto.StateStopping { 2813 vm.commandChannel <- "quit" 2814 } 2815 } 2816 2817 func (vm *vmInfoType) monitor(monitorSock net.Conn, 2818 commandChannel <-chan string) { 2819 vm.hasHealthAgent = false 2820 defer monitorSock.Close() 2821 go vm.processMonitorResponses(monitorSock) 2822 cancelChannel := make(chan struct{}, 1) 2823 go vm.probeHealthAgent(cancelChannel) 2824 go vm.serialManager() 2825 for command := range commandChannel { 2826 _, err := fmt.Fprintf(monitorSock, `{"execute":"%s"}`, command) 2827 if err != nil { 2828 vm.logger.Println(err) 2829 } else { 2830 vm.logger.Debugf(0, "sent %s command", command) 2831 } 2832 } 2833 cancelChannel <- struct{}{} 2834 } 2835 2836 func (vm *vmInfoType) probeHealthAgent(cancel <-chan struct{}) { 2837 stopTime := time.Now().Add(time.Minute * 5) 2838 for time.Until(stopTime) > 0 { 2839 select { 2840 case <-cancel: 2841 return 2842 default: 2843 } 2844 sleepUntil := time.Now().Add(time.Second) 2845 if vm.ipAddress == "0.0.0.0" { 2846 time.Sleep(time.Until(sleepUntil)) 2847 continue 2848 } 2849 conn, err := net.DialTimeout("tcp", vm.ipAddress+":6910", time.Second*5) 2850 if err == nil { 2851 conn.Close() 2852 vm.mutex.Lock() 2853 vm.hasHealthAgent = true 2854 vm.mutex.Unlock() 2855 return 2856 } 2857 time.Sleep(time.Until(sleepUntil)) 2858 } 2859 } 2860 2861 func (vm *vmInfoType) processMonitorResponses(monitorSock net.Conn) { 2862 io.Copy(ioutil.Discard, monitorSock) // Read all and drop. 2863 vm.mutex.Lock() 2864 defer vm.mutex.Unlock() 2865 close(vm.commandChannel) 2866 vm.commandChannel = nil 2867 switch vm.State { 2868 case proto.StateStarting: 2869 select { 2870 case vm.stoppedNotifier <- struct{}{}: 2871 default: 2872 } 2873 return 2874 case proto.StateRunning: 2875 select { 2876 case vm.stoppedNotifier <- struct{}{}: 2877 default: 2878 } 2879 return 2880 case proto.StateFailedToStart: 2881 return 2882 case proto.StateStopping: 2883 vm.setState(proto.StateStopped) 2884 select { 2885 case vm.stoppedNotifier <- struct{}{}: 2886 default: 2887 } 2888 case proto.StateStopped: 2889 return 2890 case proto.StateDestroying: 2891 vm.delete() 2892 return 2893 case proto.StateMigrating: 2894 return 2895 case proto.StateExporting: 2896 return 2897 default: 2898 vm.logger.Println("unknown state: " + vm.State.String()) 2899 } 2900 } 2901 2902 func (vm *vmInfoType) rootLabel() string { 2903 ipAddr := vm.Address.IpAddress 2904 return fmt.Sprintf("rootfs@%02x%02x%02x%02x", 2905 ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]) 2906 } 2907 2908 func (vm *vmInfoType) serialManager() { 2909 bootlogFile, err := os.OpenFile(filepath.Join(vm.dirname, bootlogFilename), 2910 os.O_CREATE|os.O_WRONLY|os.O_APPEND, fsutil.PublicFilePerms) 2911 if err != nil { 2912 vm.logger.Printf("error opening bootlog file: %s\n", err) 2913 return 2914 } 2915 defer bootlogFile.Close() 2916 serialSock, err := net.Dial("unix", 2917 filepath.Join(vm.dirname, serialSockFilename)) 2918 if err != nil { 2919 vm.logger.Printf("error connecting to console: %s\n", err) 2920 return 2921 } 2922 defer serialSock.Close() 2923 vm.mutex.Lock() 2924 vm.serialInput = serialSock 2925 vm.mutex.Unlock() 2926 buffer := make([]byte, 256) 2927 for { 2928 if nRead, err := serialSock.Read(buffer); err != nil { 2929 if err != io.EOF { 2930 vm.logger.Printf("error reading from serial port: %s\n", err) 2931 } else { 2932 vm.logger.Debugln(0, "serial port closed") 2933 } 2934 break 2935 } else if nRead > 0 { 2936 vm.mutex.RLock() 2937 if vm.serialOutput != nil { 2938 for _, char := range buffer[:nRead] { 2939 vm.serialOutput <- char 2940 } 2941 vm.mutex.RUnlock() 2942 } else { 2943 vm.mutex.RUnlock() 2944 bootlogFile.Write(buffer[:nRead]) 2945 } 2946 } 2947 } 2948 vm.mutex.Lock() 2949 vm.serialInput = nil 2950 if vm.serialOutput != nil { 2951 close(vm.serialOutput) 2952 vm.serialOutput = nil 2953 } 2954 vm.mutex.Unlock() 2955 } 2956 2957 func (vm *vmInfoType) setState(state proto.State) { 2958 vm.State = state 2959 if !vm.doNotWriteOrSend { 2960 vm.writeAndSendInfo() 2961 } 2962 } 2963 2964 func (vm *vmInfoType) setupVolumes(rootSize uint64, 2965 secondaryVolumes []proto.Volume, spreadVolumes bool) error { 2966 volumeDirectories, err := vm.manager.getVolumeDirectories(rootSize, 2967 secondaryVolumes, spreadVolumes) 2968 if err != nil { 2969 return err 2970 } 2971 volumeDirectory := filepath.Join(volumeDirectories[0], vm.ipAddress) 2972 os.RemoveAll(volumeDirectory) 2973 if err := os.MkdirAll(volumeDirectory, dirPerms); err != nil { 2974 return err 2975 } 2976 filename := filepath.Join(volumeDirectory, "root") 2977 vm.VolumeLocations = append(vm.VolumeLocations, 2978 proto.LocalVolume{volumeDirectory, filename}) 2979 for index := range secondaryVolumes { 2980 volumeDirectory := filepath.Join(volumeDirectories[index+1], 2981 vm.ipAddress) 2982 os.RemoveAll(volumeDirectory) 2983 if err := os.MkdirAll(volumeDirectory, dirPerms); err != nil { 2984 return err 2985 } 2986 filename := filepath.Join(volumeDirectory, 2987 fmt.Sprintf("secondary-volume.%d", index)) 2988 vm.VolumeLocations = append(vm.VolumeLocations, 2989 proto.LocalVolume{volumeDirectory, filename}) 2990 } 2991 return nil 2992 } 2993 2994 // This may grab the VM lock. 2995 func (vm *vmInfoType) startManaging(dhcpTimeout time.Duration, 2996 enableNetboot, haveManagerLock bool) (bool, error) { 2997 vm.monitorSockname = filepath.Join(vm.dirname, "monitor.sock") 2998 vm.logger.Debugln(1, "startManaging() starting") 2999 switch vm.State { 3000 case proto.StateStarting: 3001 case proto.StateRunning: 3002 case proto.StateFailedToStart: 3003 case proto.StateStopping: 3004 monitorSock, err := net.Dial("unix", vm.monitorSockname) 3005 if err == nil { 3006 commandChannel := make(chan string, 1) 3007 vm.commandChannel = commandChannel 3008 go vm.monitor(monitorSock, commandChannel) 3009 commandChannel <- "qmp_capabilities" 3010 vm.kill() 3011 } 3012 return false, nil 3013 case proto.StateStopped: 3014 return false, nil 3015 case proto.StateDestroying: 3016 vm.delete() 3017 return false, nil 3018 case proto.StateMigrating: 3019 return false, nil 3020 default: 3021 vm.logger.Println("unknown state: " + vm.State.String()) 3022 return false, nil 3023 } 3024 if dhcpTimeout >= 0 { 3025 err := vm.manager.DhcpServer.AddLease(vm.Address, vm.Hostname) 3026 if err != nil { 3027 return false, err 3028 } 3029 for _, address := range vm.SecondaryAddresses { 3030 err := vm.manager.DhcpServer.AddLease(address, vm.Hostname) 3031 if err != nil { 3032 vm.logger.Println(err) 3033 } 3034 } 3035 } 3036 monitorSock, err := net.Dial("unix", vm.monitorSockname) 3037 if err != nil { 3038 vm.logger.Debugf(1, "error connecting to: %s: %s\n", 3039 vm.monitorSockname, err) 3040 if err := vm.startVm(enableNetboot, haveManagerLock); err != nil { 3041 vm.logger.Println(err) 3042 vm.setState(proto.StateFailedToStart) 3043 return false, err 3044 } 3045 monitorSock, err = net.Dial("unix", vm.monitorSockname) 3046 } 3047 if err != nil { 3048 vm.logger.Println(err) 3049 vm.setState(proto.StateFailedToStart) 3050 return false, err 3051 } 3052 commandChannel := make(chan string, 1) 3053 vm.commandChannel = commandChannel 3054 go vm.monitor(monitorSock, commandChannel) 3055 commandChannel <- "qmp_capabilities" 3056 vm.setState(proto.StateRunning) 3057 if len(vm.Address.IpAddress) < 1 { 3058 // Must wait to see what IP address is given by external DHCP server. 3059 reqCh := vm.manager.DhcpServer.MakeRequestChannel(vm.Address.MacAddress) 3060 if dhcpTimeout < time.Minute { 3061 dhcpTimeout = time.Minute 3062 } 3063 timer := time.NewTimer(dhcpTimeout) 3064 select { 3065 case ipAddr := <-reqCh: 3066 timer.Stop() 3067 return false, vm.changeIpAddress(ipAddr.String()) 3068 case <-timer.C: 3069 return true, errors.New("timed out on external lease") 3070 } 3071 } 3072 if dhcpTimeout > 0 { 3073 ackChan := vm.manager.DhcpServer.MakeAcknowledgmentChannel( 3074 vm.Address.IpAddress) 3075 timer := time.NewTimer(dhcpTimeout) 3076 select { 3077 case <-ackChan: 3078 timer.Stop() 3079 case <-timer.C: 3080 return true, nil 3081 } 3082 } 3083 return false, nil 3084 } 3085 3086 func (vm *vmInfoType) getBridgesAndOptions(haveManagerLock bool) ( 3087 []string, []string, error) { 3088 if !haveManagerLock { 3089 vm.manager.mutex.RLock() 3090 defer vm.manager.mutex.RUnlock() 3091 } 3092 addresses := make([]proto.Address, 1, len(vm.SecondarySubnetIDs)+1) 3093 addresses[0] = vm.Address 3094 subnetIDs := make([]string, 1, len(vm.SecondarySubnetIDs)+1) 3095 subnetIDs[0] = vm.SubnetId 3096 for index, subnetId := range vm.SecondarySubnetIDs { 3097 addresses = append(addresses, vm.SecondaryAddresses[index]) 3098 subnetIDs = append(subnetIDs, subnetId) 3099 } 3100 var bridges, options []string 3101 deviceDriver := "virtio-net-pci" 3102 if vm.DisableVirtIO { 3103 deviceDriver = "e1000" 3104 } 3105 for index, address := range addresses { 3106 subnet, ok := vm.manager.subnets[subnetIDs[index]] 3107 if !ok { 3108 return nil, nil, 3109 fmt.Errorf("subnet: %s not found", subnetIDs[index]) 3110 } 3111 bridge, vlanOption, err := vm.manager.getBridgeForSubnet(subnet) 3112 if err != nil { 3113 return nil, nil, err 3114 } 3115 bridges = append(bridges, bridge) 3116 options = append(options, 3117 "-netdev", fmt.Sprintf("tap,id=net%d,fd=%d%s", 3118 index, index+3, vlanOption), 3119 "-device", fmt.Sprintf("%s,netdev=net%d,mac=%s", 3120 deviceDriver, index, address.MacAddress)) 3121 } 3122 return bridges, options, nil 3123 } 3124 3125 func (vm *vmInfoType) startVm(enableNetboot, haveManagerLock bool) error { 3126 if err := checkAvailableMemory(vm.MemoryInMiB); err != nil { 3127 return err 3128 } 3129 nCpus := vm.MilliCPUs / 1000 3130 if nCpus < 1 { 3131 nCpus = 1 3132 } 3133 if nCpus*1000 < vm.MilliCPUs { 3134 nCpus++ 3135 } 3136 bridges, netOptions, err := vm.getBridgesAndOptions(haveManagerLock) 3137 if err != nil { 3138 return err 3139 } 3140 var tapFiles []*os.File 3141 for _, bridge := range bridges { 3142 tapFile, err := createTapDevice(bridge) 3143 if err != nil { 3144 return fmt.Errorf("error creating tap device: %s", err) 3145 } 3146 defer tapFile.Close() 3147 tapFiles = append(tapFiles, tapFile) 3148 } 3149 cmd := exec.Command("qemu-system-x86_64", "-machine", "pc,accel=kvm", 3150 "-cpu", "host", // Allow the VM to take full advantage of host CPU. 3151 "-nodefaults", 3152 "-name", vm.ipAddress, 3153 "-m", fmt.Sprintf("%dM", vm.MemoryInMiB), 3154 "-smp", fmt.Sprintf("cpus=%d", nCpus), 3155 "-serial", 3156 "unix:"+filepath.Join(vm.dirname, serialSockFilename)+",server,nowait", 3157 "-chroot", "/tmp", 3158 "-runas", vm.manager.Username, 3159 "-qmp", "unix:"+vm.monitorSockname+",server,nowait", 3160 "-daemonize") 3161 if kernelPath := vm.getActiveKernelPath(); kernelPath != "" { 3162 cmd.Args = append(cmd.Args, "-kernel", kernelPath) 3163 if initrdPath := vm.getActiveInitrdPath(); initrdPath != "" { 3164 cmd.Args = append(cmd.Args, 3165 "-initrd", initrdPath, 3166 "-append", util.MakeKernelOptions("LABEL="+vm.rootLabel(), 3167 "net.ifnames=0"), 3168 ) 3169 } else { 3170 cmd.Args = append(cmd.Args, 3171 "-append", util.MakeKernelOptions("/dev/vda1", "net.ifnames=0"), 3172 ) 3173 } 3174 } else if enableNetboot { 3175 cmd.Args = append(cmd.Args, "-boot", "order=n") 3176 } 3177 cmd.Args = append(cmd.Args, netOptions...) 3178 if vm.manager.ShowVgaConsole { 3179 cmd.Args = append(cmd.Args, "-vga", "std") 3180 } else { 3181 switch vm.ConsoleType { 3182 case proto.ConsoleNone: 3183 cmd.Args = append(cmd.Args, "-nographic") 3184 case proto.ConsoleDummy: 3185 cmd.Args = append(cmd.Args, "-display", "none", "-vga", "std") 3186 case proto.ConsoleVNC: 3187 cmd.Args = append(cmd.Args, 3188 "-display", "vnc=unix:"+filepath.Join(vm.dirname, "vnc"), 3189 "-vga", "std") 3190 } 3191 } 3192 var interfaceDriver string 3193 if !vm.DisableVirtIO { 3194 interfaceDriver = ",if=virtio" 3195 } 3196 for index, volume := range vm.VolumeLocations { 3197 var volumeFormat proto.VolumeFormat 3198 if index < len(vm.Volumes) { 3199 volumeFormat = vm.Volumes[index].Format 3200 } 3201 options := interfaceDriver 3202 if vm.manager.checkTrim(volume.Filename) { 3203 options += ",discard=on" 3204 } 3205 cmd.Args = append(cmd.Args, 3206 "-drive", "file="+volume.Filename+",format="+volumeFormat.String()+ 3207 options) 3208 } 3209 os.Remove(filepath.Join(vm.dirname, "bootlog")) 3210 cmd.ExtraFiles = tapFiles // Start at fd=3 for QEMU. 3211 if output, err := cmd.CombinedOutput(); err != nil { 3212 return fmt.Errorf("error starting QEMU: %s: %s", err, output) 3213 } else if len(output) > 0 { 3214 vm.logger.Printf("QEMU started. Output: \"%s\"\n", string(output)) 3215 } else { 3216 vm.logger.Println("QEMU started.") 3217 } 3218 return nil 3219 } 3220 3221 func (vm *vmInfoType) writeAndSendInfo() { 3222 if err := vm.writeInfo(); err != nil { 3223 vm.logger.Println(err) 3224 return 3225 } 3226 vm.manager.sendVmInfo(vm.ipAddress, &vm.VmInfo) 3227 } 3228 3229 func (vm *vmInfoType) writeInfo() error { 3230 filename := filepath.Join(vm.dirname, "info.json") 3231 return json.WriteToFile(filename, publicFilePerms, " ", vm) 3232 }