github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/vm-control/importLocalVm.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"strings"
    12  	"syscall"
    13  
    14  	hyperclient "github.com/Cloud-Foundations/Dominator/hypervisor/client"
    15  	"github.com/Cloud-Foundations/Dominator/lib/errors"
    16  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    17  	"github.com/Cloud-Foundations/Dominator/lib/json"
    18  	"github.com/Cloud-Foundations/Dominator/lib/log"
    19  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    20  	"github.com/Cloud-Foundations/Dominator/lib/srpc/setupclient"
    21  	"github.com/Cloud-Foundations/Dominator/lib/stringutil"
    22  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    23  )
    24  
    25  const (
    26  	dirPerms = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP |
    27  		syscall.S_IROTH | syscall.S_IXOTH
    28  	privateFilePerms = syscall.S_IRUSR | syscall.S_IWUSR
    29  )
    30  
    31  var (
    32  	errorCommitAbandoned = errors.New("you abandoned your VM")
    33  	errorCommitDeferred  = errors.New("you deferred committing your VM")
    34  )
    35  
    36  func askForCommitDecision(client *srpc.Client, ipAddress net.IP) error {
    37  	response, err := askForInputChoice("Commit VM "+ipAddress.String(),
    38  		[]string{"commit", "defer", "abandon"})
    39  	if err != nil {
    40  		return err
    41  	}
    42  	switch response {
    43  	case "abandon":
    44  		err := hyperclient.DestroyVm(client, ipAddress, nil)
    45  		if err != nil {
    46  			return err
    47  		}
    48  		return errorCommitAbandoned
    49  	case "commit":
    50  		return commitVm(client, ipAddress)
    51  	case "defer":
    52  		return errorCommitDeferred
    53  	}
    54  	return fmt.Errorf("invalid response: %s", response)
    55  }
    56  
    57  func askForInputChoice(prompt string, choices []string) (string, error) {
    58  	reader := bufio.NewReader(os.Stdin)
    59  	choicesMap := stringutil.ConvertListToMap(choices, false)
    60  	for {
    61  		fmt.Fprintf(os.Stderr, "%s (%s)? ", prompt, strings.Join(choices, "/"))
    62  		if response, err := reader.ReadString('\n'); err != nil {
    63  			return "", fmt.Errorf("deferring, error reading input: %s", err)
    64  		} else {
    65  			response = response[:len(response)-1]
    66  			if _, ok := choicesMap[response]; ok {
    67  				return response, nil
    68  			}
    69  		}
    70  	}
    71  }
    72  
    73  func commitVm(client *srpc.Client, ipAddress net.IP) error {
    74  	request := proto.CommitImportedVmRequest{ipAddress}
    75  	var reply proto.CommitImportedVmResponse
    76  	err := client.RequestReply("Hypervisor.CommitImportedVm", request, &reply)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	return errors.New(reply.Error)
    81  }
    82  
    83  func importLocalVmSubcommand(args []string, logger log.DebugLogger) error {
    84  	if err := importLocalVm(args[0], args[1], logger); err != nil {
    85  		return fmt.Errorf("error importing VM: %s", err)
    86  	}
    87  	return nil
    88  }
    89  
    90  func importLocalVm(infoFile, rootVolume string, logger log.DebugLogger) error {
    91  	var vmInfo proto.VmInfo
    92  	if err := json.ReadFromFile(infoFile, &vmInfo); err != nil {
    93  		return err
    94  	}
    95  	return importLocalVmInfo(vmInfo, rootVolume, logger)
    96  }
    97  
    98  func importLocalVmInfo(vmInfo proto.VmInfo, rootVolume string,
    99  	logger log.DebugLogger) error {
   100  	hypervisor := fmt.Sprintf(":%d", *hypervisorPortNum)
   101  	client, err := srpc.DialHTTP("tcp", hypervisor, 0)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	defer client.Close()
   106  	rootCookie, err := readRootCookie(client, logger)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	directories, err := hyperclient.ListVolumeDirectories(client, false)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	dirname := filepath.Join(directories[0], "import")
   115  	if err := os.Mkdir(dirname, dirPerms); err != nil {
   116  		if !os.IsExist(err) {
   117  			return err
   118  		}
   119  	}
   120  	dirname = filepath.Join(dirname, fmt.Sprintf("%d", os.Getpid()))
   121  	if err := os.Mkdir(dirname, dirPerms); err != nil {
   122  		return err
   123  	}
   124  	defer os.RemoveAll(dirname)
   125  	logger.Debugf(0, "created: %s\n", dirname)
   126  	rootFilename := filepath.Join(dirname, "root")
   127  	if err := os.Link(rootVolume, rootFilename); err != nil {
   128  		err = fsutil.CopyFile(rootFilename, rootVolume, privateFilePerms)
   129  		if err != nil {
   130  			return err
   131  		}
   132  	}
   133  	request := proto.ImportLocalVmRequest{
   134  		SkipMemoryCheck:    *skipMemoryCheck,
   135  		VerificationCookie: rootCookie,
   136  		VmInfo:             vmInfo,
   137  		VolumeFilenames:    []string{rootFilename},
   138  	}
   139  	var reply proto.GetVmInfoResponse
   140  	err = client.RequestReply("Hypervisor.ImportLocalVm", request, &reply)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	if err := errors.New(reply.Error); err != nil {
   145  		return err
   146  	}
   147  	logger.Debugln(0, "imported VM")
   148  	os.RemoveAll(dirname)
   149  	err = maybeWatchVm(client, hypervisor, vmInfo.Address.IpAddress, logger)
   150  	if err != nil {
   151  		return err
   152  	}
   153  	return askForCommitDecision(client, vmInfo.Address.IpAddress)
   154  }
   155  
   156  func readRootCookie(client *srpc.Client,
   157  	logger log.DebugLogger) ([]byte, error) {
   158  	rootCookiePath, err := hyperclient.GetRootCookiePath(client)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	rootCookie, err := ioutil.ReadFile(rootCookiePath)
   163  	if err != nil && os.IsPermission(err) {
   164  		// Try again with sudo(8).
   165  		args := make([]string, 0, len(os.Args)+1)
   166  		if sudoPath, err := exec.LookPath("sudo"); err != nil {
   167  			return nil, err
   168  		} else {
   169  			args = append(args, sudoPath)
   170  		}
   171  		if myPath, err := exec.LookPath(os.Args[0]); err != nil {
   172  			return nil, err
   173  		} else {
   174  			args = append(args, myPath)
   175  		}
   176  		args = append(args, "-certDirectory", setupclient.GetCertDirectory())
   177  		args = append(args, os.Args[1:]...)
   178  		if err := syscall.Exec(args[0], args, os.Environ()); err != nil {
   179  			return nil, errors.New("unable to Exec: " + err.Error())
   180  		}
   181  	}
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	logger.Debugln(0, "have cookie")
   186  	return rootCookie, nil
   187  }