1# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import glob
16import json
17import os
18import urllib
19
20import requests
21import yaml
22
23from openstack.config import _util
24from openstack import exceptions
25
26_VENDORS_PATH = os.path.dirname(os.path.realpath(__file__))
27_VENDOR_DEFAULTS = {}
28_WELL_KNOWN_PATH = "{scheme}://{netloc}/.well-known/openstack/api"
29
30
31def _get_vendor_defaults():
32    global _VENDOR_DEFAULTS
33    if not _VENDOR_DEFAULTS:
34        for vendor in glob.glob(os.path.join(_VENDORS_PATH, '*.yaml')):
35            with open(vendor, 'r') as f:
36                vendor_data = yaml.safe_load(f)
37                _VENDOR_DEFAULTS[vendor_data['name']] = vendor_data['profile']
38        for vendor in glob.glob(os.path.join(_VENDORS_PATH, '*.json')):
39            with open(vendor, 'r') as f:
40                vendor_data = json.load(f)
41                _VENDOR_DEFAULTS[vendor_data['name']] = vendor_data['profile']
42    return _VENDOR_DEFAULTS
43
44
45def get_profile(profile_name):
46    vendor_defaults = _get_vendor_defaults()
47    if profile_name in vendor_defaults:
48        return vendor_defaults[profile_name].copy()
49    profile_url = urllib.parse.urlparse(profile_name)
50    if not profile_url.netloc:
51        # This isn't a url, and we already don't have it.
52        return
53    well_known_url = _WELL_KNOWN_PATH.format(
54        scheme=profile_url.scheme,
55        netloc=profile_url.netloc,
56    )
57    response = requests.get(well_known_url)
58    if not response.ok:
59        raise exceptions.ConfigException(
60            "{profile_name} is a remote profile that could not be fetched:"
61            " {status_code} {reason}".format(
62                profile_name=profile_name,
63                status_code=response.status_code,
64                reason=response.reason))
65        vendor_defaults[profile_name] = None
66        return
67    vendor_data = response.json()
68    name = vendor_data['name']
69    # Merge named and url cloud config, but make named config override the
70    # config from the cloud so that we can supply local overrides if needed.
71    profile = _util.merge_clouds(
72        vendor_data['profile'],
73        vendor_defaults.get(name, {}))
74    # If there is (or was) a profile listed in a named config profile, it
75    # might still be here. We just merged in content from a URL though, so
76    # pop the key to prevent doing it again in the future.
77    profile.pop('profile', None)
78    # Save the data under both names so we don't reprocess this, no matter
79    # how we're called.
80    vendor_defaults[profile_name] = profile
81    vendor_defaults[name] = profile
82    return profile
83