Blob Blame History Raw
From cf1c95354a9db8b81712d7b98d0cc55e777e0516 Mon Sep 17 00:00:00 2001
From: Ondrej Mular <omular@redhat.com>
Date: Thu, 4 Aug 2016 00:59:11 +0200
Subject: [PATCH] web UI: add support for unmanaged resources

---
 pcsd/cluster_entity.rb        | 13 ++++++++--
 pcsd/pcs.rb                   |  1 +
 pcsd/public/js/nodes-ember.js | 22 +++++++++++++----
 pcsd/public/js/pcsd.js        | 52 ++++++++++++++++++++++++++++++++++++++++
 pcsd/remote.rb                | 55 +++++++++++++++++++++++++++++++++++++++----
 pcsd/views/main.erb           | 26 ++++++++++++++++++++
 6 files changed, 158 insertions(+), 11 deletions(-)

diff --git a/pcsd/cluster_entity.rb b/pcsd/cluster_entity.rb
index fa56fe2..7216626 100644
--- a/pcsd/cluster_entity.rb
+++ b/pcsd/cluster_entity.rb
@@ -332,7 +332,11 @@ module ClusterEntity
       :unknown => {
         :val => 6,
         :str => 'unknown'
-      }
+      },
+      :unmanaged => {
+        :val => 7,
+        :str => 'unmanaged'
+      },
     }
 
     def initialize(status=:unknown)
@@ -532,8 +536,11 @@ module ClusterEntity
     def get_status
       running = 0
       failed = 0
+      unmanaged = 0
       @crm_status.each do |s|
-        if s.active
+        if !s.managed
+          unmanaged += 1
+        elsif s.active
           running += 1
         elsif s.failed
           failed += 1
@@ -542,6 +549,8 @@ module ClusterEntity
 
       if disabled?
         status = ClusterEntity::ResourceStatus.new(:disabled)
+      elsif unmanaged >0
+        status = ClusterEntity::ResourceStatus.new(:unmanaged)
       elsif running > 0
         status = ClusterEntity::ResourceStatus.new(:running)
       elsif failed > 0 or @error_list.length > 0
diff --git a/pcsd/pcs.rb b/pcsd/pcs.rb
index 1eb9e9e..553a20c 100644
--- a/pcsd/pcs.rb
+++ b/pcsd/pcs.rb
@@ -1703,6 +1703,7 @@ def get_node_status(auth_user, cib_dom)
         'sbd',
         'ticket_constraints',
         'moving_resource_in_group',
+        'unmanaged_resource',
       ]
   }
 
diff --git a/pcsd/public/js/nodes-ember.js b/pcsd/public/js/nodes-ember.js
index 3d4fe79..c51a341 100644
--- a/pcsd/public/js/nodes-ember.js
+++ b/pcsd/public/js/nodes-ember.js
@@ -57,6 +57,9 @@ Pcs = Ember.Application.createWithMixins({
       this.get("available_features").indexOf("moving_resource_in_group") != -1
     );
   }.property("available_features"),
+  is_supported_unmanaged_resource: function() {
+    return (this.get("available_features").indexOf("unmanaged_resource") != -1);
+  }.property("available_features"),
   is_sbd_running: false,
   is_sbd_enabled: false,
   is_sbd_enabled_or_running: function() {
@@ -869,9 +872,17 @@ Pcs.ResourceObj = Ember.Object.extend({
     return '<span style="' + this.get('status_style') + '">' + this.get('status') + '</span>';
   }.property("status_style", "disabled"),
   status_class: function() {
-    var show = ((Pcs.clusterController.get("show_all_resources"))? "" : "hidden ");
-    return ((this.get("status_val") == get_status_value("ok") || this.status == "disabled") ? show + "default-hidden" : "");
-  }.property("status_val"),
+    if (
+      this.get("status_val") == get_status_value("ok") ||
+      ["disabled", "unmanaged"].indexOf(this.get("status")) != -1
+    ) {
+      return (
+        Pcs.clusterController.get("show_all_resources") ? "" : "hidden "
+        ) + "default-hidden";
+    } else {
+      return "";
+    }
+  }.property("status_val", "status"),
   status_class_fence: function() {
     var show = ((Pcs.clusterController.get("show_all_fence"))? "" : "hidden ");
     return ((this.get("status_val") == get_status_value("ok")) ? show + "default-hidden" : "");
@@ -1681,8 +1692,9 @@ Pcs.Cluster = Ember.Object.extend({
     var num = 0;
     $.each(this.get(type), function(key, value) {
       if (value.get("status_val") < get_status_value("ok") &&
-        value.status != "disabled" && value.status != "standby" &&
-        value.status != "maintenance"
+        [
+          "unmanaged", "disabled", "standby", "maintenance"
+        ].indexOf(value.status) == -1
       ) {
         num++;
       }
diff --git a/pcsd/public/js/pcsd.js b/pcsd/public/js/pcsd.js
index 82187ef..56219d4 100644
--- a/pcsd/public/js/pcsd.js
+++ b/pcsd/public/js/pcsd.js
@@ -1333,6 +1333,9 @@ function remove_resource(ids, force) {
           message += "\n\n" + xhr.responseText.replace(
             "--force", "'Enforce removal'"
           );
+          alert(message);
+          $("#verify_remove_submit_btn").button("option", "disabled", false);
+          return;
         }
       }
       alert(message);
@@ -1957,6 +1960,7 @@ function get_status_value(status) {
     maintenance: 2,
     "partially running": 2,
     disabled: 3,
+    unmanaged: 3,
     unknown: 4,
     ok: 5,
     running: 5,
@@ -2987,3 +2991,51 @@ function sbd_status_dialog() {
     buttons: buttonsOpts
   });
 }
+
+function unmanage_resource(resource_id) {
+  if (!resource_id) {
+    return;
+  }
+  fade_in_out("#resource_unmanage_link");
+  ajax_wrapper({
+    type: 'POST',
+    url: get_cluster_remote_url() + "unmanage_resource",
+    data: {
+      resource_list_json: JSON.stringify([resource_id]),
+    },
+    timeout: pcs_timeout,
+    complete: function() {
+      Pcs.update();
+    },
+    error: function (xhr, status, error) {
+      alert(
+        `Unable to unmanage '${resource_id}': ` +
+        ajax_simple_error(xhr, status, error)
+      );
+    },
+  });
+}
+
+function manage_resource(resource_id) {
+  if (!resource_id) {
+    return;
+  }
+  fade_in_out("#resource_manage_link");
+  ajax_wrapper({
+    type: 'POST',
+    url: get_cluster_remote_url() + "manage_resource",
+    data: {
+      resource_list_json: JSON.stringify([resource_id]),
+    },
+    timeout: pcs_timeout,
+    complete: function() {
+      Pcs.update();
+    },
+    error: function (xhr, status, error) {
+      alert(
+        `Unable to manage '${resource_id}': ` +
+        ajax_simple_error(xhr, status, error)
+      );
+    }
+  });
+}
diff --git a/pcsd/remote.rb b/pcsd/remote.rb
index 4844adf..ebf425c 100644
--- a/pcsd/remote.rb
+++ b/pcsd/remote.rb
@@ -116,7 +116,9 @@ def remote(params, request, auth_user)
       :set_resource_utilization => method(:set_resource_utilization),
       :set_node_utilization => method(:set_node_utilization),
       :get_resource_agent_metadata => method(:get_resource_agent_metadata),
-      :get_fence_agent_metadata => method(:get_fence_agent_metadata)
+      :get_fence_agent_metadata => method(:get_fence_agent_metadata),
+      :manage_resource => method(:manage_resource),
+      :unmanage_resource => method(:unmanage_resource),
   }
 
   command = params[:command].to_sym
@@ -1575,10 +1577,10 @@ def remove_resource(params, request, auth_user)
       end
       cmd = [PCS, '-f', tmp_file.path, 'resource', 'disable']
       resource_list.each { |resource|
-        _, err, retval = run_cmd(user, *(cmd + [resource]))
+        out, err, retval = run_cmd(user, *(cmd + [resource]))
         if retval != 0
           unless (
-            err.join('').index('unable to find a resource') != -1 and
+            (out + err).join('').include?(' does not exist.') and
             no_error_if_not_exists
           )
             errors += "Unable to stop resource '#{resource}': #{err.join('')}"
@@ -1613,7 +1615,10 @@ def remove_resource(params, request, auth_user)
     end
     out, err, retval = run_cmd(auth_user, *cmd)
     if retval != 0
-      unless out.index(' does not exist.') != -1 and no_error_if_not_exists
+      unless (
+        (out + err).join('').include?(' does not exist.') and
+        no_error_if_not_exists
+      )
         errors += err.join(' ').strip + "\n"
       end
     end
@@ -2630,3 +2635,45 @@ def qdevice_client_start(param, request, auth_user)
     return [400, msg]
   end
 end
+
+def manage_resource(param, request, auth_user)
+  unless allowed_for_local_cluster(auth_user, Permissions::WRITE)
+    return 403, 'Permission denied'
+  end
+  unless param[:resource_list_json]
+    return [400, "Required parameter 'resource_list_json' is missing."]
+  end
+  begin
+    resource_list = JSON.parse(param[:resource_list_json])
+    _, err, retval = run_cmd(
+      auth_user, PCS, 'resource', 'manage', *resource_list
+    )
+    if retval != 0
+      return [400, err.join('')]
+    end
+    return [200, '']
+  rescue JSON::ParserError
+    return [400, 'Invalid input data format']
+  end
+end
+
+def unmanage_resource(param, request, auth_user)
+  unless allowed_for_local_cluster(auth_user, Permissions::WRITE)
+    return 403, 'Permission denied'
+  end
+  unless param[:resource_list_json]
+    return [400, "Required parameter 'resource_list_json' is missing."]
+  end
+  begin
+    resource_list = JSON.parse(param[:resource_list_json])
+    _, err, retval = run_cmd(
+      auth_user, PCS, 'resource', 'unmanage', *resource_list
+    )
+    if retval != 0
+      return [400, err.join('')]
+    end
+    return [200, '']
+  rescue JSON::ParserError
+    return [400, 'Invalid input data format']
+  end
+end
diff --git a/pcsd/views/main.erb b/pcsd/views/main.erb
index 1b21f92..64fe560 100644
--- a/pcsd/views/main.erb
+++ b/pcsd/views/main.erb
@@ -160,6 +160,7 @@
       </table>
     </div>
     <div id="node_options_buttons">
+    <div>
     {{#if resource.stonith}}
       <div class="xdark sprites" style="float: left"></div>
       <div id="stonith_delete_link" class="link" onclick="verify_remove_fence_devices(curStonith());">Remove</div>
@@ -174,7 +175,32 @@
       <div id="resource_cleanup_link" class="link" onclick="cleanup_resource();">Cleanup</div>
       <div class="xdark sprites" style="float: left"></div>
       <div id="resource_delete_link" class="link" onclick="verify_remove_resources(curResource());">Remove</div>
+      </div>
+      <div>
+      {{#if Pcs.is_supported_unmanaged_resource}}
+        <div>
+        <div class="checkdark sprites" style="float: left"></div>
+        <div
+          id="resource_manage_link"
+          class="link"
+          onclick="manage_resource(curResource());"
+        >
+          Manage
+        </div>
+        </div>
+        <div>
+        <div class="cancel sprites" style="float: left"></div>
+        <div
+          id="resource_unmanage_link"
+          class="link"
+          onclick="unmanage_resource(curResource());"
+        >
+          Unmanage
+        </div>
+        </div>
+      {{/if}}
     {{/if}}
+    </div>
       <!--
       <div class="move sprites" style="float: left"></div>
       <div id="resource_move_link" class="link">Move</div>
-- 
1.8.3.1