mirror of
https://github.com/ansible/awx.git
synced 2026-01-19 05:31:22 -03:30
Merge pull request #125 from jlmitch5/adhoc_pr
UI Support for adhoc commands
This commit is contained in:
commit
4b1322932f
@ -227,7 +227,7 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
def get_type_choices(self):
|
||||
type_name_map = {
|
||||
'job': 'Playbook Run',
|
||||
'ad_hoc_command': 'Ad Hoc Command',
|
||||
'ad_hoc_command': 'Command',
|
||||
'project_update': 'SCM Update',
|
||||
'inventory_update': 'Inventory Sync',
|
||||
'system_job': 'Management Job',
|
||||
|
||||
@ -41,6 +41,7 @@ import {ScheduleEditController} from 'tower/controllers/Schedules';
|
||||
import {ProjectsList, ProjectsAdd, ProjectsEdit} from 'tower/controllers/Projects';
|
||||
import {OrganizationsList, OrganizationsAdd, OrganizationsEdit} from 'tower/controllers/Organizations';
|
||||
import {InventoriesList, InventoriesAdd, InventoriesEdit, InventoriesManage} from 'tower/controllers/Inventories';
|
||||
import {AdhocCtrl} from 'tower/controllers/Adhoc';
|
||||
import {AdminsList} from 'tower/controllers/Admins';
|
||||
import {UsersList, UsersAdd, UsersEdit} from 'tower/controllers/Users';
|
||||
import {TeamsList, TeamsAdd, TeamsEdit} from 'tower/controllers/Teams';
|
||||
@ -89,6 +90,7 @@ var tower = angular.module('Tower', [
|
||||
'AdminListDefinition',
|
||||
'CustomInventoryListDefinition',
|
||||
'AWDirectives',
|
||||
'AdhocFormDefinition',
|
||||
'InventoriesListDefinition',
|
||||
'InventoryFormDefinition',
|
||||
'InventoryHelper',
|
||||
@ -169,7 +171,8 @@ var tower = angular.module('Tower', [
|
||||
'ConfigureTowerHelper',
|
||||
'ConfigureTowerJobsListDefinition',
|
||||
'CreateCustomInventoryHelper',
|
||||
'CustomInventoryListDefinition'
|
||||
'CustomInventoryListDefinition',
|
||||
'AdhocHelper'
|
||||
])
|
||||
|
||||
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
|
||||
@ -201,6 +204,11 @@ var tower = angular.module('Tower', [
|
||||
controller: JobStdoutController
|
||||
}).
|
||||
|
||||
when('/ad_hoc_commands/:id', {
|
||||
templateUrl: urlPrefix + 'partials/job_stdout_adhoc.html',
|
||||
controller: JobStdoutController
|
||||
}).
|
||||
|
||||
when('/job_templates', {
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesList
|
||||
@ -281,6 +289,11 @@ var tower = angular.module('Tower', [
|
||||
controller: InventoriesManage
|
||||
}).
|
||||
|
||||
when('/inventories/:inventory_id/adhoc', {
|
||||
templateUrl: urlPrefix + 'partials/adhoc.html',
|
||||
controller: AdhocCtrl
|
||||
}).
|
||||
|
||||
when('/organizations', {
|
||||
templateUrl: urlPrefix + 'partials/organizations.html',
|
||||
controller: OrganizationsList
|
||||
|
||||
172
awx/ui/static/js/controllers/Adhoc.js
Normal file
172
awx/ui/static/js/controllers/Adhoc.js
Normal file
@ -0,0 +1,172 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 AnsibleWorks, Inc.
|
||||
*
|
||||
* Adhoc.js
|
||||
*
|
||||
* Controller functions for the Adhoc model.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Adhoc
|
||||
* @description This controller controls the adhoc form creation, command launching and navigating to standard out after command has been succesfully ran.
|
||||
*/
|
||||
export function AdhocCtrl($scope, $rootScope, $location, $routeParams,
|
||||
AdhocForm, GenerateForm, Rest, ProcessErrors, ClearScope, GetBasePath,
|
||||
GetChoices, KindChange, LookUpInit, CredentialList, Empty, OwnerChange,
|
||||
LoginMethodChange, Wait) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var url = GetBasePath('inventory') + $routeParams.inventory_id +
|
||||
'/ad_hoc_commands/',
|
||||
generator = GenerateForm,
|
||||
form = AdhocForm,
|
||||
master = {},
|
||||
id = $routeParams.inventory_id;
|
||||
|
||||
// inject the adhoc command form
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
generator.reset();
|
||||
|
||||
// BEGIN: populate scope with the things needed to make the adhoc form
|
||||
// display
|
||||
Wait('start');
|
||||
$scope.id = id;
|
||||
$scope.argsPopOver = "<p>These arguments are used with the" +
|
||||
" specified module.</p>";
|
||||
// fix arguments help popover based on the module selected
|
||||
$scope.moduleChange = function () {
|
||||
// NOTE: for selenium testing link -
|
||||
// link will be displayed with id adhoc_module_arguments_docs_link
|
||||
// only when a module is selected
|
||||
if ($scope.module_name) {
|
||||
// give the docs for the selected module
|
||||
$scope.argsPopOver = "<p>These arguments are used with the" +
|
||||
" specified module. You can find information about the " +
|
||||
$scope.module_name.value +
|
||||
" <a id=\"adhoc_module_arguments_docs_link_for_module_" +
|
||||
$scope.module_name.value +
|
||||
"\"" +
|
||||
" href=\"http://docs.ansible.com/" + $scope.module_name.value +
|
||||
"_module.html\" target=\"_blank\">here</a>.</p>";
|
||||
} else {
|
||||
// no module selected
|
||||
$scope.argsPopOver = "<p>These arguments are used with the" +
|
||||
" specified module.</p>";
|
||||
}
|
||||
};
|
||||
|
||||
// pre-populate hostPatterns from the inventory page and
|
||||
// delete the value off of rootScope
|
||||
$scope.limit = $rootScope.hostPatterns || "all";
|
||||
$scope.providedHostPatterns = $scope.limit;
|
||||
delete $rootScope.hostPatterns;
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyAdhoc', function () {
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: (!Empty($scope.credential_id)) ? $scope.credential_id : null,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
OwnerChange({ scope: $scope });
|
||||
LoginMethodChange({ scope: $scope });
|
||||
|
||||
Wait('stop'); // END: form population
|
||||
});
|
||||
|
||||
// setup Machine Credential lookup
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: url,
|
||||
field: 'module_name',
|
||||
variable: 'adhoc_module_options',
|
||||
callback: 'choicesReadyAdhoc'
|
||||
});
|
||||
|
||||
// Handle Owner change
|
||||
$scope.ownerChange = function () {
|
||||
OwnerChange({ scope: $scope });
|
||||
};
|
||||
|
||||
// Handle Login Method change
|
||||
$scope.loginMethodChange = function () {
|
||||
LoginMethodChange({ scope: $scope });
|
||||
};
|
||||
|
||||
// Handle Kind change
|
||||
$scope.kindChange = function () {
|
||||
KindChange({ scope: $scope, form: form, reset: true });
|
||||
};
|
||||
|
||||
// launch the job with the provided form data
|
||||
$scope.launchJob = function () {
|
||||
var fld, data={};
|
||||
|
||||
// stub the payload with defaults from DRF
|
||||
data = {
|
||||
"job_type": "run",
|
||||
"limit": "",
|
||||
"credential": null,
|
||||
"module_name": "command",
|
||||
"module_args": "",
|
||||
"forks": 0,
|
||||
"verbosity": 0,
|
||||
"privilege_escalation": ""
|
||||
};
|
||||
|
||||
generator.clearApiErrors();
|
||||
|
||||
// populate data with the relevant form values
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].type === 'select') {
|
||||
data[fld] = $scope[fld].value;
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
|
||||
// Launch the adhoc job
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
Wait('stop');
|
||||
$location.path("/ad_hoc_commands/" + data.id);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to launch adhoc command. POST returned status: ' +
|
||||
status });
|
||||
// TODO: still need to implement popping up a password prompt
|
||||
// if the credential requires it. The way that the current end-
|
||||
// point works is that I find out if I need to ask for a
|
||||
// password from POST, thus I get an error response.
|
||||
});
|
||||
};
|
||||
|
||||
// Remove all data input into the form
|
||||
$scope.formReset = function () {
|
||||
generator.reset();
|
||||
for (var fld in master) {
|
||||
$scope[fld] = master[fld];
|
||||
}
|
||||
$scope.limit = $scope.providedHostPatterns;
|
||||
KindChange({ scope: $scope, form: form, reset: false });
|
||||
OwnerChange({ scope: $scope });
|
||||
LoginMethodChange({ scope: $scope });
|
||||
};
|
||||
}
|
||||
|
||||
AdhocCtrl.$inject = ['$scope', '$rootScope', '$location', '$routeParams',
|
||||
'AdhocForm', 'GenerateForm', 'Rest', 'ProcessErrors', 'ClearScope',
|
||||
'GetBasePath', 'GetChoices', 'KindChange', 'LookUpInit', 'CredentialList',
|
||||
'Empty', 'OwnerChange', 'LoginMethodChange', 'Wait'];
|
||||
@ -15,12 +15,12 @@
|
||||
|
||||
import 'tower/job-templates/main';
|
||||
|
||||
export function InventoriesList($scope, $rootScope, $location, $log, $routeParams, $compile, $filter, Rest, Alert, InventoryList, generateList,
|
||||
LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, Wait, Stream,
|
||||
export function InventoriesList($scope, $rootScope, $location, $log,
|
||||
$routeParams, $compile, $filter, Rest, Alert, InventoryList, generateList,
|
||||
LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
|
||||
ClearScope, ProcessErrors, GetBasePath, Wait, Stream,
|
||||
EditInventoryProperties, Find, Empty, LogViewer) {
|
||||
|
||||
//ClearScope();
|
||||
|
||||
var list = InventoryList,
|
||||
defaultUrl = GetBasePath('inventory'),
|
||||
view = generateList,
|
||||
@ -51,7 +51,10 @@ export function InventoriesList($scope, $rootScope, $location, $log, $routeParam
|
||||
// close any lingering tool tipss
|
||||
$(this).hide();
|
||||
});
|
||||
elem.attr({ "aw-pop-over": html, "data-popover-title": title, "data-placement": "right" });
|
||||
elem.attr({
|
||||
"aw-pop-over": html,
|
||||
"data-popover-title": title,
|
||||
"data-placement": "right" });
|
||||
$compile(elem)($scope);
|
||||
elem.on('shown.bs.popover', function() {
|
||||
$('.popover').each(function() {
|
||||
@ -837,12 +840,16 @@ InventoriesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$lo
|
||||
|
||||
|
||||
|
||||
export function InventoriesManage ($log, $scope, $location, $routeParams, $compile, generateList, ClearScope, Empty, Wait, Rest, Alert, LoadBreadCrumbs, GetBasePath, ProcessErrors,
|
||||
Breadcrumbs, InventoryGroups, InjectHosts, Find, HostsReload, SearchInit, PaginateInit, GetSyncStatusMsg, GetHostsStatusMsg, GroupsEdit, InventoryUpdate,
|
||||
GroupsCancelUpdate, ViewUpdateStatus, GroupsDelete, Store, HostsEdit, HostsDelete, EditInventoryProperties, ToggleHostEnabled, Stream, ShowJobSummary,
|
||||
InventoryGroupsHelp, HelpDialog, ViewJob, WatchInventoryWindowResize, GetHostContainerRows, GetGroupContainerRows, GetGroupContainerHeight,
|
||||
GroupsCopy, HostsCopy, Socket)
|
||||
{
|
||||
export function InventoriesManage ($log, $scope, $rootScope, $location,
|
||||
$routeParams, $compile, generateList, ClearScope, Empty, Wait, Rest, Alert,
|
||||
LoadBreadCrumbs, GetBasePath, ProcessErrors, Breadcrumbs, InventoryGroups,
|
||||
InjectHosts, Find, HostsReload, SearchInit, PaginateInit, GetSyncStatusMsg,
|
||||
GetHostsStatusMsg, GroupsEdit, InventoryUpdate, GroupsCancelUpdate,
|
||||
ViewUpdateStatus, GroupsDelete, Store, HostsEdit, HostsDelete,
|
||||
EditInventoryProperties, ToggleHostEnabled, Stream, ShowJobSummary,
|
||||
InventoryGroupsHelp, HelpDialog, ViewJob, WatchInventoryWindowResize,
|
||||
GetHostContainerRows, GetGroupContainerRows, GetGroupContainerHeight,
|
||||
GroupsCopy, HostsCopy, Socket) {
|
||||
|
||||
var PreviousSearchParams,
|
||||
url,
|
||||
@ -863,6 +870,53 @@ export function InventoriesManage ($log, $scope, $location, $routeParams, $compi
|
||||
show_failures: false
|
||||
}];
|
||||
|
||||
// TODO: only display adhoc button if the user has permission to use it.
|
||||
// TODO: figure out how to get the action-list partial to update so that
|
||||
// the tooltip can be changed based off things being selected or not.
|
||||
$scope.adhocButtonTipContents = "Launch adhoc command for the inventory";
|
||||
|
||||
// watcher for the group list checkbox changes
|
||||
$scope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
if (selection.length > 0) {
|
||||
$scope.groupsSelected = true;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "selected groups and hosts.";
|
||||
} else {
|
||||
$scope.groupsSelected = false;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "inventory.";
|
||||
}
|
||||
$scope.groupsSelectedItems = selection.selectedItems;
|
||||
});
|
||||
|
||||
// watcher for the host list checkbox changes
|
||||
hostScope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
// you need this so that the event doesn't bubble to the watcher above
|
||||
// for the host list
|
||||
e.stopPropagation();
|
||||
if (selection.length > 0) {
|
||||
$scope.hostsSelected = true;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "selected groups and hosts.";
|
||||
} else {
|
||||
$scope.hostsSelected = false;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "inventory.";
|
||||
}
|
||||
$scope.hostsSelectedItems = selection.selectedItems;
|
||||
});
|
||||
|
||||
// populates host patterns based on selected hosts/groups
|
||||
$scope.populateAdhocForm = function() {
|
||||
var host_patterns = "all";
|
||||
if ($scope.hostsSelected || $scope.groupsSelected) {
|
||||
var allSelectedItems = $scope.groupsSelectedItems.concat($scope.hostsSelectedItems);
|
||||
host_patterns = _.pluck(allSelectedItems, "name").join(":");
|
||||
}
|
||||
$rootScope.hostPatterns = host_patterns;
|
||||
$location.path('/inventories/' + $scope.inventory.id + '/adhoc');
|
||||
};
|
||||
|
||||
$scope.refreshHostsOnGroupRefresh = false;
|
||||
$scope.selected_group_id = null;
|
||||
|
||||
@ -1365,7 +1419,8 @@ export function InventoriesManage ($log, $scope, $location, $routeParams, $compi
|
||||
hostScope.show_failures = show_failures;
|
||||
$scope.groupSelect(group_id);
|
||||
hostScope.hosts = [];
|
||||
$scope.show_failures = show_failures; // turn on failed hosts filter in hosts view
|
||||
$scope.show_failures = show_failures; // turn on failed hosts
|
||||
// filter in hosts view
|
||||
} else {
|
||||
Wait('stop');
|
||||
}
|
||||
@ -1374,15 +1429,24 @@ export function InventoriesManage ($log, $scope, $location, $routeParams, $compi
|
||||
if ($scope.removeGroupDeleteCompleted) {
|
||||
$scope.removeGroupDeleteCompleted();
|
||||
}
|
||||
$scope.removeGroupDeleteCompleted = $scope.$on('GroupDeleteCompleted', function() {
|
||||
$scope.refreshGroups();
|
||||
});
|
||||
$scope.removeGroupDeleteCompleted = $scope.$on('GroupDeleteCompleted',
|
||||
function() {
|
||||
$scope.refreshGroups();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
InventoriesManage.$inject = ['$log', '$scope', '$location', '$routeParams', '$compile', 'generateList', 'ClearScope', 'Empty', 'Wait', 'Rest', 'Alert', 'LoadBreadCrumbs',
|
||||
'GetBasePath', 'ProcessErrors', 'Breadcrumbs', 'InventoryGroups', 'InjectHosts', 'Find', 'HostsReload', 'SearchInit', 'PaginateInit', 'GetSyncStatusMsg',
|
||||
'GetHostsStatusMsg', 'GroupsEdit', 'InventoryUpdate', 'GroupsCancelUpdate', 'ViewUpdateStatus', 'GroupsDelete', 'Store', 'HostsEdit', 'HostsDelete',
|
||||
'EditInventoryProperties', 'ToggleHostEnabled', 'Stream', 'ShowJobSummary', 'InventoryGroupsHelp', 'HelpDialog', 'ViewJob', 'WatchInventoryWindowResize',
|
||||
'GetHostContainerRows', 'GetGroupContainerRows', 'GetGroupContainerHeight', 'GroupsCopy', 'HostsCopy', 'Socket'
|
||||
];
|
||||
InventoriesManage.$inject = ['$log', '$scope', '$rootScope', '$location',
|
||||
'$routeParams', '$compile', 'generateList', 'ClearScope', 'Empty', 'Wait',
|
||||
'Rest', 'Alert', 'LoadBreadCrumbs', 'GetBasePath', 'ProcessErrors',
|
||||
'Breadcrumbs', 'InventoryGroups', 'InjectHosts', 'Find', 'HostsReload',
|
||||
'SearchInit', 'PaginateInit', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
|
||||
'GroupsEdit', 'InventoryUpdate', 'GroupsCancelUpdate', 'ViewUpdateStatus',
|
||||
'GroupsDelete', 'Store', 'HostsEdit', 'HostsDelete',
|
||||
'EditInventoryProperties', 'ToggleHostEnabled', 'Stream', 'ShowJobSummary',
|
||||
'InventoryGroupsHelp', 'HelpDialog', 'ViewJob',
|
||||
'WatchInventoryWindowResize', 'GetHostContainerRows',
|
||||
'GetGroupContainerRows', 'GetGroupContainerHeight', 'GroupsCopy',
|
||||
'HostsCopy', 'Socket'
|
||||
];
|
||||
|
||||
@ -1285,6 +1285,7 @@ export function JobDetailController ($location, $rootScope, $scope, $compile, $r
|
||||
status: status
|
||||
});
|
||||
};
|
||||
|
||||
scope.refresh = function(){
|
||||
$scope.$emit('LoadJob');
|
||||
};
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
|
||||
export function JobStdoutController ($log, $rootScope, $scope, $compile, $routeParams, ClearScope, GetBasePath, Wait, Rest, ProcessErrors, Socket) {
|
||||
export function JobStdoutController ($location, $log, $rootScope, $scope, $compile, $routeParams, ClearScope, GetBasePath, Wait, Rest, ProcessErrors, Socket) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
@ -152,7 +152,9 @@ export function JobStdoutController ($log, $rootScope, $scope, $compile, $routeP
|
||||
}
|
||||
});
|
||||
|
||||
Rest.setUrl(GetBasePath('jobs') + job_id + '/');
|
||||
// Note: could be ad_hoc_commands or jobs
|
||||
var jobType = $location.path().replace(/^\//, '').split('/')[0];
|
||||
Rest.setUrl(GetBasePath(jobType) + job_id + '/');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.job = data;
|
||||
@ -252,5 +254,5 @@ export function JobStdoutController ($log, $rootScope, $scope, $compile, $routeP
|
||||
|
||||
}
|
||||
|
||||
JobStdoutController.$inject = [ '$log', '$rootScope', '$scope', '$compile', '$routeParams', 'ClearScope', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors',
|
||||
JobStdoutController.$inject = [ '$location', '$log', '$rootScope', '$scope', '$compile', '$routeParams', 'ClearScope', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors',
|
||||
'Socket' ];
|
||||
|
||||
@ -1145,11 +1145,11 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log,
|
||||
action: action
|
||||
});
|
||||
});
|
||||
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.$emit("PromptForSurvey");
|
||||
}
|
||||
else {
|
||||
|
||||
PlaybookRun({
|
||||
scope: $scope,
|
||||
id: id
|
||||
|
||||
@ -65,6 +65,15 @@ export function PermissionsList($scope, $rootScope, $location, $log, $routeParam
|
||||
}
|
||||
};
|
||||
|
||||
// if the permission includes adhoc (and is not admin), display that
|
||||
$scope.getPermissionText = function () {
|
||||
if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) {
|
||||
return this.permission.permission_type + " + run commands";
|
||||
} else {
|
||||
return this.permission.permission_type;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.editPermission = function (id) {
|
||||
$location.path($location.path() + '/' + id);
|
||||
};
|
||||
@ -156,6 +165,11 @@ export function PermissionsAdd($scope, $rootScope, $compile, $location, $log, $r
|
||||
for (fld in form.fields) {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
// job template (or deploy) based permissions do not have the run
|
||||
// ad hoc commands parameter
|
||||
if (data.category === "Deploy") {
|
||||
data.run_ad_hoc_commands = false;
|
||||
}
|
||||
url = (base === 'teams') ? GetBasePath('teams') + id + '/permissions/' : GetBasePath('users') + id + '/permissions/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
@ -305,6 +319,11 @@ export function PermissionsEdit($scope, $rootScope, $compile, $location, $log, $
|
||||
for (fld in form.fields) {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
// job template (or deploy) based permissions do not have the run
|
||||
// ad hoc commands parameter
|
||||
if (data.category === "Deploy") {
|
||||
data.run_ad_hoc_commands = false;
|
||||
}
|
||||
Rest.setUrl(defaultUrl);
|
||||
if($scope.category === "Inventory"){
|
||||
delete data.project;
|
||||
@ -332,7 +351,8 @@ export function PermissionsEdit($scope, $rootScope, $compile, $location, $log, $
|
||||
|
||||
}
|
||||
|
||||
PermissionsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm',
|
||||
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'Prompt', 'GetBasePath',
|
||||
'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', 'Wait', 'PermissionCategoryChange'
|
||||
];
|
||||
PermissionsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$routeParams', 'PermissionsForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'Prompt',
|
||||
'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess',
|
||||
'Wait', 'PermissionCategoryChange'];
|
||||
|
||||
@ -294,6 +294,15 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
$routeParams.id + '. GET status: ' + status });
|
||||
});
|
||||
|
||||
// if the permission includes adhoc (and is not admin), display that
|
||||
$scope.getPermissionText = function () {
|
||||
if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) {
|
||||
return this.permission.permission_type + " + run commands";
|
||||
} else {
|
||||
return this.permission.permission_type;
|
||||
}
|
||||
};
|
||||
|
||||
// Save changes to the parent
|
||||
$scope.formSave = function () {
|
||||
var data = {}, fld;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import ActivityDetail from "tower/forms/ActivityDetail";
|
||||
import Credentials from "tower/forms/Credentials";
|
||||
import Adhoc from "tower/forms/Adhoc";
|
||||
import CustomInventory from "tower/forms/CustomInventory";
|
||||
import EventsViewer from "tower/forms/EventsViewer";
|
||||
import Groups from "tower/forms/Groups";
|
||||
@ -30,6 +31,7 @@ import Users from "tower/forms/Users";
|
||||
export
|
||||
{ ActivityDetail,
|
||||
Credentials,
|
||||
Adhoc,
|
||||
CustomInventory,
|
||||
EventsViewer,
|
||||
Groups,
|
||||
|
||||
98
awx/ui/static/js/forms/Adhoc.js
Normal file
98
awx/ui/static/js/forms/Adhoc.js
Normal file
@ -0,0 +1,98 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2015 AnsibleWorks, Inc.
|
||||
*
|
||||
* Adhoc.js
|
||||
* Form definition for the Adhoc model.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name forms.function:Adhoc
|
||||
* @description This form is for executing an adhoc command
|
||||
*/
|
||||
|
||||
export default
|
||||
angular.module('AdhocFormDefinition', [])
|
||||
.value('AdhocForm', {
|
||||
editTitle: 'Execute Command',
|
||||
name: 'adhoc',
|
||||
well: true,
|
||||
forceListeners: true,
|
||||
|
||||
fields: {
|
||||
module_name: {
|
||||
label: 'Module',
|
||||
excludeModal: true,
|
||||
type: 'select',
|
||||
ngOptions: 'module.label for module in adhoc_module_options' +
|
||||
' track by module.value',
|
||||
ngChange: 'moduleChange()',
|
||||
editRequired: true,
|
||||
awPopOver:'<p>These are the modules that Tower supports ' +
|
||||
'running commands against.',
|
||||
dataTitle: 'Module',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body'
|
||||
},
|
||||
module_args: {
|
||||
label: 'Arguments',
|
||||
type: 'text',
|
||||
awPopOverWatch: 'argsPopOver',
|
||||
awPopOver: 'See adhoc controller...set as argsPopOver',
|
||||
dataTitle: 'Arguments',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body',
|
||||
editRequired: false,
|
||||
autocomplete: false
|
||||
},
|
||||
limit: {
|
||||
label: 'Host Pattern',
|
||||
type: 'text',
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPopOver: '<p>The pattern used to target hosts in the ' +
|
||||
'inventory. Leaving the field blank, all, and * will ' +
|
||||
'all target all hosts in the inventory. You can find ' +
|
||||
'more information about Ansible\'s host patterns ' +
|
||||
'<a id=\"adhoc_form_hostpatterns_doc_link\"' +
|
||||
'href=\"http://docs.ansible.com/intro_patterns.html\" ' +
|
||||
'target=\"_blank\">here</a>.</p>',
|
||||
dataTitle: 'Host Pattern',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body'
|
||||
},
|
||||
credential: {
|
||||
label: 'Machine Credential',
|
||||
type: 'lookup',
|
||||
sourceModel: 'credential',
|
||||
sourceField: 'name',
|
||||
ngClick: 'lookUpCredential()',
|
||||
awPopOver: '<p>Select the credential you want to use when ' +
|
||||
'accessing the remote hosts to run the command. ' +
|
||||
'Choose the credential containing ' +
|
||||
'the username and SSH key or password that Ansbile ' +
|
||||
'will need to log into the remote hosts.</p>',
|
||||
dataTitle: 'Credential',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: 'body',
|
||||
awRequiredWhen: {
|
||||
variable: 'credRequired',
|
||||
init: 'false'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
launch: {
|
||||
label: 'Launch',
|
||||
ngClick: 'launchJob()',
|
||||
ngDisabled: true
|
||||
},
|
||||
reset: {
|
||||
ngClick: 'formReset()',
|
||||
ngDisabled: true
|
||||
}
|
||||
},
|
||||
|
||||
related: {}
|
||||
});
|
||||
@ -96,6 +96,7 @@ export default
|
||||
label: 'Permission',
|
||||
labelClass: 'prepend-asterisk',
|
||||
type: 'radio_group',
|
||||
class: 'squeeze',
|
||||
options: [{
|
||||
label: 'Read',
|
||||
value: 'read',
|
||||
@ -121,11 +122,26 @@ export default
|
||||
value: 'check',
|
||||
ngShow: "category == 'Deploy'"
|
||||
}],
|
||||
// hack: attach helpCollapse here if the permissions
|
||||
// category is deploy
|
||||
helpCollapse: [{
|
||||
hdr: 'Permission',
|
||||
ngBind: 'permissionTypeHelp',
|
||||
ngHide: "category == 'Inventory'"
|
||||
}]
|
||||
},
|
||||
run_ad_hoc_commands: {
|
||||
label: 'Execute commands',
|
||||
type: 'checkbox',
|
||||
// hack: attach helpCollapse here if the permissions
|
||||
// category is inventory
|
||||
helpCollapse: [{
|
||||
hdr: 'Permission',
|
||||
ngBind: 'permissionTypeHelp'
|
||||
}]
|
||||
}
|
||||
}],
|
||||
ngShow: "category == 'Inventory'",
|
||||
associated: 'permission_type'
|
||||
},
|
||||
},
|
||||
|
||||
buttons: {
|
||||
|
||||
@ -211,9 +211,9 @@ export default
|
||||
ngBind: 'permission.summary_fields.project.name'
|
||||
},
|
||||
permission_type: {
|
||||
label: 'Permission'
|
||||
label: 'Permission',
|
||||
ngBind: 'getPermissionText()'
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
fieldActions: {
|
||||
|
||||
@ -39,6 +39,7 @@ import Refresh from "tower/helpers/refresh";
|
||||
import RelatedSearch from "tower/helpers/related-search";
|
||||
import Search from "tower/helpers/search";
|
||||
import Teams from "tower/helpers/teams";
|
||||
import AdhocHelper from "tower/helpers/Adhoc";
|
||||
|
||||
export
|
||||
{ AboutAnsible,
|
||||
@ -78,5 +79,6 @@ export
|
||||
Refresh,
|
||||
RelatedSearch,
|
||||
Search,
|
||||
Teams
|
||||
Teams,
|
||||
AdhocHelper
|
||||
};
|
||||
|
||||
145
awx/ui/static/js/helpers/Adhoc.js
Normal file
145
awx/ui/static/js/helpers/Adhoc.js
Normal file
@ -0,0 +1,145 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2015 AnsibleWorks, Inc.
|
||||
*
|
||||
* AdhocHelper
|
||||
*
|
||||
* Routines shared by adhoc related controllers:
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:Adhoc
|
||||
* @description routines shared by adhoc related controllers
|
||||
* AdhocRun is currently only used for _relaunching_ an adhoc command
|
||||
* from the Jobs page.
|
||||
* TODO: once the API endpoint is figured out for running an adhoc command
|
||||
* from the form is figured out, the rest work should probably be excised from
|
||||
* the controller and moved into here. See the todo statements in the
|
||||
* controller for more information about this.
|
||||
*/
|
||||
|
||||
export default
|
||||
angular.module('AdhocHelper', ['RestServices', 'Utilities',
|
||||
'CredentialFormDefinition', 'CredentialsListDefinition', 'LookUpHelper',
|
||||
'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog',
|
||||
'FormGenerator', 'JobVarsPromptFormDefinition'])
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name helpers.function:JobSubmission#AdhocRun
|
||||
* @methodOf helpers.function:JobSubmission
|
||||
* @description The adhoc Run function is run when the user clicks the relaunch button
|
||||
*
|
||||
*/
|
||||
// Submit request to run an adhoc comamand
|
||||
.factory('AdhocRun', ['$location','$routeParams', 'LaunchJob',
|
||||
'PromptForPasswords', 'Rest', 'GetBasePath', 'Alert', 'ProcessErrors',
|
||||
'Wait', 'Empty', 'PromptForCredential', 'PromptForVars',
|
||||
'PromptForSurvey' , 'CreateLaunchDialog',
|
||||
function ($location, $routeParams, LaunchJob, PromptForPasswords,
|
||||
Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty,
|
||||
PromptForCredential, PromptForVars, PromptForSurvey,
|
||||
CreateLaunchDialog) {
|
||||
return function (params) {
|
||||
var id = params.project_id,
|
||||
scope = params.scope.$new(),
|
||||
new_job_id,
|
||||
html,
|
||||
url;
|
||||
|
||||
// this is used to cancel a running adhoc command from
|
||||
// the jobs page
|
||||
if (scope.removeCancelJob) {
|
||||
scope.removeCancelJob();
|
||||
}
|
||||
scope.removeCancelJob = scope.$on('CancelJob', function() {
|
||||
// Delete the job
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('ad_hoc_commands') + new_job_id + '/');
|
||||
Rest.destroy()
|
||||
.success(function() {
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status,
|
||||
null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url +
|
||||
' failed. DELETE returned status: ' +
|
||||
status });
|
||||
});
|
||||
});
|
||||
|
||||
if (scope.removeAdhocLaunchFinished) {
|
||||
scope.removeAdhocLaunchFinished();
|
||||
}
|
||||
scope.removeAdhocLaunchFinished = scope.$on('AdhocLaunchFinished',
|
||||
function(e, data) {
|
||||
$location.path('/ad_hoc_commands/' + data.id);
|
||||
});
|
||||
|
||||
if (scope.removeStartAdhocRun) {
|
||||
scope.removeStartAdhocRun();
|
||||
}
|
||||
|
||||
scope.removeStartAdhocRun = scope.$on('StartAdhocRun', function() {
|
||||
LaunchJob({
|
||||
scope: scope,
|
||||
url: url,
|
||||
callback: 'AdhocLaunchFinished' // send to the adhoc
|
||||
// standard out page
|
||||
});
|
||||
});
|
||||
|
||||
// start routine only if passwords need to be prompted
|
||||
if (scope.removeCreateLaunchDialog) {
|
||||
scope.removeCreateLaunchDialog();
|
||||
}
|
||||
scope.removeCreateLaunchDialog = scope.$on('CreateLaunchDialog',
|
||||
function(e, html, url) {
|
||||
CreateLaunchDialog({
|
||||
scope: scope,
|
||||
html: html,
|
||||
url: url,
|
||||
callback: 'StartAdhocRun'
|
||||
});
|
||||
});
|
||||
|
||||
if (scope.removePromptForPasswords) {
|
||||
scope.removePromptForPasswords();
|
||||
}
|
||||
scope.removePromptForPasswords = scope.$on('PromptForPasswords',
|
||||
function(e, passwords_needed_to_start,html, url) {
|
||||
PromptForPasswords({
|
||||
scope: scope,
|
||||
passwords: passwords_needed_to_start,
|
||||
callback: 'CreateLaunchDialog',
|
||||
html: html,
|
||||
url: url
|
||||
});
|
||||
}); // end password prompting routine
|
||||
|
||||
// start the adhoc relaunch routine
|
||||
Wait('start');
|
||||
url = GetBasePath('ad_hoc_commands') + id + '/relaunch/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
new_job_id = data.id;
|
||||
|
||||
scope.passwords_needed_to_start = data.passwords_needed_to_start;
|
||||
if (!Empty(data.passwords_needed_to_start) &&
|
||||
data.passwords_needed_to_start.length > 0) {
|
||||
// go through the password prompt routine before
|
||||
// starting the adhoc run
|
||||
scope.$emit('PromptForPasswords', data.passwords_needed_to_start, html, url);
|
||||
}
|
||||
else {
|
||||
// no prompting of passwords needed
|
||||
scope.$emit('StartAdhocRun');
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get job template details. GET returned status: ' + status });
|
||||
});
|
||||
};
|
||||
}]);
|
||||
@ -17,7 +17,6 @@ angular.module('CredentialsHelper', ['Utilities'])
|
||||
.factory('KindChange', ['Empty',
|
||||
function (Empty) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
reset = params.reset,
|
||||
collapse, id;
|
||||
|
||||
@ -299,7 +299,6 @@ function($compile, Rest, GetBasePath, TextareaResize,CreateDialog, GenerateForm,
|
||||
|
||||
html += "<div class=\"alert alert-info\">Launching this job requires the passwords listed below. Enter and confirm each password before continuing.</div>\n";
|
||||
// html += "<form name=\"password_form\" novalidate>\n";
|
||||
|
||||
scope.passwords.forEach(function(password) {
|
||||
// Prompt for password
|
||||
field = form.fields[password];
|
||||
|
||||
@ -70,6 +70,8 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
||||
scope.example_template_id = 'N';
|
||||
scope.setCallbackHelp();
|
||||
|
||||
// this fills the job template form both on copy of the job template
|
||||
// and on edit
|
||||
scope.fillJobTemplate = function(){
|
||||
// id = id || $rootScope.copy.id;
|
||||
// Retrieve detail record and prepopulate the form
|
||||
|
||||
@ -16,7 +16,7 @@ import listGenerator from 'tower/shared/list-generator/main';
|
||||
|
||||
export default
|
||||
angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper', 'GeneratorHelpers',
|
||||
'JobSubmissionHelper', 'LogViewerHelper', 'SearchHelper', 'PaginationHelpers', listGenerator.name])
|
||||
'JobSubmissionHelper', 'LogViewerHelper', 'SearchHelper', 'PaginationHelpers', 'AdhocHelper', listGenerator.name])
|
||||
|
||||
/**
|
||||
* JobsControllerInit({ scope: $scope });
|
||||
@ -62,7 +62,7 @@ export default
|
||||
else if (job.type === 'project_update') {
|
||||
typeId = job.project;
|
||||
}
|
||||
else if (job.type === 'job' || job.type === "system_job") {
|
||||
else if (job.type === 'job' || job.type === "system_job" || job.type === 'ad_hoc_command') {
|
||||
typeId = job.id;
|
||||
}
|
||||
RelaunchJob({ scope: scope, id: typeId, type: job.type, name: job.name });
|
||||
@ -112,8 +112,8 @@ export default
|
||||
}
|
||||
])
|
||||
|
||||
.factory('RelaunchJob', ['RelaunchInventory', 'RelaunchPlaybook', 'RelaunchSCM',
|
||||
function(RelaunchInventory, RelaunchPlaybook, RelaunchSCM) {
|
||||
.factory('RelaunchJob', ['RelaunchInventory', 'RelaunchPlaybook', 'RelaunchSCM', 'RelaunchAdhoc',
|
||||
function(RelaunchInventory, RelaunchPlaybook, RelaunchSCM, RelaunchAdhoc) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
id = params.id,
|
||||
@ -122,6 +122,9 @@ export default
|
||||
if (type === 'inventory_update') {
|
||||
RelaunchInventory({ scope: scope, id: id});
|
||||
}
|
||||
else if (type === 'ad_hoc_command') {
|
||||
RelaunchAdhoc({ scope: scope, id: id, name: name });
|
||||
}
|
||||
else if (type === 'job' || type === 'system_job') {
|
||||
RelaunchPlaybook({ scope: scope, id: id, name: name });
|
||||
}
|
||||
@ -595,4 +598,12 @@ export default
|
||||
id = params.id;
|
||||
ProjectUpdate({ scope: scope, project_id: id });
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('RelaunchAdhoc', ['AdhocRun', function(AdhocRun) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
id = params.id;
|
||||
AdhocRun({ scope: scope, project_id: id, relaunch: true });
|
||||
};
|
||||
}]);
|
||||
|
||||
@ -26,30 +26,52 @@ export default
|
||||
scope.projectrequired = false;
|
||||
html = "<dl>\n" +
|
||||
"<dt>Read</dt>\n" +
|
||||
"<dd>Only allow the user or team to view the inventory.</dd>\n" +
|
||||
"<dd>Only allow the user or team to view the inventory." +
|
||||
"</dd>\n" +
|
||||
"<dt>Write</dt>\n" +
|
||||
"<dd>Allow the user or team to modify hosts and groups contained in the inventory, add new hosts and groups, and perform inventory sync operations.\n" +
|
||||
"<dd>Allow the user or team to modify hosts and groups " +
|
||||
"contained in the inventory, add new hosts and groups" +
|
||||
", and perform inventory sync operations.\n" +
|
||||
"<dt>Admin</dt>\n" +
|
||||
"<dd>Allow the user or team full access to the inventory. This includes reading, writing, deletion of the inventory and inventory sync operations.</dd>\n" +
|
||||
"<dd>Allow the user or team full access to the " +
|
||||
"inventory. This includes reading, writing, deletion " +
|
||||
"of the inventory, inventory sync operations, and " +
|
||||
"the ability to execute commands on the inventory." +
|
||||
"</dd>\n" +
|
||||
"<dt>Execute commands</dt>\n" +
|
||||
"<dd>Allow the user to execute commands on the " +
|
||||
"inventory.</dd>\n" +
|
||||
"</dl>\n";
|
||||
scope.permissionTypeHelp = $sce.trustAsHtml(html);
|
||||
} else {
|
||||
scope.projectrequired = true;
|
||||
html = "<dl>\n" +
|
||||
"<dt>Create</dt>\n" +
|
||||
"<dd>Allow the user or team to create job templates. This implies that they have the Run and Check permissions.</dd>\n" +
|
||||
"<dt>Create</dt>\n" +
|
||||
"<dd>Allow the user or team to create job templates. " +
|
||||
"This implies that they have the Run and Check " +
|
||||
"permissions.</dd>\n" +
|
||||
"<dt>Run</dt>\n" +
|
||||
"<dd>Allow the user or team to run a job template from the project against the inventory. In Run mode modules will " +
|
||||
"be executed, and changes to the inventory will occur.</dd>\n" +
|
||||
"<dd>Allow the user or team to run a job template from " +
|
||||
"the project against the inventory. In Run mode " +
|
||||
"modules will " +
|
||||
"be executed, and changes to the inventory will occur." +
|
||||
"</dd>\n" +
|
||||
"<dt>Check</dt>\n" +
|
||||
"<dd>Only allow the user or team to run the project against the inventory as a dry-run operation. In Check mode, module operations " +
|
||||
"will only be simulated. No changes will occur.</dd>\n" +
|
||||
"<dd>Only allow the user or team to run the project " +
|
||||
"against the inventory as a dry-run operation. In " +
|
||||
"Check mode, module operations " +
|
||||
"will only be simulated. No changes will occur." +
|
||||
"</dd>\n" +
|
||||
"</dl>\n";
|
||||
scope.permissionTypeHelp = $sce.trustAsHtml(html);
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
scope.permission_type = (scope.category === 'Inventory') ? 'read' : 'run'; //default to the first option
|
||||
if (scope.category === "Inventory") {
|
||||
scope.permission_type = "read";
|
||||
} else {
|
||||
scope.permission_type = "run";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ export default
|
||||
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'SearchHelper', 'VariablesHelper',
|
||||
])
|
||||
|
||||
|
||||
.factory('GetGroupContainerHeight', [ function() {
|
||||
return function() {
|
||||
|
||||
@ -101,7 +100,6 @@ export default
|
||||
};
|
||||
}])
|
||||
|
||||
|
||||
.factory('WatchInventoryWindowResize', ['ApplyEllipsis', 'SetContainerHeights',
|
||||
function (ApplyEllipsis, SetContainerHeights) {
|
||||
return function (params) {
|
||||
@ -185,7 +183,6 @@ export default
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
.factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
|
||||
'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
|
||||
function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory,
|
||||
|
||||
@ -16,6 +16,7 @@ export default
|
||||
index: false,
|
||||
hover: true,
|
||||
'class': 'table-no-border',
|
||||
multiSelect: true,
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
@ -78,6 +79,17 @@ export default
|
||||
},
|
||||
|
||||
actions: {
|
||||
launch: {
|
||||
mode: 'all',
|
||||
// TODO: ngShow permissions
|
||||
ngClick: 'populateAdhocForm()',
|
||||
awToolTip: "Run a command on this inventory"
|
||||
// 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()",
|
||||
@ -134,7 +146,8 @@ export default
|
||||
ngClick: 'updateGroup(group.id)',
|
||||
awToolTip: "{{ group.launch_tooltip }}",
|
||||
dataTipWatch: "group.launch_tooltip",
|
||||
ngShow: "group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating'",
|
||||
ngShow: "group.status !== 'running' && group.status " +
|
||||
"!== 'pending' && group.status !== 'updating'",
|
||||
ngClass: "group.launch_class",
|
||||
dataPlacement: "top"
|
||||
},
|
||||
@ -144,7 +157,8 @@ export default
|
||||
ngClick: "cancelUpdate(group.id)",
|
||||
awToolTip: "Cancel sync process",
|
||||
'class': 'red-txt',
|
||||
ngShow: "group.status == 'running' || group.status == 'pending' || group.status == 'updating'",
|
||||
ngShow: "group.status == 'running' || group.status == 'pending' " +
|
||||
"|| group.status == 'updating'",
|
||||
dataPlacement: "top"
|
||||
},
|
||||
edit: {
|
||||
|
||||
@ -41,7 +41,8 @@ export default
|
||||
ngBind: 'permission.summary_fields.project.name'
|
||||
},
|
||||
permission_type: {
|
||||
label: 'Permission'
|
||||
label: 'Permission',
|
||||
ngBind: 'getPermissionText()'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -609,6 +609,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
params.content = collapse_array[i].content;
|
||||
params.idx = this.accordion_count++;
|
||||
params.show = (collapse_array[i].show) ? collapse_array[i].show : null;
|
||||
params.ngHide = (collapse_array[i].ngHide) ? collapse_array[i].ngHide : null;
|
||||
params.bind = (collapse_array[i].ngBind) ? collapse_array[i].ngBind : null;
|
||||
html += HelpCollapse(params);
|
||||
}
|
||||
@ -676,6 +677,10 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
html += Attr(field, 'type');
|
||||
html += "ng-model=\"" + fld + '" ';
|
||||
html += "name=\"" + fld + '" ';
|
||||
if (form.name === "permission") {
|
||||
html += "ng-disabled='permission_type === \"admin\"'";
|
||||
html += "ng-checked='permission_type === \"admin\"'";
|
||||
}
|
||||
html += (field.ngChange) ? Attr(field, 'ngChange') : "";
|
||||
html += "id=\"" + form.name + "_" + fld + "_chbox\" ";
|
||||
html += (idx !== undefined) ? "_" + idx : "";
|
||||
@ -752,7 +757,9 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
}
|
||||
|
||||
if ((!field.readonly) || (field.readonly && options.mode === 'edit')) {
|
||||
html += "<div class='form-group' ";
|
||||
html += "<div class='form-group ";
|
||||
html += (field['class']) ? (field['class']) : "";
|
||||
html += "'";
|
||||
html += (field.ngShow) ? this.attr(field, 'ngShow') : "";
|
||||
html += (field.ngHide) ? this.attr(field, 'ngHide') : "";
|
||||
html += ">\n";
|
||||
@ -1246,6 +1253,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
if (horizontal) {
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
html += (field.helpCollapse) ? this.buildHelpCollapse(field.helpCollapse) : '';
|
||||
}
|
||||
|
||||
//radio group
|
||||
@ -1642,6 +1651,10 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
button.label = 'Reset';
|
||||
button['class'] = 'btn-default';
|
||||
}
|
||||
if (btn === 'launch') {
|
||||
button.label = 'Launch';
|
||||
button['class'] = 'btn-primary';
|
||||
}
|
||||
|
||||
// Build button HTML
|
||||
html += "<button type=\"button\" ";
|
||||
|
||||
@ -130,6 +130,9 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
case 'submit':
|
||||
icon = 'fa-rocket';
|
||||
break;
|
||||
case 'launch':
|
||||
icon = 'fa-rocket';
|
||||
break;
|
||||
case 'stream':
|
||||
icon = 'fa-clock-o';
|
||||
break;
|
||||
@ -646,12 +649,14 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
var hdr = params.hdr,
|
||||
content = params.content,
|
||||
show = params.show,
|
||||
ngHide = params.ngHide,
|
||||
idx = params.idx,
|
||||
bind = params.bind,
|
||||
html = '';
|
||||
|
||||
html += "<div class=\"panel-group collapsible-help\" ";
|
||||
html += (show) ? "ng-show=\"" + show + "\"" : "";
|
||||
html += (show) ? "ng-show=\"" + show + "\" " : "";
|
||||
html += (ngHide) ? "ng-hide=\"" + ngHide + "\" " : "";
|
||||
html += ">\n";
|
||||
html += "<div class=\"panel panel-default\">\n";
|
||||
html += "<div class=\"panel-heading\" ng-click=\"accordionToggle('#accordion" + idx + "')\">\n";
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
<span ng-repeat="(name, options) in list.actions">
|
||||
<!-- TODO: Unfortunately, the data-tip-watch attribute is not loaded for
|
||||
some reason -->
|
||||
<button
|
||||
toolbar-button
|
||||
mode="options.mode"
|
||||
icon-name="{{name}}"
|
||||
aw-tool-tip="{{options.awToolTip}}"
|
||||
data-tip-watch="{{options.dataTipWatch}}"
|
||||
data-placement="{{options.dataPlacement}}"
|
||||
data-container="{{options.dataContainer}}"
|
||||
class="options.class"
|
||||
|
||||
@ -229,7 +229,6 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
selection.deselectedItems.forEach(function(item) {
|
||||
item.isSelected = false;
|
||||
});
|
||||
|
||||
}.bind(this));
|
||||
|
||||
this.scope.$on('$destroy', function() {
|
||||
@ -396,7 +395,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
});
|
||||
}
|
||||
|
||||
html += "</div><!-- list-acitons -->\n";
|
||||
html += "</div><!-- list-actions -->\n";
|
||||
|
||||
html += "</div><!-- list-actions-column -->\n";
|
||||
} else {
|
||||
|
||||
@ -1535,9 +1535,9 @@ input[type="checkbox"].checkbox-no-label {
|
||||
}
|
||||
}
|
||||
|
||||
// Inventory edit dialog, source form, ec2
|
||||
#source_form.squeeze .form-group {
|
||||
margin-bottom: 10px;
|
||||
// ad hoc permission checkbox
|
||||
.squeeze.form-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
||||
4
awx/ui/static/partials/adhoc.html
Normal file
4
awx/ui/static/partials/adhoc.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="tab-pane" id="credentials">
|
||||
<div ng-cloak id="htmlTemplate">
|
||||
</div>
|
||||
</div>
|
||||
50
awx/ui/static/partials/job_stdout_adhoc.html
Normal file
50
awx/ui/static/partials/job_stdout_adhoc.html
Normal file
@ -0,0 +1,50 @@
|
||||
<div class="tab-pane" id="jobs-stdout">
|
||||
<div ng-cloak id="htmlTemplate">
|
||||
|
||||
<div class="row">
|
||||
<div id="breadcrumb-container" class="col-md-6"
|
||||
style="position: relative;">
|
||||
<ul class="ansible-breadcrumb" id="breadcrumb-list">
|
||||
<li><a href="/#/jobs">Jobs</a></li>
|
||||
<li class="active">
|
||||
<a href="/#/ad_hoc_commands/{{ job.id }}">
|
||||
{{ job.id }} - {{ job.name }} standard out
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="home-list-actions"
|
||||
class="list-actions pull-right col-md-6">
|
||||
<button type="button" class="btn btn-xs btn-primary ng-hide"
|
||||
ng-click="refresh()" id="refresh_btn"
|
||||
aw-tool-tip="Refresh the page"
|
||||
data-placement="top" ng-show="socketStatus == 'error'"
|
||||
data-original-title="" title="">
|
||||
<i class="fa fa-refresh fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="job-status">
|
||||
<label>Job Status</label>
|
||||
<i class="fa icon-job-{{ job.status }}"></i> {{ job.status }}
|
||||
</div>
|
||||
<div class="scroll-spinner" id="stdoutMoreRowsTop">
|
||||
<i class="fa fa-cog fa-spin"></i>
|
||||
</div>
|
||||
<div id="pre-container" class="body_background
|
||||
body_foreground pre mono-space"
|
||||
lr-infinite-scroll="stdOutScrollToTop"
|
||||
scroll-threshold="300" data-direction="up" time-threshold="500">
|
||||
<div id="pre-container-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
|
||||
<i class="fa fa-cog fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Loading…
x
Reference in New Issue
Block a user