diff options
author | Martin Schwenke <martin@meltin.net> | 2016-12-11 09:09:44 +1100 |
---|---|---|
committer | Amitay Isaacs <amitay@samba.org> | 2016-12-19 07:58:45 +0100 |
commit | 59dc07ee4ab6350a2736a1bd47ba26be7fae67e2 (patch) | |
tree | 868b81a884df0c90625680bb22763af96677b49a /ctdb/tests | |
parent | 641b69da80f66e399c6ad78183067ccb4a76e02e (diff) | |
download | samba-59dc07ee4ab6350a2736a1bd47ba26be7fae67e2.tar.gz |
ctdb-tests: Remove the python LCP2 simulation
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
Diffstat (limited to 'ctdb/tests')
-rw-r--r-- | ctdb/tests/takeover/simulation/README | 6 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ctdb_takeover.py | 888 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/hey_jude.py | 24 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ip_groups1.py | 25 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ip_groups2.py | 20 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ip_groups3.py | 27 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ip_groups4.py | 25 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/ip_groups5.py | 23 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/mgmt_simple.py | 22 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/node_group.py | 46 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/node_group_extra.py | 31 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/node_group_simple.py | 26 | ||||
-rwxr-xr-x | ctdb/tests/takeover/simulation/nondet_path_01.py | 25 |
13 files changed, 0 insertions, 1188 deletions
diff --git a/ctdb/tests/takeover/simulation/README b/ctdb/tests/takeover/simulation/README deleted file mode 100644 index 4a8267bf6e1..00000000000 --- a/ctdb/tests/takeover/simulation/README +++ /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 index 1aeeaca67e8..00000000000 --- a/ctdb/tests/takeover/simulation/ctdb_takeover.py +++ /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 index a6b14c5c9b4..00000000000 --- a/ctdb/tests/takeover/simulation/hey_jude.py +++ /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 index 0808f466cf1..00000000000 --- a/ctdb/tests/takeover/simulation/ip_groups1.py +++ /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 index c6c10266461..00000000000 --- a/ctdb/tests/takeover/simulation/ip_groups2.py +++ /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 index 149946d72b4..00000000000 --- a/ctdb/tests/takeover/simulation/ip_groups3.py +++ /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 index fdcef7f0a69..00000000000 --- a/ctdb/tests/takeover/simulation/ip_groups4.py +++ /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 index 8c461506389..00000000000 --- a/ctdb/tests/takeover/simulation/ip_groups5.py +++ /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 index f891199655a..00000000000 --- a/ctdb/tests/takeover/simulation/mgmt_simple.py +++ /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 index bf7de58aa97..00000000000 --- a/ctdb/tests/takeover/simulation/node_group.py +++ /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 index 7e9e518bddd..00000000000 --- a/ctdb/tests/takeover/simulation/node_group_extra.py +++ /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 index 3c58ef7314a..00000000000 --- a/ctdb/tests/takeover/simulation/node_group_simple.py +++ /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 index a62847a2163..00000000000 --- a/ctdb/tests/takeover/simulation/nondet_path_01.py +++ /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() |