summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-Aurèle Brothier <m@brothier.org>2017-05-24 14:45:25 +0200
committerScott Moser <smoser@ubuntu.com>2017-06-06 10:07:44 -0600
commit543e25cda6235d18adb6485e4266944d59e1979d (patch)
tree84ea250beaaab4c1fd0107ea87722d52adf9a193
parentdc0e70d155b4ff7a3c914ae7aaed3a52571e2107 (diff)
downloadvyos-cloud-init-543e25cda6235d18adb6485e4266944d59e1979d.tar.gz
vyos-cloud-init-543e25cda6235d18adb6485e4266944d59e1979d.zip
net: when selecting a network device, use natural sort order
The code deciding which interface to choose as the default to request the IP address through DHCP does not sort the interfaces correctly. On Ubuntu Xenial images for example, the interfaces are named ens1, ens2, ens3..., ens11, ... depending on the pci bus address. The python sorting will list 'ens11' before 'ens3' for example despite the fact that 'ens3' should be before 'ens11'. This patch address this issue and sort the interface names according to a human sorting. Signed-off-by: Marc-Aurèle Brothier <m@brothier.org>
-rw-r--r--cloudinit/net/__init__.py13
-rw-r--r--tests/unittests/test_net.py14
2 files changed, 26 insertions, 1 deletions
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index 8c6cd057..65accbb0 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -17,6 +17,17 @@ SYS_CLASS_NET = "/sys/class/net/"
DEFAULT_PRIMARY_INTERFACE = 'eth0'
+def _natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
+ """Sorting for Humans: natural sort order. Can be use as the key to sort
+ functions.
+ This will sort ['eth0', 'ens3', 'ens10', 'ens12', 'ens8', 'ens0'] as
+ ['ens0', 'ens3', 'ens8', 'ens10', 'ens12', 'eth0'] instead of the simple
+ python way which will produce ['ens0', 'ens10', 'ens12', 'ens3', 'ens8',
+ 'eth0']."""
+ return [int(text) if text.isdigit() else text.lower()
+ for text in re.split(_nsre, s)]
+
+
def sys_dev_path(devname, path=""):
return SYS_CLASS_NET + devname + "/" + path
@@ -169,7 +180,7 @@ def generate_fallback_config():
# if eth0 exists use it above anything else, otherwise get the interface
# that we can read 'first' (using the sorted defintion of first).
- names = list(sorted(potential_interfaces))
+ names = list(sorted(potential_interfaces, key=_natural_sort_key))
if DEFAULT_PRIMARY_INTERFACE in names:
names.remove(DEFAULT_PRIMARY_INTERFACE)
names.insert(0, DEFAULT_PRIMARY_INTERFACE)
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index 5f7e902a..0000b075 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -1,6 +1,7 @@
# This file is part of cloud-init. See LICENSE file for license information.
from cloudinit import net
+from cloudinit.net import _natural_sort_key
from cloudinit.net import cmdline
from cloudinit.net import eni
from cloudinit.net import netplan
@@ -1659,6 +1660,19 @@ class TestGetInterfacesByMac(CiTestCase):
self.assertEqual('lo', ret[empty_mac])
+class TestInterfacesSorting(CiTestCase):
+
+ def test_natural_order(self):
+ data = ['ens5', 'ens6', 'ens3', 'ens20', 'ens13', 'ens2']
+ self.assertEqual(
+ sorted(data, key=_natural_sort_key),
+ ['ens2', 'ens3', 'ens5', 'ens6', 'ens13', 'ens20'])
+ data2 = ['enp2s0', 'enp2s3', 'enp0s3', 'enp0s13', 'enp0s8', 'enp1s2']
+ self.assertEqual(
+ sorted(data2, key=_natural_sort_key),
+ ['enp0s3', 'enp0s8', 'enp0s13', 'enp1s2', 'enp2s0', 'enp2s3'])
+
+
def _gzip_data(data):
with io.BytesIO() as iobuf:
gzfp = gzip.GzipFile(mode="wb", fileobj=iobuf)