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
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]
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):
# 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)
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)