Repository: ESMCI/cime

Issue:
Throw a sensible error message when submitting from the wrong machine.
I think this is the right place for this! (@mvertens is this the right place?)
This may just be me, but I am often moving back and forth between cheyenne and casper for example, and occasionally end up submitting a job I had built on cheyenne on casper which throws a traceback of course, and puzzles me for a while. Could we have a sensible exit that says straight up that I am not submitting on the machine/architecture I had originally built it on. Trivial I know, but hey.


Discussion:

Resolving patch:
diff --git a/scripts/lib/CIME/case/case.py b/scripts/lib/CIME/case/case.py
--- a/scripts/lib/CIME/case/case.py
+++ b/scripts/lib/CIME/case/case.py
@@ -97,7 +97,7 @@ class Case(object):
         check_input_data,
     )
 
-    def __init__(self, case_root=None, read_only=True, record=False):
+    def __init__(self, case_root=None, read_only=True, record=False, non_local=False):
 
         if case_root is None:
             case_root = os.getcwd()
@@ -119,7 +119,7 @@ def __init__(self, case_root=None, read_only=True, record=False):
         self._env_generic_files = []
         self._files = []
         self._comp_interface = None
-
+        self._non_local = non_local
         self.read_xml()
 
         if record:
@@ -162,6 +162,15 @@ def __init__(self, case_root=None, read_only=True, record=False):
 
         # check if case has been configured and if so initialize derived
         if self.get_value("CASEROOT") is not None:
+            if not self._non_local:
+                mach = self.get_value("MACH")
+                machobj = Machines()
+                probed_machine = machobj.probe_machine_name()
+                if probed_machine:
+                    expect(
+                        mach == probed_machine,
+                        f"Current machine {probed_machine} does not match case machine {mach}.",
+                    )
             self.initialize_derived_attributes()
 
     def check_if_comp_var(self, vid):


Test patch:
diff --git a/scripts/lib/CIME/SystemTests/system_tests_common.py b/scripts/lib/CIME/SystemTests/system_tests_common.py
--- a/scripts/lib/CIME/SystemTests/system_tests_common.py
+++ b/scripts/lib/CIME/SystemTests/system_tests_common.py
@@ -783,7 +783,7 @@ class FakeTest(SystemTestsCommon):
     Inheriters of the FakeTest Class are intended to test the code.
 
     All members of the FakeTest Class must
-    have names beginnig with "TEST" this is so that the find_system_test
+    have names beginning with "TEST" this is so that the find_system_test
     in utils.py will work with these classes.
     """
 
@@ -791,6 +791,7 @@ def __init__(self, case, expected=None):
         super(FakeTest, self).__init__(case, expected=expected)
         self._script = None
         self._requires_exe = False
+        self._case._non_local = True
         self._original_exe = self._case.get_value("run_exe")
 
     def _set_script(self, script, requires_exe=False):
diff --git a/scripts/lib/CIME/bless_test_results.py b/scripts/lib/CIME/bless_test_results.py
--- a/scripts/lib/CIME/bless_test_results.py
+++ b/scripts/lib/CIME/bless_test_results.py
@@ -49,7 +49,7 @@ def bless_namelists(
             create_test_gen_args += " -t {}".format(new_test_id)
 
         stat, out, _ = run_cmd(
-            "{}/create_test {} -n {} --baseline-root {} -o".format(
+            "{}/create_test {} --namelists-only {} --baseline-root {} -o".format(
                 get_scripts_root(), test_name, create_test_gen_args, baseline_root
             ),
             combine_output=True,
diff --git a/scripts/lib/CIME/test_scheduler.py b/scripts/lib/CIME/test_scheduler.py
--- a/scripts/lib/CIME/test_scheduler.py
+++ b/scripts/lib/CIME/test_scheduler.py
@@ -341,7 +341,10 @@ def __init__(
                     if os.path.isdir(test_baseline):
                         existing_baselines.append(test_baseline)
                         if allow_baseline_overwrite:
-                            clear_folder(test_baseline)
+                            if self._namelists_only:
+                                clear_folder(os.path.join(test_baseline, "CaseDocs"))
+                            else:
+                                clear_folder(test_baseline)
                 expect(
                     allow_baseline_overwrite or len(existing_baselines) == 0,
                     "Baseline directories already exists {}\n"
@@ -923,7 +926,7 @@ def _xml_phase(self, test):
         envtest.write()
         lock_file("env_run.xml", caseroot=test_dir, newname="env_run.orig.xml")
 
-        with Case(test_dir, read_only=False) as case:
+        with Case(test_dir, read_only=False, non_local=self._non_local) as case:
             if self._output_root is None:
                 self._output_root = case.get_value("CIME_OUTPUT_ROOT")
             # if we are running a single test we don't need sharedlibroot
diff --git a/scripts/lib/CIME/tests/test_sys_bless_tests_results.py b/scripts/lib/CIME/tests/test_sys_bless_tests_results.py
--- a/scripts/lib/CIME/tests/test_sys_bless_tests_results.py
+++ b/scripts/lib/CIME/tests/test_sys_bless_tests_results.py
@@ -71,7 +71,7 @@ def test_bless_test_results(self):
             self._create_test(compargs, test_id=test_id, run_errors=True)
 
             # compare_test_results should detect the fail
-            cpr_cmd = "{}/compare_test_results --test-root {} -t {} 2>&1".format(
+            cpr_cmd = "{}/compare_test_results --test-root {} -t {} ".format(
                 self.TOOLS_DIR, self._testroot, test_id
             )
             output = self.run_cmd_assert_result(
@@ -122,7 +122,7 @@ def test_rebless_namelist(self):
         self.run_cmd_assert_result("./case.cmpgen_namelists", from_dir=casedir)
 
         # compare_test_results should pass
-        cpr_cmd = "{}/compare_test_results --test-root {} -n -t {} 2>&1".format(
+        cpr_cmd = "{}/compare_test_results --test-root {} -n -t {} ".format(
             self.TOOLS_DIR, self._testroot, test_id
         )
         output = self.run_cmd_assert_result(cpr_cmd)
@@ -130,11 +130,11 @@ def test_rebless_namelist(self):
         # use regex
         expected_pattern = re.compile(r"PASS %s[^\s]* NLCOMP" % test_to_change)
         the_match = expected_pattern.search(output)
+        msg = f"Cmd {cpr_cmd} failed to display passed test in output:\n{output}"
         self.assertNotEqual(
             the_match,
             None,
-            msg="Cmd '%s' failed to display passed test in output:\n%s"
-            % (cpr_cmd, output),
+            msg=msg,
         )
 
         # Modify namelist
@@ -183,7 +183,7 @@ def test_rebless_namelist(self):
         )
 
         # compare_test_results should fail
-        cpr_cmd = "{}/compare_test_results --test-root {} -n -t {} 2>&1".format(
+        cpr_cmd = "{}/compare_test_results --test-root {} -n -t {} ".format(
             self.TOOLS_DIR, self._testroot, test_id
         )
         output = self.run_cmd_assert_result(
diff --git a/scripts/lib/CIME/tests/test_sys_cime_case.py b/scripts/lib/CIME/tests/test_sys_cime_case.py
--- a/scripts/lib/CIME/tests/test_sys_cime_case.py
+++ b/scripts/lib/CIME/tests/test_sys_cime_case.py
@@ -534,13 +534,15 @@ def test_cime_case_test_custom_project(self):
                 "--project=testproj",
                 test_name,
                 "--mpilib=mpi-serial",
+                "--non-local",
             ],
             test_id=self._baseline_name,
             env_changes="unset CIME_GLOBAL_WALLTIME &&",
         )
 
         result = self.run_cmd_assert_result(
-            "./xmlquery --value PROJECT --subgroup=case.test", from_dir=casedir
+            "./xmlquery --non-local --value PROJECT --subgroup=case.test",
+            from_dir=casedir,
         )
         self.assertEqual(result, "testproj")
 
diff --git a/scripts/lib/CIME/tests/test_sys_create_newcase.py b/scripts/lib/CIME/tests/test_sys_create_newcase.py
--- a/scripts/lib/CIME/tests/test_sys_create_newcase.py
+++ b/scripts/lib/CIME/tests/test_sys_create_newcase.py
@@ -271,19 +271,19 @@ def test_e_xmlquery(self):
         self.assertTrue(os.path.isfile(os.path.join(casedir, xmlquery)))
 
         # Test command line options
-        with Case(casedir, read_only=True) as case:
+        with Case(casedir, read_only=True, non_local=True) as case:
             STOP_N = case.get_value("STOP_N")
             COMP_CLASSES = case.get_values("COMP_CLASSES")
             BUILD_COMPLETE = case.get_value("BUILD_COMPLETE")
-            cmd = xmlquery + " STOP_N --value"
+            cmd = xmlquery + " --non-local STOP_N --value"
             output = utils.run_cmd_no_fail(cmd, from_dir=casedir)
             self.assertTrue(output == str(STOP_N), msg="%s != %s" % (output, STOP_N))
-            cmd = xmlquery + " BUILD_COMPLETE --value"
+            cmd = xmlquery + " --non-local BUILD_COMPLETE --value"
             output = utils.run_cmd_no_fail(cmd, from_dir=casedir)
             self.assertTrue(output == "TRUE", msg="%s != %s" % (output, BUILD_COMPLETE))
             # we expect DOCN_MODE to be undefined in this X compset
             # this test assures that we do not try to resolve this as a compvar
-            cmd = xmlquery + " DOCN_MODE --value"
+            cmd = xmlquery + " --non-local DOCN_MODE --value"
             _, output, error = utils.run_cmd(cmd, from_dir=casedir)
             self.assertTrue(
                 error == "ERROR:  No results found for variable DOCN_MODE",
@@ -294,25 +294,25 @@ def test_e_xmlquery(self):
 
             for comp in COMP_CLASSES:
                 caseresult = case.get_value("NTASKS_%s" % comp)
-                cmd = xmlquery + " NTASKS_%s --value" % comp
+                cmd = xmlquery + " --non-local NTASKS_%s --value" % comp
                 output = utils.run_cmd_no_fail(cmd, from_dir=casedir)
                 self.assertTrue(
                     output == str(caseresult), msg="%s != %s" % (output, caseresult)
                 )
-                cmd = xmlquery + " NTASKS --subgroup %s --value" % comp
+                cmd = xmlquery + " --non-local NTASKS --subgroup %s --value" % comp
                 output = utils.run_cmd_no_fail(cmd, from_dir=casedir)
                 self.assertTrue(
                     output == str(caseresult), msg="%s != %s" % (output, caseresult)
                 )
             if self.MACHINE.has_batch_system():
                 JOB_QUEUE = case.get_value("JOB_QUEUE", subgroup="case.run")
-                cmd = xmlquery + " JOB_QUEUE --subgroup case.run --value"
+                cmd = xmlquery + " --non-local JOB_QUEUE --subgroup case.run --value"
                 output = utils.run_cmd_no_fail(cmd, from_dir=casedir)
                 self.assertTrue(
                     output == JOB_QUEUE, msg="%s != %s" % (output, JOB_QUEUE)
                 )
 
-            cmd = xmlquery + " --listall"
+            cmd = xmlquery + " --non-local --listall"
             utils.run_cmd_no_fail(cmd, from_dir=casedir)
 
         cls._do_teardown.append(cls._testroot)
@@ -714,11 +714,11 @@ def test_ka_createnewcase_extra_machines_dir(self):
         cls._do_teardown.append(testdir)
 
         # Run case.setup
-        self.run_cmd_assert_result("./case.setup", from_dir=testdir)
+        self.run_cmd_assert_result("./case.setup --non-local", from_dir=testdir)
 
         # Make sure Macros file contains expected text
 
-        with Case(testdir) as case:
+        with Case(testdir, non_local=True) as case:
             with CmakeTmpBuildDir(macroloc=testdir) as cmaketmp:
                 macros_contents = cmaketmp.get_makefile_vars(case=case)
 
diff --git a/scripts/lib/CIME/tests/test_unit_case.py b/scripts/lib/CIME/tests/test_unit_case.py
--- a/scripts/lib/CIME/tests/test_unit_case.py
+++ b/scripts/lib/CIME/tests/test_unit_case.py
@@ -22,7 +22,6 @@ def make_valid_case(path):
 class TestCaseSubmit(unittest.TestCase):
     def test_check_case(self):
         case = mock.MagicMock()
-
         case_submit.check_case(case, chksum=True)
 
         case.check_all_input_data.assert_called_with(chksum=True)
@@ -57,7 +56,7 @@ def test_submit(
             ]
 
             make_valid_case(tempdir)
-            with Case(tempdir) as case:
+            with Case(tempdir, non_local=True) as case:
                 case.submit(chksum=True)
 
             _submit.assert_called_with(