[lxc-devel] [lxd/master] Add description field to networks

albertodonato on Github lxc-bot at linuxcontainers.org
Wed Apr 26 11:47:13 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 379 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170426/d4ab5137/attachment.bin>
-------------- next part --------------
From d7053f8c4e4a1c88d623710064a95fa21fda5d57 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Wed, 26 Apr 2017 11:05:07 +0200
Subject: [PATCH] Add description field to networks.

Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
 doc/api-extensions.md  |  3 +++
 doc/rest-api.md        |  1 +
 lxc/network.go         |  3 ++-
 lxd/api_1.0.go         |  1 +
 lxd/db.go              |  1 +
 lxd/db_networks.go     | 23 ++++++++++++++++++-----
 lxd/db_update.go       |  6 ++++++
 lxd/networks.go        | 43 ++++++++++++++++++++++++-------------------
 shared/api/network.go  |  7 ++++---
 test/suites/network.sh | 11 +++++++++++
 10 files changed, 71 insertions(+), 28 deletions(-)
 mode change 100644 => 100755 test/suites/network.sh

diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 8cf17f3..2c96f5d 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -264,3 +264,6 @@ places an upper limit on the amount of socket I/O allowed.
 This introduces a new tunnel.NAME.interface option for networks.
 
 This key control what host network interface is used for a VXLAN tunnel.
+
+## entity\_description
+This adds descriptions to entities like containers, snapshots, networks, storage pools and volumes.
diff --git a/doc/rest-api.md b/doc/rest-api.md
index 94a883c..8b92db3 100644
--- a/doc/rest-api.md
+++ b/doc/rest-api.md
@@ -1562,6 +1562,7 @@ Input:
 
     {
         "name": "my-network",
+        "description": "My network",
         "config": {
             "ipv4.address": "none",
             "ipv6.address": "2001:470:b368:4242::1/64",
diff --git a/lxc/network.go b/lxc/network.go
index 920a67e..0fdea24 100644
--- a/lxc/network.go
+++ b/lxc/network.go
@@ -447,7 +447,7 @@ func (c *networkCmd) doNetworkList(config *lxd.Config, args []string) error {
 		}
 
 		strUsedBy := fmt.Sprintf("%d", len(network.UsedBy))
-		data = append(data, []string{network.Name, network.Type, strManaged, strUsedBy})
+		data = append(data, []string{network.Name, network.Type, strManaged, network.Description, strUsedBy})
 	}
 
 	table := tablewriter.NewWriter(os.Stdout)
@@ -458,6 +458,7 @@ func (c *networkCmd) doNetworkList(config *lxd.Config, args []string) error {
 		i18n.G("NAME"),
 		i18n.G("TYPE"),
 		i18n.G("MANAGED"),
+		i18n.G("DESCRIPTION"),
 		i18n.G("USED BY")})
 	sort.Sort(byName(data))
 	table.AppendBulk(data)
diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index bdf7c54..122a2c7 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -104,6 +104,7 @@ func api10Get(d *Daemon, r *http.Request) Response {
 			"storage_lvm_use_thinpool",
 			"storage_rsync_bwlimit",
 			"network_vxlan_interface",
+			"entity_description",
 		},
 		APIStatus:  "stable",
 		APIVersion: version.APIVersion,
diff --git a/lxd/db.go b/lxd/db.go
index 923c293..49461dc 100644
--- a/lxd/db.go
+++ b/lxd/db.go
@@ -128,6 +128,7 @@ CREATE TABLE IF NOT EXISTS images_source (
 CREATE TABLE IF NOT EXISTS networks (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     name VARCHAR(255) NOT NULL,
+    description TEXT,
     UNIQUE (name)
 );
 CREATE TABLE IF NOT EXISTS networks_config (
diff --git a/lxd/db_networks.go b/lxd/db_networks.go
index 3040617..425edf6 100644
--- a/lxd/db_networks.go
+++ b/lxd/db_networks.go
@@ -29,11 +29,12 @@ func dbNetworks(db *sql.DB) ([]string, error) {
 }
 
 func dbNetworkGet(db *sql.DB, name string) (int64, *api.Network, error) {
+	description := sql.NullString{}
 	id := int64(-1)
 
-	q := "SELECT id FROM networks WHERE name=?"
+	q := "SELECT id, description FROM networks WHERE name=?"
 	arg1 := []interface{}{name}
-	arg2 := []interface{}{&id}
+	arg2 := []interface{}{&id, &description}
 	err := dbQueryRowScan(db, q, arg1, arg2)
 	if err != nil {
 		return -1, nil, err
@@ -49,6 +50,7 @@ func dbNetworkGet(db *sql.DB, name string) (int64, *api.Network, error) {
 		Managed: true,
 		Type:    "bridge",
 	}
+	network.Description = description.String
 	network.Config = config
 
 	return id, &network, nil
@@ -140,13 +142,13 @@ func dbNetworkConfigGet(db *sql.DB, id int64) (map[string]string, error) {
 	return config, nil
 }
 
-func dbNetworkCreate(db *sql.DB, name string, config map[string]string) (int64, error) {
+func dbNetworkCreate(db *sql.DB, name, description string, config map[string]string) (int64, error) {
 	tx, err := dbBegin(db)
 	if err != nil {
 		return -1, err
 	}
 
-	result, err := tx.Exec("INSERT INTO networks (name) VALUES (?)", name)
+	result, err := tx.Exec("INSERT INTO networks (name, description) VALUES (?, ?)", name, description)
 	if err != nil {
 		tx.Rollback()
 		return -1, err
@@ -172,7 +174,7 @@ func dbNetworkCreate(db *sql.DB, name string, config map[string]string) (int64,
 	return id, nil
 }
 
-func dbNetworkUpdate(db *sql.DB, name string, config map[string]string) error {
+func dbNetworkUpdate(db *sql.DB, name, description string, config map[string]string) error {
 	id, _, err := dbNetworkGet(db, name)
 	if err != nil {
 		return err
@@ -183,6 +185,12 @@ func dbNetworkUpdate(db *sql.DB, name string, config map[string]string) error {
 		return err
 	}
 
+	err = dbNetworkUpdateDescription(tx, id, description)
+	if err != nil {
+		tx.Rollback()
+		return err
+	}
+
 	err = dbNetworkConfigClear(tx, id)
 	if err != nil {
 		tx.Rollback()
@@ -198,6 +206,11 @@ func dbNetworkUpdate(db *sql.DB, name string, config map[string]string) error {
 	return txCommit(tx)
 }
 
+func dbNetworkUpdateDescription(tx *sql.Tx, id int64, description string) error {
+	_, err := tx.Exec("UPDATE networks SET description=? WHERE id=?", description, id)
+	return err
+}
+
 func dbNetworkConfigAdd(tx *sql.Tx, id int64, config map[string]string) error {
 	str := fmt.Sprintf("INSERT INTO networks_config (network_id, key, value) VALUES(?, ?, ?)")
 	stmt, err := tx.Prepare(str)
diff --git a/lxd/db_update.go b/lxd/db_update.go
index ee1dc71..72c60d5 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -69,6 +69,7 @@ var dbUpdates = []dbUpdate{
 	{version: 33, run: dbUpdateFromV32},
 	{version: 34, run: dbUpdateFromV33},
 	{version: 35, run: dbUpdateFromV34},
+	{version: 36, run: dbUpdateFromV35},
 }
 
 type dbUpdate struct {
@@ -125,6 +126,11 @@ func dbUpdatesApplyAll(d *Daemon) error {
 }
 
 // Schema updates begin here
+func dbUpdateFromV35(currentVersion int, version int, d *Daemon) error {
+	_, err := d.db.Exec("ALTER TABLE networks ADD COLUMN description TEXT;")
+	return err
+}
+
 func dbUpdateFromV34(currentVersion int, version int, d *Daemon) error {
 	stmt := `
 CREATE TABLE IF NOT EXISTS storage_pools (
diff --git a/lxd/networks.go b/lxd/networks.go
index 1f54977..cefcb38 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -126,7 +126,7 @@ func networksPost(d *Daemon, r *http.Request) Response {
 	}
 
 	// Create the database entry
-	_, err = dbNetworkCreate(d.db, req.Name, req.Config)
+	_, err = dbNetworkCreate(d.db, req.Name, req.Description, req.Config)
 	if err != nil {
 		return InternalError(
 			fmt.Errorf("Error inserting %s into database: %s", req.Name, err))
@@ -157,7 +157,7 @@ func networkGet(d *Daemon, r *http.Request) Response {
 		return SmartError(err)
 	}
 
-	etag := []interface{}{n.Name, n.Managed, n.Type, n.Config}
+	etag := []interface{}{n.Name, n.Description, n.Managed, n.Type, n.Config}
 
 	return SyncResponseETag(true, &n, etag)
 }
@@ -201,6 +201,7 @@ func doNetworkGet(d *Daemon, name string) (api.Network, error) {
 	} else if dbInfo != nil || shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", n.Name)) {
 		if dbInfo != nil {
 			n.Managed = true
+			n.Description = dbInfo.Description
 			n.Config = dbInfo.Config
 		}
 
@@ -301,7 +302,7 @@ func networkPut(d *Daemon, r *http.Request) Response {
 	}
 
 	// Validate the ETag
-	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}
+	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Description, dbInfo.Config}
 
 	err = etagCheck(r, etag)
 	if err != nil {
@@ -313,7 +314,7 @@ func networkPut(d *Daemon, r *http.Request) Response {
 		return BadRequest(err)
 	}
 
-	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
+	return doNetworkUpdate(d, name, dbInfo.Config, req)
 }
 
 func networkPatch(d *Daemon, r *http.Request) Response {
@@ -326,7 +327,7 @@ func networkPatch(d *Daemon, r *http.Request) Response {
 	}
 
 	// Validate the ETag
-	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Config}
+	etag := []interface{}{dbInfo.Name, dbInfo.Managed, dbInfo.Type, dbInfo.Description, dbInfo.Config}
 
 	err = etagCheck(r, etag)
 	if err != nil {
@@ -350,20 +351,20 @@ func networkPatch(d *Daemon, r *http.Request) Response {
 		}
 	}
 
-	return doNetworkUpdate(d, name, dbInfo.Config, req.Config)
+	return doNetworkUpdate(d, name, dbInfo.Config, req)
 }
 
-func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, newConfig map[string]string) Response {
+func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, req api.NetworkPut) Response {
 	// Validate the configuration
-	err := networkValidateConfig(name, newConfig)
+	err := networkValidateConfig(name, req.Config)
 	if err != nil {
 		return BadRequest(err)
 	}
 
 	// When switching to a fan bridge, auto-detect the underlay
-	if newConfig["bridge.mode"] == "fan" {
-		if newConfig["fan.underlay_subnet"] == "" {
-			newConfig["fan.underlay_subnet"] = "auto"
+	if req.Config["bridge.mode"] == "fan" {
+		if req.Config["fan.underlay_subnet"] == "" {
+			req.Config["fan.underlay_subnet"] = "auto"
 		}
 	}
 
@@ -373,7 +374,7 @@ func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, newCon
 		return NotFound
 	}
 
-	err = n.Update(api.NetworkPut{Config: newConfig})
+	err = n.Update(req)
 	if err != nil {
 		return SmartError(err)
 	}
@@ -390,7 +391,7 @@ func networkLoadByName(d *Daemon, name string) (*network, error) {
 		return nil, err
 	}
 
-	n := network{daemon: d, id: id, name: name, config: dbInfo.Config}
+	n := network{daemon: d, id: id, name: name, description: dbInfo.Description, config: dbInfo.Config}
 
 	return &n, nil
 }
@@ -421,9 +422,10 @@ func networkStartup(d *Daemon) error {
 
 type network struct {
 	// Properties
-	daemon *Daemon
-	id     int64
-	name   string
+	daemon      *Daemon
+	id          int64
+	name        string
+	description string
 
 	// config
 	config map[string]string
@@ -1271,6 +1273,7 @@ func (n *network) Update(newNetwork api.NetworkPut) error {
 
 	// Backup the current state
 	oldConfig := map[string]string{}
+	oldDescription := n.description
 	err = shared.DeepCopy(&n.config, &oldConfig)
 	if err != nil {
 		return err
@@ -1284,6 +1287,7 @@ func (n *network) Update(newNetwork api.NetworkPut) error {
 	defer func() {
 		if undoChanges {
 			n.config = oldConfig
+			n.description = oldDescription
 		}
 	}()
 
@@ -1315,7 +1319,7 @@ func (n *network) Update(newNetwork api.NetworkPut) error {
 	}
 
 	// Skip on no change
-	if len(changedConfig) == 0 {
+	if len(changedConfig) == 0 && newNetwork.Description == n.description {
 		return nil
 	}
 
@@ -1351,11 +1355,12 @@ func (n *network) Update(newNetwork api.NetworkPut) error {
 		}
 	}
 
-	// Apply the new configuration
+	// Apply changes
 	n.config = newConfig
+	n.description = newNetwork.Description
 
 	// Update the database
-	err = dbNetworkUpdate(n.daemon.db, n.name, n.config)
+	err = dbNetworkUpdate(n.daemon.db, n.name, n.description, n.config)
 	if err != nil {
 		return err
 	}
diff --git a/shared/api/network.go b/shared/api/network.go
index 21db460..d5c5590 100644
--- a/shared/api/network.go
+++ b/shared/api/network.go
@@ -22,6 +22,7 @@ type NetworkPost struct {
 //
 // API extension: network
 type NetworkPut struct {
+	Description string   `json:"description" yaml:"description"`
 	Config map[string]string `json:"config" yaml:"config"`
 }
 
@@ -29,9 +30,9 @@ type NetworkPut struct {
 type Network struct {
 	NetworkPut `yaml:",inline"`
 
-	Name   string   `json:"name" yaml:"name"`
-	Type   string   `json:"type" yaml:"type"`
-	UsedBy []string `json:"used_by" yaml:"used_by"`
+	Name        string   `json:"name" yaml:"name"`
+	Type        string   `json:"type" yaml:"type"`
+	UsedBy      []string `json:"used_by" yaml:"used_by"`
 
 	// API extension: network
 	Managed bool `json:"managed" yaml:"managed"`
diff --git a/test/suites/network.sh b/test/suites/network.sh
old mode 100644
new mode 100755
index fe66bea..6f8fc47
--- a/test/suites/network.sh
+++ b/test/suites/network.sh
@@ -1,6 +1,9 @@
 #!/bin/sh
 
 test_network() {
+  # shellcheck disable=2039
+  local config_file
+
   ensure_import_testimage
   ensure_has_localhost_remote "${LXD_ADDR}"
 
@@ -15,6 +18,14 @@ test_network() {
   lxc network set lxdt$$ ipv6.dhcp.stateful true
   lxc network delete lxdt$$
 
+  # edit network description
+  config_file=$(mktemp)
+  lxc network create lxdt$$
+  lxc network show lxdt$$ | sed 's/^description:/description: foo/' > "$config_file"
+  lxc network edit lxdt$$ < "$config_file"
+  lxc network show lxdt$$ | grep -q 'description: foo'
+  lxc network delete lxdt$$
+
   # Unconfigured bridge
   lxc network create lxdt$$ ipv4.address=none ipv6.address=none
   lxc network delete lxdt$$


More information about the lxc-devel mailing list