The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6493
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) === - 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.parr...@canonical.com>
From c964e97f07657362328342ce3cf6304ed3e0f972 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@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.parr...@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 }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel