[lxc-devel] [lxd/master] Entity descriptions
albertodonato on Github
lxc-bot at linuxcontainers.org
Wed May 3 09:59:48 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 439 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170503/765e6074/attachment.bin>
-------------- next part --------------
From c710a38186c5149d05922b9c194ace1154b0daae 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 1/6] Add description field to networks.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
doc/rest-api.md | 1 +
lxc/network.go | 3 ++-
lxd/db.go | 1 +
lxd/db_networks.go | 23 ++++++++++++++++++-----
lxd/db_update.go | 6 ++++++
lxd/networks.go | 43 ++++++++++++++++++++++++-------------------
shared/api/network.go | 3 ++-
test/suites/network.sh | 6 ++++++
8 files changed, 60 insertions(+), 26 deletions(-)
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/db.go b/lxd/db.go
index 73216c6..b2df92b 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..ac749d8 100644
--- a/shared/api/network.go
+++ b/shared/api/network.go
@@ -22,7 +22,8 @@ type NetworkPost struct {
//
// API extension: network
type NetworkPut struct {
- Config map[string]string `json:"config" yaml:"config"`
+ Description string `json:"description" yaml:"description"`
+ Config map[string]string `json:"config" yaml:"config"`
}
// Network represents a LXD network
diff --git a/test/suites/network.sh b/test/suites/network.sh
index 9c849f1..69432cf 100644
--- a/test/suites/network.sh
+++ b/test/suites/network.sh
@@ -13,6 +13,12 @@ test_network() {
lxc network set lxdt$$ ipv6.dhcp.stateful true
lxc network delete lxdt$$
+ # edit network description
+ lxc network create lxdt$$
+ lxc network show lxdt$$ | sed 's/^description:.*/description: foo/' | lxc network edit lxdt$$
+ 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$$
From ddb1aba332e27cc6c481884ef1ad5236b8868cc4 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Thu, 27 Apr 2017 18:36:50 +0200
Subject: [PATCH 2/6] Change image description type to text.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
lxd/db.go | 2 +-
lxd/db_update.go | 21 +++++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/lxd/db.go b/lxd/db.go
index b2df92b..6662552 100644
--- a/lxd/db.go
+++ b/lxd/db.go
@@ -104,7 +104,7 @@ CREATE TABLE IF NOT EXISTS images_aliases (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
image_id INTEGER NOT NULL,
- description VARCHAR(255),
+ description TEXT,
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE,
UNIQUE (name)
);
diff --git a/lxd/db_update.go b/lxd/db_update.go
index 72c60d5..369f4a2 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -70,6 +70,7 @@ var dbUpdates = []dbUpdate{
{version: 34, run: dbUpdateFromV33},
{version: 35, run: dbUpdateFromV34},
{version: 36, run: dbUpdateFromV35},
+ {version: 37, run: dbUpdateFromV36},
}
type dbUpdate struct {
@@ -126,6 +127,26 @@ func dbUpdatesApplyAll(d *Daemon) error {
}
// Schema updates begin here
+func dbUpdateFromV36(currentVersion int, version int, d *Daemon) error {
+ stmt := `
+CREATE TABLE tmp (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ image_id INTEGER NOT NULL,
+ description TEXT,
+ FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE,
+ UNIQUE (name)
+);
+INSERT INTO tmp (id, name, image_id, description)
+ SELECT id, name, image_id, description
+ FROM images_aliases;
+DROP TABLE images_aliases;
+ALTER TABLE tmp RENAME TO images_aliases;
+`
+ _, err := d.db.Exec(stmt)
+ return err
+}
+
func dbUpdateFromV35(currentVersion int, version int, d *Daemon) error {
_, err := d.db.Exec("ALTER TABLE networks ADD COLUMN description TEXT;")
return err
From 5c05714de95b70f1aadc689c715725d0723fd0bf Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Fri, 28 Apr 2017 15:54:53 +0200
Subject: [PATCH 3/6] Add description field to storage pools.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
lxc/storage.go | 3 ++-
lxd/api_internal.go | 2 +-
lxd/db.go | 1 +
lxd/db_storage_pools.go | 25 ++++++++++++++++-----
lxd/db_update.go | 6 ++++++
lxd/main_test.go | 4 +++-
lxd/patches.go | 22 +++++++++----------
lxd/storage_pools.go | 6 +++---
lxd/storage_pools_utils.go | 54 ++++++++++++++++++++++++----------------------
shared/api/storage.go | 3 ++-
test/suites/storage.sh | 11 ++++++++++
11 files changed, 88 insertions(+), 49 deletions(-)
diff --git a/lxc/storage.go b/lxc/storage.go
index 0899c2e..5f71d6e 100644
--- a/lxc/storage.go
+++ b/lxc/storage.go
@@ -596,7 +596,7 @@ func (c *storageCmd) doStoragePoolsList(config *lxd.Config, args []string) error
for _, pool := range pools {
usedby := strconv.Itoa(len(pool.UsedBy))
- data = append(data, []string{pool.Name, pool.Driver, pool.Config["source"], usedby})
+ data = append(data, []string{pool.Name, pool.Description, pool.Driver, pool.Config["source"], usedby})
}
table := tablewriter.NewWriter(os.Stdout)
@@ -605,6 +605,7 @@ func (c *storageCmd) doStoragePoolsList(config *lxd.Config, args []string) error
table.SetRowLine(true)
table.SetHeader([]string{
i18n.G("NAME"),
+ i18n.G("DESCRIPTION"),
i18n.G("DRIVER"),
i18n.G("SOURCE"),
i18n.G("USED BY")})
diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 2298d84..11d3655 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -204,7 +204,7 @@ func internalImport(d *Daemon, r *http.Request) Response {
if poolErr == NoSuchObjectError {
// Create the storage pool db entry if it doesn't exist.
- err := storagePoolDBCreate(d, containerPoolName, backup.Pool.Driver, backup.Pool.Config)
+ err := storagePoolDBCreate(d, containerPoolName, pool.Description, backup.Pool.Driver, backup.Pool.Config)
if err != nil {
return InternalError(err)
}
diff --git a/lxd/db.go b/lxd/db.go
index 6662552..1e5d0a2 100644
--- a/lxd/db.go
+++ b/lxd/db.go
@@ -184,6 +184,7 @@ CREATE TABLE IF NOT EXISTS schema (
CREATE TABLE IF NOT EXISTS storage_pools (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
+ description TEXT,
driver VARCHAR(255) NOT NULL,
UNIQUE (name)
);
diff --git a/lxd/db_storage_pools.go b/lxd/db_storage_pools.go
index a98b51c..f155b8e 100644
--- a/lxd/db_storage_pools.go
+++ b/lxd/db_storage_pools.go
@@ -78,9 +78,11 @@ func dbStoragePoolGetID(db *sql.DB, poolName string) (int64, error) {
func dbStoragePoolGet(db *sql.DB, poolName string) (int64, *api.StoragePool, error) {
var poolDriver string
poolID := int64(-1)
- query := "SELECT id, driver FROM storage_pools WHERE name=?"
+ description := sql.NullString{}
+
+ query := "SELECT id, driver, description FROM storage_pools WHERE name=?"
inargs := []interface{}{poolName}
- outargs := []interface{}{&poolID, &poolDriver}
+ outargs := []interface{}{&poolID, &poolDriver, &description}
err := dbQueryRowScan(db, query, inargs, outargs)
if err != nil {
@@ -96,6 +98,7 @@ func dbStoragePoolGet(db *sql.DB, poolName string) (int64, *api.StoragePool, err
Name: poolName,
Driver: poolDriver,
}
+ storagePool.Description = description.String
storagePool.Config = config
return poolID, &storagePool, nil
@@ -126,13 +129,13 @@ func dbStoragePoolConfigGet(db *sql.DB, poolID int64) (map[string]string, error)
}
// Create new storage pool.
-func dbStoragePoolCreate(db *sql.DB, poolName string, poolDriver string, poolConfig map[string]string) (int64, error) {
+func dbStoragePoolCreate(db *sql.DB, poolName, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
tx, err := dbBegin(db)
if err != nil {
return -1, err
}
- result, err := tx.Exec("INSERT INTO storage_pools (name, driver) VALUES (?, ?)", poolName, poolDriver)
+ result, err := tx.Exec("INSERT INTO storage_pools (name, description, driver) VALUES (?, ?, ?)", poolName, poolDescription, poolDriver)
if err != nil {
tx.Rollback()
return -1, err
@@ -188,7 +191,7 @@ func dbStoragePoolConfigAdd(tx *sql.Tx, poolID int64, poolConfig map[string]stri
}
// Update storage pool.
-func dbStoragePoolUpdate(db *sql.DB, poolName string, poolConfig map[string]string) error {
+func dbStoragePoolUpdate(db *sql.DB, poolName, description string, poolConfig map[string]string) error {
poolID, _, err := dbStoragePoolGet(db, poolName)
if err != nil {
return err
@@ -199,6 +202,12 @@ func dbStoragePoolUpdate(db *sql.DB, poolName string, poolConfig map[string]stri
return err
}
+ err = dbStoragePoolUpdateDescription(tx, poolID, description)
+ if err != nil {
+ tx.Rollback()
+ return err
+ }
+
err = dbStoragePoolConfigClear(tx, poolID)
if err != nil {
tx.Rollback()
@@ -214,6 +223,12 @@ func dbStoragePoolUpdate(db *sql.DB, poolName string, poolConfig map[string]stri
return txCommit(tx)
}
+// Update the storage pool description.
+func dbStoragePoolUpdateDescription(tx *sql.Tx, id int64, description string) error {
+ _, err := tx.Exec("UPDATE storage_pools SET description=? WHERE id=?", description, id)
+ return err
+}
+
// Delete storage pool config.
func dbStoragePoolConfigClear(tx *sql.Tx, poolID int64) error {
_, err := tx.Exec("DELETE FROM storage_pools_config WHERE storage_pool_id=?", poolID)
diff --git a/lxd/db_update.go b/lxd/db_update.go
index 369f4a2..30fc59b 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -71,6 +71,7 @@ var dbUpdates = []dbUpdate{
{version: 35, run: dbUpdateFromV34},
{version: 36, run: dbUpdateFromV35},
{version: 37, run: dbUpdateFromV36},
+ {version: 38, run: dbUpdateFromV37},
}
type dbUpdate struct {
@@ -127,6 +128,11 @@ func dbUpdatesApplyAll(d *Daemon) error {
}
// Schema updates begin here
+func dbUpdateFromV37(currentVersion int, version int, d *Daemon) error {
+ _, err := d.db.Exec("ALTER TABLE storage_pools ADD COLUMN description TEXT;")
+ return err
+}
+
func dbUpdateFromV36(currentVersion int, version int, d *Daemon) error {
stmt := `
CREATE TABLE tmp (
diff --git a/lxd/main_test.go b/lxd/main_test.go
index a6828c8..af0d88e 100644
--- a/lxd/main_test.go
+++ b/lxd/main_test.go
@@ -2,6 +2,7 @@ package main
import (
"crypto/tls"
+ "fmt"
"io/ioutil"
"os"
"testing"
@@ -61,7 +62,8 @@ func (suite *lxdTestSuite) SetupSuite() {
mockStorage, _ := storageTypeToString(storageTypeMock)
// Create the database entry for the storage pool.
- _, err = dbStoragePoolCreate(suite.d.db, lxdTestSuiteDefaultStoragePool, mockStorage, poolConfig)
+ poolDescription := fmt.Sprintf("%s storage pool", lxdTestSuiteDefaultStoragePool)
+ _, err = dbStoragePoolCreate(suite.d.db, lxdTestSuiteDefaultStoragePool, poolDescription, mockStorage, poolConfig)
if err != nil {
os.Exit(1)
}
diff --git a/lxd/patches.go b/lxd/patches.go
index 7bf8abc..837159c 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -310,12 +310,12 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string,
if pool.Config == nil {
pool.Config = poolConfig
}
- err = dbStoragePoolUpdate(d.db, defaultPoolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, defaultPoolName, "", pool.Config)
if err != nil {
return err
}
} else if err == NoSuchObjectError { // Likely a pristine upgrade.
- tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, defaultStorageTypeName, poolConfig)
+ tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, "", defaultStorageTypeName, poolConfig)
if err != nil {
return err
}
@@ -607,12 +607,12 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d
if pool.Config == nil {
pool.Config = poolConfig
}
- err = dbStoragePoolUpdate(d.db, defaultPoolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, defaultPoolName, pool.Description, pool.Config)
if err != nil {
return err
}
} else if err == NoSuchObjectError { // Likely a pristine upgrade.
- tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, defaultStorageTypeName, poolConfig)
+ tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, "", defaultStorageTypeName, poolConfig)
if err != nil {
return err
}
@@ -900,12 +900,12 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
if pool.Config == nil {
pool.Config = poolConfig
}
- err = dbStoragePoolUpdate(d.db, defaultPoolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, defaultPoolName, pool.Description, pool.Config)
if err != nil {
return err
}
} else if err == NoSuchObjectError { // Likely a pristine upgrade.
- tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, defaultStorageTypeName, poolConfig)
+ tmp, err := dbStoragePoolCreate(d.db, defaultPoolName, "", defaultStorageTypeName, poolConfig)
if err != nil {
return err
}
@@ -1400,7 +1400,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
if pool.Config == nil {
pool.Config = poolConfig
}
- err = dbStoragePoolUpdate(d.db, poolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, poolName, pool.Description, pool.Config)
if err != nil {
return err
}
@@ -1432,7 +1432,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
}
// (Use a tmp variable as Go's scoping is freaking me out.)
- tmp, err := dbStoragePoolCreate(d.db, poolName, defaultStorageTypeName, poolConfig)
+ tmp, err := dbStoragePoolCreate(d.db, poolName, defaultStorageTypeName, "", poolConfig)
if err != nil {
logger.Warnf("Storage pool already exists in the database. Proceeding...")
}
@@ -1929,7 +1929,7 @@ func patchStorageApiKeys(name string, d *Daemon) error {
}
// Update the config in the database.
- err = dbStoragePoolUpdate(d.db, poolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, poolName, pool.Description, pool.Config)
if err != nil {
return err
}
@@ -2017,7 +2017,7 @@ func patchStorageApiUpdateStorageConfigs(name string, d *Daemon) error {
}
// Update the storage pool config.
- err = dbStoragePoolUpdate(d.db, poolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, poolName, pool.Description, pool.Config)
if err != nil {
return err
}
@@ -2134,7 +2134,7 @@ func patchStorageApiLxdOnBtrfs(name string, d *Daemon) error {
pool.Config["source"] = getStoragePoolMountPoint(poolName)
// Update the storage pool config.
- err = dbStoragePoolUpdate(d.db, poolName, pool.Config)
+ err = dbStoragePoolUpdate(d.db, poolName, pool.Description, pool.Config)
if err != nil {
return err
}
diff --git a/lxd/storage_pools.go b/lxd/storage_pools.go
index bbd23f0..3ee5fe4 100644
--- a/lxd/storage_pools.go
+++ b/lxd/storage_pools.go
@@ -75,7 +75,7 @@ func storagePoolsPost(d *Daemon, r *http.Request) Response {
return BadRequest(fmt.Errorf("No driver provided"))
}
- err = storagePoolCreateInternal(d, req.Name, req.Driver, req.Config)
+ err = storagePoolCreateInternal(d, req.Name, req.Description, req.Driver, req.Config)
if err != nil {
return InternalError(err)
}
@@ -138,7 +138,7 @@ func storagePoolPut(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- err = storagePoolUpdate(d, poolName, req.Config)
+ err = storagePoolUpdate(d, poolName, req.Description, req.Config)
if err != nil {
return InternalError(err)
}
@@ -188,7 +188,7 @@ func storagePoolPatch(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- err = storagePoolUpdate(d, poolName, req.Config)
+ err = storagePoolUpdate(d, poolName, req.Description, req.Config)
if err != nil {
return InternalError(fmt.Errorf("failed to update the storage pool configuration"))
}
diff --git a/lxd/storage_pools_utils.go b/lxd/storage_pools_utils.go
index bcac4d1..95761c4 100644
--- a/lxd/storage_pools_utils.go
+++ b/lxd/storage_pools_utils.go
@@ -9,7 +9,7 @@ import (
"github.com/lxc/lxd/shared/version"
)
-func storagePoolUpdate(d *Daemon, name string, newConfig map[string]string) error {
+func storagePoolUpdate(d *Daemon, name, newDescription string, newConfig map[string]string) error {
s, err := storagePoolInit(d, name)
if err != nil {
return err
@@ -19,6 +19,7 @@ func storagePoolUpdate(d *Daemon, name string, newConfig map[string]string) erro
newWritable := oldWritable
// Backup the current state
+ oldDescription := oldWritable.Description
oldConfig := map[string]string{}
err = shared.DeepCopy(&oldWritable.Config, &oldConfig)
if err != nil {
@@ -37,34 +38,35 @@ func storagePoolUpdate(d *Daemon, name string, newConfig map[string]string) erro
}()
changedConfig, userOnly := storageConfigDiff(oldConfig, newConfig)
- // Skip on no change
- if len(changedConfig) == 0 {
- return nil
- }
-
- newWritable.Config = newConfig
+ // Apply config changes if there are any
+ if len(changedConfig) != 0 {
+ newWritable.Description = newDescription
+ newWritable.Config = newConfig
+
+ // Update the storage pool
+ if !userOnly {
+ if shared.StringInSlice("driver", changedConfig) {
+ return fmt.Errorf("the \"driver\" property of a storage pool cannot be changed")
+ }
- // Update the storage pool
- if !userOnly {
- if shared.StringInSlice("driver", changedConfig) {
- return fmt.Errorf("the \"driver\" property of a storage pool cannot be changed")
+ err = s.StoragePoolUpdate(&newWritable, changedConfig)
+ if err != nil {
+ return err
+ }
}
- err = s.StoragePoolUpdate(&newWritable, changedConfig)
+ // Apply the new configuration
+ s.SetStoragePoolWritable(&newWritable)
+ }
+
+ // Update the database if something changed
+ if len(changedConfig) != 0 || newDescription != oldDescription {
+ err = dbStoragePoolUpdate(d.db, name, newDescription, newConfig)
if err != nil {
return err
}
}
- // Apply the new configuration
- s.SetStoragePoolWritable(&newWritable)
-
- // Update the database
- err = dbStoragePoolUpdate(d.db, name, newConfig)
- if err != nil {
- return err
- }
-
// Success, update the closure to mark that the changes should be kept.
undoChanges = false
@@ -153,7 +155,7 @@ func profilesUsingPoolGetNames(db *sql.DB, poolName string) ([]string, error) {
return usedBy, nil
}
-func storagePoolDBCreate(d *Daemon, poolName string, driver string, config map[string]string) error {
+func storagePoolDBCreate(d *Daemon, poolName, poolDescription string, driver string, config map[string]string) error {
// Check if the storage pool name is valid.
err := storageValidName(poolName)
if err != nil {
@@ -184,7 +186,7 @@ func storagePoolDBCreate(d *Daemon, poolName string, driver string, config map[s
}
// Create the database entry for the storage pool.
- _, err = dbStoragePoolCreate(d.db, poolName, driver, config)
+ _, err = dbStoragePoolCreate(d.db, poolName, poolDescription, driver, config)
if err != nil {
return fmt.Errorf("Error inserting %s into database: %s", poolName, err)
}
@@ -192,8 +194,8 @@ func storagePoolDBCreate(d *Daemon, poolName string, driver string, config map[s
return nil
}
-func storagePoolCreateInternal(d *Daemon, poolName string, driver string, config map[string]string) error {
- err := storagePoolDBCreate(d, poolName, driver, config)
+func storagePoolCreateInternal(d *Daemon, poolName, poolDescription string, driver string, config map[string]string) error {
+ err := storagePoolDBCreate(d, poolName, poolDescription, driver, config)
if err != nil {
return err
}
@@ -235,7 +237,7 @@ func storagePoolCreateInternal(d *Daemon, poolName string, driver string, config
configDiff, _ := storageConfigDiff(config, postCreateConfig)
if len(configDiff) > 0 {
// Create the database entry for the storage pool.
- err = dbStoragePoolUpdate(d.db, poolName, postCreateConfig)
+ err = dbStoragePoolUpdate(d.db, poolName, poolDescription, postCreateConfig)
if err != nil {
return fmt.Errorf("Error inserting %s into database: %s", poolName, err)
}
diff --git a/shared/api/storage.go b/shared/api/storage.go
index fac6d85..297b28e 100644
--- a/shared/api/storage.go
+++ b/shared/api/storage.go
@@ -25,7 +25,8 @@ type StoragePool struct {
//
// API extension: storage
type StoragePoolPut struct {
- Config map[string]string `json:"config" yaml:"config"`
+ Description string `json:"description" yaml:"description"`
+ Config map[string]string `json:"config" yaml:"config"`
}
// StorageVolumesPost represents the fields of a new LXD storage pool volume
diff --git a/test/suites/storage.sh b/test/suites/storage.sh
index 2ddbb46..44eb939 100644
--- a/test/suites/storage.sh
+++ b/test/suites/storage.sh
@@ -6,6 +6,17 @@ test_storage() {
LXD_STORAGE_DIR=$(mktemp -d -p "${TEST_DIR}" XXXXXXXXX)
chmod +x "${LXD_STORAGE_DIR}"
spawn_lxd "${LXD_STORAGE_DIR}" false
+
+ # edit storage description
+
+ # shellcheck disable=2039
+ local storage_pool
+ storage_pool="lxdtest-$(basename "${LXD_DIR}")-pool"
+ lxc storage create "$storage_pool" "$lxd_backend"
+ lxc storage show "$storage_pool" | sed 's/^description:.*/description: foo/' | lxc storage edit "$storage_pool"
+ lxc storage show "$storage_pool" | grep -q 'description: foo'
+ lxc storage delete "$storage_pool"
+
(
set -e
# shellcheck disable=2030
From b209dc9211c430eb4641d54e302ca0dcf382b0c7 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 2 May 2017 09:38:03 +0200
Subject: [PATCH 4/6] Add description field to storage volumes.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
lxc/storage.go | 3 ++-
lxd/container_lxc.go | 2 +-
lxd/db.go | 1 +
lxd/db_storage_pools.go | 19 +++++++++++++---
lxd/db_storage_volumes.go | 23 ++++++++++++++++++++
lxd/db_update.go | 6 +++++
lxd/patches.go | 52 ++++++++++++++++++++++----------------------
lxd/storage_shared.go | 2 +-
lxd/storage_volumes.go | 6 ++---
lxd/storage_volumes_utils.go | 46 ++++++++++++++++++++-------------------
shared/api/storage.go | 4 ++--
test/suites/storage.sh | 13 +++++++----
12 files changed, 114 insertions(+), 63 deletions(-)
diff --git a/lxc/storage.go b/lxc/storage.go
index 5f71d6e..50bcc2a 100644
--- a/lxc/storage.go
+++ b/lxc/storage.go
@@ -680,7 +680,7 @@ func (c *storageCmd) doStoragePoolVolumesList(config *lxd.Config, remote string,
data := [][]string{}
for _, volume := range volumes {
usedby := strconv.Itoa(len(volume.UsedBy))
- data = append(data, []string{volume.Type, volume.Name, usedby})
+ data = append(data, []string{volume.Type, volume.Name, volume.Description, usedby})
}
table := tablewriter.NewWriter(os.Stdout)
@@ -690,6 +690,7 @@ func (c *storageCmd) doStoragePoolVolumesList(config *lxd.Config, remote string,
table.SetHeader([]string{
i18n.G("TYPE"),
i18n.G("NAME"),
+ i18n.G("DESCRIPTION"),
i18n.G("USED BY")})
sort.Sort(byNameAndType(data))
table.AppendBulk(data)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 406368c..ac362e3 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -255,7 +255,7 @@ func containerLXCCreate(d *Daemon, args containerArgs) (container, error) {
}
// Create a new database entry for the container's storage volume
- _, err = dbStoragePoolVolumeCreate(d.db, args.Name, storagePoolVolumeTypeContainer, poolID, volumeConfig)
+ _, err = dbStoragePoolVolumeCreate(d.db, args.Name, "", storagePoolVolumeTypeContainer, poolID, volumeConfig)
if err != nil {
c.Delete()
return nil, err
diff --git a/lxd/db.go b/lxd/db.go
index 1e5d0a2..b51497a 100644
--- a/lxd/db.go
+++ b/lxd/db.go
@@ -199,6 +199,7 @@ CREATE TABLE IF NOT EXISTS storage_pools_config (
CREATE TABLE IF NOT EXISTS storage_volumes (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
+ description TEXT,
storage_pool_id INTEGER NOT NULL,
type INTEGER NOT NULL,
UNIQUE (storage_pool_id, name, type),
diff --git a/lxd/db_storage_pools.go b/lxd/db_storage_pools.go
index f155b8e..ad8261f 100644
--- a/lxd/db_storage_pools.go
+++ b/lxd/db_storage_pools.go
@@ -346,6 +346,11 @@ func dbStoragePoolVolumeGetType(db *sql.DB, volumeName string, volumeType int, p
return -1, nil, err
}
+ volumeDescription, err := dbStorageVolumeDescriptionGet(db, volumeID)
+ if err != nil {
+ return -1, nil, err
+ }
+
volumeTypeName, err := storagePoolVolumeTypeToName(volumeType)
if err != nil {
return -1, nil, err
@@ -355,13 +360,14 @@ func dbStoragePoolVolumeGetType(db *sql.DB, volumeName string, volumeType int, p
Type: volumeTypeName,
}
storageVolume.Name = volumeName
+ storageVolume.Description = volumeDescription
storageVolume.Config = volumeConfig
return volumeID, &storageVolume, nil
}
// Update storage volume attached to a given storage pool.
-func dbStoragePoolVolumeUpdate(db *sql.DB, volumeName string, volumeType int, poolID int64, volumeConfig map[string]string) error {
+func dbStoragePoolVolumeUpdate(db *sql.DB, volumeName string, volumeType int, poolID int64, volumeDescription string, volumeConfig map[string]string) error {
volumeID, _, err := dbStoragePoolVolumeGetType(db, volumeName, volumeType, poolID)
if err != nil {
return err
@@ -384,6 +390,12 @@ func dbStoragePoolVolumeUpdate(db *sql.DB, volumeName string, volumeType int, po
return err
}
+ err = dbStorageVolumeDescriptionUpdate(tx, volumeID, volumeDescription)
+ if err != nil {
+ tx.Rollback()
+ return err
+ }
+
return txCommit(tx)
}
@@ -424,13 +436,14 @@ func dbStoragePoolVolumeRename(db *sql.DB, oldVolumeName string, newVolumeName s
}
// Create new storage volume attached to a given storage pool.
-func dbStoragePoolVolumeCreate(db *sql.DB, volumeName string, volumeType int, poolID int64, volumeConfig map[string]string) (int64, error) {
+func dbStoragePoolVolumeCreate(db *sql.DB, volumeName, volumeDescription string, volumeType int, poolID int64, volumeConfig map[string]string) (int64, error) {
tx, err := dbBegin(db)
if err != nil {
return -1, err
}
- result, err := tx.Exec("INSERT INTO storage_volumes (storage_pool_id, type, name) VALUES (?, ?, ?)", poolID, volumeType, volumeName)
+ result, err := tx.Exec("INSERT INTO storage_volumes (storage_pool_id, type, name, description) VALUES (?, ?, ?, ?)",
+ poolID, volumeType, volumeName, volumeDescription)
if err != nil {
tx.Rollback()
return -1, err
diff --git a/lxd/db_storage_volumes.go b/lxd/db_storage_volumes.go
index 738f387..9cce35f 100644
--- a/lxd/db_storage_volumes.go
+++ b/lxd/db_storage_volumes.go
@@ -30,6 +30,29 @@ func dbStorageVolumeConfigGet(db *sql.DB, volumeID int64) (map[string]string, er
return config, nil
}
+// Get the description of a storage volume.
+func dbStorageVolumeDescriptionGet(db *sql.DB, volumeID int64) (string, error) {
+ description := sql.NullString{}
+ query := "SELECT description FROM storage_volumes WHERE id=?"
+ inargs := []interface{}{volumeID}
+ outargs := []interface{}{&description}
+
+ err := dbQueryRowScan(db, query, inargs, outargs)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ return "", NoSuchObjectError
+ }
+ }
+
+ return description.String, nil
+}
+
+// Update description of a storage volume.
+func dbStorageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description string) error {
+ _, err := tx.Exec("UPDATE storage_volumes SET description=? WHERE id=?", description, volumeID)
+ return err
+}
+
// Add new storage volume config into database.
func dbStorageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig map[string]string) error {
str := "INSERT INTO storage_volumes_config (storage_volume_id, key, value) VALUES(?, ?, ?)"
diff --git a/lxd/db_update.go b/lxd/db_update.go
index 30fc59b..e68e1ea 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -72,6 +72,7 @@ var dbUpdates = []dbUpdate{
{version: 36, run: dbUpdateFromV35},
{version: 37, run: dbUpdateFromV36},
{version: 38, run: dbUpdateFromV37},
+ {version: 39, run: dbUpdateFromV38},
}
type dbUpdate struct {
@@ -128,6 +129,11 @@ func dbUpdatesApplyAll(d *Daemon) error {
}
// Schema updates begin here
+func dbUpdateFromV38(currentVersion int, version int, d *Daemon) error {
+ _, err := d.db.Exec("ALTER TABLE storage_volumes ADD COLUMN description TEXT;")
+ return err
+}
+
func dbUpdateFromV37(currentVersion int, version int, d *Daemon) error {
_, err := d.db.Exec("ALTER TABLE storage_pools ADD COLUMN description TEXT;")
return err
diff --git a/lxd/patches.go b/lxd/patches.go
index 837159c..b80f430 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -364,13 +364,13 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string,
_, err = dbStoragePoolVolumeGetTypeID(d.db, ct, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the container.")
- err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, "", containerPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, ct, "", storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for container \"%s\".", ct)
return err
@@ -452,13 +452,13 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string,
_, err = dbStoragePoolVolumeGetTypeID(d.db, cs, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the snapshot.")
- err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, "", snapshotPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, cs, "", storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for snapshot \"%s\".", cs)
return err
@@ -533,13 +533,13 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, defaultPoolName string,
_, err = dbStoragePoolVolumeGetTypeID(d.db, img, storagePoolVolumeTypeImage, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the image.")
- err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, "", imagePoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, img, "", storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for image \"%s\".", img)
return err
@@ -651,13 +651,13 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, ct, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the container.")
- err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, "", containerPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, ct, "", storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for container \"%s\".", ct)
return err
@@ -768,13 +768,13 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, cs, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the snapshot.")
- err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, "", snapshotPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, cs, "", storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for snapshot \"%s\".", cs)
return err
@@ -798,13 +798,13 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, img, storagePoolVolumeTypeImage, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the image.")
- err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, "", imagePoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, img, "", storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for image \"%s\".", img)
return err
@@ -954,13 +954,13 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, ct, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the container.")
- err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, "", containerPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, ct, "", storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for container \"%s\".", ct)
return err
@@ -1109,13 +1109,13 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, cs, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the snapshot.")
- err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, "", snapshotPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, cs, "", storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for snapshot \"%s\".", cs)
return err
@@ -1280,13 +1280,13 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, img, storagePoolVolumeTypeImage, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the image.")
- err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, "", imagePoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, img, "", storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for image \"%s\".", img)
return err
@@ -1471,13 +1471,13 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, ct, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the container.")
- err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, ct, storagePoolVolumeTypeContainer, poolID, "", containerPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, ct, storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, ct, "", storagePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for container \"%s\".", ct)
return err
@@ -1557,13 +1557,13 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, cs, storagePoolVolumeTypeContainer, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the snapshot.")
- err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, cs, storagePoolVolumeTypeContainer, poolID, "", snapshotPoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, cs, storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, cs, "", storagePoolVolumeTypeContainer, poolID, snapshotPoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for snapshot \"%s\".", cs)
return err
@@ -1613,13 +1613,13 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
_, err = dbStoragePoolVolumeGetTypeID(d.db, img, storagePoolVolumeTypeImage, poolID)
if err == nil {
logger.Warnf("Storage volumes database already contains an entry for the image.")
- err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ err := dbStoragePoolVolumeUpdate(d.db, img, storagePoolVolumeTypeImage, poolID, "", imagePoolVolumeConfig)
if err != nil {
return err
}
} else if err == NoSuchObjectError {
// Insert storage volumes for containers into the database.
- _, err := dbStoragePoolVolumeCreate(d.db, img, storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+ _, err := dbStoragePoolVolumeCreate(d.db, img, "", storagePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
if err != nil {
logger.Errorf("Could not insert a storage volume for image \"%s\".", img)
return err
@@ -2078,7 +2078,7 @@ func patchStorageApiUpdateStorageConfigs(name string, d *Daemon) error {
// exist in the db, so it's safe to ignore the error.
volumeType, _ := storagePoolVolumeTypeNameToType(volume.Type)
// Update the volume config.
- err = dbStoragePoolVolumeUpdate(d.db, volume.Name, volumeType, poolID, volume.Config)
+ err = dbStoragePoolVolumeUpdate(d.db, volume.Name, volumeType, poolID, volume.Description, volume.Config)
if err != nil {
return err
}
@@ -2226,7 +2226,7 @@ func patchStorageApiDetectLVSize(name string, d *Daemon) error {
// exist in the db, so it's safe to ignore the error.
volumeType, _ := storagePoolVolumeTypeNameToType(volume.Type)
// Update the volume config.
- err = dbStoragePoolVolumeUpdate(d.db, volume.Name, volumeType, poolID, volume.Config)
+ err = dbStoragePoolVolumeUpdate(d.db, volume.Name, volumeType, poolID, volume.Description, volume.Config)
if err != nil {
return err
}
diff --git a/lxd/storage_shared.go b/lxd/storage_shared.go
index d6378e5..fd5f1c6 100644
--- a/lxd/storage_shared.go
+++ b/lxd/storage_shared.go
@@ -106,7 +106,7 @@ func (s *storageShared) createImageDbPoolVolume(fingerprint string) error {
}
// Create a db entry for the storage volume of the image.
- _, err = dbStoragePoolVolumeCreate(s.d.db, fingerprint, storagePoolVolumeTypeImage, s.poolID, volumeConfig)
+ _, err = dbStoragePoolVolumeCreate(s.d.db, fingerprint, "", storagePoolVolumeTypeImage, s.poolID, volumeConfig)
if err != nil {
// Try to delete the db entry on error.
s.deleteImageDbPoolVolume(fingerprint)
diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go
index a0d09f9..dcb7a64 100644
--- a/lxd/storage_volumes.go
+++ b/lxd/storage_volumes.go
@@ -162,7 +162,7 @@ func storagePoolVolumesTypePost(d *Daemon, r *http.Request) Response {
// volume is supposed to be created.
poolName := mux.Vars(r)["name"]
- err = storagePoolVolumeCreateInternal(d, poolName, req.Name, req.Type, req.Config)
+ err = storagePoolVolumeCreateInternal(d, poolName, req.Name, req.Description, req.Type, req.Config)
if err != nil {
return InternalError(err)
}
@@ -276,7 +276,7 @@ func storagePoolVolumeTypePut(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- err = storagePoolVolumeUpdate(d, poolName, req.Name, volumeType, req.Config)
+ err = storagePoolVolumeUpdate(d, poolName, req.Name, volumeType, req.Description, req.Config)
if err != nil {
return InternalError(err)
}
@@ -349,7 +349,7 @@ func storagePoolVolumeTypePatch(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- err = storagePoolVolumeUpdate(d, poolName, req.Name, volumeType, req.Config)
+ err = storagePoolVolumeUpdate(d, poolName, req.Name, volumeType, req.Description, req.Config)
if err != nil {
return InternalError(err)
}
diff --git a/lxd/storage_volumes_utils.go b/lxd/storage_volumes_utils.go
index a216e36..0f03958 100644
--- a/lxd/storage_volumes_utils.go
+++ b/lxd/storage_volumes_utils.go
@@ -88,7 +88,7 @@ func storagePoolVolumeTypeToAPIEndpoint(volumeType int) (string, error) {
return "", fmt.Errorf("invalid storage volume type")
}
-func storagePoolVolumeUpdate(d *Daemon, poolName string, volumeName string, volumeType int, newConfig map[string]string) error {
+func storagePoolVolumeUpdate(d *Daemon, poolName string, volumeName string, volumeType int, newDescription string, newConfig map[string]string) error {
s, err := storagePoolVolumeInit(d, poolName, volumeName, volumeType)
if err != nil {
return err
@@ -98,6 +98,7 @@ func storagePoolVolumeUpdate(d *Daemon, poolName string, volumeName string, volu
newWritable := oldWritable
// Backup the current state
+ oldDescription := oldWritable.Description
oldConfig := map[string]string{}
err = shared.DeepCopy(&oldWritable.Config, &oldConfig)
if err != nil {
@@ -142,33 +143,34 @@ func storagePoolVolumeUpdate(d *Daemon, poolName string, volumeName string, volu
}
}
- // Skip on no change
- if len(changedConfig) == 0 {
- return nil
- }
+ // Apply config changes if there are any
+ if len(changedConfig) != 0 {
- // Update the storage pool
- if !userOnly {
- err = s.StoragePoolVolumeUpdate(changedConfig)
- if err != nil {
- return err
+ // Update the storage pool
+ if !userOnly {
+ err = s.StoragePoolVolumeUpdate(changedConfig)
+ if err != nil {
+ return err
+ }
}
- }
- newWritable.Config = newConfig
+ newWritable.Config = newConfig
- // Apply the new configuration
- s.SetStoragePoolVolumeWritable(&newWritable)
+ // Apply the new configuration
+ s.SetStoragePoolVolumeWritable(&newWritable)
+ }
poolID, err := dbStoragePoolGetID(d.db, poolName)
if err != nil {
return err
}
- // Update the database
- err = dbStoragePoolVolumeUpdate(d.db, volumeName, volumeType, poolID, newConfig)
- if err != nil {
- return err
+ // Update the database if something changed
+ if len(changedConfig) != 0 || newDescription != oldDescription {
+ err = dbStoragePoolVolumeUpdate(d.db, volumeName, volumeType, poolID, newDescription, newConfig)
+ if err != nil {
+ return err
+ }
}
// Success, update the closure to mark that the changes should be kept.
@@ -260,7 +262,7 @@ func profilesUsingPoolVolumeGetNames(db *sql.DB, volumeName string, volumeType s
return usedBy, nil
}
-func storagePoolVolumeDBCreate(d *Daemon, poolName string, volumeName string, volumeTypeName string, volumeConfig map[string]string) error {
+func storagePoolVolumeDBCreate(d *Daemon, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
// Check that the name of the new storage volume is valid. (For example.
// zfs pools cannot contain "/" in their names.)
err := storageValidName(volumeName)
@@ -311,7 +313,7 @@ func storagePoolVolumeDBCreate(d *Daemon, poolName string, volumeName string, vo
}
// Create the database entry for the storage volume.
- _, err = dbStoragePoolVolumeCreate(d.db, volumeName, volumeType, poolID, volumeConfig)
+ _, err = dbStoragePoolVolumeCreate(d.db, volumeName, volumeDescription, volumeType, poolID, volumeConfig)
if err != nil {
return fmt.Errorf("Error inserting %s of type %s into database: %s", poolName, volumeTypeName, err)
}
@@ -319,8 +321,8 @@ func storagePoolVolumeDBCreate(d *Daemon, poolName string, volumeName string, vo
return nil
}
-func storagePoolVolumeCreateInternal(d *Daemon, poolName string, volumeName string, volumeTypeName string, volumeConfig map[string]string) error {
- err := storagePoolVolumeDBCreate(d, poolName, volumeName, volumeTypeName, volumeConfig)
+func storagePoolVolumeCreateInternal(d *Daemon, poolName string, volumeName, volumeDescription string, volumeTypeName string, volumeConfig map[string]string) error {
+ err := storagePoolVolumeDBCreate(d, poolName, volumeName, volumeDescription, volumeTypeName, volumeConfig)
if err != nil {
return err
}
diff --git a/shared/api/storage.go b/shared/api/storage.go
index 297b28e..28b1d32 100644
--- a/shared/api/storage.go
+++ b/shared/api/storage.go
@@ -44,7 +44,6 @@ type StorageVolumesPost struct {
// API extension: storage
type StorageVolume struct {
StorageVolumePut `yaml:",inline"`
-
Name string `json:"name" yaml:"name"`
Type string `json:"type" yaml:"type"`
UsedBy []string `json:"used_by" yaml:"used_by"`
@@ -54,7 +53,8 @@ type StorageVolume struct {
//
// API extension: storage
type StorageVolumePut struct {
- Config map[string]string `json:"config" yaml:"config"`
+ Description string `json:"description" yaml:"description"`
+ Config map[string]string `json:"config" yaml:"config"`
}
// Writable converts a full StoragePool struct into a StoragePoolPut struct
diff --git a/test/suites/storage.sh b/test/suites/storage.sh
index 44eb939..3008c5f 100644
--- a/test/suites/storage.sh
+++ b/test/suites/storage.sh
@@ -7,16 +7,21 @@ test_storage() {
chmod +x "${LXD_STORAGE_DIR}"
spawn_lxd "${LXD_STORAGE_DIR}" false
- # edit storage description
-
+ # edit storage and pool description
# shellcheck disable=2039
- local storage_pool
+ local storage_pool storage_volume
storage_pool="lxdtest-$(basename "${LXD_DIR}")-pool"
+ storage_volume="${storage_pool}-vol"
lxc storage create "$storage_pool" "$lxd_backend"
lxc storage show "$storage_pool" | sed 's/^description:.*/description: foo/' | lxc storage edit "$storage_pool"
lxc storage show "$storage_pool" | grep -q 'description: foo'
- lxc storage delete "$storage_pool"
+ lxc storage volume create "$storage_pool" "$storage_volume"
+ lxc storage volume show "$storage_pool" "$storage_volume" | sed 's/^description:.*/description: bar/' | lxc storage volume edit "$storage_pool" "$storage_volume"
+ lxc storage volume show "$storage_pool" "$storage_volume" | grep -q 'description: bar'
+ lxc storage volume delete "$storage_pool" "$storage_volume"
+
+ lxc storage delete "$storage_pool"
(
set -e
# shellcheck disable=2030
From 6959aac05dbca3444a16a28b88f763a91150b1d4 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 2 May 2017 13:43:50 +0200
Subject: [PATCH 5/6] Add description field to containers.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
lxc/list.go | 9 ++++++++-
lxc/list_test.go | 2 +-
lxd/container.go | 2 ++
lxd/container_lxc.go | 13 ++++++++++++-
lxd/container_patch.go | 1 +
lxd/container_put.go | 1 +
lxd/container_state.go | 1 +
lxd/db.go | 1 +
lxd/db_containers.go | 13 ++++++++-----
lxd/db_update.go | 6 ++++++
shared/api/container.go | 1 +
shared/api/storage.go | 6 +++---
test/main.sh | 2 ++
test/suites/config.sh | 31 +++++++++++++++++++++++++++++++
14 files changed, 78 insertions(+), 11 deletions(-)
diff --git a/lxc/list.go b/lxc/list.go
index 491264a..387e71d 100644
--- a/lxc/list.go
+++ b/lxc/list.go
@@ -92,6 +92,8 @@ Pre-defined column shorthand chars:
c - Creation date
+ d - Description
+
l - Last used date
n - Name
@@ -129,7 +131,7 @@ lxc list -c ns,user.comment:comment
func (c *listCmd) flags() {
gnuflag.StringVar(&c.columnsRaw, "c", "ns46tS", i18n.G("Columns"))
- gnuflag.StringVar(&c.columnsRaw, "columns", "ns46tS", i18n.G("Columns"))
+ gnuflag.StringVar(&c.columnsRaw, "columns", "nds46tS", i18n.G("Columns"))
gnuflag.StringVar(&c.format, "format", "table", i18n.G("Format (table|json|csv)"))
gnuflag.BoolVar(&c.fast, "fast", false, i18n.G("Fast mode (same as --columns=nsacPt)"))
}
@@ -445,6 +447,7 @@ func (c *listCmd) parseColumns() ([]column, error) {
'6': {i18n.G("IPV6"), c.IP6ColumnData, true, false},
'a': {i18n.G("ARCHITECTURE"), c.ArchitectureColumnData, false, false},
'c': {i18n.G("CREATED AT"), c.CreatedColumnData, false, false},
+ 'd': {i18n.G("DESCRIPTION"), c.descriptionColumnData, false, false},
'l': {i18n.G("LAST USED AT"), c.LastUsedColumnData, false, false},
'n': {i18n.G("NAME"), c.nameColumnData, false, false},
'p': {i18n.G("PID"), c.PIDColumnData, true, false},
@@ -535,6 +538,10 @@ func (c *listCmd) nameColumnData(cInfo api.Container, cState *api.ContainerState
return cInfo.Name
}
+func (c *listCmd) descriptionColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
+ return cInfo.Description
+}
+
func (c *listCmd) statusColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
return strings.ToUpper(cInfo.Status)
}
diff --git a/lxc/list_test.go b/lxc/list_test.go
index 8364fa1..6bdf62e 100644
--- a/lxc/list_test.go
+++ b/lxc/list_test.go
@@ -52,7 +52,7 @@ func TestShouldShow(t *testing.T) {
}
// Used by TestColumns and TestInvalidColumns
-const shorthand = "46abclnpPsSt"
+const shorthand = "46abcdlnpPsSt"
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
func TestColumns(t *testing.T) {
diff --git a/lxd/container.go b/lxd/container.go
index c448762..a5c8624 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -401,6 +401,7 @@ type containerArgs struct {
// Don't set manually
Id int
+ Description string
Architecture int
BaseImage string
Config map[string]string
@@ -481,6 +482,7 @@ type container interface {
// Properties
Id() int
Name() string
+ Description() string
Architecture() int
CreationDate() time.Time
LastUsedDate() time.Time
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index ac362e3..96ac8eb 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -187,6 +187,7 @@ func containerLXCCreate(d *Daemon, args containerArgs) (container, error) {
daemon: d,
id: args.Id,
name: args.Name,
+ description: args.Description,
ephemeral: args.Ephemeral,
architecture: args.Architecture,
cType: args.Ctype,
@@ -351,6 +352,7 @@ func containerLXCLoad(d *Daemon, args containerArgs) (container, error) {
daemon: d,
id: args.Id,
name: args.Name,
+ description: args.Description,
ephemeral: args.Ephemeral,
architecture: args.Architecture,
cType: args.Ctype,
@@ -381,6 +383,7 @@ type containerLXC struct {
ephemeral bool
id int
name string
+ description string
stateful bool
// Config
@@ -2512,6 +2515,7 @@ func (c *containerLXC) Render() (interface{}, interface{}, error) {
StatusCode: statusCode,
}
+ ct.Description = c.Description()
ct.Architecture = architectureName
ct.Config = c.localConfig
ct.CreatedAt = c.creationDate
@@ -3124,6 +3128,7 @@ func (c *containerLXC) Update(args containerArgs, userRequested bool) error {
}
// Get a copy of the old configuration
+ oldDescription := c.Description()
oldArchitecture := 0
err = shared.DeepCopy(&c.architecture, &oldArchitecture)
if err != nil {
@@ -3173,6 +3178,7 @@ func (c *containerLXC) Update(args containerArgs, userRequested bool) error {
undoChanges := true
defer func() {
if undoChanges {
+ c.description = oldDescription
c.architecture = oldArchitecture
c.ephemeral = oldEphemeral
c.expandedConfig = oldExpandedConfig
@@ -3187,6 +3193,7 @@ func (c *containerLXC) Update(args containerArgs, userRequested bool) error {
}()
// Apply the various changes
+ c.description = args.Description
c.architecture = args.Architecture
c.ephemeral = args.Ephemeral
c.localConfig = args.Config
@@ -3887,7 +3894,7 @@ func (c *containerLXC) Update(args containerArgs, userRequested bool) error {
return err
}
- err = dbContainerUpdate(tx, c.id, c.architecture, c.ephemeral)
+ err = dbContainerUpdate(tx, c.id, c.description, c.architecture, c.ephemeral)
if err != nil {
tx.Rollback()
return err
@@ -6745,6 +6752,10 @@ func (c *containerLXC) Name() string {
return c.name
}
+func (c *containerLXC) Description() string {
+ return c.description
+}
+
func (c *containerLXC) Profiles() []string {
return c.profiles
}
diff --git a/lxd/container_patch.go b/lxd/container_patch.go
index f650c91..0d86bd1 100644
--- a/lxd/container_patch.go
+++ b/lxd/container_patch.go
@@ -101,6 +101,7 @@ func containerPatch(d *Daemon, r *http.Request) Response {
// Update container configuration
args := containerArgs{
Architecture: architecture,
+ Description: req.Description,
Config: req.Config,
Devices: req.Devices,
Ephemeral: req.Ephemeral,
diff --git a/lxd/container_put.go b/lxd/container_put.go
index 14640c2..29ae21d 100644
--- a/lxd/container_put.go
+++ b/lxd/container_put.go
@@ -52,6 +52,7 @@ func containerPut(d *Daemon, r *http.Request) Response {
do = func(op *operation) error {
args := containerArgs{
Architecture: architecture,
+ Description: configRaw.Description,
Config: configRaw.Config,
Devices: configRaw.Devices,
Ephemeral: configRaw.Ephemeral,
diff --git a/lxd/container_state.go b/lxd/container_state.go
index 3115f58..a766db8 100644
--- a/lxd/container_state.go
+++ b/lxd/container_state.go
@@ -100,6 +100,7 @@ func containerStatePut(d *Daemon, r *http.Request) Response {
if ephemeral {
// Unset ephemeral flag
args := containerArgs{
+ Description: c.Description(),
Architecture: c.Architecture(),
Config: c.LocalConfig(),
Devices: c.LocalDevices(),
diff --git a/lxd/db.go b/lxd/db.go
index b51497a..02cab4c 100644
--- a/lxd/db.go
+++ b/lxd/db.go
@@ -44,6 +44,7 @@ CREATE TABLE IF NOT EXISTS config (
CREATE TABLE IF NOT EXISTS containers (
id INTEGER primary key AUTOINCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
+ description TEXT,
architecture INTEGER NOT NULL,
type INTEGER NOT NULL,
ephemeral INTEGER NOT NULL DEFAULT 0,
diff --git a/lxd/db_containers.go b/lxd/db_containers.go
index 58255da..87cc988 100644
--- a/lxd/db_containers.go
+++ b/lxd/db_containers.go
@@ -53,20 +53,23 @@ func dbContainerId(db *sql.DB, name string) (int, error) {
func dbContainerGet(db *sql.DB, name string) (containerArgs, error) {
var used *time.Time // Hold the db-returned time
+ description := sql.NullString{}
args := containerArgs{}
args.Name = name
ephemInt := -1
statefulInt := -1
- q := "SELECT id, architecture, type, ephemeral, stateful, creation_date, last_use_date FROM containers WHERE name=?"
+ q := "SELECT id, description, architecture, type, ephemeral, stateful, creation_date, last_use_date FROM containers WHERE name=?"
arg1 := []interface{}{name}
- arg2 := []interface{}{&args.Id, &args.Architecture, &args.Ctype, &ephemInt, &statefulInt, &args.CreationDate, &used}
+ arg2 := []interface{}{&args.Id, &description, &args.Architecture, &args.Ctype, &ephemInt, &statefulInt, &args.CreationDate, &used}
err := dbQueryRowScan(db, q, arg1, arg2)
if err != nil {
return args, err
}
+ args.Description = description.String
+
if args.Id == -1 {
return args, fmt.Errorf("Unknown container")
}
@@ -396,8 +399,8 @@ func dbContainerRename(db *sql.DB, oldName string, newName string) error {
return txCommit(tx)
}
-func dbContainerUpdate(tx *sql.Tx, id int, architecture int, ephemeral bool) error {
- str := fmt.Sprintf("UPDATE containers SET architecture=?, ephemeral=? WHERE id=?")
+func dbContainerUpdate(tx *sql.Tx, id int, description string, architecture int, ephemeral bool) error {
+ str := fmt.Sprintf("UPDATE containers SET description=?, architecture=?, ephemeral=? WHERE id=?")
stmt, err := tx.Prepare(str)
if err != nil {
return err
@@ -409,7 +412,7 @@ func dbContainerUpdate(tx *sql.Tx, id int, architecture int, ephemeral bool) err
ephemeralInt = 1
}
- if _, err := stmt.Exec(architecture, ephemeralInt, id); err != nil {
+ if _, err := stmt.Exec(description, architecture, ephemeralInt, id); err != nil {
return err
}
diff --git a/lxd/db_update.go b/lxd/db_update.go
index e68e1ea..20e6ce8 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -73,6 +73,7 @@ var dbUpdates = []dbUpdate{
{version: 37, run: dbUpdateFromV36},
{version: 38, run: dbUpdateFromV37},
{version: 39, run: dbUpdateFromV38},
+ {version: 40, run: dbUpdateFromV39},
}
type dbUpdate struct {
@@ -129,6 +130,11 @@ func dbUpdatesApplyAll(d *Daemon) error {
}
// Schema updates begin here
+func dbUpdateFromV39(currentVersion int, version int, d *Daemon) error {
+ _, err := d.db.Exec("ALTER TABLE containers ADD COLUMN description TEXT;")
+ return err
+}
+
func dbUpdateFromV38(currentVersion int, version int, d *Daemon) error {
_, err := d.db.Exec("ALTER TABLE storage_volumes ADD COLUMN description TEXT;")
return err
diff --git a/shared/api/container.go b/shared/api/container.go
index 849b9bf..ec6ff4d 100644
--- a/shared/api/container.go
+++ b/shared/api/container.go
@@ -29,6 +29,7 @@ type ContainerPost struct {
// ContainerPut represents the modifiable fields of a LXD container
type ContainerPut struct {
+ Description string `json:"description" yaml:"description"`
Architecture string `json:"architecture" yaml:"architecture"`
Config map[string]string `json:"config" yaml:"config"`
Devices map[string]map[string]string `json:"devices" yaml:"devices"`
diff --git a/shared/api/storage.go b/shared/api/storage.go
index 28b1d32..bfe3d08 100644
--- a/shared/api/storage.go
+++ b/shared/api/storage.go
@@ -44,9 +44,9 @@ type StorageVolumesPost struct {
// API extension: storage
type StorageVolume struct {
StorageVolumePut `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"`
}
// StorageVolumePut represents the modifiable fields of a LXD storage volume.
diff --git a/test/main.sh b/test/main.sh
index d470070..5728cb7 100755
--- a/test/main.sh
+++ b/test/main.sh
@@ -611,6 +611,8 @@ run_test test_concurrent "concurrent startup"
run_test test_snapshots "container snapshots"
run_test test_snap_restore "snapshot restores"
run_test test_config_profiles "profiles and configuration"
+run_test test_config_edit "container configuration edit"
+run_test test_config_edit_container_snapshot_pool_config "container and snapshot volume configuration edit"
run_test test_server_config "server configuration"
run_test test_filemanip "file manipulations"
run_test test_network "network management"
diff --git a/test/suites/config.sh b/test/suites/config.sh
index d3e9a19..01cb22e 100644
--- a/test/suites/config.sh
+++ b/test/suites/config.sh
@@ -225,3 +225,34 @@ test_config_profiles() {
lxc stop foo --force
lxc delete foo
}
+
+
+test_config_edit() {
+ ensure_import_testimage
+
+ lxc init testimage foo -s "lxdtest-$(basename "${LXD_DIR}")"
+ lxc config show foo | sed 's/^description:.*/description: bar/' | lxc config edit foo
+ lxc config show foo | grep -q 'description: bar'
+ lxc delete foo
+}
+
+test_config_edit_container_snapshot_pool_config() {
+ # shellcheck disable=2034,2039,2155
+ local storage_pool="lxdtest-$(basename "${LXD_DIR}")"
+
+ ensure_import_testimage
+
+ lxc init testimage c1 -s "$storage_pool"
+ lxc snapshot c1 s1
+ # edit the container volume name
+ lxc storage volume show "$storage_pool" container/c1 | \
+ sed 's/^description:.*/description: bar/' | \
+ lxc storage volume edit "$storage_pool" container/c1
+ lxc storage volume show "$storage_pool" container/c1 | grep -q 'description: bar'
+ # edit the container snapshot volume name
+ lxc storage volume show "$storage_pool" container/c1/s1 | \
+ sed 's/^description:.*/description: baz/' | \
+ lxc storage volume edit "$storage_pool" container/c1/s1
+ lxc storage volume show "$storage_pool" container/c1/s1 | grep -q 'description: baz'
+ lxc delete c1
+}
From 69cb2b619c5177de0f9ed9070adfe058147ce416 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Wed, 3 May 2017 11:58:13 +0200
Subject: [PATCH 6/6] Add entity_description extension.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
doc/api-extensions.md | 3 +++
lxd/api_1.0.go | 1 +
2 files changed, 4 insertions(+)
diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index ee37780..4c81df2 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -269,3 +269,6 @@ This key control what host network interface is used for a VXLAN tunnel.
This introduces the btrfs.mount\_options property for btrfs storage pools.
This key controls what mount options will be used for the btrfs storage pool.
+
+## entity\_description
+This adds descriptions to entities like containers, snapshots, networks, storage pools and volumes.
diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index beb4f79..31ed765 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -105,6 +105,7 @@ func api10Get(d *Daemon, r *http.Request) Response {
"storage_rsync_bwlimit",
"network_vxlan_interface",
"storage_btrfs_mount_options",
+ "entity_description",
},
APIStatus: "stable",
APIVersion: version.APIVersion,
More information about the lxc-devel
mailing list