From 15648e327ad19c8332ab770698643c0e0335bc28 Mon Sep 17 00:00:00 2001 From: Cosmin Luță Date: Wed, 25 Jan 2012 13:30:55 +0200 Subject: Added support for CloudStack meta-data --- cloudinit/DataSourceCS.py | 238 ++++++++++++++++++++++++++++++++++++++++++++++ config/cloud.cfg | 2 +- 2 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 cloudinit/DataSourceCS.py diff --git a/cloudinit/DataSourceCS.py b/cloudinit/DataSourceCS.py new file mode 100644 index 00000000..f70a17a7 --- /dev/null +++ b/cloudinit/DataSourceCS.py @@ -0,0 +1,238 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# +# Author: Scott Moser +# Author: Juerg Hafliger +# Author: Cosmin Luta +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import cloudinit.DataSource as DataSource + +from cloudinit import seeddir as base_seeddir +from cloudinit import log +import cloudinit.util as util +import socket +import urllib2 +import time +import boto.utils as boto_utils +from struct import pack + +class DataSourceCS(DataSource.DataSource): + api_ver = 'latest' + seeddir = base_seeddir + '/cs' + metadata_address = None + + def __init__(self, sys_cfg=None): + DataSource.DataSource.__init__(self, sys_cfg) + # Cloudstack has its metadata/userdata URLs located on http:///latest/ + self.metadata_address = "http://" + self._get_default_gateway() + "/" + + def _get_default_gateway(self): + f = None + try: + f = open("/proc/net/route", "r") + for line in f.readlines(): + items = line.split("\t") + if items[1] == "00000000": + # found the default route, get the gateway + gw = int(items[2], 16) + log.debug("found default route, gateway %s" % items[2]) + return socket.inet_ntoa(pack(" max_wait)) + + loop_n = 0 + while True: + sleeptime = int(loop_n / 5) + 1 + for url in urls: + now = time.time() + if loop_n != 0: + if timeup(max_wait, starttime): + break + if timeout and (now + timeout > (starttime + max_wait)): + # shorten timeout to not run way over max_time + timeout = int((starttime + max_wait) - now) + + try: + req = urllib2.Request(url) + resp = urllib2.urlopen(req, timeout=timeout) + if resp.read() != "": + return url + reason = "empty data [%s]" % resp.getcode() + except urllib2.HTTPError as e: + reason = "http error [%s]" % e.code + except urllib2.URLError as e: + reason = "url error [%s]" % e.reason + except socket.timeout as e: + reason = "socket timeout [%s]" % e + except Exception as e: + reason = "unexpected error [%s]" % e + + if log: + status_cb("'%s' failed [%s/%ss]: %s" % + (url, int(time.time() - starttime), max_wait, + reason)) + + if timeup(max_wait, starttime): + break + + loop_n += 1 + time.sleep(sleeptime) + + return False + + +datasources = [ + (DataSourceCS, (DataSource.DEP_FILESYSTEM, DataSource.DEP_NETWORK)), +] + +# return a list of data sources that match this set of dependencies +def get_datasource_list(depends): + return DataSource.list_from_depends(depends, datasources) diff --git a/config/cloud.cfg b/config/cloud.cfg index 25d02cee..81085d68 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -1,7 +1,7 @@ user: ubuntu disable_root: 1 preserve_hostname: False -# datasource_list: [ "NoCloud", "OVF", "Ec2" ] +# datasource_list: [ "NoCloud", "OVF", "Ec2", "CS" ] cloud_init_modules: - bootcmd -- cgit v1.2.3 From 272bea67f8cd0fee5233d21c8f380a6d2f6b6bde Mon Sep 17 00:00:00 2001 From: Cosmin Luță Date: Sun, 4 Mar 2012 16:38:21 +0200 Subject: Removed the code that was used to wait for the metadata service since it seems CloudStack doesn't need that. --- cloudinit/DataSourceCS.py | 142 ---------------------------------------------- 1 file changed, 142 deletions(-) diff --git a/cloudinit/DataSourceCS.py b/cloudinit/DataSourceCS.py index f70a17a7..1c0a984c 100644 --- a/cloudinit/DataSourceCS.py +++ b/cloudinit/DataSourceCS.py @@ -69,8 +69,6 @@ class DataSourceCS(DataSource.DataSource): return True try: - if not self.wait_for_metadata_service(): - return False start = time.time() self.userdata_raw = boto_utils.get_instance_userdata(self.api_ver, None, self.metadata_address) @@ -89,146 +87,6 @@ class DataSourceCS(DataSource.DataSource): def get_availability_zone(self): return self.metadata['availability-zone'] - def wait_for_metadata_service(self): - mcfg = self.ds_cfg - - if not hasattr(mcfg, "get"): - mcfg = {} - - max_wait = 120 - try: - max_wait = int(mcfg.get("max_wait", max_wait)) - except Exception: - util.logexc(log) - log.warn("Failed to get max wait. using %s" % max_wait) - - if max_wait == 0: - return False - - timeout = 50 - try: - timeout = int(mcfg.get("timeout", timeout)) - except Exception: - util.logexc(log) - log.warn("Failed to get timeout, using %s" % timeout) - - def_mdurls = [self.metadata_address] - mdurls = mcfg.get("metadata_urls", def_mdurls) - - # Remove addresses from the list that wont resolve. - 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_mdurls - - urls = [] - url2base = {False: False} - for url in mdurls: - cur = "%s/%s/meta-data/instance-id" % (url, self.api_ver) - urls.append(cur) - url2base[cur] = url - - starttime = time.time() - url = wait_for_metadata_service(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 after %i seconds\n" % - int(time.time() - starttime)) - - self.metadata_address = url2base[url] - return bool(url) - - -def wait_for_metadata_service(urls, max_wait=None, timeout=None, - status_cb=None): - """ - urls: a list of urls to try - max_wait: roughly the maximum time to wait before giving up - The max time is *actually* len(urls)*timeout as each url will - be tried once and given the timeout provided. - timeout: the timeout provided to urllib2.urlopen - status_cb: call method with string message when a url is not available - - 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 - the instance hit the MD before the MD service was up. EC2 seems - to have permenantely fixed this, though. - - In openstack, the metadata service might be painfully slow, and - unable to avoid hitting a timeout of even up to 10 seconds or more - (LP: #894279) for a simple GET. - - Offset those needs with the need to not hang forever (and block boot) - on a system where cloud-init is configured to look for EC2 Metadata - service but is not going to find one. It is possible that the instance - data host (169.254.169.254) may be firewalled off Entirely for a sytem, - meaning that the connection will block forever unless a timeout is set. - """ - starttime = time.time() - - sleeptime = 1 - - def nullstatus_cb(msg): - return - - if status_cb == None: - status_cb = nullstatus_cb - - def timeup(max_wait, starttime): - return((max_wait <= 0 or max_wait is None) or - (time.time() - starttime > max_wait)) - - loop_n = 0 - while True: - sleeptime = int(loop_n / 5) + 1 - for url in urls: - now = time.time() - if loop_n != 0: - if timeup(max_wait, starttime): - break - if timeout and (now + timeout > (starttime + max_wait)): - # shorten timeout to not run way over max_time - timeout = int((starttime + max_wait) - now) - - try: - req = urllib2.Request(url) - resp = urllib2.urlopen(req, timeout=timeout) - if resp.read() != "": - return url - reason = "empty data [%s]" % resp.getcode() - except urllib2.HTTPError as e: - reason = "http error [%s]" % e.code - except urllib2.URLError as e: - reason = "url error [%s]" % e.reason - except socket.timeout as e: - reason = "socket timeout [%s]" % e - except Exception as e: - reason = "unexpected error [%s]" % e - - if log: - status_cb("'%s' failed [%s/%ss]: %s" % - (url, int(time.time() - starttime), max_wait, - reason)) - - if timeup(max_wait, starttime): - break - - loop_n += 1 - time.sleep(sleeptime) - - return False - - datasources = [ (DataSourceCS, (DataSource.DEP_FILESYSTEM, DataSource.DEP_NETWORK)), ] -- cgit v1.2.3 From 162af10bc6633d47ec8f3ece102f0bc915a0e33a Mon Sep 17 00:00:00 2001 From: Cosmin Luță Date: Sun, 4 Mar 2012 16:42:27 +0200 Subject: Importing only inet_ntoa from the socket module --- cloudinit/DataSourceCS.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinit/DataSourceCS.py b/cloudinit/DataSourceCS.py index 1c0a984c..4d199ad2 100644 --- a/cloudinit/DataSourceCS.py +++ b/cloudinit/DataSourceCS.py @@ -24,7 +24,7 @@ import cloudinit.DataSource as DataSource from cloudinit import seeddir as base_seeddir from cloudinit import log import cloudinit.util as util -import socket +from socket import inet_ntoa import urllib2 import time import boto.utils as boto_utils @@ -50,7 +50,7 @@ class DataSourceCS(DataSource.DataSource): # found the default route, get the gateway gw = int(items[2], 16) log.debug("found default route, gateway %s" % items[2]) - return socket.inet_ntoa(pack(" Date: Sun, 4 Mar 2012 16:44:33 +0200 Subject: Refactored _get_default_gateway to get_default_gateway --- cloudinit/DataSourceCS.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudinit/DataSourceCS.py b/cloudinit/DataSourceCS.py index 4d199ad2..a41f8fca 100644 --- a/cloudinit/DataSourceCS.py +++ b/cloudinit/DataSourceCS.py @@ -37,10 +37,10 @@ class DataSourceCS(DataSource.DataSource): def __init__(self, sys_cfg=None): DataSource.DataSource.__init__(self, sys_cfg) - # Cloudstack has its metadata/userdata URLs located on http:///latest/ - self.metadata_address = "http://" + self._get_default_gateway() + "/" + # Cloudstack has its metadata/userdata URLs located at http:///latest/ + self.metadata_address = "http://%s/" % self.get_default_gateway() - def _get_default_gateway(self): + def get_default_gateway(self): f = None try: f = open("/proc/net/route", "r") -- cgit v1.2.3 From 71c49a55eb6df5c74e8c167dc1ac6175b8689473 Mon Sep 17 00:00:00 2001 From: Cosmin Luță Date: Sun, 4 Mar 2012 17:10:16 +0200 Subject: Removed unused urllib2 import --- cloudinit/DataSourceCS.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cloudinit/DataSourceCS.py b/cloudinit/DataSourceCS.py index a41f8fca..cf56ebe6 100644 --- a/cloudinit/DataSourceCS.py +++ b/cloudinit/DataSourceCS.py @@ -25,7 +25,6 @@ from cloudinit import seeddir as base_seeddir from cloudinit import log import cloudinit.util as util from socket import inet_ntoa -import urllib2 import time import boto.utils as boto_utils from struct import pack -- cgit v1.2.3 From 6a77364216dadfd7e955def9846118bfd69982e2 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Mar 2012 14:02:11 -0400 Subject: cloudinit/DataSourceCloudStack: updated copyright and author Per Cosmin in personal mail: > I didn't change any of the copyright/license information, as I'm not > sure what should be there (feel free to adjust it as you please, I don't > even care for my name to appear in the file). I've added Canonical to copyright (as the file was originally copied and modified from the DataSourceEc2). And also added Cosmin's name to Authors and Copyright for the portions that he contributed. --- cloudinit/DataSourceCloudStack.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cloudinit/DataSourceCloudStack.py b/cloudinit/DataSourceCloudStack.py index 593f69de..181b9419 100644 --- a/cloudinit/DataSourceCloudStack.py +++ b/cloudinit/DataSourceCloudStack.py @@ -1,11 +1,10 @@ # vi: ts=4 expandtab # -# Copyright (C) 2009-2010 Canonical Ltd. -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Cosmin Luta # -# Author: Scott Moser -# Author: Juerg Hafliger # Author: Cosmin Luta +# Author: Scott Moser # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as -- cgit v1.2.3