add layer3domain create and ipblock create

This is the first draft of creating layer3domains and
ipblocks/containers.

This allows some testing with different things, like list building for
complex container output, but also how containers should behave.
This commit is contained in:
Gibheer 2021-05-03 22:40:27 +02:00
parent 5ed6482400
commit 1857bdd05a
4 changed files with 132 additions and 0 deletions

43
ipblock_create.go Normal file
View File

@ -0,0 +1,43 @@
package main
import (
"fmt"
"dim/types"
)
type (
IPBlockCreateOptions struct {
Attributes string `json:"attributes"`
Layer3Domain string `json:"layer3domain"`
AllowOverlap bool `json:"allow_overlap"`
}
)
func ipblockCreate(c *Context, req Request, res *Response) error {
block := new(types.Subnet)
options := IPBlockCreateOptions{
Attributes: "{}",
}
if err := req.ParseAtLeast(1, block, &options); err != nil {
res.AddMessage(LevelError, "could not parse parameters: %s", err)
return nil
}
l3Id := 0
err := c.tx.QueryRow(`select id from layer3domains where name = $1 for update`, options.Layer3Domain).Scan(&l3Id)
if err != nil {
res.AddMessage(LevelError, "could not get layer3domain")
return fmt.Errorf("could not resolve layer3domain '%s': %s", options.Layer3Domain, err)
}
_, err = c.tx.Exec(`insert into containers(layer3domain_id, network, created_by, modified_by, attributes)
values ($1, $2, $3, $3, $4::jsonb)`,
l3Id, block, c.username, options.Attributes)
if err != nil {
res.AddMessage(LevelError, "could not create ip block")
return fmt.Errorf("could not create container '%s': %s", block, err)
}
return nil
}

27
layer3domain_create.go Normal file
View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
)
type (
Layer3DomainCreateOptions string
)
func layer3DomainCreate(c *Context, req Request, res *Response) error {
name := ""
options := Layer3DomainCreateOptions("{}")
if err := req.ParseAtLeast(1, &name, &options); err != nil {
res.AddMessage(LevelError, "could not parse parameter: %s", err)
return nil
}
_, err := c.tx.Exec(`insert into layer3domains(name, attributes, created_by, modified_by)
values ($1, $2::jsonb, $3, $3)`, name, options, c.username)
// TODO handle unique constraint violation
if err != nil {
res.AddMessage(LevelError, "could not create layer3domain '%s'", name)
return fmt.Errorf("could not insert layer3domain '%s': %s", name, err)
}
return nil
}

View File

@ -64,6 +64,8 @@ func main() {
log.Fatalf("could not create server instance: %s", err)
return
}
s.Register("layer3domain_create", layer3DomainCreate)
s.Register("ipblock_create", ipblockCreate)
s.Register("zone_create", zoneCreate)
s.Register("zone_list", zoneList)

60
types/ip.go Normal file
View File

@ -0,0 +1,60 @@
package types
import (
"bytes"
"database/sql/driver"
"fmt"
"net"
)
type (
// Subnet is used to parse a subnet parameter.
Subnet net.IPNet
// IP is used to parse an IP parameter.
IP net.IP
)
// UnmarshalJSON parses a value into a subnet.
//
// It is also checked if the provided IP matches the network address
// of the subnet.
func (s *Subnet) UnmarshalJSON(in []byte) error {
in = bytes.Trim(in, `"`)
ip, ipnet, err := net.ParseCIDR(string(in))
if err != nil {
return fmt.Errorf("not a valid subnet")
}
if !ipnet.IP.Equal(ip) {
return fmt.Errorf("provided IP is not a subnet")
}
*s = Subnet(*ipnet)
return nil
}
// String returns the string representation of the subnet.
//
// The subnet is returned as the subnet address and prefix separated by `/`
// as defined in RFC 4632 and RFC 4291.
func (s *Subnet) String() string {
return (*net.IPNet)(s).String()
}
// Value implements the database Value interface.
//
// This function is needed so that a subnet can be inserted into
// the database without much casting.
func (s *Subnet) Value() (driver.Value, error) {
return s.String(), nil
}
func (i *IP) UnmarshalJSON(in []byte) error {
in = bytes.Trim(in, `"`)
ip := net.ParseIP(string(in))
if ip == nil {
return fmt.Errorf("not a valid ip")
}
*i = IP(ip)
return nil
}