summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/tests/t_iprop.py336
1 files changed, 238 insertions, 98 deletions
diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
index bd9ab8984d..1dfc55bf27 100644
--- a/src/tests/t_iprop.py
+++ b/src/tests/t_iprop.py
@@ -1,6 +1,7 @@
#!/usr/bin/python
import os
+import re
from k5test import *
@@ -46,27 +47,72 @@ def wait_for_prop(kpropd, full_expected):
fail('kadmind returned invalid result')
-# Verify the iprop log last serial number against an expected value,
-# on either the master or slave.
-def check_serial(realm, expected, env=None):
- out = realm.run([kproplog, '-h'], env=env)
- if 'Last serial # : ' not in out:
- fail('Unexpected serial number')
+# Verify the output of kproplog against the expected number of
+# entries, first and last serial number, and a list of principal names
+# for the update entrires.
+def check_ulog(num, first, last, entries, env=None):
+ out = realm.run([kproplog], env=env)
+ if 'Number of entries : ' + str(num) + '\n' not in out:
+ fail('Expected %d entries' % num)
+ if last:
+ firststr = first and str(first) or 'None'
+ if 'First serial # : ' + firststr + '\n' not in out:
+ fail('Expected first serial number %d' % first)
+ laststr = last and str(last) or 'None'
+ if 'Last serial # : ' + laststr + '\n' not in out:
+ fail('Expected last serial number %d' % last)
+ assert(len(entries) == num)
+ ser = first - 1
+ entindex = 0
+ for line in out.splitlines():
+ m = re.match(r'\tUpdate serial # : (\d+)$', line)
+ if m:
+ ser = ser + 1
+ if m.group(1) != str(ser):
+ fail('Expected serial number %d in update entry' % ser)
+ m = re.match(r'\tUpdate principal : (.*)$', line)
+ if m:
+ eprinc = entries[ser - first]
+ if m.group(1) != eprinc:
+ fail('Expected princ %s in update entry %d' % (eprinc, ser))
+# slave1 will receive updates from master, and slave2 will receive
+# updates from slave1. Because of the awkward way iprop and kprop
+# port configuration currently works, we need separate config files
+# for the slave and master sides of slave1, but they use the same DB
+# and ulog file.
+conf = {'realms': {'$realm': {'iprop_enable': 'true',
+ 'iprop_logfile': '$testdir/db.ulog'}}}
+conf_slave1 = {'realms': {'$realm': {'iprop_slave_poll': '600',
+ 'iprop_logfile': '$testdir/ulog.slave1'}},
+ 'dbmodules': {'db': {'database_name': '$testdir/db.slave1'}}}
+conf_slave1m = {'realms': {'$realm': {'iprop_logfile': '$testdir/ulog.slave1',
+ 'iprop_port': '$port8'}},
+ 'dbmodules': {'db': {'database_name': '$testdir/db.slave1'}}}
+conf_slave2 = {'realms': {'$realm': {'iprop_slave_poll': '600',
+ 'iprop_logfile': '$testdir/ulog.slave2',
+ 'iprop_port': '$port8'}},
+ 'dbmodules': {'db': {'database_name': '$testdir/db.slave2'}}}
-conf = {
- 'realms': {'$realm': {
- 'iprop_enable': 'true',
- 'iprop_logfile' : '$testdir/db.ulog'}}}
+realm = K5Realm(kdc_conf=conf, create_user=False, start_kadmind=True)
+slave1 = realm.special_env('slave1', True, kdc_conf=conf_slave1)
+slave1m = realm.special_env('slave1m', True, kdc_conf=conf_slave1m)
+slave2 = realm.special_env('slave2', True, kdc_conf=conf_slave2)
-conf_slave = {
- 'realms': {'$realm': {
- 'iprop_slave_poll': '600',
- 'iprop_logfile' : '$testdir/db.slave.ulog'}},
- 'dbmodules': {'db': {'database_name': '$testdir/db.slave'}}}
+# Define some principal names. pr3 is long enough to cause internal
+# reallocs, but not long enough to grow the basic ulog entry size.
+pr1 = 'wakawaka@' + realm.realm
+pr2 = 'w@' + realm.realm
+c = 'chocolate-flavored-school-bus'
+cs = c + '/'
+pr3 = (cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c +
+ '@' + realm.realm)
-realm = K5Realm(kdc_conf=conf, create_user=False, start_kadmind=True)
-slave = realm.special_env('slave', True, kdc_conf=conf_slave)
+# Create the kpropd ACL file.
+acl_file = os.path.join(realm.testdir, 'kpropd-acl')
+acl = open(acl_file, 'w')
+acl.write(realm.host_princ + '\n')
+acl.close()
ulog = os.path.join(realm.testdir, 'db.ulog')
if not os.path.exists(ulog):
@@ -77,113 +123,207 @@ kiprop_princ = 'kiprop/' + hostname
realm.addprinc(kiprop_princ)
realm.extract_keytab(kiprop_princ, realm.keytab)
-# Create the slave db.
+# Create the initial slave1 and slave2 databases.
dumpfile = os.path.join(realm.testdir, 'dump')
realm.run([kdb5_util, 'dump', dumpfile])
-realm.run([kdb5_util, 'load', dumpfile], slave)
-realm.run([kdb5_util, 'stash', '-P', 'master'], slave)
+realm.run([kdb5_util, 'load', dumpfile], slave1)
+realm.run([kdb5_util, 'load', dumpfile], slave2)
-# Make some changes to the master db.
-realm.addprinc('wakawaka')
-# Add a principal enough to make realloc likely, but not enough to grow
-# basic ulog entry size.
-c = 'chocolate-flavored-school-bus'
-cs = c + '/'
-longname = cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + cs + c
-realm.addprinc(longname)
-realm.addprinc('w')
-realm.run_kadminl('modprinc -allow_tix w')
-realm.run_kadminl('modprinc +allow_tix w')
+# Reinitialize the master ulog so we know exactly what to expect in
+# it.
+realm.run([kproplog, '-R'])
+check_ulog(0, 0, 0, [])
-check_serial(realm, '7')
+# Make some changes to the master DB.
+realm.addprinc(pr1)
+realm.addprinc(pr3)
+realm.addprinc(pr2)
+realm.run_kadminl('modprinc -allow_tix ' + pr2)
+realm.run_kadminl('modprinc +allow_tix ' + pr2)
+check_ulog(5, 1, 5, [pr1, pr3, pr2, pr2, pr2])
-# Set up the kpropd acl file.
-acl_file = os.path.join(realm.testdir, 'kpropd-acl')
-acl = open(acl_file, 'w')
-acl.write(realm.host_princ + '\n')
-acl.close()
-
-# Start kpropd and get a full dump from master.
-kpropd = realm.start_kpropd(slave, ['-d'])
-wait_for_prop(kpropd, True)
-out = realm.run_kadminl('listprincs', slave)
-if longname not in out or 'wakawaka' not in out or 'w@' not in out:
- fail('Slave does not have all principals from master')
+# Start kpropd for slave1 and get a full dump from master.
+kpropd1 = realm.start_kpropd(slave1, ['-d'])
+wait_for_prop(kpropd1, True)
+out = realm.run_kadminl('listprincs', slave1)
+if pr1 not in out or pr2 not in out or pr3 not in out:
+ fail('slave1 does not have all principals from master')
+check_ulog(0, 0, 5, [], slave1)
# Make a change and check that it propagates incrementally.
-realm.run_kadminl('modprinc -allow_tix w')
-check_serial(realm, '8')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, False)
-check_serial(realm, '8', slave)
-out = realm.run_kadminl('getprinc w', slave)
+realm.run_kadminl('modprinc -allow_tix ' + pr2)
+check_ulog(6, 1, 6, [pr1, pr3, pr2, pr2, pr2, pr2])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, False)
+check_ulog(1, 6, 6, [pr2], slave1)
+out = realm.run_kadminl('getprinc ' + pr2, slave1)
if 'Attributes: DISALLOW_ALL_TIX' not in out:
- fail('Slave does not have modification from master')
+ fail('slave1 does not have modification from master')
-# Make another change and check that it propagates incrementally.
-realm.run_kadminl('modprinc +allow_tix w')
-check_serial(realm, '9')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, False)
-check_serial(realm, '9', slave)
-out = realm.run_kadminl('getprinc w', slave)
-if 'Attributes:\n' not in out:
- fail('Slave does not have modification from master')
+# Start kadmind -proponly for slave1. (Use the slave1m environment
+# which defines iprop_port to $port8.)
+slave1_out_dump_path = os.path.join(realm.testdir, 'dump.slave1.out')
+slave2_in_dump_path = os.path.join(realm.testdir, 'dump.slave2.in')
+slave2_kprop_port = str(realm.portbase + 9)
+slave1m['KPROP_PORT'] = slave2_kprop_port
+realm.start_server([kadmind, '-nofork', '-proponly', '-W', '-p', kdb5_util,
+ '-K', kprop, '-F', slave1_out_dump_path], 'starting...',
+ slave1m)
+
+# Start kpropd for slave2. The -A option isn't needed since we're
+# talking to the same host as master (we specify it anyway to exercise
+# the code), but slave2 defines iprop_port to $port8 so it will talk
+# to slave1. Get a full dump from slave1.
+kpropd2 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port,
+ '-f', slave2_in_dump_path, '-p', kdb5_util,
+ '-a', acl_file, '-A', hostname], 'ready', slave2)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 6, [], slave2)
+out = realm.run_kadminl('listprincs', slave1)
+if pr1 not in out or pr2 not in out or pr3 not in out:
+ fail('slave2 does not have all principals from slave1')
+
+# Make another change and check that it propagates incrementally to
+# both slaves.
+realm.run_kadminl('modprinc -maxrenewlife "22 hours" ' + pr1)
+check_ulog(7, 1, 7, [pr1, pr3, pr2, pr2, pr2, pr2, pr1])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, False)
+check_ulog(2, 6, 7, [pr2, pr1], slave1)
+out = realm.run_kadminl('getprinc ' + pr1, slave1)
+if 'Maximum renewable life: 0 days 22:00:00\n' not in out:
+ fail('slave1 does not have modification from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, False)
+check_ulog(1, 7, 7, [pr1], slave2)
+out = realm.run_kadminl('getprinc ' + pr1, slave2)
+if 'Maximum renewable life: 0 days 22:00:00\n' not in out:
+ fail('slave2 does not have modification from slave1')
-# Reset the ulog on the slave side to force a full resync to the slave.
-realm.run([kproplog, '-R'], slave)
-check_serial(realm, 'None', slave)
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, True)
-check_serial(realm, '9', slave)
+# Reset the ulog on slave1 to force a full resync from master. The
+# resync will use the old dump file and then propagate changes.
+# slave2 should still be in sync with slave1 after the resync, so make
+# sure it doesn't take a full resync.
+realm.run([kproplog, '-R'], slave1)
+check_ulog(0, 0, 0, [], slave1)
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(2, 6, 7, [pr2, pr1], slave1)
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, False)
+check_ulog(1, 7, 7, [pr1], slave2)
-# Make another change and check that it propagates incrementally.
+# Make another change and check that it propagates incrementally to
+# both slaves.
realm.run_kadminl('modprinc +allow_tix w')
-check_serial(realm, '10')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, False)
-check_serial(realm, '10', slave)
-out = realm.run_kadminl('getprinc w', slave)
+check_ulog(8, 1, 8, [pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr2])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, False)
+check_ulog(3, 6, 8, [pr2, pr1, pr2], slave1)
+out = realm.run_kadminl('getprinc ' + pr2, slave1)
if 'Attributes:\n' not in out:
- fail('Slave has different state from master')
+ fail('slave1 does not have modification from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, False)
+check_ulog(2, 7, 8, [pr1, pr2], slave2)
+out = realm.run_kadminl('getprinc ' + pr2, slave2)
+if 'Attributes:\n' not in out:
+ fail('slave2 does not have modification from slave1')
# Create a policy and check that it propagates via full resync.
realm.run_kadminl('addpol -minclasses 2 testpol')
-check_serial(realm, 'None')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, True)
-check_serial(realm, 'None', slave)
-out = realm.run_kadminl('getpol testpol', slave)
+check_ulog(0, 0, 0, [])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(0, 0, 0, [], slave1)
+out = realm.run_kadminl('getpol testpol', slave1)
+if 'Minimum number of password character classes: 2' not in out:
+ fail('slave1 does not have policy from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 0, [], slave2)
+out = realm.run_kadminl('getpol testpol', slave2)
if 'Minimum number of password character classes: 2' not in out:
- fail('Slave does not have policy from master')
+ fail('slave2 does not have policy from slave1')
# Modify the policy and test that it also propagates via full resync.
realm.run_kadminl('modpol -minlength 17 testpol')
-check_serial(realm, 'None')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, True)
-check_serial(realm, 'None', slave)
-out = realm.run_kadminl('getpol testpol', slave)
+check_ulog(0, 0, 0, [])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(0, 0, 0, [], slave1)
+out = realm.run_kadminl('getpol testpol', slave1)
+if 'Minimum password length: 17' not in out:
+ fail('slave1 does not have policy change from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 0, [], slave2)
+out = realm.run_kadminl('getpol testpol', slave2)
if 'Minimum password length: 17' not in out:
- fail('Slave does not have policy change from master')
+ fail('slave2 does not have policy change from slave1')
# Delete the policy and test that it propagates via full resync.
realm.run_kadminl('delpol -force testpol')
-check_serial(realm, 'None')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, True)
-check_serial(realm, 'None', slave)
-out = realm.run_kadminl('getpol testpol', slave)
+check_ulog(0, 0, 0, [])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(0, 0, 0, [], slave1)
+out = realm.run_kadminl('getpol testpol', slave1)
if 'Policy does not exist' not in out:
- fail('Slave did not get policy deletion from master')
+ fail('slave1 did not get policy deletion from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 0, [], slave2)
+out = realm.run_kadminl('getpol testpol', slave2)
+if 'Policy does not exist' not in out:
+ fail('slave2 did not get policy deletion from slave1')
+
+# Modify a principal on the master and test that it propagates via
+# full resync. (The master's ulog does not remember the timestamp it
+# had at serial number 0, so it does not know that an incremental
+# propagation is possible.)
+realm.run_kadminl('modprinc -maxlife "10 minutes" ' + pr1)
+check_ulog(1, 1, 1, [pr1])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(0, 0, 1, [], slave1)
+out = realm.run_kadminl('getprinc ' + pr1, slave1)
+if 'Maximum ticket life: 0 days 00:10:00' not in out:
+ fail('slave1 does not have modification from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 1, [], slave2)
+out = realm.run_kadminl('getprinc ' + pr1, slave2)
+if 'Maximum ticket life: 0 days 00:10:00' not in out:
+ fail('slave2 does not have modification from slave1')
+
+# Delete a principal and test that it propagates incrementally to
+# slave1. slave2 needs another full resync because slave1 no longer
+# has serial number 1 in its ulog after processing its first
+# incremental update.
+realm.run_kadminl('delprinc -force ' + pr3)
+check_ulog(2, 1, 2, [pr1, pr3])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, False)
+check_ulog(1, 2, 2, [pr3], slave1)
+out = realm.run_kadminl('getprinc ' + pr3, slave1)
+if 'Principal does not exist' not in out:
+ fail('slave1 does not have principal deletion from master')
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 2, [], slave2)
+out = realm.run_kadminl('getprinc ' + pr3, slave2)
+if 'Principal does not exist' not in out:
+ fail('slave2 does not have principal deletion from slave1')
-# Reset the ulog on the master side to force a full resync to all slaves.
-# XXX Note that we only have one slave in this test, so we can't really
-# test this.
+# Reset the ulog on the master to force a full resync.
realm.run([kproplog, '-R'])
-check_serial(realm, 'None')
-kpropd.send_signal(signal.SIGUSR1)
-wait_for_prop(kpropd, True)
-check_serial(realm, 'None', slave)
+check_ulog(0, 0, 0, [])
+kpropd1.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd1, True)
+check_ulog(0, 0, 0, [], slave1)
+kpropd2.send_signal(signal.SIGUSR1)
+wait_for_prop(kpropd2, True)
+check_ulog(0, 0, 0, [], slave2)
success('iprop tests')