Skip to content

Commit

Permalink
cli:tools:test: Make use of new cffi python bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
slyon committed Aug 15, 2023
1 parent 739fd9d commit 69ca31f
Show file tree
Hide file tree
Showing 19 changed files with 221 additions and 179 deletions.
2 changes: 1 addition & 1 deletion netplan_cli/cli/commands/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ def process_link_changes(interfaces, config_manager: ConfigManager): # pragma:
newname = netdef.set_name
if not newname:
continue # Skip if no new name needs to be set
if not netdef.has_match:
if not netdef._has_match:
continue # Skip if no match for current name is given
if NetplanApply.is_composite_member(composite_interfaces, netdef.id):
logging.debug('Skipping composite member {}'.format(netdef.id))
Expand Down
18 changes: 9 additions & 9 deletions netplan_cli/cli/commands/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import io

from ..utils import NetplanCommand
from ... import libnetplan
import netplan

FALLBACK_FILENAME = '70-netplan-set.yaml'
GLOBAL_KEYS = ['renderer', 'version']
Expand Down Expand Up @@ -67,9 +67,9 @@ def command_set(self):
# Split the string into a list on the dot separators, and unescape the remaining dots
yaml_path = [s.replace(r'\.', '.') for s in re.split(r'(?<!\\)\.', key)]

parser = libnetplan.Parser()
parser = netplan.Parser()
with tempfile.TemporaryFile() as tmp:
libnetplan.create_yaml_patch(yaml_path, value, tmp)
netplan._create_yaml_patch(yaml_path, value, tmp)
tmp.flush()

# Load fields that are about to be deleted (e.g. some.setting=NULL)
Expand All @@ -85,11 +85,11 @@ def command_set(self):
parser.load_yaml(tmp)

# Validate the final parser state
state = libnetplan.State()
state = netplan.State()
state.import_parser_results(parser)

if filename: # only act on the output file (a.k.a. "origin-hint")
parser_output_file = libnetplan.Parser()
parser_output_file = netplan.Parser()

# Load fields that are about to be deleted ("some.setting=NULL")
# Ignore those fields when parsing subsequent YAML files
Expand All @@ -103,7 +103,7 @@ def command_set(self):
# (a.k.a. "origin-hint", <filename>), have they been defined in
# pre-existing YAML files or not.
tmp.seek(0, io.SEEK_SET)
parser_output_file.load_nullable_overrides(tmp, constraint=filename)
parser_output_file._load_nullable_overrides(tmp, constraint=filename)

# Parse the full YAML hierarchy and new patch, ignoring any
# nullable overrides (netdefs/globals) from pre-existing files
Expand All @@ -122,8 +122,8 @@ def command_set(self):
# Import the partial parser state, ignoring duplicated netdefs
# from pre-existing YAML files, so we can force write the patch
# contents to the output file or update this file if exists.
state_output_file = libnetplan.State()
state_output_file = netplan.State()
state_output_file.import_parser_results(parser_output_file)
state_output_file.write_yaml_file(filename, self.root_dir)
state_output_file._write_yaml_file(filename, self.root_dir)
else:
state.update_yaml_hierarchy(FALLBACK_FILENAME, self.root_dir)
state._update_yaml_hierarchy(FALLBACK_FILENAME, self.root_dir)
6 changes: 3 additions & 3 deletions netplan_cli/cli/commands/try_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'''netplan try command line'''

import logging
import netplan
import os
import time
import shutil
Expand All @@ -29,7 +30,6 @@
from .. import utils
from .apply import NetplanApply
from ... import terminal
from ... import libnetplan

# Keep a timeout long enough to allow the network to converge, 60 seconds may
# be slightly short given some complex configs, i.e. if STP must reconverge.
Expand Down Expand Up @@ -179,11 +179,11 @@ def is_revertable(self):
# more than one device in them, and they can be set with special parameters
# to tweak their behavior, which are really hard to "revert", especially
# as systemd-networkd doesn't necessarily touch them when config changes.
multi_iface = {} # type: dict[str, libnetplan.NetDefinition]
multi_iface = {} # type: dict[str, netplan.NetDefinition]
multi_iface.update(np_state.bridges)
multi_iface.update(np_state.bonds)
for itf in multi_iface.values():
if not itf.is_trivial_compound_itf:
if not itf._is_trivial_compound_itf:
reason = "reverting custom parameters for bridges and bonds is not supported"
revert_unsupported.append((itf.id, reason))

Expand Down
2 changes: 1 addition & 1 deletion netplan_cli/cli/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import os

from . import utils
from ..libnetplan import NetplanException, NetplanValidationException, NetplanParserException
from netplan import NetplanException, NetplanValidationException, NetplanParserException


FALLBACK_PATH = '/usr/bin:/snap/bin'
Expand Down
4 changes: 2 additions & 2 deletions netplan_cli/cli/ovs.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def apply_ovs_cleanup(config_manager, ovs_old, ovs_current): # pragma: nocover

config_manager.parse()
ovs_ifaces = set()
for i in config_manager.all_defs.keys():
if (is_ovs_interface(i, config_manager.all_defs)):
for i in config_manager.netdefs.keys():
if (is_ovs_interface(i, config_manager.netdefs)):
ovs_ifaces.add(i)

# Tear down old OVS interfaces, not defined in the current config.
Expand Down
38 changes: 19 additions & 19 deletions netplan_cli/cli/sriov.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
from collections import defaultdict

from . import utils
from .. import libnetplan
from ..configmanager import ConfigurationError
import netplan

import netifaces

Expand Down Expand Up @@ -186,7 +186,7 @@ def _get_target_interface(interfaces, np_state, pf_link, pfs):
if pf_link not in pfs:
# handle the match: syntax, get the actual device name
pf_dev = np_state[pf_link]
if pf_dev.has_match:
if pf_dev._has_match:
# now here it's a bit tricky
set_name = pf_dev.set_name
if set_name and set_name in interfaces:
Expand All @@ -196,10 +196,10 @@ def _get_target_interface(interfaces, np_state, pf_link, pfs):
pfs[pf_link] = set_name
else:
for interface in interfaces:
if not pf_dev.match_interface(
itf_name=interface,
itf_driver=utils.get_interface_driver_name(interface),
itf_mac=utils.get_interface_macaddress(interface)):
if not pf_dev._match_interface(
iface_name=interface,
iface_driver=utils.get_interface_driver_name(interface),
iface_mac=utils.get_interface_macaddress(interface)):
continue
# we have a matching PF
# store the matching interface in the dictionary of
Expand Down Expand Up @@ -240,12 +240,12 @@ def get_vf_count_and_functions(interfaces, np_state,
Count how many VFs each PF will need.
"""
for nid, netdef in np_state.ethernets.items():
if netdef.sriov_link and _get_target_interface(interfaces, np_state, netdef.sriov_link.id, pfs):
if netdef.links.get('sriov') and _get_target_interface(interfaces, np_state, netdef.links.get('sriov').id, pfs):
vfs[nid] = None

try:
count = netdef.vf_count
except libnetplan.NetplanException as e:
count = netdef._vf_count
except netplan.NetplanException as e:
raise ConfigurationError(str(e))
if count == 0:
continue
Expand Down Expand Up @@ -375,10 +375,10 @@ def apply_sriov_config(config_manager, rootdir='/'):
Go through all interfaces, identify which ones are SR-IOV VFs, create
them and perform all other necessary setup.
"""
parser = libnetplan.Parser()
parser = netplan.Parser()
parser.load_yaml_hierarchy(rootdir)

np_state = libnetplan.State()
np_state = netplan.State()
np_state.import_parser_results(parser)

config_manager.parse()
Expand Down Expand Up @@ -426,13 +426,13 @@ def apply_sriov_config(config_manager, rootdir='/'):
# XXX: does matching those even make sense?
for vf in vfs:
netdef = np_state[vf]
if netdef.has_match:
if netdef._has_match:
# right now we only match by name, as I don't think matching per
# driver and/or macaddress makes sense
# TODO: print warning if other matches are provided

for interface in interfaces:
if netdef.match_interface(itf_name=interface):
if netdef._match_interface(iface_name=interface):
if vf in vfs and vfs[vf]:
raise ConfigurationError('matched more than one interface for a VF device: %s' % vf)
vfs[vf] = interface
Expand All @@ -443,14 +443,14 @@ def apply_sriov_config(config_manager, rootdir='/'):
# Walk the SR-IOV PFs and check if we need to change the eswitch mode
for netdef_id, iface in pfs.items():
netdef = np_state[netdef_id]
eswitch_mode = netdef.embedded_switch_mode
eswitch_mode = netdef._embedded_switch_mode
if eswitch_mode in ['switchdev', 'legacy']:
pci_addr = _get_pci_slot_name(iface)
pcidev = PCIDevice(pci_addr)
if pcidev.is_pf:
logging.debug("Found VFs of {}: {}".format(pcidev, pcidev.vf_addrs))
if pcidev.vfs:
rebind_delayed = netdef.delay_virtual_functions_rebind
rebind_delayed = netdef._delay_virtual_functions_rebind
try:
unbind_vfs(pcidev.vfs, pcidev.driver)
pcidev.devlink_set('eswitch', 'mode', eswitch_mode)
Expand All @@ -462,10 +462,10 @@ def apply_sriov_config(config_manager, rootdir='/'):
for vlan, netdef in np_state.vlans.items():
# there is a special sriov vlan renderer that one can use to mark
# a selected vlan to be done in hardware (VLAN filtering)
if netdef.has_sriov_vlan_filter:
if netdef._has_sriov_vlan_filter:
# this only works for SR-IOV VF interfaces
link = netdef.vlan_link
vlan_id = netdef.vlan_id
link = netdef.links.get('vlan')
vlan_id = netdef._vlan_id

vf = vfs.get(link.id)
if not vf:
Expand All @@ -479,7 +479,7 @@ def apply_sriov_config(config_manager, rootdir='/'):
# get the parent pf interface
# first we fetch the related vf netplan entry
# and finally, get the matched pf interface
pf = pfs.get(link.sriov_link.id)
pf = pfs.get(link.links.get('sriov').id)

if vf in filtered_vlans_set:
raise ConfigurationError(
Expand Down
18 changes: 10 additions & 8 deletions netplan_cli/cli/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
import yaml

import dbus
import netplan

from . import utils
from .. import libnetplan

JSON = Union[Dict[str, 'JSON'], List['JSON'], int, str, float, bool, Type[None]]

Expand Down Expand Up @@ -481,25 +481,27 @@ class NetplanConfigState():

def __init__(self, subtree='all', rootdir='/'):

parser = libnetplan.Parser()
parser = netplan.Parser()
parser.load_yaml_hierarchy(rootdir)

np_state = libnetplan.State()
np_state = netplan.State()
np_state.import_parser_results(parser)

self.state = StringIO()

if subtree == 'all':
np_state.dump_yaml(output_file=self.state)
np_state._dump_yaml(output_file=self.state)
else:
if not subtree.startswith('network'):
subtree = '.'.join(('network', subtree))
# Replace the '.' with '\t' but not at '\.' via negative lookbehind expression
subtree = re.sub(r'(?<!\\)\.', '\t', subtree).replace(r'\.', '.')
# Split at '.' but not at '\.' via negative lookbehind expression
subtree = re.split(r'(?<!\\)\.', subtree)
# Replace remaining '\.' by plain '.'
subtree = [elem.replace(r'\.', '.') for elem in subtree]

tmp_in = StringIO()
np_state.dump_yaml(output_file=tmp_in)
libnetplan.dump_yaml_subtree(subtree, tmp_in, self.state)
np_state._dump_yaml(output_file=tmp_in)
netplan._dump_yaml_subtree(subtree, tmp_in, self.state)

def __str__(self) -> str:
return self.state.getvalue()
Expand Down
15 changes: 7 additions & 8 deletions netplan_cli/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
import fnmatch
import re

from .. import libnetplan as np
from ..configmanager import ConfigurationError
from ..libnetplan import NetplanException
from netplan import NetDefinition, NetplanException


NM_SERVICE_NAME = 'NetworkManager.service'
Expand Down Expand Up @@ -175,13 +174,13 @@ def get_interface_macaddress(interface):


def find_matching_iface(interfaces: list, netdef):
assert isinstance(netdef, np.NetDefinition)
assert netdef.has_match
assert isinstance(netdef, NetDefinition)
assert netdef._has_match

matches = list(filter(lambda itf: netdef.match_interface(
itf_name=itf,
itf_driver=get_interface_driver_name(itf),
itf_mac=get_interface_macaddress(itf)), interfaces))
matches = list(filter(lambda itf: netdef._match_interface(
iface_name=itf,
iface_driver=get_interface_driver_name(itf),
iface_mac=get_interface_macaddress(itf)), interfaces))

# Return current name of unique matched interface, if available
if len(matches) != 1:
Expand Down
19 changes: 12 additions & 7 deletions netplan_cli/configmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
'''netplan configuration manager'''

import logging
import netplan
import os
import shutil
import sys
import tempfile

from typing import Optional

from . import libnetplan


class ConfigManager(object):
def __init__(self, prefix="/", extra_files={}):
Expand All @@ -36,7 +35,7 @@ def __init__(self, prefix="/", extra_files={}):
self.temp_run = os.path.join(self.tempdir, "run")
self.extra_files = extra_files
self.new_interfaces = set()
self.np_state: Optional[libnetplan.State] = None
self.np_state: Optional[netplan.State] = None

def __getattr__(self, attr):
assert self.np_state is not None, "Must call parse() before accessing the config."
Expand All @@ -58,7 +57,9 @@ def virtual_interfaces(self):
# what about ovs_ports?
interfaces.update(self.np_state.bridges)
interfaces.update(self.np_state.bonds)
interfaces.update(self.np_state.dummy_devices)
interfaces.update(self.np_state.tunnels)
interfaces.update(self.np_state.virtual_ethernets)
interfaces.update(self.np_state.vlans)
interfaces.update(self.np_state.vrfs)
return interfaces
Expand All @@ -72,20 +73,24 @@ def parse(self, extra_config=None):
"""

# /run/netplan shadows /etc/netplan/, which shadows /lib/netplan
parser = libnetplan.Parser()
parser = netplan.Parser()
try:
parser.load_yaml_hierarchy(rootdir=self.prefix)

if extra_config:
for f in extra_config:
parser.load_yaml(f)

self.np_state = libnetplan.State()
self.np_state = netplan.State()
self.np_state.import_parser_results(parser)
except libnetplan.NetplanException as e:
except netplan.NetplanException as e:
raise ConfigurationError(str(e))

self.np_state.dump_to_logs()
# Convoluted way to dump the parsed config to the logs...
with tempfile.TemporaryFile() as tmp:
self.np_state._dump_yaml(output_file=tmp)
logging.debug("Merged config:\n{}".format(tmp.read()))

return self.np_state

def add(self, config_dict):
Expand Down
2 changes: 2 additions & 0 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,8 @@ handle_wifi_access_points(NetplanParser* npp, yaml_node_t* node, const char* key
g_debug("%s: adding wifi AP '%s'", npp->current.netdef->id, access_point->ssid);

/* Check if there's already an SSID with that name */
// FIXME: This check fails on multi-pass parsing, e.g. when defined in
// the same YAML file with a set of virtual-ethernets peers.
if (npp->current.netdef->access_points &&
g_hash_table_lookup(npp->current.netdef->access_points, access_point->ssid)) {
ret = yaml_error(npp, key, error, "%s: Duplicate access point SSID '%s'", npp->current.netdef->id, access_point->ssid);
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/test_get_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import yaml

from netplan_cli.cli.commands.set import FALLBACK_FILENAME
from netplan_cli.libnetplan import NetplanException
from netplan import NetplanException
from tests.test_utils import call_cli


Expand Down
Loading

0 comments on commit 69ca31f

Please sign in to comment.