From 0a96fde9b1d691268948091442c2f0075e81ab95 Mon Sep 17 00:00:00 2001 From: Ondrej Mular Date: Thu, 28 Jul 2016 15:21:18 +0200 Subject: [PATCH] web UI: add possibility to change order of resources in group --- pcsd/pcs.rb | 1 + pcsd/public/css/style.css | 10 +++ pcsd/public/js/nodes-ember.js | 167 ++++++++++++++++++++++++++++++++++++++---- pcsd/public/js/pcsd.js | 117 +++++++++++++++++------------ pcsd/remote.rb | 47 ++++++++---- pcsd/views/_dialogs.erb | 21 ++++++ pcsd/views/_resource.erb | 6 -- pcsd/views/main.erb | 51 +++++++++++-- 8 files changed, 334 insertions(+), 86 deletions(-) diff --git a/pcsd/pcs.rb b/pcsd/pcs.rb index ad54a75..1eb9e9e 100644 --- a/pcsd/pcs.rb +++ b/pcsd/pcs.rb @@ -1702,6 +1702,7 @@ def get_node_status(auth_user, cib_dom) 'constraint_colocation_set', 'sbd', 'ticket_constraints', + 'moving_resource_in_group', ] } diff --git a/pcsd/public/css/style.css b/pcsd/public/css/style.css index d41b164..0d744d5 100644 --- a/pcsd/public/css/style.css +++ b/pcsd/public/css/style.css @@ -848,3 +848,13 @@ table.args-table td.reg { .constraint-ticket-add-attribute { vertical-align: top; } + +.cursor-move { + cursor: move; +} + +.sortable-table td { + height: 1.5em; + line-height: 1.2em; + background: black; +} diff --git a/pcsd/public/js/nodes-ember.js b/pcsd/public/js/nodes-ember.js index efc0192..3d4fe79 100644 --- a/pcsd/public/js/nodes-ember.js +++ b/pcsd/public/js/nodes-ember.js @@ -52,6 +52,11 @@ Pcs = Ember.Application.createWithMixins({ this.get("available_features").indexOf("constraint_colocation_set") != -1 ); }.property("available_features"), + is_supported_moving_resource_in_group: function() { + return ( + this.get("available_features").indexOf("moving_resource_in_group") != -1 + ); + }.property("available_features"), is_sbd_running: false, is_sbd_enabled: false, is_sbd_enabled_or_running: function() { @@ -245,6 +250,154 @@ Pcs = Ember.Application.createWithMixins({ } }); +Pcs.GroupSelectorComponent = Ember.Component.extend({ + resource_id: null, + resource: function() { + var id = this.get("resource_id"); + if (id) { + var resource = Pcs.resourcesContainer.get_resource_by_id(id); + if (resource) { + return resource; + } + } + return null; + }.property("resource_id"), + resource_change: function() { + this._refresh_fn(); + this._update_resource_select_content(); + this._update_resource_select_value(); + }.observes("resource", "resource_id"), + group_list: [], + group_select_content: function() { + var list = []; + $.each(this.get("group_list"), function(_, group) { + list.push({ + name: group, + value: group + }); + }); + return list; + }.property("group_list"), + group_select_value: null, + group: function() { + var id = this.get("group_select_value"); + if (id) { + var group = Pcs.resourcesContainer.get_resource_by_id(id); + if (group) { + return group; + } + } + return null; + }.property("group_select_value"), + position_select_content: [ + { + name: "before", + value: "before" + }, + { + name: "after", + value: "after" + } + ], + position_select_value: null, + position_select_value_changed: function() { + }.observes("position_select_value"), + resource_select_content: [], + resource_select_value: null, + group_select_value_changed: function () { + this._update_resource_select_content(); + this._update_resource_select_value(); + }.observes("group_select_value"), + actions: { + refresh: function() { + this.set("group_list", Pcs.resourcesContainer.get("group_list")); + this._refresh_fn(); + this._update_resource_select_content(); + this._update_resource_select_value(); + } + }, + _refresh_fn: function() { + var id = this.get("resource_id"); + if (id) { + var resource = Pcs.resourcesContainer.get_resource_by_id(id); + if (resource) { + var parent = resource.get("parent"); + if (parent && parent.get("is_group")) { + this.set("group_select_value", parent.get("id")); + return; + } + } + } + this.set("group_select_value", null); + }, + _update_resource_select_content: function() { + var self = this; + var group = self.get("group"); + if (!group) { + self.set("resource_select_content", []); + return; + } + var list = []; + var resource_id; + $.each(group.get("members"), function(_, resource) { + resource_id = resource.get("id"); + if (resource_id != self.get("resource_id")) { + list.push({ + name: resource_id, + value: resource_id + }); + } + }); + self.set("resource_select_content", list); + }, + _update_resource_select_value: function() { + var self = this; + var group = self.get("group"); + var resource = self.get("resource"); + if (!group) { + self.set("resource_select_value", null); + return; + } + var resource_list = group.get("members"); + if ( + !resource || + !resource.get("parent") || + resource.get("parent").get("id") != group.get("id") + ) { + self.set("position_select_value", "after"); + self.set("resource_select_value", resource_list.slice(-1)[0].get("id")); + } else { + var index = resource_list.findIndex(function(item) { + return item.get("id") == resource.get("id"); + }); + if (index == 0) { + self.set("position_select_value", "before"); + self.set( + "resource_select_value", + (resource_list[1]) ? resource_list[1].get("id") : null // second + ); + } else if (index == -1) { + self.set("position_select_value", "after"); + self.set("resource_select_value", resource_list.slice(-1)[0].get("id")); + } else { + self.set("position_select_value", "after"); + self.set("resource_select_value", resource_list[index-1].get("id")); + } + } + }, + group_input_name: "group_id", + classNames: "group-selector", + init: function() { + this._super(); + if (this.get("resource_id")) { + this.set("group_list", Pcs.resourcesContainer.get("group_list")); + } + this._refresh_fn(); + this._update_resource_select_content(); + this._update_resource_select_value(); + } +}); + Pcs.ValueSelectorComponent = Ember.Component.extend({ tagName: 'select', attributeBindings: ['name'], @@ -682,20 +835,6 @@ Pcs.ResourceObj = Ember.Object.extend({ } return null; }.property('parent'), - group_selector: function() { - var self = this; - var cur_group = self.get('get_group_id'); - var html = ''; - return html; - }.property('group_list', 'get_group_id'), status: "unknown", class_type: null, // property to determine type of the resource resource_type: function() { // this property is just for displaying resource type in GUI diff --git a/pcsd/public/js/pcsd.js b/pcsd/public/js/pcsd.js index a646bed..82187ef 100644 --- a/pcsd/public/js/pcsd.js +++ b/pcsd/public/js/pcsd.js @@ -96,50 +96,77 @@ function select_menu(menu, item, initial) { } function create_group() { - var num_nodes = 0; - var node_names = ""; - $("#resource_list :checked").parent().parent().each(function (index,element) { - if (element.getAttribute("nodeID")) { - num_nodes++; - node_names += element.getAttribute("nodeID") + " " - } - }); - - if (num_nodes == 0) { + var resource_list = get_checked_ids_from_nodelist("resource_list"); + if (resource_list.length == 0) { alert("You must select at least one resource to add to a group"); return; } - - $("#resources_to_add_to_group").val(node_names); + var not_primitives = resource_list.filter(function(resource_id) { + return !Pcs.resourcesContainer.get_resource_by_id(resource_id).get( + "is_primitive" + ); + }); + if (not_primitives.length != 0) { + alert("Members of group have to be primitive resources. These resources" + + " are not primitives: " + not_primitives.join(", ")); + return; + } + var order_el = $("#new_group_resource_list tbody"); + order_el.empty(); + order_el.append(resource_list.map(function (item) { + return `${item}`; + })); + var order_obj = order_el.sortable(); + order_el.disableSelection(); $("#add_group").dialog({ title: 'Create Group', + width: 'auto', modal: true, resizable: false, - buttons: { - Cancel: function() { - $(this).dialog("close"); + buttons: [ + { + text: "Cancel", + click: function() { + $(this).dialog("close"); + } }, - "Create Group": function() { - var data = $('#add_group > form').serialize(); - var url = get_cluster_remote_url() + "add_group"; - ajax_wrapper({ - type: "POST", - url: url, - data: data, - success: function() { - Pcs.update(); - $("#add_group").dialog("close"); - }, - error: function (xhr, status, error) { - alert( - "Error creating group " - + ajax_simple_error(xhr, status, error) - ); - $("#add_group").dialog("close"); - } - }); + { + text: "Create Group", + id: "add_group_submit_btn", + click: function() { + var dialog_obj = $(this); + var submit_btn_obj = dialog_obj.parent().find( + "#add_group_submit_btn" + ); + submit_btn_obj.button("option", "disabled", true); + + ajax_wrapper({ + type: "POST", + url: get_cluster_remote_url() + "add_group", + data: { + resource_group: $( + '#add_group:visible input[name=resource_group]' + ).val(), + resources: order_obj.sortable( + "toArray", {attribute: "value"} + ).join(" ") + }, + success: function() { + submit_btn_obj.button("option", "disabled", false); + Pcs.update(); + dialog_obj.dialog("close"); + }, + error: function (xhr, status, error) { + alert( + "Error creating group " + + ajax_simple_error(xhr, status, error) + ); + submit_btn_obj.button("option", "disabled", false); + } + }); + } } - } + ] }); } @@ -2257,24 +2284,24 @@ function resource_ungroup(group_id) { }); } -function resource_change_group(resource_id, group_id) { +function resource_change_group(resource_id, form) { if (resource_id == null) { return; } show_loading_screen(); var resource_obj = Pcs.resourcesContainer.get_resource_by_id(resource_id); var data = { - resource_id: resource_id, - group_id: group_id + resource_id: resource_id }; + $.each($(form).serializeArray(), function(_, item) { + data[item.name] = item.value; + }); - if (resource_obj.get('parent')) { - if (resource_obj.get('parent').get('id') == group_id) { - return; - } - if (resource_obj.get('parent').get('class_type') == 'group') { - data['old_group_id'] = resource_obj.get('parent').get('id'); - } + if ( + resource_obj.get('parent') && + resource_obj.get('parent').get('class_type') == 'group' + ) { + data['old_group_id'] = resource_obj.get('parent').get('id'); } ajax_wrapper({ diff --git a/pcsd/remote.rb b/pcsd/remote.rb index 05a6d03..4844adf 100644 --- a/pcsd/remote.rb +++ b/pcsd/remote.rb @@ -1415,21 +1415,23 @@ def update_resource (params, request, auth_user) param_line = getParamList(params) if not params[:resource_id] - out, stderr, retval = run_cmd( - auth_user, - PCS, "resource", "create", params[:name], params[:resource_type], - *param_line - ) - if retval != 0 - return JSON.generate({"error" => "true", "stderr" => stderr, "stdout" => out}) - end + cmd = [PCS, "resource", "create", params[:name], params[:resource_type]] + cmd += param_line if params[:resource_group] and params[:resource_group] != "" - run_cmd( - auth_user, - PCS, "resource","group", "add", params[:resource_group], params[:name] + cmd += ['--group', params[:resource_group]] + if ( + ['before', 'after'].include?(params[:in_group_position]) and + params[:in_group_reference_resource_id] ) + cmd << "--#{params[:in_group_position]}" + cmd << params[:in_group_reference_resource_id] + end resource_group = params[:resource_group] end + out, stderr, retval = run_cmd(auth_user, *cmd) + if retval != 0 + return JSON.generate({"error" => "true", "stderr" => stderr, "stdout" => out}) + end if params[:resource_clone] and params[:resource_clone] != "" name = resource_group ? resource_group : params[:name] @@ -1461,10 +1463,18 @@ def update_resource (params, request, auth_user) ) end else - run_cmd( - auth_user, PCS, "resource", "group", "add", params[:resource_group], + cmd = [ + PCS, "resource", "group", "add", params[:resource_group], params[:resource_id] + ] + if ( + ['before', 'after'].include?(params[:in_group_position]) and + params[:in_group_reference_resource_id] ) + cmd << "--#{params[:in_group_position]}" + cmd << params[:in_group_reference_resource_id] + end + run_cmd(auth_user, *cmd) end end @@ -2098,10 +2108,17 @@ def resource_change_group(params, request, auth_user) end return 200 end - _, stderr, retval = run_cmd( - auth_user, + cmd = [ PCS, 'resource', 'group', 'add', params[:group_id], params[:resource_id] + ] + if ( + ['before', 'after'].include?(params[:in_group_position]) and + params[:in_group_reference_resource_id] ) + cmd << "--#{params[:in_group_position]}" + cmd << params[:in_group_reference_resource_id] + end + _, stderr, retval = run_cmd(auth_user, *cmd) if retval != 0 return [400, "Unable to add resource '#{params[:resource_id]}' to " + "group '#{params[:group_id]}': #{stderr.join('')}" diff --git a/pcsd/views/_dialogs.erb b/pcsd/views/_dialogs.erb index 46e7fdb..d18ac71 100644 --- a/pcsd/views/_dialogs.erb +++ b/pcsd/views/_dialogs.erb @@ -215,3 +215,24 @@ {{/if}} + + diff --git a/pcsd/views/_resource.erb b/pcsd/views/_resource.erb index a337160..ad2251c 100644 --- a/pcsd/views/_resource.erb +++ b/pcsd/views/_resource.erb @@ -116,10 +116,4 @@ table_id_suffix="_new" }} - <% end %> diff --git a/pcsd/views/main.erb b/pcsd/views/main.erb index 52c1900..1b21f92 100644 --- a/pcsd/views/main.erb +++ b/pcsd/views/main.erb @@ -237,7 +237,7 @@ Group: - {{{resource.group_selector}}} + {{group-selector resource_id=resource._id}} {{else}} @@ -245,7 +245,7 @@ Group: - {{{resource.group_selector}}} + {{group-selector resource_id=resource._id}} {{/if}} @@ -909,10 +909,9 @@ Use the 'Add' button to submit the form."> - {{value-selector - prompt="None" - content=groups - name="resource_group" + {{group-selector + group_list=Pcs.resourcesContainer.group_list + group_input_name="resource_group" }} @@ -1095,6 +1094,46 @@ Use the 'Add' button to submit the form."> + +