]> git.llucax.com Git - z.facultad/75.52/sercom.git/blobdiff - sercom/tester.py
fix de id
[z.facultad/75.52/sercom.git] / sercom / tester.py
index a77c573bf4e040fa3ff89bc2941d824cd03ca4c1..6e4088c1665984d039fbe768bd21a8e950e6477f 100644 (file)
@@ -6,28 +6,35 @@ from zipfile import ZipFile, BadZipfile
 from cStringIO import StringIO
 from shutil import rmtree
 from datetime import datetime
 from cStringIO import StringIO
 from shutil import rmtree
 from datetime import datetime
-from subprocess import Popen, PIPE, call #, check_call XXX Python 2.5
 from os.path import join
 from turbogears import config
 from os.path import join
 from turbogears import config
+import subprocess as sp
 import os, sys, pwd, grp
 import resource as rsrc
 import logging
 
 log = logging.getLogger('sercom.tester')
 
 import os, sys, pwd, grp
 import resource as rsrc
 import logging
 
 log = logging.getLogger('sercom.tester')
 
-class CalledProcessError(Exception): #{{{ Python 2.5 forward-compatibility
-    """This exception is raised when a process run by check_call() returns
-    a non-zero exit status.  The exit status will be stored in the
-    returncode attribute."""
-    def __init__(self, returncode, cmd):
-        self.returncode = returncode
-        self.cmd = cmd
-    def __str__(self):
-        return ("Command '%s' returned non-zero exit status %d"
-            % (self.cmd, self.returncode))
+error_interno = _(u'\n**Error interno al preparar la entrega.**')
+
+class UserInfo(object): #{{{
+    def __init__(self, user):
+        try:
+            info = pwd.getpwnam(user)
+        except:
+            info = pwd.get(int(user))
+        self.user = info[0]
+        self.uid = info[2]
+        self.gid = info[3]
+        self.name = info[4]
+        self.home = info[5]
+        self.shell = info[6]
+        self.group = grp.getgrgid(self.gid)[0]
 #}}}
 
 #}}}
 
-def check_call(*popenargs, **kwargs): #{{{ Python 2.5 forward-compatibility
+user_info = UserInfo(config.get('sercom.tester.user', 65534))
+
+def check_call(*popenargs, **kwargs): #{{{ XXX Python 2.5 forward-compatibility
     """Run command with arguments.  Wait for command to complete.  If
     the exit code was zero then return, otherwise raise
     CalledProcessError.  The CalledProcessError object will have the
     """Run command with arguments.  Wait for command to complete.  If
     the exit code was zero then return, otherwise raise
     CalledProcessError.  The CalledProcessError object will have the
@@ -38,13 +45,29 @@ def check_call(*popenargs, **kwargs): #{{{ Python 2.5 forward-compatibility
 
     check_call(["ls", "-l"])
     """
 
     check_call(["ls", "-l"])
     """
-    retcode = call(*popenargs, **kwargs)
+    retcode = sp.call(*popenargs, **kwargs)
     cmd = kwargs.get("args")
     if cmd is None:
         cmd = popenargs[0]
     if retcode:
     cmd = kwargs.get("args")
     if cmd is None:
         cmd = popenargs[0]
     if retcode:
-        raise CalledProcessError(retcode, cmd)
+        raise sp.CalledProcessError(retcode, cmd)
     return retcode
     return retcode
+sp.check_call = check_call
+#}}}
+
+#{{{ Excepciones
+
+class CalledProcessError(Exception): #{{{ XXX Python 2.5 forward-compatibility
+    """This exception is raised when a process run by check_call() returns
+    a non-zero exit status.  The exit status will be stored in the
+    returncode attribute."""
+    def __init__(self, returncode, cmd):
+        self.returncode = returncode
+        self.cmd = cmd
+    def __str__(self):
+        return ("Command '%s' returned non-zero exit status %d"
+            % (self.cmd, self.returncode))
+sp.CalledProcessError = CalledProcessError
 #}}}
 
 class Error(StandardError): pass
 #}}}
 
 class Error(StandardError): pass
@@ -53,7 +76,7 @@ class ExecutionFailure(Error, RuntimeError): pass
 
 class RsyncError(Error, EnvironmentError): pass
 
 
 class RsyncError(Error, EnvironmentError): pass
 
-error_interno = _(u'\n**Error interno al preparar la entrega.**')
+#}}}
 
 def unzip(bytes, dst): # {{{
     log.debug(_(u'Intentando descomprimir en %s'), dst)
 
 def unzip(bytes, dst): # {{{
     log.debug(_(u'Intentando descomprimir en %s'), dst)
@@ -69,22 +92,6 @@ def unzip(bytes, dst): # {{{
             file(join(dst, f), 'w').write(zfile.read(f))
 #}}}
 
             file(join(dst, f), 'w').write(zfile.read(f))
 #}}}
 
-def get_pwdgrp(unam, gnam): #{{{
-    def do(type, funcnam, funcid, name):
-        try:
-            id = funcnam(name)[2]
-        except:
-            try:
-                id = int(name)
-                name = funcid(id)[0]
-            except Exception, e:
-                log.critical(_(u'No existe el %s %s (%s)'), type, name, e)
-                sys.exit(1)
-        return (id, name)
-    return do('usuario', pwd.getpwnam, pwd.getpwuid, unam) \
-        + do('grupo', grp.getgrnam, grp.getgrgid, gnam)
-#}}}
-
 class SecureProcess(object): #{{{
     default = dict(
         max_tiempo_cpu      = 120,
 class SecureProcess(object): #{{{
     default = dict(
         max_tiempo_cpu      = 120,
@@ -95,13 +102,17 @@ class SecureProcess(object): #{{{
         max_locks_memoria   = 0,
     )
     uid = config.get('sercom.tester.chroot.user', 65534)
         max_locks_memoria   = 0,
     )
     uid = config.get('sercom.tester.chroot.user', 65534)
-    gid = config.get('sercom.tester.chroot.group', 65534)
     MB = 1048576
     # XXX probar! make de un solo archivo lleva nproc=100 y nofile=15
     def __init__(self, comando, chroot, cwd):
     MB = 1048576
     # XXX probar! make de un solo archivo lleva nproc=100 y nofile=15
     def __init__(self, comando, chroot, cwd):
-            self.comando = comando
-            self.chroot = chroot
-            self.cwd = cwd
+        self.comando = comando
+        self.chroot = chroot
+        self.cwd = cwd
+        log.debug('Proceso segurizado: chroot=%s, cwd=%s, user=%s, cpu=%s, '
+            'as=%sMiB, fsize=%sMiB, nofile=%s, nproc=%s, memlock=%s',
+            self.chroot, self.cwd, self.uid, self.max_tiempo_cpu,
+            self.max_memoria, self.max_tam_archivo, self.max_cant_archivos,
+            self.max_cant_procesos, self.max_locks_memoria)
     def __getattr__(self, name):
         if getattr(self.comando, name) is not None:
             return getattr(self.comando, name)
     def __getattr__(self, name):
         if getattr(self.comando, name) is not None:
             return getattr(self.comando, name)
@@ -110,9 +121,9 @@ class SecureProcess(object): #{{{
         x2 = lambda x: (x, x)
         os.chroot(self.chroot)
         os.chdir(self.cwd)
         x2 = lambda x: (x, x)
         os.chroot(self.chroot)
         os.chdir(self.cwd)
-        (uid, unam, gid, gnam) = get_pwdgrp(self.uid, self.gid)
-        os.setgid(gid)
-        os.setuid(uid)
+        uinfo = UserInfo(self.uid)
+        os.setgid(uinfo.gid)
+        os.setuid(uinfo.uid) # Somos mortales irreversiblemente
         rsrc.setrlimit(rsrc.RLIMIT_CPU, x2(self.max_tiempo_cpu))
         rsrc.setrlimit(rsrc.RLIMIT_AS, x2(self.max_memoria*self.MB))
         rsrc.setrlimit(rsrc.RLIMIT_FSIZE, x2(self.max_tam_archivo*self.MB)) # XXX calcular en base a archivos esperados?
         rsrc.setrlimit(rsrc.RLIMIT_CPU, x2(self.max_tiempo_cpu))
         rsrc.setrlimit(rsrc.RLIMIT_AS, x2(self.max_memoria*self.MB))
         rsrc.setrlimit(rsrc.RLIMIT_FSIZE, x2(self.max_tam_archivo*self.MB)) # XXX calcular en base a archivos esperados?
@@ -120,12 +131,6 @@ class SecureProcess(object): #{{{
         rsrc.setrlimit(rsrc.RLIMIT_NPROC, x2(self.max_cant_procesos))
         rsrc.setrlimit(rsrc.RLIMIT_MEMLOCK, x2(self.max_locks_memoria))
         rsrc.setrlimit(rsrc.RLIMIT_CORE, x2(0))
         rsrc.setrlimit(rsrc.RLIMIT_NPROC, x2(self.max_cant_procesos))
         rsrc.setrlimit(rsrc.RLIMIT_MEMLOCK, x2(self.max_locks_memoria))
         rsrc.setrlimit(rsrc.RLIMIT_CORE, x2(0))
-        log.debug('Proceso segurizado: chroot=%s, cwd=%s, user=%s(%s), '
-            'group=%s(%s), cpu=%s, as=%sMiB, fsize=%sMiB, nofile=%s, nproc=%s, '
-            'memlock=%s', self.chroot, self.cwd, unam, uid, gnam, gid,
-            self.max_tiempo_cpu, self.max_memoria, self.max_tam_archivo,
-            self.max_cant_archivos, self.max_cant_procesos,
-            self.max_locks_memoria)
         # Tratamos de forzar un sync para que entre al sleep del padre FIXME
         import time
         time.sleep(0)
         # Tratamos de forzar un sync para que entre al sleep del padre FIXME
         import time
         time.sleep(0)
@@ -139,13 +144,10 @@ class Tester(object): #{{{
         self.home = home
         self.queue = queue
         # Ahora somos mortales (oid mortales)
         self.home = home
         self.queue = queue
         # Ahora somos mortales (oid mortales)
-        euid = config.get('sercom.tester.user', 65534)
-        egid = config.get('sercom.tester.group', 65534)
-        (self.euid, self.eunam, self.egid, self.egnam) = get_pwdgrp(euid, egid)
         log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s (%s:%s)'),
         log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s (%s:%s)'),
-            self.eunam, self.egnam, self.euid, self.egid)
-        os.setegid(self.egid)
-        os.seteuid(self.euid)
+            user_info.user, user_info.group, user_info.uid, user_info.gid)
+        os.setegid(user_info.gid)
+        os.seteuid(user_info.uid)
 
     @property
     def build_path(self):
 
     @property
     def build_path(self):
@@ -158,11 +160,11 @@ class Tester(object): #{{{
     @property
     def chroot(self):
         return join(self.path, 'chroot_' + self.name)
     @property
     def chroot(self):
         return join(self.path, 'chroot_' + self.name)
-    #}}}
 
     @property
     def orig_chroot(self):
         return join(self.path, 'chroot')
 
     @property
     def orig_chroot(self):
         return join(self.path, 'chroot')
+    #}}}
 
     def run(self): #{{{
         entrega_id = self.queue.get() # blocking
 
     def run(self): #{{{
         entrega_id = self.queue.get() # blocking
@@ -210,12 +212,12 @@ class Tester(object): #{{{
         os.seteuid(0) # Dios! (para chroot)
         os.setegid(0)
         try:
         os.seteuid(0) # Dios! (para chroot)
         os.setegid(0)
         try:
-            check_call(rsync)
+            sp.check_call(rsync)
         finally:
             log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s (%s:%s)'),
         finally:
             log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s (%s:%s)'),
-                self.eunam, self.egnam, self.euid, self.egid)
-            os.setegid(self.egid) # Mortal de nuevo
-            os.seteuid(self.euid)
+                user_info.user, user_info.group, user_info.uid, user_info.gid)
+            os.setegid(user_info.gid) # Mortal de nuevo
+            os.seteuid(user_info.uid)
         unzip(entrega.archivos, self.build_path)
 
     def clean_chroot(self, entrega):
         unzip(entrega.archivos, self.build_path)
 
     def clean_chroot(self, entrega):
@@ -251,13 +253,13 @@ def ejecutar_caso_de_prueba(self, path, entrega): #{{{
             for tarea in tareas:
                 tarea.ejecutar(path, prueba)
         except ExecutionFailure, e:
             for tarea in tareas:
                 tarea.ejecutar(path, prueba)
         except ExecutionFailure, e:
-            prueba.pasada = False
+            prueba.exito = False
             if self.rechazar_si_falla:
                 entrega.exito = False
             if self.terminar_si_falla:
                 raise ExecutionError(e.comando, e.tarea, prueba)
         else:
             if self.rechazar_si_falla:
                 entrega.exito = False
             if self.terminar_si_falla:
                 raise ExecutionError(e.comando, e.tarea, prueba)
         else:
-            prueba.pasada = True
+            prueba.exito = True
     finally:
         prueba.fin = datetime.now()
 CasoDePrueba.ejecutar = ejecutar_caso_de_prueba
     finally:
         prueba.fin = datetime.now()
 CasoDePrueba.ejecutar = ejecutar_caso_de_prueba
@@ -297,27 +299,55 @@ def ejecutar_comando_fuente(self, path, entrega): #{{{
     unzip(self.archivos_entrada, path) # TODO try/except
     comando_ejecutado = entrega.add_comando_ejecutado(self)
     # Abro archivos para fds básicos (FIXME)
     unzip(self.archivos_entrada, path) # TODO try/except
     comando_ejecutado = entrega.add_comando_ejecutado(self)
     # Abro archivos para fds básicos (FIXME)
-    options = dict(close_fds=True, stdin=None, stdout=None, stderr=None,
-        preexec_fn=SecureProcess(self, 'var/chroot_pepe', '/home/sercom/build'))
-    log.debug(_(u'Ejecutando como root: %s'), ' '.join(self.comando))
-    uid = os.geteuid()
-    gid = os.getegid()
+    options = dict(
+        close_fds=True,
+        stdin=None,
+        shell=True,
+        preexec_fn=SecureProcess(self, 'var/chroot_pepe', '/home/sercom/build')
+    )
+    if self.guardar_stdouterr:
+        options['stdout'] = file('/tmp/sercom.tester.%s.stdouterr'
+            % comando_ejecutado.id, 'w') #TODO /var/lib/sercom?
+        options['stderr'] = sp.STDOUT
+    else:
+        if self.guardar_stdout:
+            options['stdout'] = file('/tmp/sercom.tester.%s.stdout'
+                % comando_ejecutado.id, 'w') #TODO /var/lib/sercom?
+        if self.guardar_stderr:
+            options['stderr'] = file('/tmp/sercom.tester.%s.stderr'
+                % comando_ejecutado.id, 'w') #TODO /var/lib/sercom?
+    log.debug(_(u'Ejecutando como root: %s'), self.comando)
     os.seteuid(0) # Dios! (para chroot)
     os.setegid(0)
     try:
         try:
     os.seteuid(0) # Dios! (para chroot)
     os.setegid(0)
     try:
         try:
-            proc = Popen(self.comando, **options)
+            proc = sp.Popen(self.comando, **options)
         finally:
         finally:
-            log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s'),
-                uid, gid)
-            os.setegid(gid) # Mortal de nuevo
-            os.seteuid(uid)
-    except Exception, e: # FIXME poner en el manejo de exceptiones estandar
+            log.debug(_(u'Cambiando usuario y grupo efectivos a %s:%s (%s:%s)'),
+                user_info.user, user_info.group, user_info.uid, user_info.gid)
+            os.setegid(user_info.gid) # Mortal de nuevo
+            os.seteuid(user_info.uid)
+    except Exception, e:
         if hasattr(e, 'child_traceback'):
             log.error(_(u'Error en el hijo: %s'), e.child_traceback)
         raise
         if hasattr(e, 'child_traceback'):
             log.error(_(u'Error en el hijo: %s'), e.child_traceback)
         raise
-    proc.wait()
+    proc.wait() #TODO un sleep grande nos caga todo, ver sercom viejo
     comando_ejecutado.fin = datetime.now()
     comando_ejecutado.fin = datetime.now()
+    buffer = StringIO()
+    zip = ZipFile(buffer, 'w')
+    if self.guardar_stdouterr:
+        zip.write('/tmp/sercom.tester.%s.stdouterr'
+            % comando_ejecutado.id, '__stdouterr__')
+    else:
+        if self.guardar_stdout:
+            azipwrite('/tmp/sercom.tester.%s.stdout'
+                % comando_ejecutado.id, '__stdout__')
+        if self.guardar_stderr:
+            zip.write('/tmp/sercom.tester.%s.stderr'
+                % comando_ejecutado.id, '__stderr__')
+    zip.close()
+    comando_ejecutado.archivos_guardados = buffer.getvalue()
+
 #    if no_anda_ejecucion: # TODO
 #        comando_ejecutado.exito = False
 #        comando_ejecutado.observaciones += 'No anduvo xxx' # TODO mas info
 #    if no_anda_ejecucion: # TODO
 #        comando_ejecutado.exito = False
 #        comando_ejecutado.observaciones += 'No anduvo xxx' # TODO mas info