#!/usr/bin/env python

# This script collides the output of isi_jcommit_d (in var/log/isi_jcommit_d.log) and
# tries to identify transactions that have conflicting state, participant lists, ...
#
# Used as:
#  isi_for_array 'cp /var/log/isi_jcommit_d.log /ifs/data/jcl.`sysctl -n efs.rbm.array_id`'
#  isi_jcommit_cmp /ifs/data/jcl.*
#
# It is also possible to read gzipped log files directly (after log rotation).

import commands
import os
import re
import stat
import string
import sys
import syslog
import zlib
import gzip


def main():
  files = {}

  for f in sys.argv[1:]:
    if f[len(f) - 3:] == ".gz":
      file = gzip.open(f,"r")
    else:
      file = open(f,"r")
    files[file] = f

  rp = re.compile("\((\d+:\d+)\): (\w+) \(participants \((.*)\)(|\[(.*)\])\)")
  seqs = { }
  sc = { }
  bad = { }
  done = { }
  lc = { }
  all = { }
  counter = 1
  first = 0
  last = ''

  while len(files):
    for file in files.keys():

      line = file.readline()
      if line == '':
        del files[file]
        continue

      if not files[file] in lc:
        lc[files[file]] = 1
      else:
        lc[files[file]] += 1

      line = line[0:len(line)-1]
      el = rp.match(line)
      if not el:
        continue

      init = el.group(1).split(":")[0]
      pl = el.group(3).split(", ")

      if not el.group(1) in seqs and len(pl) > 1:
        seqs[el.group(1)] = el.group(2) + " " + el.group(3)
        sc[el.group(1)] = [ len(pl), 1 ]
        done[el.group(1)] = { files[file] : line }
        counter += 1
        all[el.group(1)] = counter;
      elif el.group(1) in seqs:
        if el.group(1) in bad or seqs[el.group(1)] != el.group(2) + " " + el.group(3):
          # print "MISMATCH: " + el.group(1)
          if el.group(1) in bad:
            bad[el.group(1)][file] = line
          else:
            bad[el.group(1)] = { files[file] : line }
            for df in done[el.group(1)]:
              bad[el.group(1)][df] = done[el.group(1)][df]
        else:
          sc[el.group(1)][1] += 1
          done[el.group(1)][files[file]] = line
          if el.group(1) in bad:
            bad[el.group(1)][files[file]] = line
          if sc[el.group(1)][0] == sc[el.group(1)][1]:
            if first == 0:
              first = all[el.group(1)]
            else:
              if last != '':
                del all[last]
              last = el.group(1)
            del sc[el.group(1)]
            del seqs[el.group(1)]
            del done[el.group(1)]

  for f in lc:
    print f + " " + str(lc[f])

  if len(seqs):
    print "TXNs missing one or more participant:"
    print "====================================="
    for s in sorted(all, key=all.__getitem__):
      if (all[s] == first):
        print "================== FIRST COMPLETE TXN =================="
        continue
      if (s == last):
        print "================== LAST COMPLETE TXN =================="
        continue

      line = done[s].values()[0]
      el = rp.match(line)
      if not el:
        continue

      # init = el.group(1).split(":")[0]
      pl = el.group(3).split(", ")

      if el.group(5) in pl and sc[el.group(1)][1] == sc[el.group(1)][0] - 1:
        continue

      print s + ": " + seqs[s] + " " + str(sc[s][1]) + "/" + str(sc[s][0])
      for f in sorted(done[s]):
        print f + done[s][f]

  if len(bad):
    print "TXNs with mismatching resolution or participant list:"
    print "====================================================="
    for s in sorted(bad.keys()):
      print s + ": "
      for l in sorted(bad[s].keys()):
        print l + bad[s][l]

if __name__ == '__main__':
  try:
    main()
  except OSError, (errno, strerror):
    error(strerror)
