Pass assigned nodes to the launcher

This fleshes out the nodepool stub a little more.  It passes some
node information to the launcher after node requests have been
fulfilled.  It also corrects some logic errors in the node request
framework.  It moves data structures related to node requests into
the model.  Finally, it adds nodes to the configuration of some
tests to exercise the system, and adds a test to verify the correct
node is supplied on a job that has a branch variant.

Change-Id: I395ce23ae865df3a55436ee92d04e0eae07c963a
diff --git a/tests/base.py b/tests/base.py
index 552bdd6..49ba04e 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -541,14 +541,21 @@
 class FakeBuild(object):
     log = logging.getLogger("zuul.test")
 
-    def __init__(self, launch_server, job, node):
+    def __init__(self, launch_server, job):
         self.daemon = True
         self.launch_server = launch_server
         self.job = job
         self.jobdir = None
         self.uuid = job.unique
-        self.node = node
         self.parameters = json.loads(job.arguments)
+        # TODOv3(jeblair): self.node is really "the image of the node
+        # assigned".  We should rename it (self.node_image?) if we
+        # keep using it like this, or we may end up exposing more of
+        # the complexity around multi-node jobs here
+        # (self.nodes[0].image?)
+        self.node = None
+        if len(self.parameters.get('nodes')) == 1:
+            self.node = self.parameters['nodes'][0]['image']
         self.unique = self.parameters['ZUUL_UUID']
         self.name = self.parameters['job']
         self.wait_condition = threading.Condition()
@@ -707,8 +714,7 @@
                        (regex, len(self.running_builds)))
 
     def launchJob(self, job):
-        node = None
-        build = FakeBuild(self, job, node)
+        build = FakeBuild(self, job)
         job.build = build
         self.running_builds.append(build)
         self.job_builds[job.unique] = build