diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index d24dae8a2c..8498859bda 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -29,6 +29,8 @@ angular.module('ansible', [ 'InventoriesListDefinition', 'InventoryFormDefinition', 'InventoryHelper', + 'InventoryHostsFormDefinition', + 'InventoryGroupsFormDefinition', 'AWFilters', 'HostFormDefinition', 'HostListDefinition', @@ -115,6 +117,12 @@ angular.module('ansible', [ when('/inventories/:id', { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesEdit }). + when('/inventories/:inventory_id/hosts', + { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoryHosts }). + + when('/inventories/:inventory_id/groups', + { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoryGroups }). + when('/organizations', { templateUrl: urlPrefix + 'partials/organizations.html', controller: OrganizationsList }). diff --git a/awx/ui/static/js/controllers/Groups.js b/awx/ui/static/js/controllers/Groups.js new file mode 100644 index 0000000000..db67718449 --- /dev/null +++ b/awx/ui/static/js/controllers/Groups.js @@ -0,0 +1,194 @@ +/************************************ + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * Groups.js + * + * Controller functions for the Groups model. + * + */ + +function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryGroupsForm, + GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, + RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, + OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory, + GroupsDelete, HostsList, HostsAdd, HostsEdit, HostsDelete, RefreshGroupName, ParseTypeChange, + HostsReload, EditInventory, RefreshTree, LoadSearchTree, EditHostGroups) +{ + ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior + //scope. + + var generator = GenerateForm; + var form = InventoryGroupsForm; + var defaultUrl=GetBasePath('inventory'); + var scope = generator.inject(form, { mode: 'edit', related: true, buildTree: true }); + var base = $location.path().replace(/^\//,'').split('/')[0]; + var id = $routeParams.inventory_id; + + scope['inventory_id'] = id; + + // Retrieve each related sets and any lookups + if (scope.inventoryLoadedRemove) { + scope.inventoryLoadedRemove(); + } + scope.inventoryLoadedRemove = scope.$on('inventoryLoaded', function() { + LoadBreadCrumbs({ path: '/inventories/' + id, title: scope.inventory_name }); + TreeInit(scope.TreeParams); + if (!scope.$$phase) { + scope.$digest(); + } + }); + + LoadInventory({ scope: scope, doPostSteps: true }); + + scope.treeController = function($node) { + + var nodeType = $($node).attr('type'); + if (nodeType == 'inventory') { + return { + editInventory: { + label: 'Inventory Properties', + action: function(obj) { + scope.group_id = null; + if (!scope.$$phase) { + scope.$digest(); + } + EditInventory({ scope: scope, "inventory_id": id }); + }, + separator_after: true + }, + addGroup: { + label: 'Create New Group', + action: function(obj) { + scope.group_id = null; + if (!scope.$$phase) { + scope.$digest(); + } + GroupsAdd({ "inventory_id": id, group_id: null }); + } + } + } + } + else { + return { + edit: { + label: 'Group Properties', + action: function(obj) { + scope.group_id = $(obj).attr('group_id'); + if (!scope.$$phase) { + scope.$digest(); + } + GroupsEdit({ "inventory_id": id, group_id: $(obj).attr('group_id') }); + }, + separator_after: true + }, + + addGroup: { + label: 'Add Existing Group', + action: function(obj) { + scope.group_id = $(obj).attr('group_id'); + if (!scope.$$phase) { + scope.$digest(); + } + GroupsList({ "inventory_id": id, group_id: $(obj).attr('group_id') }); + } + }, + + createGroup: { + label: 'Create New Group', + action: function(obj) { + scope.group_id = $(obj).attr('group_id'); + if (!scope.$$phase) { + scope.$digest(); + } + GroupsAdd({ "inventory_id": id, group_id: $(obj).attr('group_id') }); + } + }, + + "delete": { + label: 'Delete Group', + action: function(obj) { + scope.group_id = $(obj).attr('group_id'); + if (!scope.$$phase) { + scope.$digest(); + } + GroupsDelete({ scope: scope, "inventory_id": id, group_id: $(obj).attr('group_id') }); + } + } + } + } + } + + scope.$on('NodeSelect', function(e, n) { + + // Respond to user clicking on a tree node + + var node = $('li[id="' + n.attr.id + '"]'); + var type = node.attr('type'); + var url; + + scope['selectedNode'] = node; + scope['selectedNodeName'] = node.attr('name'); + + $('#tree-view').jstree('open_node',node); + + if (type == 'group') { + url = node.attr('all'); + scope.groupAddHide = false; + scope.groupCreateHide = false; + scope.groupEditHide = false; + scope.inventoryEditHide = true; + scope.groupDeleteHide = false; + scope.createButtonShow = true; + scope.group_id = node.attr('group_id'); + //scope.groupName = n.data; + //scope.groupTitle = '

' + n.data + '

'; + //scope.groupTitle += (node.attr('description')) ? '

' + node.attr('description') + '

' : ''; + } + else if (type == 'inventory') { + url = node.attr('hosts'); + scope.groupAddHide = true; + scope.groupCreateHide = false; + scope.groupEditHide =true; + scope.inventoryEditHide=false; + scope.groupDeleteHide = true; + scope.createButtonShow = false; + //scope.groupName = 'All Hosts'; + //scope.groupTitle = '

All Hosts

'; + scope.group_id = null; + } + + if (!scope.$$phase) { + scope.$digest(); + } + }); + + scope.addGroup = function() { + GroupsList({ "inventory_id": id, group_id: scope.group_id }); + } + + scope.createGroup = function() { + GroupsAdd({ "inventory_id": id, group_id: scope.group_id }); + } + + scope.editGroup = function() { + GroupsEdit({ "inventory_id": id, group_id: scope.group_id }); + } + + scope.editInventory = function() { + EditInventory({ scope: scope, inventory_id: id }); + } + + scope.deleteGroup = function() { + GroupsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id }); + } + +} + +InventoryGroups.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryGroupsForm', + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', + 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', + 'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory', + 'GroupsDelete', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', 'RefreshGroupName', + 'ParseTypeChange', 'HostsReload', 'EditInventory', 'RefreshTree', 'LoadSearchTree', 'EditHostGroups' + ]; + \ No newline at end of file diff --git a/awx/ui/static/js/controllers/Hosts.js b/awx/ui/static/js/controllers/Hosts.js new file mode 100644 index 0000000000..86735dcf98 --- /dev/null +++ b/awx/ui/static/js/controllers/Hosts.js @@ -0,0 +1,124 @@ +/************************************ + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * Hosts.js + * + * Controller functions for the Hosts model. + * + */ + +'use strict'; + +function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryHostsForm, + GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, + RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, + GetBasePath, HostsList, HostsAdd, HostsEdit, HostsDelete, + HostsReload, LoadSearchTree, EditHostGroups) +{ + ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior + //scope. + + var generator = GenerateForm; + var form = InventoryHostsForm; + var defaultUrl=GetBasePath('inventory'); + var scope = generator.inject(form, {mode: 'edit', related: true, buildTree: true}); + var base = $location.path().replace(/^\//,'').split('/')[0]; + var id = $routeParams.inventory_id; + + scope['inventory_id'] = id; + scope['hostAddHide'] = true; + scope['hostCreateHide'] = true; + scope['hosts'] = null; + + if (scope.loadBreadCrumbsRemove) { + scope.loadBreadCrumbsRemove(); + } + scope.loadBreadCrumbsRemove = scope.$on('hostTabInit', function(e, inventory_name) { + LoadBreadCrumbs({ path: '/inventories/' + id, title: inventory_name }); + }); + + LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] }); + + // Add the selected flag to the hosts set. + if (scope.relatedHostsRemove) { + scope.relatedHostsRemove(); + } + scope.relatedHostsRemove = scope.$on('relatedhosts', function() { + scope.toggleAllFlag = false; + for (var i=0; i < scope.hosts.length; i++) { + scope.hosts[i].selected = 0; + } + }); + + scope.filterHosts = function() { + HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] }); + } + + scope.addHost = function() { + HostsList({ scope: scope, "inventory_id": id, group_id: scope.group_id }); + } + + scope.createHost = function() { + HostsAdd({ scope: scope, "inventory_id": id, group_id: scope.group_id }); + } + + scope.editHost = function(host_id, host_name) { + HostsEdit({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name }); + } + + scope.editHostGroups = function(host_id) { + EditHostGroups({ inventory_id: id, host_id: host_id }); + } + + scope.deleteHost = function(host_id, host_name) { + HostsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name, + request: 'delete' }); + } + + scope.viewJobs = function(last_job) { + $location.url('/jobs/?id__int=' + last_job ); + } + + scope.viewLastEvents = function(host_id, last_job, host_name, last_job_name) { + // Choose View-> Latest job events + LoadBreadCrumbs({ path: '/jobs/' + last_job, title: last_job_name }); + $location.url('/jobs/' + last_job + '/job_events/?host=' + escape(host_name)); + } + + scope.viewLastSummary = function(host_id, last_job, host_name, last_job_name) { + // Choose View-> Latest job events + LoadBreadCrumbs({ path: '/jobs/' + last_job, title: last_job_name }); + $location.url('/jobs/' + last_job + '/job_host_summaries/?host=' + escape(host_name)); + } + + // Respond to the scope.$emit from awTree directive + if (scope.refreshHostRemove) { + scope.refreshHostRemove(); + } + scope.refreshHostRemove = scope.$on('refreshHost', function(e, group, title) { + scope.groupTitle = title; + scope.group_id = group; + if (scope.group_id == null) { + scope.hostAddHide = true; + scope.hostCreateHide = true; + scope.hostDeleteHide = true; + } + else { + scope.hostAddHide = false; + scope.hostCreateHide = false; + scope.hostDeleteHide = false; + } + scope['hostDeleteDisabled'] = true; + scope['hostDeleteDisabledClass'] = 'disabled'; + HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: group }); + }); + +} + +InventoryHosts.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryHostsForm', + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', + 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', + 'GetBasePath', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', + 'HostsReload', 'LoadSearchTree', 'EditHostGroups' + ]; + diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 7e211ca337..ded335d844 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -81,6 +81,14 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res scope.viewFailedJobs = function(id) { $location.url('/jobs/?inventory__int=' + id + '&status=failed&order_by=status'); } + + scope.editHosts = function(id) { + $location.url('/inventories/' + id + '/hosts'); + } + + scope.editGroups = function(id) { + $location.url('/inventories/' + id + '/groups'); + } } InventoriesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'InventoryList', 'GenerateList', @@ -191,12 +199,10 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo 'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit', 'GetBasePath', 'ParseTypeChange']; -function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, +function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, - RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, - OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory, - GroupsDelete, HostsList, HostsAdd, HostsEdit, HostsDelete, RefreshGroupName, ParseTypeChange, - HostsReload, EditInventory, RefreshTree, LoadSearchTree, EditHostGroups) + RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, OrganizationList, + GetBasePath, LoadInventory, ParseTypeChange, EditInventory, SaveInventory, PostLoadInventory) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -204,79 +210,25 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP var generator = GenerateForm; var form = InventoryForm; var defaultUrl=GetBasePath('inventory'); - var scope = generator.inject(form, {mode: 'edit', related: true, buildTree: true}); + var scope = generator.inject(form, { mode: 'edit', related: true }); var base = $location.path().replace(/^\//,'').split('/')[0]; var id = $routeParams.id; - - ParseTypeChange(scope,'inventory_variables', 'inventoryParseType'); - - $('#inventory-tabs a:first').tab('show'); //activate the hosts tab scope['inventoryParseType'] = 'yaml'; scope['inventory_id'] = id; - scope['inventoryFailureFilter'] = false; - scope['hostDeleteDisabled'] = true; - scope['hostDeleteDisabledClass'] = 'disabled'; - + + ParseTypeChange(scope,'inventory_variables', 'inventoryParseType'); + // Retrieve each related sets and any lookups if (scope.inventoryLoadedRemove) { scope.inventoryLoadedRemove(); } scope.inventoryLoadedRemove = scope.$on('inventoryLoaded', function() { - LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] }); - TreeInit(scope.TreeParams); - Rest.setUrl(scope.inventoryGroupsUrl); - Rest.get() - .success(function(data, status, headers, config) { - if (data.results.length == 0) { - // No groups exist yet, activate the groups tab - scope.showGroupHelp = true; - $('#inventory-tabs a[href="#inventory-groups"]').tab('show') - } - else { - scope.showGroupHelp = false; - $('#inventory-tabs a[href="#inventory-hosts"]').tab('show') - } - }) - .error(function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get inventory groups. GET returned status: ' + status }); - }); - if (!scope.$$phase) { - scope.$digest(); - } + LoadBreadCrumbs({ path: '/inventories/' + id, title: scope.inventory_name }); + PostLoadInventory({ scope: scope }); }); - // Add the selected flag to the hosts set. - if (scope.relatedHostsRemove) { - scope.relatedHostsRemove(); - } - scope.relatedHostsRemove = scope.$on('relatedhosts', function() { - scope.toggleAllFlag = false; - for (var i=0; i < scope.hosts.length; i++) { - scope.hosts[i].selected = 0; - } - }); - - LoadInventory({ scope: scope, doPostSteps: true }); - - $('#inventory-tabs a[href="#inventory-hosts"]').on('show.bs.tab', function() { - scope['hosts'] = null; - LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] }); - if (!scope.$$phase) { - scope.$digest(); - } - }); - - scope.filterInventory = function() { - $rootScope.hostFailureFilter = scope.hostFailureFilter; - LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] }); - //HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] }); - } - - scope.filterHosts = function() { - HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] }); - } + LoadInventory({ scope: scope, doPostSteps: false }); // Cancel scope.formReset = function() { @@ -297,10 +249,16 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP $rootScope.flashMessage = null; $location.path('/' + base + '/' + $routeParams.id + '/' + set + '/' + id); }; - - scope.editInventory = function() { - EditInventory({ scope: scope, 'inventory_id': scope['inventory_id'] }); - }; + + if (scope.removeInventorySaved) { + scope.removeInventorySaved(); + } + scope.removeInventorySaved = scope.$on('inventorySaved', function() { + $location.path('/inventories'); + }); + scope.formSave = function() { + SaveInventory({ scope: scope }); + } // Related set: Delete button scope['delete'] = function(set, itm_id, name, title) { @@ -327,229 +285,12 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP }); }; - - scope.treeController = function($node) { - - var nodeType = $($node).attr('type'); - if (nodeType == 'inventory') { - return { - editInventory: { - label: 'Inventory Properties', - action: function(obj) { - scope.group_id = null; - if (!scope.$$phase) { - scope.$digest(); - } - EditInventory({ scope: scope, "inventory_id": id }); - }, - separator_after: true - }, - addGroup: { - label: 'Create New Group', - action: function(obj) { - scope.group_id = null; - if (!scope.$$phase) { - scope.$digest(); - } - GroupsAdd({ "inventory_id": id, group_id: null }); - } - } - } - } - else { - return { - edit: { - label: 'Group Properties', - action: function(obj) { - scope.group_id = $(obj).attr('group_id'); - if (!scope.$$phase) { - scope.$digest(); - } - GroupsEdit({ "inventory_id": id, group_id: $(obj).attr('group_id') }); - }, - separator_after: true - }, - - addGroup: { - label: 'Add Existing Group', - action: function(obj) { - scope.group_id = $(obj).attr('group_id'); - if (!scope.$$phase) { - scope.$digest(); - } - GroupsList({ "inventory_id": id, group_id: $(obj).attr('group_id') }); - } - }, - - createGroup: { - label: 'Create New Group', - action: function(obj) { - scope.group_id = $(obj).attr('group_id'); - if (!scope.$$phase) { - scope.$digest(); - } - GroupsAdd({ "inventory_id": id, group_id: $(obj).attr('group_id') }); - } - }, - - "delete": { - label: 'Delete Group', - action: function(obj) { - scope.group_id = $(obj).attr('group_id'); - if (!scope.$$phase) { - scope.$digest(); - } - GroupsDelete({ scope: scope, "inventory_id": id, group_id: $(obj).attr('group_id') }); - } - } - } - } - } - - scope.$on('NodeSelect', function(e, n) { - - // Respond to user clicking on a tree node - - var node = $('li[id="' + n.attr.id + '"]'); - var type = node.attr('type'); - var url; - - scope['selectedNode'] = node; - scope['selectedNodeName'] = node.attr('name'); - - $('#tree-view').jstree('open_node',node); - - if (type == 'group') { - url = node.attr('all'); - scope.groupAddHide = false; - scope.groupCreateHide = false; - scope.groupEditHide = false; - scope.inventoryEditHide = true; - scope.groupDeleteHide = false; - scope.createButtonShow = true; - scope.group_id = node.attr('group_id'); - //scope.groupName = n.data; - //scope.groupTitle = '

' + n.data + '

'; - //scope.groupTitle += (node.attr('description')) ? '

' + node.attr('description') + '

' : ''; - } - else if (type == 'inventory') { - url = node.attr('hosts'); - scope.groupAddHide = true; - scope.groupCreateHide = false; - scope.groupEditHide =true; - scope.inventoryEditHide=false; - scope.groupDeleteHide = true; - scope.createButtonShow = false; - //scope.groupName = 'All Hosts'; - //scope.groupTitle = '

All Hosts

'; - scope.group_id = null; - } - - if (!scope.$$phase) { - scope.$digest(); - } - }); - - scope.addGroup = function() { - GroupsList({ "inventory_id": id, group_id: scope.group_id }); - } - - scope.createGroup = function() { - GroupsAdd({ "inventory_id": id, group_id: scope.group_id }); - } - - scope.editGroup = function() { - GroupsEdit({ "inventory_id": id, group_id: scope.group_id }); - } - - scope.deleteGroup = function() { - GroupsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id }); - } - - scope.addHost = function() { - HostsList({ scope: scope, "inventory_id": id, group_id: scope.group_id }); - } - - scope.createHost = function() { - HostsAdd({ scope: scope, "inventory_id": id, group_id: scope.group_id }); - } - - scope.editHost = function(host_id, host_name) { - HostsEdit({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name }); - } - - scope.editHostGroups = function(host_id) { - EditHostGroups({ inventory_id: id, host_id: host_id }); - } - - scope.deleteHost = function(host_id, host_name) { - HostsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name, - request: 'delete' }); - } - - scope.viewJobs = function(last_job) { - $location.url('/jobs/?id__int=' + last_job ); - } - - scope.viewLastEvents = function(host_id, last_job, host_name, last_job_name) { - // Choose View-> Latest job events - LoadBreadCrumbs({ path: '/jobs/' + last_job, title: last_job_name }); - $location.url('/jobs/' + last_job + '/job_events/?host=' + escape(host_name)); - } - - scope.viewLastSummary = function(host_id, last_job, host_name, last_job_name) { - // Choose View-> Latest job events - LoadBreadCrumbs({ path: '/jobs/' + last_job, title: last_job_name }); - $location.url('/jobs/' + last_job + '/job_host_summaries/?host=' + escape(host_name)); - } - - scope.toggleAllHosts = function() { - scope.hostDeleteDisabled = (scope.toggleAllFlag) ? false : true; - scope.hostDeleteDisabledClass = (scope.hostDeleteDisabled) ? "disabled" : ""; - for (var i=0; i < scope.hosts.length; i++) { - scope.hosts[i].selected = scope.toggleAllFlag; - } - } - - scope.toggleOneHost = function() { - var result = true; - for (var i=0; i < scope.hosts.length; i++) { - if (scope.hosts[i].selected) { - result = false; - break; - } - } - scope.hostDeleteDisabled = result; - scope.hostDeleteDisabledClass = (scope.hostDeleteDisabled) ? "disabled" : ""; - } - - - // Respond to the scope.$emit from awTree directive - scope.$on('refreshHost', function(e, group, title) { - scope.groupTitle = title; - scope.group_id = group; - if (scope.group_id == null) { - scope.hostAddHide = true; - scope.hostCreateHide = true; - scope.hostDeleteHide = true; - } - else { - scope.hostAddHide = false; - scope.hostCreateHide = false; - scope.hostDeleteHide = false; - } - scope['hostDeleteDisabled'] = true; - scope['hostDeleteDisabledClass'] = 'disabled'; - HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: group }); - }); - } InventoriesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', - 'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory', - 'GroupsDelete', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', 'RefreshGroupName', - 'ParseTypeChange', 'HostsReload', 'EditInventory', 'RefreshTree', 'LoadSearchTree', 'EditHostGroups' + 'OrganizationList', 'GetBasePath', 'LoadInventory', 'ParseTypeChange', 'EditInventory', + 'SaveInventory', 'PostLoadInventory' ]; diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index 1ac5acbd59..60e7776f74 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -15,9 +15,6 @@ angular.module('InventoryFormDefinition', []) name: 'inventory', parseTypeName: 'inventoryParseType', well: true, - /*, - formLabelSize: 'col-lg-3', - formFieldSize: 'col-lg-9',*/ fields: { inventory_name: { @@ -63,7 +60,7 @@ angular.module('InventoryFormDefinition', []) '

View YAML examples at ansibleworks.com

', dataTitle: 'Inventory Variables', dataPlacement: 'bottom', - dataContainer: '#form-modal .modal-content' + dataContainer: '#inventory' } }, @@ -86,56 +83,6 @@ angular.module('InventoryFormDefinition', []) related: { - groups: { - type: 'tree', - open: true, - actions: { - } - }, - - hosts: { - type: 'treeview', - title: "groupTitle", - iterator: 'host', - - actions: { - }, - - fields: { - name: { - key: true, - label: 'Host Name', - ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')", - badgeShow: "\{\{ host.has_active_failures \}\}", - badgeIcon: 'icon-exclamation-sign', - badgeToolTip: 'Most recent job failed', - badgePlacement: 'bottom', - columnClass: 'col-lg-3' - }, - groups: { - label: 'Groups', - searchable: false, - sourceModel: 'groups', - sourceField: 'name', - nosort: true - }, - dropdown: { - type: 'DropDown', - label: 'View', - "class": "btn-sm", - ngDisabled: 'host.last_job == null', - options: [ - { ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest job' }, - { ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + - "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest job events' }, - { ngClick: "viewLastSummary(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + - "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest host summary' } - ] - } - }, - fieldActions: { - } - } } }); //InventoryForm diff --git a/awx/ui/static/js/forms/InventoryGroups.js b/awx/ui/static/js/forms/InventoryGroups.js new file mode 100644 index 0000000000..8a47959eca --- /dev/null +++ b/awx/ui/static/js/forms/InventoryGroups.js @@ -0,0 +1,27 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * InventoryGroups.js + * Form definition for Groups model + * + * + */ +angular.module('InventoryGroupsFormDefinition', []) + .value( + 'InventoryGroupsForm', { + + type: 'groupsview', + title: "groupTitle", + editTitle: 'Groups', + iterator: 'group', + + fields: { + }, + + actions: { + }, + + fieldActions: { + } + + }); //InventoryGroupsForm \ No newline at end of file diff --git a/awx/ui/static/js/forms/InventoryHosts.js b/awx/ui/static/js/forms/InventoryHosts.js new file mode 100644 index 0000000000..1e766a9ae3 --- /dev/null +++ b/awx/ui/static/js/forms/InventoryHosts.js @@ -0,0 +1,82 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * InventoryHosts.js + * Form definition for Hosts model + * + * + */ +angular.module('InventoryHostsFormDefinition', []) + .value( + 'InventoryHostsForm', { + + type: 'hostsview', + title: "groupTitle", + editTitle: 'Hosts', + iterator: 'host', + + fields: { + name: { + key: true, + label: 'Host Name', + ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')", + badgeIcon: "\{\{ 'icon-failures-' + host.has_active_failures \}\}", + badgePlacement: 'left', + columnClass: 'col-lg-3' + }, + groups: { + label: 'Groups', + searchable: false, + sourceModel: 'groups', + sourceField: 'name', + nosort: true + }, + dropdown: { + type: 'DropDown', + searchable: false, + nosort: true, + label: 'View Jobs', + "class": "btn-sm", + //ngDisabled: 'host.last_job == null', + options: [ + { ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest job', ngShow: 'host.last_job' }, + { ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + + "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest job events', ngShow: 'host.last_job' }, + { ngClick: "viewLastSummary(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + + "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest host summary', ngShow: 'host.last_job' }, + { ngClick: "", label: 'No job data available', ngShow: 'host.last_job == null' } + ] + } + }, + + actions: { + add: { + label: 'Add Existing Host', + ngClick: "addHost()", + ngHide: "hostAddHide", + awToolTip: "Select from a list of existing hosts", + dataPlacement: 'bottom', + 'class': 'btn-xs btn-primary', + icon: 'icon-check' + }, + create: { + label: 'Create New Host', + ngClick: 'createHost()', + ngHide: 'hostCreateHide', + awToolTip: 'Create a new host', + dataPlacement: 'bottom', + 'class': 'btn-xs btn-success', + icon: 'icon-plus' + } + }, + + fieldActions: { + "delete": { + ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')", + icon: 'icon-trash', + "class": 'btn-sm btn-danger', + awToolTip: 'Delete host' + } + } + + }); //InventoryHostsForm \ No newline at end of file diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index d99b71b152..70da23765b 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -33,7 +33,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' }); scope.formModalActionLabel = 'Select'; - scope.formModalHeader = 'Add Groups'; + scope.formModalHeader = 'Add Existing Groups'; scope.formModalCancelShow = true; scope.formModalActionClass = 'btn btn-success'; @@ -91,7 +91,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' var scope = generator.inject(form, {mode: 'add', modal: true, related: false}); scope.formModalActionLabel = 'Save'; - scope.formModalHeader = 'Create Group'; + scope.formModalHeader = 'Create New Group'; scope.formModalCancelShow = true; scope.parseType = 'yaml'; ParseTypeChange(scope); @@ -185,7 +185,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' return function(params) { var group_id = params.group_id; - var inventory_id = $routeParams.id; + var inventory_id = params.inventory_id; var generator = GenerateForm; var form = GroupForm; var defaultUrl = GetBasePath('groups') + group_id + '/'; diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index e10f725c20..770eff73d7 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -10,7 +10,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition', 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper', 'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper', - 'InventoryFormDefinition', 'SelectionHelper', 'HostGroupsFormDefinition' + 'InventoryFormDefinition', 'SelectionHelper', 'HostGroupsFormDefinition', + 'InventoryHostsFormDefinition' ]) .factory('HostsList', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostList', 'GenerateList', @@ -28,7 +29,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H list.iterator = 'subhost'; //Override the iterator and name so the scope of the modal dialog list.name = 'subhosts'; //will not conflict with the parent scope - var defaultUrl = GetBasePath('inventory') + inventory_id + '/hosts/'; var view = GenerateList; var scope = view.inject(list, { @@ -38,8 +38,10 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H selectButton: false }); + var defaultUrl = GetBasePath('inventory') + inventory_id + '/hosts/?not__groups__id=' + scope.group_id; + scope.formModalActionLabel = 'Select'; - scope.formModalHeader = 'Select Hosts'; + scope.formModalHeader = 'Add Existing Hosts'; scope.formModalCancelShow = true; SelectionInit({ scope: scope, list: list, url: GetBasePath('groups') + group_id + '/hosts/' }); @@ -89,7 +91,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H var scope = generator.inject(form, {mode: 'add', modal: true, related: false}); scope.formModalActionLabel = 'Save'; - scope.formModalHeader = 'Create Host'; + scope.formModalHeader = 'Create New Host'; scope.formModalCancelShow = true; scope.parseType = 'yaml'; ParseTypeChange(scope); @@ -349,16 +351,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H 'HostsReload', function($rootScope, $location, $log, $routeParams, Rest, Alert, Prompt, ProcessErrors, GetBasePath, HostsReload) { return function(params) { - // Remove the selected host from the current group by disassociating var scope = params.scope; - - if (scope.hostDeleteDisabled) { - // simulate a disabled link - return; - } - var group_id = scope.group_id; var inventory_id = params.inventory_id; var host_id = params.host_id; @@ -376,40 +371,18 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H }); var action_to_take = function() { - var errors = false; - var maxI; - - // Find index pointing to the last selected host - for (var i=0; i < scope.hosts.length; i++) { - if (scope.hosts[i].selected) { - maxI = i; - } - } - - function emit(i) { - // After we process the last selected host or after we hit a problem, refresh the host list - if (i >= maxI || errors) { - $('#prompt-modal').modal('hide'); - scope.$emit('hostsReload'); - } - } - Rest.setUrl(url); - for (var i=0; i < scope.hosts.length && !errors; i++) { - if (scope.hosts[i].selected) { - Rest.post({ id: scope.hosts[i].id, disassociate: 1 }) - .success( function(data, status, headers, config) { - // if this is the last selected host, clean up and exit - emit(i); - }) - .error( function(data, status, headers, config) { - errors = true; - emit(i); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Attempt to delete ' + scope.hosts[i].name + ' failed. POST returned status: ' + status }); - }); - } - } + Rest.post({ id: host_id, disassociate: 1 }) + .success( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + scope.$emit('hostsReload'); + }) + .error( function(data, status, headers, config) { + $('#prompt-modal').modal('hide'); + scope.$emit('hostsReload'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Attempt to delete ' + host_name + ' failed. POST returned status: ' + status }); + }); } //Force binds to work (not working usual way), and launch the confirmation prompt @@ -419,13 +392,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H scope['promptActionBtnClass'] = 'btn-danger'; } - /*else { - scope['promptHeader'] = 'Remove Host from Group'; - scope['promptBody'] = 'Are you sure you want to remove ' + host_name + ' from the group? ' + - host_name + ' will continue to be part of the inventory under All Hosts.'; - scope['promptActionBtnClass'] = 'btn-success'; - }*/ - scope.promptAction = action_to_take; $('#prompt-modal').modal({ @@ -442,13 +408,13 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H }]) - .factory('HostsReload', ['RelatedSearchInit', 'RelatedPaginateInit', 'InventoryForm', 'GetBasePath', 'Wait', - function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath, Wait) { + .factory('HostsReload', ['SearchInit', 'PaginateInit', 'InventoryHostsForm', 'GetBasePath', 'Wait', + function(SearchInit, PaginateInit, InventoryHostsForm, GetBasePath, Wait) { return function(params) { // Rerfresh the Hosts view on right side of page - var group_id = params.group_id; var scope = params.scope; + var group_id = scope.group_id; var postAction = params.action; scope['hosts'] = null; @@ -463,10 +429,10 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H } // Set the groups value in each element of hosts array - if (scope.removeRelatedHosts) { - scope.removeRelatedHosts(); + if (scope.removePostRefresh) { + scope.removePostRefresh(); } - scope.removeRelatedHosts = scope.$on('relatedhosts', function() { + scope.removePostRefresh = scope.$on('PostRefresh', function() { var groups, descr, found, list; for (var i=0; i < scope.hosts.length; i++) { groups = scope.hosts[i].summary_fields.groups; @@ -483,10 +449,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H } }); - var relatedSets = { hosts: { url: url, iterator: 'host' } }; - RelatedSearchInit({ scope: params.scope, form: InventoryForm, relatedSets: relatedSets }); - RelatedPaginateInit({ scope: params.scope, relatedSets: relatedSets, pageSize: 40 }); - + SearchInit({ scope: scope, set: 'hosts', list: InventoryHostsForm, url: url }); + PaginateInit({ scope: scope, list: InventoryHostsForm, url: url }); scope.search('host'); if (!params.scope.$$phase) { @@ -510,7 +474,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H if (scope.buildAllGroupsRemove) { scope.buildAllGroupsRemove(); } - scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function() { + scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function(e, inventory_name) { scope.inventory_groups = []; Rest.setUrl(GetBasePath('inventory') + inventory_id + '/groups/?order_by=name'); Rest.get() @@ -518,7 +482,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H for (var i=0; i < data.results.length; i++) { scope.inventory_groups.push({ name: data.results[i].name, id: data.results[i].id }); } - scope.$emit('hostTabInit'); + scope.$emit('hostTabInit', inventory_name); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, @@ -538,7 +502,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H groups: data.related.root_groups, children: [] }); - scope.$emit('buildAllGroups'); + scope.$emit('buildAllGroups', data.name); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, diff --git a/awx/ui/static/js/helpers/inventory.js b/awx/ui/static/js/helpers/inventory.js index 926dd791f5..f0b25ab4a4 100644 --- a/awx/ui/static/js/helpers/inventory.js +++ b/awx/ui/static/js/helpers/inventory.js @@ -325,7 +325,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi scope.relatedSets = []; scope.master = {}; - Rest.setUrl(GetBasePath('inventory') + $routeParams.id + '/'); + Rest.setUrl(GetBasePath('inventory') + scope['inventory_id'] + '/'); Rest.get() .success( function(data, status, headers, config) { LoadBreadCrumbs({ path: '/inventories/' + $routeParams.id, title: data.name }); @@ -440,11 +440,119 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi } }]) + + .factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', + 'GetBasePath', 'ParseTypeChange', 'LoadInventory', 'RefreshGroupName', + function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, + LoadInventory, RefreshGroupName) { + return function(params) { + + // Save inventory property modifications + + var scope = params.scope; + var form = InventoryForm; + var defaultUrl=GetBasePath('inventory'); + + try { + // Make sure we have valid variable data + if (scope.inventoryParseType == 'json') { + var json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses + } + else { + var json_data = jsyaml.load(scope.inventory_variables); //parse yaml + } + + // Make sure our JSON is actually an object + if (typeof json_data !== 'object') { + throw "failed to return an object!"; + } + + var data = {} + for (var fld in form.fields) { + if (fld != 'inventory_variables') { + if (form.fields[fld].realName) { + data[form.fields[fld].realName] = scope[fld]; + } + else { + data[fld] = scope[fld]; + } + } + } + + Rest.setUrl(defaultUrl + scope['inventory_id'] + '/'); + Rest.put(data) + .success( function(data, status, headers, config) { + if (scope.inventory_variables) { + Rest.setUrl(data.related.variable_data); + Rest.put(json_data) + .success( function(data, status, headers, config) { + scope.$emit('inventorySaved'); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to update inventory varaibles. PUT returned status: ' + status }); + }); + } + else { + scope.$emit('inventorySaved'); + } + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to update inventory. POST returned status: ' + status }); + }); + } + catch(err) { + Alert("Error", "Error parsing inventory variables. Parser returned: " + err); + } + } + }]) + + .factory('PostLoadInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', 'GetBasePath', + function(InventoryForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath) { + return function(params) { + + var scope = params.scope; + + LookUpInit({ + scope: scope, + form: InventoryForm, + current_item: (scope.organization !== undefined) ? scope.organization : null, + list: OrganizationList, + field: 'organization' + }); + + if (scope.variable_url) { + Rest.setUrl(scope.variable_url); + Rest.get() + .success( function(data, status, headers, config) { + if ($.isEmptyObject(data)) { + scope.inventory_variables = "---"; + } + else { + scope.inventory_variables = jsyaml.safeDump(data); + } + scope.master.inventory_variables = scope.inventory_variables; + }) + .error( function(data, status, headers, config) { + scope.inventory_variables = null; + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to retrieve inventory variables. GET returned status: ' + status }); + }); + } + else { + scope.inventory_variables = "---"; + } + if (!scope.$$phase) { + scope.$digest(); + } + } + }]) .factory('EditInventory', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', - 'GetBasePath', 'ParseTypeChange', 'LoadInventory', 'RefreshGroupName', + 'GetBasePath', 'ParseTypeChange', 'LoadInventory', 'RefreshGroupName', 'SaveInventory', 'PostLoadInventory', function(InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, - LoadInventory, RefreshGroupName) { + LoadInventory, RefreshGroupName, SaveInventory, PostLoadInventory) { return function(params) { var generator = GenerateForm; @@ -473,38 +581,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi scope.inventoryLoadedRemove(); } scope.inventoryLoadedRemove = scope.$on('inventoryLoaded', function() { - - LookUpInit({ - scope: scope, - form: form, - current_item: (scope.organization !== undefined) ? scope.organization : null, - list: OrganizationList, - field: 'organization' - }); - - if (scope.variable_url) { - Rest.setUrl(scope.variable_url); - Rest.get() - .success( function(data, status, headers, config) { - if ($.isEmptyObject(data)) { - scope.inventory_variables = "---"; - } - else { - scope.inventory_variables = jsyaml.safeDump(data); - } - }) - .error( function(data, status, headers, config) { - scope.inventory_variables = null; - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to retrieve inventory variables. GET returned status: ' + status }); - }); - } - else { - scope.inventory_variables = "---"; - } - if (!scope.$$phase) { - scope.$digest(); - } + PostLoadInventory({ scope: scope }); }); LoadInventory({ scope: scope, doPostSteps: false }); @@ -513,81 +590,19 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi scope.$digest(); } - function PostSave() { - $('#form-modal').modal('hide'); + if (scope.removeInventorySaved) { + scope.removeInventorySaved(); + } + scope.removeInventorySaved = scope.$on('inventorySaved', function() { + $('#form-modal').modal('hide'); + // Make sure the inventory name appears correctly in the tree and the navbar + RefreshGroupName($('#inventory-node'), scope['inventory_name'], scope['inventory_description']); + }); - // Make sure the inventory name appears correctly in the tree and the navbar - RefreshGroupName($('#inventory-node'), scope['inventory_name'], scope['inventory_description']); - - // Reset the form to disable the form action buttons - //scope[form.name + '_form'].$setPristine(); - - // Show the flash message for 5 seconds, letting the user know the save worked - //scope['flashMessage'] = 'Your changes were successfully saved!'; - //setTimeout(function() { - // scope['flashMessage'] = null; - // if (!scope.$$phase) { - // scope.$digest(); - // } - // }, 5000); - } - - // Save scope.formModalAction = function() { - try { - // Make sure we have valid variable data - if (scope.inventoryParseType == 'json') { - var json_data = JSON.parse(scope.inventory_variables); //make sure JSON parses - } - else { - var json_data = jsyaml.load(scope.inventory_variables); //parse yaml - } - - // Make sure our JSON is actually an object - if (typeof json_data !== 'object') { - throw "failed to return an object!"; - } - - var data = {} - for (var fld in form.fields) { - if (fld != 'inventory_variables') { - if (form.fields[fld].realName) { - data[form.fields[fld].realName] = scope[fld]; - } - else { - data[fld] = scope[fld]; - } - } - } - - Rest.setUrl(defaultUrl + scope['inventory_id'] + '/'); - Rest.put(data) - .success( function(data, status, headers, config) { - if (scope.inventory_variables) { - Rest.setUrl(data.related.variable_data); - Rest.put(json_data) - .success( function(data, status, headers, config) { - PostSave(); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to update inventory varaibles. PUT returned status: ' + status }); - }); - } - else { - PostSave(); - } - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to update inventory. POST returned status: ' + status }); - }); - } - catch(err) { - Alert("Error", "Error parsing inventory variables. Parser returned: " + err); - } - - }; + SaveInventory({ scope: scope }); + } + } }]); diff --git a/awx/ui/static/js/helpers/search.js b/awx/ui/static/js/helpers/search.js index bbbab6c1bc..8f0ad7b02b 100644 --- a/awx/ui/static/js/helpers/search.js +++ b/awx/ui/static/js/helpers/search.js @@ -158,7 +158,8 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) scope[iterator + 'SearchParams'] = (sort_order) ? 'order_by=' + escape(sort_order) : ""; } - if (iterator == 'inventory' && scope.inventoryFailureFilter) { + if ( (iterator == 'inventory' && scope.inventoryFailureFilter) || + (iterator == 'host' && scope.hostFailureFilter) ) { scope[iterator + 'SearchParams'] += '&has_active_failures=true'; } diff --git a/awx/ui/static/js/lists/Hosts.js b/awx/ui/static/js/lists/Hosts.js index ceac26a9e0..1972467d08 100644 --- a/awx/ui/static/js/lists/Hosts.js +++ b/awx/ui/static/js/lists/Hosts.js @@ -12,7 +12,7 @@ angular.module('HostListDefinition', []) name: 'hosts', iterator: 'host', - selectTitle: 'Select Host', + selectTitle: 'Add Existing Hosts', editTitle: 'Hosts', index: true, well: true, diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index c61f7e8ce0..31629fbe92 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -22,10 +22,8 @@ angular.module('InventoriesListDefinition', []) name: { key: true, label: 'Name', - badgeShow: "\{\{ inventory.has_active_failures \}\}", - badgeIcon: 'icon-exclamation-sign', - badgeToolTip: 'Contains hosts with active job failures', - badgePlacement: 'bottom' + badgeIcon: "\{\{ 'icon-failures-' + inventory.has_active_failures \}\}", + badgePlacement: 'left' }, description: { label: 'Description' @@ -54,7 +52,7 @@ angular.module('InventoriesListDefinition', []) dropdown: { type: 'DropDown', - label: 'View', + label: 'View Jobs', 'class': 'btn-xs', options: [ { ngClick: 'viewJobs(\{\{ inventory.id \}\})', label: 'Jobs' }, @@ -62,16 +60,30 @@ angular.module('InventoriesListDefinition', []) ] }, + hosts: { + label: 'Hosts', + ngClick: "editHosts(\{\{ inventory.id \}\})", + icon: 'icon-th-large', + "class": 'btn-xs btn-default', + awToolTip: 'Edit Hosts' + }, + + groups: { + label: 'Groups', + ngClick: "editGroups(\{\{ inventory.id \}\})", + icon: 'icon-group', + "class": 'btn-xs btn-default', + awToolTip: 'Edit Groups' + }, + edit: { - label: 'Edit', ngClick: "editInventory(\{\{ inventory.id \}\})", icon: 'icon-edit', "class": 'btn-xs btn-default', - awToolTip: 'View/Edit inventory' + awToolTip: 'Edit Inventory Properties' }, "delete": { - label: 'Delete', ngClick: "deleteInventory(\{\{ inventory.id \}\},'\{\{ inventory.name \}\}')", icon: 'icon-trash', "class": 'btn-xs btn-danger', diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index dbdb538e66..c4ec78ee25 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -14,7 +14,6 @@ @blue-link: #0088cc; @grey: #A9A9A9; - html { background-color: @black; } @@ -106,6 +105,7 @@ hr { border-color: #ccc; } + /* Use code-breakable in pop-over text to indent and wrap code segments */ .code-breakable { @@ -390,8 +390,20 @@ select.field-mini-height { } .tree-container { - padding: 0; - margin: 5px 0 15px 0; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 6px; +} + +.tree-controls { + padding: 10px; + border-bottom: 1px solid #e3e3e3; + + .title { + display: inline-block; + font-weight: bold; + margin-right: 10px; + } } .no-padding { @@ -436,117 +448,136 @@ select.field-mini-height { /* Jobs pages */ -.job-error, -.job-failed, -.license-expired, -.license-invalid, -.active-failures-true, -.active-failures-true a, -.active-failures-true a:active, -input[type="text"].job-failed, -input[type="text"].job-error { - color: #da4f49; -} + .job-error, + .job-failed, + .license-expired, + .license-invalid, + .icon-failures-true, + .active-failures-true a, + .active-failures-true a:active, + input[type="text"].job-failed, + input[type="text"].job-error { + color: #da4f49; + } -.active-failures-true a:hover { - color: @red; -} + .icon-failures-true a:hover { + color: @red; + } -.job-failures-true { - padding-top: 5px; - color: #da4f49; -} + .job-failures-true { + padding-top: 5px; + color: #da4f49; + } -.job-event-status, -.license-status { - padding-top: 5px; -} + .job-event-status, + .license-status { + padding-top: 5px; + } -.job-new, -input[type="text"].job-new, -.job-canceled, -input[type="text"].job-canceled { - color: #778899; -} + .job-new, + input[type="text"].job-new, + .job-canceled, + input[type="text"].job-canceled { + color: #778899; + } -.job-pending, -.job-running, -.job-success, -.job-successful, -.active-failures-false, -.license-valid, -input[type="text"].job-success, -input[type="text"].job-successful { - color: #5bb75b; -} + .job-pending, + .job-running, + .job-success, + .job-successful, + .icon-failures-false, + .license-valid, + input[type="text"].job-success, + input[type="text"].job-successful { + color: #5bb75b; + } -.field-success { - color: #5bb75b; -} + .icon-failures-true:before { + content: "\f06a"; + } -.field-success input { - border-color: #5bb75b; -} + .icon-failures-false:before { + content: "\f111"; + } -.field-failure { - color: @red; -} + .field-success { + color: #5bb75b; + } -.field-failure input { - border-color: @red; -} + .field-success input { + border-color: #5bb75b; + } -.job-changed, -.license-warning, -.license-demo { - color: @warning; -} + .field-failure { + color: @red; + } -.job-detail-status { - display: inline-block; - margin-top: 5px; - font-size: 15px; - font-weight: bold; -} + .field-failure input { + border-color: @red; + } -.form-items .search-widget { - margin-top: 15px; -} + .field-badge { + font-size: 12px; + } -.form-items .item-count { - display: inline-block; - margin-top: 25px; - font-size: small; -} + .job-changed, + .license-warning, + .license-demo { + color: @warning; + } -.child-event a { - color: @black; - cursor: default; -} + .job-detail-status { + display: inline-block; + margin-top: 5px; + font-size: 15px; + font-weight: bold; + } -.level-1 { - padding-left: 24px; -} + .form-items .search-widget { + margin-top: 15px; + } -.level-2 { - padding-left: 48px; -} + .form-items .item-count { + display: inline-block; + margin-top: 25px; + font-size: small; + } -.level-3 { - padding-left: 72px; -} + .child-event a { + color: @black; + cursor: default; + } -.level-3-detail { - padding-left: 72px; -} + .level-1 { + padding-left: 24px; + } -#job_events .control-group { - margin-top: 0; - margin-bottom: 10px; -} + .level-2 { + padding-left: 48px; + } + + .level-3 { + padding-left: 72px; + } + + .level-3-detail { + padding-left: 72px; + } + + #job_events .control-group { + margin-top: 0; + margin-bottom: 10px; + } /* End Jobs Page */ + +/* License Accordion */ + #license-collapse .ui-accordion-content { + overflow: hidden; + } + + /* Inventory Detail Groups tab */ .inventory-content { padding: 15px; @@ -614,11 +645,6 @@ input[type="text"].job-successful { } - .field-badge { - color: @red; - font-size: 12px; - } - /* Inventory Detail Hosts tab */ .hosts-well { @@ -661,6 +687,14 @@ input[type="text"].job-successful { } .search-tree { + + .title { + color: @black; + font-weight: bold; + margin-bottom: 10px; + margin-top: 0; + } + ul { list-style-type: none; padding-left: 13px; @@ -676,19 +710,31 @@ input[type="text"].job-successful { .active { font-weight: bold; - color: #000; + padding: 5px; } .expand { padding: 3px; + border: 1px solid rgb(245, 245, 245); } .expand:hover { background-color: #ddd; border: 1px solid #ddd; } + + .field-badge { + font-size: 10px; + line-height: normal; + vertical-align: middle; + } + } +.host-failure-filter { + padding: 10px; +} + .disabled { color: @grey; } @@ -744,7 +790,7 @@ tr td i { float: none; padding-top: 3px; padding-left: 0; - margin-right: 10px; + margin-right: 5px; margin-left: 0; } diff --git a/awx/ui/static/lib/ansible/authenticate.js b/awx/ui/static/lib/ansible/authenticate.js index 697cfa5ac6..ea3d87c990 100644 --- a/awx/ui/static/lib/ansible/authenticate.js +++ b/awx/ui/static/lib/ansible/authenticate.js @@ -56,7 +56,7 @@ angular.module('AuthService', ['ngCookies']) return ($rootScope.token) ? $rootScope.token : $cookieStore.get('token'); } else { - return null; + $location.path('/login'); } }, diff --git a/awx/ui/static/lib/ansible/directives.js b/awx/ui/static/lib/ansible/directives.js index 279489a7c5..2bedf5bfb5 100644 --- a/awx/ui/static/lib/ansible/directives.js +++ b/awx/ui/static/lib/ansible/directives.js @@ -421,7 +421,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos elm = angular.element(e.target); } - var sibling = angular.element(parent.children()[1]); // + var sibling = angular.element(parent.children()[2]); // var state = parent.attr('data-state'); var icon = angular.element(elm.children()[0]); @@ -449,6 +449,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos } else { icon.removeClass('icon-caret-right').addClass('icon-caret-down'); + parent.attr('data-state','open'); //activate(e); } }); @@ -485,7 +486,6 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos else { // close the element parent.attr('data-state','closed'); - var icon = angular.element(elm.children()[0]); icon.removeClass('icon-caret-down').addClass('icon-caret-right'); var childlists = parent.find('ul'); if (childlists && childlists.length > 0) { @@ -525,15 +525,14 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos html += "date-state=\"closed\" "; html += "data-hosts=\"" + data.results[i].related.all_hosts + "\" "; html += "data-description=\"" + data.results[i].description + "\" "; - html += "data-failures=\"" +data.results[i].has_active_failures + "\" "; + html += "data-failures=\"" + data.results[i].has_active_failures + "\" "; html += "data-groups=\"" + data.results[i].related.children + "\" "; html += "data-name=\"" + data.results[i].name + "\" "; html += "data-group-id=\"" + data.results[i].id + "\">"; html += " "; - html += "" + data.results[i].name + " "; - html += "" + - "\n"; + html += ""; + html += " " + data.results[i].name + " "; + html += "\n"; } html = (html !== '') ? "\n" : ""; var compiled = $compile(html)(scope); @@ -570,7 +569,8 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos Wait('start'); var container = angular.element(document.getElementById('search-tree-container')); container.empty(); - var html = "