diff options
author | Chris Houseknecht <chouse@ansibleworks.com> | 2013-11-06 09:09:02 +0100 |
---|---|---|
committer | Chris Houseknecht <chouse@ansibleworks.com> | 2013-11-06 09:09:02 +0100 |
commit | 49a8de4efed8f0de230669be5bb0dd77567d3932 (patch) | |
tree | 34e609943ff91b197159bcac36ae1b5eb64f97be | |
parent | Jobs list on Job Templates detail page now closely matches the Jobs tab. Stat... (diff) | |
download | awx-49a8de4efed8f0de230669be5bb0dd77567d3932.tar.xz awx-49a8de4efed8f0de230669be5bb0dd77567d3932.zip |
AC-570 Added host enabled flag on Hosts page, allowing one-click editing for manually managed hosts. Fixed one more bug with prepending '*' to labels of required fileds.
-rw-r--r-- | awx/ui/static/js/controllers/Hosts.js | 67 | ||||
-rw-r--r-- | awx/ui/static/js/forms/Hosts.js | 25 | ||||
-rw-r--r-- | awx/ui/static/js/forms/InventoryHosts.js | 30 | ||||
-rw-r--r-- | awx/ui/static/js/helpers/Hosts.js | 36 | ||||
-rw-r--r-- | awx/ui/static/less/ansible-ui.less | 16 | ||||
-rw-r--r-- | awx/ui/static/lib/ansible/form-generator.js | 7 | ||||
-rw-r--r-- | awx/ui/static/lib/ansible/list-generator.js | 2 |
7 files changed, 130 insertions, 53 deletions
diff --git a/awx/ui/static/js/controllers/Hosts.js b/awx/ui/static/js/controllers/Hosts.js index 20e9b058e2..bb326d6de4 100644 --- a/awx/ui/static/js/controllers/Hosts.js +++ b/awx/ui/static/js/controllers/Hosts.js @@ -13,7 +13,7 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, GetBasePath, HostsList, HostsAdd, HostsEdit, HostsDelete, - HostsReload, BuildTree, EditHostGroups, InventoryHostsHelp, HelpDialog) + HostsReload, BuildTree, EditHostGroups, InventoryHostsHelp, HelpDialog, Wait) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -33,11 +33,11 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa // buildAllGroups emits from TreeSelector.js after the inventory object is ready if (scope.loadBreadCrumbsRemove) { - scope.loadBreadCrumbsRemove(); + scope.loadBreadCrumbsRemove(); } scope.loadBreadCrumbsRemove = scope.$on('buildAllGroups', function(e, inventory_name) { - LoadBreadCrumbs({ path: '/inventories/' + id, title: inventory_name }); - }); + LoadBreadCrumbs({ path: '/inventories/' + id, title: inventory_name }); + }); scope.filterHosts = function() { HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] }); @@ -75,6 +75,39 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa scope.allJobs = function(id) { $location.url('/jobs/?job_host_summaries__host=' + id); } + + scope.toggle_host_enabled = function(id, sources) { + var host; + var i; + if (!sources) { + // Host is not managed by an external source + Wait('start'); + for (i=0; i < scope.hosts.length; i++) { + if (scope.hosts[i].id == id) { + scope.hosts[i].enabled = (scope.hosts[i].enabled) ? false : true; + scope.hosts[i].enabled_flag = scope.hosts[i].enabled; + host = scope.hosts[i]; + break; + } + } + Rest.setUrl(GetBasePath('hosts') + id + '/'); + Rest.put(host) + .success( function(data, status, headers, config) { + Wait('stop'); + }) + .error( function(data, status, headers, config) { + scope.hosts[i].enabled = (scope.hosts[i].enabled) ? false : true; + scope.hosts[i].enabled_flag = scope.hosts[i].enabled; + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to update host. PUT returned status: ' + status }); + }); + } + else { + Alert('External Host', 'This host is part of an external inventory. It can only be enabled or disabled by the ' + + 'inventory sync process.', 'alert-info'); + } + } scope.allHostSummaries = function(id, name, inventory_id) { LoadBreadCrumbs({ path: '/hosts/' + id, title: name, altPath: '/inventories/' + inventory_id + '/hosts', @@ -118,15 +151,15 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa scope.group_id = group; scope.helpCount++; if (scope.group_id == null) { - scope.groupTitle = 'All Hosts'; - scope.hostAddHide = true; - scope.hostCreateHide = true; - scope.hostDeleteHide = true; + scope.groupTitle = 'All Hosts'; + scope.hostAddHide = true; + scope.hostCreateHide = true; + scope.hostDeleteHide = true; } else { - scope.hostAddHide = false; - scope.hostCreateHide = false; - scope.hostDeleteHide = false; + scope.hostAddHide = false; + scope.hostCreateHide = false; + scope.hostDeleteHide = false; } scope['hostDeleteDisabled'] = true; scope['hostDeleteDisabledClass'] = 'disabled'; @@ -135,11 +168,11 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa // Load the tree. See TreeSelector.js BuildTree({ - scope: scope, - inventory_id: id, - emit_on_select: 'refreshHost', - target_id: 'search-tree-container' - }); + scope: scope, + inventory_id: id, + emit_on_select: 'refreshHost', + target_id: 'search-tree-container' + }); } @@ -147,6 +180,6 @@ InventoryHosts.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'GetBasePath', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', - 'HostsReload', 'BuildTree', 'EditHostGroups', 'InventoryHostsHelp', 'HelpDialog' + 'HostsReload', 'BuildTree', 'EditHostGroups', 'InventoryHostsHelp', 'HelpDialog', 'Wait' ]; diff --git a/awx/ui/static/js/forms/Hosts.js b/awx/ui/static/js/forms/Hosts.js index 333c4c857f..378230e1b9 100644 --- a/awx/ui/static/js/forms/Hosts.js +++ b/awx/ui/static/js/forms/Hosts.js @@ -39,10 +39,22 @@ angular.module('HostFormDefinition', []) addRequired: false, editRequired: false }, - inventory: { - type: 'hidden', - includeOnEdit: true, - includeOnAdd: true + enabled: { + label: 'Enabled?', + type: 'radio', + addRequired: false, + editRequired: false, + options: [ + { label: 'Yes', value: 'true'}, + { label: 'No', value: 'false' } + ], + "default": "true", + awPopOver: "<p>Indicates if a host is available and should be included in running jobs.</p><p>For hosts that " + + "are part of an external inventory, this flag cannot be changed. It will be set by the inventory sync process.</p>", + dataTitle: 'Host Enabled', + dataPlacement: 'right', + dataContainer: '#form-modal .modal-content', + ngDisabled: 'has_inventory_sources == true' }, variables: { label: 'Variables', @@ -62,6 +74,11 @@ angular.module('HostFormDefinition', []) dataTitle: 'Host Variables', dataPlacement: 'right', dataContainer: '#form-modal .modal-content' + }, + inventory: { + type: 'hidden', + includeOnEdit: true, + includeOnAdd: true } }, diff --git a/awx/ui/static/js/forms/InventoryHosts.js b/awx/ui/static/js/forms/InventoryHosts.js index 419e2de29b..0bf0a10fdf 100644 --- a/awx/ui/static/js/forms/InventoryHosts.js +++ b/awx/ui/static/js/forms/InventoryHosts.js @@ -34,19 +34,16 @@ angular.module('InventoryHostsFormDefinition', []) searchable: false, nosort: true }, - /*inventory_sources: { - label: 'External<br>Source?', - ngHref: "\{\{ host.has_inv_source_link \}\}", - badgeNgHref: "\{\{ host.has_inv_source_link \}\}", - badgeIcon: "\{\{ 'icon-cloud-' + host.has_inventory_sources \}\}", + enabled_flag: { + label: 'Enabled', + badgeIcon: "\{\{ 'icon-enabled-' + host.enabled \}\}", badgePlacement: 'left', - badgeToolTip: "\{\{ host.has_inv_source_tip \}\}", - awToolTip: "\{\{ host.has_inv_source_tip \}\}", - dataPlacement: 'top', - badgeTipPlacement: 'top', + badgeToolTip: "\{\{ host.enabledToolTip \}\}", + badgeTipPlacement: "top", + ngClick: "toggle_host_enabled(\{\{ host.id \}\}, \{\{ host.has_inventory_sources \}\})", searchable: false, - nosort: true - },*/ + showValue: false + }, groups: { label: 'Groups', searchable: true, @@ -54,6 +51,13 @@ angular.module('InventoryHostsFormDefinition', []) sourceField: 'name', nosort: true }, + enabled: { + label: 'Disabled?', + searchSingleValue: true, + searchType: 'boolean', + searchValue: 'false', + searchOnly: true + }, has_active_failures: { label: 'Has failed jobs?', searchSingleValue: true, @@ -108,7 +112,7 @@ angular.module('InventoryHostsFormDefinition', []) type: 'DropDown', label: 'Jobs', icon: 'icon-zoom-in', - "class": "btn-default btn-sm", + "class": "btn-default btn-xs", options: [ { ngClick: "allJobs(\{\{ host.id \}\})", label: 'All', ngShow: 'host.last_job' }, { ngClick: "allHostSummaries(\{\{ host.id \}\},'\{\{ host.name \}\}', \{\{ inventory_id \}\})", label: 'All summaries', @@ -125,7 +129,7 @@ angular.module('InventoryHostsFormDefinition', []) "delete": { ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')", icon: 'icon-trash', - "class": 'btn-sm btn-danger', + "class": 'btn-xs btn-danger', awToolTip: 'Delete host' } } diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index c79e5edb6c..529f29ebf2 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -155,6 +155,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H data['variables'] = JSON.stringify(json_data, undefined, '\t'); } + data['enabled'] = (scope['enabled'] == "true") ? true : false; + Rest.setUrl(defaultUrl); Rest.post(data) .success( function(data, status, headers, config) { @@ -261,6 +263,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; } } + scope['enabled'] = (data['enabled']) ? "true" : "false"; + master['enabled'] = scope['enabled']; scope.variable_url = data.related.variable_data; scope.$emit('hostLoaded'); }) @@ -309,6 +313,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H data['variables'] = JSON.stringify(json_data, undefined, '\t'); } + data['enabled'] = (scope['enabled'] == "true") ? true : false; + Rest.setUrl(defaultUrl); Rest.put(data) .success( function(data, status, headers, config) { @@ -430,22 +436,10 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H } } scope.hosts[i].groups = scope.hosts[i].groups.replace(/\, $/,''); - - if (scope.hosts[i].has_inventory_sources) { - scope.hosts[i].inventory_sources = 'yes'; - scope.hosts[i].has_inv_source_link = '/#/inventories/' + scope['inventory_id'] + '/groups/?has_external_source=true'; - scope.hosts[i].has_inv_source_tip = 'Has an external source. Click to view inventory source details.'; - } - else { - scope.hosts[i].inventory_sources = 'no'; - scope.hosts[i].has_inv_source_link = '/#/inventories/' + scope['inventory_id'] + '/groups'; - scope.hosts[i].has_inv_source_tip = 'Has no external source.'; - } - } - - // Add the value displayed in Job Status column + for (var i=0; i < scope.hosts.length; i++) { + // Add the value displayed in Job Status column scope.hosts[i].activeFailuresLink = '/#/hosts/' + scope.hosts[i].id + '/job_host_summaries/?inventory=' + scope['inventory_id'] + '&host_name=' + escape(scope.hosts[i].name); if (scope.hosts[i].has_active_failures == true) { @@ -460,7 +454,19 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H else if (scope.hosts[i].has_active_failures == false && scope.hosts[i].last_job !== null) { scope.hosts[i].badgeToolTip = "Most recent job successful. Click to view jobs."; scope.hosts[i].active_failures = 'success'; - } + } + + scope.hosts[i].enabled_flag = scope.hosts[i].enabled; + if (scope.hosts[i].has_inventory_sources) { + // Inventory sync managed, so not clickable + scope.hosts[i].enabledToolTip = (scope.hosts[i].enabled) ? 'Ready! Availabe to running jobs.' : + 'Out to lunch! This host is not available to running jobs.'; + } + else { + // Clickable + scope.hosts[i].enabledToolTip = (scope.hosts[i].enabled) ? 'Ready! Available to running jobs. Click to toggle.' : + 'Out to lunch! Host not available to running jobs. Click to toggle.'; + } } if (group_id == null || group_id == undefined) { diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index ad0279cf66..3cfa4f9ec0 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -814,6 +814,22 @@ input[type="checkbox"].checkbox-no-label { color: @red; } + .icon-enabled-true:before { + content: "\f046"; + } + + .icon-enabled-true { + color: @green; + + } + .icon-enabled-false:before { + content: "\f096"; + } + + .icon-enabled-false{ + color: @red; + } + /* Inventory cloud sourced? indicator */ .icon-cloud-true:before { content: "\f111"; diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index 213725d970..98dceb4a04 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -104,16 +104,16 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) // Prepend an asterisk to required field label $('.form-control[required], input[type="radio"][required]').each(function() { if ( Empty($(this).attr('aw-required-when')) ) { - var label = $(this).parent().parent().find('label'); + var label = $(this).parent().parent().find('label').first(); if ($(this).attr('type') == 'radio') { - label = $(this).parent().parent().parent().find('label'); + label = $(this).parent().parent().parent().find('label').first(); } if (label.length > 0) { var span = label.children('span'); if (span.length > 0 && !span.first().hasClass('prepend-asterisk')) { span.first().addClass('prepend-asterisk'); } - else if (!label.first().hasClass('prepend-asterisk')) { + else if (span.length <= 0 && !label.first().hasClass('prepend-asterisk')) { label.first().addClass('prepend-asterisk'); } } @@ -817,6 +817,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) html += (field.readonly) ? "disabled " : ""; html += (options.mode == 'edit' && field.editRequired) ? "required " : ""; html += (options.mode == 'add' && field.addRequired) ? "required " : ""; + html += (field.ngDisabled) ? this.attr(field,'ngDisabled') : ""; html += " > " + field.options[i].label + "\n"; html += "</label>\n"; } diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 41549bd485..54f700b7f9 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -373,7 +373,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) if (options.mode == 'select' && (options.selectButton == undefined || options.selectButton == true)) { html += "<div class=\"navigation-buttons\">\n"; - html += " <button class=\"btn btn-small btn-primary pull-right\" aw-tool-tip=\"Complete your selection\" " + + html += " <button class=\"btn btn-sm btn-primary pull-right\" aw-tool-tip=\"Complete your selection\" " + "ng-click=\"finishSelection()\" ng-disabled=\"selected.length == 0\"><i class=\"icon-check\"></i> Select</button>\n"; html += "</div>\n"; } |