wrap boto.utils.get_instance_metadata to ensure non-lazy loading

newer versions of boto lazily load the metadata from the ec2
metadata service. Here, we:

1. Add a ec2_utils module that checks which version of boto is being used and
   under the right versions the metadata dictionary will be expanded.
2. Use this new ec2_utils module in the cloudstack and ec2
   datasources as there entrypoints into boto.
3. Add a dependency on python-pkg-resources (from pkg_resources import
   parse_version) to determine the boto version.
This commit is contained in:
Scott Moser 2012-11-12 10:01:02 -05:00
commit babbf53f72
5 changed files with 84 additions and 12 deletions

View File

@ -23,6 +23,9 @@
- Fix the merging of group configuration when that group configuration is a
dict => members. [revno 707]
- add yum_add_repo configuration module for adding additional yum repos
- work around the lazy loading of get_instance_metadata in boto >= 2.6.0
by fully walking the dictionary. (LP: #1068801)
Added dependency on distribute's python-pkg-resources
0.7.0:
- add a 'exception_cb' argument to 'wait_for_url'. If provided, this
method will be called back with the exception received and the message.

70
cloudinit/ec2_utils.py Normal file
View File

@ -0,0 +1,70 @@
# vi: ts=4 expandtab
#
# Copyright (C) 2012 Yahoo! Inc.
#
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
#
# 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 <http://www.gnu.org/licenses/>.
import pkg_resources
from pkg_resources import parse_version
import cloudinit.util as util
import cloudinit.url_helper as uh
import boto.utils as boto_utils
# Versions of boto >= 2.6.0 try to lazily load
# the metadata backing, which doesn't work so well
# in cloud-init especially since the metadata is
# serialized and actions are performed where the
# metadata server may be blocked (thus the datasource
# will start failing) resulting in url exceptions
# when fields that do exist (or would have existed)
# do not exist due to the blocking that occurred.
BOTO_LAZY = False
try:
_boto_lib = pkg_resources.get_distribution('boto')
if _boto_lib.parsed_version > parse_version("2.5.2"):
BOTO_LAZY = True
except pkg_resources.DistributionNotFound:
pass
def _unlazy_dict(mp):
if not isinstance(mp, (dict)):
return mp
if not BOTO_LAZY:
return mp
for (k, v) in mp.items():
_unlazy_dict(v)
return mp
def get_instance_userdata(api_version, metadata_address):
# Note: boto.utils.get_instance_metadata returns '' for empty string
# so the change from non-true to '' is not specifically necessary, but
# this way cloud-init will get consistent behavior even if boto changed
# in the future to return a None on "no user-data provided".
ud = boto_utils.get_instance_userdata(api_version, None, metadata_address)
if not ud:
ud = ''
return ud
def get_instance_metadata(api_version, metadata_address):
metadata = boto_utils.get_instance_metadata(api_version, metadata_address)
if not isinstance(metadata, (dict)):
metadata = {}
return _unlazy_dict(metadata)

View File

@ -26,8 +26,7 @@ from struct import pack
import os
import time
import boto.utils as boto_utils
from cloudinit import ec2_utils as ec2
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import url_helper as uhelp
@ -116,10 +115,10 @@ class DataSourceCloudStack(sources.DataSource):
if not self.wait_for_metadata_service():
return False
start_time = time.time()
self.userdata_raw = boto_utils.get_instance_userdata(self.api_ver,
None, self.metadata_address)
self.metadata = boto_utils.get_instance_metadata(self.api_ver,
self.metadata_address)
self.userdata_raw = ec2.get_instance_userdata(self.api_ver,
self.metadata_address)
self.metadata = ec2.get_instance_metadata(self.api_ver,
self.metadata_address)
LOG.debug("Crawl of metadata service took %s seconds",
int(time.time() - start_time))
return True

View File

@ -23,8 +23,7 @@
import os
import time
import boto.utils as boto_utils
from cloudinit import ec2_utils as ec2
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import url_helper as uhelp
@ -65,10 +64,10 @@ class DataSourceEc2(sources.DataSource):
if not self.wait_for_metadata_service():
return False
start_time = time.time()
self.userdata_raw = boto_utils.get_instance_userdata(self.api_ver,
None, self.metadata_address)
self.metadata = boto_utils.get_instance_metadata(self.api_ver,
self.metadata_address)
self.userdata_raw = ec2.get_instance_userdata(self.api_ver,
self.metadata_address)
self.metadata = ec2.get_instance_metadata(self.api_ver,
self.metadata_address)
LOG.debug("Crawl of metadata service took %s seconds",
int(time.time() - start_time))
return True

View File

@ -19,6 +19,7 @@ Standards-Version: 3.9.3
Package: cloud-init
Architecture: all
Depends: cloud-utils,
distribute,
procps,
python,
#for $r in $requires