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