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
|
|
}
|