[autotest] Require lock reason to lock device

When locking a device, a locking reason now must be provided.
This applies to both adding a new device, and modifying an
existing device from both the web frontend and the atest
command-line tool.

BUG=chromium:336805
DEPLOY=migrate
TEST=Tested adding locked/unlocked devices and locking/unlocking devices
from both the web frontend and using the 'atest host ...' command-line tools.

Change-Id: I3a8cd8891a2999f026dd709ae8a79e2b8cbc251a
Reviewed-on: https://chromium-review.googlesource.com/267595
Tested-by: Matthew Sartori <msartori@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Matthew Sartori <msartori@chromium.org>
diff --git a/frontend/afe/admin.py b/frontend/afe/admin.py
index 8bf4c60..5b7143e 100644
--- a/frontend/afe/admin.py
+++ b/frontend/afe/admin.py
@@ -135,6 +135,19 @@
         self.fields['labels'].help_text = ('Please enter a comma seperated '
                                            'list of labels.')
 
+    def clean(self):
+        """ ModelForm validation
+
+        Ensure that a lock_reason is provided when locking a device.
+        """
+        cleaned_data = super(HostForm, self).clean()
+        locked = cleaned_data.get('locked')
+        lock_reason = cleaned_data.get('lock_reason')
+        if locked and not lock_reason:
+            raise forms.ValidationError(
+                    'Please provide a lock reason when locking a device.')
+        return cleaned_data
+
     class Meta:
         model = models.Host
 
@@ -158,9 +171,24 @@
         self.successful_hosts = []
         super(HostAdmin, self).__init__(model, admin_site)
 
+    def add_view(self, request, form_url='', extra_context=None):
+        """ Field layout for admin page.
+
+        fields specifies the visibility and order of HostAdmin attributes
+        displayed on the device addition page.
+
+        @param request:  django request
+        @param form_url: url
+        @param extra_context: A dict used to alter the page view
+        """
+        self.fields = ('hostname', 'locked', 'lock_reason', 'leased',
+                       'protection', 'labels', 'shard', 'labels_autodetection')
+        return super(HostAdmin, self).add_view(request, form_url, extra_context)
+
     def change_view(self, request, obj_id, form_url='', extra_context=None):
         # Hide labels_autodetection when editing a host.
-        self.fields = ('hostname', 'locked', 'leased', 'protection', 'labels')
+        self.fields = ('hostname', 'locked', 'lock_reason',
+                       'leased', 'protection', 'labels')
         # Only allow editing host attributes when a host has been created.
         self.inlines = [
             HostAttributeInline,
@@ -193,6 +221,7 @@
         hosts = [str(hostname)]
         platform = None
         locked = form.cleaned_data['locked']
+        lock_reason = form.cleaned_data['lock_reason']
         labels = [label.name for label in form.cleaned_data['labels']]
         protection = form.cleaned_data['protection']
         acls = []
@@ -200,7 +229,7 @@
         # Pipe to cli to perform autodetection and create host.
         host_create_obj = site_host.site_host_create.construct_without_parse(
                 web_server, hosts, platform,
-                locked, labels, acls,
+                locked, lock_reason, labels, acls,
                 protection)
         try:
             self.successful_hosts = host_create_obj.execute()