~gz/pyjuju/0.5_unicode_token_backport

« back to all changes in this revision

Viewing changes to juju/providers/openstack/credentials.py

  • Committer: Clint Byrum
  • Author(s): Martin Packman
  • Date: 2012-09-10 08:32:41 UTC
  • Revision ID: clint@ubuntu.com-20120910083241-xueuh0bt5jl44w2b
OpenStack Provider

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Handling of the credentials needed to authenticate with the OpenStack api
 
2
 
 
3
Supports several different sets of credentials different auth modes need:
 
4
* 'legacy' is built into nova and deprecated in favour of using keystone
 
5
* 'keypair' works with the HP public cloud implemention of keystone
 
6
* 'userpass' is the way keystone seems to want to do authentication generally
 
7
"""
 
8
 
 
9
import os
 
10
 
 
11
 
 
12
class OpenStackCredentials(object):
 
13
    """Encapsulation of credentials used to authenticate with OpenStack"""
 
14
 
 
15
    _config_vars = {
 
16
            'auth-url': ("OS_AUTH_URL", "NOVA_URL"),
 
17
            'username': ("OS_USERNAME", "NOVA_USERNAME"),
 
18
            'password': ("OS_PASSWORD", "NOVA_PASSWORD"),
 
19
            # HP exposes both a numeric id and a name for tenants, passed back
 
20
            # as tenantId and tenantName. Use the name only for simplicity.
 
21
            'project-name': ("OS_TENANT_NAME", "NOVA_PROJECT_NAME",
 
22
                             "NOVA_PROJECT_ID"),
 
23
            'region': ("OS_REGION_NAME", "NOVA_REGION_NAME", "NOVA_REGION"),
 
24
            # The key variables don't seem to have modern OS_ prefixed aliases
 
25
            'access-key': ("NOVA_API_KEY",),
 
26
            'secret-key': ("EC2_SECRET_KEY", "AWS_SECRET_ACCESS_KEY"),
 
27
            # A usable mode can normally be guessed, but may be configured
 
28
            'auth-mode': (),
 
29
        }
 
30
 
 
31
    # Really, legacy auth could pass in the project id and keystone doesn't
 
32
    # require it, but this is what the client expects for now.
 
33
    _modes = {
 
34
        'userpass': ('username', 'password', 'project-name'),
 
35
        'rax': ('username', 'password', 'project-name'),
 
36
        'keypair': ('access-key', 'secret-key', 'project-name'),
 
37
        'legacy': ('username', 'access-key'),
 
38
        }
 
39
 
 
40
    _version_to_mode = {
 
41
        "v2.0": 'userpass',
 
42
        "v1.1": 'legacy',
 
43
        "v1.0": 'legacy',
 
44
        }
 
45
 
 
46
    def __init__(self, creds_dict):
 
47
        url = creds_dict.get("auth-url")
 
48
        if not url:
 
49
            raise ValueError("Missing config 'auth-url' for OpenStack api")
 
50
        mode = creds_dict.get("auth-mode")
 
51
        if mode is None:
 
52
            mode = self._guess_auth_mode(url)
 
53
        elif mode not in self._modes:
 
54
            # The juju.environment.config layer should raise a pretty error
 
55
            raise ValueError("Unknown 'auth-mode' value %r" % (self.mode,))
 
56
        missing_keys = [key for key in self._modes[mode]
 
57
            if not creds_dict.get(key)]
 
58
        if missing_keys:
 
59
            raise ValueError("Missing config %s required for %s auth" % (
 
60
                ", ".join(map(repr, missing_keys)), mode))
 
61
        self.url = url
 
62
        self.mode = mode
 
63
        for key in self._config_vars:
 
64
            if key not in ("auth-url", "auth-mode"):
 
65
                setattr(self, key.replace("-", "_"), creds_dict.get(key))
 
66
 
 
67
    @classmethod
 
68
    def _guess_auth_mode(cls, url):
 
69
        """Pick a mode based on the version at the end of `url` given"""
 
70
        final_part = url.rstrip("/").rsplit("/", 1)[-1]
 
71
        try:
 
72
            return cls._version_to_mode[final_part]
 
73
        except KeyError:
 
74
            raise ValueError(
 
75
                "Missing config 'auth-mode' as unknown version"
 
76
                " in 'auth-url' given: " + url)
 
77
 
 
78
    @classmethod
 
79
    def _get(cls, config, key):
 
80
        """Retrieve `key` from `config` if present or in matching envvars"""
 
81
        val = config.get(key)
 
82
        if val is None:
 
83
            for env_key in cls._config_vars[key]:
 
84
 
 
85
                val = os.environ.get(env_key)
 
86
                if val:
 
87
                    return val
 
88
        return val
 
89
 
 
90
    @classmethod
 
91
    def from_environment(cls, config):
 
92
        """Create credentials from `config` falling back to environment"""
 
93
        return cls(dict((k, cls._get(config, k)) for k in cls._config_vars))
 
94
 
 
95
    def set_config_defaults(self, data):
 
96
        """Populate `data` with these credentials where not already set"""
 
97
        for key in self._config_vars:
 
98
            if key not in data:
 
99
                val = getattr(self, key.replace("auth-", "").replace("-", "_"))
 
100
                if val is not None:
 
101
                    data[key] = val