Add per-device routing long-tail: hosts, providers, routes, routing_rules
Storage + CRUD (migration 0006, model, id-keyed store, REST handlers) plus compiler rendering: each section is owned by a device and projected into that device's rendered config (hosts/providers/routes/routing_rules).
This commit is contained in:
@@ -22,36 +22,44 @@ import (
|
||||
// Input is the fully-resolved model needed to render one device. Keeping Render
|
||||
// pure (no store access) makes it unit-testable without a database.
|
||||
type Input struct {
|
||||
Generation int64
|
||||
Settings model.Settings
|
||||
Device model.Device
|
||||
Fabric *model.Fabric
|
||||
Zones map[string]model.Zone
|
||||
Groups map[string]model.AddressGroup
|
||||
PortGroups map[string]model.PortGroup
|
||||
Rules []model.Rule
|
||||
Policies []model.Policy
|
||||
Bindings []model.Binding
|
||||
SNAT []model.SNATRule
|
||||
Netmap []model.NetmapRule
|
||||
NAT []model.NATRule
|
||||
Generation int64
|
||||
Settings model.Settings
|
||||
Device model.Device
|
||||
Fabric *model.Fabric
|
||||
Zones map[string]model.Zone
|
||||
Groups map[string]model.AddressGroup
|
||||
PortGroups map[string]model.PortGroup
|
||||
Rules []model.Rule
|
||||
Policies []model.Policy
|
||||
Bindings []model.Binding
|
||||
SNAT []model.SNATRule
|
||||
Netmap []model.NetmapRule
|
||||
NAT []model.NATRule
|
||||
Hosts []model.Host
|
||||
Providers []model.Provider
|
||||
Routes []model.Route
|
||||
RoutingRules []model.RoutingRule
|
||||
}
|
||||
|
||||
// RenderedConfig is the per-device output served to the agent.
|
||||
type RenderedConfig struct {
|
||||
Generation int64 `yaml:"generation" json:"generation"`
|
||||
Device string `yaml:"device" json:"device"`
|
||||
Class model.DeviceClass `yaml:"class" json:"class"`
|
||||
Enforcing bool `yaml:"enforcing" json:"enforcing"`
|
||||
Settings RenderedSettings `yaml:"settings" json:"settings"`
|
||||
Resolver []string `yaml:"resolver,omitempty" json:"resolver,omitempty"`
|
||||
Bindings map[string][]string `yaml:"bindings,omitempty" json:"bindings,omitempty"` // zone -> interfaces
|
||||
Sets []RenderedSet `yaml:"sets,omitempty" json:"sets,omitempty"`
|
||||
Rules []RenderedRule `yaml:"rules,omitempty" json:"rules,omitempty"`
|
||||
Policies []model.Policy `yaml:"policies,omitempty" json:"policies,omitempty"`
|
||||
SNAT []RenderedSNAT `yaml:"snat,omitempty" json:"snat,omitempty"`
|
||||
Netmap []RenderedNetmap `yaml:"netmap,omitempty" json:"netmap,omitempty"`
|
||||
NAT []RenderedNAT `yaml:"nat,omitempty" json:"nat,omitempty"`
|
||||
Generation int64 `yaml:"generation" json:"generation"`
|
||||
Device string `yaml:"device" json:"device"`
|
||||
Class model.DeviceClass `yaml:"class" json:"class"`
|
||||
Enforcing bool `yaml:"enforcing" json:"enforcing"`
|
||||
Settings RenderedSettings `yaml:"settings" json:"settings"`
|
||||
Resolver []string `yaml:"resolver,omitempty" json:"resolver,omitempty"`
|
||||
Bindings map[string][]string `yaml:"bindings,omitempty" json:"bindings,omitempty"` // zone -> interfaces
|
||||
Sets []RenderedSet `yaml:"sets,omitempty" json:"sets,omitempty"`
|
||||
Rules []RenderedRule `yaml:"rules,omitempty" json:"rules,omitempty"`
|
||||
Policies []model.Policy `yaml:"policies,omitempty" json:"policies,omitempty"`
|
||||
SNAT []RenderedSNAT `yaml:"snat,omitempty" json:"snat,omitempty"`
|
||||
Netmap []RenderedNetmap `yaml:"netmap,omitempty" json:"netmap,omitempty"`
|
||||
NAT []RenderedNAT `yaml:"nat,omitempty" json:"nat,omitempty"`
|
||||
Hosts []RenderedHost `yaml:"hosts,omitempty" json:"hosts,omitempty"`
|
||||
Providers []RenderedProvider `yaml:"providers,omitempty" json:"providers,omitempty"`
|
||||
Routes []RenderedRoute `yaml:"routes,omitempty" json:"routes,omitempty"`
|
||||
RoutingRules []RenderedRoutingRule `yaml:"routing_rules,omitempty" json:"routing_rules,omitempty"`
|
||||
}
|
||||
|
||||
// RenderedSNAT is a resolved SNAT/masquerade rule: source addresses masqueraded
|
||||
@@ -197,6 +205,9 @@ func Render(in Input) (*RenderedConfig, error) {
|
||||
out.Netmap = renderNetmapRules(in, out.Bindings)
|
||||
out.NAT = renderNATRules(in)
|
||||
|
||||
// Per-device long-tail sections owned by this device.
|
||||
renderPerDevice(in, out)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -426,5 +437,17 @@ func Compile(ctx context.Context, s *store.Store, device string) (*RenderedConfi
|
||||
if in.NAT, err = s.ListNAT(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.Hosts, err = s.ListHosts(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.Providers, err = s.ListProviders(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.Routes, err = s.ListRoutes(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.RoutingRules, err = s.ListRoutingRules(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Render(in)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package compiler
|
||||
|
||||
// Per-device long-tail sections rendered into a device's config. The compiler
|
||||
// filters each global list to the entries owned by the device.
|
||||
|
||||
type RenderedHost struct {
|
||||
Zone string `yaml:"zone" json:"zone"`
|
||||
Interface string `yaml:"interface" json:"interface"`
|
||||
Addresses []string `yaml:"addresses,omitempty" json:"addresses,omitempty"`
|
||||
Exclusions []string `yaml:"exclusions,omitempty" json:"exclusions,omitempty"`
|
||||
Dynamic bool `yaml:"dynamic,omitempty" json:"dynamic,omitempty"`
|
||||
}
|
||||
|
||||
type RenderedProvider struct {
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Number int `yaml:"number" json:"number"`
|
||||
Mark int `yaml:"mark,omitempty" json:"mark,omitempty"`
|
||||
Duplicate string `yaml:"duplicate,omitempty" json:"duplicate,omitempty"`
|
||||
Interface string `yaml:"interface" json:"interface"`
|
||||
Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"`
|
||||
Copy []string `yaml:"copy,omitempty" json:"copy,omitempty"`
|
||||
}
|
||||
|
||||
type RenderedRoute struct {
|
||||
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
|
||||
Dest string `yaml:"dest" json:"dest"`
|
||||
Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"`
|
||||
Oif string `yaml:"oif,omitempty" json:"oif,omitempty"`
|
||||
Persistent bool `yaml:"persistent,omitempty" json:"persistent,omitempty"`
|
||||
Comment string `yaml:"comment,omitempty" json:"comment,omitempty"`
|
||||
}
|
||||
|
||||
type RenderedRoutingRule struct {
|
||||
Source string `yaml:"source,omitempty" json:"source,omitempty"`
|
||||
Dest string `yaml:"dest,omitempty" json:"dest,omitempty"`
|
||||
Provider string `yaml:"provider" json:"provider"`
|
||||
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
|
||||
Persistent bool `yaml:"persistent,omitempty" json:"persistent,omitempty"`
|
||||
Mark string `yaml:"mark,omitempty" json:"mark,omitempty"`
|
||||
Comment string `yaml:"comment,omitempty" json:"comment,omitempty"`
|
||||
}
|
||||
|
||||
// renderPerDevice projects the per-device long-tail sections owned by this device.
|
||||
func renderPerDevice(in Input, out *RenderedConfig) {
|
||||
dev := in.Device.Name
|
||||
for _, h := range in.Hosts {
|
||||
if h.Device != dev {
|
||||
continue
|
||||
}
|
||||
out.Hosts = append(out.Hosts, RenderedHost{
|
||||
Zone: h.Zone, Interface: h.Interface, Addresses: h.Addresses,
|
||||
Exclusions: h.Exclusions, Dynamic: h.Dynamic,
|
||||
})
|
||||
}
|
||||
for _, p := range in.Providers {
|
||||
if p.Device != dev {
|
||||
continue
|
||||
}
|
||||
out.Providers = append(out.Providers, RenderedProvider{
|
||||
Name: p.Name, Number: p.Number, Mark: p.Mark, Duplicate: p.Duplicate,
|
||||
Interface: p.Interface, Gateway: p.Gateway, Copy: p.Copy,
|
||||
})
|
||||
}
|
||||
for _, r := range in.Routes {
|
||||
if r.Device != dev {
|
||||
continue
|
||||
}
|
||||
out.Routes = append(out.Routes, RenderedRoute{
|
||||
Provider: r.Provider, Dest: r.Dest, Gateway: r.Gateway,
|
||||
Oif: r.Oif, Persistent: r.Persistent, Comment: r.Comment,
|
||||
})
|
||||
}
|
||||
for _, r := range in.RoutingRules {
|
||||
if r.Device != dev {
|
||||
continue
|
||||
}
|
||||
out.RoutingRules = append(out.RoutingRules, RenderedRoutingRule{
|
||||
Source: r.Source, Dest: r.Dest, Provider: r.Provider, Priority: r.Priority,
|
||||
Persistent: r.Persistent, Mark: r.Mark, Comment: r.Comment,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user