You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
7.5 KiB
235 lines
7.5 KiB
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 |
|
}
|
|
|