Comment 4 for bug 1815051

Revision history for this message
Dmitrii Shcherbakov (dmitriis) wrote :

From what I see, aliases come from the base library (pyyaml) which uses aliases if object references are used:

https://git.launchpad.net/~usd-import-team/ubuntu/+source/pyyaml/tree/lib3/yaml/serializer.py?h=ubuntu/bionic#n11
    ANCHOR_TEMPLATE = 'id%03d'
# ...
    def generate_anchor(self, node):
        self.last_anchor_id += 1
        return self.ANCHOR_TEMPLATE % self.last_anchor_id

In [2]: import yaml

In [6]: a = {'name': 'foo', 'mtu': '1500'}
   ...: b = {'name': 'bar', 'mtu': '9000'}
   ...: c = {'ref1': a, 'ref2': a, 'ref3': b, 'ref4': b}
   ...: print(yaml.dump(c))
   ...:
   ...:
ref1: &id001 {mtu: '1500', name: foo}
ref2: *id001
ref3: &id002 {mtu: '9000', name: bar}
ref4: *id002

So there must be something wrong with how cloud-init produces the aggregated yaml file.

I think this happens because it renders yaml for each section and bond interfaces and VLAN interfaces belong to different sections while nameservers object is identical and is reused in both sections (e.g. 2 bonds, 2 VLAN interfaces both use the same nameservers section but give you an anchor *per section* as the second entry in each section uses the same nameservers key which results in id001 being present).

https://github.com/cloud-init/cloud-init/blob/ubuntu/18.4-0ubuntu1_18.04.1/cloudinit/net/netplan.py#L359-L367

        def _render_section(name, section):
            if section:
                dump = util.yaml_dumps({name: section},
                                       explicit_start=False,
                                       explicit_end=False)
                txt = util.indent(dump, ' ' * 4)
                return [txt]
            return []

        content.append("network:\n version: 2\n")
        content += _render_section('ethernets', ethernets)
        content += _render_section('wifis', wifis)
        content += _render_section('bonds', bonds) # <--- first id001 gets generated
        content += _render_section('bridges', bridges)
        content += _render_section('vlans', vlans) # <--- second id001 gets generated