The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3277
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) === Add a `description` field to containers, networks, storage pools and volumes. It can be changed through edit operations. Closes #2022
From c710a38186c5149d05922b9c194ace1154b0daae Mon Sep 17 00:00:00 2001 From: Alberto Donato <[email protected]> Date: Wed, 26 Apr 2017 11:05:07 +0200 Subject: [PATCH 1/6] Add description field to networks. Signed-off-by: Alberto Donato <[email protected]> --- 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 <[email protected]> Date: Thu, 27 Apr 2017 18:36:50 +0200 Subject: [PATCH 2/6] Change image description type to text. Signed-off-by: Alberto Donato <[email protected]> --- 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 <[email protected]> Date: Fri, 28 Apr 2017 15:54:53 +0200 Subject: [PATCH 3/6] Add description field to storage pools. Signed-off-by: Alberto Donato <[email protected]> --- 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 <[email protected]> Date: Tue, 2 May 2017 09:38:03 +0200 Subject: [PATCH 4/6] Add description field to storage volumes. Signed-off-by: Alberto Donato <[email protected]> --- 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 <[email protected]> Date: Tue, 2 May 2017 13:43:50 +0200 Subject: [PATCH 5/6] Add description field to containers. Signed-off-by: Alberto Donato <[email protected]> --- 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 <[email protected]> Date: Wed, 3 May 2017 11:58:13 +0200 Subject: [PATCH 6/6] Add entity_description extension. Signed-off-by: Alberto Donato <[email protected]> --- 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,
_______________________________________________ lxc-devel mailing list [email protected] http://lists.linuxcontainers.org/listinfo/lxc-devel
