]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Implement the user/access keys using keystone
authorAngus Salkeld <asalkeld@redhat.com>
Wed, 13 Jun 2012 02:54:57 +0000 (12:54 +1000)
committerAngus Salkeld <asalkeld@redhat.com>
Wed, 13 Jun 2012 02:54:57 +0000 (12:54 +1000)
Still no groups or policies

Change-Id: I3a78a62f741ed5979994b327e269fb9407bcebff
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
heat/engine/resources.py
heat/engine/user.py
tools/pip-requires

index 70e6f68c7d3eb015e95f46e399aac98c2a73a799..137b41be0b6dbdd6413623780904d3cdce42726e 100644 (file)
@@ -17,7 +17,9 @@ import base64
 from datetime import datetime
 import logging
 
-from novaclient.v1_1 import client
+from novaclient.v1_1 import client as nc
+from keystoneclient.v2_0 import client as kc
+
 from novaclient.exceptions import BadRequest
 from novaclient.exceptions import NotFound
 
@@ -75,10 +77,22 @@ class Resource(object):
             self.state = None
             self.id = None
         self._nova = {}
+        self._keystone = None
 
     def parsed_template(self):
         return self.stack.resolve_runtime_data(self.t)
 
+    def keystone(self):
+        if self._keystone:
+            return self._keystone
+
+        con = self.stack.context
+        self._keystone = kc.Client(username=con.username,
+                                   password=con.password,
+                                   tenant_name=con.tenant,
+                                   auth_url=con.auth_url)
+        return self._keystone
+
     def nova(self, service_type='compute'):
         if service_type in self._nova:
             return self._nova[service_type]
@@ -89,14 +103,14 @@ class Resource(object):
             service_name = None
 
         con = self.stack.context
-        self._nova[service_type] = client.Client(con.username,
-                                                 con.password,
-                                                 con.tenant,
-                                                 con.auth_url,
-                                                 proxy_token=con.auth_token,
-                                                 proxy_tenant_id=con.tenant_id,
-                                                 service_type=service_type,
-                                                 service_name=service_name)
+        self._nova[service_type] = nc.Client(con.username,
+                                             con.password,
+                                             con.tenant,
+                                             con.auth_url,
+                                             proxy_token=con.auth_token,
+                                             proxy_tenant_id=con.tenant_id,
+                                             service_type=service_type,
+                                             service_name=service_name)
         return self._nova[service_type]
 
     def calculate_properties(self):
index 58e64a80a593fed8177d387edb057de7d16bc35f..70f1d7504144a6d380d3eb58ebf5e551d59fd732 100644 (file)
 #    under the License.
 
 import logging
-from novaclient.exceptions import BadRequest
 from heat.common import exception
 from heat.engine.resources import Resource
 
+
 logger = logging.getLogger('heat.engine.user')
 
+#
+# We are ignoring Policies and Groups as keystone does not support them.
+#
+# For now support users and accesskeys.
+#
+
+
+class DummyId:
+    def __init__(self, id):
+        self.id = id
+
 
 class User(Resource):
     properties_schema = {'Path': {'Type': 'String',
                                   'Implemented': False},
                          'Groups': {'Type': 'CommaDelimitedList',
                                     'Implemented': False},
-                         'LoginProfile': {'Type': 'String',
-                                          'Implemented': False},
-                         'Policies': {'Type': 'List'}}
+                         'LoginProfile': {'Type': 'List'},
+                         'Policies': {'Type': 'List',
+                                      'Implemented': False}}
 
     def __init__(self, name, json_snippet, stack):
         super(User, self).__init__(name, json_snippet, stack)
 
     def create(self):
+        if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
+            return
+        self.state_set(self.CREATE_IN_PROGRESS)
+        super(User, self).create()
+
+        passwd = ''
+        if 'LoginProfile' in self.properties:
+            if self.properties['LoginProfile'] and \
+                'Password' in self.properties['LoginProfile']:
+                passwd = self.properties['LoginProfile']['Password']
+
+        tenant_id = self.stack.context.tenant_id
+        user = self.keystone().users.create(self.name, passwd,
+                                            '%s@heat-api.org' % self.name,
+                                            tenant_id=tenant_id,
+                                            enabled=True)
+        self.instance_id_set(user.id)
         self.state_set(self.CREATE_COMPLETE)
 
+    def delete(self):
+        if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
+            return
+        self.state_set(self.DELETE_IN_PROGRESS)
+        super(User, self).delete()
+
+        try:
+            user = self.keystone().users.get(DummyId(self.instance_id))
+        except Exception as ex:
+            logger.info('user %s/%s does not exist' % (self.name,
+                                                       self.instance_id))
+        else:
+            user.delete()
+
+        self.state_set(self.DELETE_COMPLETE)
+
+    def FnGetRefId(self):
+        return unicode(self.name)
+
     def FnGetAtt(self, key):
         res = None
         if key == 'Policies':
-            res = self.t['Properties']['Policies']
+            res = self.properties['Policies']
         else:
             raise exception.InvalidTemplateAttribute(resource=self.name,
                                                      key=key)
@@ -50,28 +97,82 @@ class User(Resource):
 
 class AccessKey(Resource):
     properties_schema = {'Serial': {'Type': 'Integer',
-                                  'Implemented': False},
+                                    'Implemented': False},
                          'UserName': {'Type': 'String',
-                                  'Required': True},
+                                      'Required': True},
                          'Status': {'Type': 'String',
-                                  'Implemented': False,
-                                  'AllowedValues': ['Active', 'Inactive']}}
+                                    'Implemented': False,
+                                    'AllowedValues': ['Active', 'Inactive']}}
 
     def __init__(self, name, json_snippet, stack):
         super(AccessKey, self).__init__(name, json_snippet, stack)
+        self._secret = None
+
+    def _user_from_name(self, username):
+        tenant_id = self.stack.context.tenant_id
+        users = self.keystone().users.list(tenant_id=tenant_id)
+        for u in users:
+            if u.name == self.properties['UserName']:
+                return u
+        return None
 
     def create(self):
+        if self.state in [self.CREATE_IN_PROGRESS, self.CREATE_COMPLETE]:
+            return
+        self.state_set(self.CREATE_IN_PROGRESS)
+        super(AccessKey, self).create()
+
+        user = self._user_from_name(self.properties['UserName'])
+        if user is None:
+            raise exception.NotFound('could not find user %s' %
+                                     self.properties['UserName'])
+
+        tenant_id = self.stack.context.tenant_id
+        cred = self.keystone().ec2.create(user.id, tenant_id)
+        self.instance_id_set(cred.access)
+        self._secret = cred.secret
+
         self.state_set(self.CREATE_COMPLETE)
 
-    def FnGetRefId(self):
-        return unicode(self.name)
+    def delete(self):
+        if self.state in [self.DELETE_IN_PROGRESS, self.DELETE_COMPLETE]:
+            return
+        self.state_set(self.DELETE_IN_PROGRESS)
+        super(AccessKey, self).delete()
+
+        user = self._user_from_name(self.properties['UserName'])
+        if user and self.instance_id:
+            self.keystone().ec2.delete(user.id, self.instance_id)
+
+        self.state_set(self.DELETE_COMPLETE)
+
+    def _secret_accesskey(self):
+        '''
+        Return the user's access key, fetching it from keystone if necessary
+        '''
+        if self._secret is None:
+            user = self._user_from_name(self.properties['UserName'])
+            if user is None:
+                logger.warn('could not find user %s' %
+                            self.properties['UserName'])
+            else:
+                try:
+                    cred = self.keystone().ec2.get(user.id, self.instance_id)
+                    self._secret = cred.secret
+                    self.instance_id_set(cred.access)
+                except Exception as ex:
+                    logger.warn('could not get secret for %s Error:%s' %
+                                (self.properties['UserName'],
+                                 str(ex)))
+
+        return self._secret or '000-000-000'
 
     def FnGetAtt(self, key):
         res = None
         if key == 'UserName':
-            res = self.t['Properties']['UserName']
+            res = self.properties['UserName']
         if key == 'SecretAccessKey':
-            res = 'TODO-Add-Real-SecreateAccessKey'
+            res = self._secret_accesskey()
         else:
             raise exception.InvalidTemplateAttribute(resource=self.name,
                                                      key=key)
index a364bcf0b95b0cbf3965cb6fba64fb258d849f21..d32fb784819b636cbb684c1f4b1acf4a82338f08 100644 (file)
@@ -16,6 +16,7 @@ httplib2
 kombu
 iso8601>=0.1.4
 python-novaclient
+https://github.com/openstack/python-keystoneclient/zipball/master#egg=python-keystoneclient
 glance
 
 # Note you will need gcc buildtools installed and must