cfgmgmt

package
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

README

internal/cfgmgmt

Configuration management: scope tree, typed variables, service types, platform packs, and CLI render/preview.

How to design a service type (roles, fields, packs, templates, push): docs/cfgmgmt-service-design.md.

Documentation

Index

Constants

View Source
const (
	// DefaultELINEMTU/DefaultELINEControlWord are hardcoded for v1 rather
	// than service fields — matches the EOS fixture.
	DefaultELINEMTU         = 9100
	DefaultELINEControlWord = true

	FieldVLAN                 = "vlan"
	FieldSubinterfaceNetboxID = "subinterface_netbox_id"
	FieldTerminationNetboxID  = "termination_netbox_id"
)

Variables

This section is empty.

Functions

func AttachDevice

func AttachDevice(db *gorm.DB, parentID, deviceID uint) (*models.ConfigScope, error)

AttachDevice creates or updates a device-kind node under parentID and ensures a child interface node exists for each inventory interface.

func CreateScope

func CreateScope(db *gorm.DB, s *models.ConfigScope) (*models.ConfigScope, error)

func DeleteAssignment

func DeleteAssignment(db *gorm.DB, id uint) error

func DeleteScope

func DeleteScope(db *gorm.DB, id uint) error

func DescendantScopes

func DescendantScopes(db *gorm.DB, rootID uint) ([]models.ConfigScope, error)

func EncodeEndpointFields added in v1.0.3

func EncodeEndpointFields(vlan int, subNetboxID, termNetboxID uint) json.RawMessage

EncodeEndpointFields stores vlan plus optional NetBox ids on an endpoint.

func EndpointRolesForCount added in v1.0.3

func EndpointRolesForCount(st *models.ServiceType, n int) []string

EndpointRolesForCount expands st's roles into n role names for reverse- import: bounded roles are filled in order, then any unlimited role (Max==0) absorbs the rest. ELINE a/b → ["a","b"]; ELAN endpoint → n copies.

func FieldUint added in v1.0.3

func FieldUint(m map[string]any, key string) uint

func GetScope

func GetScope(db *gorm.DB, id uint) (*models.ConfigScope, error)

func InventoryMaps added in v1.0.3

func InventoryMaps(types []models.ServiceType) map[string]string

InventoryMaps returns sync_source → netbox_type for types that have both set. When two types share a source, the first in name order wins.

func IsPhysicalInterfaceType added in v1.0.3

func IsPhysicalInterfaceType(t string) bool

func IsSROS

func IsSROS(platform string) bool

func ListAssignments

func ListAssignments(db *gorm.DB, scopeID uint) ([]models.ConfigAssignment, error)

func ListEndpoints

func ListEndpoints(db *gorm.DB, serviceID uint) ([]models.ServiceEndpoint, error)

func ListScopes

func ListScopes(db *gorm.DB) ([]models.ConfigScope, error)

func ListServiceTypes added in v1.0.3

func ListServiceTypes(db *gorm.DB) ([]models.ServiceType, error)

func LookupPlatformPack

func LookupPlatformPack(db *gorm.DB, typeName, platform string) (*models.PlatformPack, error)

LookupPlatformPack returns the pack for typeName+platform. sros-md falls back to sros when no dedicated pack exists.

func LookupServiceType

func LookupServiceType(db *gorm.DB, name string) (*models.ServiceType, error)

LookupServiceType finds a service type by name.

func NetboxIDsFromFields added in v1.0.3

func NetboxIDsFromFields(raw json.RawMessage) (sub, term uint)

func NormalizePlatform

func NormalizePlatform(p string) string

func RedactAssignmentSecrets

func RedactAssignmentSecrets(db *gorm.DB, rows []models.ConfigAssignment) error

func RedactVariableSecrets

func RedactVariableSecrets(def *models.ConfigVariableDef)

func Render

func Render(db *gorm.DB, body, define string, data any) ([]string, error)

Render executes Go text/template body (or a named define) against data. FuncMap is limited: include (named ConfigMacro), join. No file/HTTP/shell.

func RenderPackApply

func RenderPackApply(db *gorm.DB, pack *models.PlatformPack, data any) ([]string, error)

RenderPackApply renders a platform pack's apply template.

func RenderPackApplyBody

func RenderPackApplyBody(db *gorm.DB, pack *models.PlatformPack, data any) ([]string, error)

RenderPackApplyBody renders the apply template with the "cleanup" define emptied so a multi-endpoint push can run teardown once, then each body.

func RenderPackCleanup

func RenderPackCleanup(db *gorm.DB, pack *models.PlatformPack, data any) ([]string, error)

RenderPackCleanup renders teardown commands from CleanupTemplate, or the apply template's "cleanup" define when CleanupTemplate is empty.

func RenderPackCleanupIfPresent

func RenderPackCleanupIfPresent(db *gorm.DB, pack *models.PlatformPack, data any) ([]string, error)

RenderPackCleanupIfPresent is RenderPackCleanup, or nil if the pack has no cleanup template and no "cleanup" define.

func ReplaceEndpoints

func ReplaceEndpoints(db *gorm.DB, serviceID uint, eps []models.ServiceEndpoint) error

func RequireCLIPack

func RequireCLIPack(pack *models.PlatformPack) error

func Resolve

func Resolve(db *gorm.DB, interfaceID uint, varName string) (any, *models.ConfigScope, error)

Resolve walks from the interface's start scope to root and returns the first assignment of varName. Falls back to the def's DefaultValue.

func ResolveMap

func ResolveMap(db *gorm.DB, interfaceID uint) (map[string]any, error)

ResolveMap is ResolveAll as name → value, skipping vars that failed.

func ResolveMapForDevice

func ResolveMapForDevice(db *gorm.DB, deviceID uint) (map[string]any, error)

ResolveMapForDevice resolves variables from the device scope (not a particular interface), filtered by the device's platform.

func RootScope

func RootScope(db *gorm.DB) (*models.ConfigScope, error)

RootScope returns the global root folder.

func SDPIDFromNeighbor

func SDPIDFromNeighbor(neighborIP string) (int, error)

SDPIDFromNeighbor is the SR OS shared SDP ID (last IPv4 octet).

func SecretDefaultUnchanged

func SecretDefaultUnchanged(raw []byte) bool

SecretDefaultUnchanged reports that a write should keep the stored secret default: the client omitted it, sent JSON null, or echoed the redaction placeholder.

func Seed

func Seed(db *gorm.DB) error

Seed creates the global root scope, built-in service types, and ELINE platform packs when they are missing. Operator-edited packs are left alone; checksum-matching rows are refreshed from the embed files.

func ServiceTypeExists

func ServiceTypeExists(db *gorm.DB, name string) (bool, error)

func StartScope

func StartScope(db *gorm.DB, interfaceID uint) (*models.ConfigScope, error)

StartScope for an interface: interface node, else device node, else global.

func TypeCheck

func TypeCheck(def *models.ConfigVariableDef, v any) (any, error)

TypeCheck coerces and validates v against def. Type, constraints, and (for vlan) the 1–4094 range. List entries and map keys/values are checked recursively when constraints.items / keys / values are set.

func TypeCheckRaw

func TypeCheckRaw(def *models.ConfigVariableDef, raw json.RawMessage) (any, error)

func TypeForNetboxKind added in v1.0.3

func TypeForNetboxKind(types []models.ServiceType, netboxType string) *models.ServiceType

TypeForNetboxKind returns the first service type whose NetboxType matches.

func UpdateScope

func UpdateScope(db *gorm.DB, id uint, patch *models.ConfigScope) (*models.ConfigScope, error)

func UpsertAssignment

func UpsertAssignment(db *gorm.DB, defID, scopeID uint, value []byte) (*models.ConfigAssignment, error)

func VLANFromFields added in v1.0.3

func VLANFromFields(raw json.RawMessage) int

func ValidScopeKind

func ValidScopeKind(k string) bool

func ValidVarType

func ValidVarType(t string) bool

func ValidateConstraints

func ValidateConstraints(typ string, raw json.RawMessage) error

ValidateConstraints checks nested items/keys/values type names and that list/map-only fields are not set on scalar types.

func ValidateELINEShape added in v1.0.3

func ValidateELINEShape(db *gorm.DB, eps []models.ServiceEndpoint) error

ValidateELINEShape enforces physical ports and that A/B are not the same device+interface. Call after ValidateEndpoints.

func ValidateEndpoints

func ValidateEndpoints(db *gorm.DB, st *models.ServiceType, eps []models.ServiceEndpoint) error

ValidateEndpoints checks endpoints against the service type's EndpointRoles and that each device/interface pair exists in inventory.

func ValidateVariableDef

func ValidateVariableDef(def *models.ConfigVariableDef) error

ValidateVariableDef checks type, constraints, and default_value.

func WalkParents

func WalkParents(db *gorm.DB, start *models.ConfigScope) ([]models.ConfigScope, error)

WalkParents returns start then each ancestor up to root. A cycle is an error.

func WouldCycle

func WouldCycle(db *gorm.DB, nodeID uint, newParentID uint) (bool, error)

WouldCycle reports whether setting node's parent to newParentID would loop.

Types

type DCIMDevice

type DCIMDevice struct {
	ID           uint   `json:"id"`
	Name         string `json:"name"`
	Platform     string `json:"platform"`
	Site         string `json:"site"`
	Role         string `json:"role"`
	ModelName    string `json:"model_name"`
	Manufacturer string `json:"manufacturer"`
	Status       string `json:"status"`
	PrimaryIPv4  string `json:"primary_ipv4"`
	PrimaryIPv6  string `json:"primary_ipv6"`
}

DCIMDevice is the read-only inventory fragment templates may use.

func DCIMFromDevice

func DCIMFromDevice(d *models.Device) DCIMDevice

type DCIMInterface

type DCIMInterface struct {
	ID           uint     `json:"id"`
	Name         string   `json:"name"`
	Description  string   `json:"description"`
	Enabled      bool     `json:"enabled"`
	Type         string   `json:"type"`
	UntaggedVLAN int      `json:"untagged_vlan"`
	TaggedVLANs  []int    `json:"tagged_vlans"`
	Addresses    []string `json:"addresses"`
}

DCIMInterface is the read-only interface fragment templates may use.

func DCIMFromInterface

func DCIMFromInterface(iface *models.Interface) DCIMInterface

type DeviceRender

type DeviceRender struct {
	DeviceID uint             `json:"device_id"`
	Name     string           `json:"name"`
	Platform string           `json:"platform"`
	Sources  []RenderedSource `json:"sources"`
}

func RenderDevice

func RenderDevice(db *gorm.DB, deviceID uint) (*DeviceRender, error)

RenderDevice returns baseline templates + terminating services for a device. It does not talk to the device.

type ELINERemote

type ELINERemote struct {
	NeighborIP   string
	PseudowireID int
	MTU          int
	ControlWord  bool
	DeviceName   string
	RemoteIface  string
	RemoteVLAN   int
}

type ELINEStale

type ELINEStale struct {
	Iface string
	VLAN  int
}

type GenericRenderData

type GenericRenderData struct {
	Name               string
	Description        string
	ServiceNumericID   int
	Fields             map[string]any
	Endpoint           map[string]any
	Vars               map[string]any
	Device             DCIMDevice
	Interface          DCIMInterface
	LocalIface         string
	LocalVLAN          int
	Role               string
	PeerLocalIface     string
	PeerLocalVLAN      int
	Remote             *ELINERemote
	StaleSubinterfaces []ELINEStale
	SDPID              int
}

GenericRenderData is the template context for platform packs. ELINE packs use the extra Peer/Remote/SDPID/Stale fields; other types leave them zero.

func GenericData

func GenericData(db *gorm.DB, svc *models.Service, ep *models.ServiceEndpoint, device *models.Device, iface *models.Interface) (*GenericRenderData, error)

type MatrixRow

type MatrixRow struct {
	InterfaceID   uint   `json:"interface_id"`
	InterfaceName string `json:"interface_name"`
	DeviceID      uint   `json:"device_id"`
	DeviceName    string `json:"device_name"`
	ScopeID       *uint  `json:"scope_id,omitempty"`
	Value         any    `json:"value"`
	SourceID      *uint  `json:"source_id,omitempty"`
	SourceName    string `json:"source_name,omitempty"`
	FromDefault   bool   `json:"from_default"`
	Error         string `json:"error,omitempty"`
}

MatrixRows are interfaces under a scope subtree, with one variable resolved.

func Matrix

func Matrix(db *gorm.DB, scopeID uint, varName string) ([]MatrixRow, error)

type RenderedSource

type RenderedSource struct {
	Source      string   `json:"source"`
	Kind        string   `json:"kind"` // template | service
	Platform    string   `json:"platform"`
	PayloadKind string   `json:"payload_kind"`
	Commands    []string `json:"commands"`
	Error       string   `json:"error,omitempty"`
}

RenderedSource is one template or service's rendered CLI (or other payload).

func RenderService

func RenderService(db *gorm.DB, serviceID uint) ([]RenderedSource, error)

RenderService renders every endpoint of a service (preview, no device I/O).

type ResolvedVar

type ResolvedVar struct {
	Name        string
	Value       any
	Source      *models.ConfigScope
	FromDefault bool
	Secret      bool
	Required    bool
	Type        string
	Err         error
}

ResolvedVar is one variable's winning value and the scope it came from.

func RedactSecrets

func RedactSecrets(vars []ResolvedVar) []ResolvedVar

func ResolveAll

func ResolveAll(db *gorm.DB, interfaceID uint) ([]ResolvedVar, error)

ResolveAll returns every variable def resolved at interfaceID. Required vars with no value are included with Err set rather than failing the batch.

type ScopeTreeData

type ScopeTreeData struct {
	ID          uint   `json:"id"`
	Kind        string `json:"kind"`
	ParentID    *uint  `json:"parent_id,omitempty"`
	SiteID      *uint  `json:"site_id,omitempty"`
	DeviceID    *uint  `json:"device_id,omitempty"`
	InterfaceID *uint  `json:"interface_id,omitempty"`
	SortOrder   int    `json:"sort_order"`
}

type ScopeTreeNode

type ScopeTreeNode struct {
	Key      string          `json:"key"`
	Title    string          `json:"title"`
	Type     string          `json:"type"`
	Data     ScopeTreeData   `json:"data"`
	Children []ScopeTreeNode `json:"children,omitempty"`
}

ScopeTreeNode is one nested scope for GET /api/config/scopes/tree.

func ScopeTree

func ScopeTree(db *gorm.DB) ([]ScopeTreeNode, error)

type StatusError

type StatusError struct {
	Status  int
	Message string
}

StatusError is an application-level failure with an HTTP-ish status so handlers don't have to string-match error text.

func AsStatusError

func AsStatusError(err error) *StatusError

func (*StatusError) Error

func (e *StatusError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL