← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~harlowja/cloud-init/shared-wait-metadata into lp:cloud-init

 

Joshua Harlow has proposed merging lp:~harlowja/cloud-init/shared-wait-metadata into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~harlowja/cloud-init/shared-wait-metadata/+merge/206567

Move shared waiting function to util

The ec2 and openstack datasources use a similar
piece of code for waiting for there metadata services
to become accessible (which varies depending on cloud,
service provider...) so its much nicer if we move that
duplicated/similar code to a standard utility method and
use that instead.
-- 
https://code.launchpad.net/~harlowja/cloud-init/shared-wait-metadata/+merge/206567
Your team cloud init development team is requested to review the proposed merge of lp:~harlowja/cloud-init/shared-wait-metadata into lp:cloud-init.
=== modified file 'cloudinit/sources/DataSourceEc2.py'
--- cloudinit/sources/DataSourceEc2.py	2014-02-01 20:03:32 +0000
+++ cloudinit/sources/DataSourceEc2.py	2014-02-15 00:55:16 +0000
@@ -26,7 +26,7 @@
 from cloudinit import ec2_utils as ec2
 from cloudinit import log as logging
 from cloudinit import sources
-from cloudinit import url_helper as uhelp
+from cloudinit import url_helper
 from cloudinit import util
 
 LOG = logging.getLogger(__name__)
@@ -83,65 +83,33 @@
         return self.metadata['instance-id']
 
     def _get_url_settings(self):
-        mcfg = self.ds_cfg
-        if not mcfg:
-            mcfg = {}
+        # max_wait < 0 indicates do not wait
         max_wait = 120
         try:
-            max_wait = int(mcfg.get("max_wait", max_wait))
+            max_wait = int(self.ds_cfg.get("max_wait", max_wait))
         except Exception:
             util.logexc(LOG, "Failed to get max wait. using %s", max_wait)
 
         timeout = 50
         try:
-            timeout = max(0, int(mcfg.get("timeout", timeout)))
+            timeout = max(0, int(self.ds_cfg.get("timeout", timeout)))
         except Exception:
             util.logexc(LOG, "Failed to get timeout, using %s", timeout)
 
         return (max_wait, timeout)
 
     def wait_for_metadata_service(self):
-        mcfg = self.ds_cfg
-        if not mcfg:
-            mcfg = {}
-
         (max_wait, timeout) = self._get_url_settings()
-        if max_wait <= 0:
-            return False
-
-        # Remove addresses from the list that wont resolve.
-        mdurls = mcfg.get("metadata_urls", DEF_MD_URLS)
-        filtered = [x for x in mdurls if util.is_resolvable_url(x)]
-
-        if set(filtered) != set(mdurls):
-            LOG.debug("Removed the following from metadata urls: %s",
-                      list((set(mdurls) - set(filtered))))
-
-        if len(filtered):
-            mdurls = filtered
-        else:
-            LOG.warn("Empty metadata url list! using default list")
-            mdurls = DEF_MD_URLS
-
-        urls = []
-        url2base = {}
-        for url in mdurls:
-            cur = "%s/%s/meta-data/instance-id" % (url, self.api_ver)
-            urls.append(cur)
-            url2base[cur] = url
-
-        start_time = time.time()
-        url = uhelp.wait_for_url(urls=urls, max_wait=max_wait,
-                                timeout=timeout, status_cb=LOG.warn)
-
-        if url:
-            LOG.debug("Using metadata source: '%s'", url2base[url])
-        else:
-            LOG.critical("Giving up on md from %s after %s seconds",
-                            urls, int(time.time() - start_time))
-
-        self.metadata_address = url2base.get(url)
-        return bool(url)
+        md_urls = self.ds_cfg.get("metadata_urls", [])
+        md_check_path = url_helper.combine_url(self.api_ver,
+                                               'meta-data', 'instance-id')
+        md_url = util.wait_for_metadata_service(md_urls,
+                                                path=md_check_path,
+                                                max_wait=max_wait,
+                                                timeout=timeout,
+                                                fallback_urls=DEF_MD_URLS)
+        self.metadata_address = md_url
+        return bool(md_url)
 
     def device_name_to_device(self, name):
         # Consult metadata service, that has

=== modified file 'cloudinit/sources/DataSourceOpenStack.py'
--- cloudinit/sources/DataSourceOpenStack.py	2014-02-14 19:24:06 +0000
+++ cloudinit/sources/DataSourceOpenStack.py	2014-02-15 00:55:16 +0000
@@ -16,8 +16,6 @@
 #    You should have received a copy of the GNU General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import time
-
 from cloudinit import log as logging
 from cloudinit import sources
 from cloudinit import url_helper
@@ -29,6 +27,7 @@
 
 # Various defaults/constants...
 DEF_MD_URL = "http://169.254.169.254";
+DEF_MD_URLS = [DEF_MD_URL]
 DEFAULT_IID = "iid-dsopenstack"
 DEFAULT_METADATA = {
     "instance-id": DEFAULT_IID,
@@ -54,19 +53,14 @@
         return mstr
 
     def _get_url_settings(self):
-        # TODO(harlowja): this is shared with ec2 datasource, we should just
-        # move it to a shared location instead...
-        # Note: the defaults here are different though.
-
         # max_wait < 0 indicates do not wait
         max_wait = -1
-        timeout = 10
-
         try:
             max_wait = int(self.ds_cfg.get("max_wait", max_wait))
         except Exception:
             util.logexc(LOG, "Failed to get max wait. using %s", max_wait)
 
+        timeout = 10
         try:
             timeout = max(0, int(self.ds_cfg.get("timeout", timeout)))
         except Exception:
@@ -74,38 +68,18 @@
         return (max_wait, timeout)
 
     def wait_for_metadata_service(self):
-        urls = self.ds_cfg.get("metadata_urls", [DEF_MD_URL])
-        filtered = [x for x in urls if util.is_resolvable_url(x)]
-        if set(filtered) != set(urls):
-            LOG.debug("Removed the following from metadata urls: %s",
-                      list((set(urls) - set(filtered))))
-        if len(filtered):
-            urls = filtered
-        else:
-            LOG.warn("Empty metadata url list! using default list")
-            urls = [DEF_MD_URL]
-
-        md_urls = []
-        url2base = {}
-        for url in urls:
-            md_url = url_helper.combine_url(url, 'openstack',
-                                            openstack.OS_LATEST,
-                                            'meta_data.json')
-            md_urls.append(md_url)
-            url2base[md_url] = url
-
         (max_wait, timeout) = self._get_url_settings()
-        start_time = time.time()
-        avail_url = url_helper.wait_for_url(urls=md_urls, max_wait=max_wait,
-                                            timeout=timeout)
-        if avail_url:
-            LOG.debug("Using metadata source: '%s'", url2base[avail_url])
-        else:
-            LOG.debug("Giving up on OpenStack md from %s after %s seconds",
-                      md_urls, int(time.time() - start_time))
-
-        self.metadata_address = url2base.get(avail_url)
-        return bool(avail_url)
+        md_urls = self.ds_cfg.get("metadata_urls", [])
+        md_check_path = url_helper.combine_url("openstack",
+                                               openstack.OS_LATEST,
+                                               'meta_data.json')
+        md_url = util.wait_for_metadata_service(md_urls,
+                                                path=md_check_path,
+                                                max_wait=max_wait,
+                                                timeout=timeout,
+                                                fallback_urls=DEF_MD_URLS)
+        self.metadata_address = md_url
+        return bool(md_url)
 
     def get_data(self):
         try:

=== modified file 'cloudinit/util.py'
--- cloudinit/util.py	2014-02-13 11:27:22 +0000
+++ cloudinit/util.py	2014-02-15 00:55:16 +0000
@@ -1796,6 +1796,43 @@
     return None
 
 
+def wait_for_metadata_service(urls, path=None, fallback_urls=None,
+                              max_wait=120, timeout=50):
+    resolvable_urls = [x for x in urls if is_resolvable_url(x)]
+    if set(resolvable_urls) != set(urls):
+        LOG.debug("Removed the following non-resolveable metadata urls: %s",
+                  list((set(urls) - set(resolvable_urls))))
+    if not resolvable_urls:
+        resolvable_urls = fallback_urls
+    if not resolvable_urls:
+        return None
+
+    urls = resolvable_urls
+    md_urls = []
+    url2base = {}
+    for url in urls:
+        if path:
+            md_url = url_helper.combine_url(url, path)
+        else:
+            md_url = url
+        md_urls.append(md_url)
+        url2base[md_url] = url
+
+    start_time = time.time()
+    url = url_helper.wait_for_url(urls=md_urls, max_wait=max_wait,
+                                  timeout=timeout, status_cb=LOG.warn)
+    total_time = int(time.time() - start_time)
+    if url:
+        md_url = url2base[url]
+        LOG.debug("Using metadata url: '%s' (found after %s seconds)",
+                  md_url, total_time)
+        return md_url
+    else:
+        LOG.critical("Giving up on metadata from %s after %s seconds",
+                     md_urls, total_time)
+        return None
+
+
 def get_mount_info(path, log=LOG):
     # Use /proc/$$/mountinfo to find the device where path is mounted.
     # This is done because with a btrfs filesystem using os.stat(path)


Follow ups