[autotest] Allow atest host create to add multiple board label for testbed

testbed may have multiple board labels, e.g.,
board:angler-1, board:angler-2, board:angler-3

autotest code was changed earlier to prevent a dut to have multiple board
labels, which may corrupt shard database.
This change allows testbed to be able to add multiple board labels.

BUG=chromium:660271
TEST=local add testbed, unittest

Change-Id: I198cafef999c7064e6459985402e3248a57470b5
Reviewed-on: https://chromium-review.googlesource.com/404535
Tested-by: Dan Shi <dshi@google.com>
Reviewed-by: Kris Rambish <krisr@chromium.org>
Commit-Queue: Dan Shi <dshi@google.com>
diff --git a/frontend/afe/rpc_interface.py b/frontend/afe/rpc_interface.py
index 6c9c131..999cd22 100644
--- a/frontend/afe/rpc_interface.py
+++ b/frontend/afe/rpc_interface.py
@@ -32,9 +32,10 @@
 __author__ = 'showard@google.com (Steve Howard)'
 
 import ast
-import sys
 import datetime
 import logging
+import re
+import sys
 
 from django.db.models import Count
 import common
@@ -426,6 +427,29 @@
     models.Host.smart_get(id).labels.add(*label_objs)
 
 
+def boards_allowed(boards):
+    """Check if the list of board labels can be set to a single host.
+
+    The only case multiple board labels can be set to a single host is for
+    testbed, which may have a list of board labels like
+    board:angler-1, board:angler-2, board:angler-3'
+
+    @param boards: A list of board labels.
+
+    @returns True if the the list of boards can be set to a single host.
+    """
+    if len(boards) <= 1:
+        return True
+    match = re.match('(board:[^-]+)(?:-\d+)?', boards[0])
+    if not match:
+        return False
+    pattern = '%s-\d+' % match.group(1)
+    for board in boards:
+        if not re.match(pattern, board):
+            return False
+    return True
+
+
 @rpc_utils.route_rpc_to_master
 def host_add_labels(id, labels):
     """Adds labels to a given host.
@@ -443,15 +467,15 @@
     platforms = [label.name for label in label_objs if label.platform]
     boards = [label.name for label in label_objs
               if label.name.startswith('board:')]
-    if len(platforms) > 1 or len(boards) > 1:
+    if len(platforms) > 1 or not boards_allowed(boards):
         raise model_logic.ValidationError(
             {'labels': 'Adding more than one platform/board label: %s %s' %
                        (', '.join(platforms), ', '.join(boards))})
 
     host_obj = models.Host.smart_get(id)
-    if len(platforms) == 1:
+    if platforms:
         models.Host.check_no_platform([host_obj])
-    if len(boards) == 1:
+    if boards:
         models.Host.check_no_board([host_obj])
     add_labels_to_host(id, labels)