Repository: dmwm/wmcore
Issue: Cleanup jobs requesting an empty OS at the job classad

**Impact of the bug**
WMAgent

**Describe the bug**
While validating these job wrapper changes:
https://github.com/dmwm/WMCore/pull/11077

I noticed that Cleanup jobs actually define an empty string for the required OS:
```
  CMS_JobType = "Cleanup"
  REQUIRED_OS = ""
```

**How to reproduce it**
Create a workflow and check the job classads for a Cleanup job.

**Expected behavior**
Cleanup jobs don't rely on any CMSSW release, so ScramArch is actually not defined for this step.
When converting the job/step requirements into job classads, we should make sure that `REQUIRED_OS` has a correct value, which is either `any` or the `rhelX` string.

**Additional context and error message**
None

Changed files:
src/python/WMCore/BossAir/Plugins/BasePlugin.py, test/python/WMCore_t/BossAir_t/BasePlugin_t.py

diff --git a/src/python/WMCore/BossAir/Plugins/BasePlugin.py b/src/python/WMCore/BossAir/Plugins/BasePlugin.py
index f6981b2d2..0dfbab16a 100644
--- a/src/python/WMCore/BossAir/Plugins/BasePlugin.py
+++ b/src/python/WMCore/BossAir/Plugins/BasePlugin.py
@@ -6,7 +6,7 @@ Base class for BossAir plugins
 """
 
 from builtins import object, str, bytes
-from future.utils import viewitems, viewvalues
+from future.utils import viewvalues
 
 from Utils.Utilities import decodeBytesToUnicode
 from WMCore.WMException import WMException
@@ -130,27 +130,25 @@ class BasePlugin(object):
     @staticmethod
     def scramArchtoRequiredOS(scramArch=None):
         """
+        Matches a ScramArch - or a list of it - against a map of Scram
+        to Operating System
 
-        Args:
-            scramArch: string or list of scramArches that are acceptable for the job
-
-        Returns:
-            string to be matched for OS requirements for job
+        :param scramArch: string or list of scramArches defined for a given job
+        :return: a string with the required OS to use
         """
-        requiredOSes = set()
+        defaultValue = 'any'
         if not scramArch:
-            requiredOSes.add('any')
-        elif isinstance(scramArch, (str, bytes)):
-            for arch, validOSes in viewitems(ARCH_TO_OS):
-                if arch in scramArch:
-                    requiredOSes.update(validOSes)
-        elif isinstance(scramArch, list):
-            for validArch in scramArch:
-                for arch, validOSes in viewitems(ARCH_TO_OS):
-                    if arch in validArch:
-                        requiredOSes.update(validOSes)
-        else:
-            requiredOSes.add('any')
+            return defaultValue
+
+        requiredOSes = set()
+        if isinstance(scramArch, (str, bytes)):
+            scramArch = [scramArch]
+        elif not isinstance(scramArch, (list, tuple)):
+            return defaultValue
+
+        for validArch in scramArch:
+            scramOS = validArch.split("_")[0]
+            requiredOSes.update(ARCH_TO_OS.get(scramOS, []))
 
         return ','.join(sorted(requiredOSes))
 
diff --git a/test/python/WMCore_t/BossAir_t/BasePlugin_t.py b/test/python/WMCore_t/BossAir_t/BasePlugin_t.py
index eb167b4f3..5a6886ff3 100644
--- a/test/python/WMCore_t/BossAir_t/BasePlugin_t.py
+++ b/test/python/WMCore_t/BossAir_t/BasePlugin_t.py
@@ -36,14 +36,17 @@ class BasePluginTest(BossAirTest):
         self.assertEqual(bp.scramArchtoRequiredOS('cc8_blah_blah'), 'rhel8')
         self.assertEqual(bp.scramArchtoRequiredOS('cs8_blah_blah'), 'rhel8')
         self.assertEqual(bp.scramArchtoRequiredOS('alma8_blah_blah'), 'rhel8')
-        self.assertEqual(bp.scramArchtoRequiredOS('el88_blah_blah'), 'rhel8')
+        self.assertEqual(bp.scramArchtoRequiredOS('el8_blah_blah'), 'rhel8')
 
         self.assertEqual(bp.scramArchtoRequiredOS(None), 'any')
         self.assertEqual(bp.scramArchtoRequiredOS(""), 'any')
         self.assertEqual(bp.scramArchtoRequiredOS([]), 'any')
 
         self.assertEqual(bp.scramArchtoRequiredOS(['slc6_blah_blah', 'slc7_blah_blah']), 'rhel6,rhel7')
+        self.assertEqual(bp.scramArchtoRequiredOS(['slc6_blah_blah', 'alma8_blah_blah']), 'rhel6,rhel8')
 
+        # unexpected case, a ScramArch being requested without the map implemented
+        self.assertEqual(bp.scramArchtoRequiredOS('slc1_blah_blah'), '')
         return
 
     def testScramArchtoRequiredArch(self):