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

     1  package common
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	"github.com/emc-advanced-dev/pkg/errors"
    11  	unikos "github.com/solo-io/unik/pkg/os"
    12  	"github.com/solo-io/unik/pkg/types"
    13  	unikutil "github.com/solo-io/unik/pkg/util"
    14  	"io/ioutil"
    15  )
    16  
    17  func ConvertRawImage(sourceFormat, targetFormat types.ImageFormat, inputFile, outputFile string) error {
    18  	targetFormatName := string(targetFormat)
    19  	if targetFormat == types.ImageFormat_VHD {
    20  		targetFormatName = "vpc" //for some reason qemu calls VHD disks vpc
    21  	}
    22  	dir := filepath.Dir(inputFile)
    23  	outDir := filepath.Dir(outputFile)
    24  
    25  	container := unikutil.NewContainer("qemu-util").WithVolume(dir, "/unik/input").
    26  		WithVolume(outDir, "/unik/output")
    27  
    28  	args := []string{"qemu-img", "convert", "-f", string(sourceFormat), "-O", targetFormatName}
    29  	if targetFormat == types.ImageFormat_VMDK {
    30  		args = append(args, "-o", "compat6")
    31  	}
    32  
    33  	//this needs to be done because docker produces files as root. argh!!!
    34  	tmpOutputFile, err := ioutil.TempFile(outDir, "convert.image.result.")
    35  	if err != nil {
    36  		return errors.New("temp file for root user", err)
    37  	}
    38  	tmpOutputFile.Close()
    39  	defer os.Remove(tmpOutputFile.Name())
    40  
    41  	args = append(args, "/unik/input/" + filepath.Base(inputFile), "/unik/output/" + filepath.Base(tmpOutputFile.Name()))
    42  
    43  	logrus.WithField("command", args).Debugf("running command")
    44  	if err := container.Run(args...); err != nil {
    45  		return errors.New("failed converting raw image to "+string(targetFormat), err)
    46  	}
    47  
    48  	if err := unikos.CopyFile(tmpOutputFile.Name(), outputFile); err != nil {
    49  		return errors.New("copying tmp result to final result", err)
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  func fixVmdk(vmdkFile string) error {
    56  	file, err := os.OpenFile(vmdkFile, os.O_RDWR, 0)
    57  	if err != nil {
    58  		return errors.New("can't open vmdk", err)
    59  	}
    60  	defer file.Close()
    61  
    62  	var buffer [1024]byte
    63  
    64  	n, err := file.Read(buffer[:])
    65  	if err != nil {
    66  		return errors.New("can't read vmdk", err)
    67  	}
    68  	if n < len(buffer) {
    69  		return errors.New("bad vmdk", err)
    70  	}
    71  
    72  	_, err = file.Seek(0, os.SEEK_SET)
    73  	if err != nil {
    74  		return errors.New("can't seek vmdk", err)
    75  	}
    76  
    77  	result := bytes.Replace(buffer[:], []byte("# The disk Data Base"), []byte("# The Disk Data Base"), 1)
    78  
    79  	_, err = file.Write(result)
    80  	if err != nil {
    81  		return errors.New("can't write vmdk", err)
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func ConvertRawToNewVmdk(inputFile, outputFile string) error {
    88  
    89  	dir := filepath.Dir(inputFile)
    90  	outDir := filepath.Dir(outputFile)
    91  
    92  	container := unikutil.NewContainer("euranova/ubuntu-vbox").WithVolume(dir, dir).
    93  		WithVolume(outDir, outDir)
    94  
    95  	args := []string{
    96  		"VBoxManage", "convertfromraw", inputFile, outputFile, "--format", "vmdk", "--variant", "Stream"}
    97  
    98  	logrus.WithField("command", args).Debugf("running command")
    99  	if err := container.Run(args...); err != nil {
   100  		return errors.New("failed converting raw image to vmdk", err)
   101  	}
   102  
   103  	err := fixVmdk(outputFile)
   104  	if err != nil {
   105  		return errors.New("failed converting raw image to vmdk", err)
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  func GetVirtualImageSize(imageFile string, imageFormat types.ImageFormat) (int64, error) {
   112  	formatName := string(imageFormat)
   113  	if imageFormat == types.ImageFormat_VHD {
   114  		formatName = "vpc" //for some reason qemu calls VHD disks vpc
   115  	}
   116  	dir := filepath.Dir(imageFile)
   117  
   118  	container := unikutil.NewContainer("qemu-util").WithVolume(dir, dir)
   119  	args := []string{"qemu-img", "info", "--output", "json", "-f", formatName, imageFile}
   120  
   121  	logrus.WithField("command", args).Debugf("running command")
   122  	out, err := container.CombinedOutput(args...)
   123  	if err != nil {
   124  		return -1, errors.New("failed getting image info", err)
   125  	}
   126  	var info imageInfo
   127  	if err := json.Unmarshal(out, &info); err != nil {
   128  		return -1, errors.New("parsing "+string(out)+" to json", err)
   129  	}
   130  	return info.VirtualSize, nil
   131  }
   132  
   133  type imageInfo struct {
   134  	VirtualSize int64  `json:"virtual-size"`
   135  	Filename    string `json:"filename"`
   136  	ClusterSize int    `json:"cluster-size"`
   137  	Format      string `json:"format"`
   138  	ActualSize  int    `json:"actual-size"`
   139  	DirtyFlag   bool   `json:"dirty-flag"`
   140  }