Skip to content

Commit

Permalink
netplan: export an ip(4|6)_addresses iterator API
Browse files Browse the repository at this point in the history
It enabled us to retrieve the list of IPs, V6 and V4) from a given
netdef via Python bindings.
  • Loading branch information
daniloegea committed Jul 18, 2023
1 parent ff55faa commit 6a5b926
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
69 changes: 69 additions & 0 deletions netplan/libnetplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,14 @@ def __init__(self, np_state, ptr):
# the GC invoking netplan_state_free
self._parent = np_state

@property
def ip4_addresses(self):
return _NetdefIP4Iterator(self._ptr)

@property
def ip6_addresses(self):
return _NetdefIP6Iterator(self._ptr)

@property
def has_match(self):
return bool(lib.netplan_netdef_has_match(self._ptr))
Expand Down Expand Up @@ -668,6 +676,67 @@ def __next__(self):
return NetDefinition(self.np_state, next_value)


class _NetdefIPIterator:
_abi_loaded = False

@classmethod
def _load_abi(cls):
if cls._abi_loaded:
return

if not hasattr(lib, '_netplan_new_netdef_addresses_iter'): # pragma: nocover (hard to unit-test against the WRONG lib)
raise NetplanException('''
The current version of libnetplan does not allow iterating by IP addresses.
Please ensure that both the netplan CLI package and its library are up to date.
''')
lib._netplan_new_netdef_addresses_iter.argtypes = [_NetplanNetDefinitionP]
lib._netplan_new_netdef_addresses_iter.restype = c_void_p

lib._netplan_netdef_ip4_addresses_iter_next.argtypes = [c_void_p]
lib._netplan_netdef_ip4_addresses_iter_next.restype = c_char_p

lib._netplan_netdef_ip6_addresses_iter_next.argtypes = [c_void_p]
lib._netplan_netdef_ip6_addresses_iter_next.restype = c_char_p

lib._netplan_netdef_addresses_free_iter.argtypes = [c_void_p]
lib._netplan_netdef_addresses_free_iter.restype = None

cls._abi_loaded = True

def __init__(self, netdef):
self._load_abi()
self.netdef = netdef
self.iterator = lib._netplan_new_netdef_addresses_iter(netdef)

def __del__(self):
lib._netplan_netdef_addresses_free_iter(self.iterator)

def __iter__(self):
return self


class _NetdefIP4Iterator(_NetdefIPIterator):
def __init__(self, netdef):
super().__init__(netdef)

def __next__(self):
next_value = lib._netplan_netdef_ip4_addresses_iter_next(self.iterator)
if not next_value:
raise StopIteration
return next_value.decode('utf-8')


class _NetdefIP6Iterator(_NetdefIPIterator):
def __init__(self, netdef):
super().__init__(netdef)

def __next__(self):
next_value = lib._netplan_netdef_ip6_addresses_iter_next(self.iterator)
if not next_value:
raise StopIteration
return next_value.decode('utf-8')


lib.netplan_util_create_yaml_patch.argtypes = [c_char_p, c_char_p, c_int, _NetplanErrorPP]
lib.netplan_util_create_yaml_patch.restype = c_int

Expand Down
39 changes: 39 additions & 0 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,45 @@ get_unspecified_address(int ip_family)
return (ip_family == AF_INET) ? "0.0.0.0" : "::";
}

struct netdef_addresses_iter {
guint ip4_index;
guint ip6_index;
NetplanNetDefinition* netdef;
};

NETPLAN_INTERNAL struct netdef_addresses_iter*
_netplan_new_netdef_addresses_iter(NetplanNetDefinition* netdef)
{
struct netdef_addresses_iter* it = g_malloc0(sizeof(struct netdef_addresses_iter));
it->ip4_index = 0;
it->ip6_index = 0;
it->netdef = netdef;

return it;
}

NETPLAN_INTERNAL char*
_netplan_netdef_ip4_addresses_iter_next(struct netdef_addresses_iter* it)
{
if (!it->netdef->ip4_addresses || it->ip4_index >= it->netdef->ip4_addresses->len)
return NULL;
return g_array_index(it->netdef->ip4_addresses, char*, it->ip4_index++);
}

NETPLAN_INTERNAL char*
_netplan_netdef_ip6_addresses_iter_next(struct netdef_addresses_iter* it)
{
if (!it->netdef->ip6_addresses || it->ip6_index >= it->netdef->ip6_addresses->len)
return NULL;
return g_array_index(it->netdef->ip6_addresses, char*, it->ip6_index++);
}

NETPLAN_INTERNAL void
_netplan_netdef_addresses_free_iter(struct netdef_addresses_iter* it)
{
g_free(it);
}

struct netdef_pertype_iter {
NetplanDefType type;
GHashTableIter iter;
Expand Down
26 changes: 26 additions & 0 deletions tests/test_libnetplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,32 @@ def test_iter_ethernets(self):
self.assertSetEqual(set(["eth0", "eth1"]), set(d.id for d in libnetplan._NetdefIterator(state, "ethernets")))


class TestNetdefAddressesIterator(TestBase):
def test_with_empty_ip_addresses(self):
state = state_from_yaml(self.confdir, '''network:
ethernets:
eth0:
dhcp4: true''')

netdef = next(libnetplan._NetdefIterator(state, "ethernets"))
self.assertSetEqual(set(), set(ip for ip in netdef.ip4_addresses))
self.assertSetEqual(set(), set(ip for ip in netdef.ip6_addresses))

def test_iter_ethernets(self):
state = state_from_yaml(self.confdir, '''network:
ethernets:
eth0:
addresses:
- 192.168.0.1/24
- 172.16.0.1/24
- 1234:4321:abcd::cdef/96
- abcd::1234/64''')

netdef = next(libnetplan._NetdefIterator(state, "ethernets"))
self.assertSetEqual(set(["192.168.0.1/24", "172.16.0.1/24"]), set(ip for ip in netdef.ip4_addresses))
self.assertSetEqual(set(["1234:4321:abcd::cdef/96", "abcd::1234/64"]), set(ip for ip in netdef.ip6_addresses))


class TestParser(TestBase):
def test_load_yaml_from_fd_empty(self):
parser = libnetplan.Parser()
Expand Down

0 comments on commit 6a5b926

Please sign in to comment.