[lxc-devel] [lxd/master] Network: Adds Network interface and network type concept
tomponline on Github
lxc-bot at linuxcontainers.org
Wed Jul 15 10:23:18 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 553 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200715/b91378d0/attachment-0001.bin>
-------------- next part --------------
From 6eaef0050085ac5be1e1ab9dbffa8b30ee74a892 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 17:15:04 +0100
Subject: [PATCH 01/17] lxc/network: Adds flagType to cmdNetwork
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxc/network.go | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lxc/network.go b/lxc/network.go
index e86e50600b..0b993bca45 100644
--- a/lxc/network.go
+++ b/lxc/network.go
@@ -23,6 +23,7 @@ type cmdNetwork struct {
global *cmdGlobal
flagTarget string
+ flagType string
}
func (c *cmdNetwork) Command() *cobra.Command {
@@ -251,10 +252,11 @@ func (c *cmdNetworkCreate) Command() *cobra.Command {
cmd := &cobra.Command{}
cmd.Use = i18n.G("create [<remote>:]<network> [key=value...]")
cmd.Short = i18n.G("Create new networks")
- cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
- `Create new networks`))
+ cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(`Create new networks`))
cmd.Flags().StringVar(&c.network.flagTarget, "target", "", i18n.G("Cluster member name")+"``")
+ cmd.Flags().StringVarP(&c.network.flagType, "type", "t", "bridge", i18n.G("Network type"))
+
cmd.RunE = c.Run
return cmd
@@ -280,6 +282,7 @@ func (c *cmdNetworkCreate) Run(cmd *cobra.Command, args []string) error {
network := api.NetworksPost{}
network.Name = resource.name
network.Config = map[string]string{}
+ network.Type = c.network.flagType
for i := 1; i < len(args); i++ {
entry := strings.SplitN(args[i], "=", 2)
From 4bb584aadce9adf7c1ba308dea3b9182a8e5b67d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 17:15:52 +0100
Subject: [PATCH 02/17] lxd/networks: Validate network types
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/networks.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lxd/networks.go b/lxd/networks.go
index d4fe8982ec..c0b99a0de6 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -113,8 +113,10 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
return response.BadRequest(err)
}
- if req.Type != "" && req.Type != "bridge" {
- return response.BadRequest(fmt.Errorf("Only 'bridge' type networks can be created"))
+ validNetworkTypes := []string{"bridge", "macvlan"}
+
+ if req.Type != "" && !shared.StringInSlice(req.Type, validNetworkTypes) {
+ return response.BadRequest(fmt.Errorf("Invalid network type specified. Must be one of: %s", strings.Join(validNetworkTypes, ", ")))
}
if req.Config == nil {
From 0867d33b8b995eb1dd4e60b2b23726b64b5c384c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 18:04:26 +0100
Subject: [PATCH 03/17] lxd/db/cluster: Adds type field to networks table
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/cluster/schema.go | 3 ++-
lxd/db/cluster/update.go | 11 +++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index e107b8885e..701847d41a 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -281,6 +281,7 @@ CREATE TABLE networks (
name TEXT NOT NULL,
description TEXT,
state INTEGER NOT NULL DEFAULT 0,
+ type INTEGER NOT NULL DEFAULT 0,
UNIQUE (name)
);
CREATE TABLE networks_config (
@@ -571,5 +572,5 @@ CREATE TABLE storage_volumes_snapshots_config (
UNIQUE (storage_volume_snapshot_id, key)
);
-INSERT INTO schema (version, updated_at) VALUES (32, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (33, strftime("%s"))
`
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index 7e721587d7..c6fd84cbfb 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -69,6 +69,17 @@ var updates = map[int]schema.Update{
30: updateFromV29,
31: updateFromV30,
32: updateFromV31,
+ 33: updateFromV32,
+}
+
+// Add type field to networks.
+func updateFromV32(tx *sql.Tx) error {
+ _, err := tx.Exec("ALTER TABLE networks ADD COLUMN type INTEGER NOT NULL DEFAULT 0;")
+ if err != nil {
+ return errors.Wrap(err, "Failed to add type column to networks table")
+ }
+
+ return nil
}
// Add failure_domain column to nodes table.
From 8714b47504dcdde29a0926051a0995a437a8814a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 18:27:46 +0100
Subject: [PATCH 04/17] lxd/db/networks: Adds internal network type constants
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/networks.go | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index ce8fcd34d0..70780230ac 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -297,6 +297,14 @@ const (
networkErrored // Network creation failed on some nodes
)
+// NetworkType indicates type of network.
+type NetworkType int
+
+// Network types.
+const (
+ NetworkTypeBridge NetworkType = iota // Network type bridge.
+)
+
// GetNetwork returns the network with the given name.
//
// The network must be in the created stated, not pending.
From eae8e6032072865da7353b998603b07891dd92de Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 18:28:02 +0100
Subject: [PATCH 05/17] lxd/db/networks: Updates CreateNetwork to accept a
network type
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/networks.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index 70780230ac..d5a3d35f80 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -485,10 +485,10 @@ func (c *Cluster) getNetworkConfig(id int64) (map[string]string, error) {
}
// CreateNetwork creates a new network.
-func (c *Cluster) CreateNetwork(name, description string, config map[string]string) (int64, error) {
+func (c *Cluster) CreateNetwork(name, description string, netType NetworkType, config map[string]string) (int64, error) {
var id int64
err := c.Transaction(func(tx *ClusterTx) error {
- result, err := tx.tx.Exec("INSERT INTO networks (name, description, state) VALUES (?, ?, ?)", name, description, networkCreated)
+ result, err := tx.tx.Exec("INSERT INTO networks (name, description, state, type) VALUES (?, ?, ?, ?)", name, description, networkCreated, netType)
if err != nil {
return err
}
From 6e4cf25dc3b485ceb560d744bdb42bc049138953 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 18:47:59 +0100
Subject: [PATCH 06/17] lxd/db/networks: Updates CreatePendingNetwork to accept
a network type
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/networks.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index d5a3d35f80..fe876c0fd8 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -149,7 +149,7 @@ WHERE networks.id = ? AND networks.state = ?
// CreatePendingNetwork creates a new pending network on the node with
// the given name.
-func (c *ClusterTx) CreatePendingNetwork(node, name string, conf map[string]string) error {
+func (c *ClusterTx) CreatePendingNetwork(node, name string, netType NetworkType, conf map[string]string) error {
// First check if a network with the given name exists, and, if
// so, that it's in the pending state.
network := struct {
@@ -182,8 +182,8 @@ func (c *ClusterTx) CreatePendingNetwork(node, name string, conf map[string]stri
if networkID == 0 {
// No existing network with the given name was found, let's create
// one.
- columns := []string{"name"}
- values := []interface{}{name}
+ columns := []string{"name", "type"}
+ values := []interface{}{name, netType}
networkID, err = query.UpsertObject(c.tx, "networks", columns, values)
if err != nil {
return err
From 1853ad6c5db6ee08574a9f0729dc683803f9eb43 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 10:58:39 +0100
Subject: [PATCH 07/17] lxd/db/networks: Populate network type in getNetwork
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/networks.go | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/lxd/db/networks.go b/lxd/db/networks.go
index fe876c0fd8..36ec050739 100644
--- a/lxd/db/networks.go
+++ b/lxd/db/networks.go
@@ -325,10 +325,11 @@ func (c *Cluster) getNetwork(name string, onlyCreated bool) (int64, *api.Network
description := sql.NullString{}
id := int64(-1)
state := 0
+ var netType NetworkType
- q := "SELECT id, description, state FROM networks WHERE name=?"
+ q := "SELECT id, description, state, type FROM networks WHERE name=?"
arg1 := []interface{}{name}
- arg2 := []interface{}{&id, &description, &state}
+ arg2 := []interface{}{&id, &description, &state, &netType}
if onlyCreated {
q += " AND state=?"
arg1 = append(arg1, networkCreated)
@@ -350,7 +351,6 @@ func (c *Cluster) getNetwork(name string, onlyCreated bool) (int64, *api.Network
network := api.Network{
Name: name,
Managed: true,
- Type: "bridge",
}
network.Description = description.String
network.Config = config
@@ -366,6 +366,13 @@ func (c *Cluster) getNetwork(name string, onlyCreated bool) (int64, *api.Network
network.Status = "Unknown"
}
+ switch netType {
+ case NetworkTypeBridge:
+ network.Type = "bridge"
+ default:
+ network.Type = "bridge"
+ }
+
nodes, err := c.networkNodes(id)
if err != nil {
return -1, nil, err
From 7762c2ff847b989132b03ab166e373c4fcb45b6d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 14 Jul 2020 18:28:23 +0100
Subject: [PATCH 08/17] lxd/networks: Converts and records network type in
networksPost
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/networks.go | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/lxd/networks.go b/lxd/networks.go
index c0b99a0de6..1e99531eaf 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -113,12 +113,20 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
return response.BadRequest(err)
}
- validNetworkTypes := []string{"bridge", "macvlan"}
+ validNetworkTypes := []string{"bridge"}
if req.Type != "" && !shared.StringInSlice(req.Type, validNetworkTypes) {
return response.BadRequest(fmt.Errorf("Invalid network type specified. Must be one of: %s", strings.Join(validNetworkTypes, ", ")))
}
+ var netType db.NetworkType
+ switch req.Type {
+ case "bridge":
+ netType = db.NetworkTypeBridge
+ default:
+ netType = db.NetworkTypeBridge
+ }
+
if req.Config == nil {
req.Config = map[string]string{}
}
@@ -153,7 +161,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
}
}
err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
- return tx.CreatePendingNetwork(targetNode, req.Name, req.Config)
+ return tx.CreatePendingNetwork(targetNode, req.Name, netType, req.Config)
})
if err != nil {
if err == db.ErrAlreadyDefined {
@@ -197,7 +205,7 @@ func networksPost(d *Daemon, r *http.Request) response.Response {
}
// Create the database entry
- _, err = d.cluster.CreateNetwork(req.Name, req.Description, req.Config)
+ _, err = d.cluster.CreateNetwork(req.Name, req.Description, netType, req.Config)
if err != nil {
return response.SmartError(fmt.Errorf("Error inserting %s into database: %s", req.Name, err))
}
From 8abb9987ede1ffce2c48f72d249728e521651b11 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:04:20 +0100
Subject: [PATCH 09/17] lxd/network/network/interface: Adds network interface
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/network_interface.go | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 lxd/network/network_interface.go
diff --git a/lxd/network/network_interface.go b/lxd/network/network_interface.go
new file mode 100644
index 0000000000..3613fae6ad
--- /dev/null
+++ b/lxd/network/network_interface.go
@@ -0,0 +1,31 @@
+package network
+
+import (
+ "github.com/lxc/lxd/lxd/cluster"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/shared/api"
+)
+
+// Network represents a LXD network.
+type Network interface {
+ // Load.
+ init(state *state.State, id int64, dbInfo *api.Network)
+
+ // Config.
+ Name() string
+ Type() string
+ Config() map[string]string
+ IsUsed() bool
+ HasDHCPv4() bool
+ HasDHCPv6() bool
+ DHCPv4Ranges() []DHCPRange
+ DHCPv6Ranges() []DHCPRange
+
+ // Actions.
+ Start() error
+ Stop() error
+ Rename(name string) error
+ Update(newNetwork api.NetworkPut, notify bool) error
+ HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
+ Delete(withDatabase bool) error
+}
From 2d1767ddc79433e4f8d37355544b0ffbcbfd2556 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 10:59:33 +0100
Subject: [PATCH 10/17] lxd/network/network: Renames network to driver_bridge
Makes way for new Network interface.
Updates bridge implementation to operate as an implementation of the Network interface
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/{network.go => driver_bridge.go} | 165 ++++---------------
1 file changed, 31 insertions(+), 134 deletions(-)
rename lxd/network/{network.go => driver_bridge.go} (90%)
diff --git a/lxd/network/network.go b/lxd/network/driver_bridge.go
similarity index 90%
rename from lxd/network/network.go
rename to lxd/network/driver_bridge.go
index 6e2fcd3dd6..49d9bd9b44 100644
--- a/lxd/network/network.go
+++ b/lxd/network/driver_bridge.go
@@ -18,9 +18,7 @@ import (
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/daemon"
"github.com/lxc/lxd/lxd/dnsmasq"
- "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/node"
- "github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -38,60 +36,20 @@ const ForkdnsServersListFile = "servers.conf"
var forkdnsServersLock sync.Mutex
-// DHCPRange represents a range of IPs from start to end.
-type DHCPRange struct {
- Start net.IP
- End net.IP
-}
-
-// Network represents a LXD network.
-type Network struct {
- // Properties
- state *state.State
- id int64
- name string
- description string
-
- // config
- config map[string]string
-}
-
-// Name returns the network name.
-func (n *Network) Name() string {
- return n.name
-}
-
-// Config returns the network config.
-func (n *Network) Config() map[string]string {
- return n.config
+// bridge represents a LXD bridge network.
+type bridge struct {
+ common
}
// IsRunning returns whether the network is up.
-func (n *Network) IsRunning() bool {
+func (n *bridge) isRunning() bool {
return shared.PathExists(fmt.Sprintf("/sys/class/net/%s", n.name))
}
-// IsUsed returns whether the network is used by any instances.
-func (n *Network) IsUsed() bool {
- // Look for instances using the interface
- insts, err := instance.LoadFromAllProjects(n.state)
- if err != nil {
- return true
- }
-
- for _, inst := range insts {
- if IsInUseByInstance(inst, n.name) {
- return true
- }
- }
-
- return false
-}
-
// Delete deletes a network.
-func (n *Network) Delete(withDatabase bool) error {
+func (n *bridge) Delete(withDatabase bool) error {
// Bring the network down
- if n.IsRunning() {
+ if n.isRunning() {
err := n.Stop()
if err != nil {
return err
@@ -114,14 +72,14 @@ func (n *Network) Delete(withDatabase bool) error {
}
// Rename renames a network.
-func (n *Network) Rename(name string) error {
+func (n *bridge) Rename(name string) error {
// Sanity checks
if n.IsUsed() {
return fmt.Errorf("The network is currently in use")
}
// Bring the network down
- if n.IsRunning() {
+ if n.isRunning() {
err := n.Stop()
if err != nil {
return err
@@ -165,12 +123,12 @@ func (n *Network) Rename(name string) error {
}
// Start starts the network.
-func (n *Network) Start() error {
+func (n *bridge) Start() error {
return n.setup(nil)
}
// setup restarts the network.
-func (n *Network) setup(oldConfig map[string]string) error {
+func (n *bridge) setup(oldConfig map[string]string) error {
// If we are in mock mode, just no-op.
if n.state.OS.MockMode {
return nil
@@ -185,7 +143,7 @@ func (n *Network) setup(oldConfig map[string]string) error {
}
// Create the bridge interface
- if !n.IsRunning() {
+ if !n.isRunning() {
if n.config["bridge.driver"] == "openvswitch" {
_, err := exec.LookPath("ovs-vsctl")
if err != nil {
@@ -1071,9 +1029,9 @@ func (n *Network) setup(oldConfig map[string]string) error {
}
// Stop stops the network.
-func (n *Network) Stop() error {
- if !n.IsRunning() {
- return fmt.Errorf("The network is already stopped")
+func (n *bridge) Stop() error {
+ if !n.isRunning() {
+ return nil
}
// Destroy the bridge interface
@@ -1135,7 +1093,7 @@ func (n *Network) Stop() error {
}
// Update updates the network.
-func (n *Network) Update(newNetwork api.NetworkPut, notify bool) error {
+func (n *bridge) Update(newNetwork api.NetworkPut, notify bool) error {
err := fillAuto(newNetwork.Config)
if err != nil {
return err
@@ -1203,14 +1161,14 @@ func (n *Network) Update(newNetwork api.NetworkPut, notify bool) error {
// Update the network
if !userOnly {
- if shared.StringInSlice("bridge.driver", changedConfig) && n.IsRunning() {
+ if shared.StringInSlice("bridge.driver", changedConfig) && n.isRunning() {
err = n.Stop()
if err != nil {
return err
}
}
- if shared.StringInSlice("bridge.external_interfaces", changedConfig) && n.IsRunning() {
+ if shared.StringInSlice("bridge.external_interfaces", changedConfig) && n.isRunning() {
devices := []string{}
for _, dev := range strings.Split(newConfig["bridge.external_interfaces"], ",") {
dev = strings.TrimSpace(dev)
@@ -1273,7 +1231,7 @@ func (n *Network) Update(newNetwork api.NetworkPut, notify bool) error {
return nil
}
-func (n *Network) spawnForkDNS(listenAddress string) error {
+func (n *bridge) spawnForkDNS(listenAddress string) error {
// Setup the dnsmasq domain
dnsDomain := n.config["dns.domain"]
if dnsDomain == "" {
@@ -1313,9 +1271,9 @@ func (n *Network) spawnForkDNS(listenAddress string) error {
return nil
}
-// RefreshForkdnsServerAddresses retrieves the IPv4 address of each cluster node (excluding ourselves)
+// HandleHeartbeat refreshes forkdns servers. Retrieves the IPv4 address of each cluster node (excluding ourselves)
// for this network. It then updates the forkdns server list file if there are changes.
-func (n *Network) RefreshForkdnsServerAddresses(heartbeatData *cluster.APIHeartbeat) error {
+func (n *bridge) HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error {
addresses := []string{}
localAddress, err := node.HTTPSAddress(n.state.Node)
if err != nil {
@@ -1373,7 +1331,7 @@ func (n *Network) RefreshForkdnsServerAddresses(heartbeatData *cluster.APIHeartb
return nil
}
-func (n *Network) getTunnels() []string {
+func (n *bridge) getTunnels() []string {
tunnels := []string{}
for k := range n.config {
@@ -1391,7 +1349,7 @@ func (n *Network) getTunnels() []string {
}
// bootRoutesV4 returns a list of IPv4 boot routes on the network's device.
-func (n *Network) bootRoutesV4() ([]string, error) {
+func (n *bridge) bootRoutesV4() ([]string, error) {
routes := []string{}
cmd := exec.Command("ip", "-4", "route", "show", "dev", n.name, "proto", "boot")
ipOut, err := cmd.StdoutPipe()
@@ -1409,7 +1367,7 @@ func (n *Network) bootRoutesV4() ([]string, error) {
}
// bootRoutesV6 returns a list of IPv6 boot routes on the network's device.
-func (n *Network) bootRoutesV6() ([]string, error) {
+func (n *bridge) bootRoutesV6() ([]string, error) {
routes := []string{}
cmd := exec.Command("ip", "-6", "route", "show", "dev", n.name, "proto", "boot")
ipOut, err := cmd.StdoutPipe()
@@ -1427,7 +1385,7 @@ func (n *Network) bootRoutesV6() ([]string, error) {
}
// applyBootRoutesV4 applies a list of IPv4 boot routes to the network's device.
-func (n *Network) applyBootRoutesV4(routes []string) error {
+func (n *bridge) applyBootRoutesV4(routes []string) error {
for _, route := range routes {
cmd := []string{"-4", "route", "replace", "dev", n.name, "proto", "boot"}
cmd = append(cmd, strings.Fields(route)...)
@@ -1441,7 +1399,7 @@ func (n *Network) applyBootRoutesV4(routes []string) error {
}
// applyBootRoutesV6 applies a list of IPv6 boot routes to the network's device.
-func (n *Network) applyBootRoutesV6(routes []string) error {
+func (n *bridge) applyBootRoutesV6(routes []string) error {
for _, route := range routes {
cmd := []string{"-6", "route", "replace", "dev", n.name, "proto", "boot"}
cmd = append(cmd, strings.Fields(route)...)
@@ -1454,7 +1412,7 @@ func (n *Network) applyBootRoutesV6(routes []string) error {
return nil
}
-func (n *Network) fanAddress(underlay *net.IPNet, overlay *net.IPNet) (string, string, string, error) {
+func (n *bridge) fanAddress(underlay *net.IPNet, overlay *net.IPNet) (string, string, string, error) {
// Sanity checks
underlaySize, _ := underlay.Mask.Size()
if underlaySize != 16 && underlaySize != 24 {
@@ -1501,7 +1459,7 @@ func (n *Network) fanAddress(underlay *net.IPNet, overlay *net.IPNet) (string, s
return fmt.Sprintf("%s/%d", ipBytes.String(), overlaySize), dev, ipStr, err
}
-func (n *Network) addressForSubnet(subnet *net.IPNet) (net.IP, string, error) {
+func (n *bridge) addressForSubnet(subnet *net.IPNet) (net.IP, string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return net.IP{}, "", err
@@ -1528,7 +1486,7 @@ func (n *Network) addressForSubnet(subnet *net.IPNet) (net.IP, string, error) {
return net.IP{}, "", fmt.Errorf("No address found in subnet")
}
-func (n *Network) killForkDNS() error {
+func (n *bridge) killForkDNS() error {
// Check if we have a running forkdns at all
pidPath := shared.VarPath("networks", n.name, "forkdns.pid")
@@ -1552,7 +1510,7 @@ func (n *Network) killForkDNS() error {
// updateForkdnsServersFile takes a list of node addresses and writes them atomically to
// the forkdns.servers file ready for forkdns to notice and re-apply its config.
-func (n *Network) updateForkdnsServersFile(addresses []string) error {
+func (n *bridge) updateForkdnsServersFile(addresses []string) error {
// We don't want to race with ourselves here
forkdnsServersLock.Lock()
defer forkdnsServersLock.Unlock()
@@ -1585,69 +1543,8 @@ func (n *Network) updateForkdnsServersFile(addresses []string) error {
return nil
}
-// HasDHCPv4 indicates whether the network has DHCPv4 enabled.
-func (n *Network) HasDHCPv4() bool {
- if n.config["ipv4.dhcp"] == "" || shared.IsTrue(n.config["ipv4.dhcp"]) {
- return true
- }
-
- return false
-}
-
-// HasDHCPv6 indicates whether the network has DHCPv6 enabled (includes stateless SLAAC router advertisement mode).
-// Technically speaking stateless SLAAC RA mode isn't DHCPv6, but for consistency with LXD's config paradigm, DHCP
-// here means "an ability to automatically allocate IPs and routes", rather than stateful DHCP with leases.
-// To check if true stateful DHCPv6 is enabled check the "ipv6.dhcp.stateful" config key.
-func (n *Network) HasDHCPv6() bool {
- if n.config["ipv6.dhcp"] == "" || shared.IsTrue(n.config["ipv6.dhcp"]) {
- return true
- }
-
- return false
-}
-
-// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
-func (n *Network) DHCPv4Ranges() []DHCPRange {
- dhcpRanges := make([]DHCPRange, 0)
- if n.config["ipv4.dhcp.ranges"] != "" {
- for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
- parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
- if len(parts) == 2 {
- startIP := net.ParseIP(parts[0])
- endIP := net.ParseIP(parts[1])
- dhcpRanges = append(dhcpRanges, DHCPRange{
- Start: startIP.To4(),
- End: endIP.To4(),
- })
- }
- }
- }
-
- return dhcpRanges
-}
-
-// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
-func (n *Network) DHCPv6Ranges() []DHCPRange {
- dhcpRanges := make([]DHCPRange, 0)
- if n.config["ipv6.dhcp.ranges"] != "" {
- for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
- parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
- if len(parts) == 2 {
- startIP := net.ParseIP(parts[0])
- endIP := net.ParseIP(parts[1])
- dhcpRanges = append(dhcpRanges, DHCPRange{
- Start: startIP.To16(),
- End: endIP.To16(),
- })
- }
- }
- }
-
- return dhcpRanges
-}
-
// HasIPv4Firewall indicates whether the network has IPv4 firewall enabled.
-func (n *Network) HasIPv4Firewall() bool {
+func (n *bridge) HasIPv4Firewall() bool {
if n.config["ipv4.firewall"] == "" || shared.IsTrue(n.config["ipv4.firewall"]) {
return true
}
@@ -1656,7 +1553,7 @@ func (n *Network) HasIPv4Firewall() bool {
}
// HasIPv6Firewall indicates whether the network has IPv6 firewall enabled.
-func (n *Network) HasIPv6Firewall() bool {
+func (n *bridge) HasIPv6Firewall() bool {
if n.config["ipv6.firewall"] == "" || shared.IsTrue(n.config["ipv6.firewall"]) {
return true
}
From 1682160307f1e54ef3afd73ef92e3952fd416e44 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:00:53 +0100
Subject: [PATCH 11/17] lxd/networks: Remove use of network.IsRunning
As not part of new Network interface (as IsRunning is driver dependent) and network.Stop is now expected to return nil if already stopped.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/networks.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/lxd/networks.go b/lxd/networks.go
index 1e99531eaf..e60d0cca24 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -918,10 +918,6 @@ func networkShutdown(s *state.State) error {
return err
}
- if !n.IsRunning() {
- continue
- }
-
err = n.Stop()
if err != nil {
logger.Error("Failed to bring down network", log.Ctx{"err": err, "name": name})
From 10c44099b112666ed551c4d859232caa86e76cad Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:02:02 +0100
Subject: [PATCH 12/17] lxd/network/network/load: Updates LoadByName to use
Network interface
And load appropriate underlying driver.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/network_load.go | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/lxd/network/network_load.go b/lxd/network/network_load.go
index c73a7a26cd..d35217fda6 100644
--- a/lxd/network/network_load.go
+++ b/lxd/network/network_load.go
@@ -4,20 +4,24 @@ import (
"github.com/lxc/lxd/lxd/state"
)
+var drivers = map[string]func() Network{
+ "bridge": func() Network { return &bridge{} },
+}
+
// LoadByName loads the network info from the database by name.
-func LoadByName(s *state.State, name string) (*Network, error) {
+func LoadByName(s *state.State, name string) (Network, error) {
id, dbInfo, err := s.Cluster.GetNetwork(name)
if err != nil {
return nil, err
}
- n := &Network{
- state: s,
- id: id,
- name: name,
- description: dbInfo.Description,
- config: dbInfo.Config,
+ driverFunc, ok := drivers[dbInfo.Type]
+ if !ok {
+ return nil, ErrUnknownDriver
}
+ n := driverFunc()
+ n.init(s, id, dbInfo)
+
return n, nil
}
From af23af4a0a1b22dae7604dd98a1c58e162fa7df0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:02:57 +0100
Subject: [PATCH 13/17] lxd/networks/utils: Updates usage of
n.RefreshForkdnsServerAddresses to generic n.HandleHearbeat
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/networks_utils.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index 5f6a52820f..200d05ba8d 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -164,8 +164,8 @@ func networkUpdateForkdnsServersTask(s *state.State, heartbeatData *cluster.APIH
return err
}
- if n.Config()["bridge.mode"] == "fan" {
- err := n.RefreshForkdnsServerAddresses(heartbeatData)
+ if n.Type() == "bridge" && n.Config()["bridge.mode"] == "fan" {
+ err := n.HandleHeartbeat(heartbeatData)
if err != nil {
return err
}
From 28dc6cfb2d1861274bb7202326936d104ef63c48 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:03:39 +0100
Subject: [PATCH 14/17] lxd/network/driver/common: Adds common driver
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/driver_common.go | 133 +++++++++++++++++++++++++++++++++++
1 file changed, 133 insertions(+)
create mode 100644 lxd/network/driver_common.go
diff --git a/lxd/network/driver_common.go b/lxd/network/driver_common.go
new file mode 100644
index 0000000000..601e2bc520
--- /dev/null
+++ b/lxd/network/driver_common.go
@@ -0,0 +1,133 @@
+package network
+
+import (
+ "net"
+ "strings"
+
+ "github.com/lxc/lxd/lxd/instance"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+// DHCPRange represents a range of IPs from start to end.
+type DHCPRange struct {
+ Start net.IP
+ End net.IP
+}
+
+// common represents a generic LXD network.
+type common struct {
+ // Properties
+ state *state.State
+ id int64
+ name string
+ netType string
+ description string
+
+ // config
+ config map[string]string
+}
+
+// init initialise internal variables.
+func (n *common) init(state *state.State, id int64, dbInfo *api.Network) {
+ n.id = id
+ n.name = dbInfo.Name
+ n.netType = dbInfo.Type
+ n.config = dbInfo.Config
+ n.state = state
+ n.description = dbInfo.Description
+}
+
+// Name returns the network name.
+func (n *common) Name() string {
+ return n.name
+}
+
+// Type returns the network type.
+func (n *common) Type() string {
+ return n.netType
+}
+
+// Config returns the network config.
+func (n *common) Config() map[string]string {
+ return n.config
+}
+
+// IsUsed returns whether the network is used by any instances.
+func (n *common) IsUsed() bool {
+ // Look for instances using the interface
+ insts, err := instance.LoadFromAllProjects(n.state)
+ if err != nil {
+ return true
+ }
+
+ for _, inst := range insts {
+ if IsInUseByInstance(inst, n.name) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// HasDHCPv4 indicates whether the network has DHCPv4 enabled.
+func (n *common) HasDHCPv4() bool {
+ if n.config["ipv4.dhcp"] == "" || shared.IsTrue(n.config["ipv4.dhcp"]) {
+ return true
+ }
+
+ return false
+}
+
+// HasDHCPv6 indicates whether the network has DHCPv6 enabled (includes stateless SLAAC router advertisement mode).
+// Technically speaking stateless SLAAC RA mode isn't DHCPv6, but for consistency with LXD's config paradigm, DHCP
+// here means "an ability to automatically allocate IPs and routes", rather than stateful DHCP with leases.
+// To check if true stateful DHCPv6 is enabled check the "ipv6.dhcp.stateful" config key.
+func (n *common) HasDHCPv6() bool {
+ if n.config["ipv6.dhcp"] == "" || shared.IsTrue(n.config["ipv6.dhcp"]) {
+ return true
+ }
+
+ return false
+}
+
+// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
+func (n *common) DHCPv4Ranges() []DHCPRange {
+ dhcpRanges := make([]DHCPRange, 0)
+ if n.config["ipv4.dhcp.ranges"] != "" {
+ for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
+ parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
+ if len(parts) == 2 {
+ startIP := net.ParseIP(parts[0])
+ endIP := net.ParseIP(parts[1])
+ dhcpRanges = append(dhcpRanges, DHCPRange{
+ Start: startIP.To4(),
+ End: endIP.To4(),
+ })
+ }
+ }
+ }
+
+ return dhcpRanges
+}
+
+// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
+func (n *common) DHCPv6Ranges() []DHCPRange {
+ dhcpRanges := make([]DHCPRange, 0)
+ if n.config["ipv6.dhcp.ranges"] != "" {
+ for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
+ parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
+ if len(parts) == 2 {
+ startIP := net.ParseIP(parts[0])
+ endIP := net.ParseIP(parts[1])
+ dhcpRanges = append(dhcpRanges, DHCPRange{
+ Start: startIP.To16(),
+ End: endIP.To16(),
+ })
+ }
+ }
+ }
+
+ return dhcpRanges
+}
From 42c204652c9549d08dfa576ee9ad96ffbfff3507 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:04:05 +0100
Subject: [PATCH 15/17] lxd/network/errors: Adds error constants
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/errors.go | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 lxd/network/errors.go
diff --git a/lxd/network/errors.go b/lxd/network/errors.go
new file mode 100644
index 0000000000..b68b4bacc6
--- /dev/null
+++ b/lxd/network/errors.go
@@ -0,0 +1,8 @@
+package network
+
+import (
+ "fmt"
+)
+
+// ErrUnknownDriver is the "Unknown driver" error
+var ErrUnknownDriver = fmt.Errorf("Unknown driver")
From b8016e87a65b9fe0e00e9ece8ab780da3e16e0fd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 10:58:09 +0100
Subject: [PATCH 16/17] lxd/device/nic/bridged: Support Network interface
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_bridged.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 2119de405b..622deda0ed 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -659,7 +659,7 @@ func (d *nicBridged) setFilters() (err error) {
// networkAllocateVethFilterIPs retrieves previously allocated IPs, or allocate new ones if needed.
// This function only works with LXD managed networks, and as such, requires the managed network's
// config to be supplied.
-func (d *nicBridged) allocateFilterIPs(n *network.Network) (net.IP, net.IP, error) {
+func (d *nicBridged) allocateFilterIPs(n network.Network) (net.IP, net.IP, error) {
var IPv4, IPv6 net.IP
// Check if there is a valid static IPv4 address defined.
@@ -798,7 +798,7 @@ func (d *nicBridged) networkDHCPValidIP(subnet *net.IPNet, ranges []network.DHCP
// getDHCPFreeIPv4 attempts to find a free IPv4 address for the device.
// It first checks whether there is an existing allocation for the instance.
// If no previous allocation, then a free IP is picked from the ranges configured.
-func (d *nicBridged) getDHCPFreeIPv4(usedIPs map[[4]byte]dnsmasq.DHCPAllocation, n *network.Network, ctName string, deviceMAC string) (net.IP, error) {
+func (d *nicBridged) getDHCPFreeIPv4(usedIPs map[[4]byte]dnsmasq.DHCPAllocation, n network.Network, ctName string, deviceMAC string) (net.IP, error) {
MAC, err := net.ParseMAC(deviceMAC)
if err != nil {
return nil, err
@@ -872,7 +872,7 @@ func (d *nicBridged) getDHCPFreeIPv4(usedIPs map[[4]byte]dnsmasq.DHCPAllocation,
// DHCPv6 stateful mode is enabled without custom ranges, then an EUI64 IP is generated from the
// device's MAC address. Finally if stateful custom ranges are enabled, then a free IP is picked
// from the ranges configured.
-func (d *nicBridged) getDHCPFreeIPv6(usedIPs map[[16]byte]dnsmasq.DHCPAllocation, n *network.Network, ctName string, deviceMAC string) (net.IP, error) {
+func (d *nicBridged) getDHCPFreeIPv6(usedIPs map[[16]byte]dnsmasq.DHCPAllocation, n network.Network, ctName string, deviceMAC string) (net.IP, error) {
netConfig := n.Config()
lxdIP, subnet, err := net.ParseCIDR(netConfig["ipv6.address"])
if err != nil {
From 68d1af76d8503cd0f700d1e9e081492109206c09 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 15 Jul 2020 11:09:12 +0100
Subject: [PATCH 17/17] lxd: Updates network tests to pass netType
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/networks_test.go | 14 +++++++-------
lxd/instance_test.go | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/lxd/db/networks_test.go b/lxd/db/networks_test.go
index 3f83278b62..9624d1c1ce 100644
--- a/lxd/db/networks_test.go
+++ b/lxd/db/networks_test.go
@@ -15,7 +15,7 @@ func TestGetNetworksLocalConfigs(t *testing.T) {
cluster, cleanup := db.NewTestCluster(t)
defer cleanup()
- _, err := cluster.CreateNetwork("lxdbr0", "", map[string]string{
+ _, err := cluster.CreateNetwork("lxdbr0", "", db.NetworkTypeBridge, map[string]string{
"dns.mode": "none",
"bridge.external_interfaces": "vlan0",
})
@@ -45,7 +45,7 @@ func TestCreatePendingNetwork(t *testing.T) {
require.NoError(t, err)
config := map[string]string{"bridge.external_interfaces": "foo"}
- err = tx.CreatePendingNetwork("buzz", "network1", config)
+ err = tx.CreatePendingNetwork("buzz", "network1", db.NetworkTypeBridge, config)
require.NoError(t, err)
networkID, err := tx.GetNetworkID("network1")
@@ -53,7 +53,7 @@ func TestCreatePendingNetwork(t *testing.T) {
assert.True(t, networkID > 0)
config = map[string]string{"bridge.external_interfaces": "bar"}
- err = tx.CreatePendingNetwork("rusp", "network1", config)
+ err = tx.CreatePendingNetwork("rusp", "network1", db.NetworkTypeBridge, config)
require.NoError(t, err)
// The initial node (whose name is 'none' by default) is missing.
@@ -61,7 +61,7 @@ func TestCreatePendingNetwork(t *testing.T) {
require.EqualError(t, err, "Network not defined on nodes: none")
config = map[string]string{"bridge.external_interfaces": "egg"}
- err = tx.CreatePendingNetwork("none", "network1", config)
+ err = tx.CreatePendingNetwork("none", "network1", db.NetworkTypeBridge, config)
require.NoError(t, err)
// Now the storage is defined on all nodes.
@@ -82,10 +82,10 @@ func TestNetworksCreatePending_AlreadyDefined(t *testing.T) {
_, err := tx.CreateNode("buzz", "1.2.3.4:666")
require.NoError(t, err)
- err = tx.CreatePendingNetwork("buzz", "network1", map[string]string{})
+ err = tx.CreatePendingNetwork("buzz", "network1", db.NetworkTypeBridge, map[string]string{})
require.NoError(t, err)
- err = tx.CreatePendingNetwork("buzz", "network1", map[string]string{})
+ err = tx.CreatePendingNetwork("buzz", "network1", db.NetworkTypeBridge, map[string]string{})
require.Equal(t, db.ErrAlreadyDefined, err)
}
@@ -94,6 +94,6 @@ func TestNetworksCreatePending_NonExistingNode(t *testing.T) {
tx, cleanup := db.NewTestClusterTx(t)
defer cleanup()
- err := tx.CreatePendingNetwork("buzz", "network1", map[string]string{})
+ err := tx.CreatePendingNetwork("buzz", "network1", db.NetworkTypeBridge, map[string]string{})
require.Equal(t, db.ErrNoSuchObject, err)
}
diff --git a/lxd/instance_test.go b/lxd/instance_test.go
index 0d72e77955..42d3e13840 100644
--- a/lxd/instance_test.go
+++ b/lxd/instance_test.go
@@ -99,7 +99,7 @@ func (suite *containerTestSuite) TestContainer_ProfilesOverwriteDefaultNic() {
Name: "testFoo",
}
- _, err := suite.d.State().Cluster.CreateNetwork("unknownbr0", "", nil)
+ _, err := suite.d.State().Cluster.CreateNetwork("unknownbr0", "", db.NetworkTypeBridge, nil)
suite.Req.Nil(err)
c, err := instanceCreateInternal(suite.d.State(), args)
@@ -133,7 +133,7 @@ func (suite *containerTestSuite) TestContainer_LoadFromDB() {
}
state := suite.d.State()
- _, err := state.Cluster.CreateNetwork("unknownbr0", "", nil)
+ _, err := state.Cluster.CreateNetwork("unknownbr0", "", db.NetworkTypeBridge, nil)
suite.Req.Nil(err)
// Create the container
More information about the lxc-devel
mailing list