diff options
-rw-r--r-- | cloudinit/net/__init__.py | 13 | ||||
-rw-r--r-- | tests/unittests/test_net.py | 14 |
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) |