Documentation
YANG Schema Authoring
Plugins declare their configuration schema using YANG (RFC 7950). This guide covers the basics for plugin authors.
Registering a Schema
Schemas are declared via the Schema field of sdk.Registration, which is passed to
p.Run(). The Schema field is a pointer to sdk.SchemaDecl.
p := sdk.NewWithConn("my-plugin", conn)
p.Run(ctx, sdk.Registration{
Schema: &sdk.SchemaDecl{
Module: "ze-my-plugin",
Namespace: "urn:ze:my-plugin",
YANGText: myPluginYANG, // YANG module text (string)
Handlers: []string{"my-plugin"},
},
WantsConfig: []string{"my-plugin"},
})
SchemaDecl Fields
| Field | Type | Purpose |
|---|---|---|
Module |
string |
YANG module name |
Namespace |
string |
YANG namespace URI |
YANGText |
string |
Full YANG module text |
Handlers |
[]string |
Config path prefixes this plugin handles |
Embedding YANG Files
Internal plugins use //go:embed to embed YANG files at compile time:
import _ "embed"
//go:embed my-plugin.yang
var myPluginYANG string
The embedded string is then passed as YANGText in the SchemaDecl.
For external plugins, the YANG text can come from any source (file read, constant, etc.).
Minimal Schema
module my-plugin {
namespace "urn:my-company:my-plugin";
prefix mp;
container my-plugin {
leaf enabled {
type boolean;
default true;
}
}
}
Module Structure
module name {
namespace "urn:..."; // Unique namespace URI
prefix prefix; // Short prefix for references
// Content: containers, lists, leafs
}
Common Types
| YANG Type | Go Type | Example |
|---|---|---|
string |
string |
"hello" |
boolean |
bool |
true/false |
uint8/16/32/64 |
uintN |
42 |
int8/16/32/64 |
intN |
-42 |
Constraints
Range (Numbers)
leaf port {
type uint16 {
range "1..65535";
}
}
leaf hold-time {
type uint16 {
range "0 | 3..65535"; // 0 or 3+
}
}
Length (Strings)
leaf name {
type string {
length "1..64";
}
}
Pattern (Regex)
leaf ipv4-address {
type string {
pattern '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+';
}
}
Enumeration
leaf action {
type enumeration {
enum allow;
enum deny;
enum log;
}
}
Containers and Lists
Container (Single Instance)
container settings {
leaf timeout { type uint32; }
leaf retries { type uint8; }
}
Config:
settings {
timeout 30;
retries 3;
}
List (Multiple Instances)
list endpoint {
key "name";
leaf name { type string; }
leaf url { type string; }
leaf enabled { type boolean; default true; }
}
Config:
endpoint api {
url "https://api.example.com";
}
endpoint backup {
url "https://backup.example.com";
enabled false;
}
Mandatory and Default
leaf required-field {
type string;
mandatory true; // Must be provided
}
leaf optional-field {
type uint32;
default 100; // Used if not provided
}
References (leafref)
Reference values from other parts of config:
// In ze-bgp-conf module
list peer-group {
key "name";
leaf name { type string; }
}
// In your module - reference peer-group
leaf group {
type leafref {
path "/bgp/peer-group/name";
}
}
Ze validates that referenced values exist.
Best Practices
- Use meaningful namespaces -- include your organization
- Add descriptions -- help users understand fields
- Set sensible defaults -- reduce required config
- Validate early -- use YANG constraints, not just code
- Use the
Handlersfield -- list the config path prefixes your plugin manages
Example: Complete Plugin with Schema
A complete example showing YANG schema registration with the SDK:
package main
import (
"context"
_ "embed"
"log"
"os"
"os/signal"
"github.com/ze-software/ze/pkg/plugin/sdk"
)
//go:embed acme-monitor.yang
var acmeMonitorYANG string
func main() {
p, err := sdk.NewFromEnv("acme-monitor")
if err != nil {
log.Fatal(err)
}
p.OnConfigure(func(sections []sdk.ConfigSection) error {
for _, s := range sections {
log.Printf("config root=%s data=%s", s.Root, s.Data)
}
return nil
})
p.OnEvent(func(event string) error {
log.Printf("event: %s", event)
return nil
})
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
if err := p.Run(ctx, sdk.Registration{
Schema: &sdk.SchemaDecl{
Module: "acme-monitor",
Namespace: "urn:acme:monitor",
YANGText: acmeMonitorYANG,
Handlers: []string{"acme-monitor"},
},
WantsConfig: []string{"acme-monitor"},
}); err != nil {
log.Fatal(err)
}
}
Example YANG Schema
module acme-monitor {
namespace "urn:acme:monitor";
prefix acme;
description "ACME endpoint monitoring plugin";
container acme-monitor {
description "Monitor configuration";
leaf endpoint {
type string;
mandatory true;
description "HTTPS endpoint to monitor";
}
leaf interval {
type uint32 {
range "10..3600";
}
default 60;
description "Check interval in seconds";
}
list alert {
key "name";
description "Alert destinations";
leaf name {
type string {
length "1..64";
}
}
leaf email {
type string;
}
}
}
}