Introducing go-ubus-rpc: a Go library and CLI tool to simplify interacting with ubus

Hello OpenWrt community! For the past several months I’ve been working on a project that I hope will prove useful to people and now I’d like to share it with the wider community. Introducing go-ubus-rpc, a Go library and CLI tool to simplify interacting with ubus.

For the developers out there, the library is structured in a way to make it as simple as possible to use. Making a call to ubus mimics the same structure as using ubus on the command line, for example:

func main() {
// create client caller
clientOpts := client.ClientOptions{Username: "root", Password: "admin", URL: "http://10.0.0.1/ubus", Timeout: session.DefaultSessionTimeout}
rpc, _ := client.NewUbusRPC(ctx, &clientOpts)

// make an RPC
uciGetOpts := client.UCIGetOptions{Config: "firewall"} // declare parameters for the call
response, _ := rpc.UCI().Get(uciGetOpts)               // make the call
result, _ := uciGetOpts.GetResult(response)            // get the typed result object from the response, in this case `result` will be a `UCIGetResult`
}

Every *Opts type has it’s own GetResult function which returns a typed object specific for that call. This library aims to shield users from the dynamic nature of ubus responses and be a consistent, typed layer on top of them with a common pattern to create calls and get responses.

For the admins, it also includes a CLI tool called gur which provides some structure to interacting with ubus, e.g:

$ gur login --url "http://10.0.0.1/ubus" -u root -p 'admin'

$ gur uci get -c dhcp -s lan
{
  "sectionArray": [
    {
      ".type": "dhcp",
      ".name": "lan",
      "dhcpv4": "server",
      "interface": "lan",
      "leasetime": "12h",
      "limit": "150",
      "ra": "server",
      "ra_flags": [
        "managed-config",
        "other-config"
      ],
      "start": "100"
    }
  ]
}

 $ gur uci get -c dhcp -s lan -o ra_flags
{
  "option": {
    "ra_flags": [
      "managed-config",
      "other-config"
    ]
  }
}

gur login stores a file with connection info into ~/.go-ubus-rpc/config.json which the CLI will automatically read and use for subsequent calls. If timeout is not specified, it will default to 0 (no expiry). A bit cleaner than manually constructing JSON calls with curl!

The library is currently in an alpha state, it only supports interacting with firewall and dhcp configs at the moment but the logical structure of the library makes it relatively straightforward to add the rest of the default configs. Most of the work still needed is to define all those options in their own structs, but then they should just work as well. A lot of thought and effort went into the logical structure of the library so that it would be easy to add all the configs in, and I’m definitely open to feedback and PRs if anyone is interested in helping to flesh it out!

1 Like

There is another reason I’m sharing this library now, this may come off as a bit of a rant but it does bother me and so I want to bring it up. I recently discovered that someone (or some team) copy/pasted my code into their own library of the same name and added more features around it. This library can be found on pkg.go.dev here, I’ve tried to track down the source code via the link listed there but have been unable to. All it takes is a cursory glance to see that this library clearly copy and pasted code from my library into theirs, specifically the client and ubus modules. I understand the nature of open source is that other people will use your code, but this doesn't feel quite right. I do want people to use this library, that’s why I made it open and licensed it under GPL. Make a fork or import it into your own project and use it to make something greater, awesome! But straight copy/pasting the code into your own repo and then building around it and presenting that library as your own, that is not open source! I made this code open in the spirit of sharing and collaboration, and I feel this violates that ethos. I believe this will be a useful tool and am thrilled to get it out there and have people use it. But at the very least set up a proper fork, and ideally contribute back things that could be core functionality. This feels like they are trying to claim it as their own!

I will continue working on my library regardless, but I just wanted to say my piece because they also used the exact same name for their library which just means people might get confused about the two in the future. OpenWRT is a great project and I hope this tool adds a useful component to the ecosystem!

2 Likes

Thank you for making this!

Now if only there was full documentation for every ubus call.

Now if only there was full documentation for every ubus call.

Hear hear! Better docs would have certainly made it a bit easier to reverse engineer all these calls, I encountered lots of unexpected behavior!

I am hopeful that by sharing the project, people who are more knowledgeable of all these different calls would be willing to contribute to the project. I spent a good bit of time thinking through the logical structure of the library so that it would ultimately be as simple as possible to implement each of the calls by just plugging it into the surrounding framework, it would be amazing to have the library fleshed out completely and cover every standard ubus command!

It seems like you're having to write custom code for every command though? This seems (outsider looking in without context) like something that should be generated? Otherwise you're having to chase upstream ubus all the time.

A code generator is not a bad idea, long-term I was hoping to build a plugin system so that any other package which registers with ubus could also have a corresponding go-ubus-rpc client it could load, sort of like Terraform providers. A code generator for that would be useful enough, but if it could also populate the functions with variables/parameters/etc based on upstream C code, that would be very sweet! I have never written a code generator before though, so I would definitely need some collaborative help to get that off the ground.

@MOSFETmisfit if you want to get in touch, I can't commit to anything at the moment but am interested. If you have a local Go user group that might be a place to find help, too