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
diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py
index fcb46b1..2c3b627 100644
--- a/cloudinit/sources/DataSourceSmartOS.py
+++ b/cloudinit/sources/DataSourceSmartOS.py
@@ -17,7 +17,7 @@
17# of a serial console.17# of a serial console.
18#18#
19# Certain behavior is defined by the DataDictionary19# Certain behavior is defined by the DataDictionary
20# http://us-east.manta.joyent.com/jmc/public/mdata/datadict.html20# https://eng.joyent.com/mdata/datadict.html
21# Comments with "@datadictionary" are snippets of the definition21# Comments with "@datadictionary" are snippets of the definition
2222
23import base6423import base64
@@ -299,6 +299,7 @@ class DataSourceSmartOS(sources.DataSource):
299 self.userdata_raw = ud299 self.userdata_raw = ud
300 self.vendordata_raw = md['vendor-data']300 self.vendordata_raw = md['vendor-data']
301 self.network_data = md['network-data']301 self.network_data = md['network-data']
302 self.routes_data = md['routes']
302303
303 self._set_provisioned()304 self._set_provisioned()
304 return True305 return True
@@ -322,7 +323,8 @@ class DataSourceSmartOS(sources.DataSource):
322 convert_smartos_network_data(323 convert_smartos_network_data(
323 network_data=self.network_data,324 network_data=self.network_data,
324 dns_servers=self.metadata['dns_servers'],325 dns_servers=self.metadata['dns_servers'],
325 dns_domain=self.metadata['dns_domain']))326 dns_domain=self.metadata['dns_domain'],
327 routes=self.routes_data))
326 return self._network_config328 return self._network_config
327329
328330
@@ -761,7 +763,8 @@ def get_smartos_environ(uname_version=None, product_name=None):
761763
762# Convert SMARTOS 'sdc:nics' data to network_config yaml764# Convert SMARTOS 'sdc:nics' data to network_config yaml
763def convert_smartos_network_data(network_data=None,765def convert_smartos_network_data(network_data=None,
764 dns_servers=None, dns_domain=None):766 dns_servers=None, dns_domain=None,
767 routes=None):
765 """Return a dictionary of network_config by parsing provided768 """Return a dictionary of network_config by parsing provided
766 SMARTOS sdc:nics configuration data769 SMARTOS sdc:nics configuration data
767770
@@ -779,6 +782,10 @@ def convert_smartos_network_data(network_data=None,
779 keys are related to ip configuration. For each ip in the 'ips' list782 keys are related to ip configuration. For each ip in the 'ips' list
780 we create a subnet entry under 'subnets' pairing the ip to a one in783 we create a subnet entry under 'subnets' pairing the ip to a one in
781 the 'gateways' list.784 the 'gateways' list.
785
786 Each route in sdc:routes is mapped to a route on each interface.
787 The sdc:routes properties 'dst' and 'gateway' map to 'network' and
788 'gateway'. The 'linklocal' sdc:routes property is ignored.
782 """789 """
783790
784 valid_keys = {791 valid_keys = {
@@ -801,6 +808,10 @@ def convert_smartos_network_data(network_data=None,
801 'scope',808 'scope',
802 'type',809 'type',
803 ],810 ],
811 'route': [
812 'network',
813 'gateway',
814 ],
804 }815 }
805816
806 if dns_servers:817 if dns_servers:
@@ -815,6 +826,9 @@ def convert_smartos_network_data(network_data=None,
815 else:826 else:
816 dns_domain = []827 dns_domain = []
817828
829 if not routes:
830 routes = []
831
818 def is_valid_ipv4(addr):832 def is_valid_ipv4(addr):
819 return '.' in addr833 return '.' in addr
820834
@@ -841,6 +855,7 @@ def convert_smartos_network_data(network_data=None,
841 if ip == "dhcp":855 if ip == "dhcp":
842 subnet = {'type': 'dhcp4'}856 subnet = {'type': 'dhcp4'}
843 else:857 else:
858 routeents = []
844 subnet = dict((k, v) for k, v in nic.items()859 subnet = dict((k, v) for k, v in nic.items()
845 if k in valid_keys['subnet'])860 if k in valid_keys['subnet'])
846 subnet.update({861 subnet.update({
@@ -862,6 +877,25 @@ def convert_smartos_network_data(network_data=None,
862 pgws[proto]['gw'] = gateways[0]877 pgws[proto]['gw'] = gateways[0]
863 subnet.update({'gateway': pgws[proto]['gw']})878 subnet.update({'gateway': pgws[proto]['gw']})
864879
880 for route in routes:
881 rcfg = dict((k, v) for k, v in route.items()
882 if k in valid_keys['route'])
883 # Linux uses the value of 'gateway' to determine
884 # automatically if the route is a forward/next-hop
885 # (non-local IP for gateway) or an interface/resolver
886 # (local IP for gateway). So we can ignore the
887 # 'interface' attribute of sdc:routes, because SDC
888 # guarantees that the gateway is a local IP for
889 # "interface=true".
890 #
891 # Eventually we should be smart and compare "gateway"
892 # to see if it's in the prefix. We can then smartly
893 # add or not-add this route. But for now,
894 # when in doubt, use brute force! Routes for everyone!
895 rcfg.update({'network': route['dst']})
896 routeents.append(rcfg)
897 subnet.update({'routes': routeents})
898
865 subnets.append(subnet)899 subnets.append(subnet)
866 cfg.update({'subnets': subnets})900 cfg.update({'subnets': subnets})
867 config.append(cfg)901 config.append(cfg)
@@ -905,12 +939,14 @@ if __name__ == "__main__":
905 keyname = SMARTOS_ATTRIB_JSON[key]939 keyname = SMARTOS_ATTRIB_JSON[key]
906 data[key] = client.get_json(keyname)940 data[key] = client.get_json(keyname)
907 elif key == "network_config":941 elif key == "network_config":
908 for depkey in ('network-data', 'dns_servers', 'dns_domain'):942 for depkey in ('network-data', 'dns_servers', 'dns_domain',
943 'routes'):
909 load_key(client, depkey, data)944 load_key(client, depkey, data)
910 data[key] = convert_smartos_network_data(945 data[key] = convert_smartos_network_data(
911 network_data=data['network-data'],946 network_data=data['network-data'],
912 dns_servers=data['dns_servers'],947 dns_servers=data['dns_servers'],
913 dns_domain=data['dns_domain'])948 dns_domain=data['dns_domain'],
949 routes=data['routes'])
914 else:950 else:
915 if key in SMARTOS_ATTRIB_MAP:951 if key in SMARTOS_ATTRIB_MAP:
916 keyname, strip = SMARTOS_ATTRIB_MAP[key]952 keyname, strip = SMARTOS_ATTRIB_MAP[key]
diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py
index 706e8eb..dca0b3d 100644
--- a/tests/unittests/test_datasource/test_smartos.py
+++ b/tests/unittests/test_datasource/test_smartos.py
@@ -1027,6 +1027,32 @@ class TestNetworkConversion(TestCase):
1027 found = convert_net(SDC_NICS_SINGLE_GATEWAY)1027 found = convert_net(SDC_NICS_SINGLE_GATEWAY)
1028 self.assertEqual(expected, found)1028 self.assertEqual(expected, found)
10291029
1030 def test_routes_on_all_nics(self):
1031 routes = [
1032 {'linklocal': False, 'dst': '3.0.0.0/8', 'gateway': '8.12.42.3'},
1033 {'linklocal': False, 'dst': '4.0.0.0/8', 'gateway': '10.210.1.4'}]
1034 expected = {
1035 'version': 1,
1036 'config': [
1037 {'mac_address': '90:b8:d0:d8:82:b4', 'mtu': 1500,
1038 'name': 'net0', 'type': 'physical',
1039 'subnets': [{'address': '8.12.42.26/24',
1040 'gateway': '8.12.42.1', 'type': 'static',
1041 'routes': [{'network': '3.0.0.0/8',
1042 'gateway': '8.12.42.3'},
1043 {'network': '4.0.0.0/8',
1044 'gateway': '10.210.1.4'}]}]},
1045 {'mac_address': '90:b8:d0:0a:51:31', 'mtu': 1500,
1046 'name': 'net1', 'type': 'physical',
1047 'subnets': [{'address': '10.210.1.27/24', 'type': 'static',
1048 'routes': [{'network': '3.0.0.0/8',
1049 'gateway': '8.12.42.3'},
1050 {'network': '4.0.0.0/8',
1051 'gateway': '10.210.1.4'}]}]}]}
1052 found = convert_net(SDC_NICS_SINGLE_GATEWAY, routes=routes)
1053 self.maxDiff = None
1054 self.assertEqual(expected, found)
1055
10301056
1031@unittest2.skipUnless(get_smartos_environ() == SMARTOS_ENV_KVM,1057@unittest2.skipUnless(get_smartos_environ() == SMARTOS_ENV_KVM,
1032 "Only supported on KVM and bhyve guests under SmartOS")1058 "Only supported on KVM and bhyve guests under SmartOS")

Subscribers

People subscribed via source and target branches