Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / helpdesk / views.py @ 3e749401

History | View | Annotate | Download (9.7 kB)

1 605d23bf Kostas Papadimitriou
# Copyright 2012 GRNET S.A. All rights reserved.
2 605d23bf Kostas Papadimitriou
#
3 605d23bf Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 605d23bf Kostas Papadimitriou
# without modification, are permitted provided that the following
5 605d23bf Kostas Papadimitriou
# conditions are met:
6 605d23bf Kostas Papadimitriou
#
7 605d23bf Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 605d23bf Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 605d23bf Kostas Papadimitriou
#      disclaimer.
10 605d23bf Kostas Papadimitriou
#
11 605d23bf Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 605d23bf Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 605d23bf Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 605d23bf Kostas Papadimitriou
#      provided with the distribution.
15 605d23bf Kostas Papadimitriou
#
16 605d23bf Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 605d23bf Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 605d23bf Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 605d23bf Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 605d23bf Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 605d23bf Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 605d23bf Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 605d23bf Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 605d23bf Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 605d23bf Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 605d23bf Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 605d23bf Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 605d23bf Kostas Papadimitriou
#
29 605d23bf Kostas Papadimitriou
# The views and conclusions contained in the software and
30 605d23bf Kostas Papadimitriou
# documentation are those of the authors and should not be
31 605d23bf Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 605d23bf Kostas Papadimitriou
# or implied, of GRNET S.A.
33 605d23bf Kostas Papadimitriou
34 d59d86d4 Kostas Papadimitriou
import re
35 605d23bf Kostas Papadimitriou
import logging
36 d59d86d4 Kostas Papadimitriou
37 6c45fa84 Kostas Papadimitriou
from django.shortcuts import redirect
38 c3564ce9 Kostas Papadimitriou
from django.views.generic.simple import direct_to_template
39 e129e26b Kostas Papadimitriou
from django.conf import settings
40 e129e26b Kostas Papadimitriou
from django.core.exceptions import PermissionDenied
41 6c45fa84 Kostas Papadimitriou
from django.http import Http404, HttpResponseRedirect
42 605d23bf Kostas Papadimitriou
from django.core.urlresolvers import reverse
43 605d23bf Kostas Papadimitriou
44 ebda2ad4 Kostas Papadimitriou
from urllib import unquote
45 c3564ce9 Kostas Papadimitriou
46 04a1b675 Christos Stavrakakis
from snf_django.lib.astakos import get_user
47 6c45fa84 Kostas Papadimitriou
from synnefo.db.models import VirtualMachine, NetworkInterface, Network
48 468ba00b Ilias Tsitsimpis
from astakosclient import AstakosClient
49 e129e26b Kostas Papadimitriou
50 cc5c8608 Kostas Papadimitriou
# server actions specific imports
51 cc5c8608 Kostas Papadimitriou
from synnefo.api import servers
52 cc5c8608 Kostas Papadimitriou
from synnefo.logic import backend as servers_backend
53 cc5c8608 Kostas Papadimitriou
54 605d23bf Kostas Papadimitriou
logger = logging.getLogger(__name__)
55 605d23bf Kostas Papadimitriou
56 d59d86d4 Kostas Papadimitriou
IP_SEARCH_REGEX = re.compile('([0-9]+)(?:\.[0-9]+){3}')
57 6c45fa84 Kostas Papadimitriou
UUID_SEARCH_REGEX = re.compile('([0-9a-z]{8}-([0-9a-z]{4}-){3}[0-9a-z]{12})')
58 ba8ff608 Kostas Papadimitriou
VM_SEARCH_REGEX = re.compile('vm(-){0,}(?P<vmid>[0-9]+)')
59 6c45fa84 Kostas Papadimitriou
60 6c45fa84 Kostas Papadimitriou
61 ebda2ad4 Kostas Papadimitriou
def get_token_from_cookie(request, cookiename):
62 ebda2ad4 Kostas Papadimitriou
    """
63 ebda2ad4 Kostas Papadimitriou
    Extract token from the cookie name provided. Cookie should be in the same
64 ebda2ad4 Kostas Papadimitriou
    form as astakos service sets its cookie contents::
65 ebda2ad4 Kostas Papadimitriou

66 ebda2ad4 Kostas Papadimitriou
        <user_uniq>|<user_token>
67 ebda2ad4 Kostas Papadimitriou
    """
68 ebda2ad4 Kostas Papadimitriou
    try:
69 ebda2ad4 Kostas Papadimitriou
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
70 ebda2ad4 Kostas Papadimitriou
        return cookie_content.split("|")[1]
71 ebda2ad4 Kostas Papadimitriou
    except AttributeError:
72 ebda2ad4 Kostas Papadimitriou
        pass
73 ebda2ad4 Kostas Papadimitriou
74 ebda2ad4 Kostas Papadimitriou
    return None
75 ebda2ad4 Kostas Papadimitriou
76 9fd36718 Kostas Papadimitriou
77 6c45fa84 Kostas Papadimitriou
AUTH_COOKIE_NAME = getattr(settings, 'HELPDESK_AUTH_COOKIE_NAME',
78 6c45fa84 Kostas Papadimitriou
                           getattr(settings, 'UI_AUTH_COOKIE_NAME',
79 6c45fa84 Kostas Papadimitriou
                                   '_pithos2_a'))
80 6c45fa84 Kostas Papadimitriou
PERMITTED_GROUPS = getattr(settings, 'HELPDESK_PERMITTED_GROUPS', ['helpdesk'])
81 605d23bf Kostas Papadimitriou
SHOW_DELETED_VMS = getattr(settings, 'HELPDESK_SHOW_DELETED_VMS', False)
82 605d23bf Kostas Papadimitriou
83 605d23bf Kostas Papadimitriou
84 605d23bf Kostas Papadimitriou
def token_check(func):
85 605d23bf Kostas Papadimitriou
    """
86 605d23bf Kostas Papadimitriou
    Mimic csrf security check using user auth token.
87 605d23bf Kostas Papadimitriou
    """
88 605d23bf Kostas Papadimitriou
    def wrapper(request, *args, **kwargs):
89 605d23bf Kostas Papadimitriou
        if not hasattr(request, 'user'):
90 605d23bf Kostas Papadimitriou
            raise PermissionDenied
91 605d23bf Kostas Papadimitriou
92 605d23bf Kostas Papadimitriou
        token = request.POST.get('token', None)
93 4edee820 Kostas Papadimitriou
        if token and token == request.user.get('auth_token', None):
94 605d23bf Kostas Papadimitriou
            return func(request, *args, **kwargs)
95 605d23bf Kostas Papadimitriou
96 605d23bf Kostas Papadimitriou
        raise PermissionDenied
97 605d23bf Kostas Papadimitriou
98 605d23bf Kostas Papadimitriou
    return wrapper
99 9fd36718 Kostas Papadimitriou
100 e129e26b Kostas Papadimitriou
101 605d23bf Kostas Papadimitriou
def helpdesk_user_required(func, permitted_groups=PERMITTED_GROUPS):
102 e129e26b Kostas Papadimitriou
    """
103 e129e26b Kostas Papadimitriou
    Django view wrapper that checks if identified request user has helpdesk
104 e129e26b Kostas Papadimitriou
    permissions (exists in helpdesk group)
105 e129e26b Kostas Papadimitriou
    """
106 e129e26b Kostas Papadimitriou
    def wrapper(request, *args, **kwargs):
107 9fd36718 Kostas Papadimitriou
        HELPDESK_ENABLED = getattr(settings, 'HELPDESK_ENABLED', True)
108 9fd36718 Kostas Papadimitriou
        if not HELPDESK_ENABLED:
109 9fd36718 Kostas Papadimitriou
            raise Http404
110 9fd36718 Kostas Papadimitriou
111 605d23bf Kostas Papadimitriou
        token = get_token_from_cookie(request, AUTH_COOKIE_NAME)
112 468ba00b Ilias Tsitsimpis
        get_user(request, settings.ASTAKOS_URL,
113 468ba00b Ilias Tsitsimpis
                 fallback_token=token, logger=logger)
114 9e3519e0 Kostas Papadimitriou
        if hasattr(request, 'user') and request.user:
115 e129e26b Kostas Papadimitriou
            groups = request.user.get('groups', [])
116 0e5c88d0 Kostas Papadimitriou
117 0e5c88d0 Kostas Papadimitriou
            if not groups:
118 4edee820 Kostas Papadimitriou
                logger.error("Failed to access helpdesk view %r",
119 4edee820 Kostas Papadimitriou
                             request.user_uniq)
120 0e5c88d0 Kostas Papadimitriou
                raise PermissionDenied
121 0e5c88d0 Kostas Papadimitriou
122 605d23bf Kostas Papadimitriou
            has_perm = False
123 e129e26b Kostas Papadimitriou
            for g in groups:
124 605d23bf Kostas Papadimitriou
                if g in permitted_groups:
125 605d23bf Kostas Papadimitriou
                    has_perm = True
126 605d23bf Kostas Papadimitriou
127 605d23bf Kostas Papadimitriou
            if not has_perm:
128 4edee820 Kostas Papadimitriou
                logger.error("Failed to access helpdesk view %r. No valid "
129 475e8578 Kostas Papadimitriou
                             "helpdesk group (%r) matches user groups (%r)",
130 475e8578 Kostas Papadimitriou
                             request.user_uniq, permitted_groups, groups)
131 605d23bf Kostas Papadimitriou
                raise PermissionDenied
132 e129e26b Kostas Papadimitriou
        else:
133 4edee820 Kostas Papadimitriou
            logger.error("Failed to access helpdesk view %r. No authenticated "
134 4edee820 Kostas Papadimitriou
                         "user found.")
135 e129e26b Kostas Papadimitriou
            raise PermissionDenied
136 e129e26b Kostas Papadimitriou
137 4edee820 Kostas Papadimitriou
        logging.info("User %s accessed helpdesk view (%s)", request.user_uniq,
138 4edee820 Kostas Papadimitriou
                     request.path)
139 e129e26b Kostas Papadimitriou
        return func(request, *args, **kwargs)
140 e129e26b Kostas Papadimitriou
141 e129e26b Kostas Papadimitriou
    return wrapper
142 e129e26b Kostas Papadimitriou
143 e129e26b Kostas Papadimitriou
144 e129e26b Kostas Papadimitriou
@helpdesk_user_required
145 c3564ce9 Kostas Papadimitriou
def index(request):
146 c3564ce9 Kostas Papadimitriou
    """
147 c3564ce9 Kostas Papadimitriou
    Helpdesk index view.
148 c3564ce9 Kostas Papadimitriou
    """
149 c3564ce9 Kostas Papadimitriou
    # if form submitted redirect to details
150 c3564ce9 Kostas Papadimitriou
    account = request.GET.get('account', None)
151 c3564ce9 Kostas Papadimitriou
    if account:
152 6c45fa84 Kostas Papadimitriou
        return redirect('synnefo.helpdesk.views.account',
153 ba8ff608 Kostas Papadimitriou
                        search_query=account)
154 c3564ce9 Kostas Papadimitriou
155 c3564ce9 Kostas Papadimitriou
    # show index template
156 c3564ce9 Kostas Papadimitriou
    return direct_to_template(request, "helpdesk/index.html")
157 c3564ce9 Kostas Papadimitriou
158 c3564ce9 Kostas Papadimitriou
159 e129e26b Kostas Papadimitriou
@helpdesk_user_required
160 ba8ff608 Kostas Papadimitriou
def account(request, search_query):
161 c3564ce9 Kostas Papadimitriou
    """
162 e129e26b Kostas Papadimitriou
    Account details view.
163 c3564ce9 Kostas Papadimitriou
    """
164 e129e26b Kostas Papadimitriou
165 4edee820 Kostas Papadimitriou
    logging.info("Helpdesk search by %s: %s", request.user_uniq, search_query)
166 605d23bf Kostas Papadimitriou
    show_deleted = bool(int(request.GET.get('deleted', SHOW_DELETED_VMS)))
167 605d23bf Kostas Papadimitriou
168 9e3519e0 Kostas Papadimitriou
    account_exists = True
169 d59d86d4 Kostas Papadimitriou
    vms = []
170 d59d86d4 Kostas Papadimitriou
    networks = []
171 ba8ff608 Kostas Papadimitriou
    is_ip = IP_SEARCH_REGEX.match(search_query)
172 ba8ff608 Kostas Papadimitriou
    is_uuid = UUID_SEARCH_REGEX.match(search_query)
173 ba8ff608 Kostas Papadimitriou
    is_vm = VM_SEARCH_REGEX.match(search_query)
174 ba8ff608 Kostas Papadimitriou
    account_name = search_query
175 6c45fa84 Kostas Papadimitriou
    auth_token = request.user.get('auth_token')
176 d59d86d4 Kostas Papadimitriou
177 d59d86d4 Kostas Papadimitriou
    if is_ip:
178 d59d86d4 Kostas Papadimitriou
        try:
179 ba8ff608 Kostas Papadimitriou
            nic = NetworkInterface.objects.get(ipv4=search_query)
180 ba8ff608 Kostas Papadimitriou
            search_query = nic.machine.userid
181 6c45fa84 Kostas Papadimitriou
            is_uuid = True
182 d59d86d4 Kostas Papadimitriou
        except NetworkInterface.DoesNotExist:
183 d59d86d4 Kostas Papadimitriou
            account_exists = False
184 ba8ff608 Kostas Papadimitriou
            account = None
185 ba8ff608 Kostas Papadimitriou
186 ba8ff608 Kostas Papadimitriou
    if is_vm:
187 ba8ff608 Kostas Papadimitriou
        vmid = is_vm.groupdict().get('vmid')
188 ba8ff608 Kostas Papadimitriou
        try:
189 ba8ff608 Kostas Papadimitriou
            vm = VirtualMachine.objects.get(pk=int(vmid))
190 ba8ff608 Kostas Papadimitriou
            search_query = vm.userid
191 ba8ff608 Kostas Papadimitriou
            is_uuid = True
192 ba8ff608 Kostas Papadimitriou
        except VirtualMachine.DoesNotExist:
193 ba8ff608 Kostas Papadimitriou
            account_exists = False
194 ba8ff608 Kostas Papadimitriou
            account = None
195 ba8ff608 Kostas Papadimitriou
            search_query = vmid
196 6c45fa84 Kostas Papadimitriou
197 468ba00b Ilias Tsitsimpis
    astakos = AstakosClient(settings.ASTAKOS_URL, retry=2,
198 468ba00b Ilias Tsitsimpis
                            use_pool=True, logger=logger)
199 468ba00b Ilias Tsitsimpis
200 6c45fa84 Kostas Papadimitriou
    if is_uuid:
201 ba8ff608 Kostas Papadimitriou
        account = search_query
202 468ba00b Ilias Tsitsimpis
        account_name = astakos.get_username(auth_token, account)
203 ba8ff608 Kostas Papadimitriou
204 ba8ff608 Kostas Papadimitriou
    if account_exists and not is_uuid:
205 ba8ff608 Kostas Papadimitriou
        account_name = search_query
206 468ba00b Ilias Tsitsimpis
        account = astakos.get_uuid(auth_token, account_name)
207 6c45fa84 Kostas Papadimitriou
208 ba8ff608 Kostas Papadimitriou
    if not account:
209 ba8ff608 Kostas Papadimitriou
        account_exists = False
210 ba8ff608 Kostas Papadimitriou
211 6c45fa84 Kostas Papadimitriou
    filter_extra = {}
212 6c45fa84 Kostas Papadimitriou
    if not show_deleted:
213 6c45fa84 Kostas Papadimitriou
        filter_extra['deleted'] = False
214 6c45fa84 Kostas Papadimitriou
215 6c45fa84 Kostas Papadimitriou
    # all user vms
216 6c45fa84 Kostas Papadimitriou
    vms = VirtualMachine.objects.filter(userid=account,
217 6c45fa84 Kostas Papadimitriou
                                        **filter_extra).order_by('deleted')
218 6c45fa84 Kostas Papadimitriou
    # return all user private and public networks
219 6c45fa84 Kostas Papadimitriou
    public_networks = Network.objects.filter(public=True,
220 6c45fa84 Kostas Papadimitriou
                                             nics__machine__userid=account,
221 6c45fa84 Kostas Papadimitriou
                                             **filter_extra
222 6c45fa84 Kostas Papadimitriou
                                             ).order_by('state').distinct()
223 6c45fa84 Kostas Papadimitriou
    private_networks = Network.objects.filter(userid=account,
224 6c45fa84 Kostas Papadimitriou
                                              **filter_extra).order_by('state')
225 6c45fa84 Kostas Papadimitriou
    networks = list(public_networks) + list(private_networks)
226 6c45fa84 Kostas Papadimitriou
227 6c45fa84 Kostas Papadimitriou
    if vms.count() == 0 and private_networks.count() == 0:
228 6c45fa84 Kostas Papadimitriou
        account_exists = False
229 e129e26b Kostas Papadimitriou
230 c3564ce9 Kostas Papadimitriou
    user_context = {
231 9e3519e0 Kostas Papadimitriou
        'account_exists': account_exists,
232 d59d86d4 Kostas Papadimitriou
        'is_ip': is_ip,
233 ba8ff608 Kostas Papadimitriou
        'is_vm': is_vm,
234 ba8ff608 Kostas Papadimitriou
        'is_uuid': is_uuid,
235 c3564ce9 Kostas Papadimitriou
        'account': account,
236 ba8ff608 Kostas Papadimitriou
        'search_query': search_query,
237 9e3519e0 Kostas Papadimitriou
        'vms': vms,
238 6c45fa84 Kostas Papadimitriou
        'show_deleted': show_deleted,
239 6c45fa84 Kostas Papadimitriou
        'account_name': account_name,
240 cc5c8608 Kostas Papadimitriou
        'token': request.user['auth_token'],
241 c3564ce9 Kostas Papadimitriou
        'networks': networks,
242 e620ca07 Olga Brani
        'UI_MEDIA_URL': settings.UI_MEDIA_URL
243 c3564ce9 Kostas Papadimitriou
    }
244 d59d86d4 Kostas Papadimitriou
245 c3564ce9 Kostas Papadimitriou
    return direct_to_template(request, "helpdesk/account.html",
246 6c45fa84 Kostas Papadimitriou
                              extra_context=user_context)
247 c3564ce9 Kostas Papadimitriou
248 ebda2ad4 Kostas Papadimitriou
249 9e3519e0 Kostas Papadimitriou
@helpdesk_user_required
250 605d23bf Kostas Papadimitriou
@token_check
251 cc5c8608 Kostas Papadimitriou
def vm_suspend(request, vm_id):
252 605d23bf Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
253 605d23bf Kostas Papadimitriou
    vm.suspended = True
254 605d23bf Kostas Papadimitriou
    vm.save()
255 4edee820 Kostas Papadimitriou
    logging.info("VM %s suspended by %s", vm_id, request.user_uniq)
256 605d23bf Kostas Papadimitriou
    account = vm.userid
257 605d23bf Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
258 605d23bf Kostas Papadimitriou
259 605d23bf Kostas Papadimitriou
260 605d23bf Kostas Papadimitriou
@helpdesk_user_required
261 605d23bf Kostas Papadimitriou
@token_check
262 cc5c8608 Kostas Papadimitriou
def vm_suspend_release(request, vm_id):
263 605d23bf Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
264 605d23bf Kostas Papadimitriou
    vm.suspended = False
265 605d23bf Kostas Papadimitriou
    vm.save()
266 4edee820 Kostas Papadimitriou
    logging.info("VM %s unsuspended by %s", vm_id, request.user_uniq)
267 605d23bf Kostas Papadimitriou
    account = vm.userid
268 605d23bf Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
269 cc5c8608 Kostas Papadimitriou
270 cc5c8608 Kostas Papadimitriou
271 cc5c8608 Kostas Papadimitriou
@helpdesk_user_required
272 cc5c8608 Kostas Papadimitriou
@token_check
273 cc5c8608 Kostas Papadimitriou
def vm_shutdown(request, vm_id):
274 cc5c8608 Kostas Papadimitriou
    logging.info("VM %s shutdown by %s", vm_id, request.user_uniq)
275 cc5c8608 Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
276 cc5c8608 Kostas Papadimitriou
    servers.start_action(vm, 'STOP')
277 cc5c8608 Kostas Papadimitriou
    servers_backend.shutdown_instance(vm)
278 cc5c8608 Kostas Papadimitriou
    account = vm.userid
279 cc5c8608 Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
280 cc5c8608 Kostas Papadimitriou
281 cc5c8608 Kostas Papadimitriou
282 cc5c8608 Kostas Papadimitriou
@helpdesk_user_required
283 cc5c8608 Kostas Papadimitriou
@token_check
284 cc5c8608 Kostas Papadimitriou
def vm_start(request, vm_id):
285 cc5c8608 Kostas Papadimitriou
    logging.info("VM %s start by %s", vm_id, request.user_uniq)
286 cc5c8608 Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
287 cc5c8608 Kostas Papadimitriou
    servers.start_action(vm, 'START')
288 cc5c8608 Kostas Papadimitriou
    servers_backend.startup_instance(vm)
289 cc5c8608 Kostas Papadimitriou
    account = vm.userid
290 cc5c8608 Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))