summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Houseknecht <chouse@ansibleworks.com>2013-11-06 09:09:02 +0100
committerChris Houseknecht <chouse@ansibleworks.com>2013-11-06 09:09:02 +0100
commit49a8de4efed8f0de230669be5bb0dd77567d3932 (patch)
tree34e609943ff91b197159bcac36ae1b5eb64f97be
parentJobs list on Job Templates detail page now closely matches the Jobs tab. Stat... (diff)
downloadawx-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.js67
-rw-r--r--awx/ui/static/js/forms/Hosts.js25
-rw-r--r--awx/ui/static/js/forms/InventoryHosts.js30
-rw-r--r--awx/ui/static/js/helpers/Hosts.js36
-rw-r--r--awx/ui/static/less/ansible-ui.less16
-rw-r--r--awx/ui/static/lib/ansible/form-generator.js7
-rw-r--r--awx/ui/static/lib/ansible/list-generator.js2
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";
}