Go Configd Client Library

Introduction

Please refer to Configd Client Library for the general concepts behind the API. This document will cover only Go specifics. The Go client library is fairly low level, like the C library. There are additional helper libraries that make the Go library easier to use. The "github.com/danos/utils/pathutil" library can do path encoding for the Go library allowing slices of strings to be used for paths in the programs using this API. The "github.com/danos/encoding/rfc7951/data" library is useful for handling the feature defined data returned from this API.

Mechanics

Before we can use the configd API we need to establish a connection to the daemon, instantiating the CfgClient object will do this. The destructor for the client object will close the connection.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package main import ( "fmt" "os" configd "github.com/danos/configd/client" ) func handleError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main() { client, err := configd.Connect() handleError(err) defer client.Close() }

Once connected we can do a variety of actions on the system, one can configure the device, request the operational state of the device, or perform feature specific RPCs. When configuring the device via the Configd APIs one follows much the same procedure as with the CLI. One creates a configuration session, makes changes, commits those changes, then tears down the session. One may view the RUNNING datastore without a session being created. Sessions persist process exit and must be explicitly removed with SessionTeardown. The minimal configuration application looks like the following.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package main import ( "fmt" "os" "strconv" configd "github.com/danos/configd/client" ) func handleError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main() { client, err := configd.Connect( configd.SessionID(strconv.Itoa(os.Getpid())), ) handleError(err) defer client.Close() err = client.SessionSetup() handleError(err) defer client.SessionTeardown() //do something with configuration. }

From here, one may perform actions similar to what one does on the CLI. Since the interface here is meant for programatic interaction, one may also introspect on the data-model and the current state of the candidate tree to determine available actions, this is similar to using "tab-completion" to inspect the CLI.

Examples

Configuration

The following example uses the Go configd API to set all dataplane interfaces to listen for DHCP addresses if no other address is assigned to that interface. While this is a silly thing to do, it demonstrates the basic mechanics of the this API.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package main import ( "fmt" "os" "strconv" "strings" configd "github.com/danos/configd/client" "github.com/danos/configd/rpc" "github.com/danos/utils/pathutil" ) func handleError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func setupInterfaceAddress(client *configd.Client, intf string) error { fmt.Printf("%s:", intf) path := []string{"interfaces", "dataplane", intf, "address"} addrs, err := client.Get(rpc.AUTO, pathutil.Pathstr(path)) if err != nil { return err } fmt.Println(strings.Join(addrs, ", ")) if len(addrs) == 0 { path = append(path, "dhcp") out, err := client.Set(pathutil.Pathstr(path)) if out != "" { fmt.Println(out) } return err } return nil } func main() { client, err := configd.Connect( configd.SessionID(strconv.Itoa(os.Getpid())), ) handleError(err) defer client.Close() err = client.SessionSetup() handleError(err) defer client.SessionTeardown() intfs, err := client.Get(rpc.AUTO, pathutil.Pathstr([]string{"interfaces", "dataplane"})) handleError(err) for _, intf := range intfs { err := setupInterfaceAddress(client, intf) handleError(err) } out, err := client.Commit("setup interface addresses", false) if out != "" { fmt.Println(out) } handleError(err) }

Below is a sample run of the above application.

1 2 3 4 5 6 7 8 9 10 $ go run configdapi.go dp0s3:10.156.58.201/23 dp0s10: dp0s11: [interfaces dataplane dp0s10 address dhcp] Starting DHCP client on dp0s10 ... [interfaces dataplane dp0s11 address dhcp] Starting DHCP client on dp0s11 ...

RPC

One can also call an RPC via this interface.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package main import ( "fmt" "os" configd "github.com/danos/configd/client" "github.com/danos/encoding/rfc7951" "github.com/danos/encoding/rfc7951/data" ) func handleError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main() { client, err := configd.Connect() handleError(err) defer client.Close() input := data.TreeFromObject( data.ObjectWith( data.PairNew("vyatta-op-v1:host", "1.1.1.1"), ), ) out, err := client.CallRpc("vyatta-op-v1", "ping", input.String(), "rfc7951") handleError(err) var output *data.Tree err = rfc7951.Unmarshal([]byte(out), &output) handleError(err) fmt.Println("rx-packet-count", output.At("/vyatta-op-v1:rx-packet-count")) }

Below is an example run of the RPC application.

1 2 $ go run configdapi.go rx-packet-count 3

Operational State Data

Finally one may query for operational data information.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package main import ( "fmt" "os" configd "github.com/danos/configd/client" "github.com/danos/configd/rpc" "github.com/danos/encoding/rfc7951" "github.com/danos/encoding/rfc7951/data" "github.com/danos/utils/pathutil" ) func handleError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main() { client, err := configd.Connect() handleError(err) defer client.Close() out, err := client.TreeGetFull( rpc.AUTO, pathutil.Pathstr([]string{"system","state","platform"}), "rfc7951", ) handleError(err) var output *data.Tree err = rfc7951.Unmarshal([]byte(out), &output) handleError(err) fmt.Println("OS Release:", output.At("/vyatta-system-v1:platform/os-release")) }

Below is an example execution of this program.

1 2 $ go run configdapi.go OS Release: Vyatta:Master