#!/usr/bin/python
# -*- coding: utf-8 -*-
# Project:     wifi-ltsp
# Module:     transformaArchivos.py
# Purpose:     Genera archivo de macs con horarios desde Rayuela y Ldap
# Language:    Python 2.5
# Date:        03-Feb-2011.
# Ver:        07-Feb-2011.
# Author:    Francisco Mora Sánchez
# Copyright:   2011 - Francisco Mora Sánchez   <adminies.maestrojuancalero@edu.juntaextremadura.net>
#
# wifi-ltsp is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# Script2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with wifi-ltsp. If not, see <http://www.gnu.org/licenses/>.

""" Programa para generar los archivos necesarios para permitir
que los portátiles de los alumnos puedan unirse al punto de acceso
hostapd del profesor, según el horario del propio profesor, y los
alumnos que tienen clase con él a esa hora.

Partiremos de los archivos de datos generados por Rayuela, el formato
de estos archivos es: por cada centro hay un directorio con cinco archivos
uno por cada día de la semana, llamados Lunes.txt, Martes.txt, Miercoles.txt,
Jueves.txt y Viernes.txt. Cada uno de estos archivos de texto contienen 
la siguiente información:

D.N.I. del docente|N.I.A. del alumno|Hora de inicio|Hora de fin
08848648L|3251023|815|870|
08848648L|3383767|815|870|
08848648L|3383770|815|870| 

la primera línea indica la estructura y a partir de ésta están los datos
que son: dni del profesor, nie del alumno y horas de inicio y fin expresadas
como minutos transcurridos desde las 00:00 horas.

Los ficheros que se generan, deben copiarse al directorio de destino
especificado como parámetro, y que en caso de no darse será por defecto
"/var/www/wifi/(lunes.txt|martex.txt|miercoles.txt|jueves.txt|viernes.txt)"
si la ruta no existe, ésta se creará. Para escribir en el destino debemos
tener los privilegios adecuados.

Los archivos que generaremos, tendrán el mismo nombre, sin mayúsculas, pero
el formato será el siguiente:

login_profesor|mac_alumno|inicio|fin|

no tenemos línea descriptora de estructura, y los datos se obtienen de
realizar consulta al directorio ldap del centro, para lo cual es necesario
que tengamos instalado el paquete python-ldap, además de python, claro está.
El dato de login_profesor es directo en la consulta a ldap, así como la 
generación de las horas y minutos, pero el dato de mac_alumno, se realiza
consultando ldap para obtener el login del alumno, que concuerda con el
nombre del portátil asignado al alumno. A partir de este dato accederemos
a la información recogida por puppet de cada ordenador, que se encuentra
en /var/lib/puppet/yaml/node/(login_del_alumno.dominiodelcentro).yaml, 
para obtener la mac de la interfaz wifi asociada a ese portátil. Las
líneas importantes son: macaddress_wlan0 o macaddress_eth1 que se
establece en algunos modelos de portátiles.

Uso: crearemos una carpeta donde copiaremos este programa y además 
pondremos los txts obtenidos por Rayuela, ejecutaremos el script pasando
como parámetros el lugar donde están los archivos con el parámetro
--datosrayuela rutarayuela, y tambien el lugar donde queremos depositar los
archivos generados, con el parámetros --datosservidor rutaservidor.
Si --datosrayuela no se establece, asumiremos el directorio actual, si
--datosservidor no está establecido, se establecerá internamente en
/var/www/wifi, creando la estructura de directorios mencionada si es que 
no existe, además comprobaremos que disponemos de privilegios para escribir
en el directorio de destino.

Francisco Mora Sánchez
IES Maestro Juan Calero
adminies.maestrojuancalero@edu.juntaextremadura.net


"""

import sys,os,re
import ldap

# Expresiones Regulares.
_re_mode = (re.I | re.M | re.S)
macwlan0_pattern = re.compile('.*macaddress_wlan0: (.*?)\n', _re_mode)
maceth1_pattern = re.compile('.*macaddress_eth1: (.*?)\n', _re_mode)

			
def EjecutaRegex(regex, cadena):
    """ ejecuta búsqueda de expresión regular en una cadena """
    m = regex.search(cadena)
    if m:
        return m.groups()[0]
    else:
        return None

			
def lee_parametros(lista,params):
	"""Funcion que crea un diccionario para leer el valor de los 
	parametros que se pasan en la linea de comandos."""
	for i in range(1,len(lista)):
		if lista[i][:2] == "--":
			#veamos si tiene valor
			if i+1 < len(lista) and lista[i+1] <> "--":
				#estamos en un parametro y valor
				params[lista[i][2:]] = lista[i+1]

def dameDatosLineaRayuela(linea):
	"""Funcion que trocea la linea de datos de rayuela y
	devuelve un diccionario cuyas claves son login_profesor
	login_alumno, inicio y final
	"""
	datos = linea.split("|")
	return { "dni_profesor" : datos[0], "nie_alumno" : datos[1], "inicio" : datos[2], "final" : datos[3]}

def consultaLdap(identificador):
	"""Función que consulta el servidor ldap, con el atributo
	employeeNumber. Devuelve el login asociado al profesor o al alumno
	"""
	try:
			l = ldap.open("ldap")
	except ldap.LDAPError, e:
			print e
			return "ERRORLDAP"
	
	base_dn='ou=People,dc=instituto,dc=extremadura,dc=es'
	searchFilter = '(employeeNumber='+identificador+')'
	searchScope = ldap.SCOPE_SUBTREE
	
	retrieveAttributes = ['uid']
	
	try:
		ldap_result_id = l.search(base_dn, searchScope, searchFilter, retrieveAttributes)
		result_type, result_data = l.result(ldap_result_id, 0)
		if len(result_data)==0:
			#print "la búsqueda no da ningún resultado"
			return "NO ENCONTRADO"
		elif len(result_data)>1:
			#print "La búsqueda da más de un resultado"
			resultado= result_data[0]
		else:
			resultado=result_data[0]
			
		#print resultado[1]['uid'][0]
	except ldap.LDAPError, e:
			print e
			print "ERRORLDAP"

	return resultado[1]['uid'][0]

def obtenerMac(login_alumno, archivosYaml, datosyaml):
	"""Función que dado un login de alumno, buscará en el archivo yaml
	de puppet asociado a ese login de alumno, la cadena macaddress_wlan0,
	o bien macaddress_eth1 en caso que no exista la primera. Esta función
	quizá se deba testear bien.
	Tenemos disponible la lista archivosYaml con todos los archivos yaml
	de puppet, incluyendo sus rutas...
	"""
	#Primero buscamos el archivo en cuestion, tendrá el login_alumno mas
	#un punto, dominio, punto y terminación yaml, busquemos el patrón con dos puntos
	#ya que suele haber archivos fantasmas con el nombre solamente.
	for a in archivosYaml:
		if a.count(".") == 2:
			#Estamos en un patrón de archivo correcto.
			pos = a.find(".")
			sufijo = a[pos:]
			break
			
	archivoDatosYaml = login_alumno + sufijo
	try:
		filearchivoDatosYaml = open(os.path.join(datosyaml,archivoDatosYaml), "r")
	except:
		pass
		return "99:99:99:99:99:99"
	contenidoyaml = filearchivoDatosYaml.read()
	filearchivoDatosYaml.close() 
	#print contenidoyaml
	mac = EjecutaRegex(macwlan0_pattern, contenidoyaml)
	if mac == None:
		mac = EjecutaRegex(maceth1_pattern, contenidoyaml)
	#print mac
	if mac == None:
		mac = "99:99:99:99:99:99"
	return mac
	
def transformaHora(datohora):
	"""Función que pasado un parámetro numérico que almacena los minutos
	desde las 00:00. 
	Devuelve una cadena en la forma "08:35", con la hora y los minutos.
	"""
	hora = datohora // 60
	minutos = datohora % 60
	return "%02d%02d" % (hora,minutos)
	
def transformaArchivos(datosrayuela, datosservidor, datosyaml, debug = False):
	#Comprobemos la existencia del directorio de datos de Rayuela
	if not os.path.exists(datosrayuela):
		if debug:
			print "Directorio para datos de rayuela",datosrayuela,"no existe, abortando ejecución..."
		return 1

	#Comprobemos que sea un directorio
	if not os.path.isdir(datosrayuela):
		if debug:
			print "Directorio para datos de rayuela",datosrayuela,"no es un directorio, abortando ejecución..."
		return 2
		
	#Comprobemos que tengamos permisos de apertura y lectura de archivos
	if os.path.isdir(datosrayuela):
		try:
			archivosRayuela = os.listdir(datosrayuela)
		except:
			if debug:
				print "Directorio para datos de rayuela",datosrayuela,"problemas de permisos en carpeta, abortando ejecución..."
			return 3

	#Comprobemos que existe el directorio de destino
	if not os.path.exists(datosservidor):
		#No existe directorio de destino, debemos crearlo
		if debug:
			print "No existe directorio de destino en servidor",datosservidor,"se intentará crear directorio..."
		try:
			os.makedirs(datosservidor)
		except OSError:
			if debug:
				print "Hay algún problema con la creación del directorio de destino, abortando ejecución..."
			return 4
	
	#Comprobemos que tenemos permisos de escritura 
	if os.path.exists(datosservidor):
		if not os.access(datosservidor, os.W_OK):
			if debug:
				print "Hay algún problema de permisos para la creación de archivos en el directorio de destino del servidor, abortando ejecución..."
			return 5

	#Comprobemos la existencia del directorio de datos yaml de puppet
	if not os.path.exists(datosyaml):
		if debug:
			print "Directorio para datos yaml de puppet",datosyaml,"no existe, abortando ejecución..."
		return 6

	#Comprobemos que sea un directorio
	if not os.path.isdir(datosyaml):
		if debug:
			print "Directorio para datos yaml de puppet",datosyaml,"no es un directorio, abortando ejecución..."
		return 7
		
	#Comprobemos que tengamos permisos de apertura y lectura de archivos
	if os.path.isdir(datosyaml):
		try:
			#archivosYaml = glob.glob(os.path.join(params["datosyaml"],"*.*.yaml"))
			archivosYaml = os.listdir(datosyaml)
		except:
			if debug:
				print "Directorio para datos yaml de puppet",datosyaml,"problemas de permisos en directorio, abortando ejecución..."
			return 8
			
	#Limpiemos las listas de archivos 
	#Tengamos en cuenta que los archivos entregados por rayuela pueden contener distinto patron en mayusculas y minusculas
	dicArchivosRayuela = {"lunes.txt":"","martes.txt":"","miercoles.txt":"","jueves.txt":"","viernes.txt":""}
	for archivoRayuela in archivosRayuela:
		if archivoRayuela.lower() in dicArchivosRayuela.keys():
			dicArchivosRayuela[archivoRayuela.lower()] = archivoRayuela

	archivosYaml = [ archivo for archivo in archivosYaml if archivo[-5:] == ".yaml"]
	#print archivosRayuela
	#print archivosYaml
	#Para evitar consultar al servidor ldap varias veces para un mismo dni o nie crearemos 
	#dos diccionarios, uno para profesores y otro para alumnos con los datos que
	#recuperaremos del servidor ldap, además otro con los logins de alumnos y
	#las macs
	#profesores = { "dni" : "login_profesor", ... }
	#alumnos = { "nie" : "login_alumno", ... }
	#macs = { "login_alumno" : "mac", ... }
	profesores = {}
	alumnos = {}
	macs = {}
	#Procesemos cada archivo de Rayuela
	for archivoRayuela in dicArchivosRayuela.keys():
		if debug:
			print "Procesando archivo",os.path.join(datosrayuela,dicArchivosRayuela[archivoRayuela])
		filearchivoRayuela = open(os.path.join(datosrayuela,dicArchivosRayuela[archivoRayuela]), 'r')
		archivoServidor = os.path.join(datosservidor,archivoRayuela)
		if debug:
			print "Generando archivo",archivoServidor
		filearchivoServidor = open(archivoServidor, 'w')
		while True:
			linea = filearchivoRayuela.readline()
			if not linea:
				break
			if linea[:6] == "D.N.I.":
				continue
			#Procesemos cada linea de Rayuela
			#datosLineaRayuela es un diccionario cuyas claves son dni_profesor, nie_alumno, inicio, final
			datosLineaRayuela = dameDatosLineaRayuela(linea)
			datosLineaServidor = ""			
			#Consultemos el login del profesor en ldap por su dni y el nie del alumno
			#comprobando que no lo hemos encontrado previamente
			dni_profesor=datosLineaRayuela["dni_profesor"]
			if profesores.has_key(dni_profesor):
				login_profesor = profesores[dni_profesor]
				#if debug:
				#	print "Nooooo se consulta a ldap, profesor dni",datosLineaRayuela["dni_profesor"],"pertenece a",login_profesor
			else:
				login_profesor = consultaLdap(dni_profesor)
				if login_profesor=="NO ENCONTRADO":
					login_profesor = consultaLdap(dni_profesor[1:])
				profesores[dni_profesor] = login_profesor
				if debug:
					print "Se consulta a ldap, profesor dni",datosLineaRayuela["dni_profesor"],"pertenece a",login_profesor								
			if alumnos.has_key(datosLineaRayuela["nie_alumno"]):
				login_alumno = alumnos[datosLineaRayuela["nie_alumno"]]
				#if debug:
				#	print "Nooooo se consulta a ldap, alumno nie",datosLineaRayuela["nie_alumno"],"pertenece a",login_alumno
			else:
				login_alumno = consultaLdap(datosLineaRayuela["nie_alumno"])
				alumnos[datosLineaRayuela["nie_alumno"]] = login_alumno
				if debug:
					print "Se consulta a ldap, alumno nie",datosLineaRayuela["nie_alumno"],"pertenece a",login_alumno

			if macs.has_key(login_alumno):
				mac_alumno = macs[login_alumno]
				#if debug:
				#	print "Nooooo se consultan datos yaml, alumno login",login_alumno,"tiene mac",mac_alumno
			else:
				mac_alumno = obtenerMac(login_alumno, archivosYaml, datosyaml)
				#mac_alumno = obtenerMac("dmoral01", archivosYaml, datosyaml)
				macs[login_alumno] = mac_alumno
				if debug:
					print "Se consultan datos yaml, alumno login",login_alumno,"tiene mac",mac_alumno
			inicio = transformaHora(int(datosLineaRayuela["inicio"]))
			final = transformaHora(int(datosLineaRayuela["final"]))
			datosLineaServidor = login_profesor+"|"+mac_alumno+"|"+str(inicio)+"|"+str(final)+"|"+"\n"			
			#datosLineaServidor = login_profesor+"|"+mac_alumno+"|"+datosLineaRayuela["inicio"]+"|"+datosLineaRayuela["final"]+"|"+"\n"			
			if debug:
				print datosLineaServidor
			filearchivoServidor.write(datosLineaServidor)
		filearchivoRayuela.close()
		filearchivoServidor.close()
	return 0

if __name__ == '__main__':
	params = {}
	lista = sys.argv
	lee_parametros(lista,params)
	#comprobaremos las combinaciones de parametros en linea de comandos
	if len(lista) == 2 and lista[1] == "--help":
		print 'uso: ./transformaArchivos.py [--datosrayuela rutadatosrayuela] [--datosservidor rutadatosservidor] [--datosyaml rutadatosyaml]'
		sys.exit(1)
	if not params.has_key("datosrayuela"):
		print "No se ha establecido la ruta a la carpeta con los datos de rayuela, usando por defecto '"+os.getcwd()+"'"
		params["datosrayuela"] = os.getcwd()
	if not params.has_key("datosservidor"):
		print "No se ha establecido la ruta dónde depositar los archivos generados, usando por defecto '/var/www/wifi'"
		params["datosservidor"] = "/var/www/wifi"
	if not params.has_key("datosyaml"):
		print "No se ha establecido la ruta dónde se encuentran los datos yaml de puppet, usando por defecto '/var/lib/puppet/yaml/node'"
		params["datosyaml"] = "/var/lib/puppet/yaml/node"			

	print
	print "Los valores pasado son los siguientes:"
	print "Directorio de datos de Rayuela:",params["datosrayuela"]
	print "Directorio de datos de para el servidor:",params["datosservidor"]
	print "Directorio de datos yaml de puppet:",params["datosyaml"]

	resultado = transformaArchivos(params["datosrayuela"], params["datosservidor"], params["datosyaml"], True)
	if resultado > 0:
		print "Se ha producido errores en el procesamiento, código de error",resultado
	else:
		print "Se ha ejecutado correctamente"
	exit(resultado)
