func (s *Server) nodeFailed(me serf.MemberEvent, wan bool) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		s.logger.Printf("[INFO] consul: removing server %s", parts)
		s.remoteLock.Lock()
		existing := s.remoteConsuls[parts.Datacenter]
		n := len(existing)
		for i := 0; i < n; i++ {
			if existing[i].Name == parts.Name {
				existing[i], existing[n-1] = existing[n-1], nil
				existing = existing[:n-1]
				n--
				break
			}
		}
		if n == 0 {
			delete(s.remoteConsuls, parts.Datacenter)
		} else {
			s.remoteConsuls[parts.Datacenter] = existing
		}
		s.remoteLock.Unlock()
		if !wan {
			s.localLock.Lock()
			delete(s.localConsuls, parts.Addr.String())
			s.localLock.Unlock()
		}
	}
}
nodeFailed is used to handle fail events on both the serf clustes
package consul
import (
	"net"
	"strings"
	"github.com/socketplane/socketplane/Godeps/_workspace/src/github.com/hashicorp/serf/serf"
)
const (
	StatusReap = serf.MemberStatus(-1)
	userEventPrefix = "consul:event:"
)
func userEventName(name string) string {
	return userEventPrefix + name
}
func isUserEvent(name string) bool {
	return strings.HasPrefix(name, userEventPrefix)
}
func rawUserEventName(name string) string {
	return strings.TrimPrefix(name, userEventPrefix)
}
func (s *Server) lanEventHandler() {
	for {
		select {
		case e := <-s.eventChLAN:
			switch e.EventType() {
			case serf.EventMemberJoin:
				s.nodeJoin(e.(serf.MemberEvent), false)
				s.localMemberEvent(e.(serf.MemberEvent))
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				s.nodeFailed(e.(serf.MemberEvent), false)
				s.localMemberEvent(e.(serf.MemberEvent))
			case serf.EventMemberReap:
				s.localMemberEvent(e.(serf.MemberEvent))
			case serf.EventUser:
				s.localEvent(e.(serf.UserEvent))
			case serf.EventMemberUpdate:  
			case serf.EventQuery:  
			default:
				s.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-s.shutdownCh:
			return
		}
	}
}
func (s *Server) wanEventHandler() {
	for {
		select {
		case e := <-s.eventChWAN:
			switch e.EventType() {
			case serf.EventMemberJoin:
				s.nodeJoin(e.(serf.MemberEvent), true)
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				s.nodeFailed(e.(serf.MemberEvent), true)
			case serf.EventMemberUpdate:  
			case serf.EventMemberReap:  
			case serf.EventUser:
			case serf.EventQuery:  
			default:
				s.logger.Printf("[WARN] consul: unhandled WAN Serf Event: %#v", e)
			}
		case <-s.shutdownCh:
			return
		}
	}
}
func (s *Server) localMemberEvent(me serf.MemberEvent) {
	if !s.IsLeader() {
		return
	}
	isReap := me.EventType() == serf.EventMemberReap
	for _, m := range me.Members {
		if isReap {
			m.Status = StatusReap
		}
		select {
		case s.reconcileCh <- m:
		default:
		}
	}
}
func (s *Server) localEvent(event serf.UserEvent) {
	if !strings.HasPrefix(event.Name, "consul:") {
		return
	}
	switch name := event.Name; {
	case name == newLeaderEvent:
		s.logger.Printf("[INFO] consul: New leader elected: %s", event.Payload)
		if s.config.ServerUp != nil {
			s.config.ServerUp()
		}
	case isUserEvent(name):
		event.Name = rawUserEventName(name)
		s.logger.Printf("[DEBUG] consul: user event: %s", event.Name)
		if s.config.UserEventHandler != nil {
			s.config.UserEventHandler(event)
		}
	default:
		s.logger.Printf("[WARN] consul: Unhandled local event: %v", event)
	}
}
func (s *Server) nodeJoin(me serf.MemberEvent, wan bool) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			if wan {
				s.logger.Printf("[WARN] consul: non-server in WAN pool: %s", m.Name)
			}
			continue
		}
		s.logger.Printf("[INFO] consul: adding server %s", parts)
		found := false
		s.remoteLock.Lock()
		existing := s.remoteConsuls[parts.Datacenter]
		for idx, e := range existing {
			if e.Name == parts.Name {
				existing[idx] = parts
				found = true
				break
			}
		}
		if !found {
			s.remoteConsuls[parts.Datacenter] = append(existing, parts)
		}
		s.remoteLock.Unlock()
		if !wan && parts.Datacenter == s.config.Datacenter {
			s.localLock.Lock()
			s.localConsuls[parts.Addr.String()] = parts
			s.localLock.Unlock()
		}
		if s.config.BootstrapExpect != 0 {
			s.maybeBootstrap()
		}
	}
}
func (s *Server) maybeBootstrap() {
	index, err := s.raftStore.LastIndex()
	if err != nil {
		s.logger.Printf("[ERR] consul: failed to read last raft index: %v", err)
		return
	}
	if index != 0 {
		s.config.BootstrapExpect = 0
		return
	}
	members := s.serfLAN.Members()
	addrs := make([]net.Addr, 0)
	for _, member := range members {
		valid, p := isConsulServer(member)
		if !valid {
			continue
		}
		if p.Datacenter != s.config.Datacenter {
			s.logger.Printf("[ERR] consul: Member %v has a conflicting datacenter, ignoring", member)
			continue
		}
		if p.Expect != 0 && p.Expect != s.config.BootstrapExpect {
			s.logger.Printf("[ERR] consul: Member %v has a conflicting expect value. All nodes should expect the same number.", member)
			return
		}
		if p.Bootstrap {
			s.logger.Printf("[ERR] consul: Member %v has bootstrap mode. Expect disabled.", member)
			return
		}
		addrs = append(addrs, &net.TCPAddr{IP: member.Addr, Port: p.Port})
	}
	if len(addrs) < s.config.BootstrapExpect {
		return
	}
	s.logger.Printf("[INFO] consul: Attempting bootstrap with nodes: %v", addrs)
	if err := s.raft.SetPeers(addrs).Error(); err != nil {
		s.logger.Printf("[ERR] consul: failed to bootstrap peers: %v", err)
	}
	s.config.BootstrapExpect = 0
}