#! /usr/bin/python
# -*- coding: iso-8859-1 -*-

#####################################################
#    Copyright (C) 2006 by Cesar Kallas   	
#    cesarkallas@soulivre.org   
#    http://cesarkallas.soulivre.org
#                                                                          
#    This program 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 2 of the License, or     
#    (at your option) any later version.                                   
#                                                                          
#    This program 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 this program; if not, write to the                         
#    Free Software Foundation, Inc.,                                       
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             
#####################################################

import time
import datetime
import md5
import Log
import Ambiente
import MySQLdb
import MySQLdb.cursors
import Ambiente
import xml.dom.ext
import xml.dom.minidom
import Repositorio

class Acoes(object):
	"""
		Classe que gerencia acoes a serem executadas em um agente
	"""
	
	def __init__(self, agente=None):
		self.agente = agente
		self.db = Ambiente.db
		
		self.hoje = datetime.date.today()
		
		# Inicia Log do
		#self.log = Log.Log("Acoes")
		#self.log = self.log.get()
		self.log = Ambiente.log


	def agente(self, agente):
		self.agente = agente

	def confirmaDataExecucao(self, data_execucao=None):
		"""
			Essa funcao recebe uma data de execucao e verifica se ela eh valida
			Entrada: data_execucao (data, formato: aaaa/mm/dd)
			Saida: ndata (data, formato aaaa/mm/dd, ajustada para 1 mes apos o dia atual, caso ela esteja no passado)
		"""
		
		mes = datetime.timedelta(days=30)
		ndata = self.hoje + mes
			
		# Verifica se a data execucao foi passara corretamente
		if isinstance(data_execucao, str) != True:
			return ndata
		
		try:
			data = datetime.date(int(data_execucao[0:4]), int(data_execucao[5:7]), int(data_execucao[8:10]))
		except Exception:
			return ndata
			
		if data < self.hoje:
			return ndata
		return data_execucao


	def instala(self, dicAcao):
		"""
		Instala o componente do agente
		Entrada:

		Retorno:
			1: acao adicionada com sucesso!
			-1: argumento invalido
		"""
		if isinstance(dicAcao, dict) != True:
			return -1
			
		dicAcao["acao"] = "instalar"
		return self.__adicionaAcao(dicAcao)


	def desinstala(self, dicAcao):
		"""
		Desinstala o componente do agente
		Entrada:

		Retorno:
			1: acao adicionada com sucesso!
			-1: argumento invalido
		"""
		if isinstance(dicAcao, dict) != True:
			return -1
			
		dicAcao["acao"] = "desinstalar"
		return self.__adicionaAcao(dicAcao)
		

	def atualiza(self, dicAcao):
		"""
		Atualiza um componente do agente
		Entrada:
			dicAcao
		Retorno:
			1: acao adicionada com sucesso!
			-1: argumento invalido
		"""
		if isinstance(dicAcao, dict) != True:
			return -1
			
		dicAcao["acao"] = "atualizar"
		return self.__adicionaAcao(dicAcao)


	def removeAcao(self, id=None):
		"""
		Remove uma acao da base de dados
		Entrada: id (Inteiro com a Identificacao da acao)
		Retorno:
			 1: concluido com sucesso
			-1: ID invalido
			-2: falha na remocao
		"""
		if id == None:
			return -1

		try:
			self.db.execute("DELETE FROM acoes where id = %s", id)
			return 1
		except MySQLdb.Error, e:
			self.log.error("%d: %s" % (e.args[0], e.args[1]))
			return -2
		return -3

	
	def atualizaStatus(self, id, status):
		try:
			status = str(status)
			id = int(id)
			self.db.execute("""UPDATE acoes SET status_execucao=%s WHERE id=%s""",  (status, id))
		except MySQLdb.Error, e:
			self.log.error("%d: %s" % (e.args[0], e.args[1]))
			return -1
		return -2
	

	def informacao(self, id=None, status=None):
		"""
		Retorna todas as informacoes de uma determina da acao
		de todas acoes do agente atual
		Entradas:
			id (Identificacao numerica de uma acao) (opcional)
			status (Status da identificacao) (opcional)
		Saida:
			OK: Informacoes da acao
			-1: falha ao buscar acao
		"""
		try:
			if id != None:
				self.db.execute("""SELECT * FROM acoes WHERE id=%s""",  (id))
			elif status == None:
				self.db.execute("""SELECT * FROM acoes WHERE agentes_mac_address=%s""",  (self.agente))
			else:
				self.db.execute("""SELECT * FROM acoes WHERE agentes_mac_address=%s and status_execucao=%s order by acao""",  (self.agente, status))
			return self.db.fetchall()
		except MySQLdb.Error, e:
			mysql_erro = "%d: %s" % (e.args[0], e.args[1])
			self.log.error(mysql_erro)
			return -1
		except Exception:
			erro = "ao buscar informacao de uma acao: %s" % agente_mac_address
			self.log.error(erro)
			return -1


	def exportaTodos(self, status=None):
		"""
		Retorna todas acoes de acordo com o status
		"""
		try:
			if status == None:
				self.db.execute("""SELECT * FROM acoes  order by id""")
			else:
				self.db.execute("""SELECT * FROM acoes WHERE status_execucao=%s order by id desc""",  (status))
			return self.db.fetchall()
		except MySQLdb.Error, e:
			mysql_erro = "%d: %s" % (e.args[0], e.args[1])
			self.log.error(mysql_erro)
			return mysql_erro
		except Exception:
			erro = "ao exporta acoes"
			self.log.error(erro)
			return -1



	def exporta(self, statusAgente):
		"""
		Exporta acoes de um determinado agente para um arquivo xml
		"""
		dicAgenteAcoes = self.informacao(status=statusAgente)
		
		if dicAgenteAcoes == -1:
			return -1
			
		# Sem acoes disponiveis
		if not dicAgenteAcoes:
			return -2

		# Cria novo arquivo xml
		doc = xml.dom.minidom.Document()
		raiz = doc.createElementNS(self.agente, "ConectaGerente")
		doc.appendChild(raiz)
		
		# Gera acoes para o xml
		for dicAgenteAcao in dicAgenteAcoes:
			# Verifica se acao nao eh invalida
			if dicAgenteAcao["acao"] == None:
				return -1 
			if dicAgenteAcao["acao"] == "":
				return -1
				
			# Cria cabecalho da acao (ex. instalar, atualizar)
			acaoAtual = dicAgenteAcao["acao"]
			grupo = doc.createElementNS(self.agente, acaoAtual)
			raiz.appendChild(grupo)
			
			##########
			# Items da Acao
			# Id da acao
			item = doc.createElementNS(self.agente, "acao_id")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(dicAgenteAcao["id"]))
			item.appendChild(descricao)
			item.normalize
			
			# Operador da acao
			item = doc.createElementNS(self.agente, "acao_operador")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(dicAgenteAcao["operadores_login"]))
			item.appendChild(descricao)
			item.normalize
			
			# Data criacao da acao
			item = doc.createElementNS(self.agente, "acao_data_criacao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(dicAgenteAcao["data_criacao"]))
			item.appendChild(descricao)
			item.normalize
			
			# Data maxima de execucao da acao
			item = doc.createElementNS(self.agente, "acao_data_execucao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(dicAgenteAcao["data_execucao"]))
			item.appendChild(descricao)
			item.normalize
			
			############
			# Items do Software
			repositorio = Repositorio.Repositorio()
			sw = repositorio.informacao(str(dicAgenteAcao["softwares_md5"]))
			if sw == -1:
				return -2
			# Software Nome
			item = doc.createElementNS(self.agente, "software_nome")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["nome"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software Versao
			item = doc.createElementNS(self.agente, "software_versao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["versao"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software liberacao
			item = doc.createElementNS(self.agente, "software_liberacao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["liberacao"]))
			item.appendChild(descricao)
			item.normalize

			# Software tamanho
			item = doc.createElementNS(self.agente, "software_tamanho")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["tamanho"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software descricao
			item = doc.createElementNS(self.agente, "software_descricao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["descricao"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software grupo
			item = doc.createElementNS(self.agente, "software_grupo")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["grupo"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software localizacao
			item = doc.createElementNS(self.agente, "software_localizacao")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["localizacao"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software plataforma
			item = doc.createElementNS(self.agente, "software_plataforma")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["plataforma"]))
			item.appendChild(descricao)
			item.normalize
			
			# Software nomeArquivo
			item = doc.createElementNS(self.agente, "software_nomeArquivo")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(sw["nomeArquivo"]))
			item.appendChild(descricao)
			item.normalize

			# Software MD5
			item = doc.createElementNS(self.agente, "software_md5")
			grupo.appendChild(item)
			descricao = doc.createTextNode(str(dicAgenteAcao["softwares_md5"]))
			item.appendChild(descricao)
			item.normalize
		
		# Retorna xml
		#return doc
		
		# Imprime arquivo
		#xml.dom.ext.PrettyPrint(doc)
		try:
			# Grava arquivo
			macAddress = str(self.agente)
			macAddress = macAddress.replace(":", "")
			arquivoXml = open("acoes/" + macAddress + ".xml", 'w')
			xml.dom.ext.PrettyPrint(doc, arquivoXml)
			arquivoXml.close()
			
			# Gera assinatura MD5 do arquivo xml gerado
			arquivoXml = open("acoes/" + macAddress + ".xml", 'rb')
			arquivoMD5 = md5.new(arquivoXml.read())
			arquivoXml.close()
			return  arquivoMD5.hexdigest()
		except:
			self.log.error("Acoes: falha ao gerar acoes do agente: " + str(self.agente))
			raise
			return -1


	def __adicionaAcao(self, dicAcao):
		"""
		Adiciona uma acao da base de dados
		Entrada: dicAcao (dicionario com o software, data_execucao, observacao e acao a ser executada)
		Retorno:
			 1: concluido com sucesso
			-2: falha ao gerar um ID para a acao
			-3: falha ao adicionar uma nova acao na base de dados
			-4: falha desconhecida
		"""
		dicAcao["data_execucao"] = self.confirmaDataExecucao(dicAcao["data_execucao"])
		dicAcao["data_criacao"] = datetime.date.today()
		dicAcao["operadores_login"] = Ambiente.operador
		dicAcao["status_execucao"] = "agendada"
		
		if self.agente == None:
			return "agente invalido"
			
		if dicAcao.get("softwares_md5") == None:
			return "falta parametro: 'softwares_md5'"
			
		if dicAcao.get("acao") == None:
			return "falta parametro: 'acao'"
			
		if dicAcao.get("observacao") == None:
			dicAcao["observacao"] = ""
		
		# Seleciona o maior ID das acoes cadastradas
		try:
			self.db.execute("SELECT MAX(id) AS ultimoID FROM acoes")
			id = self.db.fetchone()
		except MySQLdb.Error, e:
			self.log.error("%d: %s" % (e.args[0], e.args[1]))
			return -2
		
		# Se nao existir nenhuma acao cadastrada, a primeira sera 1
		if id["ultimoID"] == None:
			dicAcao["id"] = 1
		else:
			# Existe acao cadastrada, entao a proxima sera o incremento de 1
			dicAcao["id"] = 1 + id["ultimoID"]

		try:
			self.db.execute("INSERT INTO acoes \
				(id, softwares_md5, agentes_mac_address, operadores_login, \
				acao, observacao, status_execucao, data_criacao, data_execucao) \
				values (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
				(dicAcao["id"],  dicAcao["softwares_md5"],  self.agente,
				dicAcao["operadores_login"], dicAcao["acao"], dicAcao["observacao"], dicAcao["status_execucao"],
				dicAcao["data_criacao"], dicAcao["data_execucao"]))
			return 1
		except Exception:
			raise
			self.log.error("ao cadastrar nova acao", dicAcao)
			return -4
		except MySQLdb.Error, e:
			#self.log.error("%d: %s" % (e.args[0], e.args[1]))
			raise
			return -3
