summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)