X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=snaps%2Fopenstack%2Futils%2Fkeystone_utils.py;h=10ad68a91f79b0676bf1b6df50ab818a59287590;hb=1d3be6637bb78b514a0dfe32a34dbc7c93ea0611;hp=8175b9a6db3d7562cd6a855f13860d7eb1e4064a;hpb=57777f3df521553a06cd01a3861b415d2905ceca;p=snaps.git diff --git a/snaps/openstack/utils/keystone_utils.py b/snaps/openstack/utils/keystone_utils.py index 8175b9a..10ad68a 100644 --- a/snaps/openstack/utils/keystone_utils.py +++ b/snaps/openstack/utils/keystone_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs") +# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs") # and others. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,16 +12,44 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import requests +import logging + from keystoneclient.client import Client from keystoneauth1.identity import v3, v2 from keystoneauth1 import session -import logging +import requests +from snaps.domain.project import Project, Domain +from snaps.domain.role import Role +from snaps.domain.user import User logger = logging.getLogger('keystone_utils') -V2_VERSION = 'v2.0' +V2_VERSION_NUM = 2.0 +V2_VERSION_STR = 'v' + str(V2_VERSION_NUM) + + +def get_session_auth(os_creds): + """ + Return the session auth for keystone session + :param os_creds: the OpenStack credentials (OSCreds) object + :return: the auth + """ + if os_creds.identity_api_version == 3: + auth = v3.Password(auth_url=os_creds.auth_url, + username=os_creds.username, + password=os_creds.password, + project_name=os_creds.project_name, + user_domain_id=os_creds.user_domain_id, + user_domain_name=os_creds.user_domain_name, + project_domain_id=os_creds.project_domain_id, + project_domain_name=os_creds.project_domain_name) + else: + auth = v2.Password(auth_url=os_creds.auth_url, + username=os_creds.username, + password=os_creds.password, + tenant_name=os_creds.project_name) + return auth def keystone_session(os_creds): @@ -32,19 +60,21 @@ def keystone_session(os_creds): """ logger.debug('Retrieving Keystone Session') - if os_creds.identity_api_version == 3: - auth = v3.Password(auth_url=os_creds.auth_url, username=os_creds.username, password=os_creds.password, - project_name=os_creds.project_name, user_domain_id=os_creds.user_domain_id, - project_domain_id=os_creds.project_domain_id) - else: - auth = v2.Password(auth_url=os_creds.auth_url, username=os_creds.username, password=os_creds.password, - tenant_name=os_creds.project_name) + auth = get_session_auth(os_creds) req_session = None if os_creds.proxy_settings: req_session = requests.Session() - req_session.proxies = {'http': os_creds.proxy_settings.host + ':' + os_creds.proxy_settings.port} - return session.Session(auth=auth, session=req_session) + req_session.proxies = { + 'http': + os_creds.proxy_settings.host + ':' + + os_creds.proxy_settings.port, + 'https': + os_creds.proxy_settings.https_host + ':' + + os_creds.proxy_settings.https_port + } + return session.Session(auth=auth, session=req_session, + verify=os_creds.cacert) def keystone_client(os_creds): @@ -53,36 +83,68 @@ def keystone_client(os_creds): :param os_creds: the OpenStack credentials (OSCreds) object :return: the client """ - return Client(version=os_creds.identity_api_version, session=keystone_session(os_creds)) + return Client( + version=os_creds.identity_api_version, + session=keystone_session(os_creds), + interface=os_creds.interface, + region_name=os_creds.region_name) -def get_project(keystone=None, os_creds=None, project_name=None): +def get_endpoint(os_creds, service_type, interface='public'): + """ + Returns the endpoint of specific service + :param os_creds: the OpenStack credentials (OSCreds) object + :param service_type: the type of specific service + :param interface: the type of interface + :return: the endpoint url + """ + auth = get_session_auth(os_creds) + key_session = keystone_session(os_creds) + return key_session.get_endpoint( + auth=auth, service_type=service_type, interface=interface) + + +def get_project(keystone=None, os_creds=None, project_settings=None, + project_name=None): """ Returns the first project object or None if not found :param keystone: the Keystone client - :param os_creds: the OpenStack credentials used to obtain the Keystone client if the keystone parameter is None + :param os_creds: the OpenStack credentials used to obtain the Keystone + client if the keystone parameter is None + :param project_settings: a ProjectSettings object :param project_name: the name to query - :return: the ID or None + :return: the SNAPS-OO Project domain object or None """ - if not project_name: - return None - if not keystone: if os_creds: keystone = keystone_client(os_creds) else: - raise Exception('Cannot lookup project without the proper credentials') + raise KeystoneException( + 'Cannot lookup project without the proper credentials') + + proj_filter = dict() - if keystone.version == V2_VERSION: + if project_name: + proj_filter['name'] = project_name + elif project_settings: + proj_filter['name'] = project_settings.name + proj_filter['description'] = project_settings.description + proj_filter['domain_name'] = project_settings.domain_name + proj_filter['enabled'] = project_settings.enabled + + if keystone.version == V2_VERSION_STR: projects = keystone.tenants.list() else: - projects = keystone.projects.list(**{'name': project_name}) + projects = keystone.projects.list(**proj_filter) for project in projects: - if project.name == project_name: - return project + if project.name == proj_filter['name']: + domain_id = None + if keystone.version != V2_VERSION_STR: + domain_id = project.domain_id - return None + return Project(name=project.name, project_id=project.id, + domain_id=domain_id) def create_project(keystone, project_settings): @@ -90,26 +152,49 @@ def create_project(keystone, project_settings): Creates a project :param keystone: the Keystone client :param project_settings: the project configuration - :return: + :return: SNAPS-OO Project domain object """ - if keystone.version == V2_VERSION: - return keystone.tenants.create(project_settings.name, project_settings.description, project_settings.enabled) + domain_id = None - return keystone.projects.create(project_settings.name, project_settings.domain, - description=project_settings.description, - enabled=project_settings.enabled) + if keystone.version == V2_VERSION_STR: + os_project = keystone.tenants.create( + project_settings.name, project_settings.description, + project_settings.enabled) + else: + os_domain = __get_os_domain_by_name( + keystone, project_settings.domain_name) + if not os_domain: + os_domain = project_settings.domain_name + os_project = keystone.projects.create( + project_settings.name, os_domain, + description=project_settings.description, + enabled=project_settings.enabled) + domain_id = os_project.domain_id + + return Project( + name=os_project.name, project_id=os_project.id, domain_id=domain_id) def delete_project(keystone, project): """ Deletes a project :param keystone: the Keystone clien - :param project: the OpenStack project object + :param project: the SNAPS-OO Project domain object """ - if keystone.version == V2_VERSION: - keystone.tenants.delete(project) + if keystone.version == V2_VERSION_STR: + keystone.tenants.delete(project.id) else: - keystone.projects.delete(project) + keystone.projects.delete(project.id) + + +def __get_os_user(keystone, user): + """ + Returns the OpenStack user object + :param keystone: the Keystone client object + :param user: the SNAPS-OO User domain object + :return: + """ + return keystone.users.get(user.id) def get_user(keystone, username, project_name=None): @@ -118,9 +203,11 @@ def get_user(keystone, username, project_name=None): :param keystone: the keystone client :param username: the username to lookup :param project_name: the associated project (optional) - :return: + :return: a SNAPS-OO User domain object or None """ - project = get_project(keystone=keystone, project_name=project_name) + project = None + if project_name: + project = get_project(keystone=keystone, project_name=project_name) if project: users = keystone.users.list(tenant_id=project.id) @@ -129,7 +216,7 @@ def get_user(keystone, username, project_name=None): for user in users: if user.name == username: - return user + return User(name=user.name, user_id=user.id) return None @@ -139,34 +226,102 @@ def create_user(keystone, user_settings): Creates a user :param keystone: the Keystone client :param user_settings: the user configuration - :return: + :return: a SNAPS-OO User domain object """ project = None if user_settings.project_name: - project = get_project(keystone=keystone, project_name=user_settings.project_name) + project = get_project(keystone=keystone, + project_name=user_settings.project_name) - if keystone.version == V2_VERSION: + if keystone.version == V2_VERSION_STR: project_id = None if project: project_id = project.id - return keystone.users.create(name=user_settings.name, password=user_settings.password, - email=user_settings.email, tenant_id=project_id, enabled=user_settings.enabled) + os_user = keystone.users.create( + name=user_settings.name, password=user_settings.password, + email=user_settings.email, tenant_id=project_id, + enabled=user_settings.enabled) else: - # TODO - need to support groups - return keystone.users.create(name=user_settings.name, password=user_settings.password, - email=user_settings.email, project=project, - # email=user_settings.email, project=project, group='default', - domain=user_settings.domain_name, - enabled=user_settings.enabled) + os_domain = __get_os_domain_by_name( + keystone, user_settings.domain_name) + if not os_domain: + os_domain = user_settings.domain_name + os_user = keystone.users.create( + name=user_settings.name, password=user_settings.password, + email=user_settings.email, project=project, + domain=os_domain, enabled=user_settings.enabled) + + for role_name, role_project in user_settings.roles.items(): + os_role = get_role_by_name(keystone, role_name) + os_project = get_project(keystone=keystone, project_name=role_project) + + if os_role and os_project: + existing_roles = get_roles_by_user(keystone, os_user, os_project) + found = False + for role in existing_roles: + if role.id == os_role.id: + found = True + + if not found: + grant_user_role_to_project( + keystone=keystone, user=os_user, role=os_role, + project=os_project) + + if os_user: + return User(name=os_user.name, user_id=os_user.id) def delete_user(keystone, user): """ Deletes a user :param keystone: the Keystone client + :param user: the SNAPS-OO User domain object + """ + keystone.users.delete(user.id) + + +def get_role_by_name(keystone, name): + """ + Returns an OpenStack role object of a given name or None if not exists + :param keystone: the keystone client + :param name: the role name + :return: the SNAPS-OO Role domain object + """ + roles = keystone.roles.list() + for role in roles: + if role.name == name: + return Role(name=role.name, role_id=role.id) + + +def get_roles_by_user(keystone, user, project): + """ + Returns a list of SNAPS-OO Role domain objects associated with a user + :param keystone: the keystone client :param user: the OpenStack user object + :param project: the OpenStack project object (only required for v2) + :return: a list of SNAPS-OO Role domain objects """ - keystone.users.delete(user) + if keystone.version == V2_VERSION_STR: + os_user = __get_os_user(keystone, user) + roles = keystone.roles.roles_for_user(os_user, project) + else: + roles = keystone.roles.list(user=user, project=project) + + out = list() + for role in roles: + out.append(Role(name=role.name, role_id=role.id)) + return out + + +def get_role_by_id(keystone, role_id): + """ + Returns an OpenStack role object of a given name or None if not exists + :param keystone: the keystone client + :param role_id: the role ID + :return: a SNAPS-OO Role domain object + """ + role = keystone.roles.get(role_id) + return Role(name=role.name, role_id=role.id) def create_role(keystone, name): @@ -174,31 +329,65 @@ def create_role(keystone, name): Creates an OpenStack role :param keystone: the keystone client :param name: the role name - :return: + :return: a SNAPS-OO Role domain object """ - return keystone.roles.create(name) + role = keystone.roles.create(name) + return Role(name=role.name, role_id=role.id) def delete_role(keystone, role): """ Deletes an OpenStack role :param keystone: the keystone client - :param role: the role to delete + :param role: the SNAPS-OO Role domain object to delete :return: """ - keystone.roles.delete(role) + keystone.roles.delete(role.id) -def assoc_user_to_project(keystone, role, user, project): +def grant_user_role_to_project(keystone, role, user, project): """ - Adds a user to a project + Grants user and role to a project :param keystone: the Keystone client - :param role: the role used to join a project/user - :param user: the user to add to the project + :param role: the SNAPS-OO Role domain object used to join a project/user + :param user: the user to add to the project (SNAPS-OO User Domain object :param project: the project to which to add a user :return: """ - if keystone.version == V2_VERSION: - keystone.roles.add_user_role(user, role, tenant=project) + + os_role = get_role_by_id(keystone, role.id) + if keystone.version == V2_VERSION_STR: + keystone.roles.add_user_role(user, os_role, tenant=project) else: - keystone.roles.grant(role, user=user, project=project) + keystone.roles.grant(os_role, user=user, project=project) + + +def get_domain_by_id(keystone, domain_id): + """ + Returns the first OpenStack domain with the given name else None + :param keystone: the Keystone client + :param domain_id: the domain ID to retrieve + :return: the SNAPS-OO Domain domain object + """ + domain = keystone.domains.get(domain_id) + if domain: + return Domain(name=domain.name, domain_id=domain.id) + + +def __get_os_domain_by_name(keystone, domain_name): + """ + Returns the first OpenStack domain with the given name else None + :param keystone: the Keystone client + :param domain_name: the domain name to lookup + :return: the OpenStack domain object + """ + domains = keystone.domains.list(name=domain_name) + for domain in domains: + if domain.name == domain_name: + return domain + + +class KeystoneException(Exception): + """ + Exception when calls to the Keystone client cannot be served properly + """