[lxc-devel] [lxd/master] lxd/vm/qemu: Adds storage pool Mount/Unmount calls

tomponline on Github lxc-bot at linuxcontainers.org
Fri Nov 22 17:13:18 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 519 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191122/8f61c2ad/attachment-0001.bin>
-------------- next part --------------
From c964e97f07657362328342ce3cf6304ed3e0f972 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 22 Nov 2019 17:11:25 +0000
Subject: [PATCH] lxd/vm/qemu: Adds storage pool Mount/Unmount calls

- Caches storage pool and agent client handle for efficiency.
- Tweaks agent TLS config generation so that it mounts the config volume before generation.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/vm_qemu.go | 134 +++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 119 insertions(+), 15 deletions(-)

diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index a1bf599b8e..3e6bd600bb 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -64,11 +64,6 @@ func vmQemuLoad(s *state.State, args db.InstanceArgs, profiles []api.Profile) (i
 		return nil, err
 	}
 
-	err = vm.initAgentClient()
-	if err != nil {
-		return nil, err
-	}
-
 	return vm, nil
 }
 
@@ -271,17 +266,74 @@ type vmQemu struct {
 
 	expiryDate time.Time
 
+	// Cached handles.
+	// Do not use these variables directly, instead use their associated get functions so they
+	// will be initialised on demand.
 	agentClient *http.Client
+	storagePool storagePools.Pool
 }
 
-func (vm *vmQemu) initAgentClient() error {
+// getAgentClient returns the current agent client handle. To avoid TLS setup each time this
+// function is called, the handle is cached internally in the vmQemu struct.
+func (vm *vmQemu) getAgentClient() (*http.Client, error) {
+	if vm.agentClient != nil {
+		return vm.agentClient, nil
+	}
+
 	// The connection uses mutual authentication, so use the LXD server's key & cert for client.
 	agentCert, _, clientCert, clientKey, err := vm.generateAgentCert()
+	if err != nil {
+		return nil, err
+	}
+
+	agent, err := vsock.HTTPClient(vm.vsockID(), clientCert, clientKey, agentCert)
+	if err != nil {
+		return nil, err
+	}
+
+	return agent, nil
+}
+
+// getStoragePool returns the current storage pool handle. To avoid a DB lookup each time this
+// function is called, the handle is cached internally in the vmQemu struct.
+func (vm *vmQemu) getStoragePool() (storagePools.Pool, error) {
+	if vm.storagePool != nil {
+		return vm.storagePool, nil
+	}
+
+	pool, err := storagePools.GetPoolByInstance(vm.state, vm)
+	if err != nil {
+		return nil, err
+	}
+	vm.storagePool = pool
+
+	return vm.storagePool, nil
+}
+
+// mount mounts the instance's config volume if needed.
+func (vm *vmQemu) mount() (ourMount bool, err error) {
+	var pool storagePools.Pool
+	pool, err = vm.getStoragePool()
+	if err != nil {
+		return
+	}
+
+	ourMount, err = pool.MountInstance(vm, nil)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// unmount unmounts the instance's config volume if needed.
+func (vm *vmQemu) unmount() error {
+	pool, err := vm.getStoragePool()
 	if err != nil {
 		return err
 	}
 
-	vm.agentClient, err = vsock.HTTPClient(vm.vsockID(), clientCert, clientKey, agentCert)
+	_, err = pool.UnmountInstance(vm, nil)
 	if err != nil {
 		return err
 	}
@@ -291,13 +343,23 @@ func (vm *vmQemu) initAgentClient() error {
 
 // generateAgentCert creates the necessary server key and certificate if needed.
 func (vm *vmQemu) generateAgentCert() (string, string, string, string, error) {
+	// Mount the instance's config volume if needed.
+	ourMount, err := vm.mount()
+	if err != nil {
+		return "", "", "", "", err
+	}
+
+	if ourMount {
+		defer vm.unmount()
+	}
+
 	agentCertFile := filepath.Join(vm.Path(), "agent.crt")
 	agentKeyFile := filepath.Join(vm.Path(), "agent.key")
 	clientCertFile := filepath.Join(vm.Path(), "agent-client.crt")
 	clientKeyFile := filepath.Join(vm.Path(), "agent-client.key")
 
 	// Create server certificate.
-	err := shared.FindOrGenCert(agentCertFile, agentKeyFile, false, false)
+	err = shared.FindOrGenCert(agentCertFile, agentKeyFile, false, false)
 	if err != nil {
 		return "", "", "", "", err
 	}
@@ -390,6 +452,7 @@ func (vm *vmQemu) Shutdown(timeout time.Duration) error {
 	vm.cleanupDevices()
 	os.Remove(vm.pidFilePath())
 	os.Remove(vm.getMonitorPath())
+	vm.unmount()
 
 	return nil
 }
@@ -413,6 +476,12 @@ func (vm *vmQemu) Start(stateful bool) error {
 		return fmt.Errorf("The instance is already running")
 	}
 
+	// Mount the instance's config volume.
+	_, err = vm.mount()
+	if err != nil {
+		return err
+	}
+
 	err = vm.generateConfigShare()
 	if err != nil {
 		return err
@@ -629,12 +698,25 @@ func (vm *vmQemu) getNvramPath() string {
 	return filepath.Join(vm.Path(), "qemu.nvram")
 }
 
+// generateConfigShare generates the config share directory that will be exported to the VM via
+// a 9P share. Due to the unknown size of templates inside the images this directory is created
+// inside the VM's config volume so that it can be restricted by quota.
 func (vm *vmQemu) generateConfigShare() error {
+	// Mount the instance's config volume if needed.
+	ourMount, err := vm.mount()
+	if err != nil {
+		return err
+	}
+
+	if ourMount {
+		defer vm.unmount()
+	}
+
 	configDrivePath := filepath.Join(vm.Path(), "config")
 
 	// Create config drive dir.
 	os.RemoveAll(configDrivePath)
-	err := os.MkdirAll(configDrivePath, 0500)
+	err = os.MkdirAll(configDrivePath, 0500)
 	if err != nil {
 		return err
 	}
@@ -804,6 +886,7 @@ echo "To start it now, unmount this filesystem and run: systemctl start lxd-agen
 }
 
 // generateQemuConfigFile writes the qemu config file and returns its location.
+// It writes the config file inside the VM's log path.
 func (vm *vmQemu) generateQemuConfigFile(devConfs []*deviceConfig.RunConfig) (string, error) {
 	var sb *strings.Builder = &strings.Builder{}
 
@@ -1073,7 +1156,7 @@ mount_tag = "config"
 
 // addRootDriveConfig adds the qemu config required for adding the root drive.
 func (vm *vmQemu) addRootDriveConfig(sb *strings.Builder) error {
-	pool, err := storagePools.GetPoolByInstance(vm.state, vm)
+	pool, err := vm.getStoragePool()
 	if err != nil {
 		return err
 	}
@@ -1250,6 +1333,7 @@ func (vm *vmQemu) Stop(stateful bool) error {
 	vm.cleanupDevices()
 	os.Remove(vm.pidFilePath())
 	os.Remove(vm.getMonitorPath())
+	vm.unmount()
 
 	return nil
 }
@@ -1830,7 +1914,7 @@ func (vm *vmQemu) Delete() error {
 	isImport := false
 
 	// Attempt to initialize storage interface for the instance.
-	pool, err := storagePools.GetPoolByInstance(vm.state, vm)
+	pool, err := vm.getStoragePool()
 	if err != nil && err != db.ErrNoSuchObject {
 		// Because of the way vmQemuCreate creates the storage volume record before loading
 		// the storage pool driver, Delete() may be called as part of a revertion if the
@@ -2007,7 +2091,12 @@ func (vm *vmQemu) FileExists(path string) error {
 }
 
 func (vm *vmQemu) FilePull(srcPath string, dstPath string) (int64, int64, os.FileMode, string, []string, error) {
-	agent, err := lxdClient.ConnectLXDHTTP(nil, vm.agentClient)
+	client, err := vm.getAgentClient()
+	if err != nil {
+		return 0, 0, 0, "", nil, err
+	}
+
+	agent, err := lxdClient.ConnectLXDHTTP(nil, client)
 	if err != nil {
 		logger.Errorf("Failed to connect to lxd-agent on %s: %v", vm.Name(), err)
 		return 0, 0, 0, "", nil, fmt.Errorf("Failed to connect to lxd-agent")
@@ -2045,7 +2134,12 @@ func (vm *vmQemu) FilePull(srcPath string, dstPath string) (int64, int64, os.Fil
 }
 
 func (vm *vmQemu) FilePush(fileType string, srcPath string, dstPath string, uid int64, gid int64, mode int, write string) error {
-	agent, err := lxdClient.ConnectLXDHTTP(nil, vm.agentClient)
+	client, err := vm.getAgentClient()
+	if err != nil {
+		return err
+	}
+
+	agent, err := lxdClient.ConnectLXDHTTP(nil, client)
 	if err != nil {
 		logger.Errorf("Failed to connect to lxd-agent on %s: %v", vm.Name(), err)
 		return fmt.Errorf("Failed to connect to lxd-agent")
@@ -2185,7 +2279,12 @@ func (vm *vmQemu) Exec(command []string, env map[string]string, stdin *os.File,
 		}
 	}()
 
-	agent, err := lxdClient.ConnectLXDHTTP(nil, vm.agentClient)
+	client, err := vm.getAgentClient()
+	if err != nil {
+		return nil, err
+	}
+
+	agent, err := lxdClient.ConnectLXDHTTP(nil, client)
 	if err != nil {
 		logger.Errorf("Failed to connect to lxd-agent on %s: %v", vm.Name(), err)
 		return nil, fmt.Errorf("Failed to connect to lxd-agent")
@@ -2461,7 +2560,12 @@ func (vm *vmQemu) agentGetState() (*api.InstanceState, error) {
 		return nil, err
 	}
 
-	agent, err := lxdClient.ConnectLXDHTTP(nil, vm.agentClient)
+	client, err := vm.getAgentClient()
+	if err != nil {
+		return nil, err
+	}
+
+	agent, err := lxdClient.ConnectLXDHTTP(nil, client)
 	if err != nil {
 		return nil, err
 	}


More information about the lxc-devel mailing list