Revision 75380308
b/snf-astakos-app/astakos/im/forms.py | ||
---|---|---|
66 | 66 |
MODERATION_ENABLED, PROJECT_MEMBER_JOIN_POLICIES, |
67 | 67 |
PROJECT_MEMBER_LEAVE_POLICIES, EMAILCHANGE_ENABLED, |
68 | 68 |
) |
69 |
from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
|
|
69 |
from astakos.im import presentation
|
|
70 | 70 |
from astakos.im.widgets import DummyWidget, RecaptchaWidget |
71 | 71 |
from astakos.im.functions import ( |
72 | 72 |
send_change_email, submit_application, accept_membership_checks) |
... | ... | |
878 | 878 |
d.update(dict(resource=prefix, uplimit=None)) |
879 | 879 |
append(d) |
880 | 880 |
|
881 |
ordered_keys = RESOURCES_PRESENTATION_DATA['resources_order']
|
|
881 |
ordered_keys = presentation.RESOURCES['resources_order']
|
|
882 | 882 |
policies = sorted(policies, key=lambda r:ordered_keys.index(r['str_repr'])) |
883 | 883 |
return policies |
884 | 884 |
|
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
79 | 79 |
from astakos.quotaholder.api import QH_PRACTICALLY_INFINITE |
80 | 80 |
from synnefo.lib.db.intdecimalfield import intDecimalField |
81 | 81 |
from synnefo.util.text import uenc, udec |
82 |
from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
|
|
82 |
from astakos.im import presentation
|
|
83 | 83 |
|
84 | 84 |
logger = logging.getLogger(__name__) |
85 | 85 |
|
... | ... | |
134 | 134 |
_presentation_data = {} |
135 | 135 |
def get_presentation(resource): |
136 | 136 |
global _presentation_data |
137 |
presentation = _presentation_data.get(resource, {}) |
|
138 |
if not presentation: |
|
139 |
resource_presentation = RESOURCES_PRESENTATION_DATA.get('resources', {})
|
|
140 |
presentation = resource_presentation.get(resource, {})
|
|
141 |
_presentation_data[resource] = presentation |
|
142 |
return presentation |
|
137 |
resource_presentation = _presentation_data.get(resource, {})
|
|
138 |
if not resource_presentation:
|
|
139 |
resources_presentation = presentation.RESOURCES.get('resources', {})
|
|
140 |
resource_presentation = resources_presentation.get(resource, {})
|
|
141 |
_presentation_data[resource] = resource_presentation
|
|
142 |
return resource_presentation
|
|
143 | 143 |
|
144 | 144 |
class Resource(models.Model): |
145 | 145 |
name = models.CharField(_('Name'), max_length=255, unique=True) |
b/snf-astakos-app/astakos/im/presentation.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
RESOURCES_PRESENTATION_DATA = { |
|
34 |
from astakos.im import settings |
|
35 |
|
|
36 |
RESOURCES = { |
|
35 | 37 |
'groups': { |
36 | 38 |
'compute': { |
37 | 39 |
'help_text': ('Compute resources ' |
... | ... | |
55 | 57 |
}, |
56 | 58 |
}, |
57 | 59 |
'resources': { |
58 |
'pithos+.diskspace': {
|
|
60 |
'pithos.diskspace': { |
|
59 | 61 |
'help_text': ('This is the space on Pithos for storing files ' |
60 | 62 |
'and VM Images. '), |
61 | 63 |
'help_text_input_each': ('This is the total amount of space on ' |
... | ... | |
65 | 67 |
'report_desc': 'Storage Space', |
66 | 68 |
'placeholder': 'eg. 10GB', |
67 | 69 |
'verbose_name': 'Storage Space', |
70 |
'group': 'storage' |
|
68 | 71 |
}, |
69 | 72 |
'cyclades.disk': { |
70 | 73 |
'help_text': ('This is the System Disk that the VMs have that ' |
... | ... | |
77 | 80 |
'is_abbreviation': False, |
78 | 81 |
'report_desc': 'System Disk', |
79 | 82 |
'placeholder': 'eg. 5GB, 2GB etc', |
80 |
'verbose_name': 'System Disk' |
|
83 |
'verbose_name': 'System Disk', |
|
84 |
'group': 'compute' |
|
81 | 85 |
}, |
82 | 86 |
'cyclades.ram': { |
83 | 87 |
'help_text': 'RAM used by VMs ', |
... | ... | |
87 | 91 |
'is_abbreviation': True, |
88 | 92 |
'report_desc': 'RAM', |
89 | 93 |
'placeholder': 'eg. 4GB', |
90 |
'verbose_name': 'ram' |
|
94 |
'verbose_name': 'ram', |
|
95 |
'group': 'compute' |
|
96 |
|
|
91 | 97 |
}, |
92 | 98 |
'cyclades.cpu': { |
93 | 99 |
'help_text': 'CPUs used by VMs ', |
... | ... | |
97 | 103 |
'is_abbreviation': True, |
98 | 104 |
'report_desc': 'CPUs', |
99 | 105 |
'placeholder': 'eg. 1', |
100 |
'verbose_name': 'cpu' |
|
106 |
'verbose_name': 'cpu', |
|
107 |
'group': 'compute' |
|
108 |
|
|
101 | 109 |
}, |
102 | 110 |
'cyclades.vm': { |
103 | 111 |
'help_text': ('These are the VMs one can create on the ' |
... | ... | |
109 | 117 |
'report_desc': 'Virtual Machines', |
110 | 118 |
'placeholder': 'eg. 2', |
111 | 119 |
'verbose_name': 'vm', |
120 |
'group': 'compute' |
|
121 |
|
|
112 | 122 |
}, |
113 | 123 |
'cyclades.network.private': { |
114 | 124 |
'help_text': ('These are the Private Networks one can create on ' |
... | ... | |
119 | 129 |
'is_abbreviation': False, |
120 | 130 |
'report_desc': 'Private Networks', |
121 | 131 |
'placeholder': 'eg. 1', |
122 |
'verbose_name': 'Private Network' |
|
132 |
'verbose_name': 'Private Network', |
|
133 |
'group': 'network' |
|
134 |
|
|
123 | 135 |
} |
124 | 136 |
}, |
125 | 137 |
'groups_order': ['storage', 'compute', 'network'], |
126 |
'resources_order': ['pithos+.diskspace',
|
|
138 |
'resources_order': ['pithos.diskspace', |
|
127 | 139 |
'cyclades.disk', |
128 | 140 |
'cyclades.cpu', |
129 | 141 |
'cyclades.ram', |
b/snf-astakos-app/astakos/im/resources.py | ||
---|---|---|
54 | 54 |
old_uplimit = None |
55 | 55 |
|
56 | 56 |
r.uplimit = uplimit |
57 |
r.service = s |
|
57 |
r.service = service
|
|
58 | 58 |
for key, value in resource.iteritems(): |
59 | 59 |
setattr(r, key, value) |
60 | 60 |
|
b/snf-astakos-app/astakos/im/templates/im/projects/projectapplication_form.html | ||
---|---|---|
113 | 113 |
</span> |
114 | 114 |
</legend> |
115 | 115 |
<ul class="clearfix"> |
116 |
{% with object|resource_groups as groups %}
|
|
116 |
{% with object|resource_groups as groups %}
|
|
117 | 117 |
{% for g, group_info in resource_groups.items %} |
118 | 118 |
{% if g %} |
119 | 119 |
<li> |
120 |
<a href="#{{ g }}" id="{{'group_'|add:g}}" {% if g in groups %}class="selected"{% endif %}><img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a> |
|
121 |
<input type="hidden" name="proxy_{{ 'is_selected_'|add:g }}" id="proxy_{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}> |
|
120 |
<a href="#{{ g }}" |
|
121 |
id="{{'group_'|add:g}}" |
|
122 |
{% if g in groups %}class="selected"{% endif %}> |
|
123 |
<img src="/static/im/images/create-{{ g }}.png" alt="vm"/></a> |
|
124 |
<input type="hidden" name="proxy_{{ 'is_selected_'|add:g }}" |
|
125 |
id="proxy_{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}> |
|
122 | 126 |
<input type="hidden" name="{{ 'is_selected_'|add:g }}" id="{{ 'id_is_selected_'|add:g }}" {% if g in groups %}value="1"{% else %}value="0"{% endif %}> |
123 | 127 |
<p class="msg">{{ group_info.help_text }}</p> |
124 | 128 |
</li> |
... | ... | |
132 | 136 |
<div class="not-visible"> |
133 | 137 |
{% for gname, resources in resource_catalog %} |
134 | 138 |
<div class="group {{'group_'|add:gname}}" id="{{ gname }}"> |
135 |
<a href="#icons" class="delete">X remove resource</a>
|
|
139 |
<a href="#icons" class="delete">X remove resource</a>
|
|
136 | 140 |
{% for rdata in resources %} |
137 | 141 |
{% with rdata.str_repr as rname %} |
138 | 142 |
<fieldset class="quota"> |
... | ... | |
167 | 171 |
{% else %} |
168 | 172 |
value = "{% get_grant_value rname form %}" |
169 | 173 |
{% endif %} |
170 |
autocomplete="off" |
|
171 |
> |
|
174 |
autocomplete="off"> |
|
172 | 175 |
<span class="extra-img"> </span> |
173 | 176 |
<span class="info"><em>more info</em><span>{{ rdata.help_text_input_each }}</span></span> |
174 | 177 |
</p> |
b/snf-astakos-app/astakos/im/views.py | ||
---|---|---|
109 | 109 |
PAGINATE_BY_ALL, |
110 | 110 |
ACTIVATION_REDIRECT_URL, |
111 | 111 |
MODERATION_ENABLED) |
112 |
from astakos.im.presentation import RESOURCES_PRESENTATION_DATA
|
|
112 |
from astakos.im import presentation
|
|
113 | 113 |
from astakos.im.api import get_services_dict |
114 | 114 |
from astakos.im import settings as astakos_settings |
115 | 115 |
from astakos.im.api.callpoint import AstakosCallpoint |
... | ... | |
1042 | 1042 |
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname)) |
1043 | 1043 |
return response |
1044 | 1044 |
|
1045 |
|
|
1046 |
def _resources_catalog(request): |
|
1047 |
""" |
|
1048 |
`resource_catalog` contains a list of tuples. Each tuple contains the group |
|
1049 |
key the resource is assigned to and resources list of dicts that contain |
|
1050 |
resource information. |
|
1051 |
`resource_groups` contains information about the groups |
|
1052 |
""" |
|
1053 |
# presentation data |
|
1054 |
resource_groups = presentation.RESOURCES.get('groups', {}) |
|
1055 |
resource_catalog = () |
|
1056 |
|
|
1057 |
# resources in database |
|
1058 |
result = callpoint.list_resources() |
|
1059 |
if not result.is_success: |
|
1060 |
messages.error(request, 'Unable to retrieve system resources: %s' % |
|
1061 |
result.reason) |
|
1062 |
else: |
|
1063 |
# initialize resource_catalog to contain all group/resource information |
|
1064 |
for r in result.data: |
|
1065 |
if not r.get('group') in resource_groups: |
|
1066 |
resource_groups[r.get('group')] = {'icon': 'unknown'} |
|
1067 |
|
|
1068 |
resource_keys = [r.get('str_repr') for r in result.data] |
|
1069 |
resource_catalog = [[g, filter(lambda r: r.get('group', '') == g, |
|
1070 |
result.data)] for g in resource_groups] |
|
1071 |
|
|
1072 |
# order groups, also include unknown groups |
|
1073 |
groups_order = presentation.RESOURCES.get('groups_order') |
|
1074 |
for g in resource_groups.keys(): |
|
1075 |
if not g in groups_order: |
|
1076 |
groups_order.append(g) |
|
1077 |
|
|
1078 |
# order resources, also include unknown resources |
|
1079 |
resources_order = presentation.RESOURCES.get('resources_order') |
|
1080 |
for r in resource_keys: |
|
1081 |
if not r in resources_order: |
|
1082 |
resources_order.append(r) |
|
1083 |
|
|
1084 |
# sort catalog groups |
|
1085 |
resource_catalog = sorted(resource_catalog, |
|
1086 |
key=lambda g: groups_order.index(g[0])) |
|
1087 |
|
|
1088 |
# sort groups |
|
1089 |
def groupindex(g): |
|
1090 |
return groups_order.index(g[0]) |
|
1091 |
resource_groups_list = sorted([(k, v) for k, v in resource_groups.items()], |
|
1092 |
key=groupindex) |
|
1093 |
resource_groups = OrderedDict(resource_groups_list) |
|
1094 |
|
|
1095 |
# sort resources |
|
1096 |
def resourceindex(r): |
|
1097 |
return resources_order.index(r['str_repr']) |
|
1098 |
for index, group in enumerate(resource_catalog): |
|
1099 |
resource_catalog[index][1] = sorted(resource_catalog[index][1], |
|
1100 |
key=resourceindex) |
|
1101 |
if len(resource_catalog[index][1]) == 0: |
|
1102 |
resource_catalog.pop(index) |
|
1103 |
for gindex, g in enumerate(resource_groups): |
|
1104 |
if g[0] == group[0]: |
|
1105 |
resource_groups.pop(gindex) |
|
1106 |
|
|
1107 |
return resource_catalog, resource_groups |
|
1108 |
|
|
1109 |
|
|
1045 | 1110 |
@require_http_methods(["GET", "POST"]) |
1046 | 1111 |
@valid_astakos_user_required |
1047 | 1112 |
def project_add(request): |
1048 |
|
|
1049 | 1113 |
user = request.user |
1050 | 1114 |
reached, limit = reached_pending_application_limit(user.id) |
1051 | 1115 |
if not user.is_project_admin() and reached: |
... | ... | |
1055 | 1119 |
next = restrict_next(next, domain=COOKIE_DOMAIN) |
1056 | 1120 |
return redirect(next) |
1057 | 1121 |
|
1058 |
resource_groups = RESOURCES_PRESENTATION_DATA.get('groups', {}) |
|
1059 |
resource_catalog = () |
|
1060 |
result = callpoint.list_resources() |
|
1061 |
details_fields = [ |
|
1062 |
"name", "homepage", "description","start_date","end_date", "comments"] |
|
1063 |
membership_fields =[ |
|
1064 |
"member_join_policy", "member_leave_policy", "limit_on_members_number"] |
|
1065 |
if not result.is_success: |
|
1066 |
messages.error( |
|
1067 |
request, |
|
1068 |
'Unable to retrieve system resources: %s' % result.reason |
|
1069 |
) |
|
1070 |
else: |
|
1071 |
resource_catalog = [ |
|
1072 |
[g, filter(lambda r: r.get('group', '') == g, result.data)] \ |
|
1073 |
for g in resource_groups] |
|
1074 |
|
|
1075 |
# order resources |
|
1076 |
groups_order = RESOURCES_PRESENTATION_DATA.get('groups_order') |
|
1077 |
resources_order = RESOURCES_PRESENTATION_DATA.get('resources_order') |
|
1078 |
resource_catalog = sorted(resource_catalog, key=lambda g:groups_order.index(g[0])) |
|
1079 |
|
|
1080 |
resource_groups_list = sorted([(k,v) for k,v in resource_groups.items()], |
|
1081 |
key=lambda f:groups_order.index(f[0])) |
|
1082 |
resource_groups = OrderedDict(resource_groups_list) |
|
1083 |
for index, group in enumerate(resource_catalog): |
|
1084 |
resource_catalog[index][1] = sorted(resource_catalog[index][1], |
|
1085 |
key=lambda r: resources_order.index(r['str_repr'])) |
|
1086 |
|
|
1087 |
|
|
1122 |
details_fields = ["name", "homepage", "description", "start_date", |
|
1123 |
"end_date", "comments"] |
|
1124 |
membership_fields = ["member_join_policy", "member_leave_policy", |
|
1125 |
"limit_on_members_number"] |
|
1126 |
resource_catalog, resource_groups = _resources_catalog(request) |
|
1088 | 1127 |
extra_context = { |
1089 |
'resource_catalog':resource_catalog, |
|
1090 |
'resource_groups':resource_groups, |
|
1091 |
'show_form':True, |
|
1092 |
'details_fields':details_fields, |
|
1093 |
'membership_fields':membership_fields} |
|
1128 |
'resource_catalog': resource_catalog,
|
|
1129 |
'resource_groups': resource_groups,
|
|
1130 |
'show_form': True,
|
|
1131 |
'details_fields': details_fields,
|
|
1132 |
'membership_fields': membership_fields}
|
|
1094 | 1133 |
|
1095 | 1134 |
response = None |
1096 | 1135 |
with ExceptionHandler(request): |
... | ... | |
1186 | 1225 |
next = restrict_next(next, domain=COOKIE_DOMAIN) |
1187 | 1226 |
return redirect(next) |
1188 | 1227 |
|
1189 |
resource_groups = RESOURCES_PRESENTATION_DATA.get('groups', {}) |
|
1190 |
resource_catalog = () |
|
1191 |
result = callpoint.list_resources() |
|
1192 |
details_fields = [ |
|
1193 |
"name", "homepage", "description","start_date","end_date", "comments"] |
|
1194 |
membership_fields =[ |
|
1195 |
"member_join_policy", "member_leave_policy", "limit_on_members_number"] |
|
1196 |
if not result.is_success: |
|
1197 |
messages.error( |
|
1198 |
request, |
|
1199 |
'Unable to retrieve system resources: %s' % result.reason |
|
1200 |
) |
|
1201 |
else: |
|
1202 |
resource_catalog = [ |
|
1203 |
(g, filter(lambda r: r.get('group', '') == g, result.data)) \ |
|
1204 |
for g in resource_groups] |
|
1228 |
details_fields = ["name", "homepage", "description", "start_date", |
|
1229 |
"end_date", "comments"] |
|
1230 |
membership_fields = ["member_join_policy", "member_leave_policy", |
|
1231 |
"limit_on_members_number"] |
|
1232 |
resource_catalog, resource_groups = _resources_catalog(request) |
|
1205 | 1233 |
extra_context = { |
1206 |
'resource_catalog':resource_catalog, |
|
1207 |
'resource_groups':resource_groups, |
|
1208 |
'show_form':True, |
|
1209 |
'details_fields':details_fields, |
|
1234 |
'resource_catalog': resource_catalog,
|
|
1235 |
'resource_groups': resource_groups,
|
|
1236 |
'show_form': True,
|
|
1237 |
'details_fields': details_fields,
|
|
1210 | 1238 |
'update_form': True, |
1211 |
'membership_fields':membership_fields} |
|
1239 |
'membership_fields': membership_fields |
|
1240 |
} |
|
1212 | 1241 |
|
1213 | 1242 |
response = None |
1214 | 1243 |
with ExceptionHandler(request): |
1215 |
response =_update_object( |
|
1244 |
response = _update_object(
|
|
1216 | 1245 |
request, |
1217 | 1246 |
object_id=application_id, |
1218 | 1247 |
template_name='im/projects/projectapplication_form.html', |
1219 |
extra_context=extra_context, post_save_redirect=reverse('project_list'), |
|
1248 |
extra_context=extra_context, |
|
1249 |
post_save_redirect=reverse('project_list'), |
|
1220 | 1250 |
form_class=ProjectApplicationForm, |
1221 |
msg = _("The %(verbose_name)s has been received and " |
|
1222 |
"is under consideration."), |
|
1223 |
) |
|
1251 |
msg=_("The %(verbose_name)s has been received and is under " |
|
1252 |
"consideration.")) |
|
1224 | 1253 |
|
1225 | 1254 |
if response is not None: |
1226 | 1255 |
return response |
Also available in: Unified diff