#!/usr/bin/python

# CLUSTER from two nodes
# both starts in runlevel 4 with all services stopped and standby IP active
# then one is choosed and activated to serve
# if it is down, left activates

import time
import socket
import whrandom
import struct
import thread

NAME="gw.prodo.ru"
MCAST="239.192.0.10"
PORT=1999
UNIQ="%s %f"%(NAME,whrandom.random())
# UDP 1999 broadcast with name of server

def log(a):
  print a

suspending=None
def suspend(x):
  global suspending
  suspending=True
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
  s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
  while suspending:
    s.sendto(UNIQ, (MCAST,1999))
    time.sleep(3)
    
    
others=False

listening=None
def listen(x):
  global listening,others
  listening=True
  others=False
  m=socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
  m.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  if hasattr(socket, "SO_REUSEPORT"):
    m.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  m.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
  m.bind(('', PORT))
  m.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, struct.pack("4sl", socket.inet_aton(MCAST), socket.INADDR_ANY))
  m.settimeout(1)
  while listening:
    try:
      s=m.recv(1000)
      if s!=UNIQ:
        others=True
    except:
      pass


while 1: # loop suspended - activatig - active - suspending -

# initial listening
 suspended=True
 while suspended:
  log("listening")
  timeout=7+10*whrandom.random()
  thread.start_new(listen,(None,))
  time.sleep(timeout)
  listening=False
  time.sleep(2) # be sure that thread will stop: there is 1s timeout in recv
  if others:
    log("active gateway present")
    continue
  log("trying to activate")
  thread.start_new(listen,(None,))
  thread.start_new(suspend,(None,))
  time.sleep(10) # 3 suspend packet - enough to find concurrent node
  if others:
    log("collision")
    suspending=False
    listening=False
    time.sleep(2)
    continue
  # now we are the only active node
  log("activated")
  suspended=False

# bringing up server
 os.system("init 2")
 while not others: # pings from someone when gate is running
 # in case of when server was just disconnected one from other so bringed
 # up both then connectivity was restored
  time.sleep(1)

 suspending=False
 log("Suspend packet received, going to suspend")
 os.system("init 4")

# end

