The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7657
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) === In order to support additional network types, this PR adds a generic `Network` interface and moves the existing bridge network implementation into a `bridge` driver. Also adds the concept of a network type to the database and associated structures.
From 6eaef0050085ac5be1e1ab9dbffa8b30ee74a892 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel