--Debug

TRASHDIR="SGEtrash/"
SGEERRORFILE="SGEError.log"
SGEOUTPUTFILE="SGEOutput.log"

require "algClusterEnv"

local ctrl = {}
ctrl.debug = false

function debug(str)
	if ctrl.debug then
		print(str)
	end
end

--------------------------------------------------------------------------------
-- API para criação de um novo módulo SGA.
--------------------------------------------------------------------------------
--
-- Esse arquivo possui o "esqueleto" das funções que são necessárias
-- para implementar um novo SGA. A configuração do nó (em sgad-cnf.lua),
-- deverá usar o atributo loadlib para indicar o nome do arquivo que
-- contém o código desenvolvido. Esse atributo faz com que o
-- sga-daemon carregue e execute as funções implementadas. Por exemplo,
-- supondo que o arquivo com a nova API possua o nome NovaAPI.lua, a
-- configuração do nó seria da seguinte forma:
--
-- Node {
--   name = "nomeDaMaquina",
--   platform_id = "SGANovo",
--   loadlib = "NovaAPI",
-- }
--
-- A escolha de configurar o SGA como Node ou como Grid vai depender de
-- como você deseja que este SGA apareça na interface do usuário. Se
-- o usuário deve ver o SGA Boinc como uma única máquina, você deve
-- configurá-lo como Node. Porém, se o Boinc possui vários nós e você
-- deseja que o usuário possa monitorá-los, você deve configurá-lo
-- como Grid.
--
-- Grid {
--   name = "nomeDaMaquina",
--   loadlib = "NovaAPI",
--   default = {
--     platform_id = "SGANovo",
--   },
-- }
--
-- Você encontra mais detalhes em:
-- https://jira.tecgraf.puc-rio.br/confluence/pages/viewpage.action?pageId=37814618
--------------------------------------------------------------------------------

-- @TODO Rever nomenclatura do parametro server na funções

--------------------------------------------------------------------------------
--
-- Especificação das funções públicas
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Incializa o módulo para execução. Essa função deve ser chamada antes
-- de qualquer outra função desta biblioteca.
--
-- Retorna verdadeiro caso o módulo seja inicializado com sucesso. Em caso
-- de erro, retorna nil e a mensagem de erro.
--
-- ok, err = open()
--------------------------------------------------------------------------------
open = function()
  servermanager.localhost = SGAD_CONF.localhost -- XXX
  servermanager.name = SGA_STATIC_CONF.csbase_sga_name           -- XXX
  local res = os.execute("test -d "..TRASHDIR)
  if  res~= 0 and res~=true and
     not os.execute("mkdir "..TRASHDIR) then
     print("Não foi possível criar o diretório "..TRASHDIR..".")
     print("Usando ./")
     TRASHDIR=""
  end
  return true, nil
end -- function open

--------------------------------------------------------------------------------
-- Termina a execução do módulo. Os processos disparados que ainda estejam
-- rodando não são afetados por essa chamada. Essa função não retorna valores.
--
-- close()
--------------------------------------------------------------------------------
close = function()
  servermanager._nodes = nil
end -- function close

--------------------------------------------------------------------------------
--
-- Funções de consulta à configuração de servidor.
--
-- Em SGAs que são clusters, o parâmetro server indica o nó sobre o
-- qual se deseja obter a informação.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Retorna as informações de configuração correspondentes às chaves e ao
-- servidor especificados.
--
-- @param server o servidor do qual as informações serão obtidas. Caso o valor
--        seja nil será assumido localhost.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões que devem ser suportadas são:
--           csbase_num_processors
--           csbase_memory_ram_info_mb
--           csbase_memory_swap_info_mb
--           csbase_job_control_actions
--          @TODO referenciar a IDL
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- map, err = getConfiguration(server, keys)
--------------------------------------------------------------------------------
getConfiguration = function(server, keys)
  print "getConfiguration"
  print ("server", server)
  -- @TODO verificar como acessar, via Oil, as constantes definidas na interface IDL
  -- const string SGA_NODE_NUM_PROCESSORS = "csbase_num_processors";
  -- const string SGA_NODE_MEMORY_RAM_INFO_MB = "csbase_memory_ram_info_mb";
  -- const string SGA_NODE_MEMORY_SWAP_INFO_MB = "csbase_memory_swap_info_mb";
  -- @TODO Verificar se essas duas informações são obtidas neste nível ou no daemon
  -- const string SGA_NODE_CLOCK_SPEED_MHZ = "clock_speed_mhz";
  -- const string SGA_NODE_PLATFORM_ID = "csbase_platform_id";
  if not keys or #keys == 0 then
    keys = {
      "csbase_num_processors",
      "csbase_memory_ram_info_mb",
      "csbase_memory_swap_info_mb",
      "csbase_job_control_actions",
    }
  end -- if no keys

   local node = {}
   node.name = server
   local ninfo = servermanager._update(node)
  print ("ninfo", ninfo)
  print ("ninfo.name", ninfo.name)
  print "ponto 1"
  local configMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_num_processors" then
      configMap[k] = servermanager.getnumcpus(ninfo.name) or servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_info_mb" then
      configMap[k] = servermanager.getrammemory(ninfo) or servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_info_mb" then
      configMap[k] = servermanager.getswapmemory(ninfo) or servermanager.ERROR_CODE
    elseif k == "csbase_job_control_actions" then
      configMap[k] = { servermanager.HOLD, servermanager.RELEASE, servermanager.TERMINATE }
    end 
  end
  for k, v in pairs(configMap) do
      print("configMap - key: ", k, "value: ", v)
  end
  print "fim getConfiguration"
  return configMap, nil
end -- function getConfiguration

--------------------------------------------------------------------------------
--
-- Funções de monitoração de servidor.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Retorna as informações dinâmicas correspondentes às chaves e ao servidor
-- especificados.
--
-- @param server o servidor do qual as informações serão obtidas. Caso o valor
--        seja nil será assumido localhost.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
-- @param maxold indica o tempo máximo que a informação pode ter. Caso maxold
--        seja nil, é assumido zero e a informação será buscada.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões são:
--           csbase_load_perc
--           csbase_memory_ram_free
--           csbase_memory_swap_free
--           csbase_number_of_jobs
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- Algunes detalhes sobre as informações padrões:
-- csbase_load_perc:
--   a taxa média de ocupação de CPU do último minuto. Esse valor considera o
--   número de processadores que o servidor possui. Por exemplo, caso a métrica
--   de ocupação seja o número de processos na fila de prontos, este número
--   estará dividido pela quantidade de processadores.
-- csbase_memory_ram_free_perc e csbase_memory_swap_free_perc:
--   a média de ocupação, em bytes, de memória do último minuto.
-- csbase_number_of_jobs:
--   o número de jobs que estão em execução.
--
-- map, err = getInfo(server, keys, maxold)
--------------------------------------------------------------------------------
getInfo = function(server, keys, maxold)

  if not keys or #keys == 0 then
    keys = {
      "csbase_load_perc",
      "csbase_memory_ram_free",
      "csbase_memory_swap_free",
      "csbase_number_of_jobs",
    }
  end -- if no keys

   local node = {}
   node.name = server
   local ninfo = servermanager._update(node, maxold)

  --if not _disableNode(node) then
--	print(string.format("Nó %s inacessível", server))
--	return nil, string.format("Nó %s inacessível", server)
--  end

  local infoMap = {}

  for _, k in ipairs(keys) do
    if k == "csbase_load_perc" then
      infoMap[k] = servermanager.getcpuload(ninfo) or servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_free" then
      infoMap[k] = servermanager.getfreerammemory(ninfo) or servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_free" then
      infoMap[k] = servermanager.getfreeswapmemory(ninfo) or servermanager.ERROR_CODE
    elseif k == "csbase_number_of_jobs" then
      infoMap[k] = servermanager.getnumjobs(ninfo) or servermanager.ERROR_CODE
    end
  end
--[[
  for k, v in pairs(infoMap) do
      print("infoMap - key: ", k, "value: ", v)
  end
]]
  return infoMap, nil

end -- function getInfo


_disableNode = function (server)
    
    local fname = os.tmpname()

    local command = "qstat -f | grep "..SGA_STATIC_CONF.queue_name.." | grep "..server.name.." |  awk -F ' ' '{print $6}' > "..fname

    servermanager.writeMsg("Coletando info com ["..command.."]")
    os.execute( command )

    local fd = io.open(fname, "r")
    if not fd then
	servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
	return nil
    end

    --remove o header da tabela
    local str = fd:read("*a")
    fd:close()
    os.remove(fname)
    
    local disable = string.gmatch(str, "%w+")() or ""
    
    servermanager.writeMsg("Node: "..server.name.."\tState: *"..disable.."*")
    
    if disable == "" or disable == "a" then
        return false
    else
        return true
    end
end

_parseArgs = function(command)
        local paramTable = {}
        string.gsub(command, "(%S+)=(%S+)", function (name, val)
        paramTable[name] = val
        end)
        return paramTable
end

--------------------------------------------------------------------------------
--
-- Funções de execução, monitoração e controle de processos.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Executa um comando no servidor especificado e retorna um handle que é a
-- referência para o comando no contexto desta biblioteca. O handle pode ter
-- qualquer formato.
-- Os parâmetros extras para a execução podem ser específicos da plataforma ou
-- um dos seguintes já definidos:
--   csbase_command_input_path caminho do direcionamento da entrada padrão.
--   csbase_command_output_path caminho do direcionamento da saída padrão.
--   csbase_command_error_path caminho do direcionamento da saída de erro.
-- Os valores de csbase_command_output_path e csbase_command_error_path podem
-- apontar para o mesmo caminho.
--
-- @param id o identificador único para este comando.
-- @param command o comando a ser executado.
-- @param extraParams os parâmetros extras para a excução do comando.
-- @param server o servidor onde o comando será executado. Caso seja nil será
--        assumido localhost.
--
-- @return o handle do comando e, em caso de erro, nil e a mensagem de erro.
--
-- handle, err = executeCommand(id, command, extraParams, server)
--------------------------------------------------------------------------------
executeCommand = function(id, command, extraParams, server)
  -- const string COMMAND_EXECUTION_INPUT_PATH = "csbase_command_input_path";
  -- const string COMMAND_EXECUTION_OUTPUT_PATH = "csbase_command_output_path";
  -- const string COMMAND_EXECUTION_ERROR_PATH = "csbase_command_error_path";

  local ce_input_p = extraParams.csbase_command_input_path
  local ce_start_p = extraParams.csbase_command_start_path
  local ce_output_p = extraParams.csbase_command_output_path
  local ce_error_p = extraParams.csbase_command_error_path

  local output_dir = ce_output_p or TRASHDIR
  local errorlog = (not ce_error_p) and output_dir.."/"..SGEERRORFILE
  local tmpfile = os.tmpname()

  local cmd = command

  if ce_input_p then
     cmd = cmd .. " < " .. infile
  end

  --remove o @ que nao e permitido no SGE
  id = id:gsub("@", "-at-")
  local pid = 0


  local exec_string = string.format("%s > %s ", cmd, tmpfile)

  if ce_start_p then  
    servermanager.writeMsg( "Tentando execução:\n\t"..exec_string.."\n" )
    os.execute(exec_string)
    errorlog = nil
  else

            servermanager.writeMsg( "Command:\n\t"..cmd.."\n" )
	    local com = cmd:sub(cmd:find("%s")+1,cmd:len())
            print("Saida com: "..com)
	    local fd2 = io.open(com, "r")

	    local conteudo = fd2:read("*a")
	    fd2:close()
            
            local envFile = _getAlgEnvFile(com)
            _loadAlgEnvFile(envFile)
            
            local commandParams = _parseArgs(conteudo)--exec_string)
            local environment = commandParams.ambiente or algorithmEnvironment.PE or nil
            local ncpus = commandParams.numCPU or algorithmEnvironment.numCPU or 1
            local nthreads = commandParams.numThreads or algorithmEnvironment.numThreads or 1
            
            local queue = SGA_STATIC_CONF.queue_name
            
            local tmpscript = os.tmpname()
            local fd1 = io.open(tmpscript,"w")
            fd1:write("#$ -S /bin/bash\n")
            fd1:write("#$ -N "..id.."\n")
            fd1:write("#$ -q "..queue.."\n")
            
            if algorithmEnvironment.requirements ~= nil then
                fd1:write("#$ -l "..algorithmEnvironment.requirements.."\n")
            end
            
            if environment ~= nil then
                
                local PEName = environment
                
                local slots = ncpus * nthreads
                
                --------------------------------------------------------------------
                -- Legado da definição da PE no atributo "ambiente" do config.xml --
                --------------------------------------------------------------------
                if commandParams.numCPU ~= nil and commandParams.numThreads ~= nil then
                    if tonumber(commandParams.numThreads) > 1 then
                        PEName = SGA_STATIC_CONF.PE_hybrid.. ""..nthreads    
                    else
                        PEName = SGA_STATIC_CONF.PE_mpi
                    end
                elseif commandParams.numCPU ~= nil and commandParams.numThreads == nil then
                    PEName = SGA_STATIC_CONF.PE_mpi
                elseif commandParams.numCPU == nil and commandParams.numThreads ~= nil then
                     PEName = SGA_STATIC_CONF.PE_threads
                end
                --------------------------------------------------------------------
                
                fd1:write("#$ -pe "..PEName.." "..slots.."\n")
            end
            
            local envAttr = _appendAlgEnv(algorithmEnvironment)
            fd1:write("\n"..envAttr.."\n\n")
            
            fd1:write(string.format( [[cd %s; %s]], SGAD_CONF.path,cmd))
            fd1:close()

            local _outfile = output_dir.."/"..SGEOUTPUTFILE
            local _errorfile = errorfile or errorlog
    
            local cmd_string = string.format( [[cd %s; %s]], SGAD_CONF.path, cmd)
    
            os.execute("chmod +x "..tmpscript)
    
            local exec_string = string.format( [[qsub -V -o %s -e %s %s > %s]],
                    _outfile, _errorfile, tmpscript, tmpfile)

	    servermanager.writeCmd( "Tentando execução:\n\t"..exec_string.."\n".."\t\t"..cmd_string.."\n" )

	    os.execute(exec_string)
	    local fd = io.open(tmpfile, "r")

	    local str = fd:read("*a")
	    print("STR:", str)
	    pid = str:match("%d+")
	    print("PID:", pid)
	    fd:close()
	end 

	local data = io.popen("hostname")
	local server = data:read("*a")
        local index = string.find(server, "%.")
        
        if index ~= nil then
            server = server:sub(1,server:find("%.")-1)
        end

	data:close()

	local handle ={
		cmdid = id,
		command = command,
		execHost = host,
		errorfile = errorlog,
		last_update = 0,
		pid = pid,
		start_time = servermanager.now(),
		server = server,
		--server = host
	}
	handle.pid = tonumber(handle.pid)
	return handle

end -- function executeCommand


--------------------------------------------------------------------------------
-- Exerce uma ação sobre um comando iniciado através da função executecommand.
--
-- @param handle o handle do comando.
-- @param action a ação que será feito sobre o comando. Os valores estão
--        definidos na enumeração JobControlAction:
--          SUSPEND: suspender a execução do comando;
--          RESUME: (re)iniciar o comando;
--          HOLD: alterar o estado do comando para on-hold;
--          RELEASE: retirar o comando do estado de on-hold;
--          TERMINATE: terminar o comando.
-- @param childId identificador do job filho no qual será feita a ação, ou nulo
-- se a ação deve ser feita no comando.
--
-- @return true se a ação foi exercida com sucesso e, se cocorrer algum erro,
--         false ou nil e a mensagem de erro.
-- @TODO pensar em como lançar as exceções ActionNotSupportedException, InvalidActionException e InvalidTransitionException
--
-- ok, ex = control(handle, action, childId)
--------------------------------------------------------------------------------
control = function(handle, action, childId)

  if type(handle) ~= "table" or not handle.cmdid then
     return nil, "Handle inválido."
  end

  -- suspend e resume não são suportadas pelo torquePBS
  if action==servermanager.SUSPEND or acton==servermanager.RESUME then
     return nil, "Ação não suportada."
  end

  -- para enviarmos um sinal a um job filho, basta termos o id do filho e o server
  if childId and childId~="" then
     local ok, err = __control(action, childId, handle.server)
     if not ok then
         return false, err
     else
         return true, nil
     end
  end

  -- enviar sinal a todos os filhos
  local cmds, err = servermanager._getcommandall(handle.cmdid,handle.server)
  local errors = nil
  if err then
     return nil, err
  end
  for i, cmdh in pairs(cmds) do
     local ok, err = __control(action, cmdh.pid, handle.server)
     if not ok then 
        errors = (not errors and err) or errors .. err
     end
  end

  if errors then
     return false, string.format("Ocorreu erro ao realizar ação \"%s\" sobre comando \"%s\" [%s].", action, handle.cmdid, errors)
  end

  return true, nil
end -- function control

__control = function (action, pid, server)
    local errors = nil
    local fname = os.tmpname()
    local command = nil
    local res = nil
    if action==servermanager.TERMINATE then
      command = string.format("qdel %s > %s", pid, fname)
      servermanager.writeMsg("Cancelando comando ["..command.."]")
      res = os.execute(command)
    elseif action==servermanager.HOLD then
      command = string.format("qhold %s > %s", cmdh.pid, fname)
      servermanager.writeMsg("Colocando hold no comando ["..command.."]")
      res = os.execute(command)
    elseif action==servermanager.RELEASE then
      command = string.format("qrls %s > %s", cmdh.pid, fname)
      servermanager.writeMsg("Liberando execução do comando ["..command.."]")
      res = os.execute(command)
    end

    if res~=0 and res ~=true then
      errors = true
      servermanager.writeError(string.format("Falha ao exercer ação \"%s\" sobre comando [%s]", action, handle.cmdid))
    else
      local fd = io.open(fname, "r")
      if not fd then
         local err = "Falha na leitura do arquivo temporário de ação."
         servermanager.writeError(err.." Arq.:["..fname.."]")
      else
         local str = fd:read("*a")
         if str~="" then
            errors = str
         end
      end
      fd:close()
      os.remove(fname)
    end

    if errors then
     return false, string.format("Ocorreu erro ao realizar ação \"%s\" sobre comando \"%s\" [%s].", action, handle.cmdid, errors)
    end

    return true, nil
end

--------------------------------------------------------------------------------
-- Retorna uma tabela Lua com os dados do comando que precisam ser persistidos.
-- Estes dados devem ser suficientes para que um comando seja 'recuperado' após
-- a reinicialização do SGA, através da chamada à retrievecommandhandle.
--
-- @param handle o handle do comando.
--
-- @return os dados do comando ou nil e mensagem de erro na ocorrência de erro.
--
-- pdata, err = getcommandpersistentdata(handle)
--------------------------------------------------------------------------------
getCommandPersistentData = function(handle)
  if type(handle) ~= "table" or not handle.cmdid then
      return nil, "Handle inválido."
   end
   return handle, nil
end -- function getCommandPersistentData

--------------------------------------------------------------------------------
-- A partir dos dados persistidos, retorna um handle que é a referência para o
-- comando no contexto desta biblioteca. O handle pode ter qualquer formato.
--
-- @param pdata os dados do comando.
--
-- @return o handle do comando ou nil e mensagem de erro na ocorrência de erro.
--
-- handle, err = retrievecommandhandle(pdata)
--------------------------------------------------------------------------------
retrieveCommandHandle = function(pdata)
  if type(pdata) ~= "table" or not pdata.cmdid then
      return nil, "Dados inválidos."
  end

  servermanager._updatecommands()

  local cmdhTable, err = servermanager._getcommandall( pdata.cmdid, pdata.server)
  if err then
     return nil, err
  end

  -- Diferente do pid de um processo, o jobid que usamos no pbs é
  -- gerado pelo nosso sistema. Devemos considerar que qualquer
  -- job que tenha este id faz parte da mesma tarefa...
  if #cmdhTable == 0 then
     return nil, "Comando não encontrado."
  end

  pdata.last_update = 0 

  return pdata, nil
end -- function retrieveCommandHandle

--------------------------------------------------------------------------------
-- Libera todos os recursos relacionados ao processo especificado. Esta função
-- precisa ser chamada após o término do processo para que eventuais recursos
-- alocados a ele possam ser liberados. Após a chamada desta função, o handle
-- do processo não poderá mais ser utilizado.

-- @return true se os recursos foram liberados com sucesso e, se cocorrer algum
--         erro, false ou nil e a mensagem de erro.
--
-- ok, err = releaseCommandResources(handle)
--------------------------------------------------------------------------------
releaseCommandResources = function(handle)
   return true, nil
end -- function releaseCommandResources

--------------------------------------------------------------------------------
-- Retorna as informações correspondentes às chaves e ao comando especificados.
--
-- @param handle o handle do comando.
-- @param keys a lista contendo as chaves das informações que serão obtidas. Se
--        a lista for nil ou vazia serão retornadas as informações padrões.
-- @param maxold indica o tempo máximo que a informação pode ter. Caso maxold
--        seja nil, é assumido zero e a informação será buscada.
--
-- @return o mapa contendo as chaves com o valor correspondente da informação e
--         uma mensagem em caso de erro.
--         As informações padrões são:
--           csbase_command_pid
--           csbase_command_string
--           csbase_command_exec_host
--           csbase_command_state
--           csbase_command_memory_ram_size_mb
--           csbase_command_memory_swap_size_mb
--           csbase_command_cpu_perc
--           csbase_command_time_sec
--           csbase_command_wall_time_sec
--           csbase_command_user_time_sec
--           csbase_command_system_time_sec
--         Além destas, podem-se adicionar informações padrões específicas de
--         plataforma.
--
-- Algunes detalhes sobre as informações padrões:
-- csbase_command_id:
--   identificador do comando recebido na função executecommand.
-- csbase_command_pid:
--   identificador do processo iniciado através da função executecommand.
-- csbase_command_ppid:
--   identificador do processo pai iniciado através da função executecommand.
-- csbase_command_string:
--   o comando em execução.
-- csbase_command_exec_host:
--   a máquina que está executando o comando.
-- csbase_command_state:
--   o estado de um processo iniciado através da função executecommand.
-- csbase_command_memory_ram_size_mb:
--   a média de ocupação de memória do último minuto (em MB).
-- csbase_command_memory_swap_size_mb:
--   a média de ocupação da área de swap do último minuto (em MB).
-- csbase_command_cpu_perc:
--   a taxa média de ocupação de CPU do último minuto pelo comando.
-- csbase_command_cpu_time_sec:
--   o tempo de CPU da execução do comando (em SEC).
-- csbase_command_wall_time_sec:
--   o tempo de relógio da execução do comando (em SEC).
-- csbase_command_user_time_sec:
--   o tempo de usuário da execução do comando (em SEC).
-- csbase_command_system_time_sec:
--   o tempo de systema da execução do comando (em SEC).
-- csbase_command_virtual_memory_size_mb:
--   quantidade de memória virtual utilizado na execução do comando (em MB).
-- csbase_command_bytes_in_kb:
--   quantidade de dados lidos na execução do comando (em KB).
-- csbase_command_bytes_out_kb:
--   quantidade de dados escritos na execução do comando (em KB).
-- csbase_command_disk_bytes_read_kb:
--   quantidade de dados lidos do disco na execução do comando (em KB).
-- csbase_command_disk_bytes_write_kb:
--   quantidade de dados escritos no disco na execução do comando (em KB).
--
-- map, err = getCommandInfo(handle, keys, maxold)
--------------------------------------------------------------------------------
getCommandInfo = function(handle, keys, maxold)

  if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
    servermanager._updatecommands()
    handle.last_update = servermanager.now()
  end

  if not keys or #keys == 0 then
     keys = servermanager.defaultKeys
  end -- if no keys

  local cmdid = servermanager.getcommandid(handle)
  local server = handle.server
  local cmdInfoMap = servermanager.__getCommandInfo(handle, keys, maxold, 0)

  local pid = cmdInfoMap["csbase_command_pid"]

  local fakeHandler = {cmdid = cmdid, server = server, start_time = handle.start_time}
  local children = {}

  if not server or not cmdid then
     print ("Sth really wrong...", "server", server, "cmdid", cmdid)
     os.exit()
  end
  
 --print("DEBUG---Server/cmd",server, cmdid, servermanager._cmds[server][cmdid]) 

  local cmds = servermanager._cmds[server][cmdid]
  if not cmds then 
     return cmdInfoMap, nil
  end

  for _, j in ipairs(servermanager._cmds[server][cmdid]) do
     --print ("------------\n possible child job found: " .. j.pid, j.job_state, j.cmdid)
     fakeHandler.pid = j.pid
     fakeHandler.last_update = j.last_update or 0
     local childJobMap = servermanager.__getCommandInfo(fakeHandler, keys, maxold, pid)
     if tonumber(j.pid) ~= handle.pid then
	     table.insert(children, childJobMap)
     end
  end
  return cmdInfoMap, children
end

__getCommandInfo = function (handle, keys, maxold, parentId)

  if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
    servermanager._updatecommands()
    handle.last_update = servermanager.now()
  end

  if not keys or #keys == 0 then
     keys = servermanager.defaultKeys
  end -- if no keys
--[[
  for _, key in ipairs (keys) do
	print("key", key)
  end
]]
  local cmdInfoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_command_id" then
      cmdInfoMap[k] = servermanager.getcommandid(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_pid" then
      cmdInfoMap[k] = servermanager.getcommandpid(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_ppid" then
      cmdInfoMap[k] = parentId or servermanager.ERROR_CODE
    elseif k == "csbase_command_string" then
      cmdInfoMap[k] = handle.command or servermanager.ERROR_CODE
    elseif k == "csbase_command_exec_host" then
      cmdInfoMap[k] = servermanager.getcommandexechost(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_state" then
      cmdInfoMap[k] = servermanager.getcommandstatus(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_memory_ram_size_mb" then
      local memory = servermanager.getcommandmemoryload(handle)
      cmdInfoMap[k] = (memory and memory.ram) or servermanager.ERROR_CODE
    elseif k == "csbase_command_memory_swap_size_mb" then
      local memory = servermanager.getcommandmemoryload(handle)
      cmdInfoMap[k] = (memory and memory.swap) or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_perc" then
      cmdInfoMap[k] = servermanager.getcommandcpuload(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_time_sec" then
      cmdInfoMap[k] = servermanager.getcommandcputimesec(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_wall_time_sec" then
      cmdInfoMap[k] = servermanager.getcommandwalltimesec(handle) or servermanager.ERROR_CODE
    elseif k == "csbase_command_user_time_sec" then
      local timeusage = servermanager.getcommandtimeusage(handle)
      cmdInfoMap[k] = (timeusage and timeusage.user) or servermanager.ERROR_CODE
    elseif k == "csbase_command_system_time_sec" then
      local timeusage = servermanager.getcommandtimeusage(handle)
      cmdInfoMap[k] = (timeusage and timeusage.system) or servermanager.ERROR_CODE
    end
  end
--[[
  for k, v in pairs(cmdInfoMap) do
     print ("cmdInfoMap", k, v)
  end
]]
  return cmdInfoMap, nil

end

--------------------------------------------------------------------------------
-- Define as chaves padrões de informações dinâmicas que serão usadas na função
-- getConfiguration, getInfo e getCommandInfo.
-- Caso o usuário não tenha defindo o conjunto de chaves padrões a biblioteca
-- deve ter um conjunto mínimo, que deverá conter as chaves:
--   csbase_load_perc
--   csbase_memory_ram_free_perc
--   csbase_memory_swap_free_perc
--   csbase_number_of_jobs
--
--   csbase_num_processors
--   csbase_memory_ram_info_mb
--   csbase_memory_swap_info_mb
--   csbase_job_control_actions
--   csbase_command_pid
--   csbase_command_string
--   csbase_command_exec_host
--   csbase_command_state
--   csbase_command_memory_ram_size_mb
--   csbase_command_memory_swap_size_mb
--   csbase_command_cpu_perc
--   csbase_command_time_sec
--   csbase_command_wall_time_sec
--   csbase_command_user_time_sec
--   csbase_command_system_time_sec
-- @param keys o conjunto de chaves de configuração
--------------------------------------------------------------------------------
setDefaultKeys = function(keys)
  if not keys or #keys==0 then
	return
  end
  defaultKeys = {}
  for _,v in ipairs(keys) do
	table.insert(defaultKeys, v)
  end
  servermanager.defaultKeys = defaultKeys
end -- function setDefaultKeys

--------------------------------------------------------------------------------
-- Retorna todas as chaves disponíveis.
--------------------------------------------------------------------------------
getAllKeys = function()
  defaultKeysCopy = {}
  for _,v in ipairs(servermanager.defaultKeys) do
	table.insert(defaultKeysCopy, v)
  end
  return defaultKeysCopy
end -- function getAllKeys

--------------------------------------------------------------------------------
--
-- Funções de consulta à clusters.
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Obtém as informações de todos os processos em execução no SGA.
-- ** Em desenvolvimento **
--------------------------------------------------------------------------------
getJobsInfo = function()
 --return nil, "Não oferecido por esta biblioteca."
 return nil
end

--------------------------------------------------------------------------------
-- Retorna uma lista com os nós do servidor especificado.
--
-- @param server o servidor. Caso o valor seja nil, assume localhost.
--
-- @return a lista de nós e ,em caso de erro, nil e a mensagem de erro.
--
-- nodes, err = getnodes(server)
--------------------------------------------------------------------------------
getNodes = function(server)
	debug("getnodes")
	local ids = {}
	local fname = os.tmpname()
	--local command = string.format('qconf -sel > %s', fname)
        queue = SGA_STATIC_CONF.queue_name or server
        local command = "qstat -f | grep "..queue.." | grep -v '.*d$' > "..fname
	os.execute( command )
	local fd = io.open(fname, "r")
	if not fd then
		return nil, "Falha na leitura do arquivo temporário.".." ["..fname.."]"
	end

	local l = fd:read("*a")	
	fd:close()
	os.remove(fname)

	--for node in string.gmatch(l, ".-%@(%w*[_,-]*%w*[_,-]*%w*)%.*.-[\n]") do
        for node in string.gmatch(l, ".-%@(.-)%s+.-[\n]") do
            table.insert(ids, node)
	end

-- START DEBUG
	print "----debugging nodes - start----"
	print ("Quantidade de nos obtidos", #ids)
	print ("Comando:", command)
	print "Saída do comando"
	print (l)
        print "----debugging nodes - end----"

-- END DEBUG
	if #ids > 0 then
		print "----debugging nodes - start----"
		for _, nodename in ipairs (ids) do
			print ("found node "..nodename)
		end
		print "----debugging nodes - end----"
		return ids, nil
	else
		return nil, "Não foi possível obter os nós do SGE"
	end
end

--------------------------------------------------------------------------------
--
-- Funções de log de histórico
--
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
-- Indica se o histórico deve ser ativado ou não para o SGA em questão.
--
-- setHistoric(historic)
--------------------------------------------------------------------------------
setHistoric = function(historic)
  servermanager._enableHistoric = historic
end

--------------------------------------------------------------------------------
-- Retorna true se o histórico deve ser ativado para o sga em questão,
-- ou false caso contrário.
--
-- enableHistoric = getHistoric()
--------------------------------------------------------------------------------
getHistoric = function()
  return servermanager._enableHistoric
end

--------------------------------------------------------------------------------





------------------------------------------------------------------------
--
-- Especificação das funções privadas
--
------------------------------------------------------------------------
_checkforerror = function (handle)
  if not handle.errorfile then
     return
  end
  -- Erro se colocamos o path absoluto ao chamar o qsub.
  local errorfile = handle.errorfile
  local str
  local fd = io.open(errorfile, "r")
  if not fd then
     servermanager.writeError( "Erro ao abrir arquivo temporário:\n"..
                              "\t["..tostring(errorfile).."]\n"..
                              "\tpara aquisição de dados de execução\n" )
     handle.errorfile = nil
     return
  end
  str = fd:read("*a")
  fd:close()
  os.remove(errorfile)
  handle.errorfile=nil
  if str and string.len(str) > 0 then
    local errormsg = string.format(
      "Possível erro na execução do comando %s:\n%s",
      handle.cmdid, str)
    servermanager.writeError(errormsg)
  end
end
 
_updatecommands = function( )
  local cmds = {}
        local fname = os.tmpname()
        --local command = string.format('qstat -xml > %s', fname)
        local command = string.format('qstat -q %s -xml > %s', SGA_STATIC_CONF.queue_name, fname)

	servermanager.writeMsg("Coletando info com ["..command.."]")
	
        -- Para resolver bug do SGE v6.2u5: error: commlib error: got select error (Connection refused)
        --                                  error: unable to send message to qmaster using port 6444 on host
        local qstat_error = 0
        local qstat_sub_error = 0
        local qacct_error = 0
        
        qstat_error = os.execute( command )

	local fd = io.open(fname, "r")
	
        if not fd then
	    servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
	    return
	end
        
        local l = fd:read("*a")	
	fd:close()
	os.remove(fname)

	local serverdata = io.popen("hostname")
	local server = serverdata:read("*a")
        local index = string.find(server, "%.")
        
        if index ~= nil then
            server = server:sub(1,server:find("%.")-1)
        end

	serverdata:close()

	for state, properties in string.gmatch(l, [[<job_list state="(.-)">(.-)</job_list>]]) do  	
		
                local tb = {}  			
                tb["job_state"] = string.gmatch(properties, "<state>(%w+)<")()
                tb["pid"] 	    = string.gmatch(properties, "<JB_job_number>(%w+)<")()

		 print("DEBUG---PID:", tb["pid"])

                tb["pid"]       = tonumber(tb["pid"])

		print("DEBUG---PID:", tb["pid"])
                local name 	    = string.gmatch(properties, "<JB_name>(.-)<")()
                
                --servermanager.writeMsg("#############################################################\n\nJob Rodando: "..name.."\n\n#################################################################")
                
                local queue     = string.gmatch(properties, "<queue_name>(.-)<")()
                --tb["exec_host"] = string.gmatch(queue, ".-@(.-)%..-")()
                tb["exec_host"] = string.gmatch(queue, ".-@(%w*[_,-]*%w*[_,-]*%w*)%.*.-")()
                
                --[[if tb["exec_host"] then
                    server = tb["exec_host"]
                end]]
                
                fname = os.tmpname()
                
                command = string.format('qstat -q %s -xml -j %d > %s', SGA_STATIC_CONF.queue_name, tb["pid"], fname)


		print("DEBUG ----",SGA_STATIC_CONF.queue_name, tb["pid"], fname)

                --servermanager.writeMsg("Coletando info com ["..command.."]")
                qstat_sub_error = os.execute( command )
                
                if qstat_sub_error ~= 0 then
                    break
                end

                local fd_jb = io.open(fname, "r")
   
                if not fd_jb then
                     servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
                end

                local l_jb = fd_jb:read("*a")	
                fd_jb:close()
                os.remove(fname)
                
                tb["resources_used.cput"]     = string.gmatch(l_jb, [[<UA_name>cpu</UA_name>[\n]*%s*<UA_value>(.-)</UA_value>]])()
                
                if not tb["resources_used.cput"] then
                    tb["resources_used.cput"] = 0.0
                end
                    
                tb["resources_used.walltime"] = string.gmatch(l_jb, [[<UA_name>ru_wallclock</UA_name>[\n]*%s*<UA_value>(.-)</UA_value>]])()
                
                local walltime = 0.0
                
                if tb["resources_used.walltime"] then                
                    walltime = tonumber(tb["resources_used.walltime"])                    
                end
                
                if walltime <= 0.0 then
                         tb["resources_used.walltime"] = tb["resources_used.cput"]
                         walltime = 1.0
                end
                
                local mem_seconds = string.gmatch(l_jb, [[<UA_name>mem</UA_name>[\n]*%s*<UA_value>(%d+)%.*%d*</UA_value>]])()
                
                if not mem_seconds then
                    mem_seconds = 0
                else
                    mem_seconds = tonumber(mem_seconds)
                end
                
                --servermanager.writeMsg("Mem Seconds: "..mem_seconds.."\tWalltime: "..walltime)
                
                tb["resources_used.mem"] 	= mem_seconds / walltime -- dividir pelo walltime
                
                tb["resources_used.mem"] 	= tb["resources_used.mem"].."B" --será que precisa converter para bytes?
                
                --servermanager.writeMsg("Memória usada do recurso: "..tb["resources_used.mem"])
                
                tb["resources_used.mem"]        = _getvalunit(tb["resources_used.mem"])
                    
                tb["resources_used.vmem"] 	  = string.gmatch(l_jb, [[<UA_name>vmem</UA_name>[\n]*%s*<UA_value>(%d+)%.*%d*</UA_value>]])() --será que precisa converter para bytes?
                
                if not tb["resources_used.vmem"] then
                    tb["resources_used.vmem"] = "0B"
                else
                    tb["resources_used.vmem"] = tb["resources_used.vmem"].."B"
                end
                
                tb["resources_used.vmem"]        = _getvalunit(tb["resources_used.vmem"])
                
                --servermanager.writeMsg("STATE: ---------------- "..tb["job_state"])
                
                -- Job travado na fila com erro
                if tb["job_state"] == "Eqw" then
                
                
                    -- Recuperando a sandbox do job.
                    local sandbox_path = _getJobSandboxPath(l_jb)
                    
                    --servermanager.writeMsg("Sandbox do stdout: "..sandbox_path)
                    
                    -- Recuperando a mensagem de erro do job em estado Eqw.
                    local error_msg = _getErrorMsg(l_jb)
                    
                    --servermanager.writeMsg("Eqw msg error: "..error_msg)
                    
                    -- Copiando mensagem de erro do escalonador para o arquivo de log padrão do comando CSGrid.
                    _putErrorMsg(sandbox_path, error_msg)
                    
                    -- Gerando código de erro de saída para o comando CSGrid.
                    _generateErrorExiCode(sandbox_path)
                    
                    -- Cancelando o job no escalonador
                    _killSGECommand(tb["pid"])
                    
                    -- Modificando o status do job para finsished
                    tb["job_state"] = "finished"
                
                end
                
                if not cmds[server] then
                    cmds[server] = {}
                end
                
                local cmdh = cmds[server]

		if not cmdh[name] then
			cmdh[name] = {}
		end
		
                table.insert(cmdh[name], tb)
                
	end


        ------------------------------------------------------------------------------------------------
        -- Tempo para que não cause preempção e o SGE consiga gravar o comando no arquivo de account  --
        ------------------------------------------------------------------------------------------------
        os.execute("sleep 5")
        
        ------------------------------------------------------------------------------------------------
        --  Recuperando jobs já terminados                                                            --
        ------------------------------------------------------------------------------------------------
	fname = os.tmpname()
        
        local username = os.getenv("USER")

        command = string.format('qacct -q %s -j -o %s > %s', SGA_STATIC_CONF.queue_name, username, fname)

	servermanager.writeMsg("Coletando info com ["..command.."]")
	
        
        qacct_error = os.execute( command )

	fd = io.open(fname, "r")
	
        if not fd then
	    servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
	    return
	end
        
        l = fd:read("*a")	
	fd:close()
	os.remove(fname)

	--for job, properties in string.gmatch(l, [[<job_list state="(.-)">(.-)</job_list>]]) do
        for queue, properties in string.gmatch(l, "qname%s*(%w*%.*%w*)%s*[\n](.-)arid%s*%w*%s*[\n]%=*") do
	--for queue, properties in string.gmatch(l, "qname%s*(%w*%.*%w*)%s*[\n](.-)[\n]%=+") do        

                --print("Fila: "..queue.."\nConteúdo: "..properties)
                local tb = {}
                tb["job_state"] = "finished"
                tb["pid"] = tonumber(string.gmatch(properties, [[jobnumber%s*(%d*)]])())
                --tb["exec_host"] = string.gmatch(properties, "hostname%s*(.-)%..-")()
                tb["exec_host"] = string.gmatch(properties, "hostname%s*(%w*[_,-]*%w*[_,-]*%w*)%.*.-")()
                local name = string.gmatch(properties, "jobname%s*(.-)%s*[\n]")()
                --servermanager.writeMsg("-------------------------------------------------------------\n\nJob Terminado: "..name.."\n\n-------------------------------------------------------------")
                
		--servermanager.writeMsg("Fila: "..queue.."\nConteúdo: "..properties)

		tb["resources_used.walltime"] 	= string.gmatch(properties, "ru_wallclock%s*(.-)%s*[\n]")()
                
                tb["resources_used.cput"] 	= string.gmatch(properties, "cpu%s*(.-)%s*[\n]")()
                
                local walltime = 0.0
                
                if tb["resources_used.walltime"] then                
                    walltime = tonumber(tb["resources_used.walltime"])                    
                end
                
                if walltime <= 0.0 then
                         tb["resources_used.walltime"] = tb["resources_used.cput"]
                         walltime = 1.0
                end
                
                local mem_seconds = string.gmatch(properties, "mem%s*(.-)%s*[\n]")()
                
                if not mem_seconds then
                    mem_seconds = 0.0
                else
                    mem_seconds = tonumber(mem_seconds)
                end
                
                tb["resources_used.mem"] 	= mem_seconds / walltime -- dividir pelo walltime e converter de Gbytes para bytes
                tb["resources_used.mem"] 	= tb["resources_used.mem"].."G" --converter G para bytes
                
                --servermanager.writeMsg("Memória usada do recurso (qacct): "..tb["resources_used.mem"])
                
                tb["resources_used.mem"]        = _getvalunit(tb["resources_used.mem"])
                
                tb["resources_used.vmem"] 	= string.gmatch(properties, "maxvmem%s*(.-)%s*[\n]")() -- converter M ou Gbytes para bytes
                
                tb["resources_used.vmem"]        = _getvalunit(tb["resources_used.vmem"])
                
                if not tb["resources_used.vmem"] or tb["resources_used.vmem"] == 0 then
                    tb["resources_used.vmem"] = "0B"
                end
                
                if not cmds[server] then
                    cmds[server] = {}
                end
                
                local cmdh = cmds[server]

		if not cmdh[name] then
			cmdh[name] = {}
		end
		
                table.insert(cmdh[name], tb)
        end

	print("DEBUG QSTAT", qstat_error, qstat_sub_error, qacct_error)
        
        --if qstat_error == 0 and qstat_sub_error == 0 and qacct_error == 0 then
            servermanager._cmds = cmds
        --else
        --    servermanager.writeError("Falha na execução do comando qstat.")
        --end
end

_commandsdump = function( )
   for host,cmds in pairs(servermanager._cmds) do
      for cmdid, procs in pairs(cmds) do
        print("Atributos do processo "..cmdid)
        for i,attrs in pairs(procs) do
          for attr, val in pairs(attrs) do
            print(attr, val)
          end
        end
      end
      print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
   end
end

_getcommandall = function( cmdid, host)
   if servermanager._cmds[host] then
      local cmdh = servermanager._cmds[host][cmdid]
      if type(cmdh) == "table" then
        return cmdh
      end
   end

   return nil, "Comando não encontrado."
end

_getcommand = function( cmdid, host) -- EM ABERTO: PEGO O 1o PROCESSO!
   local cmdh = servermanager._getcommandall( cmdid, host)
   if type(cmdh) == "table" and type(cmdh[1]) == "table" then
     return cmdh[1]
   end

   return nil, "Comando não encontrado."
end

_updatenode = function(node)
	local attr_table = {}
	--attr_table.name = node
	local domain = node.domain or SGAD_CONF.domain or ""
	local fname = os.tmpname()
	--local command = string.format('qhost -l hostname=%s%s -xml > %s', node.name, domain, fname)
        local command = string.format('qhost -h %s -xml > %s', node.name, fname)

	servermanager.writeMsg("Coletando info com ["..command.."]")
	os.execute( command )

	local fd = io.open(fname, "r")
	if not fd then
		servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
		return nil
	end

	--remove o header da tabela
	local str = fd:read("*a")
	fd:close()
	os.remove(fname)

	attr_table = servermanager._getnodeinfo(str, node.name)
	
	--TODO verificar como achar tamanho de tabela sem indices inteiros
	if attr_table.cpu then
		attr_table.state = true
	end
	
	return attr_table
end

_getnodeinfo = function(str, name)
	local node = {}
	node["name"] = name
	for name, value in string.gmatch(str, "<hostvalue name=\'*(.-)\'*>(.-)</hostvalue>") do 
		node[name] = value
                servermanager.writeMsg("Name: *"..name.."*\tValue: *"..value.."*")
	end

	return node
end

_getvalunit = function (str)
        
        for val,unit in string.gmatch(str, "(%d+%.*%d*)(%a*)") do 
		val = tonumber(val)
                unit = unit or "B"
		val = servermanager._memvaluetobytes(val, unit)
		return val	
	end

  return val
end

_memvaluetomegabytes = function (value,unit)
  unit = string.upper(unit)
  if unit == "T" then
    return value * (1024^2)
  elseif unit == "G" then
    return value * 1024
  elseif unit == "M" then
    return value
  elseif unit == "K" or unit=="k" then
    return value * (1024^-1)
  else
    return value
  end
end

_memvaluetobytes = function (value,unit)
  unit = string.upper(unit)
  if unit == "T" then
    return value * (1024^4)
  elseif unit == "G" then
    return value * (1024^3)
  elseif unit == "M" then
    return value * (1024^2)
  elseif unit == "K" then
    return value * 1024
  else
    return value
  end
end

_str2sec = function (str) -- Retorna segundos
  -- str pode ser "SS", "MM:SS" ou "HH:MM:SS"
  local tot = 0
  string.gsub(str, "(%d+)", function (n)
    tot = tot * 60 + n
  end)

  return tot
end

writeMsg = function(str)
  local msg = "SGAD ["..os.date("%d/%m/%y - %H:%M:%S").."] - "..str
  print(msg)
  io.flush()
end
writeError = writeMsg
writeCmd = writeMsg

------------------------------------------------------------------------
-- Retorna todas as informações sobre um determinado comando.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada 
-- no servidor que estiver executando o comando.
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- load, err = getcommandallinfo(handle, maxold)
------------------------------------------------------------------------
getcommandallinfo = function (handle, maxold)

   debug("getcommandallinfo")
        --servermanager.writeMsg("Entrei no getcommandallinfo()")
	if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
		--servermanager._updatecommands(handle)
                servermanager._updatecommands()
		handle.last_update = servermanager.now()
	end

	local cmds, err = servermanager._getcommandall(handle.cmdid, handle.server)
		if err then
		return nil, err
	end

	local procs = {}
        
        --servermanager.writeMsg("Memória total: "..SGAD_CONF.node_defaults.memory_ram_info_mb)
        
	for i, cmdh in pairs(cmds) do
		local ehost = cmdh["exec_host"] or "unknown"
		local ram = tonumber(cmdh["resources_used.mem"]) or servermanager.ERROR_CODE
		local swap = tonumber(cmdh["resources_used.vmem"]) or servermanager.ERROR_CODE
		local cput = tonumber(cmdh["resources_used.cput"]) or servermanager.ERROR_CODE
		local walltime = tonumber(cmdh["resources_used.walltime"]) or servermanager.ERROR_CODE
		local cpuperc = 0
                
                --print("\tEhost: "..ehost.."\n\tRam: "..ram.."\n\tSwap: "..swap.."\n\tCpu time: "..cput.."\n\tWalltime: "..walltime)
                
                --if walltime <= 0.0 then
                --    walltime = 1.0
                --end

		--if walltime > 0 then
			local server = cmdh["exec_host"] or handle.server

			print("DEBUG --SERVER", server, type(server))
                        --local server = cmdh["exec_host"]
			--print("On GetCPU")
			local ncpus, err = servermanager.getnumcpus(server)
			ncpus = ncpus or 1

			cpuperc = (cput / ncpus) / walltime
		--end
		
		local state = servermanager.NOT_RESPONDING
		--local jobstate = cmdh.job_state
                local jobstate = cmdh["job_state"]
		if jobstate == "r" then
			state = servermanager.RUNNING
		elseif jobstate == "qw" then
			state = servermanager.WAITING
                --else
		elseif jobstate == "finished" then
			state = servermanager.FINISHED
		end

		local procinfo = {
			--pid = tonumber(cmdh.pid),
                        pid = tonumber(cmdh["pid"]),
			ppid = 0, -- XXX IMPORTANTE
			command = handle.command,
			execHost = ehost,
			state = state,
			processorId = 0, -- XXX 
			memoryRamSizeMb = ram / (1024 * 1024),
			memorySwapSizeMb = swap / (1024 * 1024),
			CPUPerc = cpuperc,
			CPUTimeSec = tonumber(cput),
			wallTimeSec = walltime,
		}
		table.insert(procs, procinfo)
	end

	if #procs < 1 then
		return nil, "Não há informações sobre esse comando."
	end
	-- EM ABERTO: Alteramos o valor do campo walltime do primeiro
	-- processo!
	-- Não podemos usar o walltime dos processos. Usamos um walltime
	-- global do job, considerando a chamada da executecommand.
	--procs[1].wallTimeSec = servermanager.now() - handle.start_time
        
        --servermanager.writeMsg("Sai do getcommandallinfo()")
        
	return procs
end

getnumcpus = function (server)
   --if not ninfo then
   --   return nil, servermanagier.NO_SERVER
   --end

--   servermanager.writeMsg("##############Procurando num de cpus para o nó: "..ninfo.name)

   return _getNumProcs(server)
end

getrammemory = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   if ninfo.physmem then
      return servermanager._getvalunit(ninfo.mem_total), nil
   end
   return nil, nil
end

getswapmemory = function (ninfo)
   --[[local tmem = servermanager.gettotalmemory(ninfo)
   local ram = servermanager.getrammemory(ninfo)
   if tmem and ram and type(tmem)=="number" and type(ram)=="number" then
      return tmem-ram
   end]]

   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   if ninfo.totmem then
      return servermanager._getvalunit(ninfo.swap_total), nil
   end

   return nil, servermanager.NO_SERVER
end

_getNumProcs = function (node)
    
    local fname = os.tmpname()

    --local command = string.format('qhost -h %s -xml > %s', node.name, fname)
    local command = "qstat -f | grep "..SGA_STATIC_CONF.queue_name.." | grep "..node.." > "..fname

    --servermanager.writeMsg("Coletando info com ["..command.."]")
    os.execute( command )

    local fd = io.open(fname, "r")
    if not fd then
	servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
	return nil
    end

    --remove o header da tabela
    local str = fd:read("*a")
    fd:close()
    os.remove(fname)
    
    local num_procs = string.gmatch(str, ".-%d+%/%d+%/(%d+).-[\n]")() or 0
    
    --servermanager.writeMsg("Número de jobs no nó: "..server.." é: "..njobs)
    
    --return nil, "Não oferecido por esta biblioteca."
    --return 0, "Não oferecido por esta biblioteca."
    return tonumber(num_procs)
end

getnumjobs = function (ninfo)
   if not ninfo then
        return nil, servermanager.NO_SERVER
    end
    
    local fname = os.tmpname()

    --local command = string.format('qhost -h %s -xml > %s', node.name, fname)
    local command = "qstat -f | grep ".. ninfo.name .." > "..fname

    servermanager.writeMsg("Coletando info de numero de jobs com ["..command.."]")
    os.execute( command )

    local fd = io.open(fname, "r")
    if not fd then
	servermanager.writeError("Falha na leitura do arquivo temporário.".." ["..fname.."]")
	return nil
    end

    --remove o header da tabela
    local str = fd:read("*a")
    fd:close()
    os.remove(fname)
    
    --local njobs = string.gmatch(str, ".-%d+%/(%d+)%/%d+.-[\n]")() or 0
    local njobs = 0
    
    local nprocs = _getNumProcs(ninfo.name)

    for value in string.gmatch(str, ".-%d+%/(%d+)%/%d+.-[\n]") do
        njobs = njobs + tonumber(value)
        --servermanager.writeMsg("Name: *"..name.."*\tValue: *"..value.."*")
    end

    --servermanager.writeMsg("Número de jobs no nó: "..server.." é: "..njobs)

    if tonumber(njobs) > tonumber(nprocs) then
        return tonumber(nprocs)
    end

    --return nil, "Não oferecido por esta biblioteca."
    --return 0, "Não oferecido por esta biblioteca."
    return tonumber(njobs), nil
end

getfreerammemory = function (ninfo)
   local memload = getmemoryload(ninfo)
   local totalram = getrammemory(ninfo)
   if memload and memload.ram and totalram then
	return totalram-memload.ram
   end
   return nil, "Não foi possível obter RAM disponível"
end

getfreeswapmemory = function (ninfo)
   local memload = getmemoryload(ninfo)
   local totalswap = getswapmemory(ninfo)
   if memload and memload.swap and totalswap then
	return totalswap-memload.swap
   end
   return nil, "Não foi possível obter SWAP disponível"
end

getcpuload = function (ninfo)
	if not ninfo.isonline then
		--return nil, "Nó não está online (aquisição de CPU)!"
                return nil, "down"
	end
	if not ninfo.cpuload then
		return nil, "Falha de aquisição de carga de CPU!"
	end
	return ninfo.cpuload, nil
end

getmemoryload = function (ninfo)
	if not ninfo.isonline then
		--return nil, "Nó não está online (aquisição de memória)!"
                return nil, "down"
	end
	if not ninfo.memoryload then
		return nil, "Falha de aquisição de carga da memória!"
	end
	return ninfo.memoryload, nil
end

------------------------------------------------------------------------
-- Retorna o identificador do processo recebido na função
-- executecommand.
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- id, err = getcommandid(handle)
------------------------------------------------------------------------
getcommandid = function (handle)
	debug("getcommandid")
	if type(handle) ~= "table" or not handle.cmdid then
		return nil, "Handle inválido."
	end

	return handle.cmdid, nil
end

------------------------------------------------------------------------
-- Retorna um identificador unico do processo iniciado através da função
-- executecommand.
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- pid, err = getcommandpid(handle)
------------------------------------------------------------------------
getcommandpid = function (handle)
   if type(handle) ~= "table" or not handle.pid or
      not tonumber(handle.pid) then
      return nil, "Handle inválido."
   end

   return tonumber(handle.pid), nil
end

------------------------------------------------------------------------
-- Retorna o estado de um processo iniciado através da função 
-- executecommand.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando.
--
-- O retorno pode ser RUNNING, NOT_RESPONDING, WAITING ou FINISHED. 
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- job_state The state of the job.
-- 'Q'    /* Job Queued record */
-- 'S'    /* Job run (Started) */
-- 'R'    /* Job Rerun record */
-- 'C'    /* Job Checkpointed and held */
-- 'T'    /* Job resTart (from chkpnt) record */
-- 'E'    /* Job Ended/usage record */
-- 'D'    /* Job Deleted by request */
-- 'A'    /* Job Abort by server */
--
-- status, err = getcommandstatus(handle, maxold)
------------------------------------------------------------------------
getcommandstatus = function (handle)

	local cmdhTable, err = servermanager._getcommandall( handle.cmdid, handle.server)
	if err then
		return nil, err
	end

	local status = servermanager.NOT_RESPONDING
	for i, cmdh in pairs(cmdhTable) do
		local state = cmdh.job_state
		if state == "r" then
			status = servermanager.RUNNING
		elseif state == "qw" then
			status = servermanager.WAITING
                        
                --else
                elseif state == "finished" then 
			status = servermanager.FINISHED
                end
	end

	return status
end

------------------------------------------------------------------------
-- Retorna a maquina que esta executando o comando definido por handle.
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando. Em caso de erro, retorna
-- nil e a mensagem de erro.
--
-- host, err = getcommandexechost(handle, maxold)
------------------------------------------------------------------------
getcommandexechost = function (handle)
	debug("getcommandexechost")
	if handle.host then
		return handle.host
	end

	local cmds, err = servermanager._getcommandall(handle.cmdid,handle.server)
	if err then
		return nil, err
	end

	local exectab = {}
	local exechost
	for i, cmdh in pairs(cmds) do
		local ehost = cmdh["exec_host"]
		if ehost and not exectab[ehost] then
			exectab[ehost] = true
			if exechost then
				exechost = exechost .. "/" .. ehost
			else
				exechost = ehost
			end
		end
	end
	handle.host = exechost
	return handle.host
end

------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de memória do último minuto pelo
-- processo especificado. O retorno é uma tabela contendo o total de
-- memória RAM e de Swap, no formato:
--     { ram = xxx, swap = yyy }
---------------------------------
-- MAS QUAL A UNIDADE?!?! Bytes?!
---------------------------------
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando. 
-- Em caso de erro, retorna nil e a mensagem de erro.
--
-- load, err = getcommandmemoryload(handle, maxold)
------------------------------------------------------------------------
getcommandmemoryload = function (handle)
	debug("getcommandmemoryload")
	-- -- EM ABERTO: Como vamos fazer o somatório nesse caso?!
	-- local cmdh, err = servermanager._getcommand(handle.cmdid,handle.server)
	-- if err then
	--    return nil, err
	-- end
	-- Fazemos um somatório das memórias de todos os nós envolvidos no jobs.
	local cmds, err = servermanager._getcommandall(handle.cmdid,handle.server)
	if err then
		return nil, err
	end

	local totram
	local totswap
	for i, cmdh in pairs(cmds) do
		local ram = cmdh["resources_used.mem"]
		local swap = cmdh["resources_used.vmem"]
		if ram then
			totram = (totram or 0) + ram
		end
		if swap then
			totswap = (totswap or 0) + swap
		end
	end
	--totram = totram or servermanager.ERROR_CODE
	--totswap = totswap or servermanager.ERROR_CODE
        host_memory = SGAD_CONF.node_defaults.memory_ram_info_mb
        host_swap = SGAD_CONF.node_defaults.memory_swap_info_mb
        
        --servermanager.writeMsg("Ram: "..totram.."\tSwap: "..totswap)
        
        -- modificação dividir pelo numero de processos para conseguir a média
        --totram = (totram / host_memory / #cmds) * 100 or servermanager.ERROR_CODE
	--totswap = (totswap / host_swap / #cmds) * 100 or servermanager.ERROR_CODE
        totram = (totram / host_memory) * 100 or servermanager.ERROR_CODE
	totswap = (totswap / host_swap) * 100 or servermanager.ERROR_CODE
	return { ram = totram, swap = totswap}
end

------------------------------------------------------------------------
-- Retorna os tempos de execução do processo especificado. O retorno é
-- uma tabela contendo os tempos de usuário, sistema e total (tempo de
-- parede), no formato:
--     { user = xxx, system = yyy, elapsed = zzz }
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando.  Em caso de erro, retorna
-- nil e a mensagem de erro.
--
-- time, err = getcommandtimeusage(handle, maxold)
------------------------------------------------------------------------------
getcommandtimeusage = function (handle)
	debug("getcommandtimeusage")
        --servermanager.writeMsg("Maxold: "..maxold.."\tTempo de agora: "..servermanager.now().."\tTempo da ultima atualização: "..handle.last_update)

	---- EM ABERTO: Como vamos fazer o somatório nesse caso?!
	-- local cmdh, err = servermanager._getcommand(handle.cmdid,handle.server)
	-- if err then
	--    return nil, err
	-- end
	-- local cput = cmdh["resources_used.cput"] or servermanager.ERROR_CODE
	-- local walltime = cmdh["resources_used.walltime"] or servermanager.ERROR_CODE

	local cmds, err = servermanager._getcommandall(handle.cmdid,handle.server)
	if err then
		return nil, err
	end

	local cput
        --local walltime
        
	for i, cmdh in pairs(cmds) do
		if cmdh["resources_used.cput"] then
			cput = (cput or 0) + cmdh["resources_used.cput"]
                        walltime = (walltime or 0) + cmdh["resources_used.walltime"]
                        --servermanager.writeMsg("Ram: "..cmdh["resources_used.mem"].."\tSwap: "..cmdh["resources_used.vmem"])
		end
	end
	local walltime = servermanager.now() - handle.start_time
        --walltime = walltime / #cmds
        
        --servermanager.writeMsg("Walltime: "..walltime)

        --modificação para dividir pelo número de processos para conseguir a média
	local system = cput / #cmds or servermanager.ERROR_CODE
	local real = walltime
	local info = {
		user = system,
		system = system,
		elapsed = real,
	}

	return info, nil
end


------------------------------------------------------------------------
-- Retorna a taxa média de ocupação de CPU do último minuto pelo
-- processo especificado. Esse valor considera o número de processadores
-- que o servidor possui. Por exemplo, caso o processo execute em apenas
-- um processador, este número estará dividido pela quantidade de
-- processadores.  Caso o servidor seja nil, assume localhost.
--
-- O parâmetro maxold indica o tempo máximo que a informação pode ter. 
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor que estiver executando o comando.  Em caso de erro, retorna
-- nil e a mensagem de erro.
--
-- load, err = getcommandcpuload(handle, maxold)
------------------------------------------------------------------------
getcommandcpuload = function (handle)
	debug("getcommandcpuload")
	-- Não tenho a ocupação do último minuto noSGE.
	-- Vou usar cput/walltime
	local procs, err = servermanager.getcommandallinfo(handle.cmdid, handle.server)
	if err then
		return nil, err
	end

	if not procs or #procs == 0 then
		return 0
	end
	local totcpuperc = 0
	for i, proc in pairs(procs) do
		totcpuperc = totcpuperc + proc.CPUPerc
	end
	return totcpuperc / #procs
end

getcommandwalltimesec = function (handle)
   local procs, err = servermanager.getcommandallinfo(handle, maxold)
   if not procs then
      return 0
   end
   return tonumber(procs[1].wallTimeSec)
end

getcommandcputimesec = function (handle)
   local procs, err = servermanager.getcommandallinfo(handle, maxold)
   if not procs then
      return 0
   end
   return tonumber(procs[1].CPUTimeSec)
end

------------------------------------------------------------------------
-- _update(server, maxload)
-- O parâmetro maxold indica o tempo máximo que a informação pode ter.
-- Caso maxold seja nil, é assumido zero e a informação será buscada no
-- servidor em questão.
--
-- Campos estáticos do nó:
-- numcpus
-- memory
--          { ram = xxx, swap = yyy }
-- byteorder
--          LITTLE_ENDIAN ou BIG_ENDIAN.
--
-- Campos dinâmicos do nó:
-- cpuload
-- memoryload
--          { ram = xxx, swap = yyy }
------------------------------------------------------------------------
_update = function(node, freq)
	local errcode = servermanager.ERROR_CODE

	print("Node name: "..node.name)
	local nodeinfo = servermanager._getnode(node.name)

	--print("Node name2: "..nodeinfo)

	for key,value in pairs(nodeinfo) do
		--print("Key: "..key.."\tValue: *"..value.."*")
		 print ("Key: "..key)
	end

	if freq and (servermanager.now() - nodeinfo.last_update) < freq then
		return nodeinfo
	end

	nodeinfo.last_update = servermanager.now()
	local NUMBER = type(0)
	-----
	local t = servermanager._updatenode(nodeinfo)
	if not t then
                nodeinfo.isonline = false
		return nodeinfo
	end

        if not _disableNode(node) then
            -- Load Average
            local _load = string.gsub(t.load_avg or "", "(%d+)", "%1")
            _load = tonumber(_load)
            --_load = tonumber(t.load_avg)
            if type(_load) == NUMBER then 
                nodeinfo.cpuload = _load
                nodeinfo.isonline = true
            else
                nodeinfo.cpuload = 0
                nodeinfo.isonline = false
            end
    
            -- Memory Use
            local _ram = servermanager._getvalunit(t.mem_used or "")
            local _swap = servermanager._getvalunit(t.swap_used or "")
            if type(_swap) ~= NUMBER or type(_ram) ~= NUMBER  then
                    nodeinfo.memoryload = {ram = errcode, swap = errcode}
                    nodeinfo.isonline= false
            else
                    nodeinfo.memoryload = {ram = _ram, swap = _swap}
                    nodeinfo.isonline = true
            end
        else
            nodeinfo.isonline = false
        end

	return nodeinfo
end

_getnode = function(server)

        --servermanager.writeMsg("Node: "..server)
	--server = server or servermanager.name
        server = server --or S
	local ninfo = servermanager._nodes[server]
	if not ninfo then
		ninfo = servermanager._add(server)
	end
	
	return ninfo
end

_add = function(nodename)
	local errcode = servermanager.ERROR_CODE
	
	print ("Antes do if do _add")

	if not servermanager._nodes[nodename] then
		print("Dentro do if do _add")
		servermanager._nodes[nodename] = {name = nodename}
		servermanager._nodes[nodename] = servermanager._updatenode(servermanager._nodes[nodename])	
	end

	print("Depois do if do _add")

	return servermanager._nodes[nodename]
end

_getnodeconf = function(name)

	-- XXX
	local i = 1
	while SGAD_CONF.nodes[i] and
		SGAD_CONF.nodes[i].name ~= name do
		i = i + 1
	end
	
	return SGAD_CONF.nodes[i]
	-- XXX
end

--------------------------------------------------------------------------------

local defaultKeys = {
   "csbase_load_perc",
   "csbase_memory_ram_free_perc",
   "csbase_memory_swap_free_perc",
   "csbase_number_of_jobs",

   "csbase_num_processors",
   "csbase_memory_ram_info_mb",
   "csbase_memory_swap_info_mb",
   "csbase_job_control_actions",
   "csbase_command_pid",
   "csbase_command_string",
   "csbase_command_exec_host",
   "csbase_command_state",
   "csbase_command_memory_ram_size_mb",
   "csbase_command_memory_swap_size_mb",
   "csbase_command_cpu_perc",
   "csbase_command_time_sec",
   "csbase_command_wall_time_sec",
   "csbase_command_user_time_sec",
   "csbase_command_system_time_sec",
}

servermanager = {
  -------------
  -- private --
  -------------
  _enableHistoric = false,

  -----------------------
  -- Constantes do módulo
  -----------------------
  RUNNING = 0,
  NOT_RESPONDING = 1,
  WAITING = 2,
  FINISHED = 3,

  ERROR_CODE = -1,

  -----------------------
  -- ACTIONS
  -----------------------
  SUSPEND = "SUSPEND",
  RESUME = "RESUME",
  HOLD = "HOLD",
  RELEASE = "RELEASE",
  TERMINATE = "TERMINATE",

  -------------
  -- public --
  -------------
  now = now, -- Definida em C nos binários para Unix.
  sleep = sleep, -- Definida em C nos binários para Unix.

  -- Funções de controle do módulo:
  open = open,
  close = close,

  -- Funções de consulta à configuração de servidor:
  getConfiguration = getConfiguration,

  -- Funções de monitoração de servidor:
  getInfo = getInfo,

  -- Funções de execução, monitoração e controle de processos:
  executeCommand = executeCommand,
  control = control,
  getCommandPersistentData = getCommandPersistentData,
  retrieveCommandHandle = retrieveCommandHandle,
  releaseCommandResources = releaseCommandResources,
  getCommandInfo = getCommandInfo,

  --
  setDefaultKeys = setDefaultKeys,
  getAllKeys = getAllKeys,

  -- Funções para clusters:
  getJobsInfo = getJobsInfo,
  getNodes = getNodes, -- OPCIONAL

  -- Funções de log de histórico:
  setHistoric = setHistoric, -- OPCIONAL
  getHistoric = getHistoric, -- OPCIONAL

  -- API antiga
  NO_SERVER= "Servidor não existente", -- Colocar num arquivo de mensagens?
  _nodes   = {},
  _cmds    = {},
  _add = _add,
  _commandsdump = _commandsdump,
  _getcommand = _getcommand,
  _getcommandall = _getcommandall,
  _getnode = _getnode,
  _getnodeconf = _getnodeconf,
  _getnodeinfo = _getnodeinfo,
  _getvalunit = _getvalunit,
  _memvaluetobytes = _memvaluetobytes,
  _memvaluetomegabytes = _memvaluetomegabytes,
  _str2sec = _str2sec,
  _update = _update,
  _updatecommand = _updatecommand,
  _updatecommands = _updatecommands,
  _updatenode = _updatenode,
  _checkforerror = _checkforerror,
  __getCommandInfo = __getCommandInfo,
  getnumcpus = getnumcpus,
  getnumjobs = getnumjobs,
  getrammemory = getrammemory,
  getswapmemory = getswapmemory,
  getfreerammemory = getfreerammemory,
  getfreeswapmemory = getfreeswapmemory,
  gettotalmemory = gettotalmemory,
  getcpuload = getcpuload,
  getmemoryload = getmemoryload,

  writeMsg = writeMsg,
  writeError = writeError,
  writeCmd = writeCmd,

  -- Funções de execução, monitoração e controle de processos:
  getcommandallinfo = getcommandallinfo,               -- EXTRA
  getcommandid = getcommandid,
  getcommandpid = getcommandpid,
  getcommandstatus = getcommandstatus,
  getcommandexechost = getcommandexechost,
  getcommandcpuload = getcommandcpuload,
  getcommandmemoryload = getcommandmemoryload,
  getcommandtimeusage = getcommandtimeusage,
  getcommandwalltimesec = getcommandwalltimesec,
  getcommandcputimesec = getcommandcputimesec,

  defaultKeys = defaultKeys,
  
}
