mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 06:51:10 +03:00
Adding nested-groups (related tab) and completed jobs to inventories
This commit is contained in:
parent
78ff5f5301
commit
0fa9aa6bcb
@ -0,0 +1,80 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2017 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
import JobsListController from '../../jobs/jobs-list.controller';
|
||||||
|
export default ['InventoryCompletedJobsList', '$stateExtender', 'templateUrl', '$injector',
|
||||||
|
function(InventoryCompletedJobsList, $stateExtender, templateUrl, $injector){
|
||||||
|
var val = function(field, formStateDefinition) {
|
||||||
|
let state,
|
||||||
|
list = field.include ? $injector.get(field.include) : field,
|
||||||
|
breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase(),
|
||||||
|
stateConfig = {
|
||||||
|
// searchPrefix: `${list.iterator}`,
|
||||||
|
name: `${formStateDefinition.name}.${list.iterator}s`,
|
||||||
|
url: `/${list.iterator}s`,
|
||||||
|
ncyBreadcrumb: {
|
||||||
|
parent: `${formStateDefinition.name}`,
|
||||||
|
label: `${breadcrumbLabel}`
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
completed_job_search: {
|
||||||
|
value: {
|
||||||
|
or__job__inventory: '',
|
||||||
|
or__adhoccommand__inventory: '',
|
||||||
|
or__inventoryupdate__inventory_source__inventory: ''
|
||||||
|
},
|
||||||
|
squash: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
views: {
|
||||||
|
'related': {
|
||||||
|
templateProvider: function(FormDefinition, GenerateForm) {
|
||||||
|
let html = GenerateForm.buildCollection({
|
||||||
|
mode: 'edit',
|
||||||
|
related: `${list.iterator}s`,
|
||||||
|
form: typeof(FormDefinition) === 'function' ?
|
||||||
|
FormDefinition() : FormDefinition
|
||||||
|
});
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
controller: JobsListController
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
ListDefinition: () => {
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope',
|
||||||
|
(list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => {
|
||||||
|
// allow related list definitions to use interpolated $rootScope / $stateParams in basePath field
|
||||||
|
let path, interpolator;
|
||||||
|
if (GetBasePath(list.basePath)) {
|
||||||
|
path = GetBasePath(list.basePath);
|
||||||
|
} else {
|
||||||
|
interpolator = $interpolate(list.basePath);
|
||||||
|
path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
$stateParams[`${list.iterator}_search`].or__job__inventory = $stateParams.inventory_id;
|
||||||
|
$stateParams[`${list.iterator}_search`].or__adhoccommand__inventory = $stateParams.inventory_id;
|
||||||
|
$stateParams[`${list.iterator}_search`].or__inventoryupdate__inventory_source__inventory = $stateParams.inventory_id;
|
||||||
|
|
||||||
|
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
state = $stateExtender.buildDefinition(stateConfig);
|
||||||
|
// appy any default search parameters in form definition
|
||||||
|
if (field.search) {
|
||||||
|
state.params[`${field.iterator}_search`].value = _.merge(state.params[`${field.iterator}_search`].value, field.search);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,88 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
export default ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
|
||||||
|
awToolTip: i18n._('Please save and run a job to view'),
|
||||||
|
dataPlacement: 'top',
|
||||||
|
name: 'completed_jobs',
|
||||||
|
basePath: 'unified_jobs',
|
||||||
|
iterator: 'completed_job',
|
||||||
|
search: {
|
||||||
|
"or__job__inventory": ''
|
||||||
|
},
|
||||||
|
editTitle: i18n._('COMPLETED JOBS'),
|
||||||
|
index: false,
|
||||||
|
hover: true,
|
||||||
|
well: false,
|
||||||
|
emptyListText: i18n._('No completed jobs'),
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
status: {
|
||||||
|
label: '',
|
||||||
|
columnClass: 'List-staticColumn--smallStatus',
|
||||||
|
awToolTip: "{{ completed_job.status_tip }}",
|
||||||
|
awTipPlacement: "right",
|
||||||
|
dataTitle: "{{ completed_job.status_popover_title }}",
|
||||||
|
icon: 'icon-job-{{ completed_job.status }}',
|
||||||
|
iconOnly: true,
|
||||||
|
ngClick:"viewjobResults(completed_job)",
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
label: 'ID',
|
||||||
|
ngClick:"viewjobResults(completed_job)",
|
||||||
|
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||||
|
awToolTip: "{{ completed_job.status_tip }}",
|
||||||
|
dataPlacement: 'top'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
label: i18n._('Name'),
|
||||||
|
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
|
||||||
|
ngClick: "viewjobResults(completed_job)",
|
||||||
|
awToolTip: "{{ completed_job.name | sanitize }}",
|
||||||
|
dataPlacement: 'top'
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
label: i18n._('Type'),
|
||||||
|
ngBind: 'completed_job.type_label',
|
||||||
|
link: false,
|
||||||
|
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
|
||||||
|
},
|
||||||
|
finished: {
|
||||||
|
label: i18n._('Finished'),
|
||||||
|
noLink: true,
|
||||||
|
filter: "longDate",
|
||||||
|
columnClass: "col-lg-3 col-md-3 col-sm-3 hidden-xs",
|
||||||
|
key: true,
|
||||||
|
desc: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: { },
|
||||||
|
|
||||||
|
fieldActions: {
|
||||||
|
|
||||||
|
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||||
|
|
||||||
|
submit: {
|
||||||
|
icon: 'icon-rocket',
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: 'relaunchJob($event, completed_job.id)',
|
||||||
|
awToolTip: i18n._('Relaunch using the same parameters'),
|
||||||
|
dataPlacement: 'top',
|
||||||
|
ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: 'deleteJob(completed_job.id)',
|
||||||
|
awToolTip: i18n._('Delete the job'),
|
||||||
|
dataPlacement: 'top',
|
||||||
|
ngShow: 'completed_job.summary_fields.user_capabilities.delete'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};}];
|
13
awx/ui/client/src/inventories/completed_jobs/main.js
Normal file
13
awx/ui/client/src/inventories/completed_jobs/main.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2017 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import list from './completed_jobs.list';
|
||||||
|
import buildInventoryCompletedJobsState from './build-inventory-completed-jobs-state.factory';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('inventoryCompletedJobs', [])
|
||||||
|
.factory('InventoryCompletedJobsList', list)
|
||||||
|
.factory('buildInventoryCompletedJobsState', buildInventoryCompletedJobsState);
|
@ -10,7 +10,9 @@
|
|||||||
* @description This form is for adding/editing a Group on the inventory page
|
* @description This form is for adding/editing a Group on the inventory page
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default ['i18n', 'nestedGroupListState',
|
||||||
|
function(i18n, nestedGroupListState){
|
||||||
|
return {
|
||||||
addTitle: 'CREATE GROUP',
|
addTitle: 'CREATE GROUP',
|
||||||
editTitle: '{{ name }}',
|
editTitle: '{{ name }}',
|
||||||
showTitle: true,
|
showTitle: true,
|
||||||
@ -21,9 +23,10 @@ export default {
|
|||||||
stateTree: 'inventories',
|
stateTree: 'inventories',
|
||||||
// form generator inspects the current state name to determine whether or not to set an active (.is-selected) class on a form tab
|
// form generator inspects the current state name to determine whether or not to set an active (.is-selected) class on a form tab
|
||||||
// this setting is optional on most forms, except where the form's edit state name is not parentStateName.edit
|
// this setting is optional on most forms, except where the form's edit state name is not parentStateName.edit
|
||||||
activeEditState: 'inventories.edit.groups.editGroup',
|
activeEditState: 'inventories.edit.groups.edit',
|
||||||
detailsClick: "$state.go('inventories.edit.groups.editGroup')",
|
detailsClick: "$state.go('inventories.edit.groups.edit')",
|
||||||
well: false,
|
well: false,
|
||||||
|
tabs: true,
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
name: {
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
@ -75,5 +78,20 @@ export default {
|
|||||||
ngDisabled: true,
|
ngDisabled: true,
|
||||||
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
related: {
|
||||||
|
nested_groups: {
|
||||||
|
name: 'nested_groups',
|
||||||
|
ngClick: "$state.go('inventories.edit.groups.edit.nested_groups')",
|
||||||
|
include: "NestedGroupListDefinition",
|
||||||
|
includeForm: "NestedGroupFormDefinition",
|
||||||
|
title: i18n._('Groups'),
|
||||||
|
iterator: 'nested_group',
|
||||||
|
listState: nestedGroupListState,
|
||||||
|
// addState: buildGroupsAddState,
|
||||||
|
// editState: buildGroupsEditState
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}];
|
||||||
|
@ -31,17 +31,9 @@ export default {
|
|||||||
name: {
|
name: {
|
||||||
label: 'Groups',
|
label: 'Groups',
|
||||||
key: true,
|
key: true,
|
||||||
ngClick: "groupSelect(group.id)",
|
ngClick: "editGroup(group.id)",
|
||||||
columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6',
|
columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6',
|
||||||
class: 'InventoryManage-breakWord',
|
class: 'InventoryManage-breakWord',
|
||||||
},
|
|
||||||
total_groups: {
|
|
||||||
nosort: true,
|
|
||||||
label: '',
|
|
||||||
type: 'badgeCount',
|
|
||||||
ngHide: 'group.total_groups == 0',
|
|
||||||
noLink: true,
|
|
||||||
awToolTip: "{{group.name | sanitize}} contains {{group.total_groups}} {{group.total_groups === 1 ? 'child' : 'children'}}"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import groupList from './list/main';
|
import groupList from './list/main';
|
||||||
import groupAdd from './add/main';
|
import groupAdd from './add/main';
|
||||||
import groupEdit from './edit/main';
|
import groupEdit from './edit/main';
|
||||||
|
import nestedGroups from './nested-groups/main';
|
||||||
import groupFormDefinition from './groups.form';
|
import groupFormDefinition from './groups.form';
|
||||||
import groupListDefinition from './groups.list';
|
import groupListDefinition from './groups.list';
|
||||||
import service from './groups.service';
|
import service from './groups.service';
|
||||||
@ -20,9 +21,10 @@ export default
|
|||||||
angular.module('group', [
|
angular.module('group', [
|
||||||
groupList.name,
|
groupList.name,
|
||||||
groupAdd.name,
|
groupAdd.name,
|
||||||
groupEdit.name
|
groupEdit.name,
|
||||||
|
nestedGroups.name
|
||||||
])
|
])
|
||||||
.value('GroupForm', groupFormDefinition)
|
.factory('GroupForm', groupFormDefinition)
|
||||||
.value('GroupList', groupListDefinition)
|
.value('GroupList', groupListDefinition)
|
||||||
.factory('GetHostsStatusMsg', GetHostsStatusMsg)
|
.factory('GetHostsStatusMsg', GetHostsStatusMsg)
|
||||||
.factory('GetSourceTypeOptions', GetSourceTypeOptions)
|
.factory('GetSourceTypeOptions', GetSourceTypeOptions)
|
||||||
|
17
awx/ui/client/src/inventories/groups/nested-groups/main.js
Normal file
17
awx/ui/client/src/inventories/groups/nested-groups/main.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2017 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import nestedGroupListState from './nested-groups-list-state.factory';
|
||||||
|
import nestedGroupListDefinition from './nested-groups.list';
|
||||||
|
import nestedGroupFormDefinition from './nested-groups.form';
|
||||||
|
import controller from './nested-groups-list.controller';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('nestedGroups', [])
|
||||||
|
.factory('nestedGroupListState', nestedGroupListState)
|
||||||
|
.value('NestedGroupListDefinition', nestedGroupListDefinition)
|
||||||
|
.factory('NestedGroupFormDefinition', nestedGroupFormDefinition)
|
||||||
|
.controller('NestedGroupsListController', controller);
|
@ -0,0 +1,224 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
export default
|
||||||
|
['$scope', '$rootScope', '$state', '$stateParams', 'NestedGroupListDefinition', 'InventoryUpdate',
|
||||||
|
'GroupManageService', 'GroupsCancelUpdate', 'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath',
|
||||||
|
'GetSyncStatusMsg', 'GetHostsStatusMsg', 'Dataset', 'Find', 'QuerySet', 'inventoryData',
|
||||||
|
function($scope, $rootScope, $state, $stateParams, NestedGroupListDefinition, InventoryUpdate,
|
||||||
|
GroupManageService, GroupsCancelUpdate, ViewUpdateStatus, rbacUiControlService, GetBasePath,
|
||||||
|
GetSyncStatusMsg, GetHostsStatusMsg, Dataset, Find, qs, inventoryData){
|
||||||
|
|
||||||
|
let list = NestedGroupListDefinition;
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
function init(){
|
||||||
|
$scope.inventory_id = $stateParams.inventory_id;
|
||||||
|
$scope.canAdhoc = inventoryData.summary_fields.user_capabilities.adhoc;
|
||||||
|
$scope.canAdd = false;
|
||||||
|
|
||||||
|
rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/groups")
|
||||||
|
.then(function(canAdd) {
|
||||||
|
$scope.canAdd = canAdd;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search init
|
||||||
|
$scope.list = list;
|
||||||
|
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||||
|
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||||
|
|
||||||
|
// The ncy breadcrumb directive will look at this attribute when attempting to bind to the correct scope.
|
||||||
|
// In this case, we don't want to incidentally bind to this scope when editing a host or a group. See:
|
||||||
|
// https://github.com/ncuillery/angular-breadcrumb/issues/42 for a little more information on the
|
||||||
|
// problem that this solves.
|
||||||
|
$scope.ncyBreadcrumbIgnore = true;
|
||||||
|
if($state.current.name === "inventoryManage.editGroup") {
|
||||||
|
$scope.rowBeingEdited = $state.params.group_id;
|
||||||
|
$scope.listBeingEdited = "groups";
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.inventory_id = $stateParams.inventory_id;
|
||||||
|
_.forEach($scope[list.name], buildStatusIndicators);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStatusIndicators(group){
|
||||||
|
if (group === undefined || group === null) {
|
||||||
|
group = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let hosts_status;
|
||||||
|
|
||||||
|
hosts_status = GetHostsStatusMsg({
|
||||||
|
active_failures: group.hosts_with_active_failures,
|
||||||
|
total_hosts: group.total_hosts,
|
||||||
|
inventory_id: $scope.inventory_id,
|
||||||
|
group_id: group.id
|
||||||
|
});
|
||||||
|
_.assign(group,
|
||||||
|
{hosts_status_tip: hosts_status.tooltip},
|
||||||
|
{hosts_status_class: hosts_status.class});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.groupSelect = function(id){
|
||||||
|
var group = $stateParams.group === undefined ? [id] : _($stateParams.group).concat(id).value();
|
||||||
|
$state.go('inventoryManage', {
|
||||||
|
inventory_id: $stateParams.inventory_id,
|
||||||
|
group: group,
|
||||||
|
group_search: {
|
||||||
|
page_size: '20',
|
||||||
|
page: '1',
|
||||||
|
order_by: 'name',
|
||||||
|
}
|
||||||
|
}, {reload: true});
|
||||||
|
};
|
||||||
|
$scope.createGroup = function(){
|
||||||
|
$state.go('inventories.edit.groups.add');
|
||||||
|
};
|
||||||
|
$scope.editGroup = function(id){
|
||||||
|
$state.go('inventories.edit.groups.edit', {group_id: id});
|
||||||
|
};
|
||||||
|
$scope.deleteGroup = function(group){
|
||||||
|
$scope.toDelete = {};
|
||||||
|
angular.extend($scope.toDelete, group);
|
||||||
|
if($scope.toDelete.total_groups === 0 && $scope.toDelete.total_hosts === 0) {
|
||||||
|
// This group doesn't have any child groups or hosts - the user is just trying to delete
|
||||||
|
// the group
|
||||||
|
$scope.deleteOption = "delete";
|
||||||
|
}
|
||||||
|
$('#group-delete-modal').modal('show');
|
||||||
|
};
|
||||||
|
$scope.confirmDelete = function(){
|
||||||
|
|
||||||
|
// Bind an even listener for the modal closing. Trying to $state.go() before the modal closes
|
||||||
|
// will mean that these two things are running async and the modal may not finish closing before
|
||||||
|
// the state finishes transitioning.
|
||||||
|
$('#group-delete-modal').off('hidden.bs.modal').on('hidden.bs.modal', function () {
|
||||||
|
// Remove the event handler so that we don't end up with multiple bindings
|
||||||
|
$('#group-delete-modal').off('hidden.bs.modal');
|
||||||
|
// Reload the inventory manage page and show that the group has been removed
|
||||||
|
$state.go('inventoryManage', null, {reload: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
switch($scope.deleteOption){
|
||||||
|
case 'promote':
|
||||||
|
GroupManageService.promote($scope.toDelete.id, $stateParams.inventory_id)
|
||||||
|
.then(() => {
|
||||||
|
if (parseInt($state.params.group_id) === $scope.toDelete.id) {
|
||||||
|
$state.go("inventoryManage", null, {reload: true});
|
||||||
|
} else {
|
||||||
|
$state.go($state.current, null, {reload: true});
|
||||||
|
}
|
||||||
|
$('#group-delete-modal').modal('hide');
|
||||||
|
$('body').removeClass('modal-open');
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GroupManageService.delete($scope.toDelete.id).then(() => {
|
||||||
|
if (parseInt($state.params.group_id) === $scope.toDelete.id) {
|
||||||
|
$state.go("inventoryManage", null, {reload: true});
|
||||||
|
} else {
|
||||||
|
$state.go($state.current, null, {reload: true});
|
||||||
|
}
|
||||||
|
$('#group-delete-modal').modal('hide');
|
||||||
|
$('body').removeClass('modal-open');
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$scope.updateGroup = function(group) {
|
||||||
|
GroupManageService.getInventorySource({group: group.id}).then(res =>InventoryUpdate({
|
||||||
|
scope: $scope,
|
||||||
|
group_id: group.id,
|
||||||
|
url: res.data.results[0].related.update,
|
||||||
|
group_name: group.name,
|
||||||
|
group_source: res.data.results[0].source
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$on(`ws-jobs`, function(e, data){
|
||||||
|
var group = Find({ list: $scope.groups, key: 'id', val: data.group_id });
|
||||||
|
|
||||||
|
if (group === undefined || group === null) {
|
||||||
|
group = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.status === 'failed' || data.status === 'successful'){
|
||||||
|
let path;
|
||||||
|
if($stateParams && $stateParams.group && $stateParams.group.length > 0) {
|
||||||
|
path = GetBasePath('groups') + _.last($stateParams.group) + '/children';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//reaches here if the user is on the root level group
|
||||||
|
path = GetBasePath('inventory') + $stateParams.inventory_id + '/root_groups';
|
||||||
|
}
|
||||||
|
qs.search(path, $state.params[`${list.iterator}_search`])
|
||||||
|
.then(function(searchResponse) {
|
||||||
|
$scope[`${list.iterator}_dataset`] = searchResponse.data;
|
||||||
|
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||||
|
_.forEach($scope[list.name], buildStatusIndicators);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var status = GetSyncStatusMsg({
|
||||||
|
status: data.status,
|
||||||
|
has_inventory_sources: group.has_inventory_sources,
|
||||||
|
source: group.source
|
||||||
|
});
|
||||||
|
group.status = data.status;
|
||||||
|
group.status_class = status.class;
|
||||||
|
group.status_tooltip = status.tooltip;
|
||||||
|
group.launch_tooltip = status.launch_tip;
|
||||||
|
group.launch_class = status.launch_class;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.cancelUpdate = function (id) {
|
||||||
|
GroupsCancelUpdate({ scope: $scope, id: id });
|
||||||
|
};
|
||||||
|
$scope.viewUpdateStatus = function (id) {
|
||||||
|
ViewUpdateStatus({
|
||||||
|
scope: $scope,
|
||||||
|
group_id: id
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$scope.showFailedHosts = function() {
|
||||||
|
$state.go('inventoryManage', {failed: true}, {reload: true});
|
||||||
|
};
|
||||||
|
$scope.scheduleGroup = function(id) {
|
||||||
|
// Add this group's id to the array of group id's so that it gets
|
||||||
|
// added to the breadcrumb trail
|
||||||
|
var groupsArr = $stateParams.group ? $stateParams.group : [];
|
||||||
|
groupsArr.push(id);
|
||||||
|
$state.go('inventoryManage.editGroup.schedules', {group_id: id, group: groupsArr}, {reload: true});
|
||||||
|
};
|
||||||
|
// $scope.$parent governed by InventoryManageController, for unified multiSelect options
|
||||||
|
$scope.$on('multiSelectList.selectionChanged', (event, selection) => {
|
||||||
|
$scope.$parent.groupsSelected = selection.length > 0 ? true : false;
|
||||||
|
$scope.$parent.groupsSelectedItems = selection.selectedItems;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.copyMoveGroup = function(id){
|
||||||
|
$state.go('inventoryManage.copyMoveGroup', {group_id: id, groups: $stateParams.groups});
|
||||||
|
};
|
||||||
|
|
||||||
|
var cleanUpStateChangeListener = $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
|
||||||
|
if (toState.name === "inventoryManage.editGroup") {
|
||||||
|
$scope.rowBeingEdited = toParams.group_id;
|
||||||
|
$scope.listBeingEdited = "groups";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete $scope.rowBeingEdited;
|
||||||
|
delete $scope.listBeingEdited;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the listener when the scope is destroyed to avoid a memory leak
|
||||||
|
$scope.$on('$destroy', function() {
|
||||||
|
cleanUpStateChangeListener();
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
@ -0,0 +1,98 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name forms.function:Groups
|
||||||
|
* @description This form is for adding/editing a Group on the inventory page
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ['i18n', 'nestedGroupListState',
|
||||||
|
function(i18n, nestedGroupListState){
|
||||||
|
return {
|
||||||
|
addTitle: 'CREATE GROUP',
|
||||||
|
editTitle: '{{ name }}',
|
||||||
|
showTitle: true,
|
||||||
|
name: 'nested_group',
|
||||||
|
iterator: "nested_group",
|
||||||
|
basePath: 'groups',
|
||||||
|
parent: 'inventories.edit.groups',
|
||||||
|
// the parent node this generated state definition tree expects to attach to
|
||||||
|
stateTree: 'inventories',
|
||||||
|
// form generator inspects the current state name to determine whether or not to set an active (.is-selected) class on a form tab
|
||||||
|
// this setting is optional on most forms, except where the form's edit state name is not parentStateName.edit
|
||||||
|
activeEditState: 'inventories.edit.groups.edit',
|
||||||
|
detailsClick: "$state.go('inventories.edit.groups.edit')",
|
||||||
|
well: false,
|
||||||
|
tabs: true,
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
label: 'Name',
|
||||||
|
type: 'text',
|
||||||
|
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||||
|
required: true,
|
||||||
|
tab: 'properties'
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)',
|
||||||
|
tab: 'properties'
|
||||||
|
},
|
||||||
|
variables: {
|
||||||
|
label: 'Variables',
|
||||||
|
type: 'textarea',
|
||||||
|
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||||
|
rows: 6,
|
||||||
|
'default': '---',
|
||||||
|
dataTitle: 'Group Variables',
|
||||||
|
dataPlacement: 'right',
|
||||||
|
parseTypeName: 'parseType',
|
||||||
|
awPopOver: "<p>Variables defined here apply to all child groups and hosts.</p>" +
|
||||||
|
"<p>Enter variables using either JSON or YAML syntax. Use the " +
|
||||||
|
"radio button to toggle between the two.</p>" +
|
||||||
|
"JSON:<br />\n" +
|
||||||
|
"<blockquote>{<br />  \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
||||||
|
"YAML:<br />\n" +
|
||||||
|
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
|
||||||
|
'<p>View JSON examples at <a href="http://www.json.org" target="_blank">www.json.org</a></p>' +
|
||||||
|
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||||
|
dataContainer: 'body',
|
||||||
|
tab: 'properties'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buttons: {
|
||||||
|
cancel: {
|
||||||
|
ngClick: 'formCancel()',
|
||||||
|
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||||
|
},
|
||||||
|
close: {
|
||||||
|
ngClick: 'formCancel()',
|
||||||
|
ngShow: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
ngClick: 'formSave()',
|
||||||
|
ngDisabled: true,
|
||||||
|
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
related: {
|
||||||
|
nested_groups: {
|
||||||
|
name: 'related_groups',
|
||||||
|
ngClick: "$state.go('inventories.edit.groups.edit.related_groups')",
|
||||||
|
include: "RelatedGroupListDefinition",
|
||||||
|
includeForm: "RelatedGroupFormDefinition",
|
||||||
|
title: i18n._('Groups'),
|
||||||
|
iterator: 'related_group',
|
||||||
|
listState: nestedGroupListState,
|
||||||
|
// addState: buildGroupsAddState,
|
||||||
|
// editState: buildGroupsEditState
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}];
|
@ -0,0 +1,146 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2017 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'nested_groups',
|
||||||
|
iterator: 'nested_group',
|
||||||
|
editTitle: '{{ inventory.name }}',
|
||||||
|
well: true,
|
||||||
|
wellOverride: true,
|
||||||
|
index: false,
|
||||||
|
hover: true,
|
||||||
|
multiSelect: true,
|
||||||
|
trackBy: 'nested_group.id',
|
||||||
|
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/root_groups/',
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
failed_hosts: {
|
||||||
|
label: '',
|
||||||
|
nosort: true,
|
||||||
|
mode: 'all',
|
||||||
|
iconOnly: true,
|
||||||
|
awToolTip: "{{ group.hosts_status_tip }}",
|
||||||
|
dataPlacement: "top",
|
||||||
|
ngClick: "showFailedHosts(group)",
|
||||||
|
icon: "{{ 'fa icon-job-' + group.hosts_status_class }}",
|
||||||
|
columnClass: 'status-column List-staticColumn--smallStatus'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
label: 'Groups',
|
||||||
|
key: true,
|
||||||
|
|
||||||
|
// ngClick: "groupSelect(group.id)",
|
||||||
|
ngClick: "editGroup(group.id)",
|
||||||
|
columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6',
|
||||||
|
class: 'InventoryManage-breakWord',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
refresh: {
|
||||||
|
mode: 'all',
|
||||||
|
awToolTip: "Refresh the page",
|
||||||
|
ngClick: "refreshGroups()",
|
||||||
|
ngShow: "socketStatus == 'error'",
|
||||||
|
actionClass: 'btn List-buttonDefault',
|
||||||
|
buttonContent: 'REFRESH'
|
||||||
|
},
|
||||||
|
launch: {
|
||||||
|
mode: 'all',
|
||||||
|
// $scope.$parent is governed by InventoryManageController,
|
||||||
|
ngDisabled: '!$parent.groupsSelected && !$parent.hostsSelected',
|
||||||
|
ngClick: '$parent.setAdhocPattern()',
|
||||||
|
awToolTip: "Select an inventory source by clicking the check box beside it. The inventory source can be a single group or host, a selection of multiple hosts, or a selection of multiple groups.",
|
||||||
|
dataTipWatch: "adhocCommandTooltip",
|
||||||
|
actionClass: 'btn List-buttonDefault',
|
||||||
|
buttonContent: 'RUN COMMANDS',
|
||||||
|
showTipWhenDisabled: true,
|
||||||
|
tooltipInnerClass: "Tooltip-wide",
|
||||||
|
ngShow: 'canAdhoc'
|
||||||
|
// TODO: set up a tip watcher and change text based on when
|
||||||
|
// things are selected/not selected. This is started and
|
||||||
|
// commented out in the inventory controller within the watchers.
|
||||||
|
// awToolTip: "{{ adhocButtonTipContents }}",
|
||||||
|
// dataTipWatch: "adhocButtonTipContents"
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: "createGroup()",
|
||||||
|
awToolTip: "Create a new group",
|
||||||
|
actionClass: 'btn List-buttonSubmit',
|
||||||
|
buttonContent: '+ ADD GROUP',
|
||||||
|
ngShow: 'canAdd',
|
||||||
|
dataPlacement: "top",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldActions: {
|
||||||
|
|
||||||
|
columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6 text-right',
|
||||||
|
|
||||||
|
// group_update: {
|
||||||
|
// //label: 'Sync',
|
||||||
|
// mode: 'all',
|
||||||
|
// ngClick: 'updateGroup(group)',
|
||||||
|
// awToolTip: "{{ group.launch_tooltip }}",
|
||||||
|
// dataTipWatch: "group.launch_tooltip",
|
||||||
|
// ngShow: "(group.status !== 'running' && group.status " +
|
||||||
|
// "!== 'pending' && group.status !== 'updating') && group.summary_fields.user_capabilities.start",
|
||||||
|
// ngClass: "group.launch_class",
|
||||||
|
// dataPlacement: "top",
|
||||||
|
// },
|
||||||
|
// cancel: {
|
||||||
|
// //label: 'Cancel',
|
||||||
|
// mode: 'all',
|
||||||
|
// ngClick: "cancelUpdate(group.id)",
|
||||||
|
// awToolTip: "Cancel sync process",
|
||||||
|
// 'class': 'red-txt',
|
||||||
|
// ngShow: "(group.status == 'running' || group.status == 'pending' " +
|
||||||
|
// "|| group.status == 'updating') && group.summary_fields.user_capabilities.start",
|
||||||
|
// dataPlacement: "top",
|
||||||
|
// iconClass: "fa fa-minus-circle"
|
||||||
|
// },
|
||||||
|
copy: {
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: "copyMoveGroup(group.id)",
|
||||||
|
awToolTip: 'Copy or move group',
|
||||||
|
ngShow: "group.id > 0 && group.summary_fields.user_capabilities.copy",
|
||||||
|
dataPlacement: "top"
|
||||||
|
},
|
||||||
|
// schedule: {
|
||||||
|
// mode: 'all',
|
||||||
|
// ngClick: "scheduleGroup(group.id)",
|
||||||
|
// awToolTip: "{{ group.group_schedule_tooltip }}",
|
||||||
|
// ngClass: "group.scm_type_class",
|
||||||
|
// dataPlacement: 'top',
|
||||||
|
// ngShow: "!(group.summary_fields.inventory_source.source === '')"
|
||||||
|
// },
|
||||||
|
edit: {
|
||||||
|
//label: 'Edit',
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: "editGroup(group.id)",
|
||||||
|
awToolTip: 'Edit group',
|
||||||
|
dataPlacement: "top",
|
||||||
|
ngShow: "group.summary_fields.user_capabilities.edit"
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
//label: 'Edit',
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: "editGroup(group.id)",
|
||||||
|
awToolTip: 'View group',
|
||||||
|
dataPlacement: "top",
|
||||||
|
ngShow: "!group.summary_fields.user_capabilities.edit"
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
//label: 'Delete',
|
||||||
|
mode: 'all',
|
||||||
|
ngClick: "deleteGroup(group)",
|
||||||
|
awToolTip: 'Delete group',
|
||||||
|
dataPlacement: "top",
|
||||||
|
ngShow: "group.summary_fields.user_capabilities.delete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -13,10 +13,29 @@
|
|||||||
export default ['i18n', 'buildGroupsListState', 'buildGroupsAddState',
|
export default ['i18n', 'buildGroupsListState', 'buildGroupsAddState',
|
||||||
'buildGroupsEditState', 'buildHostListState', 'buildHostAddState',
|
'buildGroupsEditState', 'buildHostListState', 'buildHostAddState',
|
||||||
'buildHostEditState', 'buildSourcesListState', 'buildSourcesAddState',
|
'buildHostEditState', 'buildSourcesListState', 'buildSourcesAddState',
|
||||||
'buildSourcesEditState',
|
'buildSourcesEditState', 'buildInventoryCompletedJobsState',
|
||||||
|
'InventoryCompletedJobsList',
|
||||||
function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState,
|
function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState,
|
||||||
buildHostListState, buildHostAddState, buildHostEditState,
|
buildHostListState, buildHostAddState, buildHostEditState,
|
||||||
buildSourcesListState, buildSourcesAddState,buildSourcesEditState) {
|
buildSourcesListState, buildSourcesAddState,buildSourcesEditState,
|
||||||
|
buildInventoryCompletedJobsState, InventoryCompletedJobsList) {
|
||||||
|
|
||||||
|
var completed_jobs_object = {
|
||||||
|
name: 'completed_jobs',
|
||||||
|
index: false,
|
||||||
|
basePath: "unified_jobs",
|
||||||
|
include: "InventoryCompletedJobsList",
|
||||||
|
title: i18n._('Completed Jobs'),
|
||||||
|
iterator: 'completed_job',
|
||||||
|
generateList: true,
|
||||||
|
listState: buildInventoryCompletedJobsState,
|
||||||
|
search: {
|
||||||
|
"or__job__inventory": ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let clone = _.clone(InventoryCompletedJobsList);
|
||||||
|
completed_jobs_object = angular.extend(clone, completed_jobs_object);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
addTitle: i18n._('NEW INVENTORY'),
|
addTitle: i18n._('NEW INVENTORY'),
|
||||||
@ -96,7 +115,7 @@ function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState,
|
|||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
awToolTip: i18n._('Please save before assigning permissions'),
|
awToolTip: i18n._('Please save before assigning permissions'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'api/v1/inventories/{{$stateParams.inventory_id}}/access_list/',
|
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/access_list/',
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
title: i18n._('Permissions'),
|
title: i18n._('Permissions'),
|
||||||
iterator: 'permission',
|
iterator: 'permission',
|
||||||
@ -165,39 +184,7 @@ function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState,
|
|||||||
addState: buildSourcesAddState,
|
addState: buildSourcesAddState,
|
||||||
editState: buildSourcesEditState
|
editState: buildSourcesEditState
|
||||||
},
|
},
|
||||||
//this is a placeholder for when we're ready for completed jobs
|
completed_jobs: completed_jobs_object
|
||||||
completed_jobs: {
|
|
||||||
name: 'completed_jobs',
|
|
||||||
// awToolTip: i18n._('Please save before assigning permissions'),
|
|
||||||
// dataPlacement: 'top',
|
|
||||||
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/completed_jobs/',
|
|
||||||
type: 'collection',
|
|
||||||
title: i18n._('Completed Jobs'),
|
|
||||||
iterator: 'completed_job',
|
|
||||||
index: false,
|
|
||||||
open: false,
|
|
||||||
// search: {
|
|
||||||
// order_by: 'username'
|
|
||||||
// },
|
|
||||||
actions: {
|
|
||||||
add: {
|
|
||||||
label: i18n._('Add'),
|
|
||||||
ngClick: "$state.go('.add')",
|
|
||||||
awToolTip: i18n._('Add a permission'),
|
|
||||||
actionClass: 'btn List-buttonSubmit',
|
|
||||||
buttonContent: '+ ADD',
|
|
||||||
// ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
name: {
|
|
||||||
label: i18n._('Name'),
|
|
||||||
// linkBase: 'users',
|
|
||||||
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};}];
|
};}];
|
||||||
|
@ -8,6 +8,7 @@ import host from './hosts/main';
|
|||||||
import group from './groups/main';
|
import group from './groups/main';
|
||||||
import sources from './sources/main';
|
import sources from './sources/main';
|
||||||
import relatedHost from './related-hosts/main';
|
import relatedHost from './related-hosts/main';
|
||||||
|
import inventoryCompletedJobs from './completed_jobs/main';
|
||||||
import inventoryAdd from './add/main';
|
import inventoryAdd from './add/main';
|
||||||
import inventoryEdit from './edit/main';
|
import inventoryEdit from './edit/main';
|
||||||
import inventoryList from './list/main';
|
import inventoryList from './list/main';
|
||||||
@ -22,6 +23,7 @@ angular.module('inventory', [
|
|||||||
group.name,
|
group.name,
|
||||||
sources.name,
|
sources.name,
|
||||||
relatedHost.name,
|
relatedHost.name,
|
||||||
|
inventoryCompletedJobs.name,
|
||||||
inventoryAdd.name,
|
inventoryAdd.name,
|
||||||
inventoryEdit.name,
|
inventoryEdit.name,
|
||||||
inventoryList.name
|
inventoryList.name
|
||||||
|
@ -576,6 +576,7 @@ function($injector, $stateExtender, $log, i18n) {
|
|||||||
if(field.includeForm){
|
if(field.includeForm){
|
||||||
let form = field.includeForm ? $injector.get(field.includeForm) : field;
|
let form = field.includeForm ? $injector.get(field.includeForm) : field;
|
||||||
states.push(that.generateLookupNodes(form, formState));
|
states.push(that.generateLookupNodes(form, formState));
|
||||||
|
states.push(that.generateFormListDefinitions(form, formState, params));
|
||||||
}
|
}
|
||||||
states = _.flatten(states);
|
states = _.flatten(states);
|
||||||
}
|
}
|
||||||
@ -678,6 +679,7 @@ function($injector, $stateExtender, $log, i18n) {
|
|||||||
if (field.search) {
|
if (field.search) {
|
||||||
state.params[`${field.iterator}_search`].value = _.merge(state.params[`${field.iterator}_search`].value, field.search);
|
state.params[`${field.iterator}_search`].value = _.merge(state.params[`${field.iterator}_search`].value, field.search);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
return _(form.related).map(buildListNodes).flatten().value();
|
return _(form.related).map(buildListNodes).flatten().value();
|
||||||
|
Loading…
Reference in New Issue
Block a user