def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self.cloud = cloud self.filter_tenant_id = None self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.mysql_connector = cloud.mysql_connector('keystone') self.templater = Templater() self.generator = GeneratorPassword()
def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self.keystone_client = self.proxy(self.get_client(), config) self.mysql_connector = cloud.mysql_connector self.cloud = cloud self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.templater = Templater() self.generator = GeneratorPassword()
class KeystoneIdentity(identity.Identity): """The main class for working with OpenStack Keystone Identity Service.""" def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self.cloud = cloud self.filter_tenant_id = None self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.mysql_connector = cloud.mysql_connector('keystone') self.templater = Templater() self.generator = GeneratorPassword() @property def keystone_client(self): return self.proxy(self._get_client_by_creds(), self.config) @staticmethod def convert(identity_obj, cfg): """Convert OpenStack Keystone object to CloudFerry object. :param identity_obj: Direct OpenStack Keystone object to convert, supported objects: tenants, users and roles; :param cfg: Cloud config. """ if isinstance(identity_obj, keystone_client.tenants.Tenant): return {'tenant': {'name': identity_obj.name, 'id': identity_obj.id, 'description': identity_obj.description}, 'meta': {}} elif isinstance(identity_obj, keystone_client.users.User): overwirte_user_passwords = cfg.migrate.overwrite_user_passwords return {'user': {'name': identity_obj.name, 'id': identity_obj.id, 'email': getattr(identity_obj, 'email', ''), 'tenantId': getattr(identity_obj, 'tenantId', '') }, 'meta': { 'overwrite_password': overwirte_user_passwords}} elif isinstance(identity_obj, keystone_client.roles.Role): return {'role': {'name': identity_obj.name, 'id': identity_obj}, 'meta': {}} LOG.error('KeystoneIdentity converter has received incorrect value. ' 'Please pass to it only tenants, users or role objects.') return None def has_tenants_by_id_cached(self): tenants = set([t.id for t in self.keystone_client.tenants.list()]) def func(tenant_id): return tenant_id in tenants return func def read_info(self, **kwargs): info = {'tenants': [], 'users': [], 'roles': []} if kwargs.get('tenant_id'): self.filter_tenant_id = kwargs['tenant_id'][0] tenant_list = self.get_tenants_list() for tenant in tenant_list: tnt = self.convert(tenant, self.config) info['tenants'].append(tnt) user_list = self.get_users_list() has_tenants_by_id_cached = self.has_tenants_by_id_cached() has_roles_by_ids_cached = self._get_user_roles_cached() for user in user_list: usr = self.convert(user, self.config) if has_tenants_by_id_cached(getattr(user, 'tenantId', '')): info['users'].append(usr) else: LOG.info("User's '%s' primary tenant '%s' is deleted, " "finding out if user is a member of other tenants", user.name, getattr(user, 'tenantId', '')) for t in tenant_list: roles = has_roles_by_ids_cached(user.id, t.id) if roles: LOG.info("Setting tenant '%s' for user '%s' as " "primary", t.name, user.name) usr['user']['tenantId'] = t.id info['users'].append(usr) break for role in self.get_roles_list(): rl = self.convert(role, self.config) info['roles'].append(rl) info['user_tenants_roles'] = \ self._get_user_tenants_roles(tenant_list, user_list) if self.config['migrate']['keep_user_passwords']: info['user_passwords'] = self._get_user_passwords() return info def deploy(self, info): LOG.info("Identity objects deployment started") tenants = info['tenants'] users = info['users'] roles = info['user_tenants_roles'] self._deploy_tenants(tenants) self._deploy_roles(info['roles']) self._deploy_users(users, tenants) if not self.config.migrate.migrate_users: users = info['users'] = self._update_users_info(users) if self.config['migrate']['keep_user_passwords']: passwords = info['user_passwords'] self._upload_user_passwords(users, passwords) self._upload_user_tenant_roles(roles, users, tenants) LOG.info("Done") def get_client(self): """ Getting keystone client using authentication with admin auth token. :return: OpenStack Keystone Client instance """ return self.keystone_client def _get_client_by_creds(self): """Authenticating with a user name and password. :return: OpenStack Keystone Client instance """ kwargs = { "username": self.config.cloud.user, "password": self.config.cloud.password, "tenant_name": self.config.cloud.tenant, "auth_url": self.config.cloud.auth_url, "cacert": self.config.cloud.cacert, "insecure": self.config.cloud.insecure } if self.config.cloud.region: kwargs["region_name"] = self.config.cloud.region return keystone_client.Client(**kwargs) def get_endpoint_by_service_type(self, service_type, endpoint_type): """Getting endpoint URL by service type. :param service_type: OpenStack service type (image, compute etc.) :param endpoint_type: publicURL or internalURL :return: String endpoint of specified OpenStack service """ kwargs = { "service_type": service_type, "endpoint_type": endpoint_type } if self.config.cloud.region: kwargs['region_name'] = self.config.cloud.region return self.keystone_client.service_catalog.url_for(**kwargs) def get_tenants_func(self): tenants = {tenant.id: tenant.name for tenant in self.get_tenants_list()} def func(tenant_id): return tenants.get(tenant_id, 'admin') return func def get_tenant_id_by_name(self, name): """ Getting tenant ID by name from keystone. """ return self.keystone_client.tenants.find(name=name).id def get_tenant_by_name(self, tenant_name): """ Getting tenant by name from keystone. """ for tenant in self.get_tenants_list(): if tenant.name == tenant_name: return tenant def get_tenant_by_id(self, tenant_id): """ Getting tenant by id from keystone. """ return self.keystone_client.tenants.get(tenant_id) def try_get_tenant_name_by_id(self, tenant_id, default=None): """ Same as `get_tenant_by_id` but returns `default` in case tenant ID is not present """ try: return self.keystone_client.tenants.get(tenant_id).name except keystoneclient.exceptions.NotFound: LOG.warning("Tenant '%s' not found, returning default value = " "'%s'", tenant_id, default) return default def get_services_list(self): """ Getting list of available services from keystone. """ return self.keystone_client.services.list() def get_tenants_list(self): """ Getting list of tenants from keystone. """ result = [] ks_tenants = self.keystone_client.tenants if self.filter_tenant_id: result.append(ks_tenants.find(id=self.filter_tenant_id)) # public image owners must also be migrated image_owners = set() glance = self.cloud.resources[utl.IMAGE_RESOURCE] for image in glance.get_image_list(): image_owners.add(image.owner) image_owners -= {self.filter_tenant_id} for owner in image_owners: result.append(ks_tenants.find(id=owner)) else: result = ks_tenants.list() return result def get_users_list(self): """ Getting list of users from keystone. """ if self.filter_tenant_id: tenant_id = self.filter_tenant_id else: tenant_id = None return self.keystone_client.users.list(tenant_id=tenant_id) def get_roles_list(self): """ Getting list of available roles from keystone. """ return self.keystone_client.roles.list() def try_get_username_by_id(self, user_id, default=None): try: return self.keystone_client.users.get(user_id).name except keystoneclient.exceptions.NotFound: return default def try_get_user_by_id(self, user_id, default=None): if default is None: admin_usr = \ self.try_get_user_by_name(username=self.config.cloud.user) default = admin_usr.id try: return self.keystone_client.users.find(id=user_id) except keystoneclient.exceptions.NotFound: LOG.warning("User '%s' has not been found, returning default " "value = '%s'", user_id, default) return self.keystone_client.users.find(id=default) def try_get_user_by_name(self, username, default=None): try: return self.keystone_client.users.find(name=username) except keystoneclient.exceptions.NotFound: LOG.warning("User '%s' has not been found, returning default " "value = '%s'", username, default) return self.keystone_client.users.find(name=default) def roles_for_user(self, user_id, tenant_id): """ Getting list of user roles for tenant """ return self.keystone_client.roles.roles_for_user(user_id, tenant_id) def create_role(self, role_name): """ Create new role in keystone. """ return self.keystone_client.roles.create(role_name) def create_tenant(self, tenant_name, description=None, enabled=True): """ Create new tenant in keystone. """ return self.keystone_client.tenants.create(tenant_name=tenant_name, description=description, enabled=enabled) def create_user(self, name, password=None, email=None, tenant_id=None, enabled=True): """ Create new user in keystone. """ return self.keystone_client.users.create(name=name, password=password, email=email, tenant_id=tenant_id, enabled=enabled) def update_tenant(self, tenant_id, tenant_name=None, description=None, enabled=None): """Update a tenant with a new name and description.""" return self.keystone_client.tenants.update(tenant_id, tenant_name=tenant_name, description=description, enabled=enabled) def update_user(self, user, **kwargs): """Update user data. Supported arguments include ``name``, ``email``, and ``enabled``. """ return self.keystone_client.users.update(user, **kwargs) def get_auth_token_from_user(self): self.keystone_client.authenticate() return self.keystone_client.auth_token def _deploy_tenants(self, tenants): LOG.info('Deploying tenants...') dst_tenants = {tenant.name: tenant.id for tenant in self.get_tenants_list()} for _tenant in tenants: tenant = _tenant['tenant'] if tenant['name'] not in dst_tenants: LOG.debug("Creating tenant '%s'", tenant['name']) _tenant['meta']['new_id'] = self.create_tenant( tenant['name'], tenant['description']).id else: LOG.debug("Tenant '%s' is already present on destination, " "skipping", tenant['name']) _tenant['meta']['new_id'] = dst_tenants[tenant['name']] LOG.info("Tenant deployment done.") def _deploy_users(self, users, tenants): dst_users = {user.name: user.id for user in self.get_users_list()} tenant_mapped_ids = {tenant['tenant']['id']: tenant['meta']['new_id'] for tenant in tenants} keep_passwd = self.config['migrate']['keep_user_passwords'] overwrite_passwd = self.config['migrate']['overwrite_user_passwords'] for _user in users: user = _user['user'] password = self._generate_password() if user['name'] in dst_users: # Create users mapping _user['meta']['new_id'] = dst_users[user['name']] if overwrite_passwd and not keep_passwd: self.update_user(_user['meta']['new_id'], password=password) self._passwd_notification(user['email'], user['name'], password) continue if not self.config.migrate.migrate_users: continue tenant_id = tenant_mapped_ids[user['tenantId']] _user['meta']['new_id'] = self.create_user(user['name'], password, user['email'], tenant_id).id if self.config['migrate']['keep_user_passwords']: _user['meta']['overwrite_password'] = True else: self._passwd_notification(user['email'], user['name'], password) @staticmethod def _update_users_info(users): """ Update users info. This method is needed for skip users, that have not been migrated to destination cloud and that do not exist there. So we leave information only about users with mapping and skip those, who don't have the same user on the destination cloud. This is done, because another tasks can use users mapping. :param users: OpenStack Keystone users info; :return: List with actual users info. """ users_info = [] for user in users: if user['meta'].get('new_id'): users_info.append(user) return users_info def _passwd_notification(self, email, name, password): if not self.postman: return template = 'templates/email.html' self._send_msg(email, 'New password notification', self._render_template(template, {'name': name, 'password': password})) def _deploy_roles(self, roles): LOG.info("Role deployment started...") dst_roles = { role.name.lower(): role.id for role in self.get_roles_list()} for _role in roles: role = _role['role'] if role['name'].lower() not in dst_roles: LOG.debug("Creating role '%s'", role['name']) _role['meta']['new_id'] = self.create_role(role['name']).id else: LOG.debug("Role '%s' is already present on destination, " "skipping", role['name']) _role['meta']['new_id'] = dst_roles[role['name'].lower()] LOG.info("Role deployment done.") def _get_user_passwords(self): info = {} for user in self.get_users_list(): for password in self.mysql_connector.execute( "SELECT password FROM user WHERE id = :user_id", user_id=user.id): info[user.name] = password[0] return info def _get_user_tenants_roles(self, tenant_list=None, user_list=None): if tenant_list is None: tenant_list = [] if user_list is None: user_list = [] if not self.config.migrate.optimize_user_role_fetch: user_tenants_roles = \ self._get_user_tenants_roles_by_api(tenant_list, user_list) else: user_tenants_roles = \ self._get_user_tenants_roles_by_db(tenant_list, user_list) return user_tenants_roles def _get_roles_sql_request(self): res = [] try: is_project_metadata = self.mysql_connector.execute( "SHOW TABLES LIKE 'user_project_metadata'").rowcount if is_project_metadata: # for grizzly case return self.mysql_connector.execute( "SELECT * FROM user_project_metadata") is_assignment = self.mysql_connector.execute( "SHOW TABLES LIKE 'assignment'").rowcount if is_assignment: # for icehouse case res_raw = self.mysql_connector.execute( "SELECT * FROM assignment") res_tmp = {} for (type_record, actor_id, project_id, role_id, inher_tmp) in res_raw: if (actor_id, project_id) not in res_tmp: res_tmp[(actor_id, project_id)] = {'roles': []} res_tmp[(actor_id, project_id)]['roles'].append(role_id) for k, v in res_tmp.iteritems(): res.append((k[0], k[1], str(v))) except ProgrammingError as e: LOG.warn(e.message) return res def _get_user_roles_cached(self): all_roles = {} if self.config.migrate.optimize_user_role_fetch: res = self._get_roles_sql_request() for user_id, tenant_id, roles_field in res: roles_ids = ast.literal_eval(roles_field)['roles'] all_roles[user_id] = {} \ if user_id not in all_roles else all_roles[user_id] all_roles[user_id][tenant_id] = [] \ if tenant_id not in all_roles[user_id] \ else all_roles[user_id][tenant_id] all_roles[user_id][tenant_id].extend(roles_ids) def _get_user_roles(user_id, tenant_id): if not self.config.migrate.optimize_user_role_fetch: roles = self.roles_for_user(user_id, tenant_id) else: roles = all_roles.get(user_id, {}).get(tenant_id, []) return roles return _get_user_roles def _get_user_tenants_roles_by_db(self, tenant_list, user_list): user_tenants_roles = {u.name: {t.name: [] for t in tenant_list} for u in user_list} tenant_ids = {tenant.id: tenant.name for tenant in tenant_list} user_ids = {user.id: user.name for user in user_list} roles = {r.id: r for r in self.get_roles_list()} res = self._get_roles_sql_request() for user_id, tenant_id, roles_field in res: # skip filtered tenants and users if user_id not in user_ids or tenant_id not in tenant_ids: continue roles_ids = ast.literal_eval(roles_field)['roles'] user_tenants_roles[user_ids[user_id]][tenant_ids[tenant_id]] = \ [{'role': {'name': roles[r].name, 'id': r}} for r in roles_ids] return user_tenants_roles def _get_user_tenants_roles_by_api(self, tenant_list, user_list): user_tenants_roles = {u.name: {t.name: [] for t in tenant_list} for u in user_list} for user in user_list: user_tenants_roles[user.name] = {} for tenant in tenant_list: roles = [] for role in self.roles_for_user(user.id, tenant.id): roles.append({'role': {'name': role.name, 'id': role.id}}) user_tenants_roles[user.name][tenant.name] = roles return user_tenants_roles def _upload_user_passwords(self, users, user_passwords): for _user in users: user = _user['user'] if not _user['meta']['overwrite_password']: continue self.mysql_connector.execute( "UPDATE user SET password = :password WHERE id = :user_id", user_id=_user['meta']['new_id'], password=user_passwords[user['name']]) def _upload_user_tenant_roles(self, user_tenants_roles, users, tenants): roles_id = {role.name: role.id for role in self.get_roles_list()} dst_users_obj = self.get_users_list() dst_users = {user.name: user.id for user in dst_users_obj} dst_roles = {role.id: role.name for role in self.get_roles_list()} _get_user_roles_cached = self._get_user_roles_cached() for _user in users: user = _user['user'] if user['name'] not in dst_users: continue for _tenant in tenants: tenant = _tenant['tenant'] user_roles_objs = _get_user_roles_cached( _user['meta']['new_id'], _tenant['meta']['new_id']) exists_roles = [dst_roles[role] if not hasattr(role, 'name') else role.name for role in user_roles_objs] for _role in user_tenants_roles[user['name']][tenant['name']]: role = _role['role'] if role['name'] in exists_roles: continue try: self.keystone_client.roles.add_user_role( _user['meta']['new_id'], roles_id[role['name']], _tenant['meta']['new_id']) except ks_exceptions.Conflict: LOG.info("Role '%s' for user '%s' in tenant '%s' " "already exists, skipping", role['name'], user['name'], tenant['name']) def _generate_password(self): return self.generator.get_random_password() def _send_msg(self, to, subject, msg): if self.postman: with self.postman as p: p.send(to, subject, msg) def _render_template(self, name_file, args): if self.templater: return self.templater.render(name_file, args) else: return None def check_rabbitmq(self): credentials = pika.PlainCredentials(self.config.rabbit.user, self.config.rabbit.password) for host_with_port in self.config.rabbit.hosts.split(","): host, port = host_with_port.split(':') pika.BlockingConnection( pika.ConnectionParameters(host=host.strip(), port=int(port), credentials=credentials))
class KeystoneIdentity(identity.Identity): """The main class for working with OpenStack Keystone Identity Service.""" def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self.cloud = cloud self.filter_tenant_id = None self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.mysql_connector = cloud.mysql_connector('keystone') self.templater = Templater() self.generator = GeneratorPassword() @property def keystone_client(self): return self.proxy(self._get_client_by_creds(), self.config) @staticmethod def convert(identity_obj, cfg): """Convert OpenStack Keystone object to CloudFerry object. :param identity_obj: Direct OpenStack Keystone object to convert, supported objects: tenants, users and roles; :param cfg: Cloud config. """ if isinstance(identity_obj, keystone_client.tenants.Tenant): return {'tenant': {'name': identity_obj.name, 'id': identity_obj.id, 'description': identity_obj.description}, 'meta': {}} elif isinstance(identity_obj, keystone_client.users.User): overwirte_user_passwords = cfg.migrate.overwrite_user_passwords return {'user': {'name': identity_obj.name, 'id': identity_obj.id, 'email': identity_obj.email, 'tenantId': identity_obj.tenantId}, 'meta': { 'overwrite_password': overwirte_user_passwords}} elif isinstance(identity_obj, keystone_client.roles.Role): return {'role': {'name': identity_obj.name, 'id': identity_obj}, 'meta': {}} LOG.error('KeystoneIdentity converter has received incorrect value. ' 'Please pass to it only tenants, users or role objects.') return None def has_tenants_by_id_cached(self): tenants = set([t.id for t in self.keystone_client.tenants.list()]) def func(tenant_id): return tenant_id in tenants return func def read_info(self, **kwargs): info = {'tenants': [], 'users': [], 'roles': []} if kwargs.get('tenant_id'): self.filter_tenant_id = kwargs['tenant_id'][0] tenant_list = self.get_tenants_list() for tenant in tenant_list: tnt = self.convert(tenant, self.config) info['tenants'].append(tnt) user_list = self.get_users_list() has_tenants_by_id_cached = self.has_tenants_by_id_cached() has_roles_by_ids_cached = self._get_user_roles_cached() for user in user_list: usr = self.convert(user, self.config) if has_tenants_by_id_cached(user.tenantId): info['users'].append(usr) else: LOG.info("User's '%s' primary tenant '%s' is deleted, " "finding out if user is a member of other tenants", user.name, user.tenantId) for t in tenant_list: roles = has_roles_by_ids_cached(user.id, t.id) if roles: LOG.info("Setting tenant '%s' for user '%s' as " "primary", t.name, user.name) usr['user']['tenantId'] = t.id info['users'].append(usr) break for role in self.get_roles_list(): rl = self.convert(role, self.config) info['roles'].append(rl) info['user_tenants_roles'] = \ self._get_user_tenants_roles(tenant_list, user_list) if self.config['migrate']['keep_user_passwords']: info['user_passwords'] = self._get_user_passwords() return info def deploy(self, info): LOG.info("Identity objects deployment started") tenants = info['tenants'] users = info['users'] roles = info['user_tenants_roles'] self._deploy_tenants(tenants) self._deploy_roles(info['roles']) self._deploy_users(users, tenants) if not self.config.migrate.migrate_users: users = info['users'] = self._update_users_info(users) if self.config['migrate']['keep_user_passwords']: passwords = info['user_passwords'] self._upload_user_passwords(users, passwords) self._upload_user_tenant_roles(roles, users, tenants) LOG.info("Done") def get_client(self): """ Getting keystone client using authentication with admin auth token. :return: OpenStack Keystone Client instance """ return self.keystone_client def _get_client_by_creds(self): """Authenticating with a user name and password. :return: OpenStack Keystone Client instance """ kwargs = { "username": self.config.cloud.user, "password": self.config.cloud.password, "tenant_name": self.config.cloud.tenant, "auth_url": self.config.cloud.auth_url } if self.config.cloud.region: kwargs["region_name"] = self.config.cloud.region return keystone_client.Client(**kwargs) def get_endpoint_by_service_type(self, service_type, endpoint_type): """Getting endpoint URL by service type. :param service_type: OpenStack service type (image, compute etc.) :param endpoint_type: publicURL or internalURL :return: String endpoint of specified OpenStack service """ kwargs = { "service_type": service_type, "endpoint_type": endpoint_type } if self.config.cloud.region: kwargs['region_name'] = self.config.cloud.region return self.keystone_client.service_catalog.url_for(**kwargs) def get_tenants_func(self): tenants = {tenant.id: tenant.name for tenant in self.get_tenants_list()} def func(tenant_id): return tenants.get(tenant_id, 'admin') return func def get_tenant_id_by_name(self, name): """ Getting tenant ID by name from keystone. """ return self.keystone_client.tenants.find(name=name).id def get_tenant_by_name(self, tenant_name): """ Getting tenant by name from keystone. """ for tenant in self.get_tenants_list(): if tenant.name == tenant_name: return tenant def get_tenant_by_id(self, tenant_id): """ Getting tenant by id from keystone. """ return self.keystone_client.tenants.get(tenant_id) def try_get_tenant_name_by_id(self, tenant_id, default=None): """ Same as `get_tenant_by_id` but returns `default` in case tenant ID is not present """ try: return self.keystone_client.tenants.get(tenant_id).name except keystoneclient.exceptions.NotFound: LOG.warning("Tenant '%s' not found, returning default value = " "'%s'", tenant_id, default) return default def get_services_list(self): """ Getting list of available services from keystone. """ return self.keystone_client.services.list() def get_tenants_list(self): """ Getting list of tenants from keystone. """ result = [] ks_tenants = self.keystone_client.tenants if self.filter_tenant_id: result.append(ks_tenants.find(id=self.filter_tenant_id)) else: result = ks_tenants.list() return result def get_users_list(self): """ Getting list of users from keystone. """ if self.filter_tenant_id: tenant_id = self.filter_tenant_id else: tenant_id = None return self.keystone_client.users.list(tenant_id=tenant_id) def get_roles_list(self): """ Getting list of available roles from keystone. """ return self.keystone_client.roles.list() def try_get_username_by_id(self, user_id, default=None): try: return self.keystone_client.users.get(user_id).name except keystoneclient.exceptions.NotFound: return default def try_get_user_by_id(self, user_id, default=None): if default is None: admin_usr = \ self.try_get_user_by_name(username=self.config.cloud.user) default = admin_usr.id try: return self.keystone_client.users.find(id=user_id) except keystoneclient.exceptions.NotFound: LOG.warning("User '%s' has not been found, returning default " "value = '%s'", user_id, default) return self.keystone_client.users.find(id=default) def try_get_user_by_name(self, username, default=None): try: return self.keystone_client.users.find(name=username) except keystoneclient.exceptions.NotFound: LOG.warning("User '%s' has not been found, returning default " "value = '%s'", username, default) return self.keystone_client.users.find(name=default) def roles_for_user(self, user_id, tenant_id): """ Getting list of user roles for tenant """ return self.keystone_client.roles.roles_for_user(user_id, tenant_id) def create_role(self, role_name): """ Create new role in keystone. """ return self.keystone_client.roles.create(role_name) def create_tenant(self, tenant_name, description=None, enabled=True): """ Create new tenant in keystone. """ return self.keystone_client.tenants.create(tenant_name=tenant_name, description=description, enabled=enabled) def create_user(self, name, password=None, email=None, tenant_id=None, enabled=True): """ Create new user in keystone. """ return self.keystone_client.users.create(name=name, password=password, email=email, tenant_id=tenant_id, enabled=enabled) def update_tenant(self, tenant_id, tenant_name=None, description=None, enabled=None): """Update a tenant with a new name and description.""" return self.keystone_client.tenants.update(tenant_id, tenant_name=tenant_name, description=description, enabled=enabled) def update_user(self, user, **kwargs): """Update user data. Supported arguments include ``name``, ``email``, and ``enabled``. """ return self.keystone_client.users.update(user, **kwargs) def get_auth_token_from_user(self): self.keystone_client.authenticate() return self.keystone_client.auth_token def _deploy_tenants(self, tenants): LOG.info('Deploying tenants...') dst_tenants = {tenant.name: tenant.id for tenant in self.get_tenants_list()} for _tenant in tenants: tenant = _tenant['tenant'] if tenant['name'] not in dst_tenants: LOG.debug("Creating tenant '%s'", tenant['name']) _tenant['meta']['new_id'] = self.create_tenant( tenant['name'], tenant['description']).id else: LOG.debug("Tenant '%s' is already present on destination, " "skipping", tenant['name']) _tenant['meta']['new_id'] = dst_tenants[tenant['name']] LOG.info("Tenant deployment done.") def _deploy_users(self, users, tenants): dst_users = {user.name: user.id for user in self.get_users_list()} tenant_mapped_ids = {tenant['tenant']['id']: tenant['meta']['new_id'] for tenant in tenants} keep_passwd = self.config['migrate']['keep_user_passwords'] overwrite_passwd = self.config['migrate']['overwrite_user_passwords'] for _user in users: user = _user['user'] password = self._generate_password() if user['name'] in dst_users: # Create users mapping _user['meta']['new_id'] = dst_users[user['name']] if overwrite_passwd and not keep_passwd: self.update_user(_user['meta']['new_id'], password=password) self._passwd_notification(user['email'], user['name'], password) continue if not self.config.migrate.migrate_users: continue tenant_id = tenant_mapped_ids[user['tenantId']] _user['meta']['new_id'] = self.create_user(user['name'], password, user['email'], tenant_id).id if self.config['migrate']['keep_user_passwords']: _user['meta']['overwrite_password'] = True else: self._passwd_notification(user['email'], user['name'], password) @staticmethod def _update_users_info(users): """ Update users info. This method is needed for skip users, that have not been migrated to destination cloud and that do not exist there. So we leave information only about users with mapping and skip those, who don't have the same user on the destination cloud. This is done, because another tasks can use users mapping. :param users: OpenStack Keystone users info; :return: List with actual users info. """ users_info = [] for user in users: if user['meta'].get('new_id'): users_info.append(user) return users_info def _passwd_notification(self, email, name, password): if not self.postman: return template = 'templates/email.html' self._send_msg(email, 'New password notification', self._render_template(template, {'name': name, 'password': password})) def _deploy_roles(self, roles): LOG.info("Role deployment started...") dst_roles = { role.name.lower(): role.id for role in self.get_roles_list()} for _role in roles: role = _role['role'] if role['name'].lower() not in dst_roles: LOG.debug("Creating role '%s'", role['name']) _role['meta']['new_id'] = self.create_role(role['name']).id else: LOG.debug("Role '%s' is already present on destination, " "skipping", role['name']) _role['meta']['new_id'] = dst_roles[role['name'].lower()] LOG.info("Role deployment done.") def _get_user_passwords(self): info = {} for user in self.get_users_list(): for password in self.mysql_connector.execute( "SELECT password FROM user WHERE id = :user_id", user_id=user.id): info[user.name] = password[0] return info def _get_user_tenants_roles(self, tenant_list=None, user_list=None): if tenant_list is None: tenant_list = [] if user_list is None: user_list = [] if not self.config.migrate.optimize_user_role_fetch: user_tenants_roles = \ self._get_user_tenants_roles_by_api(tenant_list, user_list) else: user_tenants_roles = \ self._get_user_tenants_roles_by_db(tenant_list, user_list) return user_tenants_roles def _get_roles_sql_request(self): res = [] try: is_project_metadata = self.mysql_connector.execute( "SHOW TABLES LIKE 'user_project_metadata'").rowcount if is_project_metadata: # for grizzly case return self.mysql_connector.execute( "SELECT * FROM user_project_metadata") is_assignment = self.mysql_connector.execute( "SHOW TABLES LIKE 'assignment'").rowcount if is_assignment: # for icehouse case res_raw = self.mysql_connector.execute( "SELECT * FROM assignment") res_tmp = {} for (type_record, actor_id, project_id, role_id, inher_tmp) in res_raw: if (actor_id, project_id) not in res_tmp: res_tmp[(actor_id, project_id)] = {'roles': []} res_tmp[(actor_id, project_id)]['roles'].append(role_id) for k, v in res_tmp.iteritems(): res.append((k[0], k[1], str(v))) except ProgrammingError as e: LOG.warn(e.message) return res def _get_user_roles_cached(self): all_roles = {} if self.config.migrate.optimize_user_role_fetch: res = self._get_roles_sql_request() for user_id, tenant_id, roles_field in res: roles_ids = ast.literal_eval(roles_field)['roles'] all_roles[user_id] = {} \ if user_id not in all_roles else all_roles[user_id] all_roles[user_id][tenant_id] = [] \ if tenant_id not in all_roles[user_id] \ else all_roles[user_id][tenant_id] all_roles[user_id][tenant_id].extend(roles_ids) def _get_user_roles(user_id, tenant_id): if not self.config.migrate.optimize_user_role_fetch: roles = self.roles_for_user(user_id, tenant_id) else: roles = all_roles.get(user_id, {}).get(tenant_id, []) return roles return _get_user_roles def _get_user_tenants_roles_by_db(self, tenant_list, user_list): user_tenants_roles = {u.name: {t.name: [] for t in tenant_list} for u in user_list} tenant_ids = {tenant.id: tenant.name for tenant in tenant_list} user_ids = {user.id: user.name for user in user_list} roles = {r.id: r for r in self.get_roles_list()} res = self._get_roles_sql_request() for user_id, tenant_id, roles_field in res: roles_ids = ast.literal_eval(roles_field)['roles'] if tenant_id not in tenant_ids: LOG.debug("Tenant ID '%s' is filtered from role migration", tenant_id) continue user_tenants_roles[user_ids[user_id]][tenant_ids[tenant_id]] = \ [{'role': {'name': roles[r].name, 'id': r}} for r in roles_ids] return user_tenants_roles def _get_user_tenants_roles_by_api(self, tenant_list, user_list): user_tenants_roles = {u.name: {t.name: [] for t in tenant_list} for u in user_list} for user in user_list: user_tenants_roles[user.name] = {} for tenant in tenant_list: roles = [] for role in self.roles_for_user(user.id, tenant.id): roles.append({'role': {'name': role.name, 'id': role.id}}) user_tenants_roles[user.name][tenant.name] = roles return user_tenants_roles def _upload_user_passwords(self, users, user_passwords): for _user in users: user = _user['user'] if not _user['meta']['overwrite_password']: continue self.mysql_connector.execute( "UPDATE user SET password = :password WHERE id = :user_id", user_id=_user['meta']['new_id'], password=user_passwords[user['name']]) def _upload_user_tenant_roles(self, user_tenants_roles, users, tenants): roles_id = {role.name: role.id for role in self.get_roles_list()} dst_users_obj = self.get_users_list() dst_users = {user.name: user.id for user in dst_users_obj} dst_roles = {role.id: role.name for role in self.get_roles_list()} _get_user_roles_cached = self._get_user_roles_cached() for _user in users: user = _user['user'] if user['name'] not in dst_users: continue for _tenant in tenants: tenant = _tenant['tenant'] user_roles_objs = _get_user_roles_cached( _user['meta']['new_id'], _tenant['meta']['new_id']) exists_roles = [dst_roles[role] if not hasattr(role, 'name') else role.name for role in user_roles_objs] for _role in user_tenants_roles[user['name']][tenant['name']]: role = _role['role'] if role['name'] in exists_roles: continue self.keystone_client.roles.add_user_role( _user['meta']['new_id'], roles_id[role['name']], _tenant['meta']['new_id']) def _generate_password(self): return self.generator.get_random_password() def _send_msg(self, to, subject, msg): if self.postman: with self.postman as p: p.send(to, subject, msg) def _render_template(self, name_file, args): if self.templater: return self.templater.render(name_file, args) else: return None def check_rabbitmq(self): credentials = pika.PlainCredentials(self.config.rabbit.user, self.config.rabbit.password) for host_with_port in self.config.rabbit.hosts.split(","): host, port = host_with_port.split(':') pika.BlockingConnection( pika.ConnectionParameters(host=host.strip(), port=int(port), credentials=credentials))
class KeystoneIdentity(identity.Identity): """The main class for working with OpenStack Keystone Identity Service.""" def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self._ks_client_creds = self.proxy(self._get_client_by_creds(), config) self.keystone_client = self.proxy(self.get_client(), config) self.mysql_connector = cloud.mysql_connector self.cloud = cloud self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.templater = Templater() self.generator = GeneratorPassword() @staticmethod def convert(identity_obj, cfg): """Convert OpenStack Keystone object to CloudFerry object. :param identity_obj: Direct OpenStack Keystone object to convert, supported objects: tenants, users and roles; :param cfg: Cloud config. """ if isinstance(identity_obj, keystone_client.tenants.Tenant): return {'tenant': {'name': identity_obj.name, 'id': identity_obj.id, 'description': identity_obj.description}, 'meta': {}} elif isinstance(identity_obj, keystone_client.users.User): overwirte_user_passwords = cfg.migrate.overwrite_user_passwords return {'user': {'name': identity_obj.name, 'id': identity_obj.id, 'email': identity_obj.email, 'tenantId': identity_obj.tenantId}, 'meta': { 'overwrite_password': overwirte_user_passwords}} elif isinstance(identity_obj, keystone_client.roles.Role): return {'role': {'name': identity_obj.name, 'id': identity_obj}, 'meta': {}} LOG.error('KeystoneIdentity converter has received incorrect value. ' 'Please pass to it only tenants, users or role objects.') return None def read_info(self, **kwargs): info = {'tenants': [], 'users': [], 'roles': []} service_tenant_id = \ self.get_tenant_id_by_name(self.config.cloud.service_tenant) for tenant in self.get_tenants_list(): if tenant.id != service_tenant_id: tnt = self.convert(tenant, self.config) info['tenants'].append(tnt) for user in self.get_users_list(): if user.tenantId != service_tenant_id: usr = self.convert(user, self.config) info['users'].append(usr) for role in self.get_roles_list(): rl = self.convert(role, self.config) info['roles'].append(rl) info['user_tenants_roles'] = self._get_user_tenants_roles() if self.config['migrate']['keep_user_passwords']: info['user_passwords'] = self._get_user_passwords() return info def deploy(self, info): print 'Deploy started' tenants = info['tenants'] users = info['users'] roles = info['user_tenants_roles'] self._deploy_tenants(tenants) self._deploy_roles(info['roles']) self._deploy_users(users, tenants) if not self.config.migrate.migrate_users: users = info['users'] = self._update_users_info(users) if self.config['migrate']['keep_user_passwords']: passwords = info['user_passwords'] self._upload_user_passwords(users, passwords) self._upload_user_tenant_roles(roles, users, tenants) print 'Finished' def get_client(self): """ Getting keystone client using authentication with admin auth token. :return: OpenStack Keystone Client instance """ return keystone_client.Client( token=self._ks_client_creds.auth_ref['token']['id'], endpoint=self.config.cloud.auth_url) def _get_client_by_creds(self): """Authenticating with a user name and password. :return: OpenStack Keystone Client instance """ return keystone_client.Client(username=self.config.cloud.user, password=self.config.cloud.password, tenant_name=self.config.cloud.tenant, auth_url=self.config.cloud.auth_url) def get_endpoint_by_service_type(self, service_type, endpoint_type): """Getting endpoint URL by service type. :param service_type: OpenStack service type (image, compute etc.) :param endpoint_type: publicURL or internalURL :return: String endpoint of specified OpenStack service """ return self._ks_client_creds.service_catalog.url_for( service_type=service_type, endpoint_type=endpoint_type) def get_tenants_func(self): tenants = {tenant.id: tenant.name for tenant in self.get_tenants_list()} def func(tenant_id): return tenants.get(tenant_id, 'admin') return func def get_tenant_id_by_name(self, name): for tenant in self.get_tenants_list(): if tenant.name == name: return tenant.id return None def get_tenant_by_name(self, tenant_name): """ Getting tenant by name from keystone. """ for tenant in self.get_tenants_list(): if tenant.name == tenant_name: return tenant def get_tenant_by_id(self, tenant_id): """ Getting tenant by id from keystone. """ return self.keystone_client.tenants.get(tenant_id) def try_get_tenant_name_by_id(self, tenant_id, default=None): """ Same as `get_tenant_by_id` but returns `default` in case tenant ID is not present """ try: return self.keystone_client.tenants.get(tenant_id).name except keystoneclient.exceptions.NotFound: LOG.warning("Tenant '%s' not found, returning default value = " "'%s'", tenant_id, default) return default def get_services_list(self): """ Getting list of available services from keystone. """ return self.keystone_client.services.list() def get_tenants_list(self): """ Getting list of tenants from keystone. """ return self.keystone_client.tenants.list() def get_users_list(self): """ Getting list of users from keystone. """ return self.keystone_client.users.list() def get_roles_list(self): """ Getting list of available roles from keystone. """ return self.keystone_client.roles.list() def try_get_username_by_id(self, user_id, default=None): try: return self.keystone_client.users.get(user_id).name except keystoneclient.exceptions.NotFound: return default def try_get_user_by_name(self, username, default=None): try: return self.keystone_client.users.find(username=username) except keystoneclient.exceptions.NotFound: LOG.warning("User '%s' has not been found, returning default " "value = '%s'", username, default) return self.keystone_client.users.find(username=default) def roles_for_user(self, user_id, tenant_id): """ Getting list of user roles for tenant """ return self.keystone_client.roles.roles_for_user(user_id, tenant_id) def create_role(self, role_name): """ Create new role in keystone. """ return self.keystone_client.roles.create(role_name) def create_tenant(self, tenant_name, description=None, enabled=True): """ Create new tenant in keystone. """ return self.keystone_client.tenants.create(tenant_name=tenant_name, description=description, enabled=enabled) def create_user(self, name, password=None, email=None, tenant_id=None, enabled=True): """ Create new user in keystone. """ return self.keystone_client.users.create(name=name, password=password, email=email, tenant_id=tenant_id, enabled=enabled) def update_tenant(self, tenant_id, tenant_name=None, description=None, enabled=None): """Update a tenant with a new name and description.""" return self.keystone_client.tenants.update(tenant_id, tenant_name=tenant_name, description=description, enabled=enabled) def update_user(self, user, **kwargs): """Update user data. Supported arguments include ``name``, ``email``, and ``enabled``. """ return self.keystone_client.users.update(user, **kwargs) def get_auth_token_from_user(self): return self.keystone_client.auth_token_from_user def _deploy_tenants(self, tenants): dst_tenants = {tenant.name: tenant.id for tenant in self.get_tenants_list()} for _tenant in tenants: tenant = _tenant['tenant'] if tenant['name'] not in dst_tenants: _tenant['meta']['new_id'] = self.create_tenant( tenant['name'], tenant['description']).id else: _tenant['meta']['new_id'] = dst_tenants[tenant['name']] def _deploy_users(self, users, tenants): dst_users = {user.name: user.id for user in self.get_users_list()} tenant_mapped_ids = {tenant['tenant']['id']: tenant['meta']['new_id'] for tenant in tenants} keep_passwd = self.config['migrate']['keep_user_passwords'] overwrite_passwd = self.config['migrate']['overwrite_user_passwords'] for _user in users: user = _user['user'] password = self._generate_password() if user['name'] in dst_users: # Create users mapping _user['meta']['new_id'] = dst_users[user['name']] if overwrite_passwd and not keep_passwd: self.update_user(_user['meta']['new_id'], password=password) self._passwd_notification(user['email'], user['name'], password) continue if not self.config.migrate.migrate_users: continue tenant_id = tenant_mapped_ids[user['tenantId']] _user['meta']['new_id'] = self.create_user(user['name'], password, user['email'], tenant_id).id if self.config['migrate']['keep_user_passwords']: _user['meta']['overwrite_password'] = True else: self._passwd_notification(user['email'], user['name'], password) @staticmethod def _update_users_info(users): """ Update users info. This method is needed for skip users, that have not been migrated to destination cloud and that do not exist there. So we leave information only about users with mapping and skip those, who don't have the same user on the destination cloud. This is done, because another tasks can use users mapping. :param users: OpenStack Keystone users info; :return: List with actual users info. """ users_info = [] for user in users: if user['meta'].get('new_id'): users_info.append(user) return users_info def _passwd_notification(self, email, name, password): if not self.postman: return template = 'templates/email.html' self._send_msg(email, 'New password notification', self._render_template(template, {'name': name, 'password': password})) def _deploy_roles(self, roles): dst_roles = {role.name: role.id for role in self.get_roles_list()} for _role in roles: role = _role['role'] if role['name'] not in dst_roles: _role['meta']['new_id'] = self.create_role(role['name']).id else: _role['meta']['new_id'] = dst_roles[role['name']] def _get_user_passwords(self): info = {} for user in self.get_users_list(): for password in self.mysql_connector.execute( "SELECT password FROM user WHERE id = :user_id", user_id=user.id): info[user.name] = password[0] return info def _get_user_tenants_roles(self): user_tenants_roles = {} tenants = self.get_tenants_list() for user in self.get_users_list(): user_tenants_roles[user.name] = {} for tenant in tenants: roles = [] for role in self.roles_for_user(user.id, tenant.id): roles.append({'role': {'name': role.name, 'id': role.id}}) user_tenants_roles[user.name][tenant.name] = roles return user_tenants_roles def _upload_user_passwords(self, users, user_passwords): for _user in users: user = _user['user'] if not _user['meta']['overwrite_password']: continue self.mysql_connector.execute( "UPDATE user SET password = :password WHERE id = :user_id", user_id=_user['meta']['new_id'], password=user_passwords[user['name']]) def _upload_user_tenant_roles(self, user_tenants_roles, users, tenants): roles_id = {role.name: role.id for role in self.get_roles_list()} dst_users = {user.name: user.id for user in self.get_users_list()} for _user in users: user = _user['user'] # FIXME should be deleted after determining how # to change self role without logout if user['name'] == self.keystone_client.username: continue if user['name'] not in dst_users: continue for _tenant in tenants: tenant = _tenant['tenant'] exists_roles = [role.name for role in self.roles_for_user(_user['meta']['new_id'], _tenant['meta']['new_id'])] for _role in user_tenants_roles[user['name']][tenant['name']]: role = _role['role'] if role['name'] in exists_roles: continue self.keystone_client.roles.add_user_role( _user['meta']['new_id'], roles_id[role['name']], _tenant['meta']['new_id']) def _generate_password(self): return self.generator.get_random_password() def _send_msg(self, to, subject, msg): if self.postman: with self.postman as p: p.send(to, subject, msg) def _render_template(self, name_file, args): if self.templater: return self.templater.render(name_file, args) else: return None def check_rabbitmq(self): credentials = pika.PlainCredentials(self.config.rabbit.user, self.config.rabbit.password) for host in self.config.rabbit.hosts.split(","): pika.BlockingConnection(pika.ConnectionParameters( host=host.strip(), credentials=credentials))
class KeystoneIdentity(identity.Identity): """The main class for working with OpenStack Keystone Identity Service.""" def __init__(self, config, cloud): super(KeystoneIdentity, self).__init__() self.config = config self.keystone_client = self.proxy(self.get_client(), config) self.mysql_connector = cloud.mysql_connector self.cloud = cloud self.postman = None if self.config.mail.server != "-": self.postman = Postman(self.config['mail']['username'], self.config['mail']['password'], self.config['mail']['from_addr'], self.config['mail']['server']) self.templater = Templater() self.generator = GeneratorPassword() @staticmethod def convert(identity_obj, cfg): """Convert OpenStack Keystone object to CloudFerry object. :param identity_obj: Direct OpenStack Keystone object to convert, supported objects: tenants, users and roles; :param cfg: Cloud config. """ if isinstance(identity_obj, keystone_client.tenants.Tenant): return {'tenant': {'name': identity_obj.name, 'id': identity_obj.id, 'description': identity_obj.description}, 'meta': {}} elif isinstance(identity_obj, keystone_client.users.User): overwirte_user_passwords = cfg.migrate.overwrite_user_passwords return {'user': {'name': identity_obj.name, 'id': identity_obj.id, 'email': identity_obj.email, 'tenantId': identity_obj.tenantId}, 'meta': { 'overwrite_password': overwirte_user_passwords}} elif isinstance(identity_obj, keystone_client.roles.Role): return {'role': {'name': identity_obj.name, 'id': identity_obj}, 'meta': {}} LOG.error('KeystoneIdentity converter has received incorrect value. ' 'Please pass to it only tenants, users or role objects.') return None def read_info(self, **kwargs): info = {'tenants': [], 'users': [], 'roles': []} for tenant in self.get_tenants_list(): tnt = self.convert(tenant, self.config) info['tenants'].append(tnt) for user in self.get_users_list(): usr = self.convert(user, self.config) info['users'].append(usr) for role in self.get_roles_list(): rl = self.convert(role, self.config) info['roles'].append(rl) info['user_tenants_roles'] = self._get_user_tenants_roles() if self.config['migrate']['keep_user_passwords']: info['user_passwords'] = self._get_user_passwords() return info def deploy(self, info): print 'Deploy started' tenants = info['tenants'] users = info['users'] roles = info['user_tenants_roles'] self._deploy_tenants(tenants) self._deploy_roles(info['roles']) self._deploy_users(users, tenants) if self.config['migrate']['keep_user_passwords']: passwords = info['user_passwords'] self._upload_user_passwords(users, passwords) self._upload_user_tenant_roles(roles, users, tenants) print 'Finished' def get_client(self): """ Getting keystone client """ ks_client_for_token = keystone_client.Client( username=self.config.cloud.user, password=self.config.cloud.password, tenant_name=self.config.cloud.tenant, auth_url="http://%s:35357/v2.0/" % self.config.cloud.host) return keystone_client.Client( token=ks_client_for_token.auth_ref['token']['id'], endpoint="http://%s:35357/v2.0/" % self.config.cloud.host) def get_service_name_by_type(self, service_type): """Getting service_name from keystone. """ for service in self.get_services_list(): if service.type == service_type: return service.name return NOVA_SERVICE def get_public_endpoint_service_by_id(self, service_id): """Getting endpoint public URL from keystone. """ for endpoint in self.keystone_client.endpoints.list(): if endpoint.service_id == service_id: return endpoint.publicurl def get_service_id(self, service_name): """Getting service_id from keystone. """ for service in self.get_services_list(): if service.name == service_name: return service.id def get_endpoint_by_service_name(self, service_name): """ Getting endpoint public URL by service name from keystone. """ service_id = self.get_service_id(service_name) return self.get_public_endpoint_service_by_id(service_id) def get_tenants_func(self): tenants = {tenant.id: tenant.name for tenant in self.get_tenants_list()} def func(tenant_id): return tenants.get(tenant_id, 'admin') return func def get_tenant_id_by_name(self, name): for tenant in self.get_tenants_list(): if tenant.name == name: return tenant.id return None def get_tenant_by_name(self, tenant_name): """ Getting tenant by name from keystone. """ for tenant in self.get_tenants_list(): if tenant.name == tenant_name: return tenant def get_tenant_by_id(self, tenant_id): """ Getting tenant by id from keystone. """ return self.keystone_client.tenants.get(tenant_id) def get_services_list(self): """ Getting list of available services from keystone. """ return self.keystone_client.services.list() def get_tenants_list(self): """ Getting list of tenants from keystone. """ return self.keystone_client.tenants.list() def get_users_list(self): """ Getting list of users from keystone. """ return self.keystone_client.users.list() def get_roles_list(self): """ Getting list of available roles from keystone. """ return self.keystone_client.roles.list() def roles_for_user(self, user_id, tenant_id): """ Getting list of user roles for tenant """ return self.keystone_client.roles.roles_for_user(user_id, tenant_id) def create_role(self, role_name): """ Create new role in keystone. """ return self.keystone_client.roles.create(role_name) def create_tenant(self, tenant_name, description=None, enabled=True): """ Create new tenant in keystone. """ return self.keystone_client.tenants.create(tenant_name=tenant_name, description=description, enabled=enabled) def create_user(self, name, password=None, email=None, tenant_id=None, enabled=True): """ Create new user in keystone. """ return self.keystone_client.users.create(name=name, password=password, email=email, tenant_id=tenant_id, enabled=enabled) def update_tenant(self, tenant_id, tenant_name=None, description=None, enabled=None): """Update a tenant with a new name and description.""" return self.keystone_client.tenants.update(tenant_id, tenant_name=tenant_name, description=description, enabled=enabled) def update_user(self, user, **kwargs): """Update user data. Supported arguments include ``name``, ``email``, and ``enabled``. """ return self.keystone_client.users.update(user, **kwargs) def get_auth_token_from_user(self): return self.keystone_client.auth_token_from_user def _deploy_tenants(self, tenants): dst_tenants = {tenant.name: tenant.id for tenant in self.get_tenants_list()} for _tenant in tenants: tenant = _tenant['tenant'] if tenant['name'] not in dst_tenants: _tenant['meta']['new_id'] = self.create_tenant( tenant['name'], tenant['description']).id else: _tenant['meta']['new_id'] = dst_tenants[tenant['name']] def _deploy_users(self, users, tenants): dst_users = {user.name: user.id for user in self.get_users_list()} tenant_mapped_ids = {tenant['tenant']['id']: tenant['meta']['new_id'] for tenant in tenants} keep_passwd = self.config['migrate']['keep_user_passwords'] overwrite_passwd = self.config['migrate']['overwrite_user_passwords'] for _user in users: user = _user['user'] password = self._generate_password() if user['name'] in dst_users: _user['meta']['new_id'] = dst_users[user['name']] if overwrite_passwd and not keep_passwd: self.update_user(_user['meta']['new_id'], password=password) self._passwd_notification(user['email'], user['name'], password) continue tenant_id = tenant_mapped_ids[user['tenantId']] _user['meta']['new_id'] = self.create_user(user['name'], password, user['email'], tenant_id).id if self.config['migrate']['keep_user_passwords']: _user['meta']['overwrite_password'] = True else: self._passwd_notification(user['email'], user['name'], password) def _passwd_notification(self, email, name, password): if not self.postman: return template = 'templates/email.html' self._send_msg(email, 'New password notification', self._render_template(template, {'name': name, 'password': password})) def _deploy_roles(self, roles): dst_roles = {role.name: role.id for role in self.get_roles_list()} for _role in roles: role = _role['role'] if role['name'] not in dst_roles: _role['meta']['new_id'] = self.create_role(role['name']).id else: _role['meta']['new_id'] = dst_roles[role['name']] def _get_user_passwords(self): info = {} for user in self.get_users_list(): for password in self.mysql_connector.execute( "SELECT password FROM user WHERE id = :user_id", user_id=user.id): info[user.name] = password[0] return info def _get_user_tenants_roles(self): user_tenants_roles = {} tenants = self.get_tenants_list() for user in self.get_users_list(): user_tenants_roles[user.name] = {} for tenant in tenants: roles = [] for role in self.roles_for_user(user.id, tenant.id): roles.append({'role': {'name': role.name, 'id': role.id}}) user_tenants_roles[user.name][tenant.name] = roles return user_tenants_roles def _upload_user_passwords(self, users, user_passwords): for _user in users: user = _user['user'] if not _user['meta']['overwrite_password']: continue self.mysql_connector.execute( "UPDATE user SET password = :password WHERE id = :user_id", user_id=_user['meta']['new_id'], password=user_passwords[user['name']]) def _upload_user_tenant_roles(self, user_tenants_roles, users, tenants): roles_id = {role.name: role.id for role in self.get_roles_list()} for _user in users: user = _user['user'] # FIXME should be deleted after determining how # to change self role without logout if user['name'] == self.keystone_client.username: continue for _tenant in tenants: tenant = _tenant['tenant'] exists_roles = [role.name for role in self.roles_for_user(_user['meta']['new_id'], _tenant['meta']['new_id'])] for _role in user_tenants_roles[user['name']][tenant['name']]: role = _role['role'] if role['name'] in exists_roles: continue self.keystone_client.roles.add_user_role( _user['meta']['new_id'], roles_id[role['name']], _tenant['meta']['new_id']) def _generate_password(self): return self.generator.get_random_password() def _send_msg(self, to, subject, msg): if self.postman: with self.postman as p: p.send(to, subject, msg) def _render_template(self, name_file, args): if self.templater: return self.templater.render(name_file, args) else: return None