src/p4l/admin/views.py
changeset 145 7c6fe1dab213
child 153 50f01260eef4
equal deleted inserted replaced
144:8c32ea1310de 145:7c6fe1dab213
       
     1 # -*- coding: utf-8 -*-
       
     2 #
       
     3 # Copyright IRI (c) 2013
       
     4 #
       
     5 # contact@iri.centrepompidou.fr
       
     6 #
       
     7 # This software is governed by the CeCILL-B license under French law and
       
     8 # abiding by the rules of distribution of free software.  You can  use, 
       
     9 # modify and/ or redistribute the software under the terms of the CeCILL-B
       
    10 # license as circulated by CEA, CNRS and INRIA at the following URL
       
    11 # "http://www.cecill.info". 
       
    12 #
       
    13 # As a counterpart to the access to the source code and  rights to copy,
       
    14 # modify and redistribute granted by the license, users are provided only
       
    15 # with a limited warranty  and the software's author,  the holder of the
       
    16 # economic rights,  and the successive licensors  have only  limited
       
    17 # liability. 
       
    18 #
       
    19 # In this respect, the user's attention is drawn to the risks associated
       
    20 # with loading,  using,  modifying and/or developing or reproducing the
       
    21 # software by the user in light of its specific status of free software,
       
    22 # that may mean  that it is complicated to manipulate,  and  that  also
       
    23 # therefore means  that it is reserved for developers  and  experienced
       
    24 # professionals having in-depth computer knowledge. Users are therefore
       
    25 # encouraged to load and test the software's suitability as regards their
       
    26 # requirements in conditions enabling the security of their systems and/or 
       
    27 # data to be ensured and,  more generally, to use and operate it in the 
       
    28 # same conditions as regards security. 
       
    29 #
       
    30 # The fact that you are presently reading this means that you have had
       
    31 # knowledge of the CeCILL-B license and that you accept its terms.
       
    32 #
       
    33 '''
       
    34 Created on Oct 9, 2013
       
    35 
       
    36 @author: ymh
       
    37 '''
       
    38 
       
    39 import itertools
       
    40 import os
       
    41 import signal
       
    42 from subprocess import PIPE, Popen, STDOUT
       
    43 import time
       
    44 import uuid
       
    45 
       
    46 from django.conf import settings
       
    47 from django.http.response import StreamingHttpResponse, HttpResponse
       
    48 from django.utils.translation import ugettext
       
    49 from django.views.generic.base import TemplateView, View
       
    50 
       
    51 
       
    52 class ConfirmScriptView(TemplateView):
       
    53     template_name = "p4l/admin/confirm_run_script.html"
       
    54     
       
    55     def get_context_data(self, **kwargs):
       
    56         return {
       
    57             'command_line' : " ".join(getattr(settings,"ADMIN_SCRIPT", {}).get('command',"")),
       
    58             'env' : repr(getattr(settings,"ADMIN_SCRIPT", {}).get('env',{})),
       
    59             'cwd' : repr(getattr(settings,"ADMIN_SCRIPT", {}).get('cwd',"")),
       
    60         }
       
    61 
       
    62 class RunScriptView(View):
       
    63     
       
    64     def __init__(self, **kwargs):
       
    65         View.__init__(self, **kwargs)
       
    66         self.boundary = "--BOUNDARY--==--%s" % str(uuid.uuid4())
       
    67 
       
    68     
       
    69     def get(self, request):
       
    70         resp = StreamingHttpResponse()
       
    71         
       
    72         command_kwargs = {
       
    73             'shell':False,
       
    74             'env':None,
       
    75             'cwd':None
       
    76         }
       
    77         admin_script = getattr(settings,"ADMIN_SCRIPT", {})
       
    78         command = admin_script.get('args',"")
       
    79         
       
    80         if not command:
       
    81             return resp
       
    82 
       
    83         command_kwargs.update(admin_script,
       
    84             stdout=PIPE,
       
    85             stderr=STDOUT,
       
    86             bufsize=0,
       
    87             close_fds=True,
       
    88             preexec_fn=os.setsid
       
    89         )
       
    90 
       
    91         resp['Connection'] = "Keep-Alive"
       
    92         doc_start = [
       
    93              '<!DOCTYPE html>',
       
    94              '<html lang="en">',
       
    95              '<head>',
       
    96              '<meta charset="utf-8">',
       
    97              '<title>output</title>',
       
    98              '<style>body {font-family: monospace; white-space: pre;}</style>',
       
    99              '</head>',
       
   100              '<body>',
       
   101         ]
       
   102         
       
   103         doc_end = [
       
   104              '<script>parent.done();</script>',
       
   105              '</html>',
       
   106              '</body>'
       
   107         ]
       
   108         
       
   109         scroll_to_bottom = '<script type="text/javascript">window.scrollBy(0,50);</script>'
       
   110 
       
   111         process = Popen(**command_kwargs)
       
   112          
       
   113         # Save the pid in the user's session (a thread-safe place)
       
   114         request.session['pid'] = process.pid
       
   115  
       
   116         def read_output():
       
   117             for line in iter(process.stdout.readline, b''):
       
   118                 yield "%s%s" %(line, scroll_to_bottom)
       
   119              
       
   120         resp.streaming_content = itertools.chain(doc_start, read_output(), doc_end)
       
   121         
       
   122         return resp
       
   123 
       
   124 
       
   125 def check_pid(pid):        
       
   126     """ Check For the existence of a unix pid. """
       
   127     try:
       
   128         os.kill(pid, 0)
       
   129     except OSError:
       
   130         return False
       
   131     else:
       
   132         return True
       
   133 
       
   134 class KillScriptView(View):
       
   135     
       
   136     def get(self, request):
       
   137         
       
   138         resp = HttpResponse()
       
   139         
       
   140         pid = request.session.get('pid', None)
       
   141         
       
   142         if not pid:
       
   143             resp.content = ugettext("No active process to kill")
       
   144         else:
       
   145             os.kill(pid, signal.SIGINT)
       
   146             i = 0
       
   147             while i <= settings.SCRIPT_MAX_WAIT and check_pid(pid):
       
   148                 time.sleep(settings.SCRIPT_WAIT)
       
   149                 i += 1
       
   150 
       
   151             if check_pid(pid):
       
   152                 os.killpg(pid, signal.SIGKILL)
       
   153             
       
   154             resp.content = ugettext("Success: The process was killed successfully.")
       
   155             
       
   156         return resp