opiproject / sztp

Secure Zero Touch Provisioning (sZTP) in OPI
Apache License 2.0
18 stars 12 forks source link

fetch DHCP leases from `nmcli` over `dbus` #418

Open glimchb opened 2 weeks ago

glimchb commented 2 weeks ago

See xPU sZTP provisioning block

today we pass lease file via cmdline arguments like this: --dhcp-lease-file /var/lib/dhclient/dhclient.leases.

but can we detect that info automatically from Network Manager over dbus ?

spec https://networkmanager.dev/

for example I found:

root@bf2:~# nmcli -f DHCP4 device show oob_net0
DHCP4.OPTION[1]:                        broadcast_address = 172.22.255.255
DHCP4.OPTION[2]:                        dad_wait_time = 0
DHCP4.OPTION[3]:                        dhcp_lease_time = 600
DHCP4.OPTION[4]:                        dhcp_message_type = 5
DHCP4.OPTION[5]:                        dhcp_server_identifier = 172.22.0.1
DHCP4.OPTION[6]:                        domain_name = lab.opiproject.org
DHCP4.OPTION[7]:                        domain_name_servers = 8.8.8.8 1.1.1.1
DHCP4.OPTION[8]:                        expiry = 1718831245
DHCP4.OPTION[9]:                        ip_address = 172.22.3.2
DHCP4.OPTION[10]:                       network_number = 172.22.0.0
DHCP4.OPTION[11]:                       next_server = 172.22.0.1
DHCP4.OPTION[12]:                       requested_broadcast_address = 1
DHCP4.OPTION[13]:                       requested_domain_name = 1
DHCP4.OPTION[14]:                       requested_domain_name_servers = 1
DHCP4.OPTION[15]:                       requested_domain_search = 1
DHCP4.OPTION[16]:                       requested_host_name = 1
DHCP4.OPTION[17]:                       requested_interface_mtu = 1
DHCP4.OPTION[18]:                       requested_ms_classless_static_routes = 1
DHCP4.OPTION[19]:                       requested_netbios_name_servers = 1
DHCP4.OPTION[20]:                       requested_netbios_scope = 1
DHCP4.OPTION[21]:                       requested_ntp_servers = 1
DHCP4.OPTION[22]:                       requested_rfc3442_classless_static_routes = 1
DHCP4.OPTION[23]:                       requested_root_path = 1
DHCP4.OPTION[24]:                       requested_routers = 1
DHCP4.OPTION[25]:                       requested_static_routes = 1
DHCP4.OPTION[26]:                       requested_subnet_mask = 1
DHCP4.OPTION[27]:                       requested_sztp_redirect_urls = 1
DHCP4.OPTION[28]:                       requested_time_offset = 1
DHCP4.OPTION[29]:                       requested_wpad = 1
DHCP4.OPTION[30]:                       routers = 172.22.0.1
DHCP4.OPTION[31]:                       subnet_mask = 255.255.0.0
DHCP4.OPTION[32]:                       sztp_redirect_urls = https://bootstrap:8080/restconf/operations/ietf-sztp-bootstrap-server:get-bootstrapping-data

or using dbus directly:

root@bf2:~# busctl -j call org.freedesktop.NetworkManager /org/freedesktop org.freedesktop.DBus.ObjectManager GetManagedObjects | jq .[] |  grep -C 3 sztp
              "type": "s",
              "data": "1"
            },
            "requested_sztp_redirect_urls": {
              "type": "s",
              "data": "1"
            },
--
              "type": "s",
              "data": "255.255.0.0"
            },
            "sztp_redirect_urls": {
              "type": "s",
              "data": "https://bootstrap:8080/restconf/operations/ietf-sztp-bootstrap-server:get-bootstrapping-data"
            }
          }
        }

Working example in python:

>>> import dbus
>>>
>>> bus = dbus.SystemBus()
>>> nm = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
>>> conn = bus.get_object("org.freedesktop.NetworkManager", nm.Get("org.freedesktop.NetworkManager", "PrimaryConnection", dbus_interface="org.freedesktop.DBus.Properties"))
>>> dhcp = bus.get_object("org.freedesktop.NetworkManager", conn.Get("org.freedesktop.NetworkManager.Connection.Active", "Dhcp4Config", dbus_interface="org.freedesktop.DBus.Properties"))
>>> options = dhcp.Get("org.freedesktop.NetworkManager.DHCP4Config", "Options", dbus_interface="org.freedesktop.DBus.Properties")
>>> print(str(options["sztp_redirect_urls"]))
https://bootstrap:8080/restconf/operations/ietf-sztp-bootstrap-server:get-bootstrapping-data

Now need to find go bindings for it...

Maybe https://github.com/Wifx/gonetworkmanager ? like https://pkg.go.dev/github.com/Wifx/gonetworkmanager#DHCP4Config

Or maybe raw https://github.com/godbus/dbus

For running in containers please mount the host dbus into the container. This can simply be done by adding -v /var/run/dbus:/var/run/dbus to your docker run command. Depending on what kind of permissions are required you may need to add --privileged to the command as well.

Please evaluate...

glimchb commented 2 weeks ago

Chatgpt produced:

dbus.go example:

package main

import (
    "fmt"
    "github.com/godbus/dbus/v5"
)

func main() {
    // Connect to the system bus
    conn, err := dbus.SystemBus()
    if err != nil {
        panic(fmt.Errorf("failed to connect to system bus: %v", err))
    }

    // Get NetworkManager object
    nm := conn.Object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")

    // Get PrimaryConnection property from NetworkManager object
    var primaryConnPath dbus.ObjectPath
    err = nm.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager", "PrimaryConnection").Store(&primaryConnPath)
    if err != nil {
        panic(fmt.Errorf("failed to get PrimaryConnection property: %v", err))
    }

    // Get Active Connection object
    connActive := conn.Object("org.freedesktop.NetworkManager", primaryConnPath)

    // Get Dhcp4Config property from Active Connection object
    var dhcpPath dbus.ObjectPath
    err = connActive.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.Connection.Active", "Dhcp4Config").Store(&dhcpPath)
    if err != nil {
        panic(fmt.Errorf("failed to get Dhcp4Config property: %v", err))
    }

    // Get Options property from DHCP4Config object
    dhcp := conn.Object("org.freedesktop.NetworkManager", dhcpPath)
    var options map[string]dbus.Variant
    err = dhcp.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.NetworkManager.DHCP4Config", "Options").Store(&options)
    if err != nil {
        panic(fmt.Errorf("failed to get Options property: %v", err))
    }

    // Print sztp_redirect_urls option
    sztpRedirectURLs := options["sztp_redirect_urls"].Value().(string)
    fmt.Println(sztpRedirectURLs)
}

or

nm,go example

package main

import (
    "fmt"
    "log"

    "github.com/Wifx/gonetworkmanager"
)

func main() {
    // Create a new NetworkManager client
    client, err := gonetworkmanager.NewClient()
    if err != nil {
        log.Fatalf("Failed to create NetworkManager client: %v", err)
    }

    // Get the active connection
    activeConnection, err := client.PrimaryConnection()
    if err != nil {
        log.Fatalf("Failed to get primary connection: %v", err)
    }

    // Get DHCP4 configuration
    dhcpConfig, err := activeConnection.DHCP4Config()
    if err != nil {
        log.Fatalf("Failed to get DHCP4 configuration: %v", err)
    }

    // Get options from DHCP4 configuration
    options, err := dhcpConfig.Options()
    if err != nil {
        log.Fatalf("Failed to get DHCP4 options: %v", err)
    }

    // Print sztp_redirect_urls option
    if sztpRedirectURLs, ok := options["sztp_redirect_urls"]; ok {
        fmt.Println(sztpRedirectURLs)
    } else {
        log.Println("sztp_redirect_urls option not found")
    }
}

tested first code snippet on bluefield2

$ cd sztp-agent
$ docker run  --rm -it -v `pwd`:/app -w /app golang:alpine go get github.com/godbus/dbus/v5
$ docker run  --rm -it --privileged -v `pwd`:/app -v /var/run/dbus:/var/run/dbus -w /app golang:alpine go run dbus.go
go: downloading github.com/godbus/dbus/v5 v5.1.0
https://bootstrap:8080/restconf/operations/ietf-sztp-bootstrap-server:get-bootstrapping-data

second code snippet is untested

glimchb commented 2 weeks ago

this is untested for looping over active connections instead of just primary connection:

import dbus

bus = dbus.SystemBus()

# Get NetworkManager object
nm = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")

# Get ActiveConnections property from NetworkManager
active_connections_paths = nm.Get("org.freedesktop.NetworkManager", "ActiveConnections", dbus_interface="org.freedesktop.DBus.Properties")
active_connections = [bus.get_object("org.freedesktop.NetworkManager", path) for path in active_connections_paths]

# Iterate through each active connection
for active_connection in active_connections:
    # Get the DHCP configuration for the active connection
    dhcp_config_path = active_connection.Get("org.freedesktop.NetworkManager.Connection.Active", "Dhcp4Config", dbus_interface="org.freedesktop.DBus.Properties")
    dhcp_config_obj = bus.get_object("org.freedesktop.NetworkManager", dhcp_config_path)

    # Get DHCP options
    dhcp_options = dhcp_config_obj.Get("org.freedesktop.NetworkManager.DHCP4Config", "Options", dbus_interface="org.freedesktop.DBus.Properties")

    # Print the desired option
    print(str(dhcp_options["sztp_redirect_urls"]))
glimchb commented 2 weeks ago

@bhoopesh369 do you want to take a stab with this issue ?

bhoopesh369 commented 2 weeks ago

@bhoopesh369 do you want to take a stab with this issue ?

Yep

glimchb commented 2 weeks ago

assigned to @bhoopesh369 please check working dbus.go example above we should do separate file/package for all dhcp work....

bhoopesh369 commented 2 weeks ago

i think going with github.com/Wifx/gonetworkmanager wont be future proof so will proceed with dbus

glimchb commented 2 weeks ago

so will proceed with dbus

agree

glimchb commented 2 weeks ago

let's discuss also how we wat to expose this to user...

we will have 3 options:

agree or you have different algorithm ?