← Back to team overview

cloud-init-dev team mailing list archive

Re: [Merge] ~chad.smith/cloud-init:feature/azure-network-on-boot into cloud-init:master

 

some little things/food for thought.
and you have some conflicts.


Diff comments:

> diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
> index 7007d9e..d30435e 100644
> --- a/cloudinit/sources/DataSourceAzure.py
> +++ b/cloudinit/sources/DataSourceAzure.py
> @@ -373,41 +390,86 @@ class DataSourceAzure(sources.DataSource):
>              except NonAzureDataSource:
>                  continue
>              except BrokenAzureDataSource as exc:
> -                raise exc
> +                msg = 'BrokenAzureDataSource: %s' % exc
> +                raise sources.InvalidMetaDataException(msg)
>              except util.MountFailedError:
>                  LOG.warning("%s was not mountable", cdev)
>                  continue
>  
>              if reprovision or self._should_reprovision(ret):
>                  ret = self._reprovision()
> -            (md, self.userdata_raw, cfg, files) = ret
> +            imds_md = get_metadata_from_imds(
> +                self.fallback_interface, retries=3)
> +            (md, userdata_raw, cfg, files) = ret
>              self.seed = cdev
> -            self.metadata = util.mergemanydict([md, DEFAULT_METADATA])
> -            self.cfg = util.mergemanydict([cfg, BUILTIN_CLOUD_CONFIG])
> +            crawled_data.update({
> +                'cfg': cfg,
> +                'files': files,
> +                'metadata': util.mergemanydict(
> +                    [md, {'imds': imds_md}]),
> +                'userdata_raw': userdata_raw})
>              found = cdev
>  
>              LOG.debug("found datasource in %s", cdev)
>              break
>  
>          if not found:
> -            return False
> +            raise sources.InvalidMetaDataException('No Azure metadata found')
>  
>          if found == ddir:
>              LOG.debug("using files cached in %s", ddir)
>  
>          seed = _get_random_seed()
>          if seed:
> -            self.metadata['random_seed'] = seed
> +            crawled_data['metadata']['random_seed'] = seed
> +        crawled_data['metadata']['instance-id'] = util.read_dmi_data(
> +            'system-uuid')
> +        return crawled_data
> +
> +    def is_platform_viable(self):

i like this idea, but I think that this is at best a @classmethod.
Ie, it doesn't use 'self' at all, and doesn't need to.

I think I prefer just having a method in the DataSourceAzure.py 
named 'is_platform_viable', and then having this call that.

thoughts?

> +        """Check platform environment to report if this datasource may run."""
> +        asset_tag = util.read_dmi_data('chassis-asset-tag')
> +        if asset_tag != AZURE_CHASSIS_ASSET_TAG:
> +            LOG.debug("Non-Azure DMI asset tag '%s' discovered.", asset_tag)
> +            return False
> +        return True
> +
> +    def clear_cached_attrs(self):
> +        """Reset any cached class attributes to defaults."""
> +        super(DataSourceAzure, self).clear_cached_attrs()
> +        self._metadata_imds = sources.UNSET
> +
> +    def _get_data(self):
> +        """Crawl and process datasource metadata caching metadata as attrs.
> +
> +        @return: True on success, False on error, invalid or disabled
> +            datasource.
> +        """
> +        if not self.is_platform_viable():
> +            return False
> +        try:
> +            crawled_data = util.log_time(
> +                        logfunc=LOG.debug, msg='Crawl of metadata service',
> +                        func=self.crawl_metadata)
> +        except sources.InvalidMetaDataException as e:
> +            LOG.warning('Could not crawl Azure metadata: %s', e)
> +            return False
> +
> +        # Process crawled data and augment with various config defaults
> +        self.cfg = util.mergemanydict(
> +            [crawled_data['cfg'], BUILTIN_CLOUD_CONFIG])
> +        self._metadata_imds = crawled_data['metadata']['imds']
> +        self.metadata = util.mergemanydict(
> +            [crawled_data['metadata'], DEFAULT_METADATA])
> +        self.userdata_raw = crawled_data['userdata_raw']
>  
>          user_ds_cfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {})
>          self.ds_cfg = util.mergemanydict([user_ds_cfg, self.ds_cfg])
>  
>          # walinux agent writes files world readable, but expects
>          # the directory to be protected.
> -        write_files(ddir, files, dirmode=0o700)
> -
> -        self.metadata['instance-id'] = util.read_dmi_data('system-uuid')
> -
> +        write_files(
> +            self.ds_cfg['data_dir'], crawled_data['files'], dirmode=0o700)
>          return True
>  
>      def device_name_to_device(self, name):
> @@ -543,22 +605,56 @@ class DataSourceAzure(sources.DataSource):
>      @property
>      def network_config(self):
>          """Generate a network config like net.generate_fallback_network() with
> -           the following execptions.
> +           the following exceptions.
>  
>             1. Probe the drivers of the net-devices present and inject them in
>                the network configuration under params: driver: <driver> value
>             2. Generate a fallback network config that does not include any of
>                the blacklisted devices.
>          """
> -        blacklist = ['mlx4_core']
>          if not self._network_config:
> -            LOG.debug('Azure: generating fallback configuration')
> -            # generate a network config, blacklist picking any mlx4_core devs
> -            netconfig = net.generate_fallback_config(
> -                blacklist_drivers=blacklist, config_driver=True)
> +            if self._metadata_imds != sources.UNSET and self._metadata_imds:
> +                netconfig = {'version': 2, 'ethernets': {}}
> +                LOG.debug('Azure: generating network configuration from IMDS')
> +                if self.distro and self.distro.name == 'ubuntu':
> +                    maybe_remove_ubuntu_network_config_scripts()

this doesnt seem the right place for this.
network_config() doesn't seem like it should have these side effects.
it is supposed to return a network config not potentially delete things.

i dont know wher those actuaons fit, but It doesn't feel right here.

> +                network_metadata = self._metadata_imds['network']
> +                for idx, intf in enumerate(network_metadata['interface']):

other datasources have this in a non-class method also, which makes it easier to test
the simple conversion.
  self.assertEqual(expected, convert_network_data(some_data))

> +                    nicname = 'eth{idx}'.format(idx=idx)
> +                    dev_config = {}
> +                    for addr4 in intf['ipv4']['ipAddress']:
> +                        privateIpv4 = addr4['privateIpAddress']
> +                        if privateIpv4:
> +                            if dev_config.get('dhcp4', False):
> +                                # Append static address config for nic > 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
> +                    for addr6 in intf['ipv6']['ipAddress']:
> +                        privateIpv6 = addr6['privateIpAddress']
> +                        if privateIpv6:
> +                            dev_config['dhcp6'] = True
> +                            break
> +                    if dev_config:
> +                        mac = ':'.join(re.findall(r'..', intf['macAddress']))
> +                        dev_config.update(
> +                            {'match': {'macaddress': mac.lower()},
> +                             'set-name': nicname})
> +                        netconfig['ethernets'][nicname] = dev_config
> +            else:
> +                blacklist = ['mlx4_core']
> +                LOG.debug('Azure: generating fallback configuration')
> +                # generate a network config, blacklist picking mlx4_core devs
> +                netconfig = net.generate_fallback_config(
> +                    blacklist_drivers=blacklist, config_driver=True)
>  
>              self._network_config = netconfig
> -
>          return self._network_config
>  
>  
> diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
> index e82716e..1258d10 100644
> --- a/tests/unittests/test_datasource/test_azure.py
> +++ b/tests/unittests/test_datasource/test_azure.py
> @@ -314,6 +511,20 @@ fdescfs            /dev/fd          fdescfs rw              0 0
>          self.assertTrue(ret)
>          self.assertEqual(data['agent_invoked'], cfg['agent_command'])
>  
> +    def test_network_config_set_from_imds(self):
> +        """Datasource.network_config returns IMDS network data."""
> +        odata = {}
> +        data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
> +        expected_network_config = {
> +            'ethernets': {
> +                'eth0': {'set-name': 'eth0',
> +                         'match': {'macaddress': '00:0d:3a:04:75:98'},
> +                         'dhcp4': True}},
> +            'version': 2}
> +        dsrc = self._get_ds(data)
> +        dsrc.get_data()
> +        self.assertEqual(expected_network_config, dsrc.network_config)

this is what i was talking about, if you have a 'convert_network_config' function in DataSourceAzure.py
then you can just test it with:
   self.assertEqual(expected_network_config, convert_network_config("That Config"))

> +
>      def test_user_cfg_set_agent_command(self):
>          # set dscfg in via base64 encoded yaml
>          cfg = {'agent_command': "my_command"}


-- 
https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/348704
Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:feature/azure-network-on-boot into cloud-init:master.


References