From 2f01345c76e6c819f2b21b2aca6d555edcaab494 Mon Sep 17 00:00:00 2001
From: Shu Muto <shu.mutow@gmail.com>
Date: Fri, 6 Apr 2018 15:12:32 +0900
Subject: [PATCH] Add new options for container creation

This patch adds following options for container creation.
* auto_heal
* disk
* availablity_zones

Also, add these parameters into details views to show.
And change layouts to add these options.

This change requires python-zunclient 1.4.0 or later.

Change-Id: Ibb9e774ea379a9999c703d6919f14dd0a6e56857
Implements: blueprint add-params-rocky-1
Depends-On: I6c0512816277a63bbf444d0775121bb5d964a36a
---
 lower-constraints.txt                         |  2 +-
 requirements.txt                              |  2 +-
 zun_ui/api/client.py                          |  9 ++-
 zun_ui/api/rest_api.py                        | 16 +++++
 .../actions/workflow/spec.help.html           | 14 ++--
 .../actions/workflow/workflow.service.js      | 64 ++++++++++++++++++-
 .../container/containers/containers.module.js |  2 +
 .../containers/details/overview.html          |  2 +-
 .../static/dashboard/container/zun.service.js | 11 ++++
 9 files changed, 112 insertions(+), 10 deletions(-)

diff --git a/lower-constraints.txt b/lower-constraints.txt
index 2046b34..e608508 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -84,7 +84,7 @@ python-neutronclient==6.7.0
 python-novaclient==10.1.0
 python-openstackclient==3.14.0
 python-swiftclient==3.5.0
-python-zunclient==1.3.0
+python-zunclient==1.4.0
 pytz==2018.3
 PyYAML==3.12
 rcssmin==1.0.6
diff --git a/requirements.txt b/requirements.txt
index 06b66c1..ca79d11 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,6 +8,6 @@
 #
 # PBR should always appear first
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
-python-zunclient>=1.3.0 # Apache-2.0
+python-zunclient>=1.4.0 # Apache-2.0
 
 horizon>=14.0.0.0b1 # Apache-2.0
diff --git a/zun_ui/api/client.py b/zun_ui/api/client.py
index 538aa9f..3f7aa01 100644
--- a/zun_ui/api/client.py
+++ b/zun_ui/api/client.py
@@ -104,11 +104,11 @@ def _cleanup_params(attrs, check, **params):
             run = value
         elif key == "cpu":
             args[key] = float(value)
-        elif key == "memory":
+        elif key == "memory" or key == "disk":
             args[key] = int(value)
         elif key == "interactive" or key == "mounts" or key == "nets" \
                 or key == "security_groups" or key == "hints"\
-                or key == "auto_remove":
+                or key == "auto_remove" or key == "auto_heal":
             args[key] = value
         elif key == "restart_policy":
             args[key] = utils.check_restart_policy(value)
@@ -267,6 +267,11 @@ def port_update_security_groups(request):
     return {"port": port, "security_group": security_groups}
 
 
+def availability_zone_list(request):
+    list = zunclient(request).availability_zones.list()
+    return list
+
+
 def image_list(request, limit=None, marker=None, sort_key=None,
                sort_dir=None, detail=True):
     return zunclient(request).images.list(limit, marker, sort_key,
diff --git a/zun_ui/api/rest_api.py b/zun_ui/api/rest_api.py
index 98564f9..1796451 100644
--- a/zun_ui/api/rest_api.py
+++ b/zun_ui/api/rest_api.py
@@ -145,6 +145,22 @@ class Containers(generic.View):
             new_container.to_dict())
 
 
+@urls.register
+class AvailabilityZones(generic.View):
+    """API for Zun AvailabilityZones"""
+    url_regex = r'zun/availability_zones/$'
+
+    @rest_utils.ajax()
+    def get(self, request):
+        """Get a list of the Zun AvailabilityZones.
+
+        The returned result is an object with property 'items' and each
+        item under this is a Zun AvailabilityZones.
+        """
+        result = client.availability_zone_list(request)
+        return {'items': [i.to_dict() for i in result]}
+
+
 @urls.register
 class Images(generic.View):
     """API for Zun Images"""
diff --git a/zun_ui/static/dashboard/container/containers/actions/workflow/spec.help.html b/zun_ui/static/dashboard/container/containers/actions/workflow/spec.help.html
index 33c1ace..3c2ef05 100644
--- a/zun_ui/static/dashboard/container/containers/actions/workflow/spec.help.html
+++ b/zun_ui/static/dashboard/container/containers/actions/workflow/spec.help.html
@@ -1,10 +1,16 @@
 <dl>
-  <dt translate>CPU</dt>
-  <dd translate>The number of virtual cpus.</dd>
-  <dt translate>Memory</dt>
-  <dd translate>The container memory size in MiB.</dd>
   <dt translate>Runtime</dt>
   <dd translate>The runtime to use for this container. Default: "runc"</dd>
+  <dt translate>CPU</dt>
+  <dd translate>The number of virtual CPUs.</dd>
+  <dt translate>Memory</dt>
+  <dd translate>The container memory size in MiB.</dd>
+  <dt translate>Disk</dt>
+  <dd translate>The disk size in GiB for per container.</dd>
+  <dt translate>Availability Zone</dt>
+  <dd translate>The availability zone of the container.</dd>
   <dt translate>Exit Policy</dt>
   <dd translate>Policy to apply when a container exits.</dd>
+  <dt translate>Auto Heal</dt>
+  <dd translate>The flag of healing non-existent container in docker.</dd>
 </dl>
diff --git a/zun_ui/static/dashboard/container/containers/actions/workflow/workflow.service.js b/zun_ui/static/dashboard/container/containers/actions/workflow/workflow.service.js
index 35795df..0c73afa 100644
--- a/zun_ui/static/dashboard/container/containers/actions/workflow/workflow.service.js
+++ b/zun_ui/static/dashboard/container/containers/actions/workflow/workflow.service.js
@@ -59,6 +59,9 @@
         {value: "unless-stopped", name: gettext("Restart if stopped")},
         {value: "remove", name: gettext("Remove container")}
       ];
+      var availabilityZones = [
+        {value: "", name: gettext("Select availability zone.")}
+      ];
 
       // schema
       schema = {
@@ -108,6 +111,15 @@
             type: "number",
             minimum: 4
           },
+          disk: {
+            title: gettext("Disk"),
+            type: "number",
+            minimum: 0
+          },
+          availability_zone: {
+            title: gettext("Availability Zone"),
+            type: "string"
+          },
           exit_policy: {
             title: gettext("Exit Policy"),
             type: "string"
@@ -117,6 +129,10 @@
             type: "number",
             minimum: 0
           },
+          auto_heal: {
+            title: gettext("Enable auto heal"),
+            type: "boolean"
+          },
           // misc
           workdir: {
             title: gettext("Working Directory"),
@@ -255,6 +271,29 @@
                     }
                   ]
                 },
+                {
+                  type: "section",
+                  htmlClass: "col-xs-6",
+                  items: [
+                    {
+                      key: "disk",
+                      step: 1,
+                      placeholder: gettext("The disk size in GiB for per container.")
+                    }
+                  ]
+                },
+                {
+                  type: "section",
+                  htmlClass: "col-xs-6",
+                  items: [
+                    {
+                      key: "availability_zone",
+                      readonly: action === "update",
+                      type: "select",
+                      titleMap: availabilityZones
+                    }
+                  ]
+                },
                 {
                   type: "section",
                   htmlClass: "col-xs-6",
@@ -269,7 +308,7 @@
                         if (notOnFailure) {
                           model.restart_policy_max_retry = "";
                         }
-                        form[0].tabs[1].items[5].items[0].readonly = notOnFailure;
+                        form[0].tabs[1].items[7].items[0].readonly = notOnFailure;
                         // set auto_remove whether exit_policy is "remove".
                         // if exit_policy is set as "remove", clear restart_policy.
                         // otherwise, set restart_policy as same value as exit_policy.
@@ -293,6 +332,16 @@
                       readonly: true
                     }
                   ]
+                },
+                {
+                  type: "section",
+                  htmlClass: "col-xs-12",
+                  items: [
+                    {
+                      key: "auto_heal",
+                      readonly: action === "update"
+                    }
+                  ]
                 }
               ]
             },
@@ -462,10 +511,13 @@
         runtime: "",
         cpu: "",
         memory: "",
+        disks: "",
+        availability_zone: "",
         exit_policy: "",
         restart_policy: "",
         restart_policy_max_retry: "",
         auto_remove: false,
+        auto_heal: false,
         // mounts
         mounts: [],
         // networks
@@ -510,6 +562,7 @@
         getVolumes();
         getNetworks();
         securityGroup.query().then(onGetSecurityGroups);
+        zun.getZunAvailabilityZones().then(onGetZunServices);
       });
 
       // get container when action equals "update"
@@ -681,6 +734,15 @@
         return response;
       }
 
+      // get availability zones from zun services
+      function onGetZunServices(response) {
+        var azs = [];
+        response.data.items.forEach(function (service) {
+          azs.push({value: service.availability_zone, name: service.availability_zone});
+        });
+        push.apply(availabilityZones, azs);
+      }
+
       var config = {
         title: title,
         submitText: submitText,
diff --git a/zun_ui/static/dashboard/container/containers/containers.module.js b/zun_ui/static/dashboard/container/containers/containers.module.js
index e49c8ee..81792ac 100644
--- a/zun_ui/static/dashboard/container/containers/containers.module.js
+++ b/zun_ui/static/dashboard/container/containers/containers.module.js
@@ -144,9 +144,11 @@
   function containerProperties() {
     return {
       'addresses': { label: gettext('Addresses'), filters: ['noValue', 'json'] },
+      'auto_heal': { label: gettext('Auto Heal'), filters: ['yesno'] },
       'auto_remove': { label: gettext('Auto Remove'), filters: ['yesno'] },
       'command': { label: gettext('Command'), filters: ['noValue'] },
       'cpu': { label: gettext('CPU'), filters: ['noValue'] },
+      'disk': { label: gettext('Disk'), filters: ['gb', 'noValue'] },
       'environment': { label: gettext('Environment'), filters: ['noValue', 'json'] },
       'host': { label: gettext('Host'), filters: ['noValue'] },
       'hostname': { label: gettext('Hostname'), filters: ['noValue'] },
diff --git a/zun_ui/static/dashboard/container/containers/details/overview.html b/zun_ui/static/dashboard/container/containers/details/overview.html
index 38753de..1cff23a 100644
--- a/zun_ui/static/dashboard/container/containers/details/overview.html
+++ b/zun_ui/static/dashboard/container/containers/details/overview.html
@@ -19,7 +19,7 @@
         cls="dl-horizontal"
         item="ctrl.container"
         property-groups="[['image', 'image_driver', 'image_pull_policy', 'hostname', 'runtime',
-                           'cpu', 'memory', 'restart_policy', 'auto_remove',
+                           'cpu', 'memory', 'disk', 'restart_policy', 'auto_remove', 'auto_heal',
                            'addresses', 'ports', 'security_groups']]">
       </hz-resource-property-list>
     </div>
diff --git a/zun_ui/static/dashboard/container/zun.service.js b/zun_ui/static/dashboard/container/zun.service.js
index 51e5647..fe01f7a 100644
--- a/zun_ui/static/dashboard/container/zun.service.js
+++ b/zun_ui/static/dashboard/container/zun.service.js
@@ -26,6 +26,7 @@
 
   function ZunAPI(apiService, toastService, gettext) {
     var containersPath = '/api/zun/containers/';
+    var zunAvailabilityZonesPath = '/api/zun/availability_zones/';
     var imagesPath = '/api/zun/images/';
     var service = {
       createContainer: createContainer,
@@ -48,6 +49,7 @@
       attachNetwork: attachNetwork,
       detachNetwork: detachNetwork,
       updatePortSecurityGroup: updatePortSecurityGroup,
+      getZunAvailabilityZones: getZunAvailabilityZones,
       pullImage: pullImage,
       getImages: getImages
     };
@@ -177,6 +179,15 @@
         .error(error(msg));
     }
 
+    //////////////////////////////
+    // Zun AvailabilityZones //
+    //////////////////////////////
+
+    function getZunAvailabilityZones() {
+      var msg = gettext('Unable to retrieve the Zun Availability Zones.');
+      return apiService.get(zunAvailabilityZonesPath).error(error(msg));
+    }
+
     ////////////
     // Images //
     ////////////