Changes between Initial Version and Version 1 of Version2/Tutoriales/Consola_Web/Tutorial_4_Jobs


Ignore:
Timestamp:
Sep 5, 2010, 4:44:02 PM (14 years ago)
Author:
adelcastillo
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Version2/Tutoriales/Consola_Web/Tutorial_4_Jobs

    v1 v1  
     1= Introducción =
     2
     3La arquitectura basada en plugins de Opengnsys permite añadir nuevas funcionalidades de forma modular y sencilla. La mayoría de estas funcionalidades no se limitan a realizar tareas en el Servidor, sino que implican comunicarse con los propios clientes de Opengnsys. Por ejemplo, un plugin sencillo podría permitir apagar o encender dichos clientes, y otro algo más complejo gestionar el particionado del disco duro.
     4
     5Para simplificar y estandarizar el trabajo de comunicación con los clientes, se crearon los trabajos o Jobs. Estos permiten ejecutar comandos en un cliente dado, y recibir una respuesta. Dichos comandos serán ejecutados con permisos de superusuario. La comunicación con los clientes se realiza mediante un socket cifrado y autenticado mediante SSL en ambos extremos: es decir, es segura, y esta seguridad es transparente al usuario. El cliente Opengnsys viene con una serie de comandos de administración e idealmente sólo sería necesario utilizar esos y no otros.
     6
     7El plugin que vamos a realizar aquí nos permitirá saber cuanto tiempo lleva encendida una máquina. Para ello crearemos una acción que se listará en el panel de acciones al mostrar un ordenador. Al hacer clic en la acción, crearemos un job que ejecutará el comando "uptime" en el cliente y mostrará una pantalla diciendo "Esperando respuesta..." en el cliente, y finalmente cuando se reciba una respuesta, se procesará y se mostrará el Uptime en pantalla.
     8
     9= Creación de la acción =
     10
     11Lo primero que debemos hacer es crear el directorio '''plugins/uptime''', donde residirá el nuevo plugin. Ahí crearemos el fichero '''plugin.conf''' que declara información básica del plugin y de la acción antes mencionada:
     12
     13{{{
     14#!python
     15[action/get_uptime]
     16description = Retrieves uptime from the client
     17human_name = Get Uptime
     18appear_in_main_panel = No
     19}}}
     20
     21
     22También es necesario definir el fichero #__init__.py#:
     23
     24{{{
     25#!python
     26'''
     27Jobs plugin example
     28'''
     29
     30from ..pluginbase import PluginBase
     31from view import UptimeView
     32
     33class Plugin(PluginBase):
     34    '''
     35    '''
     36    def enable(self):
     37        self.actions_for_url = ('navigator/computer/(.*)', 'get_uptime')
     38        self.urls = ('action/get_uptime/computer/(.*)', UptimeView)
     39}}}
     40
     41= Creando la vista =
     42
     43La vista asociada a la acción '''get_uptime''' será la encargada de crear el job, actualizar periódicamente la página mostrando el mensaje de "Esperando respuesta" hasta que el cliente responda, cuando la página dejará de ser recargada y mostrará el uptime, el fichero es '''view.py''':
     44
     45
     46{{{
     47#!python
     48import web
     49import datetime
     50from jobs import GetUptimeJob
     51from decorators import pi18n
     52
     53class UptimeView:
     54    @pi18n
     55    def GET(self, computer_name):
     56        computer = web.ctx.orm.query(Computer).filter(Computer.name == computer_name).first()
     57        job = self.get_job(computer)
     58        if job.status == 'FINISHED':
     59            if job.last_modified_date + datetime.timedelta(minutes = 2) > datetime.datetime.now():
     60                // New job needed
     61                job = GetUptimeJob(web.ctx.orm)
     62                job.send(computer.id)
     63                web.ctx.header("Refresh", "2; url=%s", web.ctx.fullpath)
     64                return web.ctx.render.plugins.uptime.view()
     65            else:
     66                // Job finished: nice!
     67                return web.ctx.render.plugins.uptime.view(job)
     68        if job.status 'ERROR':
     69            / New job needed, previous one gave an error
     70            job = GetUptimeJob(web.ctx.orm)
     71            job.send(computer.id)
     72        web.ctx.header("Refresh", "2; url=%s", web.ctx.fullpath)
     73        return web.ctx.render.plugins.uptime.view()
     74
     75    def get_job(self, computer):
     76        '''
     77        Returns any current GetUptimeJob related to the given computer
     78        '''
     79        return web.ctx.orm.query(Job).filter(Job.computer_id == computer.id).\
     80            filter(Job.module_path == GetUptimeJob.__module_path__).\
     81            filter(Job.class_path == GetUptimeJob.__class_path__).first()
     82}}}
     83
     84La función get_job realiza una consulta a la base de datos que obtiene si hay un job de nuestro tipo para el ordenador dado. Esto lo hace filtrando por '''__module_path__''' y '''__class_path__''',  que caracterizan a nuestro Job como veremos más adelante.
     85
     86Por otra parte, la función GET obtiene el model del ordenador solicitado, y obtiene el job anteriormente mencionado. En caso de que el job hubiese terminado, se utilizará si se terminó hace como mucho dos minutos, o en caso contrario se volverá a solicitar el uptime. Si el job dio error, también volvemos a solicitar el uptime. Utilizamos '''web.ctx.header''' para recargar la página cada dos segundos mientras se muestra el mensaje de "Esperando respuesta" al usuario.
     87
     88Finalmente siempre llamamos al template que mostrará al usuario el estado de la solicitud. Este template muestra el mensaje "Esperando respuesta" siempre que no le pasemos un job que contenga la información sobre el uptime, y reside en '''templates/view.html''' dentro del directorio de nuestro plugin:
     89
     90{{{
     91#!python
     92$def with (job = None)
     93$var title = _("Uptime")
     94
     95$var hierarchy = []
     96
     97$code:
     98    def show_uptime():
     99        print _("%s has been up for %s") % (job.computer.name,\
     100            job.client_message)
     101<div class="uptime">
     102    $if job == None:
     103        $_("Waiting response..")
     104    $else:
     105        $show_uptime()
     106</div>
     107
     108}}}
     109
     110= Creando el job =
     111
     112Nuestro job va a ser bien sencillo. Sólo necesitamos que envíe el comando "uptime" y que procese la salida de dicho comando. La salida es del tipo " 16:18:53 up  3:11,  1 user,  load average: 0.92, 0.64, 0.50", pero queremos quedarnos con "3:11". El fichero de nuestro job es '''job.py'':
     113
     114{{{
     115#!python
     116from __future__ import unicode_literals
     117from clientjob.model import Job
     118from decorators import pi18n
     119
     120class GetUptimeJob(Job):
     121    __module_path__ = "plugins.uptime.jobs"
     122    __class_path__ = "GetUptimeJob"
     123
     124    def __init__(self, orm):
     125        Job.__init__(self, orm, 'uptime', self.__module_path__,
     126            self.__class_path__)
     127
     128    @pi18n
     129    def get_user_message(self):
     130        if self.status != 'FINISHED':
     131            return Job.get_user_message(self)
     132
     133        return _('Uptime for %s is %s') %\
     134            (self.computer.name, self.client_message)
     135
     136    def update(self):
     137        if self.status != 'FINISHED':
     138            return
     139        self.client_message = self.client_message.split(' ')[3]
     140
     141}}}
     142
     143Para crear nuestro propio job tenemos que tener en cuenta lo siguiente:
     144
     145 * Debemos heredar de clientjob.model.Job
     146 * En nuestro constructor debemos llamar al constructor padre para inicializar correctamente el job, y establecer el comando a ejecutar, el módulo python (fichero) donde se encuentra nuestro Job y dentro del módulo, el nombre o path de la clase, el class_path (En nuestro caso fue simple porque nuestra clase no está dentro de otra padre).
     147 * La propiedad '''status''' del job indica el estado de la petición. Al crearse es "CREATED", al terminar es "FINISHED", y si ha habido algún error es "CREATED". Si el ordenador está apagado es "WAITING".
     148 * La función '''uptime()''' se llama cada vez que el job recibe una actualización y sirve para poder procesar dichas actualizaciones. Si estamos ejecutando un comando que toma un tiempo (por ejemplo formateando una partición, copiando archivos, etc) será llamada periodicamente con status en modo "INPROGRESS".
     149 * La función '''get_user_message''' es llamada para mostrar más detalladamente al usuario en la lista de jobs el estado del job.
     150 * Cuando mediante sqlalchemy obtengamos el job de la base de datos, vendrá directamente como una instancia de nuestra clase.
     151