def project_app_approve(request, project_uuid, application_id): with ExceptionHandler(request): with transaction.atomic(): approve_application(application_id, project_uuid, request_user=request.user) messages.success(request, _(astakos_messages.APPLICATION_APPROVED)) return redirect(reverse('project_detail', args=(project_uuid,)))
def project_app_dismiss(request, project_uuid, application_id): with ExceptionHandler(request): with transaction.atomic(): dismiss_application(application_id, project_uuid, request_user=request.user) messages.success(request, _(astakos_messages.APPLICATION_DISMISSED)) return redirect(reverse("project_list"))
def project_app_deny(request, project_uuid, application_id): with ExceptionHandler(request): reason = request.POST.get("reason", "") with transaction.atomic(): deny_application(application_id, project_uuid, request_user=request.user, reason=reason) messages.success(request, _(astakos_messages.APPLICATION_DENIED)) return redirect(reverse("project_list"))
def project_app_approve(request, project_uuid, application_id): with ExceptionHandler(request): with transaction.atomic(): approve_application(application_id, project_uuid, request_user=request.user) messages.success(request, _(astakos_messages.APPLICATION_APPROVED)) return redirect(reverse('project_detail', args=(project_uuid, )))
def project_app_cancel(request, project_uuid, application_id): with ExceptionHandler(request): with transaction.atomic(): cancel_application(application_id, project_uuid, request_user=request.user) messages.success(request, _(astakos_messages.APPLICATION_CANCELLED)) return redirect(reverse('project_list'))
def project_members_action(request, project_uuid, action=None, redirect_to='', memb_id=None): actions_map = { 'remove': { 'method': 'remove_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_REMOVED) }, 'accept': { 'method': 'accept_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_ACCEPTED) }, 'reject': { 'method': 'reject_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_REJECTED) } } if not action in actions_map.keys(): raise PermissionDenied if memb_id: member_ids = [memb_id] else: member_ids = request.POST.getlist('members') project = get_object_or_404(Project, uuid=project_uuid) user = request.user if not user.owns_project(project) and not user.is_project_admin(): messages.error(request, astakos_messages.NOT_ALLOWED) return redirect(reverse('index')) logger.info( "Member(s) action from %s (project: %r, action: %s, " "members: %r)", user.log_display, project.uuid, action, member_ids) action = actions_map.get(action) action_func = getattr(project_actions, action.get('method')) for member_id in member_ids: member_id = int(member_id) with ExceptionHandler(request): with transaction.atomic(): try: m = action_func(member_id, request.user) except ProjectError as e: messages.error(request, e) else: email = escape(m.person.email) msg = action.get('msg') % email messages.success(request, msg) return redirect_back(request, 'project_list')
def project_cancel_join(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): project = get_object_or_404(Project, uuid=project_uuid) memb_id = request.user.get_membership(project).pk cancel_membership(memb_id, request.user) m = _(astakos_messages.USER_REQUEST_CANCELLED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid,))
def project_cancel_join(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): project = get_object_or_404(Project, uuid=project_uuid) memb_id = request.user.get_membership(project).pk cancel_membership(memb_id, request.user) m = _(astakos_messages.USER_REQUEST_CANCELLED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid, ))
def project_join(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): membership = join_project(project_uuid, request.user) if membership.state != membership.REQUESTED: m = _(astakos_messages.USER_JOINED_PROJECT) else: m = _(astakos_messages.USER_JOIN_REQUEST_SUBMITTED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid,))
def project_join(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): membership = join_project(project_uuid, request.user) if membership.state != membership.REQUESTED: m = _(astakos_messages.USER_JOINED_PROJECT) else: m = _(astakos_messages.USER_JOIN_REQUEST_SUBMITTED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid, ))
def project_leave(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): memb_id = request.user.get_membership(project).pk auto_accepted = leave_project(memb_id, request.user) if auto_accepted: m = _(astakos_messages.USER_LEFT_PROJECT) else: m = _(astakos_messages.USER_LEAVE_REQUEST_SUBMITTED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid,))
def project_leave(request, project_uuid): project = get_object_or_404(Project, uuid=project_uuid) with ExceptionHandler(request): with transaction.atomic(): memb_id = request.user.get_membership(project).pk auto_accepted = leave_project(memb_id, request.user) if auto_accepted: m = _(astakos_messages.USER_LEFT_PROJECT) else: m = _(astakos_messages.USER_LEAVE_REQUEST_SUBMITTED) messages.success(request, m) return redirect_to_next(request, 'project_detail', args=(project.uuid, ))
def project_members_action(request, project_uuid, action=None, redirect_to='', memb_id=None): actions_map = { 'remove': { 'method': 'remove_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_REMOVED) }, 'accept': { 'method': 'accept_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_ACCEPTED) }, 'reject': { 'method': 'reject_membership', 'msg': _(astakos_messages.USER_MEMBERSHIP_REJECTED) } } if not action in actions_map.keys(): raise PermissionDenied if memb_id: member_ids = [memb_id] else: member_ids = request.POST.getlist('members') project = get_object_or_404(Project, uuid=project_uuid) user = request.user if not user.owns_project(project) and not user.is_project_admin(): messages.error(request, astakos_messages.NOT_ALLOWED) return redirect(reverse('index')) logger.info("Member(s) action from %s (project: %r, action: %s, " "members: %r)", user.log_display, project.uuid, action, member_ids) action = actions_map.get(action) action_func = getattr(project_actions, action.get('method')) for member_id in member_ids: member_id = int(member_id) with ExceptionHandler(request): with transaction.atomic(): try: m = action_func(member_id, request.user) except ProjectError as e: messages.error(request, e) else: email = escape(m.person.email) msg = action.get('msg') % email messages.success(request, msg) return redirect_back(request, 'project_list')
def setUp(self): """Common setUp for all AdminTestCases. This setUp method will create and approve a project as well as add several Cyclades model instances using Cyclades' model_factory. """ ProjectAPITest.setUp(self) # Create a simple project. h_owner = {"HTTP_X_AUTH_TOKEN": self.user1.auth_token} app1 = { "name": "test.pr", "description": u"δεσκρίπτιον", "end_date": "2113-5-5T20:20:20Z", "join_policy": "auto", "max_members": 5, "resources": { u"σέρβις1.ρίσορς11": { "project_capacity": 1024, "member_capacity": 512 } } } status, body = self.create(app1, h_owner) # Ensure that the project application has been created. self.assertEqual(status, 201) project_id = body["id"] app_id = body["application"] # Approve the application. with transaction.atomic(): self.project = approve_application(app_id, project_id) self.assertIsNotNone(self.project) # Alias owner user with a generic name self.user = self.user1 # Create cyclades models. self.vm = mf.VirtualMachineFactory() self.volume = mf.VolumeFactory() self.network = mf.NetworkFactory() self.ipv4 = mf.IPv4AddressFactory(address="1.2.3.4") self.ipv6 = mf.IPv6AddressFactory(address="::1")
def setUp(self): """Common setUp for all AdminTestCases. This setUp method will create and approve a project as well as add several Cyclades model instances using Cyclades' model_factory. """ ProjectAPITest.setUp(self) # Create a simple project. h_owner = {"HTTP_X_AUTH_TOKEN": self.user1.auth_token} app1 = {"name": "test.pr", "description": u"δεσκρίπτιον", "end_date": "2113-5-5T20:20:20Z", "join_policy": "auto", "max_members": 5, "resources": {u"σέρβις1.ρίσορς11": { "project_capacity": 1024, "member_capacity": 512}} } status, body = self.create(app1, h_owner) # Ensure that the project application has been created. self.assertEqual(status, 201) project_id = body["id"] app_id = body["application"] # Approve the application. with transaction.atomic(): self.project = approve_application(app_id, project_id) self.assertIsNotNone(self.project) # Alias owner user with a generic name self.user = self.user1 # Create cyclades models. self.vm = mf.VirtualMachineFactory() self.volume = mf.VolumeFactory() self.network = mf.NetworkFactory() self.ipv4 = mf.IPv4AddressFactory(address="1.2.3.4") self.ipv6 = mf.IPv6AddressFactory(address="::1")
def test_bad_transaction_custom_decorator_incorrect_dbs(self): settings.DATABASES['astakos'] = settings.DATABASES['default'] with self.assertRaises(TransactionException): astakos_transaction.atomic(self.bad_transaction)() self.assertEqual(AstakosUser.objects.count(), 0) settings.DATABASES.pop("astakos")
def test_get_project_modifications(self): project = self.project t2 = project.end_date + timedelta(days=12) common_output = { 'resources': [], 'policies': [], 'details': [] } common_app_data = { 'owner': self.user, 'project_id': project.id, 'request_user': self.user, 'resources': {} } # test output for change in project details last_app_data1 = common_app_data.copy() last_app_data1.update({ 'name': 'test-new.gr', 'description': u'δεσκρίπτιον2', 'end_date': t2, }) with transaction.atomic(): last_app1 = submit_application(**last_app_data1) project = Project.objects.get(id=project.id) output_details = common_output.copy() output_details.update({ 'details': [{ 'label': 'name', 'new': 'test-new.gr', 'old': 'test.pr', }, { 'label': 'description', 'new': u'δεσκρίπτιον2', 'old': u'δεσκρίπτιον', }, { 'label': 'end date', 'new': t2, 'old': project.end_date, 'diff': timedelta(days=12) }], }) input_details = admin_tags.get_project_modifications(project) self.assertEqual(input_details, output_details) # test output for change in project policies last_app_data2 = common_app_data.copy() last_app_data2.update({ 'limit_on_members_number': 42 }) with transaction.atomic(): last_app2 = submit_application(**last_app_data2) project = Project.objects.get(id=project.id) output_policies = common_output.copy() output_policies.update({ 'policies': [{ 'label': 'max members', 'new': 42, 'old': 5, 'diff': 37, }], }) input_policies = admin_tags.get_project_modifications(project) self.assertEqual(input_policies, output_policies) # test output for change in project resources last_app_data3 = common_app_data.copy() last_app_data3.update({ 'resources': {u"σέρβις1.ρίσορς11": { 'project_capacity': 1025, 'member_capacity': 511}} }) with transaction.atomic(): last_app3 = submit_application(**last_app_data3) project = Project.objects.get(id=project.id) output_resources = common_output.copy() output_resources.update({ 'resources': [{ 'label': u"σέρβις1.ρίσορς11s", 'new_member': '511', 'old_member': '512', 'diff_member': '-1', 'new_project': '1025', 'old_project': '1024', 'diff_project': '+1' }], }) input_resources = admin_tags.get_project_modifications(project) self.assertEqual(input_resources, output_resources)
def setUp(self): """Common setup method for this test suite.""" with transaction.atomic(): self.user1 = make_local_user("*****@*****.**")
def test_bad_transaction_custom_decorator_using(self): with self.assertRaises(TransactionException): astakos_transaction.atomic(using="default")(self.bad_transaction)() self.assertEqual(AstakosUser.objects.count(), 0)
def test_quota(self): """Test if quotas are displayed properly for a user.""" def get_project_id(obj): """Sort by project id.""" return obj['project'].uuid def assertQuotaEqual(quota1, quota2): """Custom assert function fro quota lists.""" quota1 = sorted(quota1, key=get_project_id) quota2 = sorted(quota2, key=get_project_id) self.assertEqual(len(quota1), len(quota2)) for q1, q2 in zip(quota1, quota2): p1 = q1['project'] p2 = q2['project'] r1 = q1['resources'] r2 = q2['resources'] self.assertEqual(p1.uuid, p2.uuid) self.assertItemsEqual(r1, r2) # Get the reported description of the resources. resource = Resource.objects.get(name=u"σέρβις1.ρίσορς11") desc11 = resource.report_desc resource = Resource.objects.get(name=u"σέρβις1.resource12") desc12 = resource.report_desc resource = Resource.objects.get(name=u"astakos.pending_app") desc13 = resource.report_desc # These should be the base quota of the user base_quota = [{ 'project': self.user.base_project, 'resources': [(desc11, '0', '100'), (desc13, '0', '3'), (desc12, '0 bytes', '1.00 kB')] }] # Test 1 - Check if get_quotas works properly for base quotas quota = get_quotas(self.user) assertQuotaEqual(quota, base_quota) # Test 2 - Check if get_quotas works properly for projects too. # # Add member to a project with transaction.atomic(): enroll_member(self.project.uuid, self.user) # These should be the additional quota from the project. new_quota = [{ 'project': self.project, 'resources': [(desc11, '0', '512')] }] # Assert that get_quotas works as intented quota = get_quotas(self.user) assertQuotaEqual(new_quota + base_quota, quota) # Test 3 - Check if get_quotas shows zero values for removed member # from a project. # # Remove member from project membership = ProjectMembership.objects.get(project=self.project, person=self.user) with transaction.atomic(): remove_membership(membership.id) # These should be the new quota from the project. They are zero since # the user has not used any of the resources, but they are still # displayed as the project limit is > 0. new_quota = [{ 'project': self.project, 'resources': [(desc11, '0', '0')] }] # Assert that get_quotas works as intented quota = get_quotas(self.user) assertQuotaEqual(new_quota + base_quota, quota)
def test_good_transaction_custom_decorator(self): astakos_transaction.atomic(self.good_transaction)() self.assertEqual(AstakosUser.objects.count(), 1)
def project_add_or_modify(request, project_uuid=None): user = request.user # only check quota for non project admin users if not user.is_project_admin(): ok, limit = check_pending_app_quota(user) if not ok: m = _(astakos_messages.PENDING_APPLICATION_LIMIT_ADD) % limit messages.error(request, m) return redirect(restrict_reverse( 'astakos.im.views.project_list')) project = None if project_uuid: project = get_object_or_404(Project, uuid=project_uuid) if not user.owns_project(project) and not user.is_project_admin(): m = _(astakos_messages.NOT_ALLOWED) raise PermissionDenied(m) details_fields = ["name", "homepage", "description", "start_date", "end_date", "comments"] membership_fields = ["member_join_policy", "member_leave_policy", "limit_on_members_number"] resource_catalog, resource_groups = _resources_catalog() resource_catalog_dict, resource_groups_dict = \ _resources_catalog(as_dict=True) extra_context = { 'resource_catalog': resource_catalog, 'resource_groups': resource_groups, 'resource_catalog_dict': resource_catalog_dict, 'resource_groups_dict': resource_groups_dict, 'show_form': True, 'details_fields': details_fields, 'membership_fields': membership_fields, 'object': project } with transaction.atomic(): template_name = 'im/projects/projectapplication_form.html' summary_template_name = \ 'im/projects/projectapplication_form_summary.html' success_msg = _("The project application has been received and " "is under consideration.") form_class = ProjectApplicationForm if project: template_name = 'im/projects/projectmodification_form.html' summary_template_name = \ 'im/projects/projectmodification_form_summary.html' success_msg = _("The project modification has been received and " "is under consideration.") form_class = ProjectModificationForm details_fields.remove('start_date') extra_context['edit'] = 0 if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=project) if form.is_valid(): verify = request.GET.get('verify') edit = request.GET.get('edit') if verify == '1': extra_context['show_form'] = False extra_context['form_data'] = form.cleaned_data template_name = summary_template_name elif edit == '1': extra_context['show_form'] = True else: new_object = form.save() messages.success(request, success_msg, fail_silently=True) return redirect(restrict_reverse('project_list')) else: # handle terminated projects for which the name attribute # has been set to null if project and not project.name: project.name = project.realname form = form_class(instance=project) extra_context['form'] = form return render_to_response(template_name, extra_context, context_instance=RequestContext(request))
def test_quota(self): """Test if quotas are displayed properly for a user.""" def get_project_id(obj): """Sort by project id.""" return obj['project'].uuid def assertQuotaEqual(quota1, quota2): """Custom assert function fro quota lists.""" quota1 = sorted(quota1, key=get_project_id) quota2 = sorted(quota2, key=get_project_id) self.assertEqual(len(quota1), len(quota2)) for q1, q2 in zip(quota1, quota2): p1 = q1['project'] p2 = q2['project'] r1 = q1['resources'] r2 = q2['resources'] self.assertEqual(p1.uuid, p2.uuid) self.assertItemsEqual(r1, r2) # Get the reported description of the resources. resource = Resource.objects.get(name=u"σέρβις1.ρίσορς11") desc11 = resource.report_desc resource = Resource.objects.get(name=u"σέρβις1.resource12") desc12 = resource.report_desc resource = Resource.objects.get(name=u"astakos.pending_app") desc13 = resource.report_desc # These should be the base quota of the user base_quota = [{'project': self.user.base_project, 'resources': [(desc11, '0', '100'), (desc13, '0', '3'), (desc12, '0 bytes', '1.00 kB')] }] # Test 1 - Check if get_quotas works properly for base quotas quota = get_quotas(self.user) assertQuotaEqual(quota, base_quota) # Test 2 - Check if get_quotas works properly for projects too. # # Add member to a project with transaction.atomic(): enroll_member(self.project.uuid, self.user) # These should be the additional quota from the project. new_quota = [{'project': self.project, 'resources': [(desc11, '0', '512')] }] # Assert that get_quotas works as intented quota = get_quotas(self.user) assertQuotaEqual(new_quota + base_quota, quota) # Test 3 - Check if get_quotas shows zero values for removed member # from a project. # # Remove member from project membership = ProjectMembership.objects.get(project=self.project, person=self.user) with transaction.atomic(): remove_membership(membership.id) # These should be the new quota from the project. They are zero since # the user has not used any of the resources, but they are still # displayed as the project limit is > 0. new_quota = [{'project': self.project, 'resources': [(desc11, '0', '0')] }] # Assert that get_quotas works as intented quota = get_quotas(self.user) assertQuotaEqual(new_quota + base_quota, quota)
def project_add_or_modify(request, project_uuid=None): user = request.user # only check quota for non project admin users if not user.is_project_admin(): ok, limit = check_pending_app_quota(user) if not ok: m = _(astakos_messages.PENDING_APPLICATION_LIMIT_ADD) % limit messages.error(request, m) return redirect(restrict_reverse('astakos.im.views.project_list')) project = None if project_uuid: project = get_object_or_404(Project, uuid=project_uuid) if not user.owns_project(project) and not user.is_project_admin(): m = _(astakos_messages.NOT_ALLOWED) raise PermissionDenied(m) details_fields = [ "name", "homepage", "description", "start_date", "end_date", "comments" ] membership_fields = [ "member_join_policy", "member_leave_policy", "limit_on_members_number" ] resource_catalog, resource_groups = _resources_catalog() resource_catalog_dict, resource_groups_dict = \ _resources_catalog(as_dict=True) extra_context = { 'resource_catalog': resource_catalog, 'resource_groups': resource_groups, 'resource_catalog_dict': resource_catalog_dict, 'resource_groups_dict': resource_groups_dict, 'show_form': True, 'details_fields': details_fields, 'membership_fields': membership_fields, 'object': project } with transaction.atomic(): template_name = 'im/projects/projectapplication_form.html' summary_template_name = \ 'im/projects/projectapplication_form_summary.html' success_msg = _("The project application has been received and " "is under consideration.") form_class = ProjectApplicationForm if project: template_name = 'im/projects/projectmodification_form.html' summary_template_name = \ 'im/projects/projectmodification_form_summary.html' success_msg = _("The project modification has been received and " "is under consideration.") form_class = ProjectModificationForm details_fields.remove('start_date') extra_context['edit'] = 0 if request.method == 'POST': form = form_class(request.POST, request.FILES, instance=project) if form.is_valid(): verify = request.GET.get('verify') edit = request.GET.get('edit') if verify == '1': extra_context['show_form'] = False extra_context['form_data'] = form.cleaned_data template_name = summary_template_name elif edit == '1': extra_context['show_form'] = True else: new_object = form.save() messages.success(request, success_msg, fail_silently=True) return redirect(restrict_reverse('project_list')) else: # handle terminated projects for which the name attribute # has been set to null if project and not project.name: project.name = project.realname form = form_class(instance=project) extra_context['form'] = form return render_to_response(template_name, extra_context, context_instance=RequestContext(request))