Gibheer
5bb473404f
Add support to show all pool attributes and set them. Also add some helper functions to FieldMap to change and check the requested attributes. This was needed because the layer3domain needs to be set through attributes instead of a link function (this should be changed, but for now we will be compatible to ndcli). So we filter out the layer3domain name and replace it with the ID, so that the update can do both at the same time. Maybe there is a better way to handle this in the future.
236 lines
7.5 KiB
Go
236 lines
7.5 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"dim/query"
|
|
"dim/types"
|
|
)
|
|
|
|
// PoolCreate will create a new pool.
|
|
func PoolCreate(c *Context, req Request, res *Response) error {
|
|
name := ""
|
|
options := struct {
|
|
Layer3Domain string `json:"layer3domain"`
|
|
VLAN uint `json:"vlan"`
|
|
Owner string `json:"owner"`
|
|
Attributes types.FieldMap `json:"attributes"`
|
|
}{
|
|
Attributes: types.NewFieldMap(map[string]interface{}{}),
|
|
}
|
|
if err := req.ParseAtLeast(1, &name, &options); err != nil {
|
|
res.AddMessage(LevelError, "could not parse options: %s", err)
|
|
return nil
|
|
}
|
|
if name == "" {
|
|
res.AddMessage(LevelError, "name is empty")
|
|
return nil
|
|
}
|
|
if options.VLAN > 0 {
|
|
options.Attributes.Set("vlan", options.VLAN)
|
|
}
|
|
attrs, err := options.Attributes.MarshalJSON()
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not encode attributes to json: %s", err)
|
|
return nil
|
|
}
|
|
|
|
l3id := 0
|
|
err = c.tx.QueryRow(`select id from layer3domains where name = $1`, options.Layer3Domain).Scan(&l3id)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not resolve layer3domain")
|
|
return fmt.Errorf("could not resolve layer3domain '%s': %#v", options.Layer3Domain, err)
|
|
}
|
|
|
|
_, err = c.tx.Exec(`insert into pools(name, created_by, modified_by, attributes, layer3domain_id)
|
|
values ($1, $2, $2, $3, $4)`, name, c.username, attrs, l3id)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not create new pool")
|
|
return fmt.Errorf("could not create new pool '%s': %#v", name, err)
|
|
}
|
|
res.AddMessage(LevelInfo, "created pool '%s'", name)
|
|
return nil
|
|
}
|
|
|
|
// PoolDelete deletes a pool.
|
|
func PoolDelete(c *Context, req Request, res *Response) error {
|
|
name := ""
|
|
options := struct {
|
|
Force bool `json:"force"`
|
|
DeleteSubnets bool `json:"delete_subnets"`
|
|
}{}
|
|
if err := req.ParseAtLeast(1, &name, &options); err != nil {
|
|
res.AddMessage(LevelError, "could not parse options: %s", err)
|
|
return nil
|
|
}
|
|
if name == "" {
|
|
res.AddMessage(LevelError, "name is empty")
|
|
return nil
|
|
}
|
|
|
|
id := 0
|
|
l3id := 0
|
|
err := c.tx.QueryRow(`select id, layer3domain_id from pools where name = $1 for update`, name).
|
|
Scan(&id, &l3id)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not fetch pool information") // TODO implement sql.ErrNotFound
|
|
return fmt.Errorf("could not find pool '%s': %#v", name, err)
|
|
}
|
|
if options.Force && options.DeleteSubnets {
|
|
// TODO what else needs to be deleted?
|
|
_, err := c.tx.Exec(`delete from containers where id = $1 and layer3domain_id = $2`, id, l3id)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not delete subnets")
|
|
return fmt.Errorf("could not delete subnets for pool '%s': %#v", name, err)
|
|
}
|
|
}
|
|
if _, err := c.tx.Exec(`delete from pools where id = $1 and layer3domain_id = $2`, id, l3id); err != nil {
|
|
res.AddMessage(LevelError, "could not delete pool")
|
|
return fmt.Errorf("could not delete pool '%s': %#v", name, err)
|
|
}
|
|
res.AddMessage(LevelInfo, "deleted pool '%s'", name)
|
|
return nil
|
|
}
|
|
|
|
// PoolList lists all pools with the requested fields.
|
|
func PoolList(c *Context, req Request, res *Response) error {
|
|
options := struct {
|
|
Attributes types.FieldList
|
|
}{
|
|
Attributes: types.NewFieldList("name", "vlan", "subnets"),
|
|
}
|
|
if err := req.ParseAtLeast(0, &options); err != nil {
|
|
res.AddMessage(LevelError, "could not parse options")
|
|
return nil
|
|
}
|
|
|
|
fieldMap := map[string]string{
|
|
"name": "p.name",
|
|
"modified_by": "p.modified_by",
|
|
"modified_at": "p.modified_at",
|
|
"created_by": "p.created_by",
|
|
"created_at": "p.created_at",
|
|
"layer3domain": "l.name",
|
|
"subnets": "(jsonb_agg(c.network::text) filter (where c.network is not null))::text",
|
|
}
|
|
selClause := query.FieldListToSelect("p", options.Attributes, fieldMap)
|
|
from := "pools p"
|
|
groupBy := "p.name, p.modified_by, p.modified_at, p.created_by, p.created_at, p.attributes"
|
|
if options.Attributes.Contains("layer3domain") {
|
|
from += " join layer3domains l on p.layer3domain_id = l.id"
|
|
groupBy += ",l.name"
|
|
}
|
|
if options.Attributes.Contains("subnets") {
|
|
from += " left join containers c on p.id = c.pool_id and p.layer3domain_id = c.layer3domain_id"
|
|
}
|
|
qry := fmt.Sprintf("select %s from %s group by %s order by p.name", selClause, from, groupBy)
|
|
rows, err := c.tx.Query(qry)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not fetch pools")
|
|
return fmt.Errorf("could not fetch pools: %#v - query: %s", err, qry)
|
|
}
|
|
defer rows.Close()
|
|
res.Result, err = query.RowsToMap(rows)
|
|
if err != nil {
|
|
res.Result = nil
|
|
res.AddMessage(LevelError, "could not parse pools")
|
|
return fmt.Errorf("could not generate pool list: %#v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func PoolGetAttr(c *Context, req Request, res *Response) error {
|
|
name := ""
|
|
if err := req.ParseAtLeast(1, &name); err != nil {
|
|
res.AddMessage(LevelError, "could not parse name: %s", err)
|
|
return nil
|
|
}
|
|
if name == "" {
|
|
res.AddMessage(LevelError, "empty name was provided")
|
|
return nil
|
|
}
|
|
|
|
result := json.RawMessage{}
|
|
selClause := query.FieldsToJSON("p", map[string]string{
|
|
"name": "p.name",
|
|
"modified_by": "p.modified_by",
|
|
"modified_at": "p.modified_at",
|
|
"created_by": "p.created_by",
|
|
"created_at": "p.created_at",
|
|
"layer3domain": "l.name",
|
|
})
|
|
queryStr := fmt.Sprintf(`select %s from pools p join layer3domains l
|
|
on p.layer3domain_id = l.id
|
|
where p.name = $1`, selClause)
|
|
err := c.tx.QueryRow(queryStr, name).Scan(&result)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not return result")
|
|
return fmt.Errorf("could not get pool '%s': %s - query: %s", name, err, queryStr)
|
|
}
|
|
res.Result = result
|
|
return nil
|
|
}
|
|
|
|
func PoolSetAttr(c *Context, req Request, res *Response) error {
|
|
name := ""
|
|
attrs := types.FieldMap{}
|
|
if err := req.ParseAtLeast(2, &name, &attrs); err != nil {
|
|
res.AddMessage(LevelError, "could not parse options: %s", err)
|
|
return nil
|
|
}
|
|
if name == "" {
|
|
res.AddMessage(LevelError, "empty name was provided")
|
|
return nil
|
|
}
|
|
if attrs.Size() == 0 {
|
|
res.AddMessage(LevelError, "no key/value pairs provided to update")
|
|
return nil
|
|
}
|
|
|
|
// TODO this is ugly. Can we have better API somehow?
|
|
fieldMap := map[string]string{
|
|
"name": "",
|
|
"modified_by": "",
|
|
"modified_at": "",
|
|
"created_by": "",
|
|
"created_at": "",
|
|
"layer3domain_id": "",
|
|
}
|
|
if attrs.Contains("subnets") {
|
|
res.AddMessage(LevelError, "can not set subnets as attributes")
|
|
return nil
|
|
}
|
|
if attrs.Contains("layer3domain") {
|
|
l3name := attrs.Fields()["layer3domain"]
|
|
attrs.Delete("layer3domain")
|
|
l3id := 0
|
|
err := c.tx.QueryRow(`select id from layer3domains where name = $1`, l3name).Scan(&l3id)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
res.AddMessage(LevelError, "layer3domain '%s' does not exist", l3name)
|
|
return nil
|
|
}
|
|
res.AddMessage(LevelError, "could not get layer3domain")
|
|
return fmt.Errorf("could not fetch layer3domain id for name '%s': %#v", l3name, err)
|
|
}
|
|
attrs.Set("layer3domain_id", l3id)
|
|
}
|
|
setClause, args, err := query.FieldMapToUpdate(attrs, fieldMap)
|
|
if err != nil {
|
|
res.AddMessage(LevelError, "could not encode requested attributes: %s", err)
|
|
return nil
|
|
}
|
|
queryStr := fmt.Sprintf("update pools p set %s where name = $%d", setClause, len(args)+1)
|
|
args = append(args, name) // don't forget to add the where clause parameter
|
|
c.Logf(LevelInfo, "query: %s - args: %#v", queryStr, args)
|
|
if _, err := c.tx.Exec(queryStr, args...); err != nil {
|
|
res.AddMessage(LevelError, "could not set attributes")
|
|
c.Logf(LevelError, "could not set attributes on layer3domain '%s': %s - query: `%s` - args: `%#v`", name, err, queryStr, args)
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|