1 # Copyright (C) 2010 Oregon State University et al.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 from itertools
import chain
, izip
, repeat
20 from django
.conf
import settings
21 from django
.contrib
.auth
.decorators
import login_required
22 from django
.contrib
.contenttypes
.models
import ContentType
23 from django
.db
.models
import Q
, Count
24 from django
.http
import HttpResponse
, HttpResponseForbidden
25 from django
.shortcuts
import render_to_response
, get_object_or_404
26 from django
.template
import RequestContext
27 from django
.utils
import simplejson
as json
28 from django
.views
.generic
.base
import TemplateView
30 from object_permissions
import get_users_any
32 from ganeti_web
.backend
.queries
import vm_qs_for_admins
33 from ganeti_web
.middleware
import Http403
34 from ganeti_web
.models
import Cluster
, VirtualMachine
, Job
, GanetiError
, \
35 ClusterUser
, Profile
, Organization
, SSHKey
36 from ganeti_web
.views
import render_404
37 from ganeti_web
.views
.generic
import NO_PRIVS
38 from django
.utils
.translation
import ugettext
as _
39 from ganeti_web
.constants
import VERSION
42 class AboutView(TemplateView
):
44 template_name
= "ganeti/about.html"
46 def render_to_response(self
, context
, **kwargs
):
47 context
["version"] = VERSION
48 return super(AboutView
, self
).render_to_response(context
, **kwargs
)
51 def merge_errors(errors
, jobs
):
53 Merge iterables of errors and jobs together.
55 The resulting list contains tuples of (bool, object) where the first
56 member indicates whether the object is a ``GanetiError`` or ``Job``.
61 Either the "finished" or "timestamp" attribute.
64 return getattr(x
[1], "finished", getattr(x
[1], "timestamp", 0))
66 i
= chain(izip(repeat(True), errors
), izip(repeat(False), jobs
))
67 return list(sorted(i
, key
=keyfunc
))
70 USED_NOTHING
= dict(disk
=0, ram
=0, virtual_cpus
=0)
74 def get_errors(request
):
75 """ Returns all errors that have ever been generated for clusters/vms
76 and then sends them to the errors page.
81 clusters
= Cluster
.objects
.all()
83 clusters
= user
.get_objects_all_perms(Cluster
, ['admin', ])
84 admin
= user
.is_superuser
or clusters
86 # Get all of the PKs from VMs that this user may administer.
87 vms
= vm_qs_for_admins(user
).values("pk")
89 # build list of job errors. Include jobs from any vm the user has access
91 # If the user has admin on any cluster then those clusters and it's objects
92 # must be included too.
94 # XXX all jobs have the cluster listed, filtering by cluster includes jobs
95 # for both the cluster itself and any of its VMs or Nodes
96 error_clause
= Q(status
='error')
97 vm_type
= ContentType
.objects
.get_for_model(VirtualMachine
)
98 select_clause
= Q(content_type
=vm_type
, object_id__in
=vms
)
100 select_clause |
= Q(cluster__in
=clusters
)
101 job_errors
= Job
.objects
.filter(error_clause
& select_clause
)
103 # Build the list of job errors. Include jobs from any VMs for which the
105 qs
= GanetiError
.objects
106 ganeti_errors
= qs
.get_errors(obj
=vms
)
107 # If the user is an admin on any cluster, then include administrated
108 # clusters and related objects.
110 ganeti_errors |
= qs
.get_errors(obj
=clusters
)
113 errors
= merge_errors(ganeti_errors
, job_errors
)
115 return render_to_response("ganeti/errors.html",
118 'cluster_list': clusters
,
119 'user': request
.user
,
122 context_instance
=RequestContext(request
))
125 def get_used_resources(cluster_user
):
126 """ help function for querying resources used for a given cluster_user """
128 owned_vms
= cluster_user
.virtual_machines
.all()
129 used
= cluster_user
.used_resources()
130 clusters
= cluster_user
.permissable
.get_objects_any_perms(Cluster
)
131 quotas
= Cluster
.get_quotas(clusters
, cluster_user
)
133 for cluster
, quota
in quotas
.items():
134 resources
[cluster
] = {
135 "used": used
.pop(cluster
.id)
136 if cluster
.id in used
else USED_NOTHING
,
139 resources
[cluster
]["total"] = owned_vms
.filter(cluster
=cluster
).count()
140 resources
[cluster
]["running"] = owned_vms \
141 .filter(cluster
=cluster
, status
="running").count()
143 # add any clusters that have used resources
144 # but no perms (and thus no quota)
145 # since we know they don't have a custom quota just add the default quota
147 for cluster
in Cluster
.objects
.filter(pk__in
=used
):
148 resources
[cluster
] = {"used": used
[cluster
.id],
149 "set": cluster
.get_default_quota()}
150 resources
[cluster
]["total"] = owned_vms \
151 .filter(cluster
=cluster
).count()
152 resources
[cluster
]["running"] = owned_vms \
153 .filter(cluster
=cluster
, status
="running").count()
158 def get_vm_counts(clusters
):
160 Helper for getting the list of orphaned/ready to import/missing VMs.
162 @param clusters the list of clusters, for which numbers of VM are counted.
163 May be None, if update is set.
165 format_key
= 'cluster_admin_%d'
166 orphaned
= import_ready
= missing
= 0
168 # update the values that were not cached
169 if clusters
.exists():
170 annotated
= VirtualMachine
.objects \
171 .filter(cluster__in
=clusters
,
172 owner
=None).order_by().values("cluster__pk") \
173 .annotate(orphaned
=Count("id"))
177 result
[format_key
% i
["cluster__pk"]] = {"orphaned": i
["orphaned"]}
178 orphaned
+= i
["orphaned"]
179 for cluster
in clusters
:
180 key
= format_key
% cluster
.pk
182 if key
not in result
:
183 result
[key
] = {"orphaned": 0}
185 result
[key
]["import_ready"] = len(cluster
.missing_in_db
)
186 result
[key
]["missing"] = len(cluster
.missing_in_ganeti
)
188 import_ready
+= result
[key
]["import_ready"]
189 missing
+= result
[key
]["missing"]
191 return orphaned
, import_ready
, missing
195 def overview(request
, rest
=False):
201 if user
.is_superuser
:
202 clusters
= Cluster
.objects
.all()
204 clusters
= user
.get_objects_any_perms(Cluster
,
205 ['admin', 'create_vm', ])
206 admin
= user
.is_superuser
or clusters
208 #orphaned, ready to import, missing
210 # build list of admin tasks for this user's clusters
211 orphaned
, import_ready
, missing
= get_vm_counts(clusters
)
213 orphaned
= import_ready
= missing
= 0
215 # Get all of the PKs from VMs that this user may administer.
216 vms
= vm_qs_for_admins(user
).values("pk")
218 # get vm summary - running and totals need to be done as separate queries
219 # and then merged into a single list
220 vms_running
= vms
.filter(status
='running') \
222 .values('cluster__hostname', 'cluster__slug') \
223 .annotate(running
=Count('pk'))
224 vms_total
= vms
.order_by()\
225 .values('cluster__hostname', 'cluster__slug') \
226 .annotate(total
=Count('pk'))
228 for cluster
in vms_total
:
229 vm_summary
[cluster
.pop('cluster__hostname')] = cluster
230 for cluster
in vms_running
:
231 vm_summary
[cluster
['cluster__hostname']]['running'] = \
234 # get list of personas for the user: All groups, plus the user.
235 # include the user only if it owns a vm or has
236 # perms on at least one cluster
237 profile
= user
.get_profile()
238 personas
= list(Organization
.objects
.filter(group__user
=user
))
239 if profile
.virtual_machines
.count() \
240 or user
.has_any_perms(Cluster
, ['admin', 'create_vm'], groups
=False) \
242 personas
.insert(0, profile
)
244 # get resources used per cluster from the first persona in the list
245 resources
= get_used_resources(personas
[0])
247 create_vm
= user
.has_perm('create_vm', clusters
)
252 return render_to_response("ganeti/overview.html", {
254 'cluster_list': clusters
,
255 'create_vm': create_vm
,
256 'user': request
.user
,
257 'orphaned': orphaned
,
258 'import_ready': import_ready
,
260 'resources': resources
,
261 'vm_summary': vm_summary
,
262 'personas': personas
,
264 context_instance
=RequestContext(request
),
269 def used_resources(request
, rest
=False):
270 """ view for returning used resources for a given cluster user """
272 cluster_user_id
= request
.GET
['id']
274 return render_404(request
, 'requested user was not found')
275 cu
= get_object_or_404(ClusterUser
, pk
=cluster_user_id
)
277 # must be a super user, the user in question, or a member of the group
279 if not user
.is_superuser
:
280 user_type
= ContentType
.objects
.get_for_model(Profile
)
281 if cu
.real_type_id
== user_type
.pk
:
282 if not Profile
.objects
.filter(clusteruser_ptr
=cu
.pk
, user
=user
) \
284 raise Http403(_('You are not authorized to view this page'))
286 if not Organization
.objects
.filter(clusteruser_ptr
=cu
.pk
,
287 group__user
=user
).exists():
288 raise Http403(_('You are not authorized to view this page'))
290 resources
= get_used_resources(cu
.cast())
294 return render_to_response("ganeti/overview/used_resources.html", {
295 'resources': resources
296 }, context_instance
=RequestContext(request
))
300 def clear_ganeti_error(request
, pk
):
302 Clear a single error message
305 error
= get_object_or_404(GanetiError
, pk
=pk
)
308 # if not a superuser, check permissions on the object itself
309 if not user
.is_superuser
:
310 if isinstance(obj
, (Cluster
,)) and not user
.has_perm('admin', obj
):
311 raise Http403(NO_PRIVS
)
312 elif isinstance(obj
, (VirtualMachine
,)):
313 # object is a virtual machine, check perms on VM and on Cluster
314 if not (obj
.owner_id
== user
.get_profile().pk
or
315 user
.has_perm('admin', obj
.cluster
)):
316 raise Http403(NO_PRIVS
)
319 GanetiError
.objects
.filter(pk
=error
.pk
).update(cleared
=True)
321 return HttpResponse('1', mimetype
='application/json')
324 def ssh_keys(request
, api_key
):
325 """ Lists all keys for all clusters managed by GWM """
327 Show all ssh keys which belong to users, who have any perms on the cluster
329 if settings
.WEB_MGR_API_KEY
!= api_key
:
330 return HttpResponseForbidden(_("You're not allowed to view keys."))
333 for cluster
in Cluster
.objects
.all():
334 users
= users
.union(set(get_users_any(cluster
)
335 .values_list("id", flat
=True)))
336 for vm
in VirtualMachine
.objects
.all():
337 users
= users
.union(set(get_users_any(vm
)
338 .values_list('id', flat
=True)))
340 keys
= SSHKey
.objects \
341 .filter(Q(user__in
=users
) |
Q(user__is_superuser
=True)) \
342 .values_list('key', 'user__username')\
343 .order_by('user__username')
345 keys_list
= list(keys
)
346 return HttpResponse(json
.dumps(keys_list
), mimetype
="application/json")