summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2019-11-13 13:00:12 -0700
committerRyan Harper <ryan.harper@canonical.com>2019-11-13 12:00:12 -0800
commit9478f0f2fa6935d685092f344b23f34b883149a5 (patch)
tree08d971da0836a97281ddbebdefa99e9876fa1186
parent0469f70c8fde69cfbcbc7181a69964aafba50bbc (diff)
downloadvyos-cloud-init-9478f0f2fa6935d685092f344b23f34b883149a5.tar.gz
vyos-cloud-init-9478f0f2fa6935d685092f344b23f34b883149a5.zip
azure: support secondary ipv6 addresses (#33)
Azure's Instance Metadata Service (IMDS) reports multiple IPv6 addresses, via the http://169.254.169.254/metadata/instance/network route. Any additional values after the first in 'ipAddresses' under the 'ipv6' interface key are extracted and configured as static IPs on the interface.
-rwxr-xr-xcloudinit/sources/DataSourceAzure.py49
-rw-r--r--tests/unittests/test_datasource/test_azure.py34
2 files changed, 56 insertions, 27 deletions
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
index 44cca210..87a848ce 100755
--- a/cloudinit/sources/DataSourceAzure.py
+++ b/cloudinit/sources/DataSourceAzure.py
@@ -1321,36 +1321,35 @@ def parse_network_config(imds_metadata):
LOG.debug('Azure: generating network configuration from IMDS')
network_metadata = imds_metadata['network']
for idx, intf in enumerate(network_metadata['interface']):
+ # First IPv4 and/or IPv6 address will be obtained via DHCP.
+ # Any additional IPs of each type will be set as static
+ # addresses.
nicname = 'eth{idx}'.format(idx=idx)
- dev_config = {'dhcp4': False, 'dhcp6': False}
dhcp_override = {'route-metric': (idx + 1) * 100}
- for addr4 in intf['ipv4']['ipAddress']:
- privateIpv4 = addr4['privateIpAddress']
- if privateIpv4:
- if dev_config.get('dhcp4', False):
- # Append static address config for ip > 1
- netPrefix = intf['ipv4']['subnet'][0].get(
- 'prefix', '24')
- if not dev_config.get('addresses'):
- dev_config['addresses'] = []
- dev_config['addresses'].append(
- '{ip}/{prefix}'.format(
- ip=privateIpv4, prefix=netPrefix))
- else:
- dev_config['dhcp4'] = True
+ dev_config = {'dhcp4': True, 'dhcp4-overrides': dhcp_override,
+ 'dhcp6': False}
+ for addr_type in ('ipv4', 'ipv6'):
+ addresses = intf.get(addr_type, {}).get('ipAddress', [])
+ if addr_type == 'ipv4':
+ default_prefix = '24'
+ else:
+ default_prefix = '128'
+ if addresses:
+ dev_config['dhcp6'] = True
# non-primary interfaces should have a higher
# route-metric (cost) so default routes prefer
# primary nic due to lower route-metric value
- dev_config['dhcp4-overrides'] = dhcp_override
- for addr6 in intf['ipv6']['ipAddress']:
- privateIpv6 = addr6['privateIpAddress']
- if privateIpv6:
- dev_config['dhcp6'] = True
- # non-primary interfaces should have a higher
- # route-metric (cost) so default routes prefer
- # primary nic due to lower route-metric value
- dev_config['dhcp6-overrides'] = dhcp_override
- break
+ dev_config['dhcp6-overrides'] = dhcp_override
+ for addr in addresses[1:]:
+ # Append static address config for ip > 1
+ netPrefix = intf[addr_type]['subnet'][0].get(
+ 'prefix', default_prefix)
+ privateIp = addr['privateIpAddress']
+ if not dev_config.get('addresses'):
+ dev_config['addresses'] = []
+ dev_config['addresses'].append(
+ '{ip}/{prefix}'.format(
+ ip=privateIp, prefix=netPrefix))
if dev_config:
mac = ':'.join(re.findall(r'..', intf['macAddress']))
dev_config.update(
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
index d92d7b2f..59e351de 100644
--- a/tests/unittests/test_datasource/test_azure.py
+++ b/tests/unittests/test_datasource/test_azure.py
@@ -197,9 +197,11 @@ class TestParseNetworkConfig(CiTestCase):
def test_ipv4_and_ipv6_route_metrics_match_for_nics(self):
"""parse_network_config emits matching ipv4 and ipv6 route-metrics."""
expected = {'ethernets': {
- 'eth0': {'dhcp4': True,
+ 'eth0': {'addresses': ['10.0.0.5/24', '2001:dead:beef::2/128'],
+ 'dhcp4': True,
'dhcp4-overrides': {'route-metric': 100},
- 'dhcp6': False,
+ 'dhcp6': True,
+ 'dhcp6-overrides': {'route-metric': 100},
'match': {'macaddress': '00:0d:3a:04:75:98'},
'set-name': 'eth0'},
'eth1': {'set-name': 'eth1',
@@ -214,6 +216,14 @@ class TestParseNetworkConfig(CiTestCase):
'dhcp6': True,
'dhcp6-overrides': {'route-metric': 300}}}, 'version': 2}
imds_data = copy.deepcopy(NETWORK_METADATA)
+ nic1 = imds_data['network']['interface'][0]
+ nic1['ipv4']['ipAddress'].append({'privateIpAddress': '10.0.0.5'})
+
+ nic1['ipv6'] = {
+ "subnet": [{"address": "2001:dead:beef::16"}],
+ "ipAddress": [{"privateIpAddress": "2001:dead:beef::1"},
+ {"privateIpAddress": "2001:dead:beef::2"}]
+ }
imds_data['network']['interface'].append(SECONDARY_INTERFACE)
third_intf = copy.deepcopy(SECONDARY_INTERFACE)
third_intf['macAddress'] = third_intf['macAddress'].replace('22', '33')
@@ -240,6 +250,26 @@ class TestParseNetworkConfig(CiTestCase):
nic1 = imds_data['network']['interface'][0]
nic1['ipv4']['ipAddress'].append({'privateIpAddress': '10.0.0.5'})
+ nic1['ipv6'] = {
+ "subnet": [{"prefix": "10", "address": "2001:dead:beef::16"}],
+ "ipAddress": [{"privateIpAddress": "2001:dead:beef::1"}]
+ }
+ self.assertEqual(expected, dsaz.parse_network_config(imds_data))
+
+ def test_ipv6_secondary_ips_will_be_static_cidrs(self):
+ """parse_network_config emits primary ipv6 as dhcp others are static"""
+ expected = {'ethernets': {
+ 'eth0': {'addresses': ['10.0.0.5/24', '2001:dead:beef::2/10'],
+ 'dhcp4': True,
+ 'dhcp4-overrides': {'route-metric': 100},
+ 'dhcp6': True,
+ 'dhcp6-overrides': {'route-metric': 100},
+ 'match': {'macaddress': '00:0d:3a:04:75:98'},
+ 'set-name': 'eth0'}}, 'version': 2}
+ imds_data = copy.deepcopy(NETWORK_METADATA)
+ nic1 = imds_data['network']['interface'][0]
+ nic1['ipv4']['ipAddress'].append({'privateIpAddress': '10.0.0.5'})
+
# Secondary ipv6 addresses currently ignored/unconfigured
nic1['ipv6'] = {
"subnet": [{"prefix": "10", "address": "2001:dead:beef::16"}],