--Debug

TRASHDIR="PBStrash/"
PBSERRORFILE="PBSError.log"
PBSOUTPUTFILE="PBSOutput.log"

--------------------------------------------------------------------------------
-- 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 = SGAD_CONF.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
--           csbase_node_resources
--          @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 ("Obtendo configuracoes do nó "..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",
      "resources",
    }
  end -- if no keys

  local ninfo = servermanager._nodes[server]
  if not ninfo then
     print(string.format("Nó %s não encontrado.", server))
     return nil, string.format("Nó %s não encontrado.", server)
  end

  node = { name = server }
  ninfo = _updatenode(node, maxold)

  local configMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_num_processors" then
      configMap[k] = servermanager.getnumcpus(ninfo) or 0 --servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_info_mb" then
      configMap[k] = servermanager.getrammemory(ninfo) or 0 --servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_info_mb" then
      configMap[k] = servermanager.getswapmemory(ninfo) or 0 --servermanager.ERROR_CODE
    elseif k == "csbase_job_control_actions" then
      configMap[k] = { servermanager.HOLD, servermanager.RELEASE, servermanager.TERMINATE }
    elseif k == "resources" then
      print "Fetching node resources..."
      for k, v in pairs(SGAD_CONF) do
--	print(k,v)
end
	print (SGA_STATIC_CONF.resources)
      if type(SGA_STATIC_CONF.resources) == "table" then
        local nres, totalres = 1, #(SGA_STATIC_CONF.resources)
        while nres <= totalres do
          print("", "resource", SGA_STATIC_CONF.resources[nres])
          configMap[k.."."..nres] = SGA_STATIC_CONF.resources[nres]
          nres = nres + 1
        end
      end
    end 
  end
  for k, v in pairs(configMap) do
      print("configMap - key: ", k, "value: ", v)
  end
  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("Obtendo informações do nó "..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

  local ninfo = servermanager._nodes[server]
  if not ninfo then
     print(string.format("Nó %s não encontrado.", server))
     return nil, string.format("Nó %s não encontrado.", server)
  end

  node = { name = server }
  ninfo = _updatenode(node, maxold)

  local infoMap = {}
  for _, k in ipairs(keys) do
    if k == "csbase_load_perc" then
      infoMap[k] = servermanager.getcpuload(ninfo) or 0 --servermanager.ERROR_CODE
    elseif k == "csbase_memory_ram_free" then
      infoMap[k] = servermanager.getfreerammemory(ninfo) or 0 --servermanager.ERROR_CODE
    elseif k == "csbase_memory_swap_free" then
      infoMap[k] = servermanager.getfreeswapmemory(ninfo) or 0 --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

--------------------------------------------------------------------------------
--
-- 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";
print("exec")
  for k, v in pairs(extraParams) do
	print("extraParam", k, v)
  end

  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.."/"..PBSERRORFILE
  local tmpfile = os.tmpname()

  local cmd = command

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

  if ce_start_p then
    local exec_string = string.format("%s > %s ", cmd, tmpfile)
    servermanager.writeMsg( "Tentando execuÃ§Ã£o:\n\t"..exec_string.."\n" )
    os.execute(exec_string)
    errorlog = nil
  else

    local strextraParams = ""
    if SGAD_CONF and SGAD_CONF.queue_name then

      strextraParams = strextraParams .. " -q " .. SGAD_CONF.queue_name
    end
    local runningJobServer = os.getenv("PBS_O_HOST")
    if runningJobServer then 
        if strextraParams=="" then
              strextraParams = strextraParams .. " -q "
        end
        strextraParams = strextraParams .. "@" .. runningJobServer ..":15001 "
    else
        print ">>>>>> SEM HOST"
    end
  --
  --outfile = outfile or "/dev/null"
  --errorfile = errorfile or "/dev/null"
  --
  --O scp dÃ¡ um erro!
  --TambÃ©m nÃ£o serve usar o tmpfile em /tmp/xxx
  --Unable to copy file 17.tissot.OU to tissot:/dev/null
  --
  --VAMOS TER QUE LIMPAR DEPOIS!!! XXX
  --
    
--[[
    if host then
      strextraParams = strextraParams .. " -l host=" .. host
    end
]]
    local _outfile = output_dir.."/"..PBSOUTPUTFILE
    local _errorfile = ce_error_p or errorlog
    local exec_string = string.format( [[qsub -N %s -V -o "%s" -e "%s" %s > %s]],
      id, _outfile, _errorfile, strextraParams, tmpfile)
    local workingDir = "./"
    if SGAD_CONF then
       workingDir = SGAD_CONF.path or "./"
    end
    local cmd_string = string.format( [[ksh -c 'cd "%s" && %s']], workingDir, cmd)
    servermanager.writeCmd( "Tentando execuÃ§Ã£o:\n\t"..exec_string.."\n" ..
                            "\t\t"..cmd_string.."\n" )
    local fd = io.popen(exec_string, "w")
    fd:write(cmd_string)
    fd:close()
  end  

  local fd = io.open(tmpfile, "r")
  if not fd then
     local strerror = "Erro ao abrir arquivo temporÃ¡rio:\n"..
         "\t["..tostring(tmpfile).."]\n"..
         "\tpara aquisiÃ§Ã£o de dados de execuÃ§Ã£o\n" 
     servermanager.writeError( strerror)
     return nil, strerror
  end

  local str = fd:read("*a")
  str = string.gsub( str, "%c", "" )
  fd:close()

  os.remove(tmpfile)
  servermanager._updatecommands()

  local handle ={
     cmdid = id,
     command = command,
     execHost = nil,
     errorfile = errorlog,
     last_update = 0,
--   pid = string.gsub( str, "(%S+)%.%S+","%1"),
--   server = string.gsub( str, "%S+%.(%S+)","%1"),
     pid = string.gsub( str, "([^%.]+)%..*","%1"),
     server = string.gsub( str, "[^%.]+%.(.*)","%1"),
     start_time = servermanager.now()
  }
  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 > %s", pid, server, fname)
      servermanager.writeMsg("Cancelando comando ["..command.."]")
      res = os.execute(command)
    elseif action==servermanager.HOLD then
      command = string.format("qhold %s.%s", cmdh.pid, handle.server)
      servermanager.writeMsg("Colocando hold no comando ["..command.."]")
      res = os.execute(command)
    elseif action==servermanager.RELEASE then
      command = string.format("qrls %s.%s", cmdh.pid, handle.server)
      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
  
  local cmds = servermanager._cmds[server] and 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

  for k, v in pairs(cmdInfoMap) do
	print("cmdInfoMap", k, v)
  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
    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, maxold) or ""--servermanager.ERROR_CODE
    elseif k == "csbase_command_state" then
      cmdInfoMap[k] = servermanager.getcommandstatus(handle, maxold) or servermanager.ERROR_CODE
    elseif k == "csbase_command_memory_ram_size_mb" then
      local memory = servermanager.getcommandmemoryload(handle, maxold)
      cmdInfoMap[k] = (memory and memory.ram) --or servermanager.ERROR_CODE
    elseif k == "csbase_command_memory_swap_size_mb" then
      local memory = servermanager.getcommandmemoryload(handle, maxold)
      cmdInfoMap[k] = (memory and memory.swap) --or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_perc" then
      cmdInfoMap[k] = servermanager.getcommandcpuload(handle, maxold) or servermanager.ERROR_CODE
    elseif k == "csbase_command_cpu_time_sec" then
      cmdInfoMap[k] = servermanager.getcommandcputimesec(handle, maxold) or servermanager.ERROR_CODE
    elseif k == "csbase_command_wall_time_sec" then
      cmdInfoMap[k] = servermanager.getcommandwalltimesec(handle, maxold) or servermanager.ERROR_CODE
    elseif k == "csbase_command_user_time_sec" then
      local timeusage = servermanager.getcommandtimeusage(handle, maxold)
      cmdInfoMap[k] = (timeusage and timeusage.user) or servermanager.ERROR_CODE
    elseif k == "csbase_command_system_time_sec" then
      local timeusage = servermanager.getcommandtimeusage(handle, maxold)
      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()
  local tmpname = os.tmpname()
  local command = string.format('qstat -x > %s', tmpname)

  servermanager.writeMsg("Coletando info sobre jobs com ["..command.."]")
  local result = os.execute(command)
  if result ~= 0 and result~=true then
     local err = "Falha na coleta de dados de jobs (qstat)."
     servermanager.writeError(err.." Cod.:"..tostring(result))
     return
  end

  local file = io.open(tmpname, "r")
  if not file then
     local err = "Falha na leitura do arquivo temporÃ¡rio que guarda info sobre jobs."
     servermanager.writeError(err.." Arq.:["..tmpname.."]")
     return
  end

  local strXML = file:read("*a")
  file:close()
  os.remove(tmpname)

  return strXML
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()
   print "GETNODES"
--
-- PROBLEMA: OVO/GALINHA! :-)
--
-- ninfo = servermanager._getnode(server)
-- if not ninfo then
--    return nil, servermanager.NO_SERVER
-- end
   local ids = {}
   local fname = os.tmpname()
   local command = string.format('pbsnodes -a > %s', fname)
   os.execute( command )
   local fd = io.open(fname, "r")
   if not fd then
     return nil, "Falha na abertura do arquivo temporÃ¡rio.".." ["..fname.."]".." de saÃ­da do comando: '"..command.."'."
   end
   local str = fd:read("*a")
   if not str then
     return nil, "Falha na leitura do arquivo temporÃ¡rio.".." ["..fname.."]".." de saÃ­da do comando: '"..command.."'."
   end	   
   fd:close()
   os.remove(fname)

   --print(str)

   for host in string.gmatch("\n"..str, "\n(%S+)\n") do
     local hostname = string.gsub(host,"%..*","")
     print("found node", hostname)
     table.insert(ids, hostname)
   end

  if #ids > 0 then
     for _, id in ipairs(ids) do
        servermanager._nodes[id] = { name = id }
     end
     return ids, nil
  else
     return nil, str
  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 -f > %s', fname)

  servermanager.writeMsg("Coletando info com ["..command.."]")
  local result = os.execute(command)
  if result ~= 0 and result~=true then
     print("resultado da coleta", result)
     local err = "Falha na coleta de dados de comandos (qstat)."
     servermanager.writeError(err.." Cod.:"..tostring(result))
     return
  end

  local fd = io.open(fname, "r")
  if not fd then
     local err = "Falha na leitura do arquivo temporÃ¡rio do qstat."
     servermanager.writeError(err.." Arq.:["..fname.."]")
     return
  end

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

  string.gsub(str, "Job Id: (%d+)%.(%S+)(.-)\n\n",
    function(pid, server, attrs)
 --   attrs = string.gsub(attrs, "%c", "")
      attrs = string.gsub(attrs, "  ", " ")
      local tb = {pid = pid}
      string.gsub(attrs, "%s*(%S+) = ([%S ]+)",
                  function (attr, val) tb[attr] = val end)
      local cmdid = tb.Job_Name

      -- Transforma o tempo em segundos
      if tb["resources_used.cput"] then
        tb["resources_used.cput"] =
          servermanager._str2sec(tb["resources_used.cput"])
      end
 
      if tb["resources_used.walltime"] then
        tb["resources_used.walltime"] =
          servermanager._str2sec(tb["resources_used.walltime"])
      end
 
      -- Transforma para bytes
      if tb["resources_used.mem"] then
        tb["resources_used.mem"] =
          servermanager._getvalunit(tb["resources_used.mem"])
      end
 
      if tb["resources_used.vmem"] then
        tb["resources_used.vmem"] =
          servermanager._getvalunit(tb["resources_used.vmem"])
      end
 
      if tb.exec_host then
        tb.exec_host = string.gsub(tb.exec_host, "^(%w+).+$", "%1")
      end

      if not cmds[server] then
         cmds[server] = {}
      end
 
      local cmdh = cmds[server]
      if not cmdh[cmdid] then
        cmdh[cmdid] = {}
      end
      table.insert(cmdh[cmdid], tb)
    end)

  servermanager._cmds = cmds
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, maxold)

  if maxold and type(maxold)==number and servermanager.now() - servermanager._nodes[node.name]._lastUpdate < maxold then
    return servermanager._nodes[node.name]
  end

  local attr_table = {}

  local fname = os.tmpname()
  local domain = node.domain or SGAD_CONF.domain or ""
  local command = string.format('pbsnodes -a %s%s > %s', node.name, domain, 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

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

  for attr, val in string.gmatch(str, "(%w+) = ([^\n]+)") do
    attr_table[attr] = val
  end
  if attr_table.status then
    for attr, val in string.gmatch(attr_table.status, "(%w+)=([^,]+)") do
      -- NÃ£o sobreescreve um valor jÃ¡ definido
      if attr=="totmem" or attr=="physmem" then
      end
      attr_table[attr] = attr_table[attr] or val
    end
  end
  if attr_table.jobs then
    local jobs = attr_table.jobs
    attr_table.jobs = {}
    for job in string.gmatch(jobs, "([^,| ]+)") do
      if job == "?" then
         break
      end
      print ("job", job)
      table.insert(attr_table.jobs, job)
    end
  end
  attr_table._lastUpdate = servermanager.now()
  servermanager._nodes[node.name] = attr_table
  return attr_table
end

_getvalunit = function (str)
  local val = string.gsub(str, "(%d+)[KkMmGgTt]?b", "%1")
  val = tonumber(val)
  local unit = string.gsub(str, "%d+([KkMmGgTt]?)b", "%1")
  val = servermanager._memvaluetomegabytes(val, unit)

  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)

   if (not maxold) or ((servermanager.now() - handle.last_update)
      > maxold) then
      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 = {}
   for i, cmdh in pairs(cmds) do
     local ehost = cmdh["exec_host"] or "unknown"
     local ram = cmdh["resources_used.mem"] or servermanager.ERROR_CODE
     local swap = cmdh["resources_used.vmem"] or servermanager.ERROR_CODE
     local cput = cmdh["resources_used.cput"] or servermanager.ERROR_CODE
     local walltime = cmdh["resources_used.walltime"] or servermanager.ERROR_CODE
     local cpuperc = 0
     local server = cmdh["exec_host"] or handle.server
     local state
     local job_state = cmdh.job_state
     if job_state then
       if job_state == 'E' or
          job_state == 'C' or
          job_state == 'D' or
          job_state == 'A' then
          state = servermanager.FINISHED
          servermanager._checkforerror(handle)
       elseif job_state == 'Q' then
          state = servermanager.WAITING
       else
          state = servermanager.RUNNING
       end
     else
        state = servermanager.NOT_RESPONDING
     end

     if walltime ~= 0 and walltime~=servermanager.ERROR_CODE and state==servermanager.RUNNING then
       print("walltime", walltime)
       local node = {}
       node.name = handle.host
       local ninfo = servermanager._updatenode(node)
       local ncpus, err = servermanager.getnumcpus(ninfo)
       ncpus = ncpus or 1

       cpuperc = (cput / ncpus) /walltime
     end
  
     local procinfo = {
        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 = 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
   return procs
end

getnumcpus = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   return tonumber(ninfo.ncpus), nil
end

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

gettotalmemory = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   if ninfo.totmem then
      return servermanager._getvalunit(ninfo.totmem), 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
   return nil, servermanager.NO_SERVER
end

getnumjobs = function (ninfo)
   if not ninfo or not ninfo.jobs or type(ninfo.jobs)~="table" then
      return nil, servermanager.NO_SERVER
   end
   return #(ninfo.jobs), nil
end

getfreerammemory = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   local availmem = false
   if ninfo.availmem then
      availmem = servermanager._getvalunit(ninfo.availmem) 
   end
   local rammem = servermanager.getrammemory(ninfo)
   local totalmem = servermanager.gettotalmemory(ninfo)
   print("availmem", availmem)
   
   if availmem then
      local mload = totalmem - availmem
      if mload <= rammem then 
         return rammem - mload
      else
         return 0
      end
   end
   return nil, servermanager.NO_SERVER
end

getfreeswapmemory = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   if ninfo.availmem then
      local availmem = servermanager._getvalunit(ninfo.availmem) 
   end
   local rammem = servermanager.getrammemory(ninfo)
   local totalmem = servermanager.gettotalmemory(ninfo)
   local swapmem = servermanager.getswapmemory(ninfo)
   if availmem then
      local mload = totalmem - availmem
      if mload <= rammem then 
         return swapmem
      else
         return swapmem-(mload-rammem)
      end
   end
   return nil, servermanager.NO_SERVER
end

getcpuload = function (ninfo)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   return tonumber(ninfo.loadave), nil
end

------------------------------------------------------------------------
-- Retorna a taxa mÃ©dia de ocupaÃ§Ã£o de CPU do Ãºltimo minuto no servidor
-- especificado. 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. 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 em questÃ£o. Em caso de erro, retorna nil e a mensagem de
-- erro.
--
-- load, err = getcpuload(server, maxold)
------------------------------------------------------------------------
--[[
getcpuload = function (server, maxold)
   ninfo = servermanager._getnode(server)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   servermanager._update(server, maxold)
   if not ninfo.isonline then
     return nil, "NÃ³ nÃ£o estÃ¡ online (aquisiÃ§Ã£o de CPU)!"
   end
   if not ninfo.cpuload then
     return nil, "Falha de aquisiÃ§Ã£o de carga de CPU!"
   end
   return ninfo.cpuload, nil
end
]]
------------------------------------------------------------------------
-- Retorna a taxa mÃ©dia de ocupaÃ§Ã£o de memÃ³ria do Ãºltimo minuto no
-- servidor especificado. O retorno Ã© uma tabela contendo o total de
-- memÃ³ria RAM e de Swap, no formato:
--   { ram = xxx, swap = yyy }
--
-- 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 em questÃ£o. Em caso de erro, retorna nil e a mensagem de
-- erro.
--
-- load, err = getmemoryload(server, maxold)
------------------------------------------------------------------------
getmemoryload = function (server, maxold)
   ninfo = servermanager._getnode(server)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   servermanager._update(server, maxold)
   if not ninfo.isonline then
     return nil, "NÃ³ nÃ£o estÃ¡ online (aquisiÃ§Ã£o de memÃ³ria)!"
   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 nÃºmero de jobs que estÃ£o sendo executados no servidor
-- especificado.
----------------------------------------------------------------------
getnumberofjobs = function(server, maxold)
   ninfo = servermanager._getnode(server)
   if not ninfo then
      return nil, servermanager.NO_SERVER
   end
   servermanager._update(server, maxold)
   if not ninfo.isonline then
     return nil, "NÃ³ nÃ£o estÃ¡ online (aquisiÃ§Ã£o de nÃºmero de jobs)!"
   end
   if not ninfo.njobs then
     return nil, "Falha de aquisiÃ§Ã£o do nÃºmero de jobs!"
   end

   return ninfo.njobs, 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)
   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, maxold)

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

   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 == 'E' or
        state == 'C' or
        state == 'D' or
        state == 'A' then
        status = servermanager.FINISHED
        servermanager._checkforerror(handle)
     elseif state == 'Q' or
	    state == 'H' then
        status = servermanager.WAITING
     else
        status = servermanager.RUNNING
        break
     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, maxold)
   if handle.host then
     return handle.host
   end
   if (not maxold) or ((servermanager.now() - handle.last_update)
      > maxold) then
      servermanager._updatecommands()
      handle.last_update = servermanager.now()
   end
-- -- EM ABERTO: Pega o host do primeiro job
-- local cmdh, err = servermanager._getcommand(handle.cmdid,handle.server)
-- if err then
--    return nil, err
-- end
   -- Fazemos a concatenaÃ§Ã£o de todos os hosts que compÃµem o job.
   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, maxold)
   if (not maxold) or ((servermanager.now() - handle.last_update)
     > maxold) then
      servermanager._updatecommands()
      handle.last_update = servermanager.now()
   end
-- -- 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 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
   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, maxold)
   if (not maxold) or ((servermanager.now() - handle.last_update)
      > maxold) then
      servermanager._updatecommands()
      handle.last_update = servermanager.now()
   end

 ---- 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
   for i, cmdh in pairs(cmds) do
     if cmdh["resources_used.cput"] then
       cput = (cput or 0) + cmdh["resources_used.cput"]
     end
   end
   local walltime = servermanager.now() - handle.start_time

   local system = cput 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, maxold)
   -- NÃ£o tenho a ocupaÃ§Ã£o do Ãºltimo minuto no PBS.
   -- Vou usar cput/walltime
   if (not maxold) or ((servermanager.now() - handle.last_update)
      > maxold) then
      servermanager._updatecommands()
      handle.last_update = servermanager.now()
   end

   local procs, err = servermanager.getcommandallinfo(handle, maxold)
   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, maxold)
   local procs, err = servermanager.getcommandallinfo(handle, maxold)
   if not procs then
      return 0
   end
   return tonumber(procs[1].wallTimeSec)
end

getcommandcputimesec = function (handle, maxold)
   local procs, err = servermanager.getcommandallinfo(handle, maxold)
   if not procs then
      return 0
   end
   return tonumber(procs[1].CPUTimeSec)
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,

  -- 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    = {},
  _commandsdump = _commandsdump,
  _getcommand = _getcommand,
  _getcommandall = _getcommandall,
  _getvalunit = _getvalunit,
  _memvaluetobytes = _memvaluetobytes,
  _memvaluetomegabytes = _memvaluetomegabytes,
  _str2sec = _str2sec,
  _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,
  getnumberofjobs = getnumberofjobs,

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