The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7546
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) ===
From f9a3adc9c0cf21af50cb9fa4b4ce2cbcdeb1e6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 17 Jun 2020 22:53:11 -0400 Subject: [PATCH 1/3] lxc: Don't over-escape URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/query.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lxc/query.go b/lxc/query.go index 084941e2a6..e4fae86dee 100644 --- a/lxc/query.go +++ b/lxc/query.go @@ -45,12 +45,16 @@ func (c *cmdQuery) Command() *cobra.Command { } func (c *cmdQuery) pretty(input interface{}) string { - pretty, err := json.MarshalIndent(input, "", "\t") + pretty := bytes.NewBufferString("") + enc := json.NewEncoder(pretty) + enc.SetEscapeHTML(false) + enc.SetIndent("", "\t") + err := enc.Encode(input) if err != nil { return fmt.Sprintf("%v", input) } - return fmt.Sprintf("%s", pretty) + return fmt.Sprintf("%s", pretty.String()) } func (c *cmdQuery) Run(cmd *cobra.Command, args []string) error { From d86e304c11b3a74e864c1e192f2b667d96899249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 17 Jun 2020 22:53:20 -0400 Subject: [PATCH 2/3] lxd: Don't over-escape URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/util/http.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lxd/util/http.go b/lxd/util/http.go index 79dda3a3b8..3e26038429 100644 --- a/lxd/util/http.go +++ b/lxd/util/http.go @@ -36,7 +36,9 @@ func WriteJSON(w http.ResponseWriter, body interface{}, debug bool) error { output = io.MultiWriter(w, captured) } - err := json.NewEncoder(output).Encode(body) + enc := json.NewEncoder(output) + enc.SetEscapeHTML(false) + err := enc.Encode(body) if captured != nil { shared.DebugJson(captured) From 4506b0d50b985a7eeebc57876fb662d5f55d286a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 17 Jun 2020 17:49:06 -0400 Subject: [PATCH 3/3] lxd/db/storage: Rework UsedBy for pools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/db/storage_pools.go | 135 +++++++++++++++++++++++++++++++++++++ lxd/storage_pools.go | 19 ++++-- lxd/storage_pools_utils.go | 63 ----------------- 3 files changed, 148 insertions(+), 69 deletions(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index f7c6d7a308..72d436d6f8 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -5,6 +5,7 @@ package db import ( "database/sql" "fmt" + "sort" "strings" "github.com/pkg/errors" @@ -37,6 +38,140 @@ storage_pools_config JOIN storage_pools ON storage_pools.id=storage_pools_config return pools, nil } +// GetStoragePoolUsedBy looks up all users of a storage pool. +func (c *ClusterTx) GetStoragePoolUsedBy(name string) ([]string, error) { + usedby := []string{} + + // Get the pool ID. + id, err := c.GetStoragePoolID(name) + if err != nil { + return nil, err + } + + // Get the cluster nodes. + nodes, err := c.GetNodes() + if err != nil { + return nil, err + } + nodesName := map[int64]string{} + + for _, node := range nodes { + nodesName[node.ID] = node.Name + } + + // Get all the storage volumes on this node. + vols := []struct { + volName string + volType int64 + projectName string + nodeID int64 + }{} + dest := func(i int) []interface{} { + vols = append(vols, struct { + volName string + volType int64 + projectName string + nodeID int64 + }{}) + + return []interface{}{&vols[i].volName, &vols[i].volType, &vols[i].projectName, &vols[i].nodeID} + } + + stmt, err := c.tx.Prepare("SELECT storage_volumes.name, storage_volumes.type, projects.name, storage_volumes.node_id FROM storage_volumes LEFT JOIN projects ON projects.id=storage_volumes.project_id WHERE storage_pool_id=? AND (node_id=? OR storage_volumes.type == 2) ORDER BY storage_volumes.type ASC, projects.name ASC, storage_volumes.name ASC, storage_volumes.node_id ASC") + if err != nil { + return nil, err + } + + err = query.SelectObjects(stmt, dest, id, c.nodeID) + if err != nil { + return nil, err + } + + for _, r := range vols { + // Handle the containers. + if r.volType == StoragePoolVolumeTypeContainer { + if r.projectName == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/container/%s", r.volName)) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/container/%s?project=%s", r.volName, r.projectName)) + } + } + + // Handle the images. + if r.volType == StoragePoolVolumeTypeImage { + // Get the projects using an image. + stmt := "SELECT projects.name FROM images LEFT JOIN projects ON projects.id=images.project_id WHERE fingerprint=?" + projects, err := query.SelectStrings(c.tx, stmt, r.volName) + if err != nil { + return nil, err + } + + for _, project := range projects { + if project == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/images/%s", r.volName)) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/images/%s?project=%s", r.volName, project)) + } + } + } + + // Handle the custom volumes. + if r.volType == StoragePoolVolumeTypeCustom { + if len(nodes) > 1 { + if r.projectName == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/storage-pools/%s/volumes/custom/%s?target=%s", name, r.volName, nodesName[r.nodeID])) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/storage-pools/%s/volumes/custom/%s?project=%s&target=%s", name, r.volName, r.projectName, nodesName[r.nodeID])) + } + } else { + if r.projectName == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/storage-pools/%s/volumes/custom/%s", name, r.volName)) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/storage-pools/%s/volumes/custom/%s?project=%s", name, r.volName, r.projectName)) + } + } + } + + // Handle the virtual machines. + if r.volType == StoragePoolVolumeTypeVM { + if r.projectName == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/virtual-machine/%s", r.volName)) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/virtual-machine/%s?project=%s", r.volName, r.projectName)) + } + } + } + + // Get all the profiles using the storage pool. + profiles, err := c.GetProfiles(ProfileFilter{}) + if err != nil { + return nil, err + } + + for _, profile := range profiles { + for _, v := range profile.Devices { + if v["type"] != "disk" { + continue + } + + if v["pool"] != name { + continue + } + } + + if profile.Project == "default" { + usedby = append(usedby, fmt.Sprintf("/1.0/profiles/%s", profile.Name)) + } else { + usedby = append(usedby, fmt.Sprintf("/1.0/profiles/%s?project=%s", profile.Name, profile.Project)) + } + } + + // Sort the output. + sort.Strings(usedby) + + return usedby, nil +} + // GetStoragePoolID returns the ID of the pool with the given name. func (c *ClusterTx) GetStoragePoolID(name string) (int64, error) { stmt := "SELECT id FROM storage_pools WHERE name=?" diff --git a/lxd/storage_pools.go b/lxd/storage_pools.go index 489d9d8f03..d6636f4558 100644 --- a/lxd/storage_pools.go +++ b/lxd/storage_pools.go @@ -56,13 +56,16 @@ func storagePoolsGet(d *Daemon, r *http.Request) response.Response { if !recursion { resultString = append(resultString, fmt.Sprintf("/%s/storage-pools/%s", version.APIVersion, pool)) } else { - plID, pl, err := d.cluster.GetStoragePool(pool) + _, pl, err := d.cluster.GetStoragePool(pool) if err != nil { continue } - // Get all users of the storage pool. - poolUsedBy, err := storagePoolUsedByGet(d.State(), projectParam(r), plID, pool) + poolUsedBy := []string{} + err = d.cluster.Transaction(func(tx *db.ClusterTx) error { + poolUsedBy, err = tx.GetStoragePoolUsedBy(pool) + return err + }) if err != nil { return response.SmartError(err) } @@ -308,14 +311,18 @@ func storagePoolGet(d *Daemon, r *http.Request) response.Response { poolName := mux.Vars(r)["name"] // Get the existing storage pool. - poolID, pool, err := d.cluster.GetStoragePool(poolName) + _, pool, err := d.cluster.GetStoragePool(poolName) if err != nil { return response.SmartError(err) } // Get all users of the storage pool. - poolUsedBy, err := storagePoolUsedByGet(d.State(), projectParam(r), poolID, poolName) - if err != nil && err != db.ErrNoSuchObject { + poolUsedBy := []string{} + err = d.cluster.Transaction(func(tx *db.ClusterTx) error { + poolUsedBy, err = tx.GetStoragePoolUsedBy(poolName) + return err + }) + if err != nil { return response.SmartError(err) } pool.UsedBy = poolUsedBy diff --git a/lxd/storage_pools_utils.go b/lxd/storage_pools_utils.go index 2594ccb543..7d7087e962 100644 --- a/lxd/storage_pools_utils.go +++ b/lxd/storage_pools_utils.go @@ -2,14 +2,12 @@ package main import ( "fmt" - "strings" "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/state" storagePools "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" - "github.com/lxc/lxd/shared/version" ) func storagePoolUpdate(state *state.State, name, newDescription string, newConfig map[string]string, withDB bool) error { @@ -21,67 +19,6 @@ func storagePoolUpdate(state *state.State, name, newDescription string, newConfi return pool.Update(!withDB, newDescription, newConfig, nil) } -// Report all LXD objects that are currently using the given storage pool. -// Volumes of type "custom" are not reported. -// /1.0/containers/alp1 -// /1.0/containers/alp1/snapshots/snap0 -// /1.0/images/cedce20b5b236f1071134beba7a5fd2aa923fda49eea4c66454dd559a5d6e906 -// /1.0/profiles/default -func storagePoolUsedByGet(state *state.State, project string, poolID int64, poolName string) ([]string, error) { - // Retrieve all non-custom volumes that exist on this storage pool. - volumes, err := state.Cluster.GetLocalStoragePoolVolumes(project, poolID, []int{db.StoragePoolVolumeTypeContainer, db.StoragePoolVolumeTypeImage, db.StoragePoolVolumeTypeCustom, db.StoragePoolVolumeTypeVM}) - if err != nil && err != db.ErrNoSuchObject { - return []string{}, err - } - - // Retrieve all profiles that exist on this storage pool. - profiles, err := profilesUsingPoolGetNames(state.Cluster, project, poolName) - - if err != nil { - return []string{}, err - } - - slicelen := len(volumes) + len(profiles) - if slicelen == 0 { - return []string{}, nil - } - - // Save some allocation cycles by preallocating the correct len. - poolUsedBy := make([]string, slicelen) - for i := 0; i < len(volumes); i++ { - apiEndpoint, _ := storagePoolVolumeTypeNameToAPIEndpoint(volumes[i].Type) - switch apiEndpoint { - case storagePoolVolumeAPIEndpointContainers: - if strings.Index(volumes[i].Name, shared.SnapshotDelimiter) > 0 { - parentName, snapOnlyName, _ := shared.InstanceGetParentAndSnapshotName(volumes[i].Name) - poolUsedBy[i] = fmt.Sprintf("/%s/containers/%s/snapshots/%s", version.APIVersion, parentName, snapOnlyName) - } else { - poolUsedBy[i] = fmt.Sprintf("/%s/containers/%s", version.APIVersion, volumes[i].Name) - } - case storagePoolVolumeAPIEndpointVMs: - if strings.Index(volumes[i].Name, shared.SnapshotDelimiter) > 0 { - parentName, snapOnlyName, _ := shared.InstanceGetParentAndSnapshotName(volumes[i].Name) - poolUsedBy[i] = fmt.Sprintf("/%s/virtual-machines/%s/snapshots/%s", version.APIVersion, parentName, snapOnlyName) - } else { - poolUsedBy[i] = fmt.Sprintf("/%s/virtual-machines/%s", version.APIVersion, volumes[i].Name) - } - case storagePoolVolumeAPIEndpointImages: - poolUsedBy[i] = fmt.Sprintf("/%s/images/%s", version.APIVersion, volumes[i].Name) - case storagePoolVolumeAPIEndpointCustom: - poolUsedBy[i] = fmt.Sprintf("/%s/storage-pools/%s/volumes/%s/%s", version.APIVersion, poolName, volumes[i].Type, volumes[i].Name) - default: - // If that happens the db is busted, so report an error. - return []string{}, fmt.Errorf("invalid storage type for storage volume \"%s\"", volumes[i].Name) - } - } - - for i := 0; i < len(profiles); i++ { - poolUsedBy[i+len(volumes)] = fmt.Sprintf("/%s/profiles/%s", version.APIVersion, profiles[i]) - } - - return poolUsedBy, err -} - func profilesUsingPoolGetNames(db *db.Cluster, project string, poolName string) ([]string, error) { usedBy := []string{}
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel