summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/distros/__init__.py31
-rw-r--r--cloudinit/distros/debian.py11
-rw-r--r--cloudinit/distros/fedora.py1
-rw-r--r--cloudinit/distros/rhel.py132
-rw-r--r--cloudinit/distros/ubuntu.py1
-rw-r--r--cloudinit/patcher.py6
-rw-r--r--cloudinit/sources/DataSourceMAAS.py43
-rw-r--r--cloudinit/sources/__init__.py2
-rw-r--r--cloudinit/stages.py2
-rw-r--r--cloudinit/url_helper.py11
-rwxr-xr-xtools/write-ssh-key-fingerprints3
11 files changed, 154 insertions, 89 deletions
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 7340fa84..13e4fd44 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -32,12 +32,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__)
@@ -104,10 +98,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
@@ -159,13 +153,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)
@@ -174,6 +166,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 get_default_user(self):
if not self.default_user:
return None
@@ -312,7 +313,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:
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 5b4aa9f8..88f4e978 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -56,6 +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 ['all']
+
+ def _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")
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..fb4f07e1 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
@@ -56,6 +57,18 @@ D_QUOTE_CHARS = {
}
+def _make_sysconfig_bool(val):
+ if val:
+ return 'yes'
+ else:
+ 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):
@@ -76,9 +89,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)
+ contents.insert(0, _make_header())
+ 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 +100,81 @@ 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:
+ lines.insert(0, _make_header())
+ 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,14 +212,12 @@ 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"))
+ util.copy(tz_file, "/etc/localtime")
def package_command(self, command, args=None):
cmd = ['yum']
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")
diff --git a/cloudinit/patcher.py b/cloudinit/patcher.py
index 8921a79a..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):
@@ -39,7 +40,8 @@ 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):
+
+ def handleError(self, record): # pylint: disable=W0613
try:
fallback_handler.handle(record)
fallback_handler.flush()
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 <http://www.gnu.org/licenses/>.
+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/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(".")
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
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
diff --git a/tools/write-ssh-key-fingerprints b/tools/write-ssh-key-fingerprints
index 5723c989..aa1f3c38 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,7 +16,7 @@ done
echo "-----END SSH HOST KEY FINGERPRINTS-----"
echo "#############################################################"
-} | logger -p user.info -s -t "ec2"
+} | logger -p user.info --stderr -t "ec2"
echo -----BEGIN SSH HOST KEY KEYS-----
for f in /etc/ssh/ssh_host_*key.pub; do