[moblab] Add UI functionality to Set/Update host attributes.

Ui demo at moblab 100.96.59.141

BUG=chromium:732505
TEST=unit test and local moblab testing.

Change-Id: Icdc3e532b50fbf9b1a2440f423a00bd74a1ac232
Reviewed-on: https://chromium-review.googlesource.com/583711
Commit-Ready: Keith Haddow <[email protected]>
Tested-by: Keith Haddow <[email protected]>
Reviewed-by: Keith Haddow <[email protected]>
Reviewed-by: Michael Tang <[email protected]>
diff --git a/frontend/afe/moblab_rpc_interface.py b/frontend/afe/moblab_rpc_interface.py
index 91645b5..f0f4fde 100644
--- a/frontend/afe/moblab_rpc_interface.py
+++ b/frontend/afe/moblab_rpc_interface.py
@@ -349,6 +349,7 @@
             return match.group('bucket')
     return None
 
+
 def _is_valid_boto_key(key_id, key_secret, directory):
   try:
       _run_bucket_performance_test(key_id, key_secret, directory)
@@ -356,6 +357,7 @@
        return(False, str(e))
   return(True, None)
 
+
 def _validate_cloud_storage_info(cloud_storage_info):
     """Checks if the cloud storage information is valid.
 
@@ -471,6 +473,9 @@
     for host in hosts:
         labels = [label.name for label in host.label_list]
         labels.sort()
+        for host_attribute in host.hostattribute_set.all():
+              labels.append("ATTR:(%s=%s)" % (host_attribute.attribute,
+                                              host_attribute.value))
         configured_duts[host.hostname] = ', '.join(labels)
 
     return rpc_utils.prepare_for_serialization(
@@ -560,6 +565,37 @@
     return (True, 'Removed label %s from DUT %s' % (label_name, ipaddress))
 
 
+@rpc_utils.moblab_only
+def set_host_attrib(ipaddress, attribute, value):
+    """ RPC handler to set an attribute of a host.
+
+    @param ipaddress: IP address of the DUT.
+    @param attribute: string name of attribute
+    @param value: string, or None to delete an attribute
+
+    @return: True if the command succeeds without an exception
+    """
+    host_obj = models.Host.smart_get(ipaddress)
+    host_obj.set_or_delete_attribute(attribute, value)
+    return (True, 'Updated attribute %s to %s on DUT %s' % (
+        attribute, value, ipaddress))
+
+
+@rpc_utils.moblab_only
+def delete_host_attrib(ipaddress, attribute):
+    """ RPC handler to delete an attribute of a host.
+
+    @param ipaddress: IP address of the DUT.
+    @param attribute: string name of attribute
+
+    @return: True if the command succeeds without an exception
+    """
+    host_obj = models.Host.smart_get(ipaddress)
+    host_obj.set_or_delete_attribute(attribute, None)
+    return (True, 'Deleted attribute %s from DUT %s' % (
+        attribute, ipaddress))
+
+
 def _get_connected_dut_labels(requested_label, only_first_label=True):
     """ Query the DUT's attached to the moblab and return a filtered list
         of labels.
diff --git a/frontend/client/src/autotest/moblab/DutManagementView.java b/frontend/client/src/autotest/moblab/DutManagementView.java
index a7f6909..67921f4 100644
--- a/frontend/client/src/autotest/moblab/DutManagementView.java
+++ b/frontend/client/src/autotest/moblab/DutManagementView.java
@@ -33,6 +33,11 @@
   private CheckBox poolCheckBox;
   private TextBox poolLabelName;
   private Label poolLabel;
+  private TextBox attributeName;
+  private TextBox attributeValue;
+  private HorizontalPanel labelActionRow;
+  private HorizontalPanel attribActionRow;
+  private HorizontalPanel attribValueActionRow;
 
   private static final int DHCP_IP_COLUMN = 0;
   private static final int DHCP_MAC_COLUMN = 1;
@@ -50,6 +55,12 @@
     dutInfoTable.removeAllRows();
     poolCheckBox.setValue(false);
     poolLabelName.setText("");
+    attributeName.setText("");
+    attributeValue.setValue("");
+
+    labelActionRow.setVisible(false);
+    attribActionRow.setVisible(false);
+
     loadData();
   }
 
@@ -58,7 +69,7 @@
     super.initialize();
     // Main table of connected DUT information.
     dutInfoTable = new FlexTable();
-    
+
     // The row of controls underneath the main data table.
     dutSetupPanel = new VerticalPanel();
 
@@ -68,18 +79,30 @@
     options.addItem("Remove Selected DUT's");
     options.addItem("Add Label Selected DUT's");
     options.addItem("Remove Label Selected DUT's");
+    options.addItem("Add Attribute to Selected DUT's");
+    options.addItem("Remove Attribute from Selected DUT's");
     options.setStyleName("dut_manage_action_row");
     options.addChangeHandler(new ChangeHandler() {
       @Override
       public void onChange(ChangeEvent event) {
         if (options.getSelectedIndex() == 2 || options.getSelectedIndex() == 3) {
-          poolCheckBox.setEnabled(true);
           poolCheckBox.setValue(false);
-          poolLabelName.setEnabled(true);
           poolLabelName.setText("");
+          labelActionRow.setVisible(true);
+          attribActionRow.setVisible(false);
+        } else if (options.getSelectedIndex() == 4 || options.getSelectedIndex() == 5) {
+          attributeName.setText("");
+          attributeValue.setValue("");
+          labelActionRow.setVisible(false);
+          attribActionRow.setVisible(true);
+          if (options.getSelectedIndex() == 4) {
+            attribValueActionRow.setVisible(true);
+          } else {
+            attribValueActionRow.setVisible(false);
+          }
         } else {
-          poolCheckBox.setEnabled(false);
-          poolLabelName.setEnabled(false);
+          labelActionRow.setVisible(false);
+          attribActionRow.setVisible(false);
         }
       }
     });
@@ -105,9 +128,13 @@
               } else if (action == 1) {
                 removeDut(i);
               } else if (action == 2) {
-                addLabel(i, getLabelString());
+                addLabel(i, getPoolLabelString());
               } else if (action == 3) {
-                removeLabel(i, getLabelString());
+                removeLabel(i, getPoolLabelString());
+              } else if (action == 4) {
+                addAttribute(i, attributeName.getText(), attributeValue.getText());
+              } else if (action == 5) {
+                removeAttribute(i, attributeName.getText());
               }
             }
           }
@@ -135,23 +162,47 @@
     poolLabelName.setStyleName("dut_manage_action_row_item");
     poolCheckBox.setEnabled(false);
 
+    labelActionRow = new HorizontalPanel();
+    labelActionRow.add(poolCheckBox);
+    labelActionRow.add(poolLabel);
+    labelActionRow.add(poolLabelName);
+
+    // The name/value pair required to set an attribute
+    Label attributeNameLabel = new Label();
+    attributeNameLabel.setText("Attribute:");
+    attributeNameLabel.setStyleName("dut_manage_action_row_item");
+    attributeName = new TextBox();
+    attributeName.setStyleName("dut_manage_action_row_item");
+
+    Label attributeValueLabel = new Label();
+    attributeValueLabel.setText("Value:");
+    attributeValueLabel.setStyleName("dut_manage_action_row_item");
+    attributeValue = new TextBox();
+    attributeValue.setStyleName("dut_manage_action_row_item");
+
+    attribActionRow = new HorizontalPanel();
+    attribActionRow.add(attributeNameLabel);
+    attribActionRow.add(attributeName);
+    attribValueActionRow = new HorizontalPanel();
+    attribValueActionRow.add(attributeValueLabel);
+    attribValueActionRow.add(attributeValue);
+    attribActionRow.add(attribValueActionRow);
+
    // Assemble the display panels in the correct order.
     dutSetupPanel.add(dutInfoTable);
     HorizontalPanel actionRow = new HorizontalPanel();
     actionRow.setStyleName("dut_manage_action_row");
     actionRow.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
     actionRow.add(options);
-    actionRow.add(poolCheckBox);
-    actionRow.add(poolLabel);
-    actionRow.add(poolLabelName);
+    actionRow.add(labelActionRow);
+    actionRow.add(attribActionRow);
     actionRow.add(actionButton);
     dutSetupPanel.add(actionRow);
     dutSetupPanel.add(informationArea);
     addWidget(dutSetupPanel, "view_dut_manage");
-    poolLabelName.setEnabled(false);
   }
 
-  private String getLabelString() {
+  private String getPoolLabelString() {
     StringBuilder builder = new StringBuilder(poolLabelName.getText());
     if (poolCheckBox.getValue()) {
       builder.insert(0, "pool:");
@@ -274,6 +325,29 @@
     MoblabRpcHelper.removeMoblabLabel(ipAddress, labelName, new LogAction(informationArea));
   }
 
+ /**
+   * Make an RPC to to the autotest system to add an attribute to a DUT whoes details are in the
+   * given row.
+   * @param row_number row in the data table that has the information about the DUT
+   * @param attributeName the attribute name to be set.
+   * @param attributeValue the attribute value to be associated with the attributeName.
+   */
+  private void addAttribute(int row_number, String attributeName, String attributeValue) {
+    String ipAddress = ((Label)dutInfoTable.getWidget(row_number, DHCP_IP_COLUMN)).getText();
+    MoblabRpcHelper.setMoblabAttribute(ipAddress, attributeName, attributeValue, new LogAction(informationArea));
+  }
+
+ /**
+   * Make an RPC to to the autotest system to remove an attribute to a DUT whoes details are in the
+   * given row.
+   * @param row_number row in the data table that has the information about the DUT
+   * @param attributeName the attribute name to be removed.
+   */
+  private void removeAttribute(int row_number, String attributeName) {
+    String ipAddress = ((Label)dutInfoTable.getWidget(row_number, DHCP_IP_COLUMN)).getText();
+    MoblabRpcHelper.removeMoblabAttribute(ipAddress, attributeName, new LogAction(informationArea));
+  }
+
   /**
    * Call back that inserts a string from an completed RPC into the UI.
    */
diff --git a/frontend/client/src/autotest/moblab/rpc/MoblabRpcHelper.java b/frontend/client/src/autotest/moblab/rpc/MoblabRpcHelper.java
index b001f07..99a2101 100644
--- a/frontend/client/src/autotest/moblab/rpc/MoblabRpcHelper.java
+++ b/frontend/client/src/autotest/moblab/rpc/MoblabRpcHelper.java
@@ -258,6 +258,53 @@
     });
   }
 
+  /**
+   * add an attribute to a specific dut.
+   * @param dutIpAddress  ipaddress of the device to have the new attribute applied.
+   * @param attributeName the attribute name
+   * @param attributeValue the attribute value to be associated with the name
+   * @param callback callback to execute when the rpc is complete.
+   */
+  public static void setMoblabAttribute(String dutIpAddress, String attributeName,
+      String attributeValue, final MoblabRpcCallbacks.LogActionCompleteCallback callback) {
+    JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
+    JSONObject params = new JSONObject();
+    params.put("ipaddress", new JSONString(dutIpAddress));
+    params.put("attribute", new JSONString(attributeName));
+    params.put("value", new JSONString(attributeValue));
+    rpcProxy.rpcCall("set_host_attrib", params, new JsonRpcCallback() {
+      @Override
+      public void onSuccess(JSONValue result) {
+        boolean didSucceed = result.isArray().get(0).isBoolean().booleanValue();
+        String information = result.isArray().get(1).isString().stringValue();
+        callback.onLogActionComplete(didSucceed, information);
+      }
+    });
+  }
+
+  /**
+   * remove an attribute from a specific dut.
+   * @param dutIpAddress  ipaddress of the device to have the new attribute applied.
+   * @param attributeName the attribute name
+   * @param callback callback to execute when the rpc is complete.
+   */
+  public static void removeMoblabAttribute(String dutIpAddress, String attributeName,
+      final  MoblabRpcCallbacks.LogActionCompleteCallback callback) {
+    JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
+    JSONObject params = new JSONObject();
+    params.put("ipaddress", new JSONString(dutIpAddress));
+    params.put("attribute", new JSONString(attributeName));
+    rpcProxy.rpcCall("delete_host_attrib", params, new JsonRpcCallback() {
+      @Override
+      public void onSuccess(JSONValue result) {
+        boolean didSucceed = result.isArray().get(0).isBoolean().booleanValue();
+        String information = result.isArray().get(1).isString().stringValue();
+        callback.onLogActionComplete(didSucceed, information);
+      }
+    });
+  }
+
+
   public static void fetchConnectedBoards(
       final MoblabRpcCallbacks.FetchConnectedBoardsCallback callback) {
     JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();