Merge ~mgerdts/cloud-init:lp1763512 into cloud-init:master

Proposed by Mike Gerdts
Status: Merged
Approved by: Scott Moser
Approved revision: 5437e98a9b721ac8d3432c957e7f89c07a48cae8
Merge reported by: Scott Moser
Merged at revision: 3f99f4aba8af559f99f1d46b2b46bea7622da1d0
Proposed branch: ~mgerdts/cloud-init:lp1763512
Merge into: cloud-init:master
Diff against target: 161 lines (+67/-5)
2 files modified
cloudinit/sources/DataSourceSmartOS.py (+41/-5)
tests/unittests/test_datasource/test_smartos.py (+26/-0)
Reviewer Review Type Date Requested Status
Ryan Harper Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+344168@code.launchpad.net

Commit message

Enable SmartOS network metadata to work with netplan via per-subnet routes

- Updated datadict reference URL
- Store sdc:routes metadata in DatasourceSmartOS
- Map sdc:routes values to per-interface subnet configuration
- Added unittest

Co-authored-by: Mike Gerdts <email address hidden>

LP: #1763512

To post a comment you must log in.
Revision history for this message
Ryan Harper (raharper) :
~mgerdts/cloud-init:lp1763512 updated
d62031d... by Mike Gerdts

address feedback from rharper

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1090/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1090/rebuild

review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) :
~mgerdts/cloud-init:lp1763512 updated
5437e98... by Mike Gerdts

more feedback from rharper

Revision history for this message
Ryan Harper (raharper) wrote :

Thanks for the doc update with the mapping. This looks good. Let's update the commit message box in this MP to match what you've added:

Enable SmartOS network metadata to work with netplan via per-subnet routes

- Updated datadict reference URL
- Store sdc:routes metadata in DatasourceSmartOS
- Map sdc:routes values to per-interface subnet configuration
- Added unittest

review: Approve
Revision history for this message
Mike Gerdts (mgerdts) wrote :

I'm not sure who will show up as the author when this is squashed. Dan McDonald was the initial author, I've been doing the tweaks to get it integrated. If I show up as the author, we should probably change the Co-author to include Dan.

Revision history for this message
Scott Moser (smoser) wrote :

An upstream commit landed for this bug.

To view that commit see the following URL:
https://git.launchpad.net/cloud-init/commit/?id=3f99f4ab

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py
2index fcb46b1..2c3b627 100644
3--- a/cloudinit/sources/DataSourceSmartOS.py
4+++ b/cloudinit/sources/DataSourceSmartOS.py
5@@ -17,7 +17,7 @@
6 # of a serial console.
7 #
8 # Certain behavior is defined by the DataDictionary
9-# http://us-east.manta.joyent.com/jmc/public/mdata/datadict.html
10+# https://eng.joyent.com/mdata/datadict.html
11 # Comments with "@datadictionary" are snippets of the definition
12
13 import base64
14@@ -299,6 +299,7 @@ class DataSourceSmartOS(sources.DataSource):
15 self.userdata_raw = ud
16 self.vendordata_raw = md['vendor-data']
17 self.network_data = md['network-data']
18+ self.routes_data = md['routes']
19
20 self._set_provisioned()
21 return True
22@@ -322,7 +323,8 @@ class DataSourceSmartOS(sources.DataSource):
23 convert_smartos_network_data(
24 network_data=self.network_data,
25 dns_servers=self.metadata['dns_servers'],
26- dns_domain=self.metadata['dns_domain']))
27+ dns_domain=self.metadata['dns_domain'],
28+ routes=self.routes_data))
29 return self._network_config
30
31
32@@ -761,7 +763,8 @@ def get_smartos_environ(uname_version=None, product_name=None):
33
34 # Convert SMARTOS 'sdc:nics' data to network_config yaml
35 def convert_smartos_network_data(network_data=None,
36- dns_servers=None, dns_domain=None):
37+ dns_servers=None, dns_domain=None,
38+ routes=None):
39 """Return a dictionary of network_config by parsing provided
40 SMARTOS sdc:nics configuration data
41
42@@ -779,6 +782,10 @@ def convert_smartos_network_data(network_data=None,
43 keys are related to ip configuration. For each ip in the 'ips' list
44 we create a subnet entry under 'subnets' pairing the ip to a one in
45 the 'gateways' list.
46+
47+ Each route in sdc:routes is mapped to a route on each interface.
48+ The sdc:routes properties 'dst' and 'gateway' map to 'network' and
49+ 'gateway'. The 'linklocal' sdc:routes property is ignored.
50 """
51
52 valid_keys = {
53@@ -801,6 +808,10 @@ def convert_smartos_network_data(network_data=None,
54 'scope',
55 'type',
56 ],
57+ 'route': [
58+ 'network',
59+ 'gateway',
60+ ],
61 }
62
63 if dns_servers:
64@@ -815,6 +826,9 @@ def convert_smartos_network_data(network_data=None,
65 else:
66 dns_domain = []
67
68+ if not routes:
69+ routes = []
70+
71 def is_valid_ipv4(addr):
72 return '.' in addr
73
74@@ -841,6 +855,7 @@ def convert_smartos_network_data(network_data=None,
75 if ip == "dhcp":
76 subnet = {'type': 'dhcp4'}
77 else:
78+ routeents = []
79 subnet = dict((k, v) for k, v in nic.items()
80 if k in valid_keys['subnet'])
81 subnet.update({
82@@ -862,6 +877,25 @@ def convert_smartos_network_data(network_data=None,
83 pgws[proto]['gw'] = gateways[0]
84 subnet.update({'gateway': pgws[proto]['gw']})
85
86+ for route in routes:
87+ rcfg = dict((k, v) for k, v in route.items()
88+ if k in valid_keys['route'])
89+ # Linux uses the value of 'gateway' to determine
90+ # automatically if the route is a forward/next-hop
91+ # (non-local IP for gateway) or an interface/resolver
92+ # (local IP for gateway). So we can ignore the
93+ # 'interface' attribute of sdc:routes, because SDC
94+ # guarantees that the gateway is a local IP for
95+ # "interface=true".
96+ #
97+ # Eventually we should be smart and compare "gateway"
98+ # to see if it's in the prefix. We can then smartly
99+ # add or not-add this route. But for now,
100+ # when in doubt, use brute force! Routes for everyone!
101+ rcfg.update({'network': route['dst']})
102+ routeents.append(rcfg)
103+ subnet.update({'routes': routeents})
104+
105 subnets.append(subnet)
106 cfg.update({'subnets': subnets})
107 config.append(cfg)
108@@ -905,12 +939,14 @@ if __name__ == "__main__":
109 keyname = SMARTOS_ATTRIB_JSON[key]
110 data[key] = client.get_json(keyname)
111 elif key == "network_config":
112- for depkey in ('network-data', 'dns_servers', 'dns_domain'):
113+ for depkey in ('network-data', 'dns_servers', 'dns_domain',
114+ 'routes'):
115 load_key(client, depkey, data)
116 data[key] = convert_smartos_network_data(
117 network_data=data['network-data'],
118 dns_servers=data['dns_servers'],
119- dns_domain=data['dns_domain'])
120+ dns_domain=data['dns_domain'],
121+ routes=data['routes'])
122 else:
123 if key in SMARTOS_ATTRIB_MAP:
124 keyname, strip = SMARTOS_ATTRIB_MAP[key]
125diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py
126index 706e8eb..dca0b3d 100644
127--- a/tests/unittests/test_datasource/test_smartos.py
128+++ b/tests/unittests/test_datasource/test_smartos.py
129@@ -1027,6 +1027,32 @@ class TestNetworkConversion(TestCase):
130 found = convert_net(SDC_NICS_SINGLE_GATEWAY)
131 self.assertEqual(expected, found)
132
133+ def test_routes_on_all_nics(self):
134+ routes = [
135+ {'linklocal': False, 'dst': '3.0.0.0/8', 'gateway': '8.12.42.3'},
136+ {'linklocal': False, 'dst': '4.0.0.0/8', 'gateway': '10.210.1.4'}]
137+ expected = {
138+ 'version': 1,
139+ 'config': [
140+ {'mac_address': '90:b8:d0:d8:82:b4', 'mtu': 1500,
141+ 'name': 'net0', 'type': 'physical',
142+ 'subnets': [{'address': '8.12.42.26/24',
143+ 'gateway': '8.12.42.1', 'type': 'static',
144+ 'routes': [{'network': '3.0.0.0/8',
145+ 'gateway': '8.12.42.3'},
146+ {'network': '4.0.0.0/8',
147+ 'gateway': '10.210.1.4'}]}]},
148+ {'mac_address': '90:b8:d0:0a:51:31', 'mtu': 1500,
149+ 'name': 'net1', 'type': 'physical',
150+ 'subnets': [{'address': '10.210.1.27/24', 'type': 'static',
151+ 'routes': [{'network': '3.0.0.0/8',
152+ 'gateway': '8.12.42.3'},
153+ {'network': '4.0.0.0/8',
154+ 'gateway': '10.210.1.4'}]}]}]}
155+ found = convert_net(SDC_NICS_SINGLE_GATEWAY, routes=routes)
156+ self.maxDiff = None
157+ self.assertEqual(expected, found)
158+
159
160 @unittest2.skipUnless(get_smartos_environ() == SMARTOS_ENV_KVM,
161 "Only supported on KVM and bhyve guests under SmartOS")

Subscribers

People subscribed via source and target branches