Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.1 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 d91de78c Christos Stavrakakis
import astakosclient
47 d91de78c Christos Stavrakakis
from snf_django.lib import astakos
48 d91de78c Christos Stavrakakis
49 6c45fa84 Kostas Papadimitriou
from synnefo.db.models import VirtualMachine, NetworkInterface, Network
50 e129e26b Kostas Papadimitriou
51 cc5c8608 Kostas Papadimitriou
# server actions specific imports
52 cc5c8608 Kostas Papadimitriou
from synnefo.api import servers
53 cc5c8608 Kostas Papadimitriou
from synnefo.logic import backend as servers_backend
54 cc5c8608 Kostas Papadimitriou
55 605d23bf Kostas Papadimitriou
logger = logging.getLogger(__name__)
56 605d23bf Kostas Papadimitriou
57 7ad07359 Kostas Papadimitriou
HELPDESK_MEDIA_URL = getattr(settings, 'HELPDESK_MEDIA_URL',
58 7ad07359 Kostas Papadimitriou
                             settings.MEDIA_URL + 'helpdesk/')
59 7ad07359 Kostas Papadimitriou
60 d59d86d4 Kostas Papadimitriou
IP_SEARCH_REGEX = re.compile('([0-9]+)(?:\.[0-9]+){3}')
61 6c45fa84 Kostas Papadimitriou
UUID_SEARCH_REGEX = re.compile('([0-9a-z]{8}-([0-9a-z]{4}-){3}[0-9a-z]{12})')
62 ba8ff608 Kostas Papadimitriou
VM_SEARCH_REGEX = re.compile('vm(-){0,}(?P<vmid>[0-9]+)')
63 6c45fa84 Kostas Papadimitriou
64 6c45fa84 Kostas Papadimitriou
65 ebda2ad4 Kostas Papadimitriou
def get_token_from_cookie(request, cookiename):
66 ebda2ad4 Kostas Papadimitriou
    """
67 ebda2ad4 Kostas Papadimitriou
    Extract token from the cookie name provided. Cookie should be in the same
68 ebda2ad4 Kostas Papadimitriou
    form as astakos service sets its cookie contents::
69 ebda2ad4 Kostas Papadimitriou

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