ctdb-tests: Remove the python LCP2 simulation
authorMartin Schwenke <martin@meltin.net>
Sat, 10 Dec 2016 22:09:44 +0000 (09:09 +1100)
committerAmitay Isaacs <amitay@samba.org>
Mon, 19 Dec 2016 06:58:45 +0000 (07:58 +0100)
It isn't used anywhere and doesn't contain some of the optimisations
that have since gone into the C code.

Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
Autobuild-User(master): Amitay Isaacs <amitay@samba.org>
Autobuild-Date(master): Mon Dec 19 07:58:45 CET 2016 on sn-devel-144

13 files changed:
ctdb/tests/takeover/simulation/README [deleted file]
ctdb/tests/takeover/simulation/ctdb_takeover.py [deleted file]
ctdb/tests/takeover/simulation/hey_jude.py [deleted file]
ctdb/tests/takeover/simulation/ip_groups1.py [deleted file]
ctdb/tests/takeover/simulation/ip_groups2.py [deleted file]
ctdb/tests/takeover/simulation/ip_groups3.py [deleted file]
ctdb/tests/takeover/simulation/ip_groups4.py [deleted file]
ctdb/tests/takeover/simulation/ip_groups5.py [deleted file]
ctdb/tests/takeover/simulation/mgmt_simple.py [deleted file]
ctdb/tests/takeover/simulation/node_group.py [deleted file]
ctdb/tests/takeover/simulation/node_group_extra.py [deleted file]
ctdb/tests/takeover/simulation/node_group_simple.py [deleted file]
ctdb/tests/takeover/simulation/nondet_path_01.py [deleted file]

diff --git a/ctdb/tests/takeover/simulation/README b/ctdb/tests/takeover/simulation/README
deleted file mode 100644 (file)
index 4a8267b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-This contains a Python simulation of CTDB's IP reallocation algorithm.
-
-It is useful for experimenting with improvements.
-
-To use this on RHEL5 you'll need python2.6 from EPEL
-<http://fedoraproject.org/wiki/EPEL>.
diff --git a/ctdb/tests/takeover/simulation/ctdb_takeover.py b/ctdb/tests/takeover/simulation/ctdb_takeover.py
deleted file mode 100755 (executable)
index 1aeeaca..0000000
+++ /dev/null
@@ -1,888 +0,0 @@
-#!/usr/bin/env python
-
-# ctdb ip takeover code
-
-# Copyright (C) Martin Schwenke, Ronnie Sahlberg 2010, 2011
-
-# Based on original CTDB C code:
-#
-# Copyright (C) Ronnie Sahlberg  2007
-# Copyright (C) Andrew Tridgell  2007
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, see <http://www.gnu.org/licenses/>.
-
-
-import os
-import sys
-# Use optparse since newer argparse not available in RHEL5/EPEL.
-from optparse import OptionParser
-import copy
-import random
-import itertools
-
-# For parsing IP addresses
-import socket
-import struct
-
-# For external algorithm
-import subprocess
-import re
-
-options = None
-
-def process_args(extra_options=[]):
-    global options
-
-    parser = OptionParser(option_list=extra_options)
-
-    parser.add_option("--nd",
-                      action="store_false", dest="deterministic_public_ips",
-                      default=True,
-                      help="turn off deterministic_public_ips")
-    parser.add_option("--ni",
-                      action="store_true", dest="no_ip_failback", default=False,
-                      help="turn on no_ip_failback")
-    parser.add_option("-L", "--lcp2",
-                      action="store_true", dest="lcp2", default=False,
-                      help="use LCP2 IP rebalancing algorithm [default: %default]")
-    parser.add_option("-e", "--external",
-                      action="store_true", dest="external", default=False,
-                      help="use external test program to implement IP allocation algorithm [default: %default]")
-    parser.add_option("-b", "--balance",
-                      action="store_true", dest="balance", default=False,
-                      help="show (im)balance information after each event")
-    parser.add_option("-d", "--diff",
-                      action="store_true", dest="diff", default=False,
-                      help="show IP address movements for each event")
-    parser.add_option("-n", "--no-print",
-                      action="store_false", dest="show", default=True,
-                      help="don't show IP address layout after each event")
-    parser.add_option("-v", "--verbose",
-                      action="count", dest="verbose", default=0,
-                      help="print information and actions taken to stdout")
-    parser.add_option("-r", "--retries",
-                      action="store", type="int", dest="retries", default=5,
-                      help="number of retry loops for rebalancing non-deterministic failback [default: %default]")
-    parser.add_option("-i", "--iterations",
-                      action="store", type="int", dest="iterations",
-                      default=1000,
-                      help="number of iterations to run in test [default: %default]")
-    parser.add_option("-o", "--odds",
-                      action="store", type="int", dest="odds", default=4,
-                      help="make the chances of a failover 1 in ODDS [default: %default]")
-    parser.add_option("-A", "--aggressive",
-                      action="store_true", dest="aggressive", default=False,
-                      help="apply ODDS to try to flip each node [default: %default]")
-
-    def seed_callback(option, opt, value, parser):
-        random.seed(value)
-    parser.add_option("-s", "--seed",
-                      action="callback", type="int", callback=seed_callback,
-                      help="initial random number seed for random events")
-
-    parser.add_option("-x", "--exit",
-                      action="store_true", dest="exit", default=False,
-                      help="exit on the 1st gratuitous IP move or IP imbalance")
-    parser.add_option("-H", "--hard-imbalance-limit",
-                      action="store", type="int", dest="hard_limit", default=1,
-                      help="exceeding this limit causes termination  [default: %default]")
-    parser.add_option("-S", "--soft-imbalance-limit",
-                      action="store", type="int", dest="soft_limit", default=1,
-                      help="exceeding this limit increments a counter [default: %default]")
-
-    (options, args) = parser.parse_args()
-
-    if len(args) != 0:
-        parser.error("too many arguments")
-
-    # Could use a callback for this or change the default, but
-    # laziness is sometimes a virtue.  ;-)
-    if options.lcp2:
-        options.deterministic_public_ips = False
-
-def print_begin(t, delim='='):
-    print delim * 40
-    print "%s:" % (t)
-
-def print_end():
-    print "-" * 40
-
-def verbose_begin(t):
-    if options.verbose > 0:
-        print_begin(t)
-
-def verbose_end():
-    if options.verbose > 0:
-        print_end()
-
-def verbose_print(t):
-    if options.verbose > 0:
-        if not type(t) == list:
-            t = [t]
-        if t != []:
-            print "\n".join([str(i) for i in t])
-
-# more than this and we switch to the logging module...  :-)
-def debug_begin(t):
-    if options.verbose > 1:
-        print_begin(t, '-')
-
-def debug_end():
-    if options.verbose > 1:
-        print_end()
-
-def debug_print(t):
-    if options.verbose > 1:
-        if not type(t) == list:
-            t = [t]
-        if t != []:
-            print "\n".join([str(i) for i in t])
-
-def ip_to_list_of_ints(ip):
-    # Be lazy... but only expose errors in IPv4 addresses, since
-    # they'll be more commonly used.  :-)
-    try:
-        l = socket.inet_pton(socket.AF_INET6, ip)
-    except:
-        # Pad with leading 0s.  This makes IPv4 addresses comparable
-        # with IPv6 but reduces the overall effectiveness of the
-        # algorithm.  The alternative would be to treat these
-        # addresses separately while trying to keep all the IPs in
-        # overall balance.
-        l = "".join(itertools.repeat("\0", 12)) + \
-            socket.inet_pton(socket.AF_INET, ip)
-
-    return map(lambda x: struct.unpack('B', x)[0], l)
-
-def ip_distance(ip1, ip2):
-    """Calculate the distance between 2 IPs.
-
-    This is the length of the longtest common prefix between the IPs.
-    It is calculated by XOR-ing the 2 IPs together and counting the
-    number of leading zeroes."""
-
-    distance = 0
-    for (o1, o2) in zip(ip_to_list_of_ints(ip1), ip_to_list_of_ints(ip2)):
-        # XOR this pair of octets
-        x = o1 ^ o2
-        # count number leading zeroes
-        if x == 0:
-            distance += 8
-        else:
-            # bin() gives minimal length '0bNNN' string
-            distance += (8 - (len(bin(x)) - 2))
-            break
-
-    return distance
-
-def ip_distance_2_sum(ip, ips):
-    """Calculate the IP distance for the given IP relative to IPs.
-
-    This could be made more efficient by insering ip_distance_2 into
-    the loop in this function.  However, that would result in some
-    loss of clarity and also will not be necessary in a C
-    implemntation."""
-
-    sum = 0
-    for i in ips:
-        sum += ip_distance(ip, i) ** 2
-
-    return sum
-
-def imbalance_metric(ips):
-    """Return the imbalance metric for a group of IPs.
-
-    This is the sum of squares of the IP distances between each pair of IPs."""
-    if len(ips) > 1:
-        (h, t) = (ips[0], ips[1:])
-        return ip_distance_2_sum(h, t) + imbalance_metric(t)
-    else:
-        return 0
-
-def mean(l):
-    return float(sum(l))/len(l)
-
-class Node(object):
-    def __init__(self, public_addresses):
-        # List of list allows groups of IPs to be passed in.  They're
-        # not actually used in the algorithm but are just used by
-        # calculate_imbalance() for checking the simulation.  Note
-        # that people can pass in garbage and make this code
-        # fail... but we're all friends here in simulation world...
-        # :-)
-        if type(public_addresses[0]) is str:
-            self.public_addresses = set(public_addresses)
-            self.ip_groups = []
-        else:
-            # flatten
-            self.public_addresses = set([i for s in public_addresses for i in s])
-            self.ip_groups = public_addresses
-
-        self.current_addresses = set()
-        self.healthy = True
-        self.imbalance = -1
-
-    def __str__(self):
-        return "%s %s%s" % \
-            ("*" if len(self.public_addresses) == 0 else \
-                 (" " if self.healthy else "#"),
-             sorted(list(self.current_addresses)),
-             " %d" % self.imbalance if options.lcp2 else "")
-
-    def can_node_serve_ip(self, ip):
-        return ip in self.public_addresses
-
-    def node_ip_coverage(self, ips=None):
-        return len([a for a in self.current_addresses if ips == None or a in ips])
-
-    def set_imbalance(self, imbalance=-1):
-        """Set the imbalance metric to the given value.  If none given
-        then calculate it."""
-
-        if imbalance != -1:
-            self.imbalance = imbalance
-        else:
-            self.imbalance = imbalance_metric(list(self.current_addresses))
-
-    def get_imbalance(self):
-        return self.imbalance
-
-class Cluster(object):
-    def __init__(self):
-        self.nodes = []
-        self.deterministic_public_ips = options.deterministic_public_ips
-        self.no_ip_failback = options.no_ip_failback
-        self.all_public_ips = set()
-
-        # Statistics
-        self.ip_moves = []
-        self.grat_ip_moves = []
-        self.imbalance = []
-        self.imbalance_groups = []
-        self.imbalance_count = 0
-        self.imbalance_groups_count = itertools.repeat(0)
-        self.imbalance_metric = []
-        self.events = -1
-        self.num_unhealthy = []
-
-        self.prev = None
-
-    def __str__(self):
-        return "\n".join(["%2d %s" % (i, n) \
-                              for (i, n) in enumerate(self.nodes)])
-
-    # This is naive.  It assumes that IP groups are indicated by the
-    # 1st node having IP groups.
-    def have_ip_groups(self):
-        return (len(self.nodes[0].ip_groups) > 0)
-
-    def print_statistics(self):
-        print_begin("STATISTICS")
-        print "Events:                      %6d" % self.events
-        print "Total IP moves:              %6d" % sum(self.ip_moves)
-        print "Gratuitous IP moves:         %6d" % sum(self.grat_ip_moves)
-        print "Max imbalance:               %6d" % max(self.imbalance)
-        if self.have_ip_groups():
-            print "Max group imbalance counts:    ", map(max, zip(*self.imbalance_groups))
-        print "Mean imbalance:              %f" % mean(self.imbalance)
-        if self.have_ip_groups():
-            print "Mean group imbalances counts:   ", map(mean, zip(*self.imbalance_groups))
-        print "Final imbalance:             %6d" % self.imbalance[-1]
-        if self.have_ip_groups():
-            print "Final group imbalances:         ", self.imbalance_groups[-1]
-        if options.lcp2:
-            print "Max LCP2 imbalance  :        %6d" % max(self.imbalance_metric)
-        print "Soft imbalance count:        %6d" % self.imbalance_count
-        if self.have_ip_groups():
-            print "Soft imbalance group counts:    ", self.imbalance_groups_count
-        if options.lcp2:
-            print "Final LCP2 imbalance  :      %6d" % self.imbalance_metric[-1]
-        print "Maximum unhealthy:           %6d" % max(self.num_unhealthy)
-        print_end()
-
-    def find_pnn_with_ip(self, ip):
-        for (i, n) in enumerate(self.nodes):
-            if ip in n.current_addresses:
-                return i
-        return -1
-
-    def quietly_remove_ip(self, ip):
-        # Remove address from old node.
-        old = self.find_pnn_with_ip(ip)
-        if old != -1:
-            self.nodes[old].current_addresses.remove(ip)
-
-    def add_node(self, node):
-        self.nodes.append(node)
-        self.all_public_ips |= node.public_addresses
-
-    def healthy(self, *pnns):
-        verbose_begin("HEALTHY")
-
-        for pnn in pnns:
-            self.nodes[pnn].healthy = True
-            verbose_print(pnn)
-
-        verbose_end()
-
-    def unhealthy(self, *pnns):
-
-        verbose_begin("UNHEALTHY")
-
-        for pnn in pnns:
-            self.nodes[pnn].healthy = False
-            verbose_print(pnn)
-
-        verbose_end()
-
-    def do_something_random(self):
-
-        """Make random node(s) healthy or unhealthy.
-
-        If options.aggressive is False then: If all nodes are healthy
-        or unhealthy, then invert one of them; otherwise, there's a 1
-        in options.odds chance of making another node unhealthy.
-
-        If options.aggressive is True then: For each node there is a 1
-        in options.odds chance of flipping the state of that node
-        between healthy and unhealthy."""
-
-        if not options.aggressive:
-            num_nodes = len(self.nodes)
-            healthy_pnns = [i for (i,n) in enumerate(self.nodes) if n.healthy]
-            num_healthy = len(healthy_pnns)
-
-            if num_nodes == num_healthy:
-                self.unhealthy(random.randint(0, num_nodes-1))
-            elif num_healthy == 0:
-                self.healthy(random.randint(0, num_nodes-1))
-            elif random.randint(1, options.odds) == 1:
-                self.unhealthy(random.choice(healthy_pnns))
-            else:
-                all_pnns = range(num_nodes)
-                unhealthy_pnns = sorted(list(set(all_pnns) - set(healthy_pnns)))
-                self.healthy(random.choice(unhealthy_pnns))
-        else:
-            # We need to make at least one change or we retry...x
-            changed = False
-            while not changed:
-                for (pnn, n) in enumerate(self.nodes):
-                    if random.randint(1, options.odds) == 1:
-                        changed = True
-                        if n.healthy:
-                            self.unhealthy(pnn)
-                        else:
-                            self.healthy(pnn)
-
-    def random_iterations(self):
-        i = 1
-        while i <= options.iterations:
-            verbose_begin("EVENT %d" % i)
-            verbose_end()
-            self.do_something_random()
-            if self.recover() and options.exit:
-                break
-            i += 1
-
-        self.print_statistics()
-
-    def imbalance_for_ips(self, ips):
-
-        imbalance = 0
-
-        maxnode = -1
-        minnode = -1
-
-        for ip in ips:
-            for (i, n) in enumerate(self.nodes):
-
-                if not n.healthy or not n.can_node_serve_ip(ip):
-                    continue
-
-                num = n.node_ip_coverage(ips)
-
-                if maxnode == -1 or num > maxnum:
-                    maxnode = i
-                    maxnum = num
-
-                if minnode == -1 or num < minnum:
-                    minnode = i
-                    minnum = num
-
-            if maxnode == -1 or minnode == -1:
-                continue
-
-            i = maxnum - minnum
-            #if i < 2:
-            #    i = 0
-            imbalance = max([imbalance, i])
-
-        return imbalance
-
-
-    def calculate_imbalance(self):
-
-        # First, do all the assigned IPs.
-        assigned = sorted([ip
-                           for n in self.nodes
-                           for ip in n.current_addresses])
-
-        i = self.imbalance_for_ips(assigned)
-
-        ig = []
-        # FIXME?  If dealing with IP groups, assume the nodes are all
-        # the same.
-        for ips in self.nodes[0].ip_groups:
-            gi = self.imbalance_for_ips(ips)
-            ig.append(gi)
-
-        return (i, ig)
-
-
-    def diff(self):
-        """Calculate differences in IP assignments between self and prev.
-
-        Gratuitous IP moves (from a healthy node to a healthy node)
-        are prefixed by !!."""
-
-        ip_moves = 0
-        grat_ip_moves = 0
-        details = []
-
-        for (new, n) in enumerate(self.nodes):
-            for ip in n.current_addresses:
-                old = self.prev.find_pnn_with_ip(ip)
-                if old != new:
-                    ip_moves += 1
-                    if old != -1 and \
-                            self.prev.nodes[new].healthy and \
-                            self.nodes[new].healthy and \
-                            self.nodes[old].healthy and \
-                            self.prev.nodes[old].healthy:
-                        prefix = "!!"
-                        grat_ip_moves += 1
-                    else:
-                        prefix = "  "
-                    details.append("%s %s: %d -> %d" %
-                                   (prefix, ip, old, new))
-
-        return (ip_moves, grat_ip_moves, details)
-
-    def find_takeover_node(self, ip):
-
-        pnn = -1
-        min = 0
-        for (i, n) in enumerate(self.nodes):
-            if not n.healthy:
-                continue
-
-            if not n.can_node_serve_ip(ip):
-                continue
-
-            num = n.node_ip_coverage()
-
-            if (pnn == -1):
-                pnn = i
-                min = num
-            else:
-                if num < min:
-                    pnn = i
-                    min = num
-
-        if pnn == -1:
-            verbose_print("Could not find node to take over public address %s" % ip)
-            return False
-
-        self.nodes[pnn].current_addresses.add(ip)
-
-        verbose_print("%s -> %d" % (ip, pnn))
-        return True
-
-    def basic_allocate_unassigned(self):
-
-        assigned = set([ip for n in self.nodes for ip in n.current_addresses])
-        unassigned = sorted(list(self.all_public_ips - assigned))
-
-        for ip in unassigned:
-            self.find_takeover_node(ip)
-
-    def basic_failback(self, retries_l):
-
-        assigned = sorted([ip
-                           for n in self.nodes
-                           for ip in n.current_addresses])
-        for ip in assigned:
-
-            maxnode = -1
-            minnode = -1
-            for (i, n) in enumerate(self.nodes):
-                if not n.healthy:
-                    continue
-
-                if not n.can_node_serve_ip(ip):
-                    continue
-
-                num = n.node_ip_coverage()
-
-                if maxnode == -1:
-                    maxnode = i
-                    maxnum = num
-                else:
-                    if num > maxnum:
-                        maxnode = i
-                        maxnum = num
-                if minnode == -1:
-                    minnode = i
-                    minnum = num
-                else:
-                    if num < minnum:
-                        minnode = i
-                        minnum = num
-
-            if maxnode == -1:
-                print "Could not find maxnode. May not be able to serve ip", ip
-                continue
-
-            #if self.deterministic_public_ips:
-            #    continue
-
-            if maxnum > minnum + 1 and retries_l[0] < options.retries:
-                # Remove the 1st ip from maxnode
-                t = sorted(list(self.nodes[maxnode].current_addresses))
-                realloc = t[0]
-                verbose_print("%s <- %d" % (realloc, maxnode))
-                self.nodes[maxnode].current_addresses.remove(realloc)
-                # Redo the outer loop.
-                retries_l[0] += 1
-                return True
-
-        return False
-
-
-    def lcp2_allocate_unassigned(self):
-
-        # Assign as many unassigned addresses as possible.  Keep
-        # selecting the optimal assignment until we don't manage to
-        # assign anything.
-        assigned = set([ip for n in self.nodes for ip in n.current_addresses])
-        unassigned = sorted(list(self.all_public_ips - assigned))
-
-        should_loop = True
-        while len(unassigned) > 0 and should_loop:
-            should_loop = False
-
-            debug_begin(" CONSIDERING MOVES (UNASSIGNED)")
-
-            minnode = -1
-            mindsum = 0
-            minip = None
-
-            for ip in unassigned:
-                for dstnode in range(len(self.nodes)):
-                    if self.nodes[dstnode].can_node_serve_ip(ip) and \
-                            self.nodes[dstnode].healthy:
-                        dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses)
-                        dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum
-                        debug_print(" %s -> %d [+%d]" % \
-                                        (ip,
-                                         dstnode,
-                                         dstimbl - self.nodes[dstnode].get_imbalance()))
-
-                        if (minnode == -1) or (dstdsum < mindsum):
-                            minnode = dstnode
-                            minimbl = dstimbl
-                            mindsum = dstdsum
-                            minip = ip
-                            should_loop = True
-            debug_end()
-
-            if minnode != -1:
-                self.nodes[minnode].current_addresses.add(minip)
-                self.nodes[minnode].set_imbalance(self.nodes[minnode].get_imbalance() + mindsum)
-                verbose_print("%s -> %d [+%d]" % (minip, minnode, mindsum))
-                unassigned.remove(minip)
-
-        for ip in unassigned:
-            verbose_print("Could not find node to take over public address %s" % ip)
-
-    def lcp2_failback(self, targets):
-
-        # Get the node with the highest imbalance metric.
-        srcnode = -1
-        maximbl = 0
-        for (pnn, n) in enumerate(self.nodes):
-            b = n.get_imbalance()
-            if (srcnode == -1) or (b > maximbl):
-                srcnode = pnn
-                maximbl = b
-
-        # This means that all nodes had 0 or 1 addresses, so can't
-        # be imbalanced.
-        if maximbl == 0:
-            return False
-
-        # We'll need this a few times...
-        ips = self.nodes[srcnode].current_addresses
-
-        # Find an IP and destination node that best reduces imbalance.
-        optimum = None
-        debug_begin(" CONSIDERING MOVES FROM %d [%d]" % (srcnode, maximbl))
-        for ip in ips:
-            # What is this IP address costing the source node?
-            srcdsum = ip_distance_2_sum(ip, ips - set([ip]))
-            srcimbl = maximbl - srcdsum
-
-            # Consider this IP address would cost each potential
-            # destination node.  Destination nodes are limited to
-            # those that are newly healthy, since we don't want to
-            # do gratuitous failover of IPs just to make minor
-            # balance improvements.
-            for dstnode in targets:
-                if self.nodes[dstnode].can_node_serve_ip(ip) and \
-                        self.nodes[dstnode].healthy:
-                    dstdsum = ip_distance_2_sum(ip, self.nodes[dstnode].current_addresses)
-                    dstimbl = self.nodes[dstnode].get_imbalance() + dstdsum
-                    debug_print(" %d [%d] -> %s -> %d [+%d]" % \
-                                    (srcnode,
-                                     srcimbl - self.nodes[srcnode].get_imbalance(),
-                                     ip,
-                                     dstnode,
-                                     dstimbl - self.nodes[dstnode].get_imbalance()))
-
-                    if (dstimbl < maximbl) and (dstdsum < srcdsum):
-                        if optimum is None:
-                            optimum = (ip, srcnode, srcimbl, dstnode, dstimbl)
-                        else:
-                            (x, sn, si, dn, di) = optimum
-                            if (srcimbl + dstimbl) < (si + di):
-                                optimum = (ip, srcnode, srcimbl, dstnode, dstimbl)
-        debug_end()
-
-        if optimum is not None:
-            # We found a move that makes things better...
-            (ip, srcnode, srcimbl, dstnode, dstimbl) = optimum
-            ini_srcimbl = self.nodes[srcnode].get_imbalance()
-            ini_dstimbl = self.nodes[dstnode].get_imbalance()
-
-            self.nodes[srcnode].current_addresses.remove(ip)
-            self.nodes[srcnode].set_imbalance(srcimbl)
-
-            self.nodes[dstnode].current_addresses.add(ip)
-            self.nodes[dstnode].set_imbalance(dstimbl)
-
-            verbose_print("%d [%d] -> %s -> %d [+%d]" % \
-                              (srcnode,
-                               srcimbl - ini_srcimbl,
-                               ip,
-                               dstnode,
-                               dstimbl - ini_dstimbl))
-
-            return True
-
-        return False
-
-    def ctdb_takeover_run_python(self):
-
-        # Don't bother with the num_healthy stuff.  It is an
-        # irrelevant detail.
-
-        # We just keep the allocate IPs in the current_addresses field
-        # of the node.  This needs to readable, not efficient!
-
-        if self.deterministic_public_ips:
-            # Remap everything.
-            addr_list = sorted(list(self.all_public_ips))
-            for (i, ip) in enumerate(addr_list):
-                self.quietly_remove_ip(ip)
-                # Add addresses to new node.
-                pnn = i % len(self.nodes)
-                self.nodes[pnn].current_addresses.add(ip)
-                verbose_print("%s -> %d" % (ip, pnn))
-
-        # Remove public addresses from unhealthy nodes.
-        for (pnn, n) in enumerate(self.nodes):
-            if not n.healthy:
-                verbose_print(["%s <- %d" % (ip, pnn)
-                               for ip in n.current_addresses])
-                n.current_addresses = set()
-
-        # If a node can't serve an assigned address then remove it.
-        for n in self.nodes:
-            verbose_print(["%s <- %d" % (ip, pnn)
-                           for ip in n.current_addresses - n.public_addresses])
-            n.current_addresses &= n.public_addresses
-
-        if options.lcp2:
-            newly_healthy = [pnn for (pnn, n) in enumerate(self.nodes)
-                             if len(n.current_addresses) == 0 and n.healthy]
-            for n in self.nodes:
-                n.set_imbalance()
-
-        # We'll only retry the balancing act up to options.retries
-        # times (for the basic non-deterministic algorithm).  This
-        # nonsense gives us a reference on the retries count in
-        # Python.  It will be easier in C.  :-)
-        # For LCP2 we reassignas many IPs from heavily "loaded" nodes
-        # to nodes that are newly healthy, looping until we fail to
-        # reassign an IP.
-        retries_l = [0]
-        should_loop = True
-        while should_loop:
-            should_loop = False
-
-            if options.lcp2:
-                self.lcp2_allocate_unassigned()
-            else:
-                self.basic_allocate_unassigned()
-
-            if self.no_ip_failback or self.deterministic_public_ips:
-                break
-
-            if options.lcp2:
-                if len(newly_healthy) == 0:
-                    break
-                should_loop = self.lcp2_failback(newly_healthy)
-            else:
-                should_loop = self.basic_failback(retries_l)
-
-    def ctdb_takeover_run_external(self):
-
-        # Written while asleep...
-
-        # Convert the cluster state to something that be fed to
-        # ctdb_takeover_tests ipalloc ...
-
-        in_lines = []
-        for ip in sorted(list(self.all_public_ips)):
-            allowed = []
-            assigned = -1
-            for (i, n) in enumerate(self.nodes):
-                if n.can_node_serve_ip(ip):
-                    allowed.append("%s" % i)
-                if ip in n.current_addresses:
-                    assigned = i
-            line = "%s\t%d\t%s" % (ip, assigned, ",".join(allowed))
-            in_lines.append(line)
-
-        nodestates = ",".join(["0" if n.healthy else "1" for n in self.nodes])
-
-        if options.lcp2:
-            os.environ["CTDB_LCP2"] = "yes"
-        if options.verbose > 1:
-            os.environ["CTDB_TEST_LOGLEVEL"] = "4"
-        elif options.verbose == 1:
-            os.environ["CTDB_TEST_LOGLEVEL"] = "3"
-        else:
-            os.environ["CTDB_TEST_LOGLEVEL"] = "0"
-
-        p = subprocess.Popen("../../bin/ctdb_takeover_tests ipalloc %s 2>&1" % nodestates,
-                             shell=True,
-                             stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-        p.stdin.write("\n".join(in_lines))
-        p.stdin.close()
-
-        # Flush all of the assigned IPs.
-        for n in self.nodes:
-            n.current_addresses = set()
-
-        # Uses the results to populate the current_addresses for each
-        # node.
-        for line in p.stdout.read().split("\n"):
-            # Some lines are debug, some are the final IP
-            # configuration.  Let's use a gross hack that assumes any
-            # line with 2 words is IP configuration.  That will do for
-            # now.
-            words = re.split("\s+", line)
-            if len(words) == 2:
-                # Add the IP as current for the specified node.
-                self.nodes[int(words[1])].current_addresses.add(words[0])
-            else:
-                 # First 3 words are log date/time, remove them...
-                 print " ".join(words[3:])
-
-        # Now fake up the LCP calculations.
-        for n in self.nodes:
-            n.set_imbalance()
-
-    def ctdb_takeover_run(self):
-
-        self.events += 1
-
-        if options.external:
-            return self.ctdb_takeover_run_external()
-        else:
-            return self.ctdb_takeover_run_python()
-
-    def recover(self):
-        verbose_begin("TAKEOVER")
-
-        self.ctdb_takeover_run()
-
-        verbose_end()
-
-        grat_ip_moves = 0
-
-        if self.prev is not None:
-            (ip_moves, grat_ip_moves, details) = self.diff()
-            self.ip_moves.append(ip_moves)
-            self.grat_ip_moves.append(grat_ip_moves)
-
-            if options.diff:
-                print_begin("DIFF")
-                print "\n".join(details)
-                print_end()
-
-        (imbalance, imbalance_groups) = self.calculate_imbalance()
-        self.imbalance.append(imbalance)
-        self.imbalance_groups.append(imbalance_groups)
-
-        if imbalance > options.soft_limit:
-            self.imbalance_count += 1
-
-        # There must be a cleaner way...
-        t = []
-        for (c, i) in zip(self.imbalance_groups_count, imbalance_groups):
-            if i > options.soft_limit:
-                t.append(c + i)
-            else:
-                t.append(c)
-        self.imbalance_groups_count = t
-
-        imbalance_metric = max([n.get_imbalance() for n in self.nodes])
-        self.imbalance_metric.append(imbalance_metric)
-        if options.balance:
-            print_begin("IMBALANCE")
-            print "ALL IPS:", imbalance
-            if self.have_ip_groups():
-                print "IP GROUPS:", imbalance_groups
-            if options.lcp2:
-                print "LCP2 IMBALANCE:", imbalance_metric
-            print_end()
-
-        num_unhealthy = len(self.nodes) - \
-            len([n for n in self.nodes if n.healthy])
-        self.num_unhealthy.append(num_unhealthy)
-
-        if options.show:
-            print_begin("STATE")
-            print self
-            print_end()
-
-        self.prev = None
-        self.prev = copy.deepcopy(self)
-
-        # True is bad!
-        return (grat_ip_moves > 0) or \
-            (not self.have_ip_groups() and imbalance > options.hard_limit) or \
-            (self.have_ip_groups() and (max(imbalance_groups) > options.hard_limit))
diff --git a/ctdb/tests/takeover/simulation/hey_jude.py b/ctdb/tests/takeover/simulation/hey_jude.py
deleted file mode 100755 (executable)
index a6b14c5..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env python
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses10 = ['10.4.20.%d' % n for n in range(154, 168)]
-addresses172a = ['172.20.106.%d' % n for n in range(110, 124)]
-addresses172b = ['172.20.107.%d' % n for n in range(110, 117)]
-
-c = Cluster()
-
-#for i in range(7):
-#    c.add_node(Node([addresses10, addresses172]))
-
-
-for i in range(4):
-    c.add_node(Node([addresses172a, addresses172b]))
-for i in range(3):
-    c.add_node(Node(addresses10))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/ip_groups1.py b/ctdb/tests/takeover/simulation/ip_groups1.py
deleted file mode 100755 (executable)
index 0808f46..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# 2 IP groups, both on the same 5 nodes, with each group on different
-# interfaces/VLANs.  One group has many more addresses to test how
-# well an "imbalanced" configuration will balance...
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses20 = ['192.168.20.%d' % n for n in range(1, 13)]
-addresses128 = ['192.168.128.%d' % n for n in range(1, 5)]
-
-c = Cluster()
-
-for i in range(5):
-    c.add_node(Node([addresses20, addresses128]))
-
-#for i in range(3):
-#    c.add_node(Node([addresses20]))
-
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/ip_groups2.py b/ctdb/tests/takeover/simulation/ip_groups2.py
deleted file mode 100755 (executable)
index c6c1026..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-
-# 2 groups of addresses, combined into 1 pool so the checking
-# algorithm doesn't know about the groups, across 2 nodes.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses20 = ['192.168.20.%d' % n for n in range(1, 13)]
-addresses21 = ['192.168.21.%d' % n for n in range(1, 5)]
-
-c = Cluster()
-
-for i in range(2):
-    c.add_node(Node(addresses20 + addresses21))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/ip_groups3.py b/ctdb/tests/takeover/simulation/ip_groups3.py
deleted file mode 100755 (executable)
index 149946d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python
-
-# 4 IP groups, across 10 nodes, with each group on different
-# interfaces/VLANs.  80 addresses in total but not evenly balanced, to
-# help check some of the more extreme behaviour.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 41)]
-addresses2 = ['192.168.2.%d' % n for n in range(1, 21)]
-addresses3 = ['192.168.3.%d' % n for n in range(1, 11)]
-addresses4 = ['192.168.4.%d' % n for n in range(1, 11)]
-
-# Try detecting imbalance with square root of number of nodes?  Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(10):
-    c.add_node(Node([addresses1, addresses2, addresses3, addresses4]))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/ip_groups4.py b/ctdb/tests/takeover/simulation/ip_groups4.py
deleted file mode 100755 (executable)
index fdcef7f..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# 2 IP groups, across 2 nodes, with each group on different
-# interfaces.  4 addresses per group.  A nice little canonical 2 node
-# configuration.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 5)]
-addresses2 = ['192.168.2.%d' % n for n in range(1, 5)]
-
-# Try detecting imbalance with square root of number of nodes?  Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(2):
-    c.add_node(Node([addresses1, addresses2]))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/ip_groups5.py b/ctdb/tests/takeover/simulation/ip_groups5.py
deleted file mode 100755 (executable)
index 8c46150..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-
-# 1 IP group, to test backward compatibility of LCP2 algorithm.  16
-# addresses across 4 nodes.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['192.168.1.%d' % n for n in range(1, 17)]
-
-# Try detecting imbalance with square root of number of nodes?  Or
-# just with a parameter indicating how unbalanced you're willing to
-# accept...
-
-c = Cluster()
-
-for i in range(4):
-    c.add_node(Node(addresses1))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/mgmt_simple.py b/ctdb/tests/takeover/simulation/mgmt_simple.py
deleted file mode 100755 (executable)
index f891199..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-
-# This is an example showing a current SONAS configuration with 3
-# interface node and a management node.  When run with deterministic
-# IPs there are gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
-
-c = Cluster()
-
-for i in range(3):
-    c.add_node(Node(addresses))
-
-c.add_node(Node([]))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/node_group.py b/ctdb/tests/takeover/simulation/node_group.py
deleted file mode 100755 (executable)
index bf7de58..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python
-
-# This demonstrates a node group configurations.
-#
-# Node groups can be defined with the syntax "-g N@IP0,IP1-IP2,IP3".
-# This says to create a group of N nodes with IPs IP0, IP1, ..., IP2,
-# IP3.  Run it with deterministic IPs causes lots of gratuitous IP
-# reassignments.  Running with --nd fixes this.
-
-import ctdb_takeover
-import sys
-from optparse import make_option
-import string
-
-ctdb_takeover.process_args([
-        make_option("-g", "--group",
-                    action="append", type="string", dest="groups",
-                    help="define a node group using N@IPs syntax"),
-        ])
-
-def expand_range(r):
-    sr = r.split("-", 1)
-    if len(sr) == 2:
-        all = string.ascii_uppercase + string.ascii_lowercase
-        sr = list(all[all.index(sr[0]):all.index(sr[1])+1])
-    return sr
-            
-def add_node_group(s):
-    (count, ips_str) = s.split("@", 1)
-    ips = [i for r in ips_str.split(",") \
-               for i in expand_range(r) if r != ""]
-    for i in range(int(count)):
-        c.add_node(ctdb_takeover.Node(ips))
-
-c = ctdb_takeover.Cluster()
-
-if ctdb_takeover.options.groups is None:
-    print "Error: no node groups defined."
-    sys.exit(1)
-
-for g in ctdb_takeover.options.groups:
-    add_node_group(g)
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/node_group_extra.py b/ctdb/tests/takeover/simulation/node_group_extra.py
deleted file mode 100755 (executable)
index 7e9e518..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-
-# This example demonstrates a node group configuration.  Is it meant
-# to be the same as node_group_simple.py, but with a couple of nodes
-# added later, so they are listed after the management node.
-
-# When run with deterministic IPs (use "-d" to show the problem) it
-# does many gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] + ['P', 'Q', 'R', 'S', 'T', 'U']
-addresses2 = ['I', 'J', 'K', 'L']
-
-c = Cluster()
-
-for i in range(4):
-    c.add_node(Node(addresses1))
-
-for i in range(3):
-    c.add_node(Node(addresses2))
-
-c.add_node(Node([]))
-c.add_node(Node(addresses1))
-c.add_node(Node(addresses2))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/node_group_simple.py b/ctdb/tests/takeover/simulation/node_group_simple.py
deleted file mode 100755 (executable)
index 3c58ef7..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-
-# This example demonstrates a simple, sensible node group
-# configuration.  When run with deterministic IPs (use "-d" to show
-# the problem) it does many gratuitous IP reassignments.
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
-addresses2 = ['I', 'J', 'K']
-
-c = Cluster()
-
-for i in range(4):
-    c.add_node(Node(addresses1))
-
-for i in range(3):
-    c.add_node(Node(addresses2))
-
-c.add_node(Node([]))
-
-c.recover()
-
-c.random_iterations()
diff --git a/ctdb/tests/takeover/simulation/nondet_path_01.py b/ctdb/tests/takeover/simulation/nondet_path_01.py
deleted file mode 100755 (executable)
index a62847a..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-# This is a contrived example that makes the balancing algorithm fail
-# for nondeterministic IPs (run with "-dv --nd" to see the failure).
-
-from ctdb_takeover import Cluster, Node, process_args
-
-process_args()
-
-addresses1 = ['A', 'B', 'C', 'D']
-addresses2 = ['B', 'E', 'F']
-
-c = Cluster()
-
-for i in range(2):
-    c.add_node(Node(addresses1))
-
-c.add_node(Node(addresses2))
-
-c.recover()
-
-c.unhealthy(1)
-c.recover()
-c.healthy(1)
-c.recover()