[autotest] Forward modify_label and delete_label RPCs

BUG=chromium:499660
TEST=puppylab. Let test_host1 and test_host11 have a label l1.
test_host1 is in master and test_host11 is in stumpy shard.
Send modify_label and delete_label RPCs for the label l1,
and check master DB and stumpy shard's DB if they are
updated in the same way.
DEPLOY=apache

Change-Id: I1d7b0f06a4aeb0b2470a677cea12d195535d154f
Reviewed-on: https://chromium-review.googlesource.com/277143
Tested-by: Mungyung Ryu <mkryu@google.com>
Reviewed-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Mungyung Ryu <mkryu@google.com>
diff --git a/frontend/afe/rpc_interface.py b/frontend/afe/rpc_interface.py
index 119d0dc..64e8ca7 100644
--- a/frontend/afe/rpc_interface.py
+++ b/frontend/afe/rpc_interface.py
@@ -64,11 +64,34 @@
 # labels
 
 def modify_label(id, **data):
-    models.Label.smart_get(id).update_object(data)
+    """Modify a label.
+
+    @param id: id or name of a label. More often a label name.
+    @param data: New data for a label.
+    """
+    label_model = models.Label.smart_get(id)
+
+    # Master forwards the RPC to shards
+    if not utils.is_shard():
+        rpc_utils.fanout_rpc(label_model.host_set.all(), 'modify_label', False,
+                             id=id, **data)
+
+    label_model.update_object(data)
 
 
 def delete_label(id):
-    models.Label.smart_get(id).delete()
+    """Delete a label.
+
+    @param id: id or name of a label. More often a label name.
+    """
+    label_model = models.Label.smart_get(id)
+
+    # Master forwards the RPC to shards
+    if not utils.is_shard():
+        rpc_utils.fanout_rpc(label_model.host_set.all(), 'delete_label', False,
+                             id=id)
+
+    label_model.delete()
 
 
 def add_label(name, ignore_exception_if_exists=False, **kwargs):