[lxc-devel] [lxd/master] When no agent, use DHCP leases for VM IP address

stgraber on Github lxc-bot at linuxcontainers.org
Wed Nov 20 21:41:58 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191120/ed3203c1/attachment.bin>
-------------- next part --------------
From 2a5182f52015de80678532a46ec2ae7db02848f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 20 Nov 2019 14:54:37 -0500
Subject: [PATCH 1/4] lxd/storage: Rename storagePoolVolumeUsedByContainersGet
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/storage.go               |  2 +-
 lxd/storage_btrfs.go         |  2 +-
 lxd/storage_ceph.go          |  2 +-
 lxd/storage_cephfs.go        |  2 +-
 lxd/storage_dir.go           |  2 +-
 lxd/storage_lvm.go           |  2 +-
 lxd/storage_volumes_utils.go | 10 +++++-----
 lxd/storage_zfs.go           |  2 +-
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/lxd/storage.go b/lxd/storage.go
index 163bff5435..138f7d144f 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -488,7 +488,7 @@ func storagePoolVolumeAttachInit(s *state.State, poolName string, volumeName str
 		logger.Debugf("Shifting storage volume")
 
 		if !shared.IsTrue(poolVolumePut.Config["security.shifted"]) {
-			volumeUsedBy, err := storagePoolVolumeUsedByContainersGet(s, "default", poolName, volumeName)
+			volumeUsedBy, err := storagePoolVolumeUsedByInstancesGet(s, "default", poolName, volumeName)
 			if err != nil {
 				return nil, err
 			}
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 1d31793083..99ef097b09 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -747,7 +747,7 @@ func (s *storageBtrfs) StoragePoolVolumeRename(newName string) error {
 		return err
 	}
 
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index 20d619fe22..45a21a4f19 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -675,7 +675,7 @@ func (s *storageCeph) StoragePoolVolumeRename(newName string) error {
 		return err
 	}
 
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage_cephfs.go b/lxd/storage_cephfs.go
index b0b5622297..495165b75d 100644
--- a/lxd/storage_cephfs.go
+++ b/lxd/storage_cephfs.go
@@ -588,7 +588,7 @@ func (s *storageCephFs) StoragePoolVolumeRename(newName string) error {
 	logger.Infof(`Renaming CEPHFS storage volume on storage pool "%s" from "%s" to "%s`, s.pool.Name, s.volume.Name, newName)
 
 	// Sanity check
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 9355713cc2..71073bb63d 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -468,7 +468,7 @@ func (s *storageDir) StoragePoolVolumeRename(newName string) error {
 		return err
 	}
 
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index ce7aa01137..2f0166193d 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -885,7 +885,7 @@ func (s *storageLvm) StoragePoolVolumeRename(newName string) error {
 		return err
 	}
 
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/storage_volumes_utils.go b/lxd/storage_volumes_utils.go
index 2774cd4b30..464b90a0c7 100644
--- a/lxd/storage_volumes_utils.go
+++ b/lxd/storage_volumes_utils.go
@@ -214,13 +214,13 @@ func storagePoolVolumeUpdate(state *state.State, poolName string, volumeName str
 	return nil
 }
 
-func storagePoolVolumeUsedByContainersGet(s *state.State, project, poolName string, volumeName string) ([]string, error) {
+func storagePoolVolumeUsedByInstancesGet(s *state.State, project, poolName string, volumeName string) ([]string, error) {
 	insts, err := instanceLoadByProject(s, project)
 	if err != nil {
 		return []string{}, err
 	}
 
-	ctsUsingVolume := []string{}
+	instUsingVolume := []string{}
 	for _, inst := range insts {
 		for _, dev := range inst.LocalDevices() {
 			if dev["type"] != "disk" {
@@ -228,13 +228,13 @@ func storagePoolVolumeUsedByContainersGet(s *state.State, project, poolName stri
 			}
 
 			if dev["pool"] == poolName && dev["source"] == volumeName {
-				ctsUsingVolume = append(ctsUsingVolume, inst.Name())
+				instUsingVolume = append(instUsingVolume, inst.Name())
 				break
 			}
 		}
 	}
 
-	return ctsUsingVolume, nil
+	return instUsingVolume, nil
 }
 
 func storagePoolVolumeUpdateUsers(d *Daemon, oldPoolName string,
@@ -436,7 +436,7 @@ func storagePoolVolumeUsedByGet(s *state.State, project, poolName string, volume
 	}
 
 	// Look for containers using this volume
-	ctsUsingVolume, err := storagePoolVolumeUsedByContainersGet(s, project, poolName, volumeName)
+	ctsUsingVolume, err := storagePoolVolumeUsedByInstancesGet(s, project, poolName, volumeName)
 	if err != nil {
 		return []string{}, err
 	}
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 26c5b4af2d..a19e435b4a 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -729,7 +729,7 @@ func (s *storageZfs) StoragePoolVolumeRename(newName string) error {
 	logger.Infof(`Renaming ZFS storage volume on storage pool "%s" from "%s" to "%s`,
 		s.pool.Name, s.volume.Name, newName)
 
-	usedBy, err := storagePoolVolumeUsedByContainersGet(s.s, "default", s.pool.Name, s.volume.Name)
+	usedBy, err := storagePoolVolumeUsedByInstancesGet(s.s, "default", s.pool.Name, s.volume.Name)
 	if err != nil {
 		return err
 	}

From 3eba8b03cff2e534e4029eaacfe3d27d774dc239 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 20 Nov 2019 14:54:57 -0500
Subject: [PATCH 2/4] lxd/storage: Rename
 storagePoolVolumeUsedByRunningInstancesWithProfilesGet
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/storage_volumes.go       |  4 ++--
 lxd/storage_volumes_utils.go | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go
index 0e04c9b611..9c8954091d 100644
--- a/lxd/storage_volumes.go
+++ b/lxd/storage_volumes.go
@@ -596,7 +596,7 @@ func storagePoolVolumeTypePost(d *Daemon, r *http.Request, volumeTypeName string
 	}
 
 	// Check if a running container is using it.
-	ctsUsingVolume, err := storagePoolVolumeUsedByRunningContainersWithProfilesGet(d.State(), poolName, volumeName, volumeTypeName, true)
+	ctsUsingVolume, err := storagePoolVolumeUsedByRunningInstancesWithProfilesGet(d.State(), poolName, volumeName, volumeTypeName, true)
 	if err != nil {
 		return response.SmartError(err)
 	}
@@ -1010,7 +1010,7 @@ func storagePoolVolumeTypePut(d *Daemon, r *http.Request, volumeTypeName string)
 	} else {
 
 		if req.Restore != "" {
-			ctsUsingVolume, err := storagePoolVolumeUsedByRunningContainersWithProfilesGet(d.State(), poolName, vol.Name, storagePoolVolumeTypeNameCustom, true)
+			ctsUsingVolume, err := storagePoolVolumeUsedByRunningInstancesWithProfilesGet(d.State(), poolName, vol.Name, storagePoolVolumeTypeNameCustom, true)
 			if err != nil {
 				return response.InternalError(err)
 			}
diff --git a/lxd/storage_volumes_utils.go b/lxd/storage_volumes_utils.go
index 464b90a0c7..dd5a672b34 100644
--- a/lxd/storage_volumes_utils.go
+++ b/lxd/storage_volumes_utils.go
@@ -43,7 +43,7 @@ var supportedVolumeTypesExceptImages = []int{storagePoolVolumeTypeContainer, sto
 var supportedVolumeTypes = append(supportedVolumeTypesExceptImages, storagePoolVolumeTypeImage)
 
 func init() {
-	storagePools.VolumeUsedByInstancesWithProfiles = storagePoolVolumeUsedByRunningContainersWithProfilesGet
+	storagePools.VolumeUsedByInstancesWithProfiles = storagePoolVolumeUsedByRunningInstancesWithProfilesGet
 }
 
 func storagePoolVolumeTypeNameToAPIEndpoint(volumeTypeName string) (string, error) {
@@ -178,7 +178,7 @@ func storagePoolVolumeUpdate(state *state.State, poolName string, volumeName str
 
 	// Confirm that no containers are running when changing shifted state
 	if newConfig["security.shifted"] != oldConfig["security.shifted"] {
-		ctsUsingVolume, err := storagePoolVolumeUsedByRunningContainersWithProfilesGet(state, poolName, volumeName, storagePoolVolumeTypeNameCustom, true)
+		ctsUsingVolume, err := storagePoolVolumeUsedByRunningInstancesWithProfilesGet(state, poolName, volumeName, storagePoolVolumeTypeNameCustom, true)
 		if err != nil {
 			return err
 		}
@@ -371,7 +371,7 @@ func storagePoolVolumeUpdateUsers(d *Daemon, oldPoolName string,
 	return nil
 }
 
-func storagePoolVolumeUsedByRunningContainersWithProfilesGet(s *state.State,
+func storagePoolVolumeUsedByRunningInstancesWithProfilesGet(s *state.State,
 	poolName string, volumeName string, volumeTypeName string,
 	runningOnly bool) ([]string, error) {
 	insts, err := instanceLoadAll(s)
@@ -379,7 +379,7 @@ func storagePoolVolumeUsedByRunningContainersWithProfilesGet(s *state.State,
 		return []string{}, err
 	}
 
-	ctsUsingVolume := []string{}
+	instUsingVolume := []string{}
 	volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName)
 	for _, inst := range insts {
 		if runningOnly && !inst.IsRunning() {
@@ -399,12 +399,12 @@ func storagePoolVolumeUsedByRunningContainersWithProfilesGet(s *state.State,
 			// "container////bla" but only against "container/bla".
 			cleanSource := filepath.Clean(dev["source"])
 			if cleanSource == volumeName || cleanSource == volumeNameWithType {
-				ctsUsingVolume = append(ctsUsingVolume, inst.Name())
+				instUsingVolume = append(instUsingVolume, inst.Name())
 			}
 		}
 	}
 
-	return ctsUsingVolume, nil
+	return instUsingVolume, nil
 }
 
 // volumeUsedBy = append(volumeUsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))

From 6cc59b1729a06008a646090a2aa1a1796dc24e60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 20 Nov 2019 14:56:27 -0500
Subject: [PATCH 3/4] lxd: Have instanceLoadByProject return all instances
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container.go     | 2 +-
 lxd/container_lxc.go | 4 ++++
 lxd/networks.go      | 5 +++++
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/lxd/container.go b/lxd/container.go
index 55aaed404c..1b25779c21 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1172,7 +1172,7 @@ func instanceLoadByProject(s *state.State, project string) ([]Instance, error) {
 	err := s.Cluster.Transaction(func(tx *db.ClusterTx) error {
 		filter := db.InstanceFilter{
 			Project: project,
-			Type:    instancetype.Container,
+			Type:    instancetype.Any,
 		}
 		var err error
 		cts, err = tx.InstanceList(filter)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 1c800c0b08..9cd60ca5ae 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -745,6 +745,10 @@ func findIdmap(state *state.State, cName string, isolatedStr string, configBase
 
 	mapentries := idmap.ByHostid{}
 	for _, container := range cts {
+		if container.Type() != instancetype.Container {
+			continue
+		}
+
 		name := container.Name()
 
 		/* Don't change our map Just Because. */
diff --git a/lxd/networks.go b/lxd/networks.go
index dfcb831a65..e94f8e879f 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -735,6 +735,11 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
 					if err != nil {
 						continue
 					}
+				} else if inst.Type() == instancetype.VM {
+					d, err = inst.(*vmQemu).fillNetworkDevice(k, d)
+					if err != nil {
+						continue
+					}
 				}
 
 				// Record the MAC

From 252776eecc29b58ef1f40e14b584978ec2823b71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 20 Nov 2019 16:40:24 -0500
Subject: [PATCH 4/4] lxd/vm: Use leases to get IP
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/networks.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
 lxd/vm_qemu.go  | 59 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/lxd/networks.go b/lxd/networks.go
index e94f8e879f..9505777088 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -863,6 +863,77 @@ func networkLeasesGet(d *Daemon, r *http.Request) response.Response {
 	return response.SyncResponse(true, leases)
 }
 
+func networkGetLeaseAddresses(s *state.State, network string, hwaddr string) ([]api.InstanceStateNetworkAddress, error) {
+	addresses := []api.InstanceStateNetworkAddress{}
+
+	leaseFile := shared.VarPath("networks", network, "dnsmasq.leases")
+	if !shared.PathExists(leaseFile) {
+		return addresses, nil
+	}
+
+	dbInfo, err := networkLoadByName(s, network)
+	if err != nil {
+		return nil, err
+	}
+
+	content, err := ioutil.ReadFile(leaseFile)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, lease := range strings.Split(string(content), "\n") {
+		fields := strings.Fields(lease)
+		if len(fields) < 5 {
+			continue
+		}
+
+		// Parse the MAC
+		mac := networkGetMacSlice(fields[1])
+		macStr := strings.Join(mac, ":")
+
+		if len(macStr) < 17 && fields[4] != "" {
+			macStr = fields[4][len(fields[4])-17:]
+		}
+
+		if macStr != hwaddr {
+			continue
+		}
+
+		// Parse the IP
+		addr := api.InstanceStateNetworkAddress{
+			Address: fields[2],
+			Scope:   "global",
+		}
+
+		ip := net.ParseIP(addr.Address)
+		if ip == nil {
+			continue
+		}
+
+		if ip.To4() != nil {
+			addr.Family = "inet"
+
+			_, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv4.address"])
+			if subnet != nil {
+				mask, _ := subnet.Mask.Size()
+				addr.Netmask = fmt.Sprintf("%d", mask)
+			}
+		} else {
+			addr.Family = "inet6"
+
+			_, subnet, _ := net.ParseCIDR(dbInfo.Config()["ipv6.address"])
+			if subnet != nil {
+				mask, _ := subnet.Mask.Size()
+				addr.Netmask = fmt.Sprintf("%d", mask)
+			}
+		}
+
+		addresses = append(addresses, addr)
+	}
+
+	return addresses, nil
+}
+
 // The network structs and functions
 func networkLoadByName(s *state.State, name string) (*network, error) {
 	id, dbInfo, err := s.Cluster.NetworkGet(name)
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index 00bf6366c0..19781a3d2f 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"net"
 	"net/http"
 	"os"
 	"os/exec"
@@ -2376,13 +2377,61 @@ func (vm *vmQemu) RenderState() (*api.InstanceState, error) {
 		status, err := vm.agentGetState()
 		if err != nil {
 			logger.Warn("Could not get VM state from agent", log.Ctx{"project": vm.Project(), "instance": vm.Name(), "err": err})
-		} else {
-			status.Pid = int64(pid)
-			status.Status = statusCode.String()
-			status.StatusCode = statusCode
+			status = &api.InstanceState{}
+			status.Processes = -1
+
+			networks := map[string]api.InstanceStateNetwork{}
+			for k, m := range vm.ExpandedDevices() {
+				// We only care about nics.
+				if m["type"] != "nic" || m["nictype"] != "bridged" {
+					continue
+				}
+
+				// Fill the MAC address.
+				m, err := vm.fillNetworkDevice(k, m)
+				if err != nil {
+					return nil, err
+				}
+
+				// Parse the lease file.
+				addresses, err := networkGetLeaseAddresses(vm.state, m["parent"], m["hwaddr"])
+				if err != nil {
+					return nil, err
+				}
+
+				if len(addresses) == 0 {
+					continue
+				}
 
-			return status, nil
+				// Get MTU.
+				iface, err := net.InterfaceByName(m["parent"])
+				if err != nil {
+					return nil, err
+				}
+
+				if m["host_name"] == "" {
+					m["host_name"] = vm.localConfig[fmt.Sprintf("volatile.%s.host_name", k)]
+				}
+
+				networks[k] = api.InstanceStateNetwork{
+					Addresses: addresses,
+					Counters:  api.InstanceStateNetworkCounters(shared.NetworkGetCounters(m["host_name"])),
+					Hwaddr:    m["hwaddr"],
+					HostName:  m["host_name"],
+					Mtu:       iface.MTU,
+					State:     "up",
+					Type:      "broadcast",
+				}
+			}
+
+			status.Network = networks
 		}
+
+		status.Pid = int64(pid)
+		status.Status = statusCode.String()
+		status.StatusCode = statusCode
+
+		return status, nil
 	}
 
 	// At least return the Status and StatusCode if we couldn't get any


More information about the lxc-devel mailing list