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 }