[autotest] do not enforce satisfiabiliy of provisionable label

Provisionable labels should not already need to exist and be applied to
a host in order for a job to be validated (hosts with acquire the label
once they are provisioned).

Because of some strange database/transauction/consistency weirdness, I
was not able to use server.cros.provision.ensure_label_exists from
inside of create_new_job in rpc_utils. I will file a separate bug about
that mystery.

BUG=chromium:265660
TEST=In a fresh autotest database with no labels yet created:
`./atest job create -d carrot -b board:lumpy --test=dummy_Pass
dummy_Pass` -> ValidationError as desired.
`./atest job create -d cros-version:carrot -b board:lumpy --test=dummy_Pass
dummy_Pass` -> Job and label (cros-version:carrot) created.

DEPLOY=apache

Change-Id: I9b93811f401a82e68dbad7388b655ab1ffec2602
Reviewed-on: https://gerrit.chromium.org/gerrit/64242
Commit-Queue: Aviv Keshet <akeshet@chromium.org>
Reviewed-by: Aviv Keshet <akeshet@chromium.org>
Tested-by: Aviv Keshet <akeshet@chromium.org>
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index 7d370d1..c91633d 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -10,6 +10,7 @@
 import django.http
 from autotest_lib.frontend.afe import models, model_logic
 from autotest_lib.client.common_lib import control_data
+from autotest_lib.server.cros import provision
 
 NULL_DATETIME = datetime.datetime.max
 NULL_DATE = datetime.date.max
@@ -233,7 +234,8 @@
     hosts_in_job = models.Host.objects.filter(id__in=host_ids)
     ok_hosts = hosts_in_job
     for index, dependency in enumerate(job_dependencies):
-        ok_hosts = ok_hosts.filter(labels__name=dependency)
+        if not provision.can_provision(dependency):
+            ok_hosts = ok_hosts.filter(labels__name=dependency)
     failing_hosts = (set(host.hostname for host in host_objects) -
                      set(host.hostname for host in ok_hosts))
     if failing_hosts:
@@ -492,6 +494,21 @@
     check_for_duplicate_hosts(host_objects)
 
     check_job_dependencies(host_objects, dependencies)
+
+    # There may be provisionable labels in the dependencies list
+    # that do not yet exist in the database. If so, create them.
+    new_label_added = False
+    for label_name in dependencies:
+        if provision.can_provision(label_name):
+            # TODO: We could save a few queries
+            # if we had a bulk ensure-label-exists function, which used
+            # a bulk .get() call. The win is probably very small.
+            new_label_added = (_ensure_label_exists(label_name) |
+                               new_label_added)
+    if new_label_added:
+        labels_by_name = dict((label.name, label)
+                              for label in models.Label.objects.all())
+
     options['dependencies'] = [labels_by_name[label_name]
                                for label_name in dependencies]
 
@@ -517,6 +534,29 @@
     return job.id
 
 
+def _ensure_label_exists(name):
+    """
+    Ensure that a label called |name| exists in the Django models.
+
+    This function is to be called from within afe rpcs only, as an
+    alternative to server.cros.provision.ensure_label_exists(...). It works
+    by Django model manipulation, rather than by making another create_label
+    rpc call.
+
+    @param name: the label to check for/create.
+    @raises ValidationError: There was an error in the response that was
+                             not because the label already existed.
+    @returns True is a label was created, False otherwise.
+    """
+    try:
+        models.Label.objects.get(name=name)
+    except models.Label.DoesNotExist:
+        new_label = models.Label.objects.create(name=name)
+        new_label.save()
+        return True
+    return False
+
+
 def find_platform_and_atomic_group(host):
     """
     Figure out the platform name and atomic group name for the given host