From 6208fe41e4e73d1f14fd4afc152565c8908684a2 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 19 Sep 2012 18:40:20 -0700 Subject: Fix the ifup so that if a list of devices is provided then each interface is brought up individually instead of using the '--all' which isn't on rhel. The default debian behavior will be to use this still though as it overrides the new bring up interfaces function for this case. --- cloudinit/distros/__init__.py | 29 +++++++++++++++-------------- cloudinit/distros/debian.py | 6 ++++++ cloudinit/distros/fedora.py | 1 - cloudinit/distros/rhel.py | 2 ++ cloudinit/distros/ubuntu.py | 1 - 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 3e9d934d..442e75b4 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -34,12 +34,6 @@ from cloudinit import log as logging from cloudinit import ssh_util from cloudinit import util -# TODO(harlowja): Make this via config?? -IFACE_ACTIONS = { - 'up': ['ifup', '--all'], - 'down': ['ifdown', '--all'], -} - LOG = logging.getLogger(__name__) @@ -134,10 +128,10 @@ class Distro(object): def apply_network(self, settings, bring_up=True): # Write it out - self._write_network(settings) + dev_names = self._write_network(settings) # Now try to bring them up if bring_up: - return self._interface_action('up') + return self._bring_up_interfaces(dev_names) return False @abc.abstractmethod @@ -189,13 +183,11 @@ class Distro(object): util.write_file(self._paths.join(False, "/etc/hosts"), contents, mode=0644) - def _interface_action(self, action): - if action not in IFACE_ACTIONS: - raise NotImplementedError("Unknown interface action %s" % (action)) - cmd = IFACE_ACTIONS[action] + def _bring_up_interface(self, device_name): + cmd = ['ifup', device_name] + LOG.debug("Attempting to run bring up interface %s using command %s", + device_name, cmd) try: - LOG.debug("Attempting to run %s interface action using command %s", - action, cmd) (_out, err) = util.subp(cmd) if len(err): LOG.warn("Running %s resulted in stderr output: %s", cmd, err) @@ -204,6 +196,15 @@ class Distro(object): util.logexc(LOG, "Running interface command %s failed", cmd) return False + def _bring_up_interfaces(self, device_names): + am_failed = 0 + for d in device_names: + if not self._bring_up_interface(d): + am_failed += 1 + if am_failed == 0: + return True + return False + def isuser(self, name): try: if pwd.getpwnam(name): diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index 5b4aa9f8..777c8530 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -56,6 +56,12 @@ class Distro(distros.Distro): def _write_network(self, settings): net_fn = self._paths.join(False, "/etc/network/interfaces") util.write_file(net_fn, settings) + return [] + + def _bring_up_interfaces(self, device_names): + if not device_names: + device_names = ['--all'] + return distros.Distro._bring_up_interfaces(self, device_names) def set_hostname(self, hostname): out_fn = self._paths.join(False, "/etc/hostname") diff --git a/cloudinit/distros/fedora.py b/cloudinit/distros/fedora.py index 9f76a116..f65a820d 100644 --- a/cloudinit/distros/fedora.py +++ b/cloudinit/distros/fedora.py @@ -28,5 +28,4 @@ LOG = logging.getLogger(__name__) class Distro(rhel.Distro): - distro_name = 'fedora' default_user = 'ec2-user' diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index ec4dc2cc..0e451b02 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -88,6 +88,7 @@ class Distro(distros.Distro): # Make the intermediate format as the rhel format... nameservers = [] searchservers = [] + dev_names = entries.keys() for (dev, info) in entries.iteritems(): net_fn = NETWORK_FN_TPL % (dev) net_ro_fn = self._paths.join(True, net_fn) @@ -127,6 +128,7 @@ class Distro(distros.Distro): util.write_file(net_rw_fn, w_contents, 0644) if nameservers or searchservers: self._write_resolve(nameservers, searchservers) + return dev_names def set_hostname(self, hostname): out_fn = self._paths.join(False, '/etc/sysconfig/network') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 22f8c2c5..4e697f82 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -29,7 +29,6 @@ LOG = logging.getLogger(__name__) class Distro(debian.Distro): - distro_name = 'ubuntu' default_user = 'ubuntu' default_user_groups = ("adm,audio,cdrom,dialout,floppy,video," "plugdev,dip,netdev,sudo") -- cgit v1.2.3 From 355b0e372aec58360d4fb728088edefec27f86a1 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 19 Sep 2012 20:57:30 -0700 Subject: Rework the rhel sysconfig writing/updating so that it goes through a single function which helps ensure correctness. Also write to /etc/sysconfig/network when we have written out devices to ensure that networking is on. --- cloudinit/distros/rhel.py | 123 ++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index ec4dc2cc..c00627c6 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -26,6 +26,7 @@ from cloudinit import distros from cloudinit import helpers from cloudinit import log as logging from cloudinit import util +from cloudinit import version from cloudinit.settings import PER_INSTANCE @@ -55,6 +56,12 @@ D_QUOTE_CHARS = { '`': '\`', } +def _make_sysconfig_bool(val): + if val: + return 'yes' + else: + return 'no' + class Distro(distros.Distro): @@ -76,9 +83,8 @@ class Distro(distros.Distro): if search_servers: contents.append("search %s" % (" ".join(search_servers))) if contents: - resolve_rw_fn = self._paths.join(False, "/etc/resolv.conf") contents.insert(0, '# Created by cloud-init') - util.write_file(resolve_rw_fn, "\n".join(contents), 0644) + util.write_file("/etc/resolv.conf", "\n".join(contents), 0644) def _write_network(self, settings): # TODO(harlowja) fix this... since this is the ubuntu format @@ -88,81 +94,82 @@ class Distro(distros.Distro): # Make the intermediate format as the rhel format... nameservers = [] searchservers = [] + dev_names = entries.keys() for (dev, info) in entries.iteritems(): net_fn = NETWORK_FN_TPL % (dev) - net_ro_fn = self._paths.join(True, net_fn) - (prev_exist, net_cfg) = self._read_conf(net_ro_fn) - net_cfg['DEVICE'] = dev - boot_proto = info.get('bootproto') - if boot_proto: - net_cfg['BOOTPROTO'] = boot_proto - net_mask = info.get('netmask') - if net_mask: - net_cfg["NETMASK"] = net_mask - addr = info.get('address') - if addr: - net_cfg["IPADDR"] = addr - if info.get('auto'): - net_cfg['ONBOOT'] = 'yes' - else: - net_cfg['ONBOOT'] = 'no' - gtway = info.get('gateway') - if gtway: - net_cfg["GATEWAY"] = gtway - bcast = info.get('broadcast') - if bcast: - net_cfg["BROADCAST"] = bcast - mac_addr = info.get('hwaddress') - if mac_addr: - net_cfg["MACADDR"] = mac_addr - lines = net_cfg.write() + net_cfg = { + 'DEVICE': dev, + 'NETMASK': info.get('netmask'), + 'IPADDR': info.get('address'), + 'BOOTPROTO': info.get('bootproto'), + 'GATEWAY': info.get('gateway'), + 'BROADCAST': info.get('broadcast'), + 'MACADDR': info.get('hwaddress'), + 'ONBOOT': _make_sysconfig_bool(info.get('auto')), + } + self._update_sysconfig_file(net_fn, net_cfg) if 'dns-nameservers' in info: nameservers.extend(info['dns-nameservers']) if 'dns-search' in info: searchservers.extend(info['dns-search']) - if not prev_exist: - lines.insert(0, '# Created by cloud-init') - w_contents = "\n".join(lines) - net_rw_fn = self._paths.join(False, net_fn) - util.write_file(net_rw_fn, w_contents, 0644) if nameservers or searchservers: self._write_resolve(nameservers, searchservers) + if dev_names: + net_cfg = { + 'NETWORKING': _make_sysconfig_bool(True), + } + self._update_sysconfig_file("/etc/sysconfig/network", net_cfg) + return dev_names + + def _update_sysconfig_file(self, fn, adjustments, allow_empty=False): + if not adjustments: + return + (exists, contents) = self._read_conf(fn) + updated_am = 0 + for (k, v) in adjustments.items(): + if v is None: + continue + v = str(v) + if len(v) == 0 and not allow_empty: + continue + contents[k] = v + updated_am += 1 + if updated_am: + lines = contents.write() + if not exists: + ci_ver = version.version_string() + lines.insert(0, '# Created by cloud-init v. %s' % (ci_ver)) + util.write_file(fn, "\n".join(lines), 0644) def set_hostname(self, hostname): - out_fn = self._paths.join(False, '/etc/sysconfig/network') - self._write_hostname(hostname, out_fn) - if out_fn == '/etc/sysconfig/network': - # Only do this if we are running in non-adjusted root mode - LOG.debug("Setting hostname to %s", hostname) - util.subp(['hostname', hostname]) + self._write_hostname(hostname, '/etc/sysconfig/network') + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) def apply_locale(self, locale, out_fn=None): if not out_fn: - out_fn = self._paths.join(False, '/etc/sysconfig/i18n') - ro_fn = self._paths.join(True, '/etc/sysconfig/i18n') - (_exists, contents) = self._read_conf(ro_fn) - contents['LANG'] = locale - w_contents = "\n".join(contents.write()) - util.write_file(out_fn, w_contents, 0644) + out_fn = '/etc/sysconfig/i18n' + locale_cfg = { + 'LANG': locale, + } + self._update_sysconfig_file(out_fn, locale_cfg) def _write_hostname(self, hostname, out_fn): - (_exists, contents) = self._read_conf(out_fn) - contents['HOSTNAME'] = hostname - w_contents = "\n".join(contents.write()) - util.write_file(out_fn, w_contents, 0644) + host_cfg = { + 'HOSTNAME': hostname, + } + self._update_sysconfig_file(out_fn, host_cfg) def update_hostname(self, hostname, prev_file): hostname_prev = self._read_hostname(prev_file) - read_fn = self._paths.join(True, "/etc/sysconfig/network") - hostname_in_sys = self._read_hostname(read_fn) + hostname_in_sys = self._read_hostname("/etc/sysconfig/network") update_files = [] if not hostname_prev or hostname_prev != hostname: update_files.append(prev_file) if (not hostname_in_sys or (hostname_in_sys == hostname_prev and hostname_in_sys != hostname)): - write_fn = self._paths.join(False, "/etc/sysconfig/network") - update_files.append(write_fn) + update_files.append("/etc/sysconfig/network") for fn in update_files: try: self._write_hostname(hostname, fn) @@ -200,12 +207,10 @@ class Distro(distros.Distro): raise RuntimeError(("Invalid timezone %s," " no file found at %s") % (tz, tz_file)) # Adjust the sysconfig clock zone setting - read_fn = self._paths.join(True, "/etc/sysconfig/clock") - (_exists, contents) = self._read_conf(read_fn) - contents['ZONE'] = tz - tz_contents = "\n".join(contents.write()) - write_fn = self._paths.join(False, "/etc/sysconfig/clock") - util.write_file(write_fn, tz_contents) + clock_cfg = { + 'ZONE': tz, + } + self._update_sysconfig_file("/etc/sysconfig/clock", clock_cfg) # This ensures that the correct tz will be used for the system util.copy(tz_file, self._paths.join(False, "/etc/localtime")) -- cgit v1.2.3 From f59369d1bb0eac874dcd2fa91abfad9186b22810 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 19 Sep 2012 21:13:48 -0700 Subject: Use a common header creation function to avoid duplicating the same code in this file. --- cloudinit/distros/rhel.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index c00627c6..1ea7952b 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -63,6 +63,11 @@ def _make_sysconfig_bool(val): return 'no' +def _make_header(): + ci_ver = version.version_string() + return '# Created by cloud-init v. %s' % (ci_ver) + + class Distro(distros.Distro): def __init__(self, name, cfg, paths): @@ -83,7 +88,7 @@ class Distro(distros.Distro): if search_servers: contents.append("search %s" % (" ".join(search_servers))) if contents: - contents.insert(0, '# Created by cloud-init') + contents.insert(0, _make_header()) util.write_file("/etc/resolv.conf", "\n".join(contents), 0644) def _write_network(self, settings): @@ -137,8 +142,7 @@ class Distro(distros.Distro): if updated_am: lines = contents.write() if not exists: - ci_ver = version.version_string() - lines.insert(0, '# Created by cloud-init v. %s' % (ci_ver)) + lines.insert(0, _make_header()) util.write_file(fn, "\n".join(lines), 0644) def set_hostname(self, hostname): @@ -212,7 +216,7 @@ class Distro(distros.Distro): } self._update_sysconfig_file("/etc/sysconfig/clock", clock_cfg) # This ensures that the correct tz will be used for the system - util.copy(tz_file, self._paths.join(False, "/etc/localtime")) + util.copy(tz_file, "/etc/localtime") def package_command(self, command, args=None): cmd = ['yum'] -- cgit v1.2.3 From e055c795581c2846aa6ae128de8cfd0b2c5d3f32 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 20 Sep 2012 15:55:52 -0700 Subject: Instead of special casing the empty list we are going to check for the 'all' entry and if that exists then only fire off one call (since debian supports this). --- cloudinit/distros/debian.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index 777c8530..88f4e978 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -56,12 +56,17 @@ class Distro(distros.Distro): def _write_network(self, settings): net_fn = self._paths.join(False, "/etc/network/interfaces") util.write_file(net_fn, settings) - return [] + return ['all'] def _bring_up_interfaces(self, device_names): - if not device_names: - device_names = ['--all'] - return distros.Distro._bring_up_interfaces(self, device_names) + use_all = False + for d in device_names: + if d == 'all': + use_all = True + if use_all: + return distros.Distro._bring_up_interface(self, '--all') + else: + return distros.Distro._bring_up_interfaces(self, device_names) def set_hostname(self, hostname): out_fn = self._paths.join(False, "/etc/hostname") -- cgit v1.2.3 From d852495da6ae8eefee0ff7d2de14c398c0a87a51 Mon Sep 17 00:00:00 2001 From: Garrett Holmstrom Date: Sat, 22 Sep 2012 19:35:07 -0700 Subject: Use 0440 permissions on sudoers, not 0644 --- cloudinit/distros/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 3e9d934d..612d44af 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -339,7 +339,7 @@ class Distro(object): content += "\n" if not os.path.exists(sudo_file): - util.write_file(sudo_file, content, 0644) + util.write_file(sudo_file, content, 0440) else: try: -- cgit v1.2.3 From 0a4f91c84c096e0b0df2e5a1d42c38609ae7fa93 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 14:40:41 -0400 Subject: send stderr from write-ssh-key-fingerprints to stdout This changes all output write-ssh-key-fingerprints to go to its stdout by redirecting stderr to stdout. The reason for this is that cc_keys_to_console.py was swallowing stderr and not replaying it to /dev/console. Ideally, we'd have a way in 'util.subp' to do effectively the same thing as we're doing here in the shell script. LP: #1055688 --- tools/write-ssh-key-fingerprints | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/write-ssh-key-fingerprints b/tools/write-ssh-key-fingerprints index 5723c989..130fc0c1 100755 --- a/tools/write-ssh-key-fingerprints +++ b/tools/write-ssh-key-fingerprints @@ -1,4 +1,5 @@ #!/bin/sh +exec 2>&1 fp_blist=",${1}," key_blist=",${2}," { @@ -15,8 +16,6 @@ done echo "-----END SSH HOST KEY FINGERPRINTS-----" echo "#############################################################" -} | logger -p user.info -s -t "ec2" - echo -----BEGIN SSH HOST KEY KEYS----- for f in /etc/ssh/ssh_host_*key.pub; do [ -f "$f" ] || continue @@ -26,3 +25,5 @@ for f in /etc/ssh/ssh_host_*key.pub; do cat $f done echo -----END SSH HOST KEY KEYS----- + +} | logger -p user.info --stderr -t "ec2" -- cgit v1.2.3 From 54e11417c93b646746a704a5ea85b47c3831fed4 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 14:45:31 -0400 Subject: do not create 'sems' directory. 'sem' is proper instance/ path --- cloudinit/stages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinit/stages.py b/cloudinit/stages.py index af902925..4ed1a750 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -240,7 +240,7 @@ class Init(object): return ds def _get_instance_subdirs(self): - return ['handlers', 'scripts', 'sems'] + return ['handlers', 'scripts', 'sem'] def _get_ipath(self, subname=None): # Force a check to see if anything -- cgit v1.2.3 From ad22d407085009dcd1c860185e29f21858cdd968 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 14:48:44 -0400 Subject: write-ssh-key-fingerprints: do not send HOST KEYS through logger In the previous commit to htis file I had wrapped the writing of 'BEGIN SSH HOST KEY KEYS' to go through logger. This would cause the keys to be prefixed with 'ec2:' which, previously they were not. That would break existing users *and* make it more difficult to consume that data, which was explicitly added to be easy to consume. --- tools/write-ssh-key-fingerprints | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/write-ssh-key-fingerprints b/tools/write-ssh-key-fingerprints index 130fc0c1..aa1f3c38 100755 --- a/tools/write-ssh-key-fingerprints +++ b/tools/write-ssh-key-fingerprints @@ -16,6 +16,8 @@ done echo "-----END SSH HOST KEY FINGERPRINTS-----" echo "#############################################################" +} | logger -p user.info --stderr -t "ec2" + echo -----BEGIN SSH HOST KEY KEYS----- for f in /etc/ssh/ssh_host_*key.pub; do [ -f "$f" ] || continue @@ -25,5 +27,3 @@ for f in /etc/ssh/ssh_host_*key.pub; do cat $f done echo -----END SSH HOST KEY KEYS----- - -} | logger -p user.info --stderr -t "ec2" -- cgit v1.2.3 From cde52cc8449d82d5bdce2fbb73516bee144e293c Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 16:54:51 -0400 Subject: fix make pep8 --- cloudinit/distros/fedora.py | 2 +- cloudinit/patcher.py | 1 + cloudinit/sources/__init__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cloudinit/distros/fedora.py b/cloudinit/distros/fedora.py index 9f76a116..e7092dd8 100644 --- a/cloudinit/distros/fedora.py +++ b/cloudinit/distros/fedora.py @@ -28,5 +28,5 @@ LOG = logging.getLogger(__name__) class Distro(rhel.Distro): - distro_name = 'fedora' + distro_name = 'fedora' default_user = 'ec2-user' diff --git a/cloudinit/patcher.py b/cloudinit/patcher.py index 8921a79a..fa140f04 100644 --- a/cloudinit/patcher.py +++ b/cloudinit/patcher.py @@ -39,6 +39,7 @@ def _patch_logging(): # sys.stderr using a fallback logger fallback_handler = QuietStreamHandler(sys.stderr) fallback_handler.setFormatter(logging.Formatter(FALL_FORMAT)) + def handleError(self, record): try: fallback_handler.handle(record) diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 6f126091..04083d0c 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -173,7 +173,7 @@ class DataSource(object): # make up a hostname (LP: #475354) in format ip-xx.xx.xx.xx lhost = self.metadata['local-hostname'] if util.is_ipv4(lhost): - toks = [ "ip-%s" % lhost.replace(".", "-") ] + toks = ["ip-%s" % lhost.replace(".", "-")] else: toks = lhost.split(".") -- cgit v1.2.3 From e3b29659672acd757122bc5f90a13670b96b6952 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 16:59:23 -0400 Subject: fix pylint --- cloudinit/patcher.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cloudinit/patcher.py b/cloudinit/patcher.py index fa140f04..0f3c034e 100644 --- a/cloudinit/patcher.py +++ b/cloudinit/patcher.py @@ -23,7 +23,8 @@ import logging import sys # Default fallback format -FALL_FORMAT = 'FALLBACK: %(asctime)s - %(filename)s[%(levelname)s]: %(message)s' +FALL_FORMAT = ('FALLBACK: %(asctime)s - %(filename)s[%(levelname)s]: ' + + '%(message)s') class QuietStreamHandler(logging.StreamHandler): @@ -40,7 +41,7 @@ def _patch_logging(): fallback_handler = QuietStreamHandler(sys.stderr) fallback_handler.setFormatter(logging.Formatter(FALL_FORMAT)) - def handleError(self, record): + def handleError(self, record): # pylint: disable=W0613 try: fallback_handler.handle(record) fallback_handler.flush() -- cgit v1.2.3 From 70cc7536f45a8d7052617ad88e2816291db0a309 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 17:13:38 -0400 Subject: DataSourceMAAS: if a oauth request fails due to 403 try updating local time In the event of a 403 (Unauthorized) in oauth, try set a 'oauth_clockskew' variable. In future headers, use a time created by 'time.time() + self.oauth_clockskew'. The idea here is that if the local time is bad (or even if the server time is bad) we will essentially use something that should be similar to the remote clock. This fixes LP: #978127. LP: #978127 --- cloudinit/sources/DataSourceMAAS.py | 43 +++++++++++++++++++++++++++++++++---- cloudinit/url_helper.py | 11 ++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py index c568d365..581e9a4b 100644 --- a/cloudinit/sources/DataSourceMAAS.py +++ b/cloudinit/sources/DataSourceMAAS.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from email.utils import parsedate import errno import oauth.oauth as oauth import os @@ -46,6 +47,7 @@ class DataSourceMAAS(sources.DataSource): sources.DataSource.__init__(self, sys_cfg, distro, paths) self.base_url = None self.seed_dir = os.path.join(paths.seed_dir, 'maas') + self.oauth_clockskew = None def __str__(self): return "%s [%s]" % (util.obj_name(self), self.base_url) @@ -95,11 +97,17 @@ class DataSourceMAAS(sources.DataSource): return {} consumer_secret = mcfg.get('consumer_secret', "") + + timestamp = None + if self.oauth_clockskew: + timestamp = int(time.time()) + self.oauth_clockskew + return oauth_headers(url=url, consumer_key=mcfg['consumer_key'], token_key=mcfg['token_key'], token_secret=mcfg['token_secret'], - consumer_secret=consumer_secret) + consumer_secret=consumer_secret, + timestamp=timestamp) def wait_for_metadata_service(self, url): mcfg = self.ds_cfg @@ -124,7 +132,7 @@ class DataSourceMAAS(sources.DataSource): check_url = "%s/%s/meta-data/instance-id" % (url, MD_VERSION) urls = [check_url] url = uhelp.wait_for_url(urls=urls, max_wait=max_wait, - timeout=timeout, status_cb=LOG.warn, + timeout=timeout, exception_cb=self._except_cb, headers_cb=self.md_headers) if url: @@ -135,6 +143,26 @@ class DataSourceMAAS(sources.DataSource): return bool(url) + def _except_cb(self, msg, exception): + if not (isinstance(exception, urllib2.HTTPError) and + exception.code == 403): + return + if 'date' not in exception.headers: + LOG.warn("date field not in 403 headers") + return + + date = exception.headers['date'] + + try: + ret_time = time.mktime(parsedate(date)) + except: + LOG.warn("failed to convert datetime '%s'") + return + + self.oauth_clockskew = int(ret_time - time.time()) + LOG.warn("set oauth clockskew to %d" % self.oauth_clockskew) + return + def read_maas_seed_dir(seed_d): """ @@ -229,13 +257,20 @@ def check_seed_contents(content, seed): return (userdata, md) -def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret): +def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret, + timestamp=None): consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) token = oauth.OAuthToken(token_key, token_secret) + + if timestamp is None: + ts = int(time.time()) + else: + ts = timestamp + params = { 'oauth_version': "1.0", 'oauth_nonce': oauth.generate_nonce(), - 'oauth_timestamp': int(time.time()), + 'oauth_timestamp': ts, 'oauth_token': token.key, 'oauth_consumer_key': consumer.key, } diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py index 732d6aec..f3e3fd7e 100644 --- a/cloudinit/url_helper.py +++ b/cloudinit/url_helper.py @@ -136,7 +136,8 @@ def readurl(url, data=None, timeout=None, def wait_for_url(urls, max_wait=None, timeout=None, - status_cb=None, headers_cb=None, sleep_time=1): + status_cb=None, headers_cb=None, sleep_time=1, + exception_cb=None): """ urls: a list of urls to try max_wait: roughly the maximum time to wait before giving up @@ -146,6 +147,8 @@ def wait_for_url(urls, max_wait=None, timeout=None, status_cb: call method with string message when a url is not available headers_cb: call method with single argument of url to get headers for request. + exception_cb: call method with 2 arguments 'msg' (per status_cb) and + 'exception', the exception that occurred. the idea of this routine is to wait for the EC2 metdata service to come up. On both Eucalyptus and EC2 we have seen the case where @@ -164,7 +167,7 @@ def wait_for_url(urls, max_wait=None, timeout=None, """ start_time = time.time() - def log_status_cb(msg): + def log_status_cb(msg, exc=None): LOG.debug(msg) if status_cb is None: @@ -196,8 +199,10 @@ def wait_for_url(urls, max_wait=None, timeout=None, resp = readurl(url, headers=headers, timeout=timeout) if not resp.contents: reason = "empty response [%s]" % (resp.code) + e = ValueError(reason) elif not resp.ok(): reason = "bad status code [%s]" % (resp.code) + e = ValueError(reason) else: return url except urllib2.HTTPError as e: @@ -214,6 +219,8 @@ def wait_for_url(urls, max_wait=None, timeout=None, time_taken, max_wait, reason) status_cb(status_msg) + if exception_cb: + exception_cb(msg=status_msg, exception=e) if timeup(max_wait, start_time): break -- cgit v1.2.3 From c780ad5898f54741037ad66c28c3423aa0495fba Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 24 Sep 2012 17:56:42 -0700 Subject: Oopies, missed u. --- cloudinit/distros/rhel.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 67d80bf4..1ea7952b 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -144,8 +144,6 @@ class Distro(distros.Distro): if not exists: lines.insert(0, _make_header()) util.write_file(fn, "\n".join(lines), 0644) -======= ->>>>>>> MERGE-SOURCE def set_hostname(self, hostname): self._write_hostname(hostname, '/etc/sysconfig/network') -- cgit v1.2.3 From 58be2ec4a3a3213700905f6bd3e6752775369471 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 24 Sep 2012 20:58:05 -0400 Subject: fix pep8 --- cloudinit/distros/rhel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 1ea7952b..1a47fc33 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -1,4 +1,4 @@ -# vi: ts=4 expandtab + # vi: ts=4 expandtab # # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012 Hewlett-Packard Development Company, L.P. @@ -56,6 +56,7 @@ D_QUOTE_CHARS = { '`': '\`', } + def _make_sysconfig_bool(val): if val: return 'yes' @@ -160,7 +161,7 @@ class Distro(distros.Distro): def _write_hostname(self, hostname, out_fn): host_cfg = { - 'HOSTNAME': hostname, + 'HOSTNAME': hostname, } self._update_sysconfig_file(out_fn, host_cfg) -- cgit v1.2.3