From ed887d10b8f139b4a23e9f259957aad357464715 Mon Sep 17 00:00:00 2001 From: Chi Lo Date: Tue, 16 Apr 2019 09:20:30 -0700 Subject: [PATCH] Assign and Unassign group roles This patch provides support for assigning and unassigning roles to group on domain or project. Change-Id: If7b8c54720f3bcab2da71bbd8ca088ac572e382a --- orm/orm_client/ormcli/cmscli.py | 85 ++++- .../controllers/v1/orm/group/roles.py | 111 ++++++ .../cms_rest/controllers/v1/orm/group/root.py | 3 + .../cms_rest/data/data_manager.py | 87 ++++- .../cms_rest/data/sql_alchemy/group_record.py | 6 +- .../groups_customer_role_record.py | 130 +++++++ .../sql_alchemy/groups_domain_role_record.py | 128 +++++++ .../data/sql_alchemy/groups_region_record.py | 6 +- .../data/sql_alchemy/groups_role_record.py | 100 +++++ .../cms_rest/data/sql_alchemy/models.py | 149 +++++++- .../customer_manager/cms_rest/etc/policy.json | 4 +- .../cms_rest/logic/group_logic.py | 207 +++++++++-- .../cms_rest/model/GroupModels.py | 53 ++- .../db_scripts/ranger_cms_create_db.sql | 29 +- .../rds/services/helpers.py | 2 +- .../rds/services/yaml_group_builder.py | 106 +++--- orm/tests/unit/cms/test_group_logic.py | 179 ++++++++- orm/tests/unit/cms/test_groups_role.py | 168 +++++++++ orm/tests/unit/ormcli/test_cmscli.py | 24 +- .../unit/rds/services/test_group_yaml.py | 4 +- .../ranger_tempest_plugin/config.py | 5 +- .../schemas/group_schema.py | 39 +- .../services/grp_client.py | 128 ++++--- .../tests/api/grp_base.py | 351 +++++++++--------- .../tests/api/test_groups.py | 303 +++++++++------ .../tempest_setup/tempest.conf | 2 + 26 files changed, 1915 insertions(+), 494 deletions(-) create mode 100644 orm/services/customer_manager/cms_rest/controllers/v1/orm/group/roles.py create mode 100644 orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_customer_role_record.py create mode 100644 orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_domain_role_record.py create mode 100644 orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_role_record.py create mode 100644 orm/tests/unit/cms/test_groups_role.py diff --git a/orm/orm_client/ormcli/cmscli.py b/orm/orm_client/ormcli/cmscli.py index c9958464..42b6487f 100644 --- a/orm/orm_client/ormcli/cmscli.py +++ b/orm/orm_client/ormcli/cmscli.py @@ -271,7 +271,7 @@ def add_to_parser(service_sub): type=str, help='') # group - parser_create_group = subparsers.add_parser('creste_group', + parser_create_group = subparsers.add_parser('create_group', help='[<"X-RANGER-Client" ' 'header>] ' + parser_get_group = subparsers.add_parser('get_group', + help='%s %s' % (h1, h2)) + parser_get_group.add_argument('client', **cli_common.ORM_CLIENT_KWARGS) + parser_get_group.add_argument('groupid', type=str, help=h2) + + # list groups + h1 = '[<"X-RANGER-Client" header>]' + h2 = '[--region ] [--starts_with ] [--contains ]' + parser_list_groups = subparsers.add_parser('list_groups', + help='%s %s' % (h1, h2)) + parser_list_groups.add_argument('client', **cli_common.ORM_CLIENT_KWARGS) + parser_list_groups.add_argument('--region', type=str, help='region name') + parser_list_groups.add_argument('--starts_with', type=str, + help='group name') + parser_list_groups.add_argument('--contains', type=str, + help='* contains in group name') + # groups region parser_add_groups_region = subparsers.add_parser( 'add_groups_region', @@ -318,24 +337,35 @@ def add_to_parser(service_sub): help='force delete groups region', action="store_true") - # get group - h1, h2 = '[<"X-RANGER-Client" header>]', '' - parser_get_group = subparsers.add_parser('get_group', - help='%s %s' % (h1, h2)) - parser_get_group.add_argument('client', **cli_common.ORM_CLIENT_KWARGS) - parser_get_group.add_argument('groupid', type=str, help=h2) + # groups roles + parser_assign_group_roles = subparsers.add_parser( + 'assign_group_roles', + help='[<"X-RANGER-Client" ' + 'header>] ' + '') + parser_assign_group_roles.add_argument( + 'client', **cli_common.ORM_CLIENT_KWARGS) + parser_assign_group_roles.add_argument( + 'groupid', type=str, help='') + parser_assign_group_roles.add_argument( + 'datafile', type=argparse.FileType('r'), + help='') - # list groups - h1 = '[<"X-RANGER-Client" header>]' - h2 = '[--region ] [--starts_with ] [--contains ]' - parser_list_groups = subparsers.add_parser('list_groups', - help='%s %s' % (h1, h2)) - parser_list_groups.add_argument('client', **cli_common.ORM_CLIENT_KWARGS) - parser_list_groups.add_argument('--region', type=str, help='region name') - parser_list_groups.add_argument('--starts_with', type=str, - help='group name') - parser_list_groups.add_argument('--contains', type=str, - help='* contains in group name') + parser_unassign_group_roles = subparsers.add_parser( + 'unassign_group_roles', + help='[<"X-RANGER-Client" ' + 'header>] < --customer ' + 'or <--domain >') + parser_unassign_group_roles.add_argument( + 'client', **cli_common.ORM_CLIENT_KWARGS) + parser_unassign_group_roles.add_argument( + 'groupid', type=str, help='') + parser_unassign_group_roles.add_argument( + 'role', type=str, help='') + parser_unassign_group_roles.add_argument( + '--customer', type=str, help='customer id') + parser_unassign_group_roles.add_argument( + '--domain', type=str, help='domain name') return parser @@ -423,6 +453,25 @@ def cmd_details(args): if args.contains: param += '%scontains=%s' % (preparm(param), args.contains) return requests.get, 'groups/%s' % param + elif args.subcmd == 'assign_group_roles': + return requests.post, 'groups/%s/roles' % args.groupid + elif args.subcmd == 'unassign_group_roles': + if args.customer and args.domain: + print("--customer and --domain cannot be specified " + "at the same time") + exit(1) + assignment_type = assignment_value = "" + if args.customer: + assignment_type = "customer" + assignment_value = args.customer + elif args.domain: + assignment_type = "domain" + assignment_value = args.domain + return requests.delete, 'groups/%s/roles/%s/%s/%s' % ( + args.groupid, + args.role, + assignment_type, + assignment_value) def get_token(timeout, args, host): diff --git a/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/roles.py b/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/roles.py new file mode 100644 index 00000000..595d2d31 --- /dev/null +++ b/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/roles.py @@ -0,0 +1,111 @@ +from oslo_db.exception import DBDuplicateEntry +from pecan import request, rest +from wsmeext.pecan import wsexpose + +from orm.common.orm_common.utils import api_error_utils as err_utils +from orm.common.orm_common.utils import utils +from orm.services.customer_manager.cms_rest.logger import get_logger +from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus +from orm.services.customer_manager.cms_rest.logic.group_logic import GroupLogic +from orm.services.customer_manager.cms_rest.model.GroupModels import \ + RoleAssignment, RoleResultWrapper +from orm.services.customer_manager.cms_rest.utils import authentication + +LOG = get_logger(__name__) + + +class RoleController(rest.RestController): + + @wsexpose([str], str, rest_content_types='json') + def get(self, group_id): + return ["This is groups role controller ", "group id: " + group_id] + + @wsexpose(RoleResultWrapper, str, body=[RoleAssignment], + rest_content_types='json', status_code=200) + def post(self, group_id, role_assignments): + LOG.info("RoleController - Assign Roles to group id {0} " + "roles: {1}".format(group_id, str(role_assignments))) + authentication.authorize(request, 'groups:assign_region') + try: + group_logic = GroupLogic() + result = group_logic.assign_roles(group_id, + role_assignments, + request.transaction_id) + LOG.info("RoleController - Roles assigned: " + str(result)) + + event_details = 'Group {} - roles assigned.'.format(group_id) + utils.audit_trail('assigned group roles', + request.transaction_id, + request.headers, + group_id, + event_details=event_details) + + except DBDuplicateEntry as exception: + LOG.log_exception( + "DBDuplicateEntry - Group Roles already assigned.", exception) + print exception.message + raise err_utils.get_error( + request.transaction_id, + status_code=409, + message='Duplicate Entry - Group Roles already assigned.', + error_details=exception.message) + + except ErrorStatus as exception: + LOG.log_exception( + "ErrorStatus - Failed to assign roles", exception) + raise err_utils.get_error(request.transaction_id, + message=exception.message, + status_code=exception.status_code) + except Exception as exception: + LOG.log_exception( + "Exception - Failed in assign roles", exception) + raise err_utils.get_error(request.transaction_id, + status_code=500, + error_details=str(exception)) + + return result + + @wsexpose(None, str, str, str, str, status_code=204) + def delete(self, group_id, role_name, assignment_type, assignment_value): + + requester = request.headers.get('X-RANGER-Requester') + is_rds_client_request = requester == 'rds_resource_service_proxy' + LOG.info("Unassign Roles from group id: {0} role_name: {1} " + "assignment_type: {2} assignment_value: {3}. ".format( + group_id, role_name, assignment_type, + assignment_value)) + + authentication.authorize(request, 'groups:unassign_region') + try: + group_logic = GroupLogic() + group_logic.unassign_roles(group_id, + role_name, + assignment_type, + assignment_value, + request.transaction_id) + + LOG.info("RoleController - Unassign Roles finished") + + event_details = 'Group {} roles unassigned'.format(group_id) + utils.audit_trail('unassign group roles', + request.transaction_id, + request.headers, + group_id, + event_details=event_details) + + except ValueError as exception: + raise err_utils.get_error(request.transaction_id, + message=exception.message, + status_code=404) + except ErrorStatus as exception: + LOG.log_exception("ErrorStatus - Failed to unassign roles", + exception) + raise err_utils.get_error(request.transaction_id, + message=exception.message, + status_code=exception.status_code) + except Exception as exception: + LOG.log_exception("Exception - Failed in unassign roles", + exception) + raise err_utils.get_error(request.transaction_id, + status_code=500, + error_details=str(exception)) diff --git a/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/root.py b/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/root.py index 300cf2eb..f36198f3 100755 --- a/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/root.py +++ b/orm/services/customer_manager/cms_rest/controllers/v1/orm/group/root.py @@ -8,6 +8,8 @@ from orm.services.customer_manager.cms_rest.controllers.v1.orm.customer.users \ import DefaultUserController from orm.services.customer_manager.cms_rest.controllers.v1.orm.group.regions \ import RegionController +from orm.services.customer_manager.cms_rest.controllers.v1.orm.group.roles \ + import RoleController from orm.services.customer_manager.cms_rest.logger import get_logger from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus from orm.services.customer_manager.cms_rest.logic.group_logic import GroupLogic @@ -19,6 +21,7 @@ LOG = get_logger(__name__) class GroupController(rest.RestController): + roles = RoleController() regions = RegionController() users = DefaultUserController() diff --git a/orm/services/customer_manager/cms_rest/data/data_manager.py b/orm/services/customer_manager/cms_rest/data/data_manager.py index 9ef0eba8..2d2d8f0f 100755 --- a/orm/services/customer_manager/cms_rest/data/data_manager.py +++ b/orm/services/customer_manager/cms_rest/data/data_manager.py @@ -6,12 +6,18 @@ from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ customer_region_record import CustomerRegionRecord from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ group_record import GroupRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ + groups_customer_role_record import GroupsCustomerRoleRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ + groups_domain_role_record import GroupsDomainRoleRecord from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ groups_region_record import GroupsRegionRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.\ + groups_role_record import GroupsRoleRecord from orm.services.customer_manager.cms_rest.data.sql_alchemy.models \ - import (CmsRole, CmsUser, Customer, - Groups, GroupRegion, - CustomerRegion, Quota, + import (CmsRole, CmsUser, Customer, CustomerRegion, + Groups, GroupsCustomerRole, GroupsDomainRole, GroupRegion, + GroupsRole, Quota, QuotaFieldDetail, Region, UserRole) from orm.services.customer_manager.cms_rest.data.sql_alchemy.user_role_record \ @@ -135,27 +141,48 @@ class DataManager(object): self.customer_record = CustomerRecord(self.session) return self.customer_record - if record_name == "Group" or record_name == "group": + elif record_name == "Group" or record_name == "group": if not hasattr(self, "group_record"): self.group_record = GroupRecord(self.session) return self.group_record - if record_name == "CustomerRegion" or record_name == "customer_region": + elif (record_name == "CustomerRegion" or + record_name == "customer_region"): if not hasattr(self, "customer_region_record"): self.customer_region_record = CustomerRegionRecord( self.session) return self.customer_region_record - if record_name == "GroupRegion" or record_name == "group_region": + elif record_name == "GroupsRegion" or record_name == "groups_region": if not hasattr(self, "groups_region_record"): self.groups_region_record = GroupsRegionRecord( self.session) return self.groups_region_record - if record_name == "UserRole" or record_name == "user_role": + elif record_name == "UserRole" or record_name == "user_role": if not hasattr(self, "user_role_record"): self.user_role_record = UserRoleRecord(self.session) return self.user_role_record + + elif record_name == "GroupsRole" or record_name == "groups_role": + if not hasattr(self, "groups_role_record"): + self.groups_role_record = GroupsRoleRecord(self.session) + return self.groups_role_record + + elif (record_name == "GroupsCustomerRole" or + record_name == "groups_customer_role"): + if not hasattr(self, "groups_customer_role_record"): + self.groups_customer_role_record = GroupsCustomerRoleRecord( + self.session) + return self.groups_customer_role_record + + elif (record_name == "GroupsDomainRole" or + record_name == "groups_domain_role"): + if not hasattr(self, "groups_domain_role_record"): + self.groups_domain_role_record = GroupsDomainRoleRecord( + self.session) + return self.groups_domain_role_record + return None def add_user(self, user): @@ -227,7 +254,7 @@ class DataManager(object): sql_group = Groups( uuid=uuid, name=group.name, - domain_name='default', + domain_name=group.domain_name, enabled=group.enabled, description=group.description ) @@ -318,6 +345,50 @@ class DataManager(object): return customer_id + def get_role_id_by_name(self, name): + role_id = self.session.query(CmsRole.id).filter( + CmsRole.name == name).scalar() + + return role_id + + def check_groups_role_exist(self, role_id, group_id): + count = self.session.query(GroupsRole).filter( + GroupsRole.role_id == role_id, + GroupsRole.group_id == group_id).count() + return count >= 1 + + def add_groups_role(self, role_id, group_id): + # if there is no existing record, proceed to add it + if not self.check_groups_role_exist(role_id, group_id): + groups_role = GroupsRole(role_id=role_id, + group_id=group_id) + self.session.add(groups_role) + self.flush() + + def add_groups_role_on_domain(self, + group_id, role_id, region_id, domain): + self.add_groups_role(role_id, group_id) + + groups_domain_role = GroupsDomainRole(role_id=role_id, + group_id=group_id, + domain_name=domain, + region_id=region_id) + self.session.add(groups_domain_role) + self.flush() + return groups_domain_role + + def add_groups_role_on_project(self, + group_id, role_id, region_id, customer_id): + self.add_groups_role(role_id, group_id) + + groups_customer_role = GroupsCustomerRole(role_id=role_id, + group_id=group_id, + customer_id=customer_id, + region_id=region_id) + self.session.add(groups_customer_role) + self.flush() + return groups_customer_role + @classmethod def get_dict_from_quota(cls, quota, quota_type): types = { diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/group_record.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/group_record.py index a2ede37f..1748f7f5 100755 --- a/orm/services/customer_manager/cms_rest/data/sql_alchemy/group_record.py +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/group_record.py @@ -41,7 +41,7 @@ class GroupRecord: def delete_by_primary_key(self, group_id): cmd = 'DELETE FROM groups WHERE id = %s' - result = self.session.connection().execute(cmd, (group_id)) + result = self.session.connection().execute(cmd, (group_id,)) return result def read_by_primary_key(self): @@ -70,7 +70,7 @@ class GroupRecord: def get_group_id_from_uuid(self, group_uuid): cmd = "SELECT id from groups WHERE uuid = %s" - result = self.session.connection().scalar(cmd, (group_uuid)) + result = self.session.connection().scalar(cmd, (group_uuid,)) if result: return int(result) @@ -80,7 +80,7 @@ class GroupRecord: def get_groups_status_by_uuids(self, uuid_str): cmd = "SELECT id, resource_id, region, status FROM " \ "rds_resource_status_view WHERE resource_id IN (%s)" - results = self.session.connection().execute(cmd, (uuid_str)) + results = self.session.connection().execute(cmd % uuid_str) group_region = {} if results: diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_customer_role_record.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_customer_role_record.py new file mode 100644 index 00000000..d2164b3f --- /dev/null +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_customer_role_record.py @@ -0,0 +1,130 @@ +from orm.services.customer_manager.cms_rest.data.sql_alchemy.group_record \ + import GroupRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.models \ + import GroupsCustomerRole +from orm.services.customer_manager.cms_rest.data.sql_alchemy.region_record \ + import RegionRecord +from orm.services.customer_manager.cms_rest.logger import get_logger + +LOG = get_logger(__name__) + + +class GroupsCustomerRoleRecord: + def __init__(self, session): + + # thie model uses for the parameters for any acceess methods - not + # as instance of record in the table + self.__groups_customer_role = GroupsCustomerRole() + self.__TableName = "groups_customer_role" + + if (session): + self.session = session + + def setDBSession(self, session): + self.session = session + + @property + def groups_customer_role(self): + return self.__groups_customer_role + + @groups_customer_role.setter + def groups_customer_role(self): + self.__groups_customer_role = GroupsCustomerRole() + + def insert(self, groups_customer_role): + try: + self.session.add(groups_customer_role) + except Exception as exception: + LOG.log_exception( + "Failed to insert groups_customer_role" + + str(groups_customer_role), exception) + raise + + def get_group_customer_role_by_keys(self, + group_uuid, + role_id, + region_name, + customer_uuid): + region_record = RegionRecord(self.session) + region_id = region_record.get_region_id_from_name(region_name) + if region_id is None: + raise ValueError( + 'region with the region name {0} not found'.format( + region_name)) + try: + group = self.session.query(GroupsCustomerRole).filter( + and_, ( + GroupsCustomerRole.group_id == group_uuid, + GroupsCustomerRole.customer_id == customer_uuid, + GroupsCustomerRole.region_id == region_id, + GroupsCustomerRole.role_id == role_id)) + return group.first() + + except Exception as exception: + message = "Failed to get group/project by keys: " \ + " group_uuid:%s customer_uuid:%s region_name:%s " \ + " role_id:%s " \ + % group_uuid, customer_uuid, region_id, role_id + LOG.log_exception(message, exception) + raise + + def get_customer_roles_for_group(self, group_uuid): + groups_customer_roles = [] + + try: + query = self.session.query(GroupsCustomerRole).filter( + GroupsCustomerRole.group_id == group_uuid) + + for groups_customer_role in query.all(): + groups_customer_roles.append(groups_customer_role) + return groups_customer_roles + + except Exception as exception: + message = "Failed to get projects for group: %s" % (group_uuid) + LOG.log_exception(message, exception) + raise + + def check_groups_customer_role_exist(self, role_id, group_id): + count = self.session.query(GroupsCustomerRole).filter( + GroupsCustomerRole.role_id == role_id, + GroupsCustomerRole.group_id == group_id).count() + return count >= 1 + + def remove_customer_role_from_group(self, + group_uuid, + region_id, + customer_id, + role_id): + + cmd = 'DELETE FROM groups_customer_role WHERE group_id = %s and \ + region_id = %s and customer_id = %s and role_id = %s' + result = self.session.connection().execute(cmd, + (group_uuid, + region_id, + customer_id, + role_id)) + + self.session.flush() + + if result.rowcount == 0: + LOG.warn('customer with the customer id {0} not found'.format( + customer_id)) + raise ValueError( + 'customer with the customer id {0} not found'.format( + customer_id)) + + LOG.debug("num records deleted: " + str(result.rowcount)) + return result + + def delete_all_customers_from_group(self, group_id): + # group_id can be a uuid (type of string) or id (type of int). + # If group_id is uuid, then get id from uuid and use the id in the + # next sql command + if isinstance(group_id, basestring): + group_record = GroupRecord(self.session) + group_id = group_record.get_group_id_from_uuid(group_id) + + # not including default region which is -1 + cmd = 'DELETE FROM groups_customer_role WHERE group_id = %s' + result = self.session.connection().execute(cmd, (group_id,)) + return result diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_domain_role_record.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_domain_role_record.py new file mode 100644 index 00000000..cadc0a93 --- /dev/null +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_domain_role_record.py @@ -0,0 +1,128 @@ +from orm.services.customer_manager.cms_rest.data.sql_alchemy.group_record \ + import GroupRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.models \ + import GroupsDomainRole +from orm.services.customer_manager.cms_rest.data.sql_alchemy.region_record \ + import RegionRecord +from orm.services.customer_manager.cms_rest.logger import get_logger + +LOG = get_logger(__name__) + + +class GroupsDomainRoleRecord: + def __init__(self, session): + + # thie model uses for the parameters for any acceess methods - not + # as instance of record in the table + self.__groups_domain_role = GroupsDomainRole() + self.__TableName = "groups_domain_role" + + if (session): + self.session = session + + def setDBSession(self, session): + self.session = session + + @property + def groups_domain_role(self): + return self.__groups_domain_role + + @groups_domain_role.setter + def groups_domain_role(self): + self.__groups_domain_role = GroupsDomainRole() + + def insert(self, groups_domain_role): + try: + self.session.add(groups_domain_role) + except Exception as exception: + LOG.log_exception( + "Failed to insert groups_domain_role" + + str(groups_domain_role), exception) + raise + + def get_group_domain_role_by_keys(self, + group_uuid, + region_name, + domain, + role_id): + region_record = RegionRecord(self.session) + region_id = region_record.get_region_id_from_name(region_name) + if region_id is None: + raise ValueError( + 'region with the region name {0} not found'.format( + region_name)) + try: + group = self.session.query(GroupsDomainRole).filter( + and_( + GroupsDomainRole.group_id == group_uuid, + GroupsDomainRole.domain_name == domain, + GroupsDomainRole.region_id == region_id, + GroupsDomainRole.role_id == role_id)) + return group.first() + + except Exception as exception: + message = "Failed to get group/domain by primary keys: " \ + " group_uuid:%s domain:%s region_name:%s role_id: %s" \ + % group_uuid, domain, region_id, role_id + LOG.log_exception(message, exception) + raise + + def get_domain_roles_for_group(self, group_uuid): + groups_domain_roles = [] + + try: + query = self.session.query(GroupsDomainRole).filter( + GroupsDomainRole.group_id == group_uuid) + + for groups_domain_role in query.all(): + groups_domain_roles.append(groups_domain_role) + return groups_domain_roles + + except Exception as exception: + message = "Failed to get domains for group: %s" % (group_uuid) + LOG.log_exception(message, exception) + raise + + def check_groups_domain_role_exist(self, role_id, group_id): + count = self.session.query(GroupsDomainRole).filter( + GroupsDomainRole.role_id == role_id, + GroupsDomainRole.group_id == group_id).count() + return count >= 1 + + def remove_domain_role_from_group(self, + group_uuid, + region_id, + domain, + role_id): + cmd = 'DELETE FROM groups_domain_role WHERE group_id = %s and \ + region_id = %s and domain_name = %s and role_id = %s' + result = self.session.connection().execute(cmd, + (group_uuid, + region_id, + domain, + role_id)) + + self.session.flush() + + if result.rowcount == 0: + LOG.warn('domain with domain name {0} not found'.format( + domain)) + raise ValueError( + 'domain with domain name {0} not found'.format( + domain)) + + LOG.debug("num records deleted: " + str(result.rowcount)) + return result + + def delete_all_customers_from_group(self, group_id): + # group_id can be a uuid (type of string) or id (type of int). + # If group_id is uuid, then get id from uuid and use the id in the + # next sql command + if isinstance(group_id, basestring): + group_record = GroupRecord(self.session) + group_id = group_record.get_group_id_from_uuid(group_id) + + # not including default region which is -1 + cmd = 'DELETE FROM groups_domain_role WHERE group_id = %s' + result = self.session.connection().execute(cmd, (group_id,)) + return result diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_region_record.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_region_record.py index 4cfed89a..39938fb2 100755 --- a/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_region_record.py +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_region_record.py @@ -44,16 +44,16 @@ class GroupsRegionRecord: try: group_record = GroupRecord(self.session) - group_id = group_record.get_group_id_from_uuid(group_uuid) query = self.session.query(GroupRegion).filter( - GroupRegion.group_id == group_id) + GroupRegion.group_id == group_uuid, + GroupRegion.region_id != -1) for group_region in query.all(): group_regions.append(group_region) return group_regions except Exception as exception: - message = "Failed to get_region_names_for_group: %d" % (group_id) + message = "Failed to get_region_id_for_group: %d" % (group_uuid) LOG.log_exception(message, exception) raise diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_role_record.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_role_record.py new file mode 100644 index 00000000..60cd8dcc --- /dev/null +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/groups_role_record.py @@ -0,0 +1,100 @@ +from orm.services.customer_manager.cms_rest.data.sql_alchemy.group_record \ + import GroupRecord +from orm.services.customer_manager.cms_rest.data.sql_alchemy.models \ + import GroupsRole +from orm.services.customer_manager.cms_rest.logger import get_logger + +LOG = get_logger(__name__) + + +class GroupsRoleRecord: + def __init__(self, session): + + # thie model uses for the parameters for any acceess methods - not + # as instance of record in the table + self.__groups_role = GroupsRole() + self.__TableName = "groups_role" + + if (session): + self.session = session + + def setDBSession(self, session): + self.session = session + + @property + def groups_role(self): + return self.__groups_role + + @groups_role.setter + def groups_role(self): + self.__groups_role = GroupsRole() + + def insert(self, groups_role): + try: + self.session.add(groups_role) + except Exception as exception: + LOG.log_exception( + "Failed to insert groups_role" + str(groups_role), exception) + raise + + def get_role_for_group_by_primary_key(self, group_uuid, role_id): + try: + group = self.session.query(GroupsRole).filter( + and_( + GroupsRole.group_id == group_uuid, + GroupsRole.role_id == role_id)) + return group.first() + + except Exception as exception: + message = "Failed to get role by primary keys: group_uuid:%s " \ + "role_id:%s " % group_uuid, role_id + LOG.log_exception(message, exception) + raise + + def get_roles_for_group(self, group_uuid): + groups_roles = [] + + try: + query = self.session.query(GroupsRole).filter( + GroupsRole.group_id == group_uuid) + + for groups_role in query.all(): + groups_roles.append(groups_role) + return groups_roles + + except Exception as exception: + message = "Failed to get roles for group: %s" % (group_uuid) + LOG.log_exception(message, exception) + raise + + def remove_role_from_group(self, group_uuid, role_id): + cmd = 'DELETE FROM groups_role WHERE group_id = %s and role_id = %s' + result = self.session.connection().execute(cmd, + (group_uuid, + role_id)) + + self.session.flush() + + if result.rowcount == 0: + LOG.warn('role with the role id {0} not found'.format( + role_id)) + raise ValueError( + 'role with the role id {0} not found'.format( + role_id)) + + LOG.debug("num records deleted: " + str(result.rowcount)) + return result + + def delete_all_roles_for_group(self, group_id): + # group_id can be a uuid (type of string) or id (type of int). + # If group_id is uuid, then get id from uuid and use the id in the + # next sql command + if isinstance(group_id, basestring): + group_record = GroupRecord(self.session) + group_id = group_record.get_group_id_from_uuid(group_id) + + # not including default region which is -1 + cmd = 'DELETE FROM groups_role WHERE group_id = %s and \ + region_id <> -1' + result = self.session.connection().execute(cmd, (group_id,)) + return result diff --git a/orm/services/customer_manager/cms_rest/data/sql_alchemy/models.py b/orm/services/customer_manager/cms_rest/data/sql_alchemy/models.py index 25f51483..b1c44039 100755 --- a/orm/services/customer_manager/cms_rest/data/sql_alchemy/models.py +++ b/orm/services/customer_manager/cms_rest/data/sql_alchemy/models.py @@ -48,6 +48,9 @@ class Groups(Base, CMSBaseModel): description = Column(String(255), nullable=True) enabled = Column(SmallInteger, nullable=False) group_regions = relationship("GroupRegion", cascade="all, delete, delete-orphan") + groups_roles = relationship("GroupsRole", cascade="all, delete, delete-orphan") + groups_customer_roles = relationship("GroupsCustomerRole", cascade="all, delete, delete-orphan") + groups_domain_roles = relationship("GroupsDomainRole", cascade="all, delete, delete-orphan") def __json__(self): return dict( @@ -74,6 +77,16 @@ class Groups(Base, CMSBaseModel): group_regions = self.get_group_regions() proxy_dict["regions"] = [group_region.get_proxy_dict() for group_region in group_regions] + proxy_dict["groups_roles"] = [group_role.get_proxy_dict() for group_role in self.groups_roles] + + proxy_dict["groups_customer_roles"] = [ + group_customer_role.get_proxy_dict() + for group_customer_role in self.groups_customer_roles] + + proxy_dict["groups_domain_roles"] = [ + group_domain_role.get_proxy_dict() + for group_domain_role in self.groups_domain_roles] + return proxy_dict def get_group_regions(self): @@ -120,13 +133,13 @@ class GroupRegion(Base, CMSBaseModel): ) def get_proxy_dict(self): - proxy_dict = { + return { "name": self.region.name, + "group_id": self.group_id, + "region_id": self.region_id, "action": "modify" } - return proxy_dict - def to_wsme(self): name = self.region.name type = self.region.type @@ -134,9 +147,135 @@ class GroupRegion(Base, CMSBaseModel): type=type) return region + ''' -' CmsUser is a DataObject and contains all the fields defined in CmsUser table record. -' defined as SqlAlchemy model map to a table +' GroupRole is a DataObject and contains all the fields defined in GroupRole +' table record, defined as SqlAlchemy model map to a table +''' + + +class GroupsRole(Base, CMSBaseModel): + __tablename__ = 'groups_role' + + role_id = Column(Integer, ForeignKey('cms_role.id'), + primary_key=True, nullable=False) + + group_id = Column(String(64), ForeignKey('groups.uuid'), + primary_key=True, nullable=False, index=True) + + role = relationship("CmsRole", viewonly=True) + + def __json__(self): + return dict( + role_id=self.role_id, + group_id=self.group_id + ) + + def get_proxy_dict(self): + return { + "role_name": self.role.name, + "role_id": self.role_id, + "group_id": self.group_id + } + + def to_wsme(self): + role = GroupWsmeModels.Role(name=self.role.name) + return role + + +''' +' GroupsCustomerRole is a DataObject and contains all the fields defined in +' GroupsCustomerRole table record, defined as SqlAlchemy model map to a table +''' + + +class GroupsCustomerRole(Base, CMSBaseModel): + __tablename__ = 'groups_customer_role' + + group_id = Column(String(64), ForeignKey('groups.uuid'), + primary_key=True, nullable=False) + + region_id = Column(Integer, ForeignKey('cms_region.id')) + + customer_id = Column(Integer, ForeignKey('customer.id'), + primary_key=True, nullable=False, index=True) + + role_id = Column(Integer, ForeignKey('groups_role.role_id'), + primary_key=True, nullable=False, index=True) + + groups = relationship("Groups", viewonly=True) + customer = relationship("Customer", viewonly=True) + groups_role = relationship("GroupsRole", viewonly=True) + + def __json__(self): + return dict( + group_id=self.group_id, + region_id=self.region_id, + customer_id=self.customer_id, + role_id=self.role_id + ) + + def get_proxy_dict(self): + return { + "group_id": self.group_id, + "region_id": self.region_id, + "customer_id": self.customer_id, + "customer_uuid": self.customer.uuid, + "role_id": self.role_id, + "role_name": self.groups_role.role.name + } + + def to_wsme(self): + customer = GroupWsmeModels.Customer(customer_uuid=self.customer_id, + group=self.group.name, + role_id=self.role_id) + return customer + + +''' +' GroupsDomainRole is a DataObject and contains all the fields defined in +' GroupsDomainRole table record, defined as SqlAlchemy model map to a table +''' + + +class GroupsDomainRole(Base, CMSBaseModel): + __tablename__ = 'groups_domain_role' + + group_id = Column(String(64), ForeignKey('groups.uuid'), + primary_key=True, nullable=False) + + region_id = Column(Integer, ForeignKey('cms_region.id')) + + domain_name = Column(String(64), ForeignKey('cms_domain.name'), + primary_key=True, nullable=False) + + role_id = Column(Integer, ForeignKey('groups_role.role_id'), + primary_key=True, nullable=False, index=True) + + groups = relationship("Groups", viewonly=True) + groups_role = relationship("GroupsRole", viewonly=True) + + def __json__(self): + return dict( + group_id=self.group_id, + region_id=self.region_id, + domain_name=self.domain_name, + role_id=self.role_id + ) + + def get_proxy_dict(self): + return { + "group_id": self.group_id, + "region_id": self.region_id, + "domain_name": self.domain_name, + "role_id": self.role_id, + "role_name": self.groups_role.role.name + } + + +''' +' CmsRole is a DataObject and contains all the fields defined in CmsRole +' table record, defined as SqlAlchemy model map to a table ''' diff --git a/orm/services/customer_manager/cms_rest/etc/policy.json b/orm/services/customer_manager/cms_rest/etc/policy.json index 6405b865..8a53688f 100755 --- a/orm/services/customer_manager/cms_rest/etc/policy.json +++ b/orm/services/customer_manager/cms_rest/etc/policy.json @@ -43,5 +43,7 @@ "groups:update": "rule:admin_or_creator", "groups:delete": "rule:admin", "groups:add_region": "rule:admin_or_support_or_creator", - "groups:delete_region": "rule:admin_or_creator" + "groups:delete_region": "rule:admin_or_creator", + "groups:assign_role": "rule:admin_or_support_or_creator", + "groups:unassign_role": "rule:admin_or_creator" } diff --git a/orm/services/customer_manager/cms_rest/logic/group_logic.py b/orm/services/customer_manager/cms_rest/logic/group_logic.py index 10ac6c1b..244708b2 100755 --- a/orm/services/customer_manager/cms_rest/logic/group_logic.py +++ b/orm/services/customer_manager/cms_rest/logic/group_logic.py @@ -10,11 +10,15 @@ from orm.services.customer_manager.cms_rest.data.data_manager import \ DataManager from orm.services.customer_manager.cms_rest.logger import get_logger from orm.services.customer_manager.cms_rest.logic.error_base import ( - DuplicateEntryError, ErrorStatus) + DuplicateEntryError, ErrorStatus, NotFound) from orm.services.customer_manager.cms_rest.model.GroupModels import ( - GroupResultWrapper, GroupSummary, GroupSummaryResponse) + GroupResultWrapper, + RoleResultWrapper, + GroupSummary, + GroupSummaryResponse) from orm.services.customer_manager.cms_rest.rds_proxy import RdsProxy + LOG = get_logger(__name__) @@ -51,14 +55,153 @@ class GroupLogic(object): ' already associated with group') raise ex + def assign_roles(self, + group_uuid, + role_assignments, + transaction_id): + + datamanager = DataManager() + try: + [assignment.validate_model() for assignment in role_assignments] + + group_record = datamanager.get_record('group') + region_record = datamanager.get_record('groups_region') + groups_regions = region_record.get_regions_for_group(group_uuid) + + for role_assignment in role_assignments: + for role in role_assignment.roles: + role_id = datamanager.get_role_id_by_name(role) + + for group_region in groups_regions: + region_id = group_region.region_id + + if role_assignment.domain_name: + datamanager.add_groups_role_on_domain( + group_uuid, + role_id, + region_id, + role_assignment.domain_name) + elif role_assignment.project: + project_id = datamanager.get_customer_id_by_uuid( + role_assignment.project) + datamanager.add_groups_role_on_project( + group_uuid, + role_id, + region_id, + project_id) + + datamanager.flush() + group = group_record.read_group_by_uuid(group_uuid) + group_dict = group.get_proxy_dict() + + if len(group_dict["regions"]) > 0: + RdsProxy.send_group_dict(group_dict, transaction_id, "PUT") + + roles = [{'roles': role_assignment.roles, + 'domain': role_assignment.domain_name, + 'project': role_assignment.project} + for role_assignment in role_assignments] + role_result_wrapper = build_response(group_uuid, + transaction_id, + 'role_assignment', + roles=roles) + datamanager.commit() + return role_result_wrapper + except Exception as exp: + LOG.log_exception("GroupLogic - Failed to Assign Role(s)", exp) + datamanager.rollback() + raise + + def unassign_roles(self, + group_uuid, + role_name, + assignment_type, + assignment_value, + transaction_id): + + datamanager = DataManager() + try: + group_record = datamanager.get_record('group') + region_record = datamanager.get_record('groups_region') + groups_regions = region_record.get_regions_for_group(group_uuid) + groups_role = datamanager.get_record('groups_role') + sql_group = datamanager.get_group_by_uuid_or_name(group_uuid) + + if assignment_type != "customer" and assignment_type != "domain": + raise ErrorStatus(400, + "Role unassignment type must either be " + "domain or project.") + + if sql_group is None: + raise ErrorStatus( + 404, + "group with id {} does not exist".format(group_uuid)) + + role_id = datamanager.get_role_id_by_name(role_name) + domain = datamanager.get_record('groups_domain_role') + customer = datamanager.get_record('groups_customer_role') + + for group_region in groups_regions: + region_id = group_region.region_id + + if assignment_type == "domain": + result = domain.remove_domain_role_from_group( + group_uuid, region_id, assignment_value, role_id) + + elif assignment_type == "customer": + customer_id = datamanager.get_customer_id_by_uuid( + assignment_value) + + result = customer.remove_customer_role_from_group( + group_uuid, region_id, customer_id, role_id) + + if result.rowcount == 0: + raise NotFound("Record not found for unassignment type: {}" + " value: {} group: {} region id: {} role" + " {} ".format( + assignment_type, assignment_value, + group_uuid, region_id, role_name)) + + if (not customer.check_groups_customer_role_exist( + role_id, group_uuid) and + not domain.check_groups_domain_role_exist( + role_id, group_uuid)): + result = groups_role.remove_role_from_group(group_uuid, + role_id) + if result.rowcount == 0: + raise NotFound("Record not found for group: {}" + " role: {}".format(group_uuid, + role_name)) + + datamanager.flush() + group = group_record.read_group_by_uuid(group_uuid) + group_dict = group.get_proxy_dict() + + if len(group_dict["regions"]) > 0: + RdsProxy.send_group_dict(group_dict, transaction_id, "PUT") + + datamanager.commit() + LOG.info("Role unassgined - type: {} value: {} group: {} " + "role {} ".format(assignment_type, assignment_value, + group_uuid, role_name)) + except NotFound as e: + datamanager.rollback() + LOG.log_exception("Failed to unassign role, record not found", + e.message) + raise NotFound("Failed to unassign role, not found - %s" % + e.message) + except Exception as exp: + datamanager.rollback() + raise + def create_group(self, group, uuid, transaction_id): datamanager = DataManager() try: group.handle_region_group() sql_group = self.build_full_group(group, uuid, datamanager) - group_result_wrapper = build_response(uuid, transaction_id, - 'create') - + group_result_wrapper = build_response(uuid, + transaction_id, + 'create_group') if sql_group.group_regions and len(sql_group.group_regions) > 1: group_dict = sql_group.get_proxy_dict() for region in group_dict["regions"]: @@ -100,8 +243,9 @@ class GroupLogic(object): datamanager) # new_group_dict = sql_group.get_proxy_dict() - group_result_wrapper = build_response(group_uuid, transaction_id, - 'update') + group_result_wrapper = build_response(group_uuid, + transaction_id, + 'update_group') datamanager.flush() datamanager.commit() @@ -116,7 +260,7 @@ class GroupLogic(object): on_success_by_rds, force_delete): datamanager = DataManager() try: - group_region = datamanager.get_record('group_region') + group_region = datamanager.get_record('groups_region') sql_group = datamanager.get_group_by_uuid_or_name(group_id) if on_success_by_rds and sql_group is None: return @@ -197,10 +341,11 @@ class GroupLogic(object): limit=limit) response = GroupSummaryResponse() if sql_groups: - uuids = ','.join(str(sql_group.uuid) - for sql_group in sql_groups - if sql_group and sql_group.uuid) - resource_status = group_record.get_groups_status_by_uuids(uuids) + uuids = [sql_group.uuid for sql_group in sql_groups + if sql_group and sql_group.uuid] + + sql_in = ', '.join(list(map(lambda arg: "'%s'" % arg, uuids))) + resource_status = group_record.get_groups_status_by_uuids(sql_in) for sql_group in sql_groups: groups = GroupSummary.from_db_model(sql_group) @@ -302,24 +447,38 @@ class GroupLogic(object): raise -def build_response(group_uuid, transaction_id, context): +def build_response(group_uuid, transaction_id, context, roles=[]): """this function generate th group action response JSON :param group_uuid: :param transaction_id: - :param context: create or update + :param context: + :param roles: :return: """ + timestamp = utils.get_time_human() # The link should point to the group itself (/v1/orm/groups/{id}) link_elements = request.url.split('/') base_link = '/'.join(link_elements) - if context == 'create': - base_link = base_link + '/' + group_uuid + if context == 'create_group' or context == 'update_group': + if context == 'create_group': + base_link = base_link + group_uuid - timestamp = utils.get_time_human() - group_result_wrapper = GroupResultWrapper( - transaction_id=transaction_id, - id=group_uuid, - updated=None, - created=timestamp, - links={'self': base_link}) - return group_result_wrapper + group_result_wrapper = GroupResultWrapper( + transaction_id=transaction_id, + id=group_uuid, + updated=None, + created=timestamp, + links={'self': base_link}) + + return group_result_wrapper + + elif context == 'role_assignment': + role_result_wrapper = RoleResultWrapper( + transaction_id=transaction_id, + roles=roles, + links={'self': base_link}, + created=timestamp) + + return role_result_wrapper + else: + return None diff --git a/orm/services/customer_manager/cms_rest/model/GroupModels.py b/orm/services/customer_manager/cms_rest/model/GroupModels.py index e62015c8..1d752cc9 100755 --- a/orm/services/customer_manager/cms_rest/model/GroupModels.py +++ b/orm/services/customer_manager/cms_rest/model/GroupModels.py @@ -35,6 +35,27 @@ class Region(Model): self.error_message = error_message +class RoleAssignment(Model): + roles = wsme.wsattr([str], mandatory=True) + project = wsme.wsattr(wsme.types.text, mandatory=False) + domain_name = wsme.wsattr(wsme.types.text, mandatory=False) + + def __init__(self, status="", domain="", project="", roles=[]): + self.domain_name = domain + self.project = project + self.roles = roles + + def validate_model(self): + if self.project and self.domain_name: + raise ErrorStatus(400, + "Found both project and domain_name tag used. " + "Only one can be specified for role assignment.") + + if len(set(self.roles)) != len(self.roles): + raise ErrorStatus(400, + "Duplicate role in roles tag found ") + + class Group(Model): """group entity with all it's related data """ @@ -168,9 +189,6 @@ class GroupSummaryResponse(Model): self.groups = [] -""" Region Result Handler """ - - class RegionResult(Model): id = wsme.wsattr(wsme.types.text, mandatory=True) added = wsme.wsattr(wsme.types.text, mandatory=False) @@ -195,4 +213,31 @@ class RegionResultWrapper(Model): self.regions = regions_result -""" ****************************************************************** """ +class RoleResult(Model): + roles = wsme.wsattr([str], mandatory=True) + project = wsme.wsattr(wsme.types.text, mandatory=False) + domain_name = wsme.wsattr(wsme.types.text, mandatory=False) + + def __init__(self, roles, project="", domain=""): + Model.__init__(self) + self.roles = roles + if project: + self.project = project + if domain: + self.domain_name = domain + + +class RoleResultWrapper(Model): + transaction_id = wsme.wsattr(wsme.types.text, mandatory=True) + roles = wsme.wsattr([RoleResult], mandatory=True) + links = wsme.wsattr({str: str}, mandatory=True) + created = wsme.wsattr(wsme.types.text, mandatory=True) + + def __init__(self, transaction_id, roles, links, created): + roles_result = [RoleResult(role['roles'], + project=role['project'], + domain=role['domain']) for role in roles] + self.roles = roles_result + self.transaction_id = transaction_id + self.links = links + self.created = created diff --git a/orm/services/customer_manager/scripts/db_scripts/ranger_cms_create_db.sql b/orm/services/customer_manager/scripts/db_scripts/ranger_cms_create_db.sql index 4845700a..49967917 100755 --- a/orm/services/customer_manager/scripts/db_scripts/ranger_cms_create_db.sql +++ b/orm/services/customer_manager/scripts/db_scripts/ranger_cms_create_db.sql @@ -130,11 +130,9 @@ create table if not exists groups_role ( role_id integer not null, group_id varchar(64) not null, - region_id integer not null, - primary key (role_id, region_id, group_id), - foreign key (role_id) references cms_role(id), + primary key (role_id, group_id), + foreign key (role_id) references cms_role(id), foreign key (`group_id`) references `groups` (`uuid`) ON DELETE CASCADE ON UPDATE NO ACTION, - index region_id (region_id), index group_id_idx (group_id)); create table if not exists groups_user @@ -147,14 +145,29 @@ create table if not exists groups_user index user_id (user_id), index group_id (group_id)); -create table if not exists groups_customer +create table if not exists groups_customer_role ( group_id varchar(64) not null, customer_id integer not null, - region_id integer not null, - primary key (group_id, customer_id, region_id), + region_id integer, + role_id integer not null, + primary key (group_id, customer_id, role_id), foreign key (`group_id`) references `groups` (`uuid`) ON DELETE CASCADE ON UPDATE NO ACTION, foreign key (`customer_id`) references `customer` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, + foreign key (`role_id`) references `groups_role` (`role_id`) ON DELETE CASCADE ON UPDATE NO ACTION, foreign key (`region_id`) references `cms_region` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, index customer_id_idx (customer_id), - index regio_id_idx (region_id)); + index role_id_idx (role_id)); + +create table if not exists groups_domain_role + ( + group_id varchar(64) not null, + domain_name varchar(64) not null, + region_id integer, + role_id integer not null, + primary key (group_id, domain_name, role_id), + foreign key (`group_id`) references `groups` (`uuid`) ON DELETE CASCADE ON UPDATE NO ACTION, + foreign key (`domain_name`) references `cms_domain` (`name`) ON DELETE CASCADE ON UPDATE NO ACTION, + foreign key (`role_id`) references `groups_role` (`role_id`) ON DELETE CASCADE ON UPDATE NO ACTION, + foreign key (`region_id`) references `cms_region` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, + index role_id_idx (role_id)); diff --git a/orm/services/resource_distributor/rds/services/helpers.py b/orm/services/resource_distributor/rds/services/helpers.py index 65d34a1e..afd0d1f3 100644 --- a/orm/services/resource_distributor/rds/services/helpers.py +++ b/orm/services/resource_distributor/rds/services/helpers.py @@ -17,7 +17,7 @@ def create_final_yaml(title, description, resources, outputs): yaml.dump(resources, default_flow_style=False)).strip() outputs_yaml = yaml.dump(outputs).strip() - return '\n\n'.join([ + return '\n'.join([ title_yaml, description_yaml, resources_yaml, diff --git a/orm/services/resource_distributor/rds/services/yaml_group_builder.py b/orm/services/resource_distributor/rds/services/yaml_group_builder.py index 290b9334..5e450d1a 100755 --- a/orm/services/resource_distributor/rds/services/yaml_group_builder.py +++ b/orm/services/resource_distributor/rds/services/yaml_group_builder.py @@ -1,6 +1,7 @@ """yaml build build yaml from json input.""" import logging -from orm.services.resource_distributor.rds.services.helpers import create_final_yaml +from orm.services.resource_distributor.rds.services.helpers import \ + create_final_yaml from pecan import conf from pprint import pformat @@ -17,20 +18,60 @@ def yamlbuilder(alldata, region): :param region: data per region :return: the full string of yaml file """ - logger.info("group alldata {} for region {}".format(pformat(alldata), region)) + logger.info("group alldata {} for region {}".format(pformat(alldata), + region)) - outputs = {} - resources = {} + outputs = {"outputs": {}} + resources = {"resources": {}} yaml_version = conf.yaml_configs.group_yaml.yaml_version title = {'heat_template_version': yaml_version} description = {'description': 'yaml file for region - %s' % region['name']} jsondata = alldata - status = {"0": False, "1": True}[str(jsondata['enabled'])] + group_name = jsondata['name'] - if "roles" in alldata: - outputs['outputs'], resources['resources'] = build_group_roles_yaml(jsondata) - else: - outputs['outputs'], resources['resources'] = build_group_yaml(jsondata) + resources["resources"][group_name] = { + 'type': 'OS::Keystone::Group\n', + 'properties': { + 'name': "%s" % group_name, + 'description': jsondata['description'], + 'domain': jsondata['domain_name'] + } + } + + if "groups_roles" in jsondata and len(jsondata["groups_roles"]) > 0: + + template_name = "{}-Role-Assignment".format(group_name) + roles = [] + for customer_role in jsondata["groups_customer_roles"]: + roles.append({ + "role": customer_role["role_name"], + "project": customer_role["customer_uuid"] + }) + + for domain_role in jsondata["groups_domain_roles"]: + roles.append({ + "role": domain_role["role_name"], + "domain": domain_role["domain_name"] + }) + + resources["resources"][template_name] = { + 'type': 'OS::Keystone::GroupRoleAssignment\n', + 'properties': { + 'group': "%s" % group_name, + 'roles': roles + } + } + outputs["outputs"][template_name + "_id"] = { + "value": { + "get_resource": "%s" % template_name + } + } + + outputs["outputs"][group_name + "_id"] = { + "value": { + "get_resource": "%s" % group_name + } + } # putting all parts together for full yaml yamldata = create_final_yaml(title, description, resources, outputs) @@ -38,50 +79,3 @@ def yamlbuilder(alldata, region): "done building group yaml for region %s " % region['name']) return yamldata - - -def build_group_yaml(jsondata): - resources = {} - outputs = {} - group_name = jsondata['name'] - - resources[group_name] = { - 'type': 'OS::Keystone::Group\n', - 'properties': { - 'name': "%s" % group_name, - 'description': jsondata['description'], - 'domain': jsondata['domain_name'], - 'roles': [] - } - } - - outputs[group_name] = { - "value": { - "get_resource": "%s" % group_name - } - } - - return outputs, resources - - -def build_group_roles_yaml(jsondata): - resources = {} - outputs = {} - group_name = jsondata['name'] - template_name = "{}-Role-Assignment".format(group_name) - - resources[template_name] = { - 'type': 'OS::Keystone::GroupRoleAssignment\n', - 'properties': { - 'group': "%s" % group_name, - 'roles': jsondata['roles'] - } - } - - outputs[template_name] = { - "value": { - "get_resource": "%s" % template_name - } - } - - return outputs, resources diff --git a/orm/tests/unit/cms/test_group_logic.py b/orm/tests/unit/cms/test_group_logic.py index a524a95f..6b151f18 100644 --- a/orm/tests/unit/cms/test_group_logic.py +++ b/orm/tests/unit/cms/test_group_logic.py @@ -1,6 +1,7 @@ -from orm.services.customer_manager.cms_rest.data.sql_alchemy\ +from orm.services.customer_manager.cms_rest.data.sql_alchemy \ import models as sql_models -from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus +from orm.services.customer_manager.cms_rest.logic.error_base \ + import ErrorStatus, NotFound from orm.services.customer_manager.cms_rest.logic import group_logic import orm.services.customer_manager.cms_rest.model.GroupModels as models from orm.tests.unit.cms import FunctionalTest @@ -55,7 +56,7 @@ class TestGroupLogic(FunctionalTest): group_logic.utils.get_time_human.return_value = '111' group_logic.RdsProxy = mock.MagicMock() - group_logic.RdsProxy.send_group.return_value = None + group_logic.RdsProxy.send_group_dict.return_value = None group_logic.RdsProxy.get_status.return_value = RdsStatus() group_logic.build_response = mock.MagicMock() @@ -179,6 +180,150 @@ class TestGroupLogic(FunctionalTest): self.assertRaises(group_logic.ErrorStatus, logic.delete_group_by_uuid, 'group_id') + def test_assign_roles_to_group_on_domain_success(self): + roles_assginments = [models.RoleAssignment( + roles=['a_role'], domain='domain')] + + logic = group_logic.GroupLogic() + logic.assign_roles('group_uuid', roles_assginments, 'some_trans_id') + + assert data_manager_mock.add_groups_role_on_domain.called + assert data_manager_mock.get_role_id_by_name.called + assert data_manager_mock.commit.called + assert not data_manager_mock.rollback.called + assert record_mock.read_group_by_uuid.called + assert record_mock.get_regions_for_group.called + + def test_assign_roles_to_group_on_project_success(self): + roles_assginments = [models.RoleAssignment( + roles=['a_role'], project='project')] + + logic = group_logic.GroupLogic() + logic.assign_roles('group_uuid', roles_assginments, 'some_trans_id') + + assert data_manager_mock.add_groups_role_on_project.called + assert data_manager_mock.get_customer_id_by_uuid.called + assert data_manager_mock.commit.called + assert data_manager_mock.get_role_id_by_name.called + assert not data_manager_mock.rollback.called + assert record_mock.read_group_by_uuid.called + assert record_mock.get_regions_for_group.called + + def test_assign_roles_to_group_on_domain_error(self): + global mock_returns_error + mock_returns_error = True + + roles_assginments = [models.RoleAssignment( + roles=['a_role'], domain='domain')] + + logic = group_logic.GroupLogic() + self.assertRaises(SystemError, logic.assign_roles, 'group_uuid', + roles_assginments, 'some_trans_id') + + assert data_manager_mock.rollback.called + + def test_assign_roles_to_group_on_project_error(self): + global mock_returns_error + mock_returns_error = True + + roles_assginments = [models.RoleAssignment( + roles=['a_role'], project='project')] + + logic = group_logic.GroupLogic() + self.assertRaises(SystemError, logic.assign_roles, 'group_uuid', + roles_assginments, 'some_trans_id') + + assert data_manager_mock.rollback.called + + def test_unassign_roles_from_group_on_domain_success(self): + logic = group_logic.GroupLogic() + logic.unassign_roles('group_uuid', 'role', 'domain', 'domain_name', + 'some_trans_id') + + assert record_mock.get_regions_for_group.called + assert record_mock.remove_domain_role_from_group.called + assert record_mock.check_groups_customer_role_exist.called + assert record_mock.check_groups_domain_role_exist.called + assert record_mock.read_group_by_uuid.called + assert record_mock.remove_role_from_group.called + assert data_manager_mock.commit.called + assert data_manager_mock.get_role_id_by_name.called + + def test_unassign_roles_from_group_on_project_success(self): + logic = group_logic.GroupLogic() + logic.unassign_roles('group_uuid', 'role', 'customer', 'customer_id', + 'some_trans_id') + + assert record_mock.get_regions_for_group.called + assert record_mock.remove_customer_role_from_group.called + assert record_mock.check_groups_customer_role_exist.called + assert record_mock.check_groups_domain_role_exist.called + assert record_mock.read_group_by_uuid.called + assert record_mock.remove_role_from_group.called + assert data_manager_mock.commit.called + assert data_manager_mock.get_role_id_by_name.called + + def test_unassign_roles_from_group_group_role_not_removed(self): + global flow_type + flow_type = 3 + logic = group_logic.GroupLogic() + logic.unassign_roles('group_uuid', 'role', 'customer', 'customer_id', + 'some_trans_id') + + assert record_mock.get_regions_for_group.called + assert record_mock.remove_customer_role_from_group.called + assert record_mock.check_groups_customer_role_exist.called + assert not record_mock.check_groups_domain_role_exist.called + assert record_mock.read_group_by_uuid.called + assert not record_mock.remove_role_from_group.called + assert data_manager_mock.commit.called + assert data_manager_mock.get_role_id_by_name.called + + def test_unassign_roles_from_group_on_domain_error(self): + global mock_returns_error + mock_returns_error = True + + logic = group_logic.GroupLogic() + self.assertRaises(SystemError, logic.unassign_roles, 'group_uuid', + 'role', 'domain', 'domain_name', 'some_trans_id') + + assert record_mock.get_regions_for_group.called + assert record_mock.remove_domain_role_from_group.called + assert data_manager_mock.rollback.called + assert data_manager_mock.get_role_id_by_name.called + + def test_unassign_roles_from_group_on_project_error(self): + global mock_returns_error + mock_returns_error = True + + logic = group_logic.GroupLogic() + self.assertRaises(SystemError, logic.unassign_roles, 'group_uuid', + 'role', 'customer', 'customer_id', 'some_trans_id') + + assert record_mock.get_regions_for_group.called + assert record_mock.remove_customer_role_from_group.called + assert data_manager_mock.rollback.called + assert data_manager_mock.get_role_id_by_name.called + + def test_unassign_roles_from_group_group_not_found(self): + global flow_type + flow_type = 1 + logic = group_logic.GroupLogic() + self.assertRaises(ErrorStatus, logic.unassign_roles, 'group_uuid', + 'role', 'customer', 'customer_id', 'some_trans_id') + + def test_unassign_roles_from_group_incorrect_type(self): + logic = group_logic.GroupLogic() + self.assertRaises(ErrorStatus, logic.unassign_roles, 'group_uuid', + 'role', 'wrong_type', 'customer_id', 'some_trans_id') + + def test_unassign_roles_from_group_group_role_not_found(self): + global flow_type + flow_type = 4 + logic = group_logic.GroupLogic() + self.assertRaises(NotFound, logic.unassign_roles, 'group_uuid', + 'role', 'customer', 'customer_id', 'some_trans_id') + def get_mock_datamanager(): global data_manager_mock @@ -190,6 +335,8 @@ def get_mock_datamanager(): data_manager_mock = mock.MagicMock() record_mock = mock.MagicMock() record_mock.get_groups_by_criteria.return_value = [sql_group] + result_mock = mock.Mock() + result_mock.rowcount = 1 def _get_group(): def mock_to_wsme(): @@ -205,9 +352,20 @@ def get_mock_datamanager(): if not mock_returns_error: data_manager_mock.get_group_by_uuid_or_name.return_value = _get_group() + data_manager_mock.get_role_id_by_name.return_value = 1 record_mock.delete_region_for_group.return_value = None record_mock.delete_group_by_uuid.return_value = None + # mock for assign/unassign roles + record_mock.read_group_by_uuid.return_value = sql_group + record_mock.get_regions_for_group.return_value = [ + sql_models.GroupRegion(region_id=1, group_id="group_id")] + record_mock.remove_customer_role_from_group.return_value = result_mock + record_mock.remove_domain_role_from_group.return_value = result_mock + record_mock.check_groups_customer_role_exist.return_value = False + record_mock.check_groups_domain_role_exist.return_value = False + record_mock.remove_role_from_group.return_value = result_mock + if flow_type == 1: record_mock.read_group_by_uuid.return_value = None data_manager_mock.get_group_by_uuid_or_name.return_value = None @@ -216,9 +374,24 @@ def get_mock_datamanager(): q.get_group_regions.return_value = [mock.MagicMock()] record_mock.read_group_by_uuid.return_value = q record_mock.delete_group_by_uuid.side_effect = SystemError() + elif flow_type == 3: + record_mock.check_groups_customer_role_exist.return_value = True + elif flow_type == 4: + result_mock.rowcount = 0 + record_mock.remove_role_from_group.return_value = result_mock else: record_mock.read_group_by_uuid.side_effect = SystemError() record_mock.delete_region_for_group.side_effect = SystemError() + # mock for assign roles + data_manager_mock.add_groups_role_on_domain.side_effect = \ + SystemError() + data_manager_mock.add_groups_role_on_project.side_effect = \ + SystemError() + record_mock.get_regions_for_group.return_value = [ + sql_models.GroupRegion(region_id=1, group_id="group_id")] + record_mock.remove_domain_role_from_group.side_effect = SystemError() + record_mock.remove_customer_role_from_group.side_effect = SystemError() + data_manager_mock.get_record.return_value = record_mock return data_manager_mock diff --git a/orm/tests/unit/cms/test_groups_role.py b/orm/tests/unit/cms/test_groups_role.py new file mode 100644 index 00000000..7da0e3cf --- /dev/null +++ b/orm/tests/unit/cms/test_groups_role.py @@ -0,0 +1,168 @@ +import mock +import requests + +from orm.services.customer_manager.cms_rest.controllers.v1.orm.group\ + import roles +from orm.services.customer_manager.cms_rest.logic.error_base import ErrorStatus +from orm.services.customer_manager.cms_rest.model import GroupModels +from orm.tests.unit.cms import FunctionalTest +from wsme.exc import ClientSideError + +group_logic_mock = None + + +class TestGroupsRoleController(FunctionalTest): + def setUp(self): + FunctionalTest.setUp(self) + + roles.authentication = mock.MagicMock() + + roles.GroupLogic = get_mock_group_logic + roles.GroupLogic.return_error = 0 + + roles.utils = mock.MagicMock() + roles.utils.make_transid.return_value = 'some_trans_id' + roles.utils.audit_trail.return_value = None + roles.utils.make_uuid.return_value = 'some_uuid' + + roles.err_utils = mock.MagicMock() + + def tearDown(self): + FunctionalTest.tearDown(self) + + def test_assign_roles_to_group(self): + # given + requests.post = mock.MagicMock(return_value=ResponseMock(200)) + + # when + response = self.app.post_json('/v1/orm/groups/{groups id}/roles/', + GROUPS_ROLE_JSON) + + # assert + assert response.status_int == 200 + assert roles.utils.audit_trail.called + assert group_logic_mock.assign_roles_called + + def test_assign_roles_to_group_fail(self): + # given + requests.post = mock.MagicMock() + + roles.GroupLogic.return_error = 1 + + roles.err_utils.get_error = mock.MagicMock( + return_value=ClientSideError("blabla", 500)) + # when + response = self.app.post_json('/v1/orm/groups/{groups id}/roles/', + GROUPS_ROLE_JSON, expect_errors=True) + # assert + self.assertEqual(response.status_int, 500) + + def test_assign_roles_to_group_bad_request(self): + # given + requests.post = mock.MagicMock() + + roles.GroupLogic.return_error = 2 + roles.err_utils.get_error = mock.MagicMock( + return_value=ClientSideError("blabla", 404)) + + # when + response = self.app.post_json('/v1/orm/groups/{groups_id}/roles/', + GROUPS_ROLE_JSON, expect_errors=True) + + # assert + self.assertEqual(response.status_int, 404) + + @mock.patch.object(roles, 'request') + def test_unassign_roles_to_group(self, request): + # given + requests.delete = mock.MagicMock(return_value=ResponseMock(204)) + request.headers = {'X-RANGER-Requester': "rds_resource_service_proxy"} + + response = self.app.delete( + '/v1/orm/groups/{groups id}/roles/{role name}/{type}/{type id}') + + # assert + assert response.status_int == 204 + assert roles.utils.audit_trail.called + assert group_logic_mock.unassign_roles_called + + def test_unassign_roles_to_group_fail(self): + # given + requests.delete = mock.MagicMock() + + roles.GroupLogic.return_error = 1 + roles.err_utils.get_error = mock.MagicMock( + return_value=ClientSideError("blabla", 500)) + + # when + response = self.app.delete( + '/v1/orm/groups/{groups id}/roles/{role name}/{type}/{type id}', + expect_errors=True) + + # assert + self.assertEqual(response.status_int, 500) + + @mock.patch.object(roles, 'request') + def test_unassign_roles_to_group_bad_request(self, request): + # given + requests.delete = mock.MagicMock() + request.headers = {'X-RANGER-Requester': "rds_resource_service_proxy"} + + roles.GroupLogic.return_error = 2 + roles.err_utils.get_error = mock.MagicMock( + return_value=ClientSideError("blabla", 404)) + + # when + response = self.app.delete( + '/v1/orm/groups/{groups id}/roles/{role name}/{type}/{type id}', + expect_errors=True) + + # assert + self.assertEqual(response.status_int, 404) + + +def get_mock_group_logic(): + global group_logic_mock + group_logic_mock = mock.MagicMock() + + if roles.GroupLogic.return_error == 0: + res = GroupModels.RoleResultWrapper(transaction_id='1', + roles=[], + links={}, + created='1') + + res1 = GroupModels.RoleResultWrapper(transaction_id='1', + roles=[], + links={}, + created='1') + + group_logic_mock.assign_roles.return_value = res + group_logic_mock.unassign_roles.return_value = res1 + + elif roles.GroupLogic.return_error == 1: + group_logic_mock.assign_roles.side_effect = SystemError() + group_logic_mock.unassign_roles.side_effect = SystemError() + + else: + group_logic_mock.assign_roles.side_effect = ErrorStatus( + status_code=404) + group_logic_mock.unassign_roles.side_effect = ErrorStatus( + status_code=404) + + return group_logic_mock + + +class ResponseMock: + def __init__(self, status_code=200): + self.status_code = status_code + + +GROUPS_ROLE_JSON = [ + { + "project": "project-id", + "roles": [ + "role1", + "role2" + ] + } +] diff --git a/orm/tests/unit/ormcli/test_cmscli.py b/orm/tests/unit/ormcli/test_cmscli.py index 5c2ec224..14f7ceca 100755 --- a/orm/tests/unit/ormcli/test_cmscli.py +++ b/orm/tests/unit/ormcli/test_cmscli.py @@ -41,6 +41,8 @@ class CmsTests(TestCase): args.starts_with = 'test_startswith' args.contains = 'test_contains' args.force_delete is False + args.role = 'test_role_name' + args.assignment_value = 'test_role_assignment_value' subcmd_to_result = { 'create_customer': (requests.post, 'customers/',), @@ -93,7 +95,9 @@ class CmsTests(TestCase): 'groups/?region=%s&starts_with=%s' '&contains=%s' % (args.region, args.starts_with, - args.contains)) + args.contains)), + 'assign_group_roles': ( + requests.post, 'groups/%s/roles' % args.groupid,) } # Assert that each subcommand returns the expected details @@ -102,6 +106,24 @@ class CmsTests(TestCase): self.assertEqual(subcmd_to_result[subcmd], cmscli.cmd_details(args)) + args.subcmd = 'unassign_group_roles' + for assignment_type in ['customer', 'domain']: + if assignment_type == 'customer': + args.customer = args.assignment_value + args.domain = None + else: + args.domain = args.assignment_value + args.customer = None + cmd_to_result = (requests.delete, + 'groups/%s/roles/%s/%s/%s' % ( + args.groupid, + args.role, + assignment_type, + args.assignment_value)) + + self.assertEqual(cmd_to_result, + cmscli.cmd_details(args)) + @mock.patch.object(cmscli.cli_common, 'get_keystone_ep', return_value=None) def test_get_token_keystone_ep_not_found(self, mock_get_keystone_ep): diff --git a/orm/tests/unit/rds/services/test_group_yaml.py b/orm/tests/unit/rds/services/test_group_yaml.py index 3b4a22dd..95a39332 100644 --- a/orm/tests/unit/rds/services/test_group_yaml.py +++ b/orm/tests/unit/rds/services/test_group_yaml.py @@ -18,10 +18,10 @@ yaml_group = \ ' test_group:\n properties:\n'\ ' description: "this is a description"\n'\ ' domain: nc\n'\ - ' name: test_group\n roles: []\n'\ + ' name: test_group\n'\ ' type: OS::Keystone::Group\n\n\n'\ 'outputs:\n'\ - ' test_group:\n'\ + ' test_group_id:\n'\ ' value: {get_resource: test_group}\n' region = {'name': 'regionname', diff --git a/ranger-tempest-plugin/ranger_tempest_plugin/config.py b/ranger-tempest-plugin/ranger_tempest_plugin/config.py index cc005340..08633c8f 100755 --- a/ranger-tempest-plugin/ranger_tempest_plugin/config.py +++ b/ranger-tempest-plugin/ranger_tempest_plugin/config.py @@ -62,5 +62,8 @@ OrmGroup = [ help="Ranger Region Service URL"), cfg.ListOpt("flavor_series", default=['xx'], - help="Supported flavor series") + help="Supported flavor series"), + cfg.StrOpt("domain", + default='Default', + help="Domain used for Ranger tempest testing") ] diff --git a/ranger-tempest-plugin/ranger_tempest_plugin/schemas/group_schema.py b/ranger-tempest-plugin/ranger_tempest_plugin/schemas/group_schema.py index ac45662a..4dd1fc98 100644 --- a/ranger-tempest-plugin/ranger_tempest_plugin/schemas/group_schema.py +++ b/ranger-tempest-plugin/ranger_tempest_plugin/schemas/group_schema.py @@ -13,6 +13,10 @@ # License for the specific language governing permissions and limitations # under the License +_delete = { + 'status_code': [204] +} + _status = { 'type': 'string', 'enum': ['Success', 'no regions', 'Error', 'Pending', 'Submitted'] @@ -106,8 +110,37 @@ list_groups = { } } -delete_group = { - 'status_code': [204] +delete_group = _delete +delete_groups_region = _delete + +_roles = { + 'type': 'object', + 'properties': { + 'roles': { + 'type': 'array', + 'items': {'type': 'string'} + }, + 'project': {'type': 'string'}, + 'domain_name': {'type': 'string'} + }, + 'required': ['roles'] } -delete_region_from_group = delete_group +assign_group_roles = { + 'status_code': [200], + 'response_body': { + 'type': 'object', + 'properties': { + 'transaction_id': {'type': 'string'}, + 'roles': { + 'type': 'array', + 'items': _roles + }, + 'links': _links, + 'created': {'type': 'string', 'format': 'date-time'} + }, + 'required': ['transaction_id', 'roles', 'links', 'created'] + } +} + +unassign_group_roles = _delete diff --git a/ranger-tempest-plugin/ranger_tempest_plugin/services/grp_client.py b/ranger-tempest-plugin/ranger_tempest_plugin/services/grp_client.py index 23e139d8..e01e9f9f 100755 --- a/ranger-tempest-plugin/ranger_tempest_plugin/services/grp_client.py +++ b/ranger-tempest-plugin/ranger_tempest_plugin/services/grp_client.py @@ -1,56 +1,72 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the 'License'); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, 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 json -import urllib - -from ranger_tempest_plugin.schemas import group_schema as schema -from ranger_tempest_plugin.services import base_client - -from tempest import config - -CONF = config.CONF - - -class GrpClient(base_client.RangerClientBase): - - cms_url = CONF.ranger.RANGER_CMS_BASE_URL - version = 'v1' - - def create_group(self, **kwargs): - uri = '%s/%s/orm/groups' % (self.cms_url, self.version) - post_body = json.dumps(kwargs) - return self.post_request(uri, post_body, schema.create_group) - - def get_group(self, identifier): - uri = '%s/%s/orm/groups/%s' \ - % (self.cms_url, self.version, identifier) - return self.get_request(uri, schema.get_group) - - def list_groups(self, filter=None): - uri = '%s/%s/orm/groups' % (self.cms_url, self.version) - if filter is not None: - uri += '?' + urllib.urlencode(filter) - return self.get_request(uri, schema.list_groups) - - def delete_region_from_group(self, group_id, region_id): - uri = '%s/%s/orm/groups/%s/regions/%s' % ( - self.cms_url, self.version, group_id, region_id) - return self.delete_request(uri, schema.delete_region_from_group) - - def delete_group(self, group_id): - uri = '%s/%s/orm/groups/%s' \ - % (self.cms_url, self.version, group_id) - return self.delete_request(uri, schema.delete_group) +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, 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 json +import urllib + +from ranger_tempest_plugin.schemas import group_schema as schema +from ranger_tempest_plugin.services import base_client + +from tempest import config + +CONF = config.CONF + + +class GrpClient(base_client.RangerClientBase): + + cms_url = CONF.ranger.RANGER_CMS_BASE_URL + version = 'v1' + + def create_group(self, **kwargs): + uri = '%s/%s/orm/groups' % (self.cms_url, self.version) + post_body = json.dumps(kwargs) + return self.post_request(uri, post_body, schema.create_group) + + def get_group(self, identifier): + uri = '%s/%s/orm/groups/%s' \ + % (self.cms_url, self.version, identifier) + return self.get_request(uri, schema.get_group) + + def list_groups(self, filter=None): + uri = '%s/%s/orm/groups' % (self.cms_url, self.version) + if filter is not None: + uri += '?' + urllib.urlencode(filter) + return self.get_request(uri, schema.list_groups) + + def delete_groups_region(self, group_id, region_id): + uri = '%s/%s/orm/groups/%s/regions/%s' % ( + self.cms_url, self.version, group_id, region_id) + return self.delete_request(uri, schema.delete_groups_region) + + def delete_group(self, group_id): + uri = '%s/%s/orm/groups/%s' \ + % (self.cms_url, self.version, group_id) + return self.delete_request(uri, schema.delete_group) + + def assign_group_roles(self, group_id, *args): + uri = '%s/%s/orm/groups/%s/roles' % ( + self.cms_url, self.version, group_id) + post_body = json.dumps(args) + return self.post_request(uri, post_body, schema.assign_group_roles) + + def unassign_group_roles( + self, group_id, role, assignmenet_type, assignment_value): + uri = '%s/%s/orm/groups/%s/roles/%s/%s/%s' % (self.cms_url, + self.version, + group_id, + role, + assignmenet_type, + assignment_value) + return self.delete_request(uri, schema.unassign_group_roles) diff --git a/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/grp_base.py b/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/grp_base.py index b5c4dd31..3e2cbf18 100755 --- a/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/grp_base.py +++ b/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/grp_base.py @@ -1,176 +1,175 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, 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 time - -from oslo_log import log as logging -from ranger_tempest_plugin.tests.api import base -from tempest.common.utils import data_utils -from tempest import config -from tempest.lib import exceptions - -CONF = config.CONF -LOG = logging.getLogger(__name__) - - -class GrpBaseOrmTest(base.BaseOrmTest): - credentials = ['admin', 'primary', 'alt'] - - @classmethod - def setup_clients(cls): - super(GrpBaseOrmTest, cls).setup_clients() - cls.client = cls.os_primary.grp_client - - @classmethod - def _get_group_params(cls, enabled=True): - region, payload = {}, {} - grp_name = data_utils.rand_name('ormTempestGrp') - domain_name = CONF.auth.admin_domain_name - region['name'] = CONF.identity.region - region['type'] = 'single' - regions = [region] - payload["description"] = grp_name - payload["domain_name"] = domain_name - payload["enabled"] = True if enabled else False - payload["name"] = grp_name - payload["regions"] = regions - return payload - - @classmethod - def _get_user_params(cls, alt=False): - users = [] - if not alt: - users.append({'id': cls.os_primary.credentials.username, - 'role': ['admin']}) - else: - users.append({'id': cls.os_alt.credentials.username, - 'role': ['admin_viewer', 'admin_support']}) - return users - - @classmethod - def _get_region_params(cls): - region = {} - region['name'] = CONF.identity.region - region['type'] = 'single' - return [region] - - @classmethod - def _create_grp_validate_creation_on_dcp_and_lcp(self, **kwargs): - """ Creates a keystone group record: kwargs contains field data - needed for group customer POST body: - - name - - description - - enabled - - domain_name - - regions - """ - _, body = self.client.create_group(**kwargs) - group_id = body["group"]["id"] - _, group = self.client.get_group(group_id) - if group["name"] == kwargs["name"]: - if group["regions"] == []: - group_status = "no regions" - else: - group_status = "Success" - - self._wait_for_group_status(group_id, group_status) - return group_id - else: - message = "group %s not created successfully" % kwargs["name"] - exceptions.TempestException(message) - - @classmethod - def _wait_for_group_status(cls, group_id, status): - group_status = cls.client.get_group(group_id)[1]["status"] - start = int(time.time()) - while group_status != status: - time.sleep(cls.build_interval) - group_status = cls.client.get_group(group_id)[1]["status"] - if group_status == 'Error': - message = ('group %s failed to reach %s status' - ' and is in ERROR status on orm' % - (group_id, status)) - raise exceptions.TempestException(message) - if int(time.time()) - start >= cls.build_timeout: - message = ('group %s failed to reach %s' - 'status within the required time (%s s)' - 'on orm and is in %s status.' - % (group_id, status, - cls.build_timeout, - group_status)) - raise exceptions.TimeoutException(message) - - @classmethod - def _del_group_validate_deletion_on_dcp_and_lcp(cls, group_id): - _, group = cls.client.get_group(group_id) - regions_on_group = [region for region in group["regions"]] - if regions_on_group: - region_name_on_group = regions_on_group[0]["name"] - cls._delete_region_from_group_and_validate_deletion( - group_id, region_name_on_group) - cls.client.delete_group(group_id) - cls._wait_for_group_deletion_on_dcp(group_id) - cls._validate_group_deletion_on_lcp(group_id) - - @classmethod - def _delete_region_from_group_and_validate_deletion( - cls, group_id, rname): - _, region = cls.os_admin.rms_client.get_region(rname) - region_id = region["id"] - cls.client.delete_region_from_group(group_id, region_id) - cls._wait_for_group_status(group_id, "no regions") - _, body = cls.client.get_group(group_id) - regions_on_group = [rgn for rgn in body["regions"]] - if regions_on_group: - message = "Region %s failed to get deleted from group %s " % ( - rname, group_id) - raise exceptions.TempestException(message) - - @classmethod - def _wait_for_group_deletion_on_dcp(cls, group_id): - _, body = cls.client.list_groups() - group_list = body["groups"] - group_ids = [group["id"] for group in group_list - if group["id"] == group_id] - start = int(time.time()) - while group_ids: - time.sleep(cls.build_interval) - _, body = cls.client.list_groups()["groups"] - group_list = body["groups"] - group_ids = [group["id"] for group in group_list - if group["id"] == group_id] - if group_ids: - group_status = group_ids[0]["status"] - if group_status == 'Error': - message = "group %s failed to get deleted and is in\ - error status" % group_id - raise exceptions.TempestException(message) - if int(time.time()) - start >= cls.build_timeout: - message = ( - 'group %s failed to get deleted within ' - 'the required time (%s s) and is in %s status.' - % (group_id, cls.build_timeout, - group_status)) - raise exceptions.TimeoutException(message) - - @classmethod - def _validate_group_deletion_on_lcp(cls, group_id): - _, body = cls.client.list_groups() - group_ids = [group["id"] for group in body["groups"] - if group["id"] == group_id] - if group_ids: - message = "group %s failed to get deleted on lcp" \ - % group_id - raise exceptions.TempestException(message) +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, 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 time + +from oslo_log import log as logging +from ranger_tempest_plugin.tests.api.cms_base import CmsBaseOrmTest +from tempest.common.utils import data_utils +from tempest import config +from tempest.lib import exceptions + +CONF = config.CONF +LOG = logging.getLogger(__name__) + + +class GrpBaseOrmTest(CmsBaseOrmTest): + credentials = ['admin', 'primary', 'alt'] + + @classmethod + def setup_clients(cls): + super(GrpBaseOrmTest, cls).setup_clients() + cls.grp_client = cls.os_primary.grp_client + + @classmethod + def _get_group_params(cls, enabled=True): + region, payload = {}, {} + grp_name = data_utils.rand_name('ormTempestGrp') + region['name'] = CONF.identity.region + region['type'] = 'single' + regions = [region] + payload["description"] = grp_name + payload["domain_name"] = CONF.ranger.domain + payload["enabled"] = True if enabled else False + payload["name"] = grp_name + payload["regions"] = regions + return payload + + @classmethod + def _get_user_params(cls, alt=False): + users = [] + if not alt: + users.append({'id': cls.os_primary.credentials.username, + 'role': ['admin']}) + else: + users.append({'id': cls.os_alt.credentials.username, + 'role': ['admin_viewer', 'admin_support']}) + return users + + @classmethod + def _get_region_params(cls): + region = {} + region['name'] = CONF.identity.region + region['type'] = 'single' + return [region] + + @classmethod + def _create_grp_validate_creation_on_dcp_and_lcp(self, **kwargs): + """ Creates a keystone group record: kwargs contains field data + needed for group customer POST body: + - name + - description + - enabled + - domain_name + - regions + """ + _, body = self.grp_client.create_group(**kwargs) + group_id = body["group"]["id"] + _, group = self.grp_client.get_group(group_id) + if group["name"] == kwargs["name"]: + if group["regions"] == []: + group_status = "no regions" + else: + group_status = "Success" + + self._wait_for_group_status(group_id, group_status) + return group_id + else: + message = "group %s not created successfully" % kwargs["name"] + exceptions.TempestException(message) + + @classmethod + def _wait_for_group_status(cls, group_id, status): + group_status = cls.grp_client.get_group(group_id)[1]["status"] + start = int(time.time()) + while group_status != status: + time.sleep(cls.build_interval) + group_status = cls.grp_client.get_group(group_id)[1]["status"] + if group_status == 'Error': + message = ('group %s failed to reach %s status' + ' and is in ERROR status on orm' % + (group_id, status)) + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ('group %s failed to reach %s' + 'status within the required time (%s s)' + 'on orm and is in %s status.' + % (group_id, status, + cls.build_timeout, + group_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _del_group_validate_deletion_on_dcp_and_lcp(cls, group_id): + _, group = cls.grp_client.get_group(group_id) + regions_on_group = [region for region in group["regions"]] + if regions_on_group: + region_name_on_group = regions_on_group[0]["name"] + cls._delete_region_from_group_and_validate_deletion( + group_id, region_name_on_group) + cls.grp_client.delete_group(group_id) + cls._wait_for_group_deletion_on_dcp(group_id) + cls._validate_group_deletion_on_lcp(group_id) + + @classmethod + def _delete_region_from_group_and_validate_deletion( + cls, group_id, rname): + _, region = cls.os_admin.rms_client.get_region(rname) + region_id = region["id"] + cls.grp_client.delete_groups_region(group_id, region_id) + cls._wait_for_group_status(group_id, "no regions") + _, body = cls.grp_client.get_group(group_id) + regions_on_group = [rgn for rgn in body["regions"]] + if regions_on_group: + message = "Region %s failed to get deleted from group %s " % ( + rname, group_id) + raise exceptions.TempestException(message) + + @classmethod + def _wait_for_group_deletion_on_dcp(cls, group_id): + _, body = cls.grp_client.list_groups() + group_list = body["groups"] + group_ids = [group["id"] for group in group_list + if group["id"] == group_id] + start = int(time.time()) + while group_ids: + time.sleep(cls.build_interval) + _, body = cls.grp_client.list_groups()["groups"] + group_list = body["groups"] + group_ids = [group["id"] for group in group_list + if group["id"] == group_id] + if group_ids: + group_status = group_ids[0]["status"] + if group_status == 'Error': + message = "group %s failed to get deleted and is in\ + error status" % group_id + raise exceptions.TempestException(message) + if int(time.time()) - start >= cls.build_timeout: + message = ( + 'group %s failed to get deleted within ' + 'the required time (%s s) and is in %s status.' + % (group_id, cls.build_timeout, + group_status)) + raise exceptions.TimeoutException(message) + + @classmethod + def _validate_group_deletion_on_lcp(cls, group_id): + _, body = cls.grp_client.list_groups() + group_ids = [group["id"] for group in body["groups"] + if group["id"] == group_id] + if group_ids: + message = "group %s failed to get deleted on lcp" \ + % group_id + raise exceptions.TempestException(message) diff --git a/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/test_groups.py b/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/test_groups.py index e5848299..979f10e1 100755 --- a/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/test_groups.py +++ b/ranger-tempest-plugin/ranger_tempest_plugin/tests/api/test_groups.py @@ -1,121 +1,182 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the 'License'); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, 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 random - -from ranger_tempest_plugin.tests.api import grp_base -from tempest import config -from tempest.lib import decorators -from tempest.lib import exceptions - -CONF = config.CONF - - -class TestTempestGrp(grp_base.GrpBaseOrmTest): - - @classmethod - def resource_setup(cls): - cls.setup_group = cls._get_group_params() - cls.setup_group_id = \ - cls._create_grp_validate_creation_on_dcp_and_lcp( - **cls.setup_group) - super(TestTempestGrp, cls).resource_setup() - - @classmethod - def resource_cleanup(cls): - cls._del_group_validate_deletion_on_dcp_and_lcp(cls.setup_group_id) - super(TestTempestGrp, cls).resource_cleanup() - - @decorators.idempotent_id('deeb3b8a-fb38-46e1-97ba-c878b0ba890f') - def test_get_group(self): - """ Execute 'get_group' using the following options: - - get group by id - - get group by name - """ - - # execute get_group using group id and group name - for identifier in [self.setup_group_id, - self.setup_group['name']]: - _, body = self.client.get_group(identifier) - self.assertIn(self.setup_group_id, body['uuid']) - - @decorators.idempotent_id('8068e33f-a6aa-416a-9505-048c6ad037b2') - def test_list_groups_with_filters(self): - """ This function executes 'list groups' with all available filters: - - no filter (i.e. list all groups) - - filter by region - - group name contains a substring - - group name starts_with a string - """ - - # format filter parameter values - region_name = [ - region['name'] for region in self.setup_group['regions']] - group_name = self.setup_group['name'] - substr_name = random.randint(0, len(group_name)) - - # define the list groups filters to be used for this test - no_filter = None - region_filter = {'region': region_name[0]} - contains_filter = {'contains': group_name[substr_name:]} - startswith_filter = {'starts_with': group_name[:substr_name]} - - # execute list_groups with the available filters - for list_filter in [no_filter, region_filter, contains_filter, - startswith_filter]: - _, body = self.client.list_groups(list_filter) - groups = [grp['id'] for grp in body['groups']] - self.assertIn(self.setup_group_id, groups) - - @decorators.idempotent_id('880f614f-6317-4973-a244-f2e44443f551') - def test_delete_regions(self): - # setup data for delete_region - post_body = self._get_group_params() - region_name = post_body["regions"][0]["name"] - test_group_id = self._create_grp_validate_creation_on_dcp_and_lcp( - **post_body) - self.addCleanup(self._del_group_validate_deletion_on_dcp_and_lcp, - test_group_id) - _, group = self.client.get_group(test_group_id) - self.assertTrue(group["regions"]) - _, body = self.client.delete_region_from_group(test_group_id, - region_name) - self._wait_for_group_status(test_group_id, 'no regions') - _, group = self.client.get_group(test_group_id) - self.assertFalse(group["regions"]) - - @decorators.idempotent_id('bba25028-d962-47df-9566-557eec48f22d') - def test_create_group(self): - post_body = self._get_group_params() - test_group_name = post_body['name'] - _, body = self.client.create_group(**post_body) - test_group_id = body['group']['id'] - self.addCleanup(self._del_group_validate_deletion_on_dcp_and_lcp, - test_group_id) - self._wait_for_group_status(test_group_id, 'Success') - _, body = self.client.get_group(test_group_name) - self.assertIn(test_group_id, body['uuid']) - - @decorators.idempotent_id('356633f0-c615-4bdc-8f0f-d97b6ca409e0') - def test_delete_group(self): - # setup data for test case - post_body = self._get_group_params() - test_group_id = self._create_grp_validate_creation_on_dcp_and_lcp( - **post_body) - - # delete the data and do get_group to ensure 404-NotFound response - self._del_group_validate_deletion_on_dcp_and_lcp(test_group_id) - self.assertRaises(exceptions.NotFound, self.client.get_group, - test_group_id) +# Copyright 2016 AT&T Corp +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the 'License'); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, 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 random + +from ranger_tempest_plugin.tests.api import grp_base +from tempest import config +from tempest.lib import decorators +from tempest.lib import exceptions + +CONF = config.CONF + + +class TestTempestGrp(grp_base.GrpBaseOrmTest): + + @classmethod + def resource_setup(cls): + cls.setup_customer_params = cls._get_customer_params() + cls.setup_customer_id = \ + cls._create_cust_validate_creation_on_dcp_and_lcp( + **cls.setup_customer_params) + + cls.setup_group = cls._get_group_params() + cls.setup_group_id = \ + cls._create_grp_validate_creation_on_dcp_and_lcp( + **cls.setup_group) + super(TestTempestGrp, cls).resource_setup() + + @classmethod + def resource_cleanup(cls): + cls._del_group_validate_deletion_on_dcp_and_lcp(cls.setup_group_id) + + cls._del_cust_validate_deletion_on_dcp_and_lcp( + cls.setup_customer_id) + + super(TestTempestGrp, cls).resource_cleanup() + + @decorators.idempotent_id('deeb3b8a-fb38-46e1-97ba-c878b0ba890f') + def test_get_group(self): + """ Execute 'get_group' using the following options: + - get group by id + - get group by name + """ + + # execute get_group using group id and group name + for identifier in [self.setup_group_id, + self.setup_group['name']]: + _, body = self.grp_client.get_group(identifier) + self.assertIn(self.setup_group_id, body['uuid']) + + @decorators.idempotent_id('8068e33f-a6aa-416a-9505-048c6ad037b2') + def test_list_groups_with_filters(self): + """ This function executes 'list groups' with all available filters: + - no filter (i.e. list all groups) + - filter by region + - group name contains a substring + - group name starts_with a string + """ + + # format filter parameter values + region_name = [ + region['name'] for region in self.setup_group['regions']] + group_name = self.setup_group['name'] + substr_name = random.randint(0, len(group_name)) + + # define the list groups filters to be used for this test + no_filter = None + region_filter = {'region': region_name[0]} + contains_filter = {'contains': group_name[substr_name:]} + startswith_filter = {'starts_with': group_name[:substr_name]} + + # execute list_groups with the available filters + for list_filter in [no_filter, region_filter, contains_filter, + startswith_filter]: + _, body = self.grp_client.list_groups(list_filter) + groups = [grp['id'] for grp in body['groups']] + self.assertIn(self.setup_group_id, groups) + + @decorators.idempotent_id('880f614f-6317-4973-a244-f2e44443f551') + def test_delete_regions(self): + # setup data for delete_region + post_body = self._get_group_params() + region_name = post_body["regions"][0]["name"] + test_group_id = self._create_grp_validate_creation_on_dcp_and_lcp( + **post_body) + self.addCleanup(self._del_group_validate_deletion_on_dcp_and_lcp, + test_group_id) + _, group = self.grp_client.get_group(test_group_id) + self.assertTrue(group["regions"]) + _, body = self.grp_client.delete_groups_region(test_group_id, + region_name) + self._wait_for_group_status(test_group_id, 'no regions') + _, group = self.grp_client.get_group(test_group_id) + self.assertFalse(group["regions"]) + + @decorators.idempotent_id('bba25028-d962-47df-9566-557eec48f22d') + def test_create_group(self): + post_body = self._get_group_params() + test_group_name = post_body['name'] + _, body = self.grp_client.create_group(**post_body) + test_group_id = body['group']['id'] + self.addCleanup(self._del_group_validate_deletion_on_dcp_and_lcp, + test_group_id) + self._wait_for_group_status(test_group_id, 'Success') + _, body = self.grp_client.get_group(test_group_name) + self.assertIn(test_group_id, body['uuid']) + + @decorators.idempotent_id('356633f0-c615-4bdc-8f0f-d97b6ca409e0') + def test_delete_group(self): + # setup data for test case + post_body = self._get_group_params() + test_group_id = self._create_grp_validate_creation_on_dcp_and_lcp( + **post_body) + + # delete the data and do get_group to ensure 404-NotFound response + self._del_group_validate_deletion_on_dcp_and_lcp(test_group_id) + self.assertRaises(exceptions.NotFound, self.grp_client.get_group, + test_group_id) + + @decorators.idempotent_id('afe5c72f-499b-493f-b61b-68bbaca12b7a') + def test_assign_unassign_role_to_group_on_domain(self): + role = { + 'roles': ["admin"], + 'domain_name': CONF.ranger.domain + } + post_body = [role] + + _, body = self.grp_client.assign_group_roles(self.setup_group_id, + *post_body) + + self._wait_for_group_status(self.setup_group_id, 'Success') + self.assertEqual(body['roles'][0]['domain_name'], role['domain_name']) + self.assertEqual(body['roles'][0]['roles'][0], role['roles'][0]) + + _, body = self.grp_client.unassign_group_roles(self.setup_group_id, + role['roles'][0], + 'domain', + role['domain_name']) + + self._wait_for_group_status(self.setup_group_id, 'Success') + # Once the get groups role function is implemented, it will be + # added here to retreive the role and call assert to verfify that + # the role has indeed been unassigned. + self.assertEqual(body, '') + + @decorators.idempotent_id('67f5e46e-9267-4cbb-84d6-ee8521370e23') + def test_assign_unassign_role_to_group_on_project(self): + role = { + 'roles': ["admin"], + 'project': self.setup_customer_id + } + post_body = [role] + + _, body = self.grp_client.assign_group_roles(self.setup_group_id, + *post_body) + + self._wait_for_group_status(self.setup_group_id, 'Success') + self.assertEqual(body['roles'][0]['project'], role['project']) + self.assertEqual(body['roles'][0]['roles'][0], role['roles'][0]) + + _, body = self.grp_client.unassign_group_roles(self.setup_group_id, + role['roles'][0], + 'customer', + role['project']) + + self._wait_for_group_status(self.setup_group_id, 'Success') + # Once the get groups role function is implemented, it will be + # added here to retreive the role and call assert to verfify that + # the role has indeed been unassigned. + self.assertEqual(body, '') diff --git a/ranger-tempest-plugin/tempest_setup/tempest.conf b/ranger-tempest-plugin/tempest_setup/tempest.conf index 8a980340..c28a9ae1 100644 --- a/ranger-tempest-plugin/tempest_setup/tempest.conf +++ b/ranger-tempest-plugin/tempest_setup/tempest.conf @@ -118,3 +118,5 @@ multi_backend = false catalog_type = ranger # uncomment flavor_series and set it accordingly # flavor_series = +# uncomment domain and set it accordingly +# domain =