module MongoStatModule

  class MongoStat
    require_relative 'oms_common'

    # sample mongostat record
    # $ mongostat --all
    # connected to: 127.0.0.1
    # insert  query update delete getmore command flushes mapped  vsize    res non-mapped faults  locked db idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:34
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:35
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:36
    #     *0     *0     *0     *0       0     2|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0   124b     5k     2   18:19:37
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:38
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:39
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:40
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:41
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:42
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:43
    # insert  query update delete getmore command flushes mapped  vsize    res non-mapped faults  locked db idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:44
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:45
    #     *0     *0     *0     *0       0     1|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0    62b     2k     2   18:19:46
    #     *0     *0     *0     *0       0     2|0       0   160m   605m    33m       445m      0 local:0.0%          0       0|0     0|0   124b     5k     2   18:19:47

    def transform_data(record)
      if record.start_with?('insert')
        record.sub!("locked db", "locked-db") if record.include? "locked db"
        record.sub!("idx miss %", "idx-miss-%") if record.include? "idx miss %"
        record.sub!("% dirty", "dirty") if record.include? "% dirty"
        record.sub!("% used", "used") if record.include? "% used"

        # @counters saves the state of the counters as the counternames get printed at fixed intervals
        # reset values to [] when counter names are reset
        @counters = record.split
        @values = []
      else
        @values = record.split
      end

      if @counters && @values && @values.length > 0
        rec = Hash[@counters.zip @values]
        transformed_rec = {}
        begin
          transformed_rec["Insert Operations/sec"] = rec["insert"].delete("*")
          transformed_rec["Query Operations/sec"] = rec["query"].delete("*")
          transformed_rec["Update Operations/sec"] = rec["update"].delete("*")
          transformed_rec["Delete Operations/sec"] = rec["delete"].delete("*")
          transformed_rec["Total Data Mapped (MB)"] = rec["mapped"].delete("M") if rec.has_key?("mapped")
          transformed_rec["Virtual Memory Process Usage (MB)"] = rec["vsize"].delete("M")
          transformed_rec["Resident Memory Process Usage (MB)"] = rec["res"].delete("M")
          transformed_rec["Get More Operations/sec"] = rec["getmore"]
          transformed_rec["Page Faults/sec"] = rec["faults"] if rec.has_key?("faults")
          transformed_rec["Global Write Lock %"] = rec["locked-db"] if rec.has_key?("locked-db")
          transformed_rec["% Index Access Miss"] = rec["idx-miss-%"] if rec.has_key?("idx-miss-%")
          transformed_rec["Total Open Connections"] = rec["conn"]
          transformed_rec["Replication Status"] = rec["repl"] if rec.has_key?("repl")
          transformed_rec["Network In (Bytes)"] = to_bytes(rec["netIn"])
          transformed_rec["Network Out (Bytes)"] = to_bytes(rec["netOut"])

          ar, aw = rec["ar|aw"].split("|")
          transformed_rec["Active Clients (Read)"] = ar
          transformed_rec["Active Clients (Write)"] = aw

          qr, qw = rec["qr|qw"].split("|")
          transformed_rec["Queue Length (Read)"] = qr
          transformed_rec["Queue Length (Write)"] = qw

          if rec.has_key?("command")
            command = rec["command"]
            if command.include?("|")
              local, replicated = command.split("|")
              transformed_rec["Local Commands/sec"] = local
              transformed_rec["Replicated Commands/sec"] = replicated
            else
              transformed_rec["Commands/sec"] = command
            end
          end

          #version >= 3 rec has the counternames locked, dirty, used, non-mapped, flushes
          transformed_rec["% Time Global Write Lock"] = rec["locked"] if rec.has_key?("locked")
          transformed_rec["% WiredTiger Dirty Byte Cache"] = rec["dirty"]  if rec.has_key?("dirty")
          transformed_rec["% WiredTiger Cache in Use"] = rec["used"] if rec.has_key?("used")
          if rec.has_key?("non-mapped")
            transformed_rec["Total Virtual Memory (MB)"] = rec["non-mapped"].delete("M")
          end
          if rec.has_key?("flushes")
            version = get_mongostat_version
            transformed_rec["WiredTiger Checkpoints Triggered"] = rec["flushes"] if version >= 3.2
            transformed_rec["Fsync Operations/sec"] = rec["flushes"] if version < 3.2
          end

          #version >=3.2 has the counternames lr|lw, lrt|lwt
          if rec.has_key?("lr|lw")
            lr, lw = rec["lr|lw"].split("|")
            transformed_rec["% Read Lock Acquisition Time"] = lr
            transformed_rec["% Write Lock Acquisition Time"] = lw
          end
          if rec.has_key?("lrt|lwt")
            lrt, lwt = rec["lrt|let"].split("|")
            transformed_rec["Avg. Read Lock Acquisition Time (ms)"] = lrt
            transformed_rec["Avg. Write Lock Acquisition Time (ms)"] = lwt
          end
        rescue => e
          $log.warn e.to_s
        end
 
        dataitems = {}       
        dataitems["Timestamp"] = OMS::Common.format_time(Time.now.to_f)
        dataitems["Host"] = OMS::Common.get_hostname
        dataitems["ObjectName"] = "MongoDB"
        dataitems["InstanceName"] = OMS::Common.get_hostname
        collections = []

        transformed_rec.each { |k,v|
          if v.nil? or v == "nil"
            OMS::Log.warn_once("Dropping null value for counter #{k}")
          else
            counter_pair = {"CounterName" => k, "Value" => v} 
            collections.push(counter_pair) 
          end
        }
        dataitems["Collections"] = collections       
                  
        return dataitems
      end
    end

    def get_mongostat_version
      begin
        version = (%x(mongostat --version)).match(/(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/)
        return (version["major"] + '.' + version["minor"]).to_f
      rescue => e
        $log.error e.to_s
      end
    end

    def to_bytes(val)
      num = val.match(/\d+\.?\d*/)[0].to_f
      case val[-1]
      when "k"
        num*1024
      when "M"
        num*(1024 ** 2)
      when "G"
        num*(1024 ** 3)
      when "T"
        num*(1024 ** 4)
      else
        num
      end
    end

   def transform_and_wrap(record)
     return nil if record.to_s.empty?

     data_items = transform_data(record)
     if (!data_items.nil? and data_items.size>0)
       wrapper = {
          "DataType"=>"LINUX_PERF_BLOB",
          "IPName"=>"LogManagement",
          "DataItems"=>[data_items]
        }
       return wrapper
     else
       return nil 
     end
   end

  end
end

