[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