Skip to content

Commit 2b6be8d

Browse files
Merge branch 'sinfo_resource_management' of https://github.com/theam/fasrc-coldfront into sinfo_resource_management
2 parents 5d2df29 + 357cc4e commit 2b6be8d

File tree

21 files changed

+934
-79
lines changed

21 files changed

+934
-79
lines changed

.github/workflows/django.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
docker run -d --rm --name coldfront -v `pwd`:/usr/src/app \
5959
-e PLUGIN_SFTOCF=True -e SFUSER=${{ secrets.SFUSER }} -e SFPASS=${{ secrets.SFPASS }} \
6060
-e PLUGIN_IFX=True -e PLUGIN_FASRC=True -e NEO4JP=${{ secrets.NEO4JP }} \
61-
-e PLUGIN_API=True -e PLUGIN_LDAP=True \
61+
-e PLUGIN_API=True -e PLUGIN_LDAP=True -e PLUGIN_LFS=True \
6262
-e AUTH_LDAP_SERVER_URI=${{ secrets.AUTH_LDAP_SERVER_URI }} \
6363
-e AUTH_LDAP_BIND_DN=${{ secrets.AUTH_LDAP_BIND_DN }} \
6464
-e AUTH_LDAP_BIND_PASSWORD=${{ secrets.AUTH_LDAP_BIND_PASSWORD }} \

coldfront/config/plugins/lfs.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from coldfront.config.env import ENV
2+
from coldfront.config.logging import LOGGING
3+
from coldfront.config.base import INSTALLED_APPS
4+
5+
INSTALLED_APPS += ['coldfront.plugins.lfs']
6+
7+
LFS_HOST = ENV.str('LFS_HOST', default='localhost')
8+
LFS_PORT = ENV.int('LFS_PORT', default=50051)

coldfront/config/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
'PLUGIN_LDAP_USER_SEARCH': 'plugins/ldap_user_search.py',
2929
'PLUGIN_API': 'plugins/api.py',
3030
'PLUGIN_LDAP': 'plugins/ldap_fasrc.py',
31+
'PLUGIN_LFS': 'plugins/lfs.py',
3132
'PLUGIN_SFTOCF': 'plugins/sftocf.py',
3233
'PLUGIN_FASRC': 'plugins/fasrc.py',
3334
'PLUGIN_FASRC_MONITORING': 'plugins/fasrc_monitoring.py',

coldfront/core/allocation/templates/allocation/allocation_add_users.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,12 @@ <h2>Add users to allocation for project: {{allocation.project.title}}</h2>
155155
}
156156
if (q.indexOf('\n') >= 0) {
157157
$("#id_id_search_by_0_1").prop("checked", true);
158+
q = q.replace(/(?:\r\n|\r|\n)/g, ' ')
158159
}
159160

160161
var pk = "{{ pk }}"
161162
$.ajax({
162-
url : "{% url 'allocation-add-users' allocation.pk %}?search="+q, // the endpoint
163+
url : encodeURI("{% url 'allocation-add-users' allocation.pk %}?search="+q), // the endpoint
163164
type : "GET", // http method
164165
// handle a successful response
165166
success : function(data) {

coldfront/core/allocation/tests/test_views_cluster.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""View tests for cluster allocations"""
22
import logging
33

4+
import urllib.parse
45
from django.db.models import Count
56
from django.test import TestCase
67
from django.urls import reverse
@@ -155,6 +156,26 @@ def test_allocationaddusersview_access(self):
155156
utils.test_user_can_access(self, self.pi_user, self.url)
156157
utils.test_user_cannot_access(self, self.proj_allocationuser, self.url)
157158

159+
def test_allocation_non_project_user_space_separated_search(self):
160+
username_list = ['iberlin', 'gvanrossum']
161+
space_separated_search = " ".join(username_list)
162+
self.client.force_login(self.admin_user, backend="django.contrib.auth.backends.ModelBackend")
163+
url = f'/allocation/{self.cluster_allocation.pk}/add-users?search={space_separated_search}'
164+
space_separated_search_response = str(self.client.get(url).content)
165+
self.assertTrue('Found 2 matchs.' in space_separated_search_response)
166+
self.assertTrue('<td>iberlin</td>' in space_separated_search_response)
167+
self.assertTrue('<td>gvanrossum</td>' in space_separated_search_response)
168+
169+
def test_allocation_non_project_user_new_line_separated_search(self):
170+
username_list = ['iberlin', 'gvanrossum']
171+
new_line_separated_search = urllib.parse.quote_plus("\n".join(username_list))
172+
self.client.force_login(self.admin_user, backend="django.contrib.auth.backends.ModelBackend")
173+
url = f'/allocation/{self.cluster_allocation.pk}/add-users?search={new_line_separated_search}'
174+
new_line_separated_search_response = str(self.client.get(url).content)
175+
self.assertTrue('<td>iberlin</td>' in new_line_separated_search_response)
176+
self.assertTrue('<td>gvanrossum</td>' in new_line_separated_search_response)
177+
self.assertTrue('Found 2 matchs.' in new_line_separated_search_response)
178+
158179

159180
class ClusterAllocationEditUsersViewTest(ClusterAllocationViewBaseTest):
160181
"""Tests for the AllocationEditUsersView"""

coldfront/core/allocation/views.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from io import BytesIO
99
from lib2to3.fixes.fix_input import context
10+
import requests
1011

1112
from xhtml2pdf import pisa
1213

@@ -869,15 +870,26 @@ def get_users_to_add(self, allocation_obj):
869870
return users_to_add
870871

871872
def non_project_users_search(self, allocation_obj, search_term):
872-
like_filter = (Q(username__icontains=search_term) or Q(first_name__icontains=search_term) or Q(
873-
last_name__icontains=search_term) or Q(title_icontains=search_term))
874-
non_project_users = (
875-
self.get_non_project_users_to_add(allocation_obj, return_all=True)
876-
.filter(like_filter)
877-
.exclude(project=allocation_obj.project)
878-
.values('username', 'first_name', 'last_name', 'email')
879-
)
880-
return non_project_users
873+
user_list = []
874+
search_term = requests.utils.unquote(search_term)
875+
user_search = search_term.split(" ") if " " in search_term else search_term.splitlines()
876+
for username in user_search:
877+
like_filter = (
878+
Q(username__icontains=username)
879+
or Q(first_name__icontains=username)
880+
or Q(last_name__icontains=username)
881+
or Q(title_icontains=username)
882+
)
883+
884+
non_project_users = (
885+
self.get_non_project_users_to_add(allocation_obj, return_all=True)
886+
.filter(like_filter)
887+
.exclude(project=allocation_obj.project)
888+
.values('username', 'first_name', 'last_name', 'email')
889+
)
890+
user_list += non_project_users
891+
892+
return user_list
881893

882894
def search_non_project_users(self, allocation_obj, search_term, request):
883895
found_non_project_users = self.non_project_users_search(allocation_obj, search_term)

coldfront/core/portal/views.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,11 @@ def allocation_by_fos(request):
269269
def allocation_summary(request):
270270

271271
allocation_resources = [
272-
allocation.get_parent_resource.parent_resource if allocation.get_parent_resource.parent_resource else allocation.get_parent_resource for allocation in Allocation.objects.filter(status__name='Active')]
272+
allocation.get_parent_resource.parent_resource
273+
if allocation.get_parent_resource.parent_resource
274+
else allocation.get_parent_resource
275+
for allocation in Allocation.objects.filter(status__name='Active').distinct()
276+
]
273277

274278
allocations_count_by_resource = dict(Counter(allocation_resources))
275279

coldfront/core/project/templates/project/project_detail.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ <h4>Storage &nbsp;
128128
<td>{{ allocation.get_parent_resource.name }}</td>
129129
<td>{{ allocation.path }}</td>
130130
<td>{{ allocation.allocationuser_set.count }}</td>
131-
<td>{{ allocation.size|floatformat:1 }} {{ allocation.get_parent_resource.unit }}</td>
131+
<td>{{ allocation.size|floatformat:1 }} {{ allocation.get_parent_resource.quantity_label }}</td>
132132
<td>{{ allocation.usage|floatformat:1 }}</td>
133133
<td>
134134
{% if allocation.requires_payment %}
@@ -198,7 +198,7 @@ <h4>Cluster</h4>
198198
<td>{{ allocation.get_parent_resource.name }}</td>
199199
<td>{{ allocation.allocationuser_set.count }}</td>
200200
<td>
201-
{{ allocation.size|floatformat:1 }} {{ allocation.get_parent_resource.unit }}
201+
{{ allocation.size|floatformat:1 }} {{ allocation.get_parent_resource.quantity_label }}
202202
</td>
203203
<td>
204204
<a href="{% url 'allocation-detail' allocation.pk %}">

coldfront/core/resource/management/commands/add_resource_defaults.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ def handle(self, *args, **options):
2222
AttributeType.objects.get_or_create(name=attribute_type)
2323

2424
for resource_attribute_type, attribute_type in (
25+
#FASRC
2526
('capacity_tb', 'Float'),
2627
('free_tb', 'Float'),
2728
('used_tb', 'Float'),
2829
('file_count', 'Int'),
2930
('allocated_tb', 'Float'),
3031
('ChangeableAllocations', 'Yes/No'),
31-
('Core Count', 'Int'),
32+
('CPU Count', 'Int'),
3233
('GPU Count', 'Int'),
34+
('Features', 'Text'),
35+
# UBCCR
36+
('Core Count', 'Int'),
3337
# ('expiry_time', 'Int'),
3438
# ('fee_applies', 'Yes/No'),
3539
('Node Count', 'Int'),
@@ -70,7 +74,8 @@ def handle(self, *args, **options):
7074
# ('Storage', 'NAS storage'),
7175
):
7276
ResourceType.objects.get_or_create(
73-
name=resource_type, description=description
77+
name=resource_type,
78+
defaults={'description': description},
7479
)
7580

7681

coldfront/core/resource/templates/resource_detail.html

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ <h3><i class="fas fa-list" aria-hidden="true"></i> Resource Information</h3>
7979
</div>
8080

8181
<!-- Start Storage Report -->
82-
{% if resource.resource_type.name == "Storage" %}
82+
{% if resource.resource_type.name == "Storage" and user_is_manager %}
8383
<div class="card mb-3">
8484
<div class="card-header">
8585
<h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> Storage Report</h3>
@@ -91,7 +91,7 @@ <h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> Stora
9191
{% endif %}
9292

9393
<!-- Start Resource Attributes -->
94-
{% if attributes or request.user.is_superuser %}
94+
{% if user_is_manager %}
9595
<div class="card mb-3">
9696
<div class="card-header">
9797
<h3 class="d-inline"><i class="fas fa-info-circle" aria-hidden="true"></i> Resource Attributes</h3>
@@ -170,7 +170,7 @@ <h3 class="d-inline"><i class="fas fa-users" aria-hidden="true"></i> Resource Ad
170170
</div>
171171

172172
<!-- Start Resource Allocations -->
173-
{% if user_is_manager and 'Cluster' in resource.resource_type.name %}
173+
{% if user_is_manager %}
174174
<div class="card mb-3">
175175
<div class="card-header">
176176
<h3 class="d-inline"><i class="fas fa-users" aria-hidden="true"></i> Resource Allocations</h3>
@@ -179,11 +179,13 @@ <h3 class="d-inline"><i class="fas fa-users" aria-hidden="true"></i> Resource Al
179179
{# {% if user_sync_dt %}#}
180180
{# <span class="float-right">Last Sync: {{user_sync_dt}}</span>#}
181181
{# {% endif %}#}
182-
<div class="float-right">
183-
<a class="btn btn-danger" href="{% url 'resource-allocations-edit' resource.pk %}" role="button">
184-
<i class="fas fa-edit" aria-hidden="true"></i> Edit Resource Allocations
185-
</a>
186-
</div>
182+
{% if 'Cluster' in resource.resource_type.name %}
183+
<div class="float-right">
184+
<a class="btn btn-danger" href="{% url 'resource-allocations-edit' resource.pk %}" role="button">
185+
<i class="fas fa-edit" aria-hidden="true"></i> Edit Resource Allocations
186+
</a>
187+
</div>
188+
{% endif %}
187189
</div>
188190
<div class="card-body">
189191
<div class="table-responsive">
@@ -194,22 +196,28 @@ <h3 class="d-inline"><i class="fas fa-users" aria-hidden="true"></i> Resource Al
194196
<tr>
195197
<th scope="col">Project</th>
196198
<th scope="col">Users</th>
197-
<th scope="col">CPU Hours</th>
198-
<th scope="col">Percent Usage</th>
199-
<th scope="col">RawShare</th>
200-
<th scope="col">EffectvUsage <a class="info-button" title="EffectvUsage" data-toggle="popover" data-trigger="click"
201-
data-content="The fraction of the cluster the account has been granted. For more information, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."><i class="fas fa-info-circle"
202-
aria-hidden="true"></i></a>
203-
</th>
204-
<th scope="col">NormShares <a class="info-button" title="NormShares" data-toggle="popover" data-trigger="click"
205-
data-content="Calculated fairshare per user. For more information on this number, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."
206-
><i class="fas fa-info-circle" aria-hidden="true"></i></a>
207-
</th>
208-
<th scope="col">FairShare <a class="info-button" title="FairShare" data-toggle="popover" data-trigger="click"
209-
data-content="User fairshare, calculated by the equation 2^(-EffectvUsage/NormShares). For more information on fairshare calculation, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."
210-
><i class="fas fa-info-circle" aria-hidden="true"></i>
211-
</a>
212-
</th>
199+
{% if 'Cluster' in resource.resource_type.name %}
200+
<th scope="col">CPU Hours</th>
201+
<th scope="col">Percent Usage</th>
202+
<th scope="col">RawShare</th>
203+
<th scope="col">EffectvUsage <a class="info-button" title="EffectvUsage" data-toggle="popover" data-trigger="click"
204+
data-content="The fraction of the cluster the account has been granted. For more information, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."><i class="fas fa-info-circle"
205+
aria-hidden="true"></i></a>
206+
</th>
207+
<th scope="col">NormShares <a class="info-button" title="NormShares" data-toggle="popover" data-trigger="click"
208+
data-content="Calculated fairshare per user. For more information on this number, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."
209+
><i cl ass="fas fa-info-circle" aria-hidden="true"></i></a>
210+
</th>
211+
<th scope="col">FairShare <a class="info-button" title="FairShare" data-toggle="popover" data-trigger="click"
212+
data-content="User fairshare, calculated by the equation 2^(-EffectvUsage/NormShares). For more information on fairshare calculation, go <a href='https://docs.rc.fas.harvard.edu/kb/fairshare/#articleTOC_4' title='fairshare calculation'>here</a>."
213+
><i cla ss="fas fa-info-circle" aria-hidden="true"></i>
214+
</a>
215+
</th>
216+
{% elif resource.resource_type.name == 'Storage' %}
217+
<th scope="col">Path</th>
218+
<th scope="col">Quota ({{ resource.quantity_label }})</th>
219+
<th scope="col">Used ({{ resource.quantity_label }})</th>
220+
{% endif %}
213221
</tr>
214222
</thead>
215223

@@ -222,28 +230,44 @@ <h3 class="d-inline"><i class="fas fa-users" aria-hidden="true"></i> Resource Al
222230
</a>
223231
</td>
224232
<td>{{ allocation.user_count }}</td>
225-
<td data-sort="{{allocation.usage}}" name="usage">
226-
{% if allocation.usage is None %}
227-
0
228-
{% else %}
229-
{{ allocation.usage|floatformat:1 }}
230-
{% endif %}
231-
</td>
232-
<td data-sort="{{allocation.usage}}" name="usage_pct">
233-
{% if allocation.usage is None or allocation.usage == 0 %}
234-
0%
235-
{% else %}
236-
{{allocation.usage|div:total_hours|mul:100|floatformat:2 }}%
237-
{% endif %}
238-
</td>
239-
<td>{{ allocation.rawshares}}</td>
240-
<td>{{ allocation.effectvusage }}</td>
241-
<td>{{ allocation.normshares }}</td>
242-
<td>{{ allocation.fairshare }}</td>
243-
233+
{% if 'Cluster' in resource.resource_type.name %}
234+
<td data-sort="{{allocation.usage}}" name="usage">
235+
{% if allocation.usage is None %}
236+
0
237+
{% else %}
238+
{{ allocation.usage|floatformat:1 }}
239+
{% endif %}
240+
</td>
241+
<td data-sort="{{allocation.usage}}" name="usage_pct">
242+
{% if allocation.usage is None or allocation.usage == 0 %}
243+
0%
244+
{% else %}
245+
{{allocation.usage|div:total_hours|mul:100|floatformat:2 }}%
246+
{% endif %}
247+
</td>
248+
<td>{{ allocation.rawshares}}</td>
249+
<td>{{ allocation.effectvusage }}</td>
250+
<td>{{ allocation.normshares }}</td>
251+
<td>{{ allocation.fairshare }}</td>
252+
{% elif resource.resource_type.name == 'Storage' %}
253+
<td>{{ allocation.path }}</td>
254+
<td>{{ allocation.size|floatformat:2 }}</td>
255+
<td>{{ allocation.usage|floatformat:2 }}</td>
256+
{% endif %}
244257
</tr>
245258
{% endfor %}
246259
</tbody>
260+
{% if resource.resource_type.name == 'Storage' %}
261+
<tfoot>
262+
<tr style="background-color:#C2C2C2;font-weight:bold">
263+
<td>Total Tracked Storage</td>
264+
<td></td>
265+
<td></td>
266+
<td>{{ allocation_total.size|floatformat:1}}</td>
267+
<td>{{ allocation_total.usage|floatformat:1}}</td>
268+
</tr>
269+
</tfoot>
270+
{% endif %}
247271
</table>
248272
</div>
249273

0 commit comments

Comments
 (0)