--------------------------------------------------------------------------------
--
-- EspecificaÃ§Ã£o das funÃ§Ãµes pÃºblicas
--
--------------------------------------------------------------------------------
local ericaPrint = function (...)
  print ("[refactor]", ...)
end

local ericaPrintParam = function (functionName, paramName, param)
  local printable = "  " .. functionName .. ": param \"" .. paramName .. "\" type: " .. type(param)
  if (type(param)~="table" and type(param)~="function" and type(param)~="nil") then
    printable = printable .. "; value = " .. param
  elseif (param==nil) then
    printable = printable .. "; value = nil"
  end
  ericaPrint (printable)

  if type(param)=="table" then
    for k, v in pairs(param) do
      ericaPrint("\t key: " .. k, "\t value: " .. v)
    end
  end
end

DRMAA_OUTPUT_FILE = "output_file.txt"
DRMAA_ERROR_FILE = "error_file.txt"

--------------------------------------------------------------------------------
-- 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()
--  require "genericloader"
--  local genericLoaderOpen = assert(package.loadlib("genericloader.so", "luaopen_genericloader"))
--  genericLoaderOpen()
  require "drmaa"
  local ok, err = drmaa_init()
  if not ok then
     servermanager.writeError("Erro ao inicializar sessÃ£o DRMAA: " .. err)
     return false, nil
  end

  --require "slurm_extralib"
  --print "Opened slurm extra lib"

  servermanager.writeMsg("SessÃ£o DRMAA inicializada com sucesso.")

  if SGAD_CONF.drmaa_extra_lib then
	ok, err = load_as_global(DRMSystem.."extralib.so", "luaopen_extralib")
  	if not ok then
        	print("Error: Could not open lib \'"..SGAD_CONF.drmaa_extra_lib.."\' found in SGA configuration file", err)
        	os.exit()
  	end
  else
	local DRMSystem = drmaa_get_DRM_system()
	print("DRM system detected:", DRMSystem)
	local name = string.gsub(DRMSystem, "(%w)%s(.*)", "%1")
	local libname = ""
        if name=="SLURM" then
		libname = "SLURM_extralib.so"
	elseif name== "Torque" then
		libname = "Torque_extralib.so"
	end
	ok, err = load_as_global(libname, "luaopen_extralib")
	if not ok then
		print("Error: Could not open default extra library for ".. DRMSystem, err)
		os.exit()
	end
  end

  print ">>>>>>>>>>>> Teste de corrupção de memória >>>>>>>>>>>>"
  nodes, err = getNodes()
  nodes, err = nil, nil
  print ("Memory", collectgarbage("count"))
  collectgarbage("collect")
  print "Collected."
  print ("Memory", collectgarbage("count"))

  nodes, err = getNodes()
  nodes, err = nil, nil
 -- getInfo("torquepbs")
  print ("Memory", collectgarbage("count"))
  collectgarbage("collect")
  print "Collected."
  print ("Memory", collectgarbage("count"))
  print "<<<<<<<<<< Fim Teste de corrupção de memória <<<<<<<<<<"
--  print "Opened Extralib"

  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()
  local ok, err = drmaa_exit()
  if not ok then
     servermanager.writeError("Erro ao encerrar sessÃ£o DRMAA: " .. err)
     return false, nil
  end
  servermanager.writeMsg("SessÃ£o DRMAA encerrada com sucesso.")
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)
  -- @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";
  print ("getConfiguration", "server", server)
  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",
    }, nil
  end -- if no keys

  nodesTable, err = get_all_machines()
  if not nodesTable then 
     return nil, err
  end

  local nodeT = nil

  for _, node in ipairs(nodesTable) do
     --print("name", node.Name)
     if node.Name==server then
         nodeT = node
	 break
     end
  end

  for k, v in pairs (nodeT) do
	print("", k, v)
  end

  local configMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_num_processors" then
      configMap[k] = nodeT.CPUs or servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_info_mb" then
      configMap[k] = nodeT.REALMEMORY or servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_info_mb" then
      configMap[k] = nodeT.SwapMemory or servermanager.ERROR_CODE
    elseif k == "csbase_job_control_actions" then
      configMap[k] = {"TERMINATE", "HOLD", "RELEASE", }
    end
  end
--[[
  print ("Memory", collectgarbage("count"))
  collectgarbage("collect")
  print "Collected."
  print ("Memory", collectgarbage("count"))
--]]
  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)
  print("getInfo", "server", server)
  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

  nodesTable, err = get_all_machines()
  if not nodesTable then 
     return nil, err
  end

  local nodeT = nil

  for _, node in ipairs(nodesTable) do
     print(node.Name)
     if node.Name==server then
         nodeT = node
         print "found node"
	 break
     end
  end

  local infoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_load_perc" then
      infoMap[k] = nodeT.CPULoad or servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_free" then
      infoMap[k] = nodeT.FreeRam or servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_free" then
      infoMap[k] = nodeT.FreeSwap or servermanager.ERROR_CODE
    elseif k == "csbase_number_of_jobs" then
      infoMap[k] = nodeT.NumJobs or servermanager.ERROR_CODE
    end
  end

  for k, v in pairs (infoMap) do
	print(k, v)
  end

  return infoMap, nil
end -- function getInfo

--------------------------------------------------------------------------------
--
-- 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)

  ericaPrintParam("executeCommand", "id", id)
  ericaPrintParam("executeCommand", "command", command)
  ericaPrintParam("executeCommand", "extraParams", extraParams)
  ericaPrintParam("executeCommand", "server", 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";

  -- Cria arquivo temporÃ¡rio com comando para ser passado com o drmaa

  local tmpfile = os.tmpname()
  local fd = io.open(tmpfile, "w")
  if not fd then
     local strerror = "Erro ao abrir arquivo temporÃ¡rio:\n" .. "\t[" .. tostring(tmpfile).. "]\n" .. "\tpara escrita de comando\n" 
     servermanager.writeError(strerror)
     return nil, strerror
  end
  fd:write("#!/bin/bash\ndate\nksh -c \'" .. command .. "\'\ndate")
  fd:close()

  os.execute(string.format("chmod 777 %s", tmpfile))

  -- Descobre nome da mÃ¡quina local, se nÃ£o for passado
  if not server then
     local serverfile = os.tmpname()
     local res = os.execute(string.format("hostname > %s", serverfile))
     if  res~= 0 and res~=true then
          local err = "Erro ao obter nome da mÃ¡quina"
          servermanager.writeError(err)
          return nil , err
     end
     local fd = io.open(serverfile, "r")
     if not fd then
          local err = "Erro ao abrir arquivo \"".. serverfile .. "\" para obtenÃ§Ã£o da mÃ¡quina"
          servermanager.writeError(err)
          return nil , err
     end
     server = fd:read("*a")
     fd:close()
     os.remove(serverfile)
     if not server then
        local err = "Erro ao obter nome da mÃ¡quina"
        servermanager.writeError(err)
        return nil , err
     end
  end

  -- Cria Descritor do Job
  local jobDescriptor = {}
  jobDescriptor.CommandFile = tmpfile
  jobDescriptor.Name = id
  jobDescriptor.OutputFile = string.format("@%s:%s", server, extraParams.csbase_command_output_path .. "/" .. DRMAA_OUTPUT_FILE)
  jobDescriptor.ErrorFile = string.format("@%s:%s", server, (extraParams.csbase_command_error_path or extraParams.csbase_command_output_path) .. "/" .. DRMAA_ERROR_FILE)
  jobDescriptor.InputFile = extraParams.csbase_command_input_path
  
  print "PrÃ© submissÃ£o"
  local jobId, err = drmaa_run_job(jobDescriptor)
  print "PÃ³s submissÃ£o"

  if not jobId then
     servermanager.writeError("Erro ao submeter comando \"" .. command .. "\"")
     return nil, err
  end

  servermanager.writeMsg(string.format("Submetido comando \"%s\" \n\tID: %s\n\tFile: %s\n\tOutfile: %s", command, jobId, tmpfile, jobDescriptor.OutputFile)) 

  local handle = {
     cmdid = id,
     command = command,
     execHost = nil,
     errorfile = errorlog,
     last_update = 0,
     last_status_update = 0,
     pid = jobId,
     server = server,
     start_time = servermanager.now()
  }
  return handle, nil
end -- function executeCommand

--------------------------------------------------------------------------------
-- Excerce 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)
  -- Como seria a identificaÃ§Ã£o de jobs filhos em qualquer DRM que tenha implementaÃ§Ã£o DRMAA1?
  local ok, err = drmaa_control(handle.pid, action)

  print("control", handle.pid, ok, err)

  if not ok then
     servermanager.writeError(string.format("Erro ao enviar sinal %s ao comando %s: %s", action, handle.pid, err))
     return false, err
  end

  return true, nil
end -- function control

--------------------------------------------------------------------------------
-- 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)
  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)
  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 leberados 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)
--------------------------------------------------------------------------------
updateAllCommands = function ()
   if get_all_jobs and type(get_all_jobs)=="function" then
      servermanager.COMMANDS = {}
      local commands, err = get_all_jobs()
      if not commands then
          servermanager.writeError("Erro ao obter informaÃ§Ãµes dos comandos: "..err)
          return nil, err
      end
      servermanager.COMMANDS = commands
   else
      local err = "Erro ao buscar funÃ§Ã£o \'get_all_jobs\'. Verifique se biblioteca extra foi encontrada."
      servermanager.writeError(err)
      return nil, err
   end
end

getCommandInfo = function(handle, keys, maxold)
  if not keys or #keys == 0 then
    keys = servermanager.defaultKeys
  end -- if no keys

  local searchedCommand = {}
  if (not maxold) or ((servermanager.now() - handle.last_update) > maxold) then
      servermanager.updateAllCommands()
      handle.last_status_update = servermanager.now()
      local commands = servermanager.COMMANDS
      for _, command in ipairs(commands) do
         if commands.JobID==handle.pid then
            searchedCommand = command
            break
         end
      end
  end

  local cmdInfoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_command_id" then
      cmdInfoMap[k] = handle.id
    elseif k == "csbase_command_pid" then
      cmdInfoMap[k] = handle.pid
    elseif k == "csbase_command_ppid" then
      cmdInfoMap[k] = 0
    elseif k == "csbase_command_string" then
      cmdInfoMap[k] = handle.command
    elseif k == "csbase_command_exec_host" then
      cmdInfoMap[k] = searchedCommand.ExecHost or "unknown"
    elseif k == "csbase_command_state" then
      cmdInfoMap[k] = servermanager.getCommandStatus(handle, maxold)
    elseif k == "csbase_command_memory_ram_size_mb" then
      cmdInfoMap[k] = searchedCommand.RamSize or servermanager.ERROR_CODE
    elseif k == "csbase_command_memory_swap_size_mb" then
      cmdInfoMap[k] = searchedCommand.SwapSize or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_perc" then
      cmdInfoMap[k] = searchedCommand.CPUPerc or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_time_sec" then
      cmdInfoMap[k] = searchedCommand.CPUTime or servermanager.ERROR_CODE
    elseif k == "csbase_command_wall_time_sec" then
      cmdInfoMap[k] = searchedCommand.WallTime or servermanager.ERROR_CODE
    elseif k == "csbase_command_user_time_sec" then
      cmdInfoMap[k] = searchedCommand.UserTime or servermanager.ERROR_CODE
    elseif k == "csbase_command_system_time_sec" then
      cmdInfoMap[k] = searchedCommand.SystemTime or servermanager.ERROR_CODE
    end
  end
  return cmdInfoMap, nil
end

local getCommandStatus = function (handle, maxold)
   local status = "UNKNOWN"
   local err = ""
   if (not maxold) or ((servermanager.now() - handle.last_status_update) > maxold) then
      status, err = drmaa_job_ps(handle.pid)
      if not status then
         servermanager.writeError(string.format("Erro ao consultar status do job %s: %s", handle.pid, err))
         return NOT_RESPONDING
      end
      handle.status = status
      handle.last_status_update = servermanager.now()
   else
      status = handle.status
   end

   if status == "RUNNING" then
      return servermanager.RUNNING
   elseif status == "FAILED" or status == "DONE" then
      return servermanager.FINISHED
   elseif status == "UNKNOWN" then
      return servermanager.NOT_RESPONDING
   else
      return servermanager.WAITING
   end
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)
end -- function setDefaultKeys

--------------------------------------------------------------------------------
-- Retirna todas as chaves disponÃ­veis.
--------------------------------------------------------------------------------
getAllKeys = function()
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
end

--------------------------------------------------------------------------------
-- Retorna uma lista com os nÃ³s do servidor.
--
-- @return a lista de nÃ³s e ,em caso de erro, nil e a mensagem de erro.
--
-- nodes, err = getnodes(server)
--------------------------------------------------------------------------------
--
getNodes = function()
  print "Called getNodes()"
  nodesTable, err = get_all_machines()
  if not nodesTable then 
     return nil, err
  end

  nodeNames = {}

  for _, node in ipairs(nodesTable) do
	table.insert(nodeNames, node.Name)
	print("Found node", node.Name)
  end
  --print ("Memory", collectgarbage("count"))
  --collectgarbage("collect")
  --print "Collected."
  --print ("Memory", collectgarbage("count"))
  return nodeNames, nil
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

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

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

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

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

  ERROR_CODE = -1,

  -------------
  -- 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,

  -- FunÃ§Ãµes para clusters:
  getJobsInfo = getJobsInfo,
  getNodes = getNodes, -- OPCIONAL

  -- FunÃ§Ãµes de log de histÃ³rico:
  setHistoric = setHistoric, -- OPCIONAL
  getHistoric = getHistoric, -- OPCIONAL

  -- FunÃ§Ãµes de log 2:
  writeError = writeError,
  writeMsg = writeMsg,

  -- Default keys
  defaultKeys = {
      "csbase_command_id",
      "csbase_command_pid",
      "csbase_command_ppid",
      "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_cpu_time_sec",
      "csbase_command_wall_time_sec",
      "csbase_command_user_time_sec",
      "csbase_command_system_time_sec"
  },--]]

  -- CommandInfo
  getCommandStatus = getCommandStatus,

  -- Cache
  COMMANDS = {},
  NODES = {},

  -- FunÃ§Ãµes auxiliares
  updateAllCommands = updateAllCommands,
}
