diff --git a/os_collect_config/cfn.py b/os_collect_config/cfn.py new file mode 100644 index 0000000..f66ba21 --- /dev/null +++ b/os_collect_config/cfn.py @@ -0,0 +1,59 @@ +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +from oslo.config import cfg +import requests + +from openstack.common import log +from os_collect_config import exc + +EC2_METADATA_URL = 'http://169.254.169.254/latest/meta-data' +CONF = cfg.CONF + +opts = [ + cfg.StrOpt('metadata-url', + help='URL to query for CloudFormation Metadata'), + cfg.MultiStrOpt('path', + help='Path to Metadata'), +] + + +def _fetch_metadata(fetch_url, session): + try: + r = session.get(fetch_url) + r.raise_for_status() + except (requests.HTTPError, + requests.ConnectionError, + requests.Timeout) as e: + log.getLogger(__name__).warn(e) + raise exc.Ec2MetadataNotAvailable + content = r.text + if fetch_url[-1] == '/': + new_content = {} + for subkey in content.split("\n"): + if '=' in subkey: + subkey = subkey[:subkey.index('=')] + '/' + sub_fetch_url = fetch_url + subkey + if subkey[-1] == '/': + subkey = subkey[:-1] + new_content[subkey] = _fetch_metadata(sub_fetch_url, session) + content = new_content + return content + + +def collect(): + root_url = '%s/' % (CONF.ec2.metadata_url) + session = requests.Session() + return _fetch_metadata(root_url, session) diff --git a/os_collect_config/tests/test_cfn.py b/os_collect_config/tests/test_cfn.py new file mode 100644 index 0000000..2719363 --- /dev/null +++ b/os_collect_config/tests/test_cfn.py @@ -0,0 +1,87 @@ +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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 fixtures +import json +import requests +import testtools +from testtools import matchers +import urlparse + +from os_collect_config import cfn +from os_collect_config import collect +from os_collect_config import exc + + +META_DATA = {u'int1': 1, + u'strfoo': u'foo', + u'map_ab': { + u'a': 'apple', + u'b': 'banana', + }} + + +class FakeResponse(dict): + def __init__(self, text): + self.text = text + + def raise_for_status(self): + pass + + +class FakeSession(object): + def get(self, url): + url = urlparse.urlparse(url) + params = urlparse.parse_qsl(url.query) + # TODO(clint-fewbar) Refactor usage of requests to a factory + if 'Action' not in params: + raise Exception('No Action') + if params['Action'] != 'DescribeStackResources': + raise Exception('Wrong Action (%s)' % params['Action']) + return FakeResponse(json.dumps(META_DATA)) + + +class FakeFailSession(object): + def get(self, url): + raise requests.exceptions.HTTPError(403, 'Forbidden') + + +class TestCfn(testtools.TestCase): + def setUp(self): + super(TestCfn, self).setUp() + self.log = self.useFixture(fixtures.FakeLogger()) + + def test_collect_cfn(self): + self.useFixture( + fixtures.MonkeyPatch('requests.Session', FakeSession)) + collect.setup_conf() + cfn_md = cfn.collect() + self.assertThat(cfn_md, matchers.IsInstance(dict)) + + for k in ('int1', 'strfoo', 'mapab'): + self.assertIn(k, cfn_md) + self.assertEquals(cfn_md[k], META_DATA[k]) + + self.assertEquals(cfn_md['block-device-mapping']['ami'], 'vda') + + self.assertEquals('', self.log.output) + + def test_collect_cfn_fail(self): + self.useFixture( + fixtures.MonkeyPatch( + 'requests.Session', FakeFailSession)) + collect.setup_conf() + self.assertRaises(exc.CfnMetadataNotAvailable, cfn.collect) + self.assertIn('Forbidden', self.log.output)