The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7008

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 2689168252b2f1122bb08bb949f8f61dfa973f06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 11 Mar 2020 19:28:09 -0400
Subject: [PATCH 1/4] lxd/images: Allow virtual-machine and instance as source
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/images.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/images.go b/lxd/images.go
index c05c7d6582..5be76ebca6 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -174,7 +174,7 @@ func imgPostContInfo(d *Daemon, r *http.Request, req 
api.ImagesPost, op *operati
                if !shared.IsSnapshot(name) {
                        return nil, fmt.Errorf("Not a snapshot")
                }
-       case "container":
+       case "container", "virtual-machine", "instance":
                if shared.IsSnapshot(name) {
                        return nil, fmt.Errorf("This is a snapshot")
                }

From 49484aceac6fb2e396f8705e29d48d19d2688251 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 11 Mar 2020 19:33:18 -0400
Subject: [PATCH 2/4] lxd/images: Set right image type on publish
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/images.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/images.go b/lxd/images.go
index 5be76ebca6..fa9cb1cee6 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -160,7 +160,6 @@ func compressFile(compress string, infile io.Reader, 
outfile io.Writer) error {
  */
 func imgPostContInfo(d *Daemon, r *http.Request, req api.ImagesPost, op 
*operations.Operation, builddir string) (*api.Image, error) {
        info := api.Image{}
-       info.Type = "container"
        info.Properties = map[string]string{}
        project := projectParam(r)
        name := req.Source.Name
@@ -195,6 +194,8 @@ func imgPostContInfo(d *Daemon, r *http.Request, req 
api.ImagesPost, op *operati
                return nil, err
        }
 
+       info.Type = c.Type().String()
+
        // Build the actual image file
        imageFile, err := ioutil.TempFile(builddir, "lxd_build_image_")
        if err != nil {

From 261c5165ca1a9182e489b6b525903a7b5e4d5d67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 11 Mar 2020 17:59:53 -0400
Subject: [PATCH 3/4] lxd/vm: Implement Export
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/instance/drivers/driver_qemu.go | 237 +++++++++++++++++++++++++++-
 1 file changed, 236 insertions(+), 1 deletion(-)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 3c83c55619..874b64c3bd 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -50,6 +50,7 @@ import (
        "github.com/lxc/lxd/lxd/vsock"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
+       "github.com/lxc/lxd/shared/containerwriter"
        log "github.com/lxc/lxd/shared/log15"
        "github.com/lxc/lxd/shared/logger"
        "github.com/lxc/lxd/shared/osarch"
@@ -2956,7 +2957,241 @@ func (vm *qemu) deviceRemove(deviceName string, 
rawConfig deviceConfig.Device) e
 
 // Export publishes the instance.
 func (vm *qemu) Export(w io.Writer, properties map[string]string) error {
-       return instance.ErrNotImplemented
+       ctxMap := log.Ctx{
+               "project":   vm.project,
+               "name":      vm.name,
+               "created":   vm.creationDate,
+               "ephemeral": vm.ephemeral,
+               "used":      vm.lastUsedDate}
+
+       if vm.IsRunning() {
+               return fmt.Errorf("Cannot export a running instance as an 
image")
+       }
+
+       logger.Info("Exporting instance", ctxMap)
+
+       // Start the storage.
+       ourStart, err := vm.mount()
+       if err != nil {
+               logger.Error("Failed exporting instance", ctxMap)
+               return err
+       }
+       if ourStart {
+               defer vm.unmount()
+       }
+
+       // Create the tarball.
+       ctw := containerwriter.NewContainerTarWriter(w, nil)
+
+       // Path inside the tar image is the pathname starting after cDir.
+       cDir := vm.Path()
+       offset := len(cDir) + 1
+
+       writeToTar := func(path string, fi os.FileInfo, err error) error {
+               if err != nil {
+                       return err
+               }
+
+               err = ctw.WriteFile(offset, path, fi)
+               if err != nil {
+                       logger.Debugf("Error tarring up %s: %s", path, err)
+                       return err
+               }
+
+               return nil
+       }
+
+       // Look for metadata.yaml.
+       fnam := filepath.Join(cDir, "metadata.yaml")
+       if !shared.PathExists(fnam) {
+               // Generate a new metadata.yaml.
+               tempDir, err := ioutil.TempDir("", "lxd_lxd_metadata_")
+               if err != nil {
+                       ctw.Close()
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+               defer os.RemoveAll(tempDir)
+
+               // Get the instance's architecture.
+               var arch string
+               if vm.IsSnapshot() {
+                       parentName, _, _ := 
shared.InstanceGetParentAndSnapshotName(vm.name)
+                       parent, err := instance.LoadByProjectAndName(vm.state, 
vm.project, parentName)
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+
+                       arch, _ = osarch.ArchitectureName(parent.Architecture())
+               } else {
+                       arch, _ = osarch.ArchitectureName(vm.architecture)
+               }
+
+               if arch == "" {
+                       arch, err = 
osarch.ArchitectureName(vm.state.OS.Architectures[0])
+                       if err != nil {
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+               }
+
+               // Fill in the metadata.
+               meta := api.ImageMetadata{}
+               meta.Architecture = arch
+               meta.CreationDate = time.Now().UTC().Unix()
+               meta.Properties = properties
+
+               data, err := yaml.Marshal(&meta)
+               if err != nil {
+                       ctw.Close()
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+
+               // Write the actual file.
+               fnam = filepath.Join(tempDir, "metadata.yaml")
+               err = ioutil.WriteFile(fnam, data, 0644)
+               if err != nil {
+                       ctw.Close()
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+
+               fi, err := os.Lstat(fnam)
+               if err != nil {
+                       ctw.Close()
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+
+               tmpOffset := len(filepath.Dir(fnam)) + 1
+               if err := ctw.WriteFile(tmpOffset, fnam, fi); err != nil {
+                       ctw.Close()
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+       } else {
+               if properties != nil {
+                       // Parse the metadata.
+                       content, err := ioutil.ReadFile(fnam)
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+
+                       metadata := new(api.ImageMetadata)
+                       err = yaml.Unmarshal(content, &metadata)
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+                       metadata.Properties = properties
+
+                       // Generate a new metadata.yaml.
+                       tempDir, err := ioutil.TempDir("", "lxd_lxd_metadata_")
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+                       defer os.RemoveAll(tempDir)
+
+                       data, err := yaml.Marshal(&metadata)
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+
+                       // Write the actual file.
+                       fnam = filepath.Join(tempDir, "metadata.yaml")
+                       err = ioutil.WriteFile(fnam, data, 0644)
+                       if err != nil {
+                               ctw.Close()
+                               logger.Error("Failed exporting instance", 
ctxMap)
+                               return err
+                       }
+               }
+
+               // Include metadata.yaml in the tarball.
+               fi, err := os.Lstat(fnam)
+               if err != nil {
+                       ctw.Close()
+                       logger.Debugf("Error statting %s during export", fnam)
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+
+               if properties != nil {
+                       tmpOffset := len(filepath.Dir(fnam)) + 1
+                       err = ctw.WriteFile(tmpOffset, fnam, fi)
+               } else {
+                       err = ctw.WriteFile(offset, fnam, fi)
+               }
+               if err != nil {
+                       ctw.Close()
+                       logger.Debugf("Error writing to tarfile: %s", err)
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+       }
+
+       // Convert and include the root image.
+       pool, err := vm.getStoragePool()
+       if err != nil {
+               return err
+       }
+
+       rootDrivePath, err := pool.GetInstanceDisk(vm)
+       if err != nil {
+               return err
+       }
+
+       // Convert from raw to qcow2 and add to tarball.
+       tmpPath, err := ioutil.TempDir("", "lxd_export_")
+       if err != nil {
+               return err
+       }
+       defer os.RemoveAll(tmpPath)
+
+       fPath := fmt.Sprintf("%s/rootfs.img", tmpPath)
+       _, err = shared.RunCommand("qemu-img", "convert", "-c", "-O", "qcow2", 
rootDrivePath, fPath)
+       if err != nil {
+               return fmt.Errorf("Failed converting image to qcow2: %v", err)
+       }
+
+       fi, err := os.Lstat(fPath)
+       if err != nil {
+               return err
+       }
+
+       err = ctw.WriteFile(len(tmpPath)+1, fPath, fi)
+       if err != nil {
+               return err
+       }
+
+       // Include all the templates.
+       fnam = vm.TemplatesPath()
+       if shared.PathExists(fnam) {
+               err = filepath.Walk(fnam, writeToTar)
+               if err != nil {
+                       logger.Error("Failed exporting instance", ctxMap)
+                       return err
+               }
+       }
+
+       err = ctw.Close()
+       if err != nil {
+               logger.Error("Failed exporting instance", ctxMap)
+               return err
+       }
+
+       logger.Info("Exported instance", ctxMap)
+       return nil
 }
 
 // Migrate migrates the instance to another node.

From 9b75384f0309f5d53108fe386de3df8d53a1f14f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Wed, 11 Mar 2020 22:57:06 -0400
Subject: [PATCH 4/4] lxd/images: Unpack unified VM images
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/storage/utils.go | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index f5a4ebe734..939082396e 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -590,26 +590,25 @@ func ImageUnpack(imageFile, destPath, destBlockFile 
string, blockBackend, runnin
        } else {
                // If a rootBlockPath is supplied then this is a VM image 
unpack.
 
-               // VM images require a separate rootfs file.
-               if !shared.PathExists(imageRootfsFile) {
-                       return fmt.Errorf("Image is missing a rootfs file: %s", 
imageRootfsFile)
-               }
-
-               // Check that the rootBlockPath exists and is a file.
+               // Validate the target.
                fileInfo, err := os.Stat(destBlockFile)
                if err != nil && !os.IsNotExist(err) {
                        return err
                }
 
-               if os.IsNotExist(err) || !fileInfo.IsDir() {
+               if fileInfo.IsDir() {
+                       // If the dest block file exists, and it is a 
directory, fail.
+                       return fmt.Errorf("Root block path isn't a file: %s", 
destBlockFile)
+               }
+
+               if shared.PathExists(imageRootfsFile) {
                        // Convert the qcow2 format to a raw block device.
                        _, err = shared.RunCommand("qemu-img", "convert", "-O", 
"raw", imageRootfsFile, destBlockFile)
                        if err != nil {
                                return fmt.Errorf("Failed converting image to 
raw at %s: %v", destBlockFile, err)
                        }
                } else {
-                       // If the dest block file exists, and it is a 
directory, fail.
-                       return fmt.Errorf("Root block path isn't a file: %s", 
destBlockFile)
+                       // Get the qcow2 image out of the tarball.
                }
        }
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to