summaryrefslogtreecommitdiff
path: root/google-daemon/usr/share/google/google_daemon
diff options
context:
space:
mode:
Diffstat (limited to 'google-daemon/usr/share/google/google_daemon')
-rw-r--r--google-daemon/usr/share/google/google_daemon/accounts_manager.py6
-rwxr-xr-xgoogle-daemon/usr/share/google/google_daemon/desired_accounts.py6
-rwxr-xr-xgoogle-daemon/usr/share/google/google_daemon/manage_accounts.py6
-rwxr-xr-xgoogle-daemon/usr/share/google/google_daemon/manage_clock_sync.py85
-rwxr-xr-xgoogle-daemon/usr/share/google/google_daemon/metadata_watcher.py97
5 files changed, 191 insertions, 9 deletions
diff --git a/google-daemon/usr/share/google/google_daemon/accounts_manager.py b/google-daemon/usr/share/google/google_daemon/accounts_manager.py
index 0e9897b..c1f2351 100644
--- a/google-daemon/usr/share/google/google_daemon/accounts_manager.py
+++ b/google-daemon/usr/share/google/google_daemon/accounts_manager.py
@@ -50,9 +50,9 @@ class AccountsManager(object):
while True:
# Fork and run the key regeneration and account update while the
# parent waits for the subprocess to finish before continuing.
-
+
# Create a pipe used to get the new etag value from child
- reader, writer = os.pipe() # these are file descriptors, not file objects
+ reader, writer = os.pipe() # these are file descriptors, not file objects
pid = os.fork()
if pid:
# we are the parent
@@ -117,7 +117,7 @@ class AccountsManager(object):
for entry in all_accounts
if os.path.isfile(os.path.join(entry.pw_dir, keyfile_suffix))]
extra_usernames = set(sshable_usernames) - set(desired_accounts.keys())
-
+
if desired_accounts:
for username, ssh_keys in desired_accounts.iteritems():
if not username:
diff --git a/google-daemon/usr/share/google/google_daemon/desired_accounts.py b/google-daemon/usr/share/google/google_daemon/desired_accounts.py
index 9afe78c..feb3f14 100755
--- a/google-daemon/usr/share/google/google_daemon/desired_accounts.py
+++ b/google-daemon/usr/share/google/google_daemon/desired_accounts.py
@@ -87,7 +87,7 @@ def KeyHasExpired(key):
expire_str)
logging.error('Not expiring key.')
return False
-
+
# Expire the key if and only if we have exceeded the expiration timestamp.
return (datetime.datetime.utcnow() > expire_time)
@@ -170,7 +170,7 @@ class DesiredAccounts(object):
return (attribute_value, etag)
except urllib2.HTTPError as e:
if e.code == 404:
- # The attribute doesn't exist. Return None.
+ # The attribute doesn't exist. Return None.
# No need to log a warning.
return None
# rethrow the exception since we don't know what it is. Let the
@@ -231,6 +231,6 @@ class DesiredAccounts(object):
logging.debug('Project sshKeys attribute not found.')
# sshKeys doesn't exist for either project or instance.
account_data = None
-
+
self.attributes_etag = attributes_etag_cache
return AccountDataToDictionary(account_data)
diff --git a/google-daemon/usr/share/google/google_daemon/manage_accounts.py b/google-daemon/usr/share/google/google_daemon/manage_accounts.py
index 0696ab2..7e18ae6 100755
--- a/google-daemon/usr/share/google/google_daemon/manage_accounts.py
+++ b/google-daemon/usr/share/google/google_daemon/manage_accounts.py
@@ -41,12 +41,12 @@ from utils import System
def Main(accounts, desired_accounts, system, logger,
log_handler, lock_file, lock_fname=None, single_pass=True,
daemon_mode=False, debug_mode=False):
-
+
if not log_handler:
log_handler = system.MakeLoggingHandler(
'accounts-from-metadata', logging.handlers.SysLogHandler.LOG_AUTH)
system.SetLoggingHandler(logger, log_handler)
-
+
if debug_mode:
system.EnableDebugLogging(logger)
logging.debug('Running in Debug Mode')
@@ -54,7 +54,7 @@ def Main(accounts, desired_accounts, system, logger,
accounts_manager = AccountsManager(
accounts, desired_accounts, system, lock_file, lock_fname,
single_pass)
-
+
if daemon_mode:
manager_daemon = AccountsManagerDaemon(None, accounts_manager)
manager_daemon.StartDaemon()
diff --git a/google-daemon/usr/share/google/google_daemon/manage_clock_sync.py b/google-daemon/usr/share/google/google_daemon/manage_clock_sync.py
new file mode 100755
index 0000000..c49f699
--- /dev/null
+++ b/google-daemon/usr/share/google/google_daemon/manage_clock_sync.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Manages clock syncing after migration on GCE instances."""
+
+import logging
+import os
+import sys
+
+def FixPath():
+ parent_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ if os.path.isdir(parent_dir):
+ sys.path.append(parent_dir)
+
+
+FixPath()
+
+from utils import LockFile
+from utils import System
+from metadata_watcher import MetadataWatcher
+
+
+LOCKFILE = '/var/lock/google-clock-sync.lock'
+
+
+def HandleClockDriftToken(metadata_watcher, on_change):
+ """Watches for and responds to drift-token changes.
+
+ Args:
+ metadata_watcher: a MetadataWatcher object.
+ on_change: a callable to call for any change.
+ """
+ clock_drift_token_key = 'instance/virtual-clock/drift-token'
+
+ def Handler(event):
+ on_change(event)
+
+ metadata_watcher.WatchMetadataForever(clock_drift_token_key,
+ Handler, initial_value='')
+
+
+def OnChange(event):
+ """Called when clock drift token changes.
+
+ Args:
+ event: the new value of the drift token.
+ """
+ system = System()
+ logging.info('Clock drift token has changed: %s', event)
+ logging.info('Syncing system time with hardware clock...')
+ result = system.RunCommand(['/sbin/hwclock', '--hctosys'])
+ if system.RunCommandFailed(result):
+ logging.error('Syncing system time failed.')
+ else:
+ logging.info('Synced system time with hardware clock.')
+
+
+def Main(system=System(), logger=logging.getLogger(), log_handler=None,
+ lock_file=LockFile(), lock_fname=None):
+ if not log_handler:
+ log_handler = system.MakeLoggingHandler(
+ 'google-clock-sync', logging.handlers.SysLogHandler.LOG_SYSLOG)
+ system.SetLoggingHandler(logger, log_handler)
+ logging.info('Starting GCE clock sync')
+
+ if not lock_fname:
+ lock_fname = LOCKFILE
+ watcher = MetadataWatcher()
+ lock_file.RunExclusively(lock_fname, HandleClockDriftToken(watcher, OnChange))
+
+
+if __name__ == '__main__':
+ Main()
diff --git a/google-daemon/usr/share/google/google_daemon/metadata_watcher.py b/google-daemon/usr/share/google/google_daemon/metadata_watcher.py
new file mode 100755
index 0000000..af0a90a
--- /dev/null
+++ b/google-daemon/usr/share/google/google_daemon/metadata_watcher.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import httplib
+import time
+import urllib
+import urllib2
+
+
+METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1/'
+
+
+class Error(Exception):
+ pass
+
+
+class UnexpectedStatusException(Error):
+ pass
+
+
+class MetadataWatcher(object):
+ """Watches for changing metadata."""
+
+ def __init__(self, httplib_module=httplib, time_module=time,
+ urllib_module=urllib, urllib2_module=urllib2):
+ self.httplib = httplib_module
+ self.time = time_module
+ self.urllib = urllib_module
+ self.urllib2 = urllib2_module
+
+ def WatchMetadataForever(self, metadata_key, handler, initial_value=None):
+ """Watches for a change in the value of metadata.
+
+ Args:
+ metadata_key: The key identifying which metadata to watch for changes.
+ handler: A callable to call when the metadata value changes. Will be passed
+ a single parameter, the new value of the metadata.
+ initial_value: The expected initial value for the metadata. The handler will
+ not be called on the initial metadata request unless the value differs
+ from this.
+
+ Raises:
+ UnexpectedStatusException: If the http request is unsuccessful for an
+ unexpected reason.
+ """
+ params = {
+ 'wait_for_change': 'true',
+ 'last_etag': 0,
+ }
+
+ value = initial_value
+ while True:
+ # start a hanging-GET request for metadata key.
+ url = '{base_url}{key}?{params}'.format(
+ base_url=METADATA_URL,
+ key=metadata_key,
+ params=self.urllib.urlencode(params)
+ )
+ req = self.urllib2.Request(url, headers={'Metadata-Flavor': 'Google'})
+
+ try:
+ response = self.urllib2.urlopen(req)
+ content = response.read()
+ status = response.getcode()
+ except self.urllib2.HTTPError as e:
+ content = None
+ status = e.code
+
+ if status == self.httplib.SERVICE_UNAVAILABLE:
+ self.time.sleep(1)
+ continue
+ elif status == self.httplib.OK:
+ # Extract new metadata value and latest etag.
+ new_value = content
+ headers = response.info()
+ params['last_etag'] = headers['ETag']
+ else:
+ raise UnexpectedStatusException(status)
+
+ # If the metadata value changed, call the appropriate handler.
+ if value == initial_value:
+ value = new_value
+ elif value != new_value:
+ value = new_value
+ handler(value)