← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~smoser/maas/trunk.maas-signal-clockskew into lp:maas

 

Scott Moser has proposed merging lp:~smoser/maas/trunk.maas-signal-clockskew into lp:maas.

Requested reviews:
  MAAS Maintainers (maas-maintainers)
Related bugs:
  Bug #978127 in MAAS: "incorrect time on node causes failed oauth"
  https://bugs.launchpad.net/maas/+bug/978127

For more details, see:
https://code.launchpad.net/~smoser/maas/trunk.maas-signal-clockskew/+merge/128794

make maas-signal adjust clock and retry on 401 or 403 errors.

oauth failures due to incorrect clock by maas return error 401.
If we see this error, then maas-signal will read the timestamp in the
response header and update future requests based on that time.

This is the same approach fix that went into cloud-init.

-- 
https://code.launchpad.net/~smoser/maas/trunk.maas-signal-clockskew/+merge/128794
Your team MAAS Maintainers is requested to review the proposed merge of lp:~smoser/maas/trunk.maas-signal-clockskew into lp:maas.
=== modified file 'etc/maas/commissioning-user-data'
--- etc/maas/commissioning-user-data	2012-10-08 01:51:07 +0000
+++ etc/maas/commissioning-user-data	2012-10-09 19:01:25 +0000
@@ -305,6 +305,7 @@
 add_bin "maas-signal" <<"END_MAAS_SIGNAL"
 #!/usr/bin/python
 
+from email.utils import parsedate
 import mimetypes
 import oauth.oauth as oauth
 import os.path
@@ -369,13 +370,17 @@
     return body, headers
 
 
-def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret):
+def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
+                  clockskew=0):
     consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
     token = oauth.OAuthToken(token_key, token_secret)
+
+    timestamp = int(time.time()) + clockskew
+
     params = {
         'oauth_version': "1.0",
         'oauth_nonce': oauth.generate_nonce(),
-        'oauth_timestamp': int(time.time()),
+        'oauth_timestamp': timestamp,
         'oauth_token': token.key,
         'oauth_consumer_key': consumer.key,
     }
@@ -393,13 +398,43 @@
     else:
         headers = dict(headers)
 
-    if creds.get('consumer_key', None) != None:
-        headers.update(oauth_headers(url,
-            consumer_key=creds['consumer_key'], token_key=creds['token_key'],
-            token_secret=creds['token_secret'],
-            consumer_secret=creds['consumer_secret']))
-    req = urllib2.Request(url=url, data=data, headers=headers)
-    return(urllib2.urlopen(req).read())
+    clockskew = 0
+
+    def warn(msg):
+        sys.stderr.write(msg + "\n")
+
+    exc = Exception("Unexpected Error")
+    for naptime in (1, 1, 2, 4, 8, 16, 32):
+        if creds.get('consumer_key', None) != None:
+            headers.update(oauth_headers(url,
+                consumer_key=creds['consumer_key'],
+                token_key=creds['token_key'],
+                token_secret=creds['token_secret'],
+                consumer_secret=creds['consumer_secret'],
+                clockskew=clockskew))
+        try:
+            req = urllib2.Request(url=url, data=data, headers=headers)
+            return(urllib2.urlopen(req).read())
+        except urllib2.HTTPError as exc:
+            if 'date' not in exc.headers:
+                warn("date field not in %d headers" % exc.code)
+                pass
+            elif (exc.code == 401 or exc.code == 403):
+                date = exc.headers['date']
+                try:
+                    ret_time = time.mktime(parsedate(date))
+                    clockskew = int(ret_time - time.time())
+                    warn("updated clock skew to %d" % clockskew)
+                except:
+                    warn("failed to convert date '%s'" % date)
+        except Exception as exc:
+            pass
+
+        warn("request to %s failed. sleeping %d.: %s" % (url, naptime, exc))
+        time.sleep(naptime)
+
+    raise exc
+
 
 def read_config(url, creds):
     if url.startswith("http://";) or url.startswith("https://";):


Follow ups