From 6f028ace004e26873439cf86efc4c6c41c31c450 Mon Sep 17 00:00:00 2001 From: James Foucar Date: Tue, 24 Jan 2017 13:54:09 -0700 Subject: [PATCH] Lock env_mach.xml file If user changes walltime or queue, this will not affect the batch directives in case.run or case.test unless the user cleans and re-runs case.setup. We've gotten complaints from users that their WALLTIME changes are not taking affect. This branch changes that by locking the env_batch.xml file at case.setup time. check_lockedfiles will then fail if the user tries to change WALLTIME without re-running case.setup. This branch also does some work to encapsulate the lockfile concept better inside check_lockedfiles.py. --- scripts/create_newcase | 7 +- utils/python/CIME/SystemTests/erp.py | 30 +++---- utils/python/CIME/SystemTests/ncr.py | 29 +++---- utils/python/CIME/SystemTests/seq.py | 20 ++--- .../CIME/SystemTests/system_tests_common.py | 20 ++--- utils/python/CIME/build.py | 13 +-- utils/python/CIME/case.py | 11 +-- utils/python/CIME/case_setup.py | 44 +++-------- utils/python/CIME/check_lockedfiles.py | 79 ++++++++++++++++++- utils/python/CIME/test_scheduler.py | 7 +- 10 files changed, 145 insertions(+), 115 deletions(-) diff --git a/scripts/create_newcase b/scripts/create_newcase index 3fa7f8035f1..8c0d9494a3f 100755 --- a/scripts/create_newcase +++ b/scripts/create_newcase @@ -1,9 +1,9 @@ #!/usr/bin/env python from Tools.standard_script_setup import * -from shutil import copyfile from CIME.utils import expect, get_model from CIME.case import Case +from CIME.check_lockedfiles import lock_file logger = logging.getLogger(__name__) @@ -191,9 +191,8 @@ def _main_func(description): user_mods_dir = os.path.abspath(user_mods_dir) case.apply_user_mods(user_mods_dir) - # Copy env_case.xml into LockedFiles - copyfile(os.path.join(caseroot,"env_case.xml"), - os.path.join(caseroot,"LockedFiles","env_case.xml")) + # Lock env_case.xml + lock_file("env_case.xml", caseroot) ############################################################################### diff --git a/utils/python/CIME/SystemTests/erp.py b/utils/python/CIME/SystemTests/erp.py index fdc556f6dff..00fa0ec279c 100644 --- a/utils/python/CIME/SystemTests/erp.py +++ b/utils/python/CIME/SystemTests/erp.py @@ -13,6 +13,7 @@ from CIME.case_setup import case_setup import CIME.utils from CIME.SystemTests.system_tests_common import SystemTestsCommon +from CIME.check_lockedfiles import * logger = logging.getLogger(__name__) @@ -40,15 +41,20 @@ def build_phase(self, sharedlib_only=False, model_only=False): # env_build.xml in LockedFiles if they are not there. If there # are already copies there then simply copy them back to # have the starting env_mach_pes.xml and env_build.xml - machpes1 = os.path.join("LockedFiles","env_mach_pes.ERP1.xml") - envbuild1 = os.path.join("LockedFiles","env_build.ERP1.xml") + machpes1 = "env_mach_pes.ERP1.xml" + envbuild1 = "env_build.ERP1.xml" + if is_locked(machpes1): + restore(machpes1, newname="env_mach_pes.xml") + else: + lock_file("env_mach_pes.xml", newname=machpes1) + if ( os.path.isfile(machpes1) ): shutil.copy(machpes1,"env_mach_pes.xml") else: shutil.copy("env_mach_pes.xml","env_mach_pes.ERP1.xml") - if ( os.path.isfile(envbuild1) ): - shutil.copy(envbuild1,"env_build.xml") + if is_locked(envbuild1): + restore(envbuild1, newname="env_build.xml") # Build two executables, one using the original tasks and threads (ERP1) and # one using the modified tasks and threads (ERP2) @@ -86,23 +92,19 @@ def build_phase(self, sharedlib_only=False, model_only=False): # Make copies of the new env_mach_pes.xml and the new # env_build.xml to be used in the run phase - shutil.copy("env_mach_pes.xml", os.path.join("LockedFiles","env_mach_pes.ERP%s.xml"%bld )) - shutil.copy("env_build.xml", os.path.join("LockedFiles","env_build.ERP%s.xml"%bld )) - # - # + lock_file("env_mach_pes.xml", newname="env_mach_pes.ERP%s.xml" % bld) + lock_file("env_build.xml", newname="env_build.ERP%s.xml" % bld) def run_phase(self): # run will have values 1,2 for run in range(1,3): - expect(os.path.isfile(os.path.join("LockedFiles","env_mach_pes.ERP%d.xml"%run)), - "ERROR: LockedFiles/env_mach_pes.ERP%d.xml does not exist, run case.build"%run ) + expect(is_locked("env_mach_pes.ERP%d.xml" % run), + "ERROR: LockedFiles/env_mach_pes.ERP%d.xml does not exist, run case.build" % run ) # Use the second env_mach_pes.xml and env_build.xml files - shutil.copy(os.path.join("LockedFiles","env_mach_pes.ERP%d.xml"%run), "env_mach_pes.xml") - shutil.copy("env_mach_pes.xml", os.path.join("LockedFiles","env_mach_pes.xml")) - shutil.copy(os.path.join("LockedFiles","env_build.ERP%d.xml")%run, "env_build.xml") - shutil.copy("env_build.xml", os.path.join("LockedFiles","env_build.xml")) + restore("env_mach_pes.ERP%d.xml" % run, newname="env_mach_pes.xml") + restore("env_build.ERP%d.xml" % run, newname="env_build.xml") # update the case to use the new values self._case.read_xml() diff --git a/utils/python/CIME/SystemTests/ncr.py b/utils/python/CIME/SystemTests/ncr.py index 6c1e65a2f9b..abd3ac460cb 100644 --- a/utils/python/CIME/SystemTests/ncr.py +++ b/utils/python/CIME/SystemTests/ncr.py @@ -10,6 +10,7 @@ from CIME.case_setup import case_setup import CIME.utils from CIME.SystemTests.system_tests_common import SystemTestsCommon +from CIME.check_lockedfiles import * logger = logging.getLogger(__name__) @@ -25,16 +26,16 @@ def build_phase(self, sharedlib_only=False, model_only=False): exeroot = self._case.get_value("EXEROOT") cime_model = CIME.utils.get_model() - machpes1 = os.path.join("LockedFiles","env_mach_pes.NCR1.xml") - if ( os.path.isfile(machpes1) ): - shutil.copy(machpes1,"env_mach_pes.xml") + machpes1 = "env_mach_pes.NCR1.xml" + if is_locked(machpes1): + restore(machpes1, newname="env_mach_pes.xml") # Build two exectuables for this test, the first is a default build, the # second halves the number of tasks and runs two instances for each component # Lay all of the components out concurrently for bld in range(1,3): logging.warn("Starting bld %s"%bld) - machpes = os.path.join("LockedFiles","env_mach_pes.NCR%s.xml"%bld) + machpes = "env_mach_pes.NCR%s.xml" % bld ntasks_sum = 0 for comp in ['ATM','OCN','WAV','GLC','ICE','ROF','LND']: self._case.set_value("NINST_%s"%comp,str(bld)) @@ -54,14 +55,12 @@ def build_phase(self, sharedlib_only=False, model_only=False): self.build_indv(sharedlib_only, model_only) shutil.move("%s/%s.exe"%(exeroot,cime_model), "%s/%s.exe.NCR%s"%(exeroot,cime_model,bld)) - shutil.copy("env_build.xml",os.path.join("LockedFiles","env_build.NCR%s.xml"%bld)) - shutil.copy("env_mach_pes.xml", machpes) + lock_file("env_build.xml", newname="env_build.NCR%s.xml" % bld) + lock_file("env_mach_pes.xml", newname=machpes) # Because mira/cetus interprets its run script differently than # other systems we need to copy the original env_mach_pes.xml back - shutil.copy(machpes1,"env_mach_pes.xml") - shutil.copy("env_mach_pes.xml", - os.path.join("LockedFiles","env_mach_pes.xml")) + restore(machpes1, newname="env_mach_pes.xml") def run_phase(self): os.chdir(self._caseroot) @@ -70,16 +69,15 @@ def run_phase(self): cime_model = CIME.utils.get_model() # Reset beginning test settings - expect(os.path.exists("LockedFiles/env_mach_pes.NCR1.xml"), + expect(is_locked("env_mach_pes.NCR1.xml"), "ERROR: LockedFiles/env_mach_pes.NCR1.xml does not exist\n" " this would been produced in the build - must run case.test_build") - shutil.copy("LockedFiles/env_mach_pes.NCR1.xml", "env_mach_pes.xml") - shutil.copy("env_mach_pes.xml", "LockedFiles/env_mach_pes.xml") + restore("env_mach_pes.NCR1.xml", newname="env_mach_pes.xml") + restore("env_build.NCR1.xml", newname="env_build.xml") shutil.copy("%s/%s.exe.NCR1" % (exeroot, cime_model), "%s/%s.exe" % (exeroot, cime_model)) - shutil.copy("LockedFiles/env_build.NCR1.xml", "env_build.xml") - shutil.copy("env_build.xml", "LockedFiles/env_build.xml") + stop_n = self._case.get_value("STOP_N") stop_option = self._case.get_value("STOP_OPTION") @@ -104,8 +102,7 @@ def run_phase(self): os.remove("%s/%s.exe" % (exeroot, cime_model)) shutil.copy("%s/%s.exe.NCR2" % (exeroot, cime_model), "%s/%s.exe" % (exeroot, cime_model)) - shutil.copy("LockedFiles/env_build.NCR2.xml", "env_build.xml") - shutil.copy("env_build.xml", "LockedFiles/env_build.xml") + restore("env_build.NCR2.xml", "env_build.xml") logger.info("default: doing a %s %s with NINST2" % (stop_n, stop_option)) self.run_indv(suffix="multiinst") diff --git a/utils/python/CIME/SystemTests/seq.py b/utils/python/CIME/SystemTests/seq.py index 289d40b0f65..579b92ad471 100644 --- a/utils/python/CIME/SystemTests/seq.py +++ b/utils/python/CIME/SystemTests/seq.py @@ -4,6 +4,7 @@ from CIME.XML.standard_module_setup import * from CIME.SystemTests.system_tests_common import SystemTestsCommon from CIME.case_setup import case_setup +from CIME.check_lockedfiles import * import shutil logger = logging.getLogger(__name__) @@ -31,12 +32,11 @@ def build_phase(self, sharedlib_only=False, model_only=False): shutil.move("%s/%s.exe"%(exeroot,cime_model), "%s/%s.exe.SEQ1"%(exeroot,cime_model)) any_changes = False - machpes1 = os.path.join("LockedFiles","env_mach_pes.SEQ1.xml") - if ( os.path.isfile(machpes1) ): - shutil.copy(machpes1,"env_mach_pes.xml") + machpes1 = "env_mach_pes.SEQ1.xml" + if is_locked(machpes1): + restore(machpes1, newname="env_mach_pes.xml") else: - logging.info("Copying env_mach_pes.xml to %s"%(machpes1)) - shutil.copy("env_mach_pes.xml", machpes1) + lock_file("env_mach_pes.xml", newname=machpes1) comp_classes = self._case.get_values("COMP_CLASSES") for comp in comp_classes: @@ -63,9 +63,7 @@ def build_phase(self, sharedlib_only=False, model_only=False): self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only) shutil.move("%s/%s.exe"%(exeroot,cime_model), "%s/%s.exe.SEQ2"%(exeroot,cime_model)) - machpes2 = os.path.join("LockedFiles","env_mach_pes.SEQ2.xml") - logging.info("Copying env_mach_pes.xml to %s"%(machpes2)) - shutil.copy("env_mach_pes.xml", machpes2) + lock_file("env_mach_pes.xml", newname="env_mach_pes.SEQ2.xml") def run_phase(self): # Move to config_tests.xml once that's ready. @@ -87,12 +85,10 @@ def run_phase(self): shutil.copy("%s/%s.exe.SEQ1"%(exeroot,cime_model), "%s/%s.exe"%(exeroot,cime_model)) - shutil.copy(os.path.join("LockedFiles", "env_mach_pes.SEQ1.xml"), "env_mach_pes.xml") - shutil.copy("env_mach_pes.xml", os.path.join("LockedFiles", "env_mach_pes.xml")) + restore("env_mach_pes.SEQ1.xml", newname="env_mach_pes.xml") self.run_indv() - shutil.copy(os.path.join("LockedFiles", "env_mach_pes.SEQ2.xml"), "env_mach_pes.xml") - shutil.copy("env_mach_pes.xml", os.path.join("LockedFiles", "env_mach_pes.xml")) + restore("env_mach_pes.SEQ2.xml", newname="env_mach_pes.xml") os.remove("%s/%s.exe"%(exeroot,cime_model)) shutil.copy("%s/%s.exe.SEQ1"%(exeroot,cime_model), diff --git a/utils/python/CIME/SystemTests/system_tests_common.py b/utils/python/CIME/SystemTests/system_tests_common.py index 10189f5cd4e..fa32cdca7d2 100644 --- a/utils/python/CIME/SystemTests/system_tests_common.py +++ b/utils/python/CIME/SystemTests/system_tests_common.py @@ -8,6 +8,7 @@ from CIME.case_run import case_run from CIME.case_st_archive import case_st_archive from CIME.test_status import * +from CIME.check_lockedfiles import * from CIME.hist_utils import * import CIME.build as build @@ -20,7 +21,7 @@ class SystemTestsCommon(object): def __init__(self, case, expected=None): """ - initialize a CIME system test object, if the file LockedFiles/env_run.orig.xml + initialize a CIME system test object, if the locked env_run.orig.xml does not exist copy the current env_run.xml file. If it does exist restore values changed in a previous run of the test. """ @@ -43,20 +44,14 @@ def _init_environment(self, caseroot): def _init_locked_files(self, caseroot, expected): """ - If the file LockedFiles/env_run.orig.xml does not exist, copy the current + If the locked env_run.orig.xml does not exist, copy the current env_run.xml file. If it does exist, restore values changed in a previous run of the test. """ - if os.path.isfile(os.path.join(caseroot, "LockedFiles", "env_run.orig.xml")): + if is_locked("env_run.orig.xml"): self.compare_env_run(expected=expected) elif os.path.isfile(os.path.join(caseroot, "env_run.xml")): - lockedfiles = os.path.join(caseroot, "LockedFiles") - try: - os.stat(lockedfiles) - except: - os.mkdir(lockedfiles) - shutil.copy(os.path.join(caseroot,"env_run.xml"), - os.path.join(lockedfiles, "env_run.orig.xml")) + lock_file("env_run.xml", caseroot=caseroot, newname="env_run.orig.xml") def _resetup_case(self, phase): """ @@ -307,12 +302,13 @@ def _check_for_memleak(self): self._test_status.set_status(MEMLEAK_PHASE, TEST_FAIL_STATUS, comments=comment) def compare_env_run(self, expected=None): + # JGF implement in check_lockedfiles? f1obj = EnvRun(self._caseroot, "env_run.xml") - f2obj = EnvRun(self._caseroot, os.path.join("LockedFiles", "env_run.orig.xml")) + f2obj = EnvRun(self._caseroot, os.path.join(LOCKED_DIR, "env_run.orig.xml")) diffs = f1obj.compare_xml(f2obj) for key in diffs.keys(): if expected is not None and key in expected: - logging.warn(" Resetting %s for test"%key) + logging.warn(" Resetting %s for test" % key) f1obj.set_value(key, f2obj.get_value(key, resolved=False)) else: print "Found difference in %s: case: %s original value %s" %\ diff --git a/utils/python/CIME/build.py b/utils/python/CIME/build.py index 4abd8311a6e..de58396843a 100644 --- a/utils/python/CIME/build.py +++ b/utils/python/CIME/build.py @@ -5,6 +5,7 @@ from CIME.utils import get_model, append_status, analyze_build_log from CIME.provenance import save_build_provenance from CIME.preview_namelists import create_namelists +from CIME.check_lockedfiles import check_lockedfiles, lock_file, unlock_file import glob, shutil, time, threading, gzip, subprocess logger = logging.getLogger(__name__) @@ -18,7 +19,6 @@ def stringify_bool(val): def build_model(build_threaded, exeroot, clm_config_opts, incroot, complist, lid, caseroot, cimeroot, compiler): ############################################################################### - logs = [] thread_bad_results = [] @@ -137,10 +137,7 @@ def post_build(case, logs): case.set_value("SMP_BUILD", os.environ["SMP_VALUE"]) case.flush() - if os.path.exists("LockedFiles/env_build.xml"): - os.remove("LockedFiles/env_build.xml") - - shutil.copy("env_build.xml", "LockedFiles") + lock_file("env_build.xml") # must ensure there's an lid lid = os.environ["LID"] if "LID" in os.environ else run_cmd_no_fail("date +%y%m%d-%H%M%S") @@ -168,7 +165,7 @@ def case_build(caseroot, case, sharedlib_only=False, model_only=False): comp_classes = case.get_values("COMP_CLASSES") - run_cmd_no_fail("./Tools/check_lockedfiles --caseroot %s" % caseroot) + check_lockedfiles(caseroot) # Retrieve relevant case data # This environment variable gets set for cesm Make and @@ -542,9 +539,7 @@ def clean(case, cleanlist=None): run_cmd_no_fail(cmd) # unlink Locked files directory - locked_env_build = os.path.join(caseroot,"LockedFiles/env_build.xml") - if os.path.isfile(locked_env_build): - os.unlink(locked_env_build) + unlock_file("env_build.xml") # reset following values in xml files case.set_value("SMP_BUILD",str(0)) diff --git a/utils/python/CIME/case.py b/utils/python/CIME/case.py index d10af07645b..865834c1412 100644 --- a/utils/python/CIME/case.py +++ b/utils/python/CIME/case.py @@ -11,6 +11,7 @@ from CIME.utils import expect, get_cime_root, append_status from CIME.utils import convert_to_type, get_model, get_project from CIME.utils import get_build_threaded, get_current_commit +from CIME.check_lockedfiles import LOCKED_DIR, lock_file from CIME.XML.machines import Machines from CIME.XML.pes import Pes from CIME.XML.files import Files @@ -907,13 +908,13 @@ def create_caseroot(self, clone=False): # Create relevant directories in $self._caseroot if clone: - newdirs = ("LockedFiles", "Tools") + newdirs = (LOCKED_DIR, "Tools") else: - newdirs = ("SourceMods", "LockedFiles", "Buildconf", "Tools") + newdirs = ("SourceMods", LOCKED_DIR, "Buildconf", "Tools") for newdir in newdirs: os.makedirs(newdir) - # Open a new README.case file in $self._caseroot + # Open a new README.case file in $self._caseroot append_status(" ".join(sys.argv), caseroot=self._caseroot, sfile="README.case") append_status("Compset longname is %s"%self.get_value("COMPSET"), caseroot=self._caseroot, sfile="README.case") @@ -1022,8 +1023,8 @@ def create_clone(self, newcase, keepexe=False, mach_dir=None, project=None, cime for casesub in ("SourceMods", "Buildconf"): shutil.copytree(os.path.join(cloneroot, casesub), os.path.join(newcaseroot, casesub)) - # copy env_case.xml to LockedFiles - shutil.copy(os.path.join(newcaseroot,"env_case.xml"), os.path.join(newcaseroot,"LockedFiles")) + # lock env_case.xml in new case + lock_file("env_case.xml", newcaseroot) # Update README.case fclone = open(cloneroot + "/README.case", "r") diff --git a/utils/python/CIME/case_setup.py b/utils/python/CIME/case_setup.py index 3714e76110a..7bf6d122bb5 100644 --- a/utils/python/CIME/case_setup.py +++ b/utils/python/CIME/case_setup.py @@ -4,7 +4,7 @@ from CIME.XML.standard_module_setup import * -from CIME.check_lockedfiles import check_lockedfiles +from CIME.check_lockedfiles import * from CIME.preview_namelists import create_dirs, create_namelists from CIME.XML.env_mach_pes import EnvMachPes from CIME.XML.machines import Machines @@ -16,36 +16,6 @@ logger = logging.getLogger(__name__) -############################################################################### -def _check_pelayouts_require_rebuild(case, models): -############################################################################### - """ - Create if we require a rebuild, expects cwd is caseroot - """ - locked_pes = "LockedFiles/env_mach_pes.xml" - if os.path.exists(locked_pes): - # Look to see if $comp_PE_CHANGE_REQUIRES_REBUILD is defined - # for any component - env_mach_pes_locked = EnvMachPes(infile=locked_pes, components=case.get_values("COMP_CLASSES")) - for comp in models: - if case.get_value("%s_PE_CHANGE_REQUIRES_REBUILD" % comp): - # Changing these values in env_mach_pes.xml will force - # you to clean the corresponding component - old_tasks = env_mach_pes_locked.get_value("NTASKS_%s" % comp) - old_threads = env_mach_pes_locked.get_value("NTHRDS_%s" % comp) - old_inst = env_mach_pes_locked.get_value("NINST_%s" % comp) - - new_tasks = case.get_value("NTASKS_%s" % comp) - new_threads = case.get_value("NTHRDS_%s" % comp) - new_inst = case.get_value("NINST_%s" % comp) - - if old_tasks != new_tasks or old_threads != new_threads or old_inst != new_inst: - logger.warn("%s pe change requires clean build %s %s" % (comp, old_tasks, new_tasks)) - cleanflag = comp.lower() - run_cmd_no_fail("./case.build --clean %s" % cleanflag) - - os.remove(locked_pes) - ############################################################################### def _build_usernl_files(case, model, comp): ############################################################################### @@ -128,6 +98,8 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): os.remove("case.testdriver") logger.info("Successfully cleaned test script case.testdriver") + unlock_file("env_batch.xml") + logger.info("Successfully cleaned batch script case.run") msg = "case.setup clean complete" @@ -171,10 +143,9 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): if os.path.exists("case.run"): logger.info("Machine/Decomp/Pes configuration has already been done ...skipping") else: - _check_pelayouts_require_rebuild(case, models) + check_pelayouts_require_rebuild(case, models) - if os.path.exists("LockedFiles/env_build.xml"): - os.remove("LockedFiles/env_build.xml") + unlock_file("env_build.xml") case.flush() check_lockedfiles() @@ -208,6 +179,9 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): logger.info("Writing %s script from input template %s" % (job, input_batch_script)) env_batch.make_batch_script(input_batch_script, job, case, pestot, tasks_per_node, num_nodes, thread_count) + # Lock env_batch + lock_file("env_batch.xml") + # Make sure pio settings are consistant for comp in models: pio_stride = case.get_value("PIO_STRIDE_%s"%comp) @@ -225,7 +199,7 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): logger.info("Locking file env_mach_pes.xml") case.flush() logger.debug("at copy TOTALPES = %s"%case.get_value("TOTALPES")) - shutil.copy("env_mach_pes.xml", "LockedFiles") + lock_file("env_mach_pes.xml") # Create user_nl files for the required number of instances if not os.path.exists("user_nl_cpl"): diff --git a/utils/python/CIME/check_lockedfiles.py b/utils/python/CIME/check_lockedfiles.py index ce8898ad144..70827798938 100644 --- a/utils/python/CIME/check_lockedfiles.py +++ b/utils/python/CIME/check_lockedfiles.py @@ -6,8 +6,73 @@ from CIME.XML.env_build import EnvBuild from CIME.XML.env_case import EnvCase from CIME.XML.env_mach_pes import EnvMachPes +from CIME.XML.env_batch import EnvBatch +from CIME.utils import run_cmd_no_fail -import glob +import glob, shutil + +LOCKED_DIR = "LockedFiles" + +def lock_file(filename, caseroot=None, newname=None): + expect("/" not in filename, "Please just provide basename of locked file") + caseroot = os.getcwd() if caseroot is None else caseroot + newname = filename if newname is None else newname + fulllockdir = os.path.join(caseroot, LOCKED_DIR) + if not os.path.exists(fulllockdir): + os.mkdir(fulllockdir) + shutil.copyfile(os.path.join(caseroot, filename), os.path.join(fulllockdir, newname)) + +def unlock_file(filename, caseroot=None): + expect("/" not in filename, "Please just provide basename of locked file") + caseroot = os.getcwd() if caseroot is None else caseroot + locked_path = os.path.join(caseroot, LOCKED_DIR, filename) + if os.path.exists(locked_path): + os.remove(locked_path) + +def is_locked(filename, caseroot=None): + expect("/" not in filename, "Please just provide basename of locked file") + caseroot = os.getcwd() if caseroot is None else caseroot + return os.path.exists(os.path.join(caseroot, LOCKED_DIR, filename)) + +def restore(filename, caseroot=None, newname=None): + """ + Restore the locked version of filename into main case dir + """ + expect("/" not in filename, "Please just provide basename of locked file") + caseroot = os.getcwd() if caseroot is None else caseroot + newname = filename if newname is None else newname + shutil.copyfile(os.path.join(caseroot, LOCKED_DIR, newname), os.path.join(caseroot, filename)) + # relock the restored file if names diffs + if newname != filename: + lock_file(newname, caseroot) + +def check_pelayouts_require_rebuild(case, models): + """ + Create if we require a rebuild, expects cwd is caseroot + """ + locked_pes = os.path.join(LOCKED_DIR, "env_mach_pes.xml") + if os.path.exists(locked_pes): + # Look to see if $comp_PE_CHANGE_REQUIRES_REBUILD is defined + # for any component + env_mach_pes_locked = EnvMachPes(infile=locked_pes, components=case.get_values("COMP_CLASSES")) + for comp in models: + if case.get_value("%s_PE_CHANGE_REQUIRES_REBUILD" % comp): + # Changing these values in env_mach_pes.xml will force + # you to clean the corresponding component + old_tasks = env_mach_pes_locked.get_value("NTASKS_%s" % comp) + old_threads = env_mach_pes_locked.get_value("NTHRDS_%s" % comp) + old_inst = env_mach_pes_locked.get_value("NINST_%s" % comp) + + new_tasks = case.get_value("NTASKS_%s" % comp) + new_threads = case.get_value("NTHRDS_%s" % comp) + new_inst = case.get_value("NINST_%s" % comp) + + if old_tasks != new_tasks or old_threads != new_threads or old_inst != new_inst: + logging.warn("%s pe change requires clean build %s %s" % (comp, old_tasks, new_tasks)) + cleanflag = comp.lower() + run_cmd_no_fail("./case.build --clean %s" % cleanflag) + + unlock_file("env_mach_pes.xml", case.get_value("CASEROOT")) def check_lockedfiles(caseroot=None): """ @@ -32,6 +97,12 @@ def check_lockedfiles(caseroot=None): elif objname == "env_case": f1obj = EnvCase(caseroot, cfile) f2obj = EnvCase(caseroot, lfile) + elif objname == "env_batch": + f1obj = EnvBatch(caseroot, cfile) + f2obj = EnvBatch(caseroot, lfile) + else: + logging.warn("Locked XML file '%s' is not current being handled" % fpart) + continue diffs = f1obj.compare_xml(f2obj) if diffs: @@ -39,6 +110,7 @@ def check_lockedfiles(caseroot=None): for key in diffs.keys(): print(" found difference in %s : case %s locked %s" % (key, repr(diffs[key][0]), repr(diffs[key][1]))) + if objname == "env_mach_pes": expect(False, "Invoke case.setup --reset ") elif objname == "env_case": @@ -55,6 +127,7 @@ def check_lockedfiles(caseroot=None): else: f1obj.set_value("BUILD_STATUS", 1) f1obj.write() + elif objname == "env_batch": + expect(False, "Batch configuration has changed, please run case.setup --reset") else: - # JGF : Error? - pass + expect(False, "'%s' diff was not handled" % objname) diff --git a/utils/python/CIME/test_scheduler.py b/utils/python/CIME/test_scheduler.py index 3a42876d1f5..6edcc4e1f07 100644 --- a/utils/python/CIME/test_scheduler.py +++ b/utils/python/CIME/test_scheduler.py @@ -21,6 +21,7 @@ from CIME.XML.tests import Tests from CIME.case import Case from CIME.wait_for_tests import wait_for_tests +from CIME.check_lockedfiles import lock_file import CIME.test_utils logger = logging.getLogger(__name__) @@ -495,11 +496,7 @@ def _xml_phase(self, test): expect(False, "Could not parse option '%s' " %opt) envtest.write() - lockedfiles = os.path.join(test_dir, "LockedFiles") - if not os.path.exists(lockedfiles): - os.mkdir(lockedfiles) - shutil.copy(os.path.join(test_dir,"env_run.xml"), - os.path.join(lockedfiles, "env_run.orig.xml")) + lock_file("env_run.xml", caseroot=test_dir, newname="env_run.orig.xml") with Case(test_dir, read_only=False) as case: if self._output_root is None: