Conversation
917a3f1 to
a44c6b6
Compare
daniloegea
left a comment
There was a problem hiding this comment.
I think the most critical issue I've found is in the Interface class' constructor. It crashes in systems where network-manager is not installed, like LXD images (at least kinetic).
| import subprocess | ||
| import sys | ||
| import yaml | ||
| import netplan.cli.utils as utils |
There was a problem hiding this comment.
nitpick: We probably should follow PEP8 and group imports in this order:
- standard library import
- third party imports
- application imports
| self.adminstate: str = 'UP' if 'UP' in ip['flags'] else 'DOWN' | ||
| self.operstate: str = ip['operstate'].upper() | ||
| self.macaddress: str = None | ||
| if 'address' in ip and len(ip['address']) == 17: # 6 byte MAC |
There was a problem hiding this comment.
suggestion: Magic numbers look better as 'constants' with meaningful names, or we could even have a little private method returning the MAC address or None.
| @property | ||
| def netdef_id(self) -> str: | ||
| if self.backend == 'networkd': | ||
| return self.nd.get('NetworkFile', '').split( |
There was a problem hiding this comment.
question: If the goal here is to get the interface name, wouldn't it be better to just use the Name attribute?
There was a problem hiding this comment.
No. This is not about getting the interface name, but rather the Netplan netdef_id (which COULD be equal to the interface name, but can also be different). E.g. on my system I have this, where "usbC" is the NetdefID inside my netplan YAML config, while "lan0" is the interface name.
{
"Index" : 14,
"Name" : "lan0",
"AlternativeNames" : [],
"Type" : "ether",
"Driver" : "ax88179_178a",
"SetupState" : "configured",
"OperationalState" : "routable",
"CarrierState" : "carrier",
"AddressState" : "routable",
"IPv4AddressState" : "routable",
"IPv6AddressState" : "routable",
"OnlineState" : "online",
"NetworkFile" : "/run/systemd/network/10-netplan-usbC.network",
"LinkFile" : "/run/systemd/network/10-netplan-usbC.link",
"Path" : "pci-0000:00:0d.0-usb-0:3.2.1:1.0",
"Vendor" : "ASIX Electronics Corp.",
"Model" : "AX88179 Gigabit Ethernet"
}
| if 'flags' not in extra or 'link' not in extra['flags']: | ||
| non_local_ips.append(ip) | ||
| default_routes = [x for x in itf.routes if x.get('to', None) == 'default'] | ||
| if len(non_local_ips) > 0 and len(default_routes) > 0 and len(itf.dns_addresses) > 0: |
There was a problem hiding this comment.
nitpick: empty lists will evaluate to False so this could be simplified to if list1 and list2 and list3.
| dns_addr = ns.get('addresses', []) | ||
| dns_mode = ns.get('mode') | ||
| dns_search = ns.get('search', []) | ||
| if len(dns_addr) > 0: |
There was a problem hiding this comment.
nitpick: empty lists evaluate to False, could be simplified to if dns_addr:. There are few more instances like this across the code.
| # due to hard package dependencies | ||
| iproute2 = self.query_iproute2() | ||
| networkd = self.query_networkd() | ||
| if not iproute2 or not networkd: |
There was a problem hiding this comment.
question: Is it expected to fail on systems that don't use networkd? It's stopping here on my system (standard kinetic upgraded from jammy).
There was a problem hiding this comment.
systemd[-networkd] should be installed on any relevant system, as it is a dependency of netplan.io. IIRC I designed this new CLI in a way that it would assume networkd to be available... But maybe I should double-check if networkd is actually up and running on a system which does not necessarily depend on it (like a standard Ubuntu Desktop system).
There was a problem hiding this comment.
Dang. I can confirm that systemd-networkd is installed but not running on a default Ubuntu Desktop installation. Let's see what I can do about that (maybe starting the service before trying to query it).
There was a problem hiding this comment.
Checking the activity status of systemd-networkd.service and starting it if needed solves this issue. We assume systemd-networkd to be installed by default (it's part of the systemd package in Ubuntu).
One drawback: netplan status would be able to run without sudo privileges, if not for this reason of starting a system service... But maybe that's something to solve at a later time, as Netplan is a system/admin tool and requires root privileges for most of it's actions anyways.
There was a problem hiding this comment.
Right. So, assuming sd-networkd is a hard dependency for netplan status, and I don't have a strong opinion on that, we should check first if we can start it. If it's masked for example netplan status will crash.
It would be nice to show a nice message like "netplan status depends on networkd" if we can't start it. A good thing is that once it's running one can call netplan status as a non-privileged user.
There was a problem hiding this comment.
Or maybe we could just tell the user that networkd is not running and ask them to start it instead of trying to do that inside netplan...
There was a problem hiding this comment.
I'd like to have it working "out of the box" on a default Ubuntu Desktop installation, that's why I'd like to automatically have it started if needed.
But checking if systemd-networkd.service is masked is a fair point. We'll now be bailing out if networkd is masked with an error message notifying the user to start networkd. If it's just disabled OTOH we'll try to start it automatically.
Netplan's 'dbus' tests module was in conflict with the system's global 'dbus' module (i.e. D-Bus bindings), leading to failing tests. We rename the tests to 'netplan_dbus' to avoid this. Cleanup pytest-3 deprecation warnings while on it
We make use of the previously assembled structural JSON data, to make sure we always present the same data in JSON/YAML and human readable format and don't miss anything in the structual output.
|
Thank you very much for the review @daniloegea I think I addressed all of your remarks. PTAL. |
daniloegea
left a comment
There was a problem hiding this comment.
The output looks really nice. Now it's working on the scenarios I've tested before.
Found only 2 things we probably should address: we should at least check if networkd is not masked before trying to start it to avoid a crash and disable the conversion of sequences like :ab: to emojis.
| def command(self): | ||
| networkd: str = subprocess.check_output(['networkctl', '--json=short'], | ||
| universal_newlines=True) | ||
| networkd_data = yaml.safe_load(networkd) |
There was a problem hiding this comment.
thought: hmm using the yaml module to load a JSON string, not a problem just counterintuitive. Anyway, the result of json.loads(networkd) is exactly the same.
| logging.debug('Cannot query resolved DNS data: {}'.format(str(e))) | ||
| return (addresses, search) | ||
|
|
||
| def pretty_print(self, data: JSON, total: int, _console_width=None) -> None: |
There was a problem hiding this comment.
I've found by accident that sequences like :ab: and :1234: are being converted to emojis:
● 13: br123 bridge DOWN/UP (NetworkManager: br123)
MAC Address: ee:21:ad:24:f9:71
Addresses: a🆎b🆎a🔢a:abcd/128
Routes: a🆎b🆎a🔢a:abcd metric 426
| self.routes.append(elem) | ||
|
|
||
| self.addresses: list = None | ||
| if 'addr_info' in ip and ip['addr_info']: |
There was a problem hiding this comment.
nitpick (no need to change): since python 3.8 this pattern can be simplified a bit with the := operator
if addr_info := ip.get('addr_info'):
for addr in addr_info:
| # due to hard package dependencies | ||
| iproute2 = self.query_iproute2() | ||
| networkd = self.query_networkd() | ||
| if not iproute2 or not networkd: |
There was a problem hiding this comment.
Or maybe we could just tell the user that networkd is not running and ask them to start it instead of trying to do that inside netplan...
|
Thanks for another sanity check and spotting those issues! |
Add python3-dbus and python3-rich[1] to RDEPENDS. [1] canonical/netplan#290 Signed-off-by: Yi Zhao <yi.zhao@windriver.com> Signed-off-by: Khem Raj <raj.khem@gmail.com>
Add python3-dbus and python3-rich[1] to RDEPENDS. [1] canonical/netplan#290 Signed-off-by: Yi Zhao <yi.zhao@windriver.com> Signed-off-by: Khem Raj <raj.khem@gmail.com>
Description
Implement new, initial
netplan statuscommand, according to spec FO049, which will show a system's current networking state, querying data from multiple backends (e.g. sd-networkd, NetworkManager, iproute2, DBus/sd-resolved, ...) and connecting the interface data with its corresponding Netplan/Netdef ID.A new depdendency
python3-richis used for pretty printing the output to a terminal, in addition to structural JSON or YAML output. Usage:Example output (non-pretty, as we're missing colors/bold text in this GitHub PR view):
The code in this branch can be executed like this:
Checklist
make checksuccessfully.make check-coverage).