Return nodes after use

This returns the nodes after use (or in some cases, before use)
and unlocks them.

Change-Id: I84e998a6099217f0a0924caf1872f33dce28ba01
diff --git a/tests/test_nodepool.py b/tests/test_nodepool.py
index 3019f08..677ae73 100644
--- a/tests/test_nodepool.py
+++ b/tests/test_nodepool.py
@@ -83,6 +83,12 @@
         for node in nodeset.getNodes():
             self.assertEqual(node.state, 'in-use')
 
+        # Return the nodes
+        self.nodepool.returnNodeset(nodeset)
+        for node in nodeset.getNodes():
+            self.assertIsNone(node.lock)
+            self.assertEqual(node.state, 'used')
+
     def test_node_request_disconnect(self):
         # Test that node requests are re-submitted after disconnect
 
diff --git a/zuul/manager/__init__.py b/zuul/manager/__init__.py
index d1e12c5..f5a35cd 100644
--- a/zuul/manager/__init__.py
+++ b/zuul/manager/__init__.py
@@ -599,6 +599,13 @@
         self.sched.mutex.release(item, build.job)
         self.log.debug("Item %s status is now:\n %s" %
                        (item, item.formatStatus()))
+
+        try:
+            nodeset = build.build_set.getJobNodeSet(build.job.name)
+            self.nodepool.returnNodeset(nodeset)
+        except Exception:
+            self.log.exception("Unable to return nodeset %s" % (nodeset,))
+
         return True
 
     def onMergeCompleted(self, event):
diff --git a/zuul/nodepool.py b/zuul/nodepool.py
index eceac0b..11b02b6 100644
--- a/zuul/nodepool.py
+++ b/zuul/nodepool.py
@@ -45,8 +45,14 @@
             node.state = 'in-use'
             self.sched.zk.storeNode(node)
 
-    def returnNodes(self, nodes, used=True):
-        pass
+    def returnNodeset(self, nodeset):
+        for node in nodeset.getNodes():
+            if node.lock is None:
+                raise Exception("Node %s is not locked" % (node,))
+            if node.state == 'in-use':
+                node.state = 'used'
+            self.sched.zk.storeNode(node)
+        self._unlockNodes(nodeset.getNodes())
 
     def unlockNodeset(self, nodeset):
         self._unlockNodes(nodeset.getNodes())
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 270e055..4a0a9eb 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -810,12 +810,13 @@
 
         if build_set is not build_set.item.current_build_set:
             self.log.warning("Build set %s is not current" % (build_set,))
-            self.nodepool.returnNodes(request.nodes, used=False)
+            self.nodepool.returnNodeset(request.nodeset)
             return
         pipeline = build_set.item.pipeline
         if not pipeline:
             self.log.warning("Build set %s is not associated with a pipeline" %
                              (build_set,))
+            self.nodepool.returnNodeset(request.nodeset)
             return
         pipeline.manager.onNodesProvisioned(event)