~cloud-init-dev/cloud-init/trunk

« back to all changes in this revision

Viewing changes to tests/unittests/test_net.py

improvements to eni rendering

Some improvements here, and some bug fixes.
 - bring curtin revno 394's to support post-up for interface aliases.
 - sort attributes per interface for nicer order and consistent rendering
 - use arrays for each 'section' rather than content += . This allows
   better separation of the sections and also will perform better as long
   strings with += are slow.
 - improve how 'lo' is handled. If a network state that was being rendered
   had an entry for 'lo', then the rendered ENI would have 2 'lo'
   sections.
 - no longer skip 'lo' sections when loading an ENI in parse_deb_config
 - fix inet value for subnets, don't add interface attributes to alias
   (LP: #1588547)

Also add some tests of reading yaml and rendering ENI.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
from cloudinit.sources.helpers import openstack
7
7
from cloudinit import util
8
8
 
 
9
from .helpers import dir2dict
9
10
from .helpers import mock
10
11
from .helpers import TestCase
11
12
 
17
18
import os
18
19
import shutil
19
20
import tempfile
 
21
import textwrap
 
22
import yaml
20
23
 
21
24
DHCP_CONTENT_1 = """
22
25
DEVICE='eth0'
141
144
    }
142
145
]
143
146
 
 
147
EXAMPLE_ENI = """
 
148
auto lo
 
149
iface lo inet loopback
 
150
   dns-nameservers 10.0.0.1
 
151
   dns-search foo.com
 
152
 
 
153
auto eth0
 
154
iface eth0 inet static
 
155
        address 1.2.3.12
 
156
        netmask 255.255.255.248
 
157
        broadcast 1.2.3.15
 
158
        gateway 1.2.3.9
 
159
        dns-nameservers 69.9.160.191 69.9.191.4
 
160
auto eth1
 
161
iface eth1 inet static
 
162
        address 10.248.2.4
 
163
        netmask 255.255.255.248
 
164
        broadcast 10.248.2.7
 
165
"""
 
166
 
 
167
RENDERED_ENI = """
 
168
auto lo
 
169
iface lo inet loopback
 
170
    dns-nameservers 10.0.0.1
 
171
    dns-search foo.com
 
172
 
 
173
auto eth0
 
174
iface eth0 inet static
 
175
    address 1.2.3.12
 
176
    broadcast 1.2.3.15
 
177
    dns-nameservers 69.9.160.191 69.9.191.4
 
178
    gateway 1.2.3.9
 
179
    netmask 255.255.255.248
 
180
 
 
181
auto eth1
 
182
iface eth1 inet static
 
183
    address 10.248.2.4
 
184
    broadcast 10.248.2.7
 
185
    netmask 255.255.255.248
 
186
""".lstrip()
 
187
 
 
188
NETWORK_CONFIGS = {
 
189
    'small': {
 
190
        'expected_eni': textwrap.dedent("""\
 
191
            auto lo
 
192
            iface lo inet loopback
 
193
                dns-nameservers 1.2.3.4 5.6.7.8
 
194
                dns-search wark.maas
 
195
 
 
196
            iface eth1 inet manual
 
197
 
 
198
            auto eth99
 
199
            iface eth99 inet dhcp
 
200
                post-up ifup eth99:1
 
201
 
 
202
 
 
203
            auto eth99:1
 
204
            iface eth99:1 inet static
 
205
                address 192.168.21.3/24
 
206
                dns-nameservers 8.8.8.8 8.8.4.4
 
207
                dns-search barley.maas sach.maas
 
208
                post-up route add default gw 65.61.151.37 || true
 
209
                pre-down route del default gw 65.61.151.37 || true
 
210
        """).rstrip(' '),
 
211
        'yaml': textwrap.dedent("""
 
212
            version: 1
 
213
            config:
 
214
                # Physical interfaces.
 
215
                - type: physical
 
216
                  name: eth99
 
217
                  mac_address: "c0:d6:9f:2c:e8:80"
 
218
                  subnets:
 
219
                      - type: dhcp4
 
220
                      - type: static
 
221
                        address: 192.168.21.3/24
 
222
                        dns_nameservers:
 
223
                          - 8.8.8.8
 
224
                          - 8.8.4.4
 
225
                        dns_search: barley.maas sach.maas
 
226
                        routes:
 
227
                          - gateway: 65.61.151.37
 
228
                            netmask: 0.0.0.0
 
229
                            network: 0.0.0.0
 
230
                            metric: 2
 
231
                - type: physical
 
232
                  name: eth1
 
233
                  mac_address: "cf:d6:af:48:e8:80"
 
234
                - type: nameserver
 
235
                  address:
 
236
                    - 1.2.3.4
 
237
                    - 5.6.7.8
 
238
                  search:
 
239
                    - wark.maas
 
240
        """),
 
241
    },
 
242
    'all': {
 
243
        'expected_eni': ("""\
 
244
auto lo
 
245
iface lo inet loopback
 
246
    dns-nameservers 8.8.8.8 4.4.4.4 8.8.4.4
 
247
    dns-search barley.maas wark.maas foobar.maas
 
248
 
 
249
iface eth0 inet manual
 
250
 
 
251
auto eth1
 
252
iface eth1 inet manual
 
253
    bond-master bond0
 
254
    bond-mode active-backup
 
255
 
 
256
auto eth2
 
257
iface eth2 inet manual
 
258
    bond-master bond0
 
259
    bond-mode active-backup
 
260
 
 
261
iface eth3 inet manual
 
262
 
 
263
iface eth4 inet manual
 
264
 
 
265
# control-manual eth5
 
266
iface eth5 inet dhcp
 
267
 
 
268
auto bond0
 
269
iface bond0 inet6 dhcp
 
270
    bond-mode active-backup
 
271
    bond-slaves none
 
272
    hwaddress aa:bb:cc:dd:ee:ff
 
273
 
 
274
auto br0
 
275
iface br0 inet static
 
276
    address 192.168.14.2/24
 
277
    bridge_ports eth3 eth4
 
278
    bridge_stp off
 
279
    post-up ifup br0:1
 
280
 
 
281
 
 
282
auto br0:1
 
283
iface br0:1 inet6 static
 
284
    address 2001:1::1/64
 
285
 
 
286
auto bond0.200
 
287
iface bond0.200 inet dhcp
 
288
    vlan-raw-device bond0
 
289
    vlan_id 200
 
290
 
 
291
auto eth0.101
 
292
iface eth0.101 inet static
 
293
    address 192.168.0.2/24
 
294
    dns-nameservers 192.168.0.10 10.23.23.134
 
295
    dns-search barley.maas sacchromyces.maas brettanomyces.maas
 
296
    gateway 192.168.0.1
 
297
    mtu 1500
 
298
    vlan-raw-device eth0
 
299
    vlan_id 101
 
300
    post-up ifup eth0.101:1
 
301
 
 
302
 
 
303
auto eth0.101:1
 
304
iface eth0.101:1 inet static
 
305
    address 192.168.2.10/24
 
306
 
 
307
post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
 
308
pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
 
309
"""),
 
310
        'yaml': textwrap.dedent("""
 
311
            version: 1
 
312
            config:
 
313
                # Physical interfaces.
 
314
                - type: physical
 
315
                  name: eth0
 
316
                  mac_address: "c0:d6:9f:2c:e8:80"
 
317
                - type: physical
 
318
                  name: eth1
 
319
                  mac_address: "aa:d6:9f:2c:e8:80"
 
320
                - type: physical
 
321
                  name: eth2
 
322
                  mac_address: "c0:bb:9f:2c:e8:80"
 
323
                - type: physical
 
324
                  name: eth3
 
325
                  mac_address: "66:bb:9f:2c:e8:80"
 
326
                - type: physical
 
327
                  name: eth4
 
328
                  mac_address: "98:bb:9f:2c:e8:80"
 
329
                # specify how ifupdown should treat iface
 
330
                # control is one of ['auto', 'hotplug', 'manual']
 
331
                # with manual meaning ifup/ifdown should not affect the iface
 
332
                # useful for things like iscsi root + dhcp
 
333
                - type: physical
 
334
                  name: eth5
 
335
                  mac_address: "98:bb:9f:2c:e8:8a"
 
336
                  subnets:
 
337
                    - type: dhcp
 
338
                      control: manual
 
339
                # VLAN interface.
 
340
                - type: vlan
 
341
                  name: eth0.101
 
342
                  vlan_link: eth0
 
343
                  vlan_id: 101
 
344
                  mtu: 1500
 
345
                  subnets:
 
346
                    - type: static
 
347
                      address: 192.168.0.2/24
 
348
                      gateway: 192.168.0.1
 
349
                      dns_nameservers:
 
350
                        - 192.168.0.10
 
351
                        - 10.23.23.134
 
352
                      dns_search:
 
353
                        - barley.maas
 
354
                        - sacchromyces.maas
 
355
                        - brettanomyces.maas
 
356
                    - type: static
 
357
                      address: 192.168.2.10/24
 
358
                # Bond.
 
359
                - type: bond
 
360
                  name: bond0
 
361
                  # if 'mac_address' is omitted, the MAC is taken from
 
362
                  # the first slave.
 
363
                  mac_address: "aa:bb:cc:dd:ee:ff"
 
364
                  bond_interfaces:
 
365
                    - eth1
 
366
                    - eth2
 
367
                  params:
 
368
                    bond-mode: active-backup
 
369
                  subnets:
 
370
                    - type: dhcp6
 
371
                # A Bond VLAN.
 
372
                - type: vlan
 
373
                  name: bond0.200
 
374
                  vlan_link: bond0
 
375
                  vlan_id: 200
 
376
                  subnets:
 
377
                      - type: dhcp4
 
378
                # A bridge.
 
379
                - type: bridge
 
380
                  name: br0
 
381
                  bridge_interfaces:
 
382
                      - eth3
 
383
                      - eth4
 
384
                  ipv4_conf:
 
385
                      rp_filter: 1
 
386
                      proxy_arp: 0
 
387
                      forwarding: 1
 
388
                  ipv6_conf:
 
389
                      autoconf: 1
 
390
                      disable_ipv6: 1
 
391
                      use_tempaddr: 1
 
392
                      forwarding: 1
 
393
                      # basically anything in /proc/sys/net/ipv6/conf/.../
 
394
                  params:
 
395
                      bridge_stp: 'off'
 
396
                      bridge_fd: 0
 
397
                      bridge_maxwait: 0
 
398
                  subnets:
 
399
                      - type: static
 
400
                        address: 192.168.14.2/24
 
401
                      - type: static
 
402
                        address: 2001:1::1/64 # default to /64
 
403
                # A global nameserver.
 
404
                - type: nameserver
 
405
                  address: 8.8.8.8
 
406
                  search: barley.maas
 
407
                # global nameservers and search in list form
 
408
                - type: nameserver
 
409
                  address:
 
410
                    - 4.4.4.4
 
411
                    - 8.8.4.4
 
412
                  search:
 
413
                    - wark.maas
 
414
                    - foobar.maas
 
415
                # A global route.
 
416
                - type: route
 
417
                  destination: 10.0.0.0/8
 
418
                  gateway: 11.0.0.1
 
419
                  metric: 3
 
420
        """).lstrip(),
 
421
    }
 
422
}
 
423
 
144
424
 
145
425
def _setup_test(tmp_dir, mock_get_devicelist, mock_sys_netdev_info,
146
426
                mock_sys_dev_path):
354
634
        self.assertEqual(found, self.simple_cfg)
355
635
 
356
636
 
 
637
class TestEniRoundTrip(TestCase):
 
638
    def setUp(self):
 
639
        super(TestCase, self).setUp()
 
640
        self.tmp_dir = tempfile.mkdtemp()
 
641
        self.addCleanup(shutil.rmtree, self.tmp_dir)
 
642
 
 
643
    def _render_and_read(self, network_config=None, state=None, eni_path=None,
 
644
                         links_prefix=None, netrules_path=None):
 
645
        if network_config:
 
646
            ns = network_state.parse_net_config_data(network_config)
 
647
        elif state:
 
648
            ns = state
 
649
        else:
 
650
            raise ValueError("Expected data or state, got neither")
 
651
 
 
652
        if eni_path is None:
 
653
            eni_path = 'etc/network/interfaces'
 
654
 
 
655
        renderer = eni.Renderer(
 
656
            config={'eni_path': eni_path, 'links_path_prefix': links_prefix,
 
657
                    'netrules_path': netrules_path})
 
658
 
 
659
        renderer.render_network_state(self.tmp_dir, ns)
 
660
        return dir2dict(self.tmp_dir)
 
661
 
 
662
    def testsimple_convert_and_render(self):
 
663
        network_config = eni.convert_eni_data(EXAMPLE_ENI)
 
664
        files = self._render_and_read(network_config=network_config)
 
665
        self.assertEqual(
 
666
            RENDERED_ENI.splitlines(),
 
667
            files['/etc/network/interfaces'].splitlines())
 
668
 
 
669
    def testsimple_render_all(self):
 
670
        entry = NETWORK_CONFIGS['all']
 
671
        files = self._render_and_read(network_config=yaml.load(entry['yaml']))
 
672
        self.assertEqual(
 
673
            entry['expected_eni'].splitlines(),
 
674
            files['/etc/network/interfaces'].splitlines())
 
675
 
 
676
    def testsimple_render_small(self):
 
677
        entry = NETWORK_CONFIGS['small']
 
678
        files = self._render_and_read(network_config=yaml.load(entry['yaml']))
 
679
        self.assertEqual(
 
680
            entry['expected_eni'].splitlines(),
 
681
            files['/etc/network/interfaces'].splitlines())
 
682
 
 
683
 
357
684
def _gzip_data(data):
358
685
    with io.BytesIO() as iobuf:
359
686
        gzfp = gzip.GzipFile(mode="wb", fileobj=iobuf)