~ubuntu-branches/ubuntu/wily/heat/wily-proposed

« back to all changes in this revision

Viewing changes to heat/engine/resources/stack_user.py

  • Committer: Package Import Robot
  • Author(s): James Page, Corey Bryant, James Page
  • Date: 2015-03-30 11:11:18 UTC
  • mfrom: (1.1.23)
  • Revision ID: package-import@ubuntu.com-20150330111118-2qpycylx6swu4yhj
Tags: 2015.1~b3-0ubuntu1
[ Corey Bryant ]
* New upstream milestone release for OpenStack kilo:
  - d/control: Align with upstream dependencies.
  - d/p/sudoers_patch.patch: Rebased.
  - d/p/fix-requirements.patch: Rebased.

[ James Page ]
* d/p/fixup-assert-regex.patch: Tweak test to use assertRegexpMatches.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
3
#    not use this file except in compliance with the License. You may obtain
 
4
#    a copy of the License at
 
5
#
 
6
#         http://www.apache.org/licenses/LICENSE-2.0
 
7
#
 
8
#    Unless required by applicable law or agreed to in writing, software
 
9
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
10
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
11
#    License for the specific language governing permissions and limitations
 
12
#    under the License.
 
13
 
 
14
import keystoneclient.exceptions as kc_exception
 
15
from oslo_log import log as logging
 
16
 
 
17
from heat.common import exception
 
18
from heat.common.i18n import _
 
19
from heat.common.i18n import _LW
 
20
from heat.engine import resource
 
21
 
 
22
LOG = logging.getLogger(__name__)
 
23
 
 
24
 
 
25
class StackUser(resource.Resource):
 
26
 
 
27
    # Subclasses create a user, and optionally keypair associated with a
 
28
    # resource in a stack. Users are created  in the heat stack user domain
 
29
    # (in a project specific to the stack)
 
30
    def __init__(self, name, json_snippet, stack):
 
31
        super(StackUser, self).__init__(name, json_snippet, stack)
 
32
 
 
33
    def handle_create(self):
 
34
        self._create_user()
 
35
 
 
36
    def _create_user(self):
 
37
        # Check for stack user project, create if not yet set
 
38
        if not self.stack.stack_user_project_id:
 
39
            project_id = self.keystone().create_stack_domain_project(
 
40
                self.stack.id)
 
41
            self.stack.set_stack_user_project_id(project_id)
 
42
 
 
43
        # Create a keystone user in the stack domain project
 
44
        user_id = self.keystone().create_stack_domain_user(
 
45
            username=self.physical_resource_name(),
 
46
            password=getattr(self, 'password', None),
 
47
            project_id=self.stack.stack_user_project_id)
 
48
 
 
49
        # Store the ID in resource data, for compatibility with SignalResponder
 
50
        self.data_set('user_id', user_id)
 
51
 
 
52
    def _user_token(self):
 
53
        project_id = self.stack.stack_user_project_id
 
54
        if not project_id:
 
55
            raise ValueError(_("Can't get user token, user not yet created"))
 
56
        password = getattr(self, 'password', None)
 
57
        # FIXME(shardy): the create and getattr here could allow insane
 
58
        # passwords, e.g a zero length string, if these happen it almost
 
59
        # certainly means a bug elsewhere in heat, so add assertion to catch
 
60
        if password is None:
 
61
            raise ValueError(_("Can't get user token without password"))
 
62
 
 
63
        return self.keystone().stack_domain_user_token(
 
64
            user_id=self._get_user_id(),
 
65
            project_id=project_id, password=password)
 
66
 
 
67
    def _get_user_id(self):
 
68
        user_id = self.data().get('user_id')
 
69
        if user_id:
 
70
            return user_id
 
71
        else:
 
72
            # FIXME(shardy): This is a legacy hack for backwards compatibility
 
73
            # remove after an appropriate transitional period...
 
74
            # Assume this is a resource that was created with
 
75
            # a previous version of heat and that the resource_id
 
76
            # is the user_id
 
77
            if self.resource_id:
 
78
                self.data_set('user_id', self.resource_id)
 
79
                return self.resource_id
 
80
 
 
81
    def handle_delete(self):
 
82
        self._delete_user()
 
83
 
 
84
    def _delete_user(self):
 
85
        user_id = self._get_user_id()
 
86
        if user_id is None:
 
87
            return
 
88
        try:
 
89
            self.keystone().delete_stack_domain_user(
 
90
                user_id=user_id, project_id=self.stack.stack_user_project_id)
 
91
        except kc_exception.NotFound:
 
92
            pass
 
93
        except ValueError:
 
94
            # FIXME(shardy): This is a legacy delete path for backwards
 
95
            # compatibility with resources created before the migration
 
96
            # to stack_user.StackUser domain users.  After an appropriate
 
97
            # transitional period, this should be removed.
 
98
            LOG.warn(_LW('Reverting to legacy user delete path'))
 
99
            try:
 
100
                self.keystone().delete_stack_user(user_id)
 
101
            except kc_exception.NotFound:
 
102
                pass
 
103
        for data_key in ('credential_id', 'access_key', 'secret_key'):
 
104
            self.data_delete(data_key)
 
105
 
 
106
    def handle_suspend(self):
 
107
        user_id = self._get_user_id()
 
108
        try:
 
109
            self.keystone().disable_stack_domain_user(
 
110
                user_id=user_id, project_id=self.stack.stack_user_project_id)
 
111
        except ValueError:
 
112
            # FIXME(shardy): This is a legacy path for backwards compatibility
 
113
            self.keystone().disable_stack_user(user_id=user_id)
 
114
 
 
115
    def handle_resume(self):
 
116
        user_id = self._get_user_id()
 
117
        try:
 
118
            self.keystone().enable_stack_domain_user(
 
119
                user_id=user_id, project_id=self.stack.stack_user_project_id)
 
120
        except ValueError:
 
121
            # FIXME(shardy): This is a legacy path for backwards compatibility
 
122
            self.keystone().enable_stack_user(user_id=user_id)
 
123
 
 
124
    def _create_keypair(self):
 
125
        # Subclasses may optionally call this in handle_create to create
 
126
        # an ec2 keypair associated with the user, the resulting keys are
 
127
        # stored in resource_data
 
128
        user_id = self._get_user_id()
 
129
        kp = self.keystone().create_stack_domain_user_keypair(
 
130
            user_id=user_id, project_id=self.stack.stack_user_project_id)
 
131
        if not kp:
 
132
            raise exception.Error(_("Error creating ec2 keypair for user %s") %
 
133
                                  user_id)
 
134
        else:
 
135
            try:
 
136
                credential_id = kp.id
 
137
            except AttributeError:
 
138
                # keystone v2 keypairs do not have an id attribute. Use the
 
139
                # access key instead.
 
140
                credential_id = kp.access
 
141
            self.data_set('credential_id', credential_id, redact=True)
 
142
            self.data_set('access_key', kp.access, redact=True)
 
143
            self.data_set('secret_key', kp.secret, redact=True)
 
144
        return kp
 
145
 
 
146
    def _delete_keypair(self):
 
147
        # Subclasses may optionally call this to delete a keypair created
 
148
        # via _create_keypair
 
149
        user_id = self._get_user_id()
 
150
        credential_id = self.data().get('credential_id')
 
151
        if not credential_id:
 
152
            return
 
153
 
 
154
        try:
 
155
            self.keystone().delete_stack_domain_user_keypair(
 
156
                user_id=user_id, project_id=self.stack.stack_user_project_id,
 
157
                credential_id=credential_id)
 
158
        except ValueError:
 
159
            self.keystone().delete_ec2_keypair(
 
160
                user_id=user_id, credential_id=credential_id)
 
161
 
 
162
        for data_key in ('access_key', 'secret_key', 'credential_id'):
 
163
            self.data_delete(data_key)