github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/pkg/providers/qemu/run_instance.go (about)

     1  package qemu
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"strings"
     8  	"time"
     9  
    10  	"io/ioutil"
    11  
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/emc-advanced-dev/pkg/errors"
    14  	"github.com/solo-io/unik/pkg/compilers"
    15  	"github.com/solo-io/unik/pkg/providers/common"
    16  	"github.com/solo-io/unik/pkg/types"
    17  	"github.com/solo-io/unik/pkg/util"
    18  )
    19  
    20  func (p *QemuProvider) RunInstance(params types.RunInstanceParams) (_ *types.Instance, err error) {
    21  	logrus.WithFields(logrus.Fields{
    22  		"image-id": params.ImageId,
    23  		"mounts":   params.MntPointsToVolumeIds,
    24  		"env":      params.Env,
    25  	}).Infof("running instance %s", params.Name)
    26  
    27  	if _, err := p.GetInstance(params.Name); err == nil {
    28  		return nil, errors.New("instance with name "+params.Name+" already exists. qemu provider requires unique names for instances", nil)
    29  	}
    30  
    31  	image, err := p.GetImage(params.ImageId)
    32  	if err != nil {
    33  		return nil, errors.New("getting image", err)
    34  	}
    35  
    36  	if err := common.VerifyMntsInput(p, image, params.MntPointsToVolumeIds); err != nil {
    37  		return nil, errors.New("invalid mapping for volume", err)
    38  	}
    39  
    40  	volumeIdInOrder := make([]string, len(params.MntPointsToVolumeIds))
    41  
    42  	for mntPoint, volumeId := range params.MntPointsToVolumeIds {
    43  
    44  		controllerPort, err := common.GetControllerPortForMnt(image, mntPoint)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  		volumeIdInOrder[controllerPort] = volumeId
    49  	}
    50  
    51  	logrus.Debugf("creating qemu vm")
    52  
    53  	volImagesInOrder, err := p.getVolumeImages(volumeIdInOrder)
    54  	if err != nil {
    55  		return nil, errors.New("can't get volumes", err)
    56  	}
    57  
    58  	volArgs := volPathToQemuArgs(volImagesInOrder)
    59  
    60  	if params.InstanceMemory == 0 {
    61  		params.InstanceMemory = image.RunSpec.DefaultInstanceMemory
    62  	}
    63  
    64  	qemuArgs := []string{"-m", fmt.Sprintf("%v", params.InstanceMemory), "-net",
    65  		"nic,model=virtio,netdev=mynet0", "-netdev", "user,id=mynet0,net=192.168.76.0/24,dhcpstart=192.168.76.9",
    66  	}
    67  
    68  	cmdlinedata, err := ioutil.ReadFile(getCmdlinePath(image.Name))
    69  	if err != nil {
    70  		logrus.Debugf("cmdLine not found, assuming classic bootloader")
    71  		qemuArgs = append(qemuArgs, "-drive", fmt.Sprintf("file=%s,format=raw,if=ide", getImagePath(image.Name)))
    72  	} else {
    73  		// inject env for rump:
    74  		cmdline := string(cmdlinedata)
    75  		if compilers.CompilerType(image.RunSpec.Compiler).Base() == compilers.Rump {
    76  			cmdline = injectEnv(cmdline, params.Env)
    77  		}
    78  
    79  		// qemu escape
    80  		cmdline = strings.Replace(cmdline, ",", ",,", -1)
    81  
    82  		if _, err := os.Stat(getImagePath(image.Name)); err == nil {
    83  			qemuArgs = append(qemuArgs, "-device", "virtio-blk-pci,id=blk0,drive=hd0")
    84  			qemuArgs = append(qemuArgs, "-drive", fmt.Sprintf("file=%s,format=qcow2,if=none,id=hd0", getImagePath(image.Name)))
    85  		}
    86  
    87  		qemuArgs = append(qemuArgs, "-kernel", getKernelPath(image.Name))
    88  		qemuArgs = append(qemuArgs, "-append", cmdline)
    89  	}
    90  
    91  	if params.DebugMode {
    92  		logrus.Debugf("running instance in debug mode.\nattach unik debugger to port :%v", p.config.DebuggerPort)
    93  		qemuArgs = append(qemuArgs, "-s", "-S")
    94  		debuggerTargetImageName = image.Name
    95  	}
    96  
    97  	if p.config.NoGraphic {
    98  		qemuArgs = append(qemuArgs, "-nographic", "-vga", "none")
    99  	}
   100  
   101  	qemuArgs = append(qemuArgs, volArgs...)
   102  	cmd := exec.Command("qemu-system-x86_64", qemuArgs...)
   103  
   104  	util.LogCommand(cmd, true)
   105  
   106  	if err := cmd.Start(); err != nil {
   107  		return nil, errors.New("can't start qemu - make sure it's in your path.", nil)
   108  	}
   109  
   110  	var instanceIp string
   111  
   112  	instance := &types.Instance{
   113  		Id:             fmt.Sprintf("%d", cmd.Process.Pid),
   114  		Name:           params.Name,
   115  		State:          types.InstanceState_Running,
   116  		IpAddress:      instanceIp,
   117  		Infrastructure: types.Infrastructure_QEMU,
   118  		ImageId:        image.Id,
   119  		Created:        time.Now(),
   120  	}
   121  
   122  	if err := p.state.ModifyInstances(func(instances map[string]*types.Instance) error {
   123  		instances[instance.Id] = instance
   124  		return nil
   125  	}); err != nil {
   126  		return nil, errors.New("modifying instance map in state", err)
   127  	}
   128  
   129  	logrus.WithField("instance", instance).Infof("instance created successfully")
   130  
   131  	return instance, nil
   132  }
   133  
   134  func (p *QemuProvider) getVolumeImages(volumeIdInOrder []string) ([]string, error) {
   135  
   136  	var volPath []string
   137  	for _, v := range volumeIdInOrder {
   138  		v, err := p.GetVolume(v)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  		volPath = append(volPath, getVolumePath(v.Name))
   143  	}
   144  	return volPath, nil
   145  }
   146  
   147  func volPathToQemuArgs(volPaths []string) []string {
   148  	var res []string
   149  	for _, v := range volPaths {
   150  		res = append(res, "-drive", fmt.Sprintf("if=virtio,file=%s,format=qcow2", v))
   151  	}
   152  	return res
   153  }
   154  
   155  func injectEnv(cmdline string, env map[string]string) string {
   156  	// rump json is not really json so we can't parse it
   157  	var envRumpJson []string
   158  	for key, value := range env {
   159  		envRumpJson = append(envRumpJson, fmt.Sprintf("\"env\": \"%s=%s\"", key, value))
   160  	}
   161  
   162  	cmdline = cmdline[:len(cmdline)-2] + "," + strings.Join(envRumpJson, ",") + "}}"
   163  	return cmdline
   164  }