Repository: dmwm/wmcore
Issue: JobCreator in 1.5.4 branch crashes when processing workflows pre-GPU support

**Impact of the bug**
WMAgent

**Describe the bug**
When WMAgent is processing a workflow that was created before the GPU support was implemented in ReqMgr2, it fails to parse the workload and the component actually crashes. Problem is that it fails to find the `gpu` section in the workload spec object.

**How to reproduce it**
Run any workflow created before 5 Sept, 2021; in WMAgent >= 1.5.4

**Expected behavior**
Make this GPU feature backwards compatible, meaning that the agent should not crash when processing old workflows and the default values have to be set in case a workflow is not specifying GPU paramaters.

**Additional context and error message**
JobCreator log looks like (in cmsgwms-submit1):
```
2021-10-12 10:40:00,761:140267653744384:INFO:JobCreatorPoller:Retrieved 1 jobGroups from jobSplitter
2021-10-12 10:40:00,827:140267653744384:ERROR:JobCreatorPoller:Failed to execute JobCreator. Error: 'ConfigSection' object has no attribute 'gpu'
Traceback (most recent call last):
  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMComponent/JobCreator/JobCreatorPoller.py", line 379, in algorithm
    self.pollSubscriptions()
  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMComponent/JobCreator/JobCreatorPoller.py", line 539, in pollSubscriptions
    'requiresGPU': wmTask.getRequiresGPU(),
  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMCore/WMSpec/WMTask.py", line 1531, in getRequiresGPU
    if stepHelper.stepType() == "CMSSW" and stepHelper.getGPURequired():
  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMCore/WMSpec/WMStep.py", line 72, in getGPURequired
    if hasattr(self.data.application.gpu, "gpuRequired"):
AttributeError: 'ConfigSection' object has no attribute 'gpu'
2021-10-12 10:40:01,102:140267653744384:ERROR:BaseWorkerThread:Error in worker algorithm (1):
Backtrace:
  <WMComponent.JobCreator.JobCreatorPoller.JobCreatorPoller object at 0x7f92a4f13910> <@========== WMException Start ==========@>
Exception Class: JobCreatorException
Message: Failed to execute JobCreator. Error: 'ConfigSection' object has no attribute 'gpu'
        ClassName : None
        ModuleName : WMComponent.JobCreator.JobCreatorPoller
        MethodName : algorithm
        ClassInstance : None
        FileName : /data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMComponent/JobCreator/JobCreatorPoller.py
        LineNumber : 400
        ErrorNr : 0

Traceback: 
  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMComponent/JobCreator/JobCreatorPoller.py", line 379, in algorithm
    self.pollSubscriptions()

  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMComponent/JobCreator/JobCreatorPoller.py", line 539, in pollSubscriptions
    'requiresGPU': wmTask.getRequiresGPU(),

  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMCore/WMSpec/WMTask.py", line 1531, in getRequiresGPU
    if stepHelper.stepType() == "CMSSW" and stepHelper.getGPURequired():

  File "/data/srv/wmagent/v1.5.4.patch2/sw/slc7_amd64_gcc630/cms/wmagentpy3/1.5.4.patch2/lib/python3.8/site-packages/WMCore/WMSpec/WMStep.py", line 72, in getGPURequired
    if hasattr(self.data.application.gpu, "gpuRequired"):
```

Changed files:
test/python/WMCore_t/WMSpec_t/WMTask_t.py, src/python/WMCore/WMSpec/WMStep.py, test/python/WMCore_t/WMSpec_t/WMStep_t.py

diff --git a/src/python/WMCore/WMSpec/WMStep.py b/src/python/WMCore/WMSpec/WMStep.py
index a530edd88..4155fb0a2 100644
--- a/src/python/WMCore/WMSpec/WMStep.py
+++ b/src/python/WMCore/WMSpec/WMStep.py
@@ -69,16 +69,18 @@ class WMStepHelper(TreeHelper):
         """
         Return whether GPU is required or not for this step object
         """
-        if hasattr(self.data.application.gpu, "gpuRequired"):
-            return self.data.application.gpu.gpuRequired
+        if hasattr(self.data.application, "gpu"):
+            if hasattr(self.data.application.gpu, "gpuRequired"):
+                return self.data.application.gpu.gpuRequired
         return None
 
     def getGPURequirements(self):
         """
         Return the GPU requirements for this step object
         """
-        if hasattr(self.data.application.gpu, "gpuRequirements"):
-            return self.data.application.gpu.gpuRequirements
+        if hasattr(self.data.application, "gpu"):
+            if hasattr(self.data.application.gpu, "gpuRequirements"):
+                return self.data.application.gpu.gpuRequirements
         return None
 
     def addStep(self, stepName):
diff --git a/test/python/WMCore_t/WMSpec_t/WMStep_t.py b/test/python/WMCore_t/WMSpec_t/WMStep_t.py
index 28bfc8c37..0f5dabc72 100644
--- a/test/python/WMCore_t/WMSpec_t/WMStep_t.py
+++ b/test/python/WMCore_t/WMSpec_t/WMStep_t.py
@@ -162,8 +162,7 @@ class WMStepTest(unittest.TestCase):
         wmStep = makeWMStep("step1")
         self.assertIsNone(wmStep.stepType())
         self.assertFalse(hasattr(wmStep.data, "gpu"))
-        with self.assertRaises(AttributeError):
-            wmStep.getGPURequired()
+        self.assertIsNone(wmStep.getGPURequired())
 
         # now apply the CMSSW template
         wmStep.setStepType("CMSSW")
diff --git a/test/python/WMCore_t/WMSpec_t/WMTask_t.py b/test/python/WMCore_t/WMSpec_t/WMTask_t.py
index 5c9888ef4..b0aea45d1 100644
--- a/test/python/WMCore_t/WMSpec_t/WMTask_t.py
+++ b/test/python/WMCore_t/WMSpec_t/WMTask_t.py
@@ -867,9 +867,9 @@ class WMTaskTest(unittest.TestCase):
                     self.assertEqual(stepHelper.getGPURequired(), "forbidden")
                     self.assertIsNone(stepHelper.getGPURequirements())
                 else:
-                    # AttributeError: 'ConfigSection' object has no attribute 'gpu'
-                    with self.assertRaises(AttributeError):
-                        stepHelper.getGPURequired()
+                    # 'ConfigSection' object has no attribute 'gpu'
+                    self.assertIsNone(stepHelper.getGPURequired())
+
 
         ### Now set a single value for both tasks
         gpuParams = {"GPUMemoryMB": 1234, "CUDARuntime": "11.2.3", "CUDACapabilities": ["7.5", "8.0"]}
@@ -894,6 +894,11 @@ class WMTaskTest(unittest.TestCase):
             self.assertEqual(taskObj.getRequiresGPU(), gpuRequired[taskName])
             self.assertItemsEqual(taskObj.getGPURequirements(), gpuParams[taskName])
 
+        # Now delete the "gpu" ConfigSection to make sure we can deal with older workloads/tasks
+        for taskObj in task1.taskIterator():
+            delattr(taskObj.data.steps.cmsRun1.application, "gpu")
+            self.assertEqual(taskObj.getRequiresGPU(), "forbidden")
+
     def testGPUTaskSettingsMultiStep(self):
         """
         Test whether we can properly set/get GPU settings for a