#!/usr/bin/env python3 # -*- encoding: UTF8 -*- # test_SDP.py # Tests SDP (Server Deployment Package) on Linux VMs # Intended to be run from with a Vagrant VM. from __future__ import print_function import os, sys, re, socket, time, shutil, logging, time, pwd, stat import P4 import unittest import fileinput, subprocess LOGGER_NAME = 'SDPTest' mkdirs_file = '/depotdata/sdp/Server/Unix/setup/mkdirs.sh' MAILTO = 'mailto-admin@example.com' MAILFROM = 'mailfrom-admin@example.com' logger = logging.getLogger(LOGGER_NAME) class NotSudo(Exception): pass def get_host_ipaddress(): try: address = socket.gethostbyname(socket.gethostname()) # On my system, this always gives me 127.0.0.1. Hence... except: address = '' if not address or address.startswith('127.'): # ...the hard way. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('4.2.2.1', 0)) address = s.getsockname()[0] s.detach() logger.debug('IPAddress: %s' % address) return address def init_logging(): global logger logger.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s: %(message)s') fh = logging.FileHandler('/tmp/%s.log' % LOGGER_NAME, mode='w') fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) logger.addHandler(fh) def do_unlink(filename): "Unlink file if it exists" if os.path.lexists(filename): os.unlink(filename) def configure_p4_vars(): "Configure p4_vars" for line in fileinput.input('/p4/common/bin/p4_vars', inplace=True): line = line.rstrip() if line.startswith('export MAILTO='): print("export MAILTO=%s" % MAILTO) elif line.startswith('export MAILFROM='): print("export MAILFROM=%s" % MAILFROM) else: print(line) def configure_instance_vars(instance, port): "Configure instance vars" for line in fileinput.input('/p4/%s/config/instance_vars' % instance, inplace=True): line = line.rstrip() if line.startswith('export SSL_PREFIX=ssl:'): print("export SSL_PREFIX=") elif line.startswith('export P4MASTERPORTNUM='): print("export P4MASTERPORTNUM=%s" % port) else: print(line) class SDPTest_base(unittest.TestCase): "Generic test class for others to inherit" def setup_everything(self): if 'perforce' != pwd.getpwuid(os.getuid())[0]: raise Exception("This test harness should be run as user 'perforce'") try: result = subprocess.check_call("sudo ls > /dev/null", shell=True, timeout=20) except Exception as e: raise NotSudo("This test harness must be run as user perforce with sudo privileges or it will not work") def setUp(self): self.setup_everything() def run_test(self): pass def run_cmd(self, cmd, get_output=True, timeout=5, stop_on_error=True): "Run cmd logging input and output" output = "" try: logger.debug("Running: %s" % cmd) if get_output: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True, shell=True, timeout=timeout) logger.debug("Output:\n%s" % output) else: result = subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=True, timeout=timeout) logger.debug('Result: %d' % result) except Exception as e: if get_output: logger.debug("Failed Output: %s" % output) if stop_on_error: msg = 'Failed sudo_cmd: %s' % str(e) logger.debug(msg) self.fail(msg) return output def sudo_cmd(self, cmd, get_output=True, stop_on_error=True): "Run cmd with sudo" output = self.run_cmd("sudo %s" % cmd, get_output=get_output, stop_on_error=stop_on_error) return output def configure_mkdirs(self): "Configure mkdirs.sh with a couple of key variables" ipaddr = get_host_ipaddress() for line in fileinput.input(mkdirs_file, inplace=True): line = line.rstrip() if line.startswith('P4DNSNAME'): print("P4DNSNAME=%s" % ipaddr) elif line.startswith('P4ADMINPASS'): print("P4ADMINPASS=Password1") else: print(line) def run_mkdirs(self, instance): "Runs the mkdirs script" output = self.sudo_cmd("%s %s" % (mkdirs_file, instance)) valid_lines = ["Verified: Running as root.", "It is recommended that the perforce's umask be changed to 0026 to block world access to Perforce files.", "Add umask 0026 to perforce's .bash_profile to make this change."] for line in output.split('\n'): line = line.strip() if line and line not in valid_lines: self.fail('Unexpected line in mkdirs output:\n%s' % line) #--- Test Cases class configure_master(SDPTest_base): def check_dirs(self, rootdir, dirlist): "Checks specified directories are present" found_dirs = self.run_cmd("find %s -type d" % rootdir, stop_on_error=False).split() for d in [x.strip() for x in dirlist.split()]: self.assertIn(d, found_dirs) def p4service(self, cmd, instance, stop_on_error=True): "Start or stop service" self.run_cmd("/p4/%s/bin/p4d_%s_init %s" % (instance, instance, cmd), get_output=False, stop_on_error=stop_on_error) def remove_test_dirs(self, instances): "Remove all appropriate directories created" dirs_to_remove = "/depotdata/sdp /depotdata/p4 /metadata/p4 /logs/p4".split() for d in dirs_to_remove: if os.path.exists(d): self.sudo_cmd("rm -rf %s" % d) for instance in instances: for f in ["/p4/%s" % instance, "/p4/common"]: if os.path.lexists(f): self.sudo_cmd("unlink %s" % f) def liveCheckpointTest(self, instance): "Test live checkpoint script" self.assertFalse(os.path.exists('/p4/%s/offline_db/db.domain' % instance)) self.run_cmd('/p4/common/bin/p4master_run %s /p4/common/bin/live_checkpoint.sh' % instance) # Quick check on c=log file contents log_contents = self.readLog('checkpoint.log', instance) self.assertRegex(log_contents, "Checkpointing to /p4/%s/checkpoints/p4_%s.ckp" % (instance, instance)) self.assertRegex(log_contents, "Rotating /p4/%s/logs/journal" % instance) # Make sure offline db is present self.assertTrue(os.path.exists('/p4/%s/offline_db/db.domain' % instance)) def dailyBackupTest(self, instance): "Test daily backup script" jnl_counter = self.p4run('counter', 'journal')[0]['value'] self.run_cmd('/p4/common/bin/p4master_run %s /p4/common/bin/daily_backup.sh' % instance) # Quick check on c=log file contents log_contents = self.readLog('checkpoint.log', instance) self.assertRegex(log_contents, "Dumping to /p4/%s/checkpoints/p4_%s.ckp" % (instance, instance)) self.assertRegex(log_contents, "Rotating /p4/%s/logs/journal" % instance) new_jnl_counter = self.p4run('counter', 'journal')[0]['value'] self.assertEqual(int(new_jnl_counter), int(jnl_counter) + 1) def readLog(self, log_name, instance): "Read the appropriate log file contents" with open('/p4/%s/logs/%s' % (instance, log_name), 'r') as fh: log_contents = fh.read() return log_contents def verifyTest(self, instance): "Test verify script" verify_cmd = '/p4/common/bin/p4master_run %s /p4/common/bin/p4verify.sh' % instance self.run_cmd(verify_cmd) log_contents = self.readLog('p4verify.log', instance) for depot in ["depot", "specs"]: verify_ok = re.compile("verify -qz //%s/...\nexit: 0" % depot, re.MULTILINE) self.assertRegex(log_contents, verify_ok) # Streams depot doesn't have any files so gives an error - we just search for it self.assertRegex(log_contents, re.compile("verify -qz //streams/...\n[^\n]*\nexit: 0", re.MULTILINE)) self.assertRegex(log_contents, re.compile("verify -U -q //unload/...\n[^\n]*\nexit: 0", re.MULTILINE)) self.assertNotRegex(log_contents, "//archive") # Now create verify errors and make sure we see them orig_depot_name = '/p4/%s/depots/depot' % instance new_depot_name = orig_depot_name + '.new' os.rename(orig_depot_name, new_depot_name) self.run_cmd(verify_cmd, stop_on_error=False) log_contents = self.readLog('p4verify.log', instance) for depot in ["depot"]: verify_ok = re.compile("verify -qz //%s/...\nerror: [^\n]*MISSING!\nexit: 1" % depot, re.MULTILINE) self.assertRegex(log_contents, verify_ok) # Rename things back again and all should be well! os.rename(new_depot_name, orig_depot_name) self.run_cmd(verify_cmd, stop_on_error=True) log_contents = self.readLog('p4verify.log', instance) for depot in ["depot", "specs"]: verify_ok = re.compile("verify -qz //%s/...\nexit: 0" % depot, re.MULTILINE) self.assertRegex(log_contents, verify_ok) def weeklyBackupTest(self, instance): "Test weekly backup script" jnl_counter = self.p4run('counter', 'journal')[0]['value'] self.run_cmd('/p4/common/bin/p4master_run %s /p4/common/bin/weekly_backup.sh' % instance, timeout=35) # Quick check on c=log file contents log_contents = self.readLog('checkpoint.log', instance) self.assertRegex(log_contents, "Dumping to /p4/%s/checkpoints/p4_%s.ckp" % (instance, instance)) self.assertRegex(log_contents, "Rotating /p4/%s/logs/journal" % instance) self.p4.disconnect() # Need to reconnect as weekly has restarted p4d self.p4.connect() new_jnl_counter = self.p4run('counter', 'journal')[0]['value'] self.assertEqual(int(new_jnl_counter), int(jnl_counter) + 1) def configureServer(self, instance): "Set various configurables for master" configurables = """ security=3 defaultChangeType=restricted run.users.authorize=1 db.peeking=2 dm.user.noautocreate=2 dm.user.resetpassword=1 filesys.P4ROOT.min=1G filesys.depot.min=1G filesys.P4JOURNAL.min=1G p4 configure unset monitor server=3 net.tcpsize=256k lbr.bufsize=256k server.commandlimits=2 serverlog.retain.3=7 serverlog.retain.7=7 serverlog.retain.8=7""".split("\n") instance_configurables = """ journalPrefix=/p4/${INSTANCE}/checkpoints/p4_${INSTANCE} server.depot.root=/p4/${INSTANCE}/depots serverlog.file.3=/p4/${INSTANCE}/logs/errors.csv serverlog.file.7=/p4/${INSTANCE}/logs/events.csv serverlog.file.8=/p4/${INSTANCE}/logs/integrity.csv""".split("\n") for c in [x.strip() for x in configurables]: self.p4run("configure set %s" % c) for ic in instance_configurables: ic = ic.strip() ic.replace("${INSTANCE}", instance) self.p4run("configure set %s" % ic) def configureReplication(self): "Configures stuff required for replication" def p4run(self, *args): "Run the command logging" logger.debug('p4 cmd: %s' % ",".join([str(x) for x in args])) result = self.p4.run(args) logger.debug('result: %s' % str(result)) return result def resetTest(self, instances): for instance in instances: self.sudo_cmd("ps -ef | grep p4d_%s | awk '{print $2}' | xargs kill > /dev/null 2>&1" % instance, stop_on_error=False) self.remove_test_dirs(instances) self.sudo_cmd("cp -R /sdp /depotdata/sdp") self.sudo_cmd("sudo chown -R perforce:perforce /depotdata/sdp") for instance in instances: filename = "/p4/%s" %instance do_unlink(filename) do_unlink(filename.lower()) def configureInstance(self, instance, port): "Configure the master instance" # Stop the Perforce service if currently running from a previous run in case it is accessing dirs self.resetTest(instance) self.configure_mkdirs() self.run_mkdirs(instance) depotdata_dir_list = """ /depotdata/p4 /depotdata/p4/INSTANCE /depotdata/p4/INSTANCE/depots /depotdata/p4/INSTANCE/ssl /depotdata/p4/INSTANCE/bin /depotdata/p4/INSTANCE/tmp /depotdata/p4/INSTANCE/checkpoints /depotdata/p4/common /depotdata/p4/common/bin /depotdata/p4/common/bin/triggers /depotdata/p4/common/lib /depotdata/p4/common/etc /depotdata/p4/common/etc/init.d /depotdata/p4/common/etc/cron.d""".replace("INSTANCE", instance) logdata_dir_list = """ /logs /logs/p4 /logs/p4/INSTANCE /logs/p4/INSTANCE/logs""".replace("INSTANCE", instance) metadata_dir_list = """ /metadata /metadata/p4 /metadata/p4/INSTANCE /metadata/p4/INSTANCE/root /metadata/p4/INSTANCE/root/save /metadata/p4/INSTANCE/offline_db""".replace("INSTANCE", instance) self.check_dirs('/depotdata', depotdata_dir_list) self.check_dirs('/logs', logdata_dir_list) self.check_dirs('/metadata', metadata_dir_list) configure_instance_vars(instance, port) configure_p4_vars() def instanceTest(self, instance, port): # So now we want to start up the Perforce service self.p4service("start", instance) p4 = P4.P4() self.p4 = p4 p4.port = 'localhost:%s' % port p4.user = 'p4admin' p4.connect() # Create our user and set password user = p4.fetch_user('p4admin') p4.save_user(user) p4.run_password('', 'Password1') p4.password = 'Password1' p4.run_login() # Make him superuser prot = p4.fetch_protect() p4.save_protect(prot) # Things to setup # - create spec depot # - create a workspace and add at least one file # - configure the various tunables # - create server definitions - master and replica # - create service user for replica # - run daily_checkpoint - check for error # - run live_checkpoint - make sure offline_db seeded # - run daily checkpoint more than once - create change lists in between times # - run weekly_checkpoint - check result p4.run('configure', 'set', 'server.depot.root=/p4/%s/depots' % instance) p4.run('admin', 'restart') p4.disconnect() # New depot won't show up unless we do this time.sleep(1) p4.connect() if instance == 'Master': if not os.path.lexists("/p4/%s" % instance.lower()): self.run_cmd("ln -s /p4/%s /p4/%s" % (instance, instance.lower())) depot = p4.fetch_depot('specs') self.assertEqual(depot['Map'], 'specs/...') depot['Type'] = 'spec' p4.save_depot(depot) depot = p4.fetch_depot('unload') self.assertEqual(depot['Map'], 'unload/...') depot['Type'] = 'unload' p4.save_depot(depot) depot = p4.fetch_depot('archive') self.assertEqual(depot['Map'], 'archive/...') depot['Type'] = 'archive' p4.save_depot(depot) depot = p4.fetch_depot('streams') self.assertEqual(depot['Map'], 'streams/...') depot['Type'] = 'stream' p4.save_depot(depot) p4.disconnect() # New depot won't show up unless we do this p4.connect() depots = p4.run_depots() self.assertEqual(5, len(depots)) ws_name = 'test_ws' ws = p4.fetch_client(ws_name) ws['Root'] = '/tmp/test_ws' ws['View'] = ['//depot/main/... //%s/...' % ws_name] p4.save_client(ws) p4.client = ws_name if not os.path.exists(ws['Root']): os.mkdir(ws['Root']) fname = '/tmp/%s/file1' % ws_name if os.path.exists(fname): os.chmod(fname, stat.S_IWRITE) os.unlink(fname) with open(fname, 'w') as fh: fh.write('test data\n') p4.run_add(fname) chg = p4.fetch_change() chg['Description'] = 'Initial file' p4.save_submit(chg) changes = p4.run_changes() self.assertEqual(1, len(changes)) self.liveCheckpointTest(instance) self.dailyBackupTest(instance) self.dailyBackupTest(instance) # Manually rotate journals again and ensure daily backup handles that self.p4run('admin', 'journal', '/p4/%s/checkpoints/p4_%s' % (instance, instance)) self.dailyBackupTest(instance) self.verifyTest(instance) # Tests: # - totalusers.py # - the various _init scripts # print('\n\nAbout to run weekly backup which sleeps for 30 seconds, so be patient...!') self.weeklyBackupTest(instance) # print(p4.run_admin('stop')) def runTest(self): "Configure the master instance" instances = ["1", "Master"] self.resetTest(instances) self.configureInstance("1", "1667") self.instanceTest("1", "1667") self.resetTest(instances) self.configureInstance("Master", "2667") self.instanceTest("Master", "2667") if __name__ == "__main__": init_logging() unittest.main()
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#48 | 30388 | C. Thomas Tyler |
Released SDP 2024.1.30385 (2024/06/11). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#47 | 29443 | C. Thomas Tyler |
Released SDP 2022.2.29441 (2023/02/27). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#46 | 29401 | C. Thomas Tyler |
Released SDP 2022.2.29399 (2023/02/06). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#45 | 29252 | C. Thomas Tyler |
Released SDP 2022.2.29250 (2022/12/08). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#44 | 29205 | C. Thomas Tyler |
Released SDP 2022.1.29203 (2022/11/22). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#43 | 28858 | C. Thomas Tyler |
Released SDP 2022.1.28855 (2022/05/27). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#42 | 28651 | C. Thomas Tyler |
Released SDP 2021.2.28649 (2022/03/03). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#41 | 28240 | C. Thomas Tyler |
Released SDP 2021.1.28238 (2021/11/12). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#40 | 27761 | C. Thomas Tyler |
Released SDP 2020.1.27759 (2021/05/07). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#39 | 27331 | C. Thomas Tyler |
Released SDP 2020.1.27325 (2021/01/29). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#38 | 26480 | C. Thomas Tyler |
Released SDP 2019.3.26478 (2020/04/12). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#37 | 26470 | C. Thomas Tyler |
Released SDP 2019.3.26468 (2020/04/10). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#36 | 26403 | C. Thomas Tyler |
Released SDP 2019.3.26400 (2020/03/28). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#35 | 26161 | C. Thomas Tyler |
Released SDP 2019.3.26159 (2019/11/06). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#34 | 25596 | C. Thomas Tyler |
Released SDP 2019.2.25594 (2019/05/02). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#33 | 25483 | C. Thomas Tyler |
Released SDP 2019.1.25480 (2019/04/11). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#32 | 25279 | C. Thomas Tyler |
Released SDP 2019.1.25276 (2019/03/06). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#31 | 25245 | C. Thomas Tyler |
Released SDP 2019.1.25238 (2019/03/02). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#30 | 23331 | C. Thomas Tyler |
Released SDP 2017.4.23329 (2017/12/05). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#29 | 22685 | Russell C. Jackson (Rusty) | Update main with current changes from dev. | ||
#28 | 22185 | C. Thomas Tyler |
Released SDP 2017.2.22177 (2017/05/17). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#27 | 21338 | C. Thomas Tyler |
Released SDP 2016.2.21328 (2016/12/16). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#26 | 20974 | C. Thomas Tyler |
Released SDP 2016.2.20972 (2016/11/01). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#25 | 20481 | C. Thomas Tyler |
Released SDP 2016.1.20460. Copy Up using 'p4 copy -r -b perforce_software-sdp-dev', with selective removal of changes related to work-in-progress files. |
||
#24 | 19898 | C. Thomas Tyler |
Released SDP/MultiArch/2016.1/19888 (2016/07/07). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#23 | 19694 | C. Thomas Tyler |
Released SDP/MultiArch/2016.1/19661 (2016/06/08). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
#22 | 19414 | C. Thomas Tyler | Released SDP/MultiArch/2016.1/19410 (2016/05/17). | ||
#21 | 18961 | C. Thomas Tyler | Released: SDP/MultiArch/2016.1/18958 (2016/04/08). | ||
#20 | 18625 | Russell C. Jackson (Rusty) | Copy dev to main to pick up corrected test script. | ||
#19 | 18619 | Russell C. Jackson (Rusty) | Updating main with current changes. | ||
#18 | 16783 | Robert Cowham | Doc reference to test. | ||
#17 | 16555 | C. Thomas Tyler |
Updated assertions in the test suite due to changes in @16554 (moved 'tmp' to logs volume). |
||
#16 | 16372 | C. Thomas Tyler |
Follow on changes related to changing default admin user from 'p4admin' to 'perforce' in change in change 16370. //guest/perforce_software/sdp/main/Server/Unix/p4/common/bin/triggers/command_block.py # edit //guest/perforce_software/sdp/main/Server/Unix/p4/common/bin/triggers/keep_group_unset.py # edit //guest/perforce_software/sdp/main/Server/Unix/p4/common/lib/MinP4.pm # edit //guest/perforce_software/sdp/main/Server/Windows/setup/sdp_master_config.ini # edit //guest/perforce_software/sdp/main/Server/test/test_SDP.py # edit //guest/perforce_software/sdp/main/Server/test/test_SDPEnv.py # edit |
||
#15 | 16265 | Robert Cowham | Fix tests broken by previous few changes. | ||
#14 | 14045 | Robert Cowham |
Move some stuff from instance_vars to p4_vars (or at least for defaults). Clarify user messages. |
||
#13 | 13912 | C. Thomas Tyler | Pushing SDP 2015.1.13910. | ||
#12 | 12172 | Russell C. Jackson (Rusty) | Removed p4master_run | ||
#11 | 12115 | Russell C. Jackson (Rusty) | Dropped /p4/ssl | ||
#10 | 12114 | Russell C. Jackson (Rusty) | Updated location of ssl directory. | ||
#9 | 11923 | Russell C. Jackson (Rusty) | Correct the name/location of the instance vars file. | ||
#8 | 11878 | Russell C. Jackson (Rusty) |
Corrected crontab name to p4.crontab in output when crontab's exist. Corrected test to remove p4.crontab and p4.crontab.replica - All Reb's fault. |
||
#7 | 11830 | Russell C. Jackson (Rusty) | Updated to set MASTERINSTANCE to instance. | ||
#6 | 11732 | Robert Cowham | Fix tests to remove crontab entries when resetting | ||
#5 | 11707 | Robert Cowham |
Refactored sending of mail to a common function. Make the setting of "MAILFROM" work for Ubuntu (GNU Mailutils) as well as CentOS |
||
#4 | 11545 | Robert Cowham | Fix failure to create link under ubuntu | ||
#3 | 11544 | Robert Cowham | Tests now work with Rusty's new version | ||
#2 | 11539 | Robert Cowham | Make tests run with alphanumeric instance names (even if with old version of SDP). | ||
#1 | 10148 | C. Thomas Tyler | Promoted the Perforce Server Deployment Package to The Workshop. |