Repository: ESMCI/cime

Issue:
Check locked files function is broken.
When a user who doesn't have access to the default account (or repo) on a machine, they can try (at least) these 2 things:
a) use the -p option to create_test
b) go into case directory and xmlchange CHARGE_ACCOUNT=new_repo

Both of these methods show some issues.

a) Example:
./create_test -p m1759 SMS.ne4_ne4.FC5AV1C-L

Then looking at CHARGE_ACCOUNT:
cjyang@cori12:/global/cscratch1/sd/cjyang/acme_scratch/cori-knl/SMS.ne4_ne4.FC5AV1C-L.cori-knl_intel.20180125_112550_jdwdsv> ./xmlquery CHARGE_ACCOUNT
 
Results in group case.run
	CHARGE_ACCOUNT: /project/projectdirs/nstaff/cjyang
 
Results in group case.st_archive
	CHARGE_ACCOUNT: /project/projectdirs/nstaff/cjyang
 
Results in group case.test
	CHARGE_ACCOUNT: /project/projectdirs/nstaff/cjyang

However, user job seems to be OK. ?

b) Somewhat similar issue.  And for both, it looks like what is reported from ./preview_run is a little confusing.



Discussion:
@ndkeen Can you please make it clear which version of cime you are using and want machine you are running on.  
I think I know what is going on - this variable affects the job queue stanza in submitted jobs, thus you must run ./case.setup --reset after changing it for it to take effect.
@jedwards4b - thanks for tracking this down. Can we update the
documentation with this - or issue some type of error command?

On Thu, Jan 25, 2018 at 1:00 PM, jedwards4b <notifications@github.com>
wrote:

> I think I know what is going on - this variable affects the job queue
> stanza in submitted jobs, thus you must run ./case.setup --reset after
> changing it for it to take effect.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/ESMCI/cime/issues/2252#issuecomment-360583128>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AHlxEzv35qztm3QLnYFwGPmtz-Npvkoiks5tON1mgaJpZM4RtY0w>
> .
>


Resolving patch:
diff --git a/scripts/lib/CIME/XML/entry_id.py b/scripts/lib/CIME/XML/entry_id.py
--- a/scripts/lib/CIME/XML/entry_id.py
+++ b/scripts/lib/CIME/XML/entry_id.py
@@ -391,29 +391,32 @@ def cleanupnode(self, node):
         """
         expect(False, " Not expected to be here {}".format(self.get(node, "id")))
 
-    def compare_xml(self, other):
+    def compare_xml(self, other, root=None, otherroot=None):
         xmldiffs = {}
-        f1nodes = self.scan_children("entry")
+        if root is not None:
+            expect(otherroot is not None," inconsistant request")
+        f1nodes = self.scan_children("entry", root=root)
         for node in f1nodes:
             vid = self.get(node, "id")
-            f2match = other.scan_optional_child("entry", attributes={"id":vid})
-            if f2match is not None:
+            logger.info("Compare vid {}".format(vid))
+            f2match = other.scan_optional_child("entry", attributes={"id":vid},root=otherroot)
+            expect(f2match is not None,"Could not find {} in Locked file".format(vid))
+            if node != f2match:
                 f1val = self.get_value(vid, resolved=False)
                 if f1val is not None:
                     f2val = other.get_value(vid, resolved=False)
                     if f1val != f2val:
                         xmldiffs[vid] = [f1val, f2val]
-                else:
-                    for comp in self.get_values("COMP_CLASSES"):
+                elif hasattr(self, "_components"):
+                    # pylint: disable=no-member
+                    for comp in self._components:
                         f1val = self.get_value("{}_{}".format(vid,comp), resolved=False)
                         if f1val is not None:
                             f2val = other.get_value("{}_{}".format(vid,comp), resolved=False)
                             if f1val != f2val:
                                 xmldiffs[vid] = [f1val, f2val]
                         else:
-                            f1val = self.to_string(node, method="text")
-                            f2val = self.to_string(f2match, method="text")
-                            if f2val != f1val:
+                            if node != f2match:
                                 f1value_nodes = self.get_children("value", root=node)
                                 for valnode in f1value_nodes:
                                     f2valnodes = other.get_children("value", root=f2match, attributes=self.attrib(valnode))
diff --git a/scripts/lib/CIME/XML/env_batch.py b/scripts/lib/CIME/XML/env_batch.py
--- a/scripts/lib/CIME/XML/env_batch.py
+++ b/scripts/lib/CIME/XML/env_batch.py
@@ -615,3 +615,30 @@ def cancel_job(self, jobid):
                 logger.warning("Batch cancel command '{}' failed with error '{}'".format(cmd, out + "\n" + err))
             else:
                 return True
+
+    def compare_xml(self, other):
+        xmldiffs = {}
+        f1batchnodes = self.get_children("batch_system")
+        for bnode in f1batchnodes:
+            f2bnodes = other.get_children("batch_system",
+                                          attributes = self.attrib(bnode))
+            f2bnode=None
+            if len(f2bnodes):
+                f2bnode = f2bnodes[0]
+            f1batchnodes = self.get_children(root=bnode)
+            for idx, node in enumerate(f1batchnodes):
+                f2matches = other.scan_children(self.name(node), attributes=self.attrib(node), root=f2bnode)
+                f2match = f2matches[idx] if len(f2matches)> idx else None
+                if f2match is not None:
+                    name = self.name(node)
+                    text1 = self.text(node)
+                    text2 = other.text(f2match)
+                    if text1 != text2:
+                        xmldiffs[name] = [text1, text2]
+        f1groups = self.get_children("group")
+        for node in f1groups:
+            group = self.get(node, "id")
+            f2group = other.get_child("group", attributes={"id":group})
+            xmldiffs.update(super(EnvBatch, self).compare_xml(other,
+                                              root=node, otherroot=f2group))
+        return xmldiffs
diff --git a/scripts/lib/CIME/case_setup.py b/scripts/lib/CIME/case_setup.py
--- a/scripts/lib/CIME/case_setup.py
+++ b/scripts/lib/CIME/case_setup.py
@@ -151,6 +151,7 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False):
             check_pelayouts_require_rebuild(case, models)
 
             unlock_file("env_build.xml")
+            unlock_file("env_batch.xml")
 
             case.flush()
             check_lockedfiles(case)
@@ -181,9 +182,11 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False):
             # Make a copy of env_mach_pes.xml in order to be able
             # to check that it does not change once case.setup is invoked
             logger.info("Locking file env_mach_pes.xml")
+            logger.info("Locking file env_batch.xml")
             case.flush()
             logger.debug("at copy TOTALPES = {}".format(case.get_value("TOTALPES")))
             lock_file("env_mach_pes.xml")
+            lock_file("env_batch.xml")
 
         # Create user_nl files for the required number of instances
         if not os.path.exists("user_nl_cpl"):


Test patch:
diff --git a/scripts/tests/scripts_regression_tests.py b/scripts/tests/scripts_regression_tests.py
--- a/scripts/tests/scripts_regression_tests.py
+++ b/scripts/tests/scripts_regression_tests.py
@@ -281,6 +281,23 @@ def test_a_createnewcase(self):
         run_cmd_assert_result(self, "./case.setup", from_dir=testdir)
         run_cmd_assert_result(self, "./case.build", from_dir=testdir)
 
+        with Case(testdir, read_only=False) as case:
+            ntasks = case.get_value("NTASKS_ATM")
+            case.set_value("NTASKS_ATM", ntasks+1)
+        # this should fail with a locked file issue
+        run_cmd_assert_result(self, "./case.build",
+                              from_dir=testdir, expected_stat=1)
+
+        run_cmd_assert_result(self, "./case.setup --reset", from_dir=testdir)
+        run_cmd_assert_result(self, "./case.build", from_dir=testdir)
+        with Case(testdir, read_only=False) as case:
+            case.set_value("CHARGE_ACCOUNT", "fred")
+        # this should fail with a locked file issue
+        run_cmd_assert_result(self, "./case.build",
+                              from_dir=testdir, expected_stat=1)
+        run_cmd_assert_result(self, "./case.setup --reset", from_dir=testdir)
+        run_cmd_assert_result(self, "./case.build", from_dir=testdir)
+
         cls._do_teardown.append(testdir)
 
     def test_aa_no_flush_on_instantiate(self):