mirror of
https://github.com/ansible/awx.git
synced 2026-02-19 12:10:06 -03:30
Merge branch 'modularize-helpers-utilities' of https://github.com/mabashian/ansible-tower into mabashian-modularize-helpers-utilities
# Conflicts: # awx/ui/client/src/activity-stream/main.js # awx/ui/client/src/app.js # awx/ui/client/src/helpers.js # awx/ui/client/src/helpers/Credentials.js # awx/ui/client/src/helpers/Groups.js # awx/ui/client/src/helpers/Hosts.js # awx/ui/client/src/helpers/teams.js
This commit is contained in:
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
/* jshint unused: vars */
|
/* jshint unused: vars */
|
||||||
export default ['addPermissionsTeamsList', 'addPermissionsUsersList', 'TemplateList', 'ProjectList',
|
export default ['addPermissionsTeamsList', 'addPermissionsUsersList', 'TemplateList', 'ProjectList',
|
||||||
'InventoryList', 'CredentialList', '$compile', 'generateList', 'GetBasePath', 'SelectionInit',
|
'InventoryList', 'CredentialList', '$compile', 'generateList', 'GetBasePath',
|
||||||
function(addPermissionsTeamsList, addPermissionsUsersList, TemplateList, ProjectList,
|
function(addPermissionsTeamsList, addPermissionsUsersList, TemplateList, ProjectList,
|
||||||
InventoryList, CredentialList, $compile, generateList, GetBasePath, SelectionInit) {
|
InventoryList, CredentialList, $compile, generateList, GetBasePath) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
export default
|
||||||
|
function GetTargetTitle(i18n) {
|
||||||
|
return function (target) {
|
||||||
|
|
||||||
|
var rtnTitle = i18n._('ALL ACTIVITY');
|
||||||
|
|
||||||
|
switch(target) {
|
||||||
|
case 'project':
|
||||||
|
rtnTitle = i18n._('PROJECTS');
|
||||||
|
break;
|
||||||
|
case 'inventory':
|
||||||
|
rtnTitle = i18n._('INVENTORIES');
|
||||||
|
break;
|
||||||
|
case 'credential':
|
||||||
|
rtnTitle = i18n._('CREDENTIALS');
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
rtnTitle = i18n._('USERS');
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
rtnTitle = i18n._('TEAMS');
|
||||||
|
break;
|
||||||
|
case 'notification_template':
|
||||||
|
rtnTitle = i18n._('NOTIFICATION TEMPLATES');
|
||||||
|
break;
|
||||||
|
case 'organization':
|
||||||
|
rtnTitle = i18n._('ORGANIZATIONS');
|
||||||
|
break;
|
||||||
|
case 'job':
|
||||||
|
rtnTitle = i18n._('JOBS');
|
||||||
|
break;
|
||||||
|
case 'custom_inventory_script':
|
||||||
|
rtnTitle = i18n._('INVENTORY SCRIPTS');
|
||||||
|
break;
|
||||||
|
case 'schedule':
|
||||||
|
rtnTitle = i18n._('SCHEDULES');
|
||||||
|
break;
|
||||||
|
case 'host':
|
||||||
|
rtnTitle = i18n._('HOSTS');
|
||||||
|
break;
|
||||||
|
case 'template':
|
||||||
|
rtnTitle = i18n._('TEMPLATES');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtnTitle;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetTargetTitle.$inject = ['i18n'];
|
||||||
@@ -12,6 +12,8 @@ import BuildAnchor from './factories/build-anchor.factory';
|
|||||||
import BuildDescription from './factories/build-description.factory';
|
import BuildDescription from './factories/build-description.factory';
|
||||||
import ShowDetail from './factories/show-detail.factory';
|
import ShowDetail from './factories/show-detail.factory';
|
||||||
import Stream from './factories/stream.factory';
|
import Stream from './factories/stream.factory';
|
||||||
|
import GetTargetTitle from './get-target-title.factory';
|
||||||
|
import ModelToBasePathKey from './model-to-base-path-key.factory';
|
||||||
|
|
||||||
export default angular.module('activityStream', [streamDetailModal.name])
|
export default angular.module('activityStream', [streamDetailModal.name])
|
||||||
.controller('activityStreamController', activityStreamController)
|
.controller('activityStreamController', activityStreamController)
|
||||||
@@ -20,6 +22,8 @@ export default angular.module('activityStream', [streamDetailModal.name])
|
|||||||
.factory('BuildDescription', BuildDescription)
|
.factory('BuildDescription', BuildDescription)
|
||||||
.factory('ShowDetail', ShowDetail)
|
.factory('ShowDetail', ShowDetail)
|
||||||
.factory('Stream', Stream)
|
.factory('Stream', Stream)
|
||||||
|
.factory('GetTargetTitle', GetTargetTitle)
|
||||||
|
.factory('ModelToBasePathKey', ModelToBasePathKey)
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(activityStreamRoute);
|
$stateExtender.addState(activityStreamRoute);
|
||||||
}]);
|
}]);
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name helpers.function:ApiModel
|
||||||
|
* @description Helper functions to convert singular/plural versions of our models to the opposite
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default
|
||||||
|
function ModelToBasePathKey() {
|
||||||
|
return function(model) {
|
||||||
|
// This function takes in the singular model string and returns the key needed
|
||||||
|
// to get the base path from $rootScope/local storage.
|
||||||
|
|
||||||
|
var basePathKey;
|
||||||
|
|
||||||
|
switch(model) {
|
||||||
|
case 'project':
|
||||||
|
basePathKey = 'projects';
|
||||||
|
break;
|
||||||
|
case 'inventory':
|
||||||
|
basePathKey = 'inventory';
|
||||||
|
break;
|
||||||
|
case 'job_template':
|
||||||
|
basePathKey = 'job_templates';
|
||||||
|
break;
|
||||||
|
case 'credential':
|
||||||
|
basePathKey = 'credentials';
|
||||||
|
break;
|
||||||
|
case 'user':
|
||||||
|
basePathKey = 'users';
|
||||||
|
break;
|
||||||
|
case 'team':
|
||||||
|
basePathKey = 'teams';
|
||||||
|
break;
|
||||||
|
case 'notification_template':
|
||||||
|
basePathKey = 'notification_templates';
|
||||||
|
break;
|
||||||
|
case 'organization':
|
||||||
|
basePathKey = 'organizations';
|
||||||
|
break;
|
||||||
|
case 'management_job':
|
||||||
|
basePathKey = 'management_jobs';
|
||||||
|
break;
|
||||||
|
case 'custom_inventory_script':
|
||||||
|
basePathKey = 'inventory_scripts';
|
||||||
|
break;
|
||||||
|
case 'workflow_job_template':
|
||||||
|
basePathKey = 'workflow_job_templates';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return basePathKey;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ if ($basePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
import './helpers';
|
import './forms';
|
||||||
import './lists';
|
import './lists';
|
||||||
import './filters';
|
import './filters';
|
||||||
import portalMode from './portal-mode/main';
|
import portalMode from './portal-mode/main';
|
||||||
@@ -71,7 +71,6 @@ import projects from './projects/main';
|
|||||||
|
|
||||||
import RestServices from './rest/main';
|
import RestServices from './rest/main';
|
||||||
import access from './access/main';
|
import access from './access/main';
|
||||||
import './shared/Modal';
|
|
||||||
import './shared/prompt-dialog';
|
import './shared/prompt-dialog';
|
||||||
import './shared/directives';
|
import './shared/directives';
|
||||||
import './shared/filters';
|
import './shared/filters';
|
||||||
@@ -91,6 +90,7 @@ var tower = angular.module('Tower', [
|
|||||||
require('angular-sanitize'),
|
require('angular-sanitize'),
|
||||||
require('angular-scheduler').name,
|
require('angular-scheduler').name,
|
||||||
require('angular-tz-extensions'),
|
require('angular-tz-extensions'),
|
||||||
|
require('angular-md5'),
|
||||||
require('lr-infinite-scroll'),
|
require('lr-infinite-scroll'),
|
||||||
require('ng-toast'),
|
require('ng-toast'),
|
||||||
'gettext',
|
'gettext',
|
||||||
@@ -138,66 +138,46 @@ var tower = angular.module('Tower', [
|
|||||||
'OrganizationListDefinition',
|
'OrganizationListDefinition',
|
||||||
'templates',
|
'templates',
|
||||||
'UserListDefinition',
|
'UserListDefinition',
|
||||||
'UserHelper',
|
|
||||||
'PromptDialog',
|
'PromptDialog',
|
||||||
'AWDirectives',
|
'AWDirectives',
|
||||||
'InventoriesListDefinition',
|
'InventoriesListDefinition',
|
||||||
'InventoryFormDefinition',
|
'InventoryFormDefinition',
|
||||||
'InventoryHelper',
|
|
||||||
'InventoryGroupsDefinition',
|
'InventoryGroupsDefinition',
|
||||||
'InventoryHostsDefinition',
|
'InventoryHostsDefinition',
|
||||||
'HostsHelper',
|
|
||||||
'AWFilters',
|
'AWFilters',
|
||||||
'HostFormDefinition',
|
'HostFormDefinition',
|
||||||
'HostListDefinition',
|
'HostListDefinition',
|
||||||
'GroupFormDefinition',
|
'GroupFormDefinition',
|
||||||
'GroupListDefinition',
|
'GroupListDefinition',
|
||||||
'GroupsHelper',
|
|
||||||
'TeamsListDefinition',
|
'TeamsListDefinition',
|
||||||
'TeamFormDefinition',
|
'TeamFormDefinition',
|
||||||
'TeamHelper',
|
|
||||||
'CredentialsListDefinition',
|
'CredentialsListDefinition',
|
||||||
'CredentialFormDefinition',
|
'CredentialFormDefinition',
|
||||||
'TemplatesListDefinition',
|
'TemplatesListDefinition',
|
||||||
'PortalJobTemplatesListDefinition',
|
'PortalJobTemplatesListDefinition',
|
||||||
'JobTemplateFormDefinition',
|
'JobTemplateFormDefinition',
|
||||||
'JobTemplatesHelper',
|
|
||||||
'JobSubmissionHelper',
|
|
||||||
'ProjectsListDefinition',
|
'ProjectsListDefinition',
|
||||||
'ProjectFormDefinition',
|
'ProjectFormDefinition',
|
||||||
'ProjectStatusDefinition',
|
'ProjectStatusDefinition',
|
||||||
'ProjectsHelper',
|
|
||||||
'CompletedJobsDefinition',
|
'CompletedJobsDefinition',
|
||||||
'AllJobsDefinition',
|
'AllJobsDefinition',
|
||||||
'JobSummaryDefinition',
|
'JobSummaryDefinition',
|
||||||
'ParseHelper',
|
|
||||||
'ChildrenHelper',
|
|
||||||
'ProjectPathHelper',
|
|
||||||
'md5Helper',
|
|
||||||
'SelectionHelper',
|
|
||||||
'HostGroupsFormDefinition',
|
'HostGroupsFormDefinition',
|
||||||
'JobsHelper',
|
|
||||||
'CredentialsHelper',
|
|
||||||
'StreamListDefinition',
|
'StreamListDefinition',
|
||||||
'ActivityDetailDefinition',
|
'ActivityDetailDefinition',
|
||||||
'VariablesHelper',
|
|
||||||
'SchedulesListDefinition',
|
'SchedulesListDefinition',
|
||||||
'ScheduledJobsDefinition',
|
'ScheduledJobsDefinition',
|
||||||
//'Timezones',
|
//'Timezones',
|
||||||
'SchedulesHelper',
|
|
||||||
'JobsListDefinition',
|
'JobsListDefinition',
|
||||||
'LogViewerStatusDefinition',
|
'LogViewerStatusDefinition',
|
||||||
'StandardOutHelper',
|
'StandardOutHelper',
|
||||||
'LogViewerOptionsDefinition',
|
'LogViewerOptionsDefinition',
|
||||||
'lrInfiniteScroll',
|
'lrInfiniteScroll',
|
||||||
'LoadConfigHelper',
|
|
||||||
'PortalJobsListDefinition',
|
'PortalJobsListDefinition',
|
||||||
'features',
|
'features',
|
||||||
'longDateFilter',
|
'longDateFilter',
|
||||||
'pendolytics',
|
'pendolytics',
|
||||||
scheduler.name,
|
scheduler.name,
|
||||||
'ApiModelHelper',
|
|
||||||
'ActivityStreamHelper',
|
|
||||||
'WorkflowFormDefinition',
|
'WorkflowFormDefinition',
|
||||||
'InventorySourcesListDefinition',
|
'InventorySourcesListDefinition',
|
||||||
'WorkflowMakerFormDefinition'
|
'WorkflowMakerFormDefinition'
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
export default ['$scope', '$rootScope', '$compile', '$location',
|
export default ['$scope', '$rootScope', '$compile', '$location',
|
||||||
'$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert',
|
'$log', '$stateParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert',
|
||||||
'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'BecomeMethodChange',
|
'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'Empty', 'KindChange', 'BecomeMethodChange',
|
||||||
'OwnerChange', 'FormSave', '$state', 'CreateSelect2', 'i18n',
|
'OwnerChange', 'CredentialFormSave', '$state', 'CreateSelect2', 'i18n',
|
||||||
function($scope, $rootScope, $compile, $location, $log,
|
function($scope, $rootScope, $compile, $location, $log,
|
||||||
$stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors,
|
$stateParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||||
ClearScope, GetBasePath, GetChoices, Empty, KindChange, BecomeMethodChange,
|
ClearScope, GetBasePath, GetChoices, Empty, KindChange, BecomeMethodChange,
|
||||||
OwnerChange, FormSave, $state, CreateSelect2, i18n) {
|
OwnerChange, CredentialFormSave, $state, CreateSelect2, i18n) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
// Save
|
// Save
|
||||||
$scope.formSave = function() {
|
$scope.formSave = function() {
|
||||||
if ($scope[form.name + '_form'].$valid) {
|
if ($scope[form.name + '_form'].$valid) {
|
||||||
FormSave({ scope: $scope, mode: 'add' });
|
CredentialFormSave({ scope: $scope, mode: 'add' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
'$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert',
|
'$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert',
|
||||||
'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices',
|
'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices',
|
||||||
'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange',
|
'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange',
|
||||||
'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n',
|
'CredentialFormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n',
|
||||||
function($scope, $rootScope, $compile, $location, $log,
|
function($scope, $rootScope, $compile, $location, $log,
|
||||||
$stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt,
|
$stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt,
|
||||||
GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, FormSave, Wait,
|
GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, CredentialFormSave, Wait,
|
||||||
$state, CreateSelect2, Authorization, i18n) {
|
$state, CreateSelect2, Authorization, i18n) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
@@ -236,7 +236,7 @@ export default ['$scope', '$rootScope', '$compile', '$location',
|
|||||||
// Save changes to the parent
|
// Save changes to the parent
|
||||||
$scope.formSave = function() {
|
$scope.formSave = function() {
|
||||||
if ($scope[form.name + '_form'].$valid) {
|
if ($scope[form.name + '_form'].$valid) {
|
||||||
FormSave({ scope: $scope, mode: 'edit' });
|
CredentialFormSave({ scope: $scope, mode: 'edit' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
export default
|
||||||
|
function BecomeMethodChange(Empty, i18n) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope;
|
||||||
|
|
||||||
|
if (!Empty(scope.kind)) {
|
||||||
|
// Apply kind specific settings
|
||||||
|
switch (scope.kind.value) {
|
||||||
|
case 'aws':
|
||||||
|
scope.aws_required = true;
|
||||||
|
break;
|
||||||
|
case 'rax':
|
||||||
|
scope.rackspace_required = true;
|
||||||
|
scope.username_required = true;
|
||||||
|
break;
|
||||||
|
case 'ssh':
|
||||||
|
scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
|
||||||
|
scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
|
||||||
|
scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
|
||||||
|
break;
|
||||||
|
case 'scm':
|
||||||
|
scope.sshKeyDataLabel = i18n._('SCM Private Key');
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
break;
|
||||||
|
case 'gce':
|
||||||
|
scope.usernameLabel = i18n._('Service Account Email Address');
|
||||||
|
scope.sshKeyDataLabel = i18n._('RSA Private Key');
|
||||||
|
scope.email_required = true;
|
||||||
|
scope.key_required = true;
|
||||||
|
scope.project_required = true;
|
||||||
|
scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
|
||||||
|
scope.projectLabel = i18n._("Project");
|
||||||
|
scope.project_required = false;
|
||||||
|
scope.projectPopOver = "<p>" + i18n._("The Project ID is the " +
|
||||||
|
"GCE assigned identification. It is constructed as " +
|
||||||
|
"two words followed by a three digit number. Such " +
|
||||||
|
"as: ") + "</p><p>adjective-noun-000</p>";
|
||||||
|
break;
|
||||||
|
case 'azure':
|
||||||
|
scope.sshKeyDataLabel = i18n._('Management Certificate');
|
||||||
|
scope.subscription_required = true;
|
||||||
|
scope.key_required = true;
|
||||||
|
scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
|
||||||
|
break;
|
||||||
|
case 'azure_rm':
|
||||||
|
scope.usernameLabel = i18n._("Username");
|
||||||
|
scope.subscription_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.azure_rm_required = true;
|
||||||
|
break;
|
||||||
|
case 'vmware':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.hostLabel = "vCenter Host";
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
|
||||||
|
break;
|
||||||
|
case 'openstack':
|
||||||
|
scope.hostLabel = i18n._("Host (Authentication URL)");
|
||||||
|
scope.projectLabel = i18n._("Project (Tenant Name)");
|
||||||
|
scope.domainLabel = i18n._("Domain Name");
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.project_required = true;
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.projectPopOver = "<p>" + i18n._("This is the tenant name. " +
|
||||||
|
" This value is usually the same " +
|
||||||
|
" as the username.") + "</p>";
|
||||||
|
scope.hostPopOver = "<p>" + i18n._("The host to authenticate with.") +
|
||||||
|
"<br />" + i18n.sprintf(i18n._("For example, %s"), "https://openstack.business.com/v2.0/");
|
||||||
|
break;
|
||||||
|
case 'satellite6':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.hostLabel = i18n._("Satellite 6 URL");
|
||||||
|
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL which corresponds to your %s" +
|
||||||
|
"Red Hat Satellite 6 server. %s" +
|
||||||
|
"For example, %s"), "<br />", "<br />", "https://satellite.example.org");
|
||||||
|
break;
|
||||||
|
case 'cloudforms':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.hostLabel = i18n._("CloudForms URL");
|
||||||
|
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL for the virtual machine which %s" +
|
||||||
|
"corresponds to your CloudForm instance. %s" +
|
||||||
|
"For example, %s"), "<br />", "<br />", "https://cloudforms.example.org");
|
||||||
|
break;
|
||||||
|
case 'net':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = false;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.sshKeyDataLabel = i18n._('SSH Key');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BecomeMethodChange.$inject =
|
||||||
|
[ 'Empty', 'i18n' ];
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
export default
|
||||||
|
function CredentialFormSave($rootScope, $location, Alert, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait, $state, i18n) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
mode = params.mode,
|
||||||
|
form = CredentialForm,
|
||||||
|
data = {}, fld, url;
|
||||||
|
|
||||||
|
for (fld in form.fields) {
|
||||||
|
if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' &&
|
||||||
|
fld !== 'ssh_password') {
|
||||||
|
if (fld === "organization" && !scope[fld]) {
|
||||||
|
data.user = $rootScope.current_user.id;
|
||||||
|
} else if (scope[fld] === null) {
|
||||||
|
data[fld] = "";
|
||||||
|
} else {
|
||||||
|
data[fld] = scope[fld];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.kind = scope.kind.value;
|
||||||
|
if (scope.become_method === null || typeof scope.become_method === 'undefined') {
|
||||||
|
data.become_method = "";
|
||||||
|
data.become_username = "";
|
||||||
|
data.become_password = "";
|
||||||
|
} else {
|
||||||
|
data.become_method = (scope.become_method.value) ? scope.become_method.value : "";
|
||||||
|
}
|
||||||
|
switch (data.kind) {
|
||||||
|
case 'ssh':
|
||||||
|
data.password = scope.ssh_password;
|
||||||
|
break;
|
||||||
|
case 'aws':
|
||||||
|
data.username = scope.access_key;
|
||||||
|
data.password = scope.secret_key;
|
||||||
|
break;
|
||||||
|
case 'rax':
|
||||||
|
data.password = scope.api_key;
|
||||||
|
break;
|
||||||
|
case 'gce':
|
||||||
|
data.username = scope.email_address;
|
||||||
|
data.project = scope.project;
|
||||||
|
break;
|
||||||
|
case 'azure':
|
||||||
|
data.username = scope.subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
if (mode === 'add') {
|
||||||
|
url = GetBasePath("credentials");
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post(data)
|
||||||
|
.success(function (data) {
|
||||||
|
scope.addedItem = data.id;
|
||||||
|
|
||||||
|
Wait('stop');
|
||||||
|
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||||
|
if (base === 'credentials') {
|
||||||
|
$state.go('credentials.edit', {credential_id: data.id}, {reload: true});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ReturnToCaller(1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
Wait('stop');
|
||||||
|
// TODO: hopefully this conditional error handling will to away in a future version of tower. The reason why we cannot
|
||||||
|
// simply pass this error to ProcessErrors is because it will actually match the form element 'ssh_key_unlock' and show
|
||||||
|
// the error there. The ssh_key_unlock field is not shown when the kind of credential is gce/azure and as a result the
|
||||||
|
// error is never shown. In the future, the API will hopefully either behave or respond differently.
|
||||||
|
if(status && status === 400 && data && data.ssh_key_unlock && (scope.kind.value === 'gce' || scope.kind.value === 'azure')) {
|
||||||
|
scope.ssh_key_data_api_error = i18n._("Encrypted credentials are not supported.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ProcessErrors(scope, data, status, form, {
|
||||||
|
hdr: i18n._('Error!'),
|
||||||
|
msg: i18n._('Failed to create new Credential. POST status: ') + status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
url = GetBasePath('credentials') + scope.id + '/';
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.put(data)
|
||||||
|
.success(function () {
|
||||||
|
Wait('stop');
|
||||||
|
$state.go($state.current, {}, {reload: true});
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
Wait('stop');
|
||||||
|
ProcessErrors(scope, data, status, form, {
|
||||||
|
hdr: i18n._('Error!'),
|
||||||
|
msg: i18n._('Failed to update Credential. PUT status: ') + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialFormSave.$inject =
|
||||||
|
[ '$rootScope', '$location', 'Alert', 'Rest',
|
||||||
|
'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm',
|
||||||
|
'ReturnToCaller', 'Wait', '$state', 'i18n'
|
||||||
|
];
|
||||||
192
awx/ui/client/src/credentials/factories/kind-change.factory.js
Normal file
192
awx/ui/client/src/credentials/factories/kind-change.factory.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
export default
|
||||||
|
function KindChange(Empty, i18n) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
reset = params.reset,
|
||||||
|
collapse, id;
|
||||||
|
|
||||||
|
$('.popover').each(function() {
|
||||||
|
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
$('.tooltip').each( function() {
|
||||||
|
// close any lingering tool tipss
|
||||||
|
$(this).hide();
|
||||||
|
});
|
||||||
|
// Put things in a default state
|
||||||
|
scope.usernameLabel = i18n._('Username');
|
||||||
|
scope.aws_required = false;
|
||||||
|
scope.email_required = false;
|
||||||
|
scope.rackspace_required = false;
|
||||||
|
scope.sshKeyDataLabel = i18n._('Private Key');
|
||||||
|
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
|
||||||
|
scope.key_required = false; // JT -- doing the same for key and project
|
||||||
|
scope.project_required = false;
|
||||||
|
scope.subscription_required = false;
|
||||||
|
scope.key_description = i18n.sprintf(i18n._("Paste the contents of the SSH private key file.%s or click to close%s"), "<div class=\"popover-footer\"><span class=\"key\">Esc</span>", "</div>");
|
||||||
|
scope.host_required = false;
|
||||||
|
scope.password_required = false;
|
||||||
|
scope.hostLabel = '';
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
|
||||||
|
$('.popover').each(function() {
|
||||||
|
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
$('.tooltip').each( function() {
|
||||||
|
// close any lingering tool tipss
|
||||||
|
$(this).hide();
|
||||||
|
});
|
||||||
|
// Put things in a default state
|
||||||
|
scope.usernameLabel = i18n._('Username');
|
||||||
|
scope.aws_required = false;
|
||||||
|
scope.email_required = false;
|
||||||
|
scope.rackspace_required = false;
|
||||||
|
scope.sshKeyDataLabel = i18n._('Private Key');
|
||||||
|
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
|
||||||
|
scope.key_required = false; // JT -- doing the same for key and project
|
||||||
|
scope.project_required = false;
|
||||||
|
scope.domain_required = false;
|
||||||
|
scope.subscription_required = false;
|
||||||
|
scope.key_description = i18n._("Paste the contents of the SSH private key file.");
|
||||||
|
scope.host_required = false;
|
||||||
|
scope.password_required = false;
|
||||||
|
scope.hostLabel = '';
|
||||||
|
scope.projectLabel = '';
|
||||||
|
scope.domainLabel = '';
|
||||||
|
scope.project_required = false;
|
||||||
|
scope.passwordLabel = i18n._('Password (API Key)');
|
||||||
|
scope.projectPopOver = "<p>" + i18n._("The project value") + "</p>";
|
||||||
|
scope.hostPopOver = "<p>" + i18n._("The host value") + "</p>";
|
||||||
|
scope.ssh_key_data_api_error = '';
|
||||||
|
|
||||||
|
if (!Empty(scope.kind)) {
|
||||||
|
// Apply kind specific settings
|
||||||
|
switch (scope.kind.value) {
|
||||||
|
case 'aws':
|
||||||
|
scope.aws_required = true;
|
||||||
|
break;
|
||||||
|
case 'rax':
|
||||||
|
scope.rackspace_required = true;
|
||||||
|
scope.username_required = true;
|
||||||
|
break;
|
||||||
|
case 'ssh':
|
||||||
|
scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
|
||||||
|
scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
|
||||||
|
scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
|
||||||
|
break;
|
||||||
|
case 'scm':
|
||||||
|
scope.sshKeyDataLabel = i18n._('SCM Private Key');
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
break;
|
||||||
|
case 'gce':
|
||||||
|
scope.usernameLabel = i18n._('Service Account Email Address');
|
||||||
|
scope.sshKeyDataLabel = i18n._('RSA Private Key');
|
||||||
|
scope.email_required = true;
|
||||||
|
scope.key_required = true;
|
||||||
|
scope.project_required = true;
|
||||||
|
scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
|
||||||
|
scope.projectLabel = i18n._("Project");
|
||||||
|
scope.project_required = false;
|
||||||
|
scope.projectPopOver = "<p>" + i18n._("The Project ID is the " +
|
||||||
|
"GCE assigned identification. It is constructed as " +
|
||||||
|
"two words followed by a three digit number. Such " +
|
||||||
|
"as: ") + "</p><p>adjective-noun-000</p>";
|
||||||
|
break;
|
||||||
|
case 'azure':
|
||||||
|
scope.sshKeyDataLabel = i18n._('Management Certificate');
|
||||||
|
scope.subscription_required = true;
|
||||||
|
scope.key_required = true;
|
||||||
|
scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
|
||||||
|
break;
|
||||||
|
case 'azure_rm':
|
||||||
|
scope.usernameLabel = i18n._("Username");
|
||||||
|
scope.subscription_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.azure_rm_required = true;
|
||||||
|
break;
|
||||||
|
case 'vmware':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.hostLabel = "vCenter Host";
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
|
||||||
|
break;
|
||||||
|
case 'openstack':
|
||||||
|
scope.hostLabel = i18n._("Host (Authentication URL)");
|
||||||
|
scope.projectLabel = i18n._("Project (Tenant Name)");
|
||||||
|
scope.domainLabel = i18n._("Domain Name");
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.project_required = true;
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.projectPopOver = "<p>" + i18n._("This is the tenant name. " +
|
||||||
|
" This value is usually the same " +
|
||||||
|
" as the username.") + "</p>";
|
||||||
|
scope.hostPopOver = "<p>" + i18n._("The host to authenticate with.") +
|
||||||
|
"<br />" + i18n.sprintf(i18n._("For example, %s"), "https://openstack.business.com/v2.0/");
|
||||||
|
break;
|
||||||
|
case 'satellite6':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.hostLabel = i18n._("Satellite 6 URL");
|
||||||
|
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL which corresponds to your %s" +
|
||||||
|
"Red Hat Satellite 6 server. %s" +
|
||||||
|
"For example, %s"), "<br />", "<br />", "https://satellite.example.org");
|
||||||
|
break;
|
||||||
|
case 'cloudforms':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = true;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.host_required = true;
|
||||||
|
scope.hostLabel = i18n._("CloudForms URL");
|
||||||
|
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL for the virtual machine which %s" +
|
||||||
|
"corresponds to your CloudForm instance. %s" +
|
||||||
|
"For example, %s"), "<br />", "<br />", "https://cloudforms.example.org");
|
||||||
|
break;
|
||||||
|
case 'net':
|
||||||
|
scope.username_required = true;
|
||||||
|
scope.password_required = false;
|
||||||
|
scope.passwordLabel = i18n._('Password');
|
||||||
|
scope.sshKeyDataLabel = i18n._('SSH Key');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all the field values related to Kind.
|
||||||
|
if (reset) {
|
||||||
|
scope.access_key = null;
|
||||||
|
scope.secret_key = null;
|
||||||
|
scope.api_key = null;
|
||||||
|
scope.username = null;
|
||||||
|
scope.password = null;
|
||||||
|
scope.password_confirm = null;
|
||||||
|
scope.ssh_key_data = null;
|
||||||
|
scope.ssh_key_unlock = null;
|
||||||
|
scope.ssh_key_unlock_confirm = null;
|
||||||
|
scope.become_username = null;
|
||||||
|
scope.become_password = null;
|
||||||
|
scope.authorize = false;
|
||||||
|
scope.authorize_password = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collapse or open help widget based on whether scm value is selected
|
||||||
|
collapse = $('#credential_kind').parent().find('.panel-collapse').first();
|
||||||
|
id = collapse.attr('id');
|
||||||
|
if (!Empty(scope.kind) && scope.kind.value !== '') {
|
||||||
|
if ($('#' + id + '-icon').hasClass('icon-minus')) {
|
||||||
|
scope.accordionToggle('#' + id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($('#' + id + '-icon').hasClass('icon-plus')) {
|
||||||
|
scope.accordionToggle('#' + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
KindChange.$inject =
|
||||||
|
[ 'Empty', 'i18n' ];
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export default
|
||||||
|
function OwnerChange() {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
owner = scope.owner;
|
||||||
|
if (owner === 'team') {
|
||||||
|
scope.team_required = true;
|
||||||
|
scope.user_required = false;
|
||||||
|
scope.user = null;
|
||||||
|
scope.user_username = null;
|
||||||
|
} else {
|
||||||
|
scope.team_required = false;
|
||||||
|
scope.user_required = true;
|
||||||
|
scope.team = null;
|
||||||
|
scope.team_name = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,11 +8,19 @@ import ownerList from './ownerList.directive';
|
|||||||
import CredentialsList from './list/credentials-list.controller';
|
import CredentialsList from './list/credentials-list.controller';
|
||||||
import CredentialsAdd from './add/credentials-add.controller';
|
import CredentialsAdd from './add/credentials-add.controller';
|
||||||
import CredentialsEdit from './edit/credentials-edit.controller';
|
import CredentialsEdit from './edit/credentials-edit.controller';
|
||||||
|
import BecomeMethodChange from './factories/become-method-change.factory';
|
||||||
|
import CredentialFormSave from './factories/credential-form-save.factory';
|
||||||
|
import KindChange from './factories/kind-change.factory';
|
||||||
|
import OwnerChange from './factories/owner-change.factory';
|
||||||
import { N_ } from '../i18n';
|
import { N_ } from '../i18n';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('credentials', [])
|
angular.module('credentials', [])
|
||||||
.directive('ownerList', ownerList)
|
.directive('ownerList', ownerList)
|
||||||
|
.factory('BecomeMethodChange', BecomeMethodChange)
|
||||||
|
.factory('CredentialFormSave', CredentialFormSave)
|
||||||
|
.factory('KindChange', KindChange)
|
||||||
|
.factory('OwnerChange', OwnerChange)
|
||||||
.controller('CredentialsList', CredentialsList)
|
.controller('CredentialsList', CredentialsList)
|
||||||
.controller('CredentialsAdd', CredentialsAdd)
|
.controller('CredentialsAdd', CredentialsAdd)
|
||||||
.controller('CredentialsEdit', CredentialsEdit)
|
.controller('CredentialsEdit', CredentialsEdit)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
import './forms';
|
|
||||||
import './lists';
|
|
||||||
|
|
||||||
import Children from "./helpers/Children";
|
|
||||||
import Credentials from "./helpers/Credentials";
|
|
||||||
import Events from "./helpers/Events";
|
|
||||||
import Groups from "./helpers/Groups";
|
|
||||||
import Hosts from "./helpers/Hosts";
|
|
||||||
import JobSubmission from "./helpers/JobSubmission";
|
|
||||||
import JobTemplates from "./helpers/JobTemplates";
|
|
||||||
import Jobs from "./helpers/Jobs";
|
|
||||||
import LoadConfig from "./helpers/LoadConfig";
|
|
||||||
import Parse from "./helpers/Parse";
|
|
||||||
import ProjectPath from "./helpers/ProjectPath";
|
|
||||||
import Projects from "./helpers/Projects";
|
|
||||||
import Schedules from "./helpers/Schedules";
|
|
||||||
import Selection from "./helpers/Selection";
|
|
||||||
import Users from "./helpers/Users";
|
|
||||||
import Variables from "./helpers/Variables";
|
|
||||||
import ApiDefaults from "./helpers/api-defaults";
|
|
||||||
import inventory from "./helpers/inventory";
|
|
||||||
import MD5 from "./helpers/md5";
|
|
||||||
import Teams from "./helpers/teams";
|
|
||||||
import AdhocHelper from "./helpers/Adhoc";
|
|
||||||
import ApiModelHelper from "./helpers/ApiModel";
|
|
||||||
import ActivityStreamHelper from "./helpers/ActivityStream";
|
|
||||||
|
|
||||||
export
|
|
||||||
{ Children,
|
|
||||||
Credentials,
|
|
||||||
Events,
|
|
||||||
Groups,
|
|
||||||
Hosts,
|
|
||||||
JobSubmission,
|
|
||||||
JobTemplates,
|
|
||||||
Jobs,
|
|
||||||
LoadConfig,
|
|
||||||
Parse,
|
|
||||||
ProjectPath,
|
|
||||||
Projects,
|
|
||||||
Schedules,
|
|
||||||
Selection,
|
|
||||||
Users,
|
|
||||||
Variables,
|
|
||||||
ApiDefaults,
|
|
||||||
inventory,
|
|
||||||
MD5,
|
|
||||||
Teams,
|
|
||||||
AdhocHelper,
|
|
||||||
ApiModelHelper,
|
|
||||||
ActivityStreamHelper
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2016 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:ActivityStream
|
|
||||||
* @description Helper functions for the activity stream
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ActivityStreamHelper', ['Utilities'])
|
|
||||||
.factory('GetTargetTitle', ['i18n',
|
|
||||||
function (i18n) {
|
|
||||||
return function (target) {
|
|
||||||
|
|
||||||
var rtnTitle = i18n._('ALL ACTIVITY');
|
|
||||||
|
|
||||||
switch(target) {
|
|
||||||
case 'project':
|
|
||||||
rtnTitle = i18n._('PROJECTS');
|
|
||||||
break;
|
|
||||||
case 'inventory':
|
|
||||||
rtnTitle = i18n._('INVENTORIES');
|
|
||||||
break;
|
|
||||||
case 'credential':
|
|
||||||
rtnTitle = i18n._('CREDENTIALS');
|
|
||||||
break;
|
|
||||||
case 'user':
|
|
||||||
rtnTitle = i18n._('USERS');
|
|
||||||
break;
|
|
||||||
case 'team':
|
|
||||||
rtnTitle = i18n._('TEAMS');
|
|
||||||
break;
|
|
||||||
case 'notification_template':
|
|
||||||
rtnTitle = i18n._('NOTIFICATION TEMPLATES');
|
|
||||||
break;
|
|
||||||
case 'organization':
|
|
||||||
rtnTitle = i18n._('ORGANIZATIONS');
|
|
||||||
break;
|
|
||||||
case 'job':
|
|
||||||
rtnTitle = i18n._('JOBS');
|
|
||||||
break;
|
|
||||||
case 'custom_inventory_script':
|
|
||||||
rtnTitle = i18n._('INVENTORY SCRIPTS');
|
|
||||||
break;
|
|
||||||
case 'schedule':
|
|
||||||
rtnTitle = i18n._('SCHEDULES');
|
|
||||||
break;
|
|
||||||
case 'host':
|
|
||||||
rtnTitle = i18n._('HOSTS');
|
|
||||||
break;
|
|
||||||
case 'template':
|
|
||||||
rtnTitle = i18n._('TEMPLATES');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtnTitle;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Adhoc
|
|
||||||
* @description These routines are shared by adhoc command related controllers.
|
|
||||||
* The content here is very similar to the JobSubmission helper, and in fact,
|
|
||||||
* certain services are pulled from that helper. This leads to an important
|
|
||||||
* point: if you need to create functionality that is shared between the command
|
|
||||||
* and playbook run process, put that code in the JobSubmission helper and make
|
|
||||||
* it into a reusable step (by specifying a callback parameter in the factory).
|
|
||||||
* For a good example of this, please see how the AdhocLaunch factory in this
|
|
||||||
* file utilizes the CheckPasswords factory from the JobSubmission helper.
|
|
||||||
*
|
|
||||||
* #AdhocRelaunch Step 1: preparing the GET to ad_hoc_commands/n/relaunch
|
|
||||||
* The adhoc relaunch process is called from the JobSubmission helper. It is a
|
|
||||||
* separate process from the initial adhoc run becuase of the way the API
|
|
||||||
* endpoints work. For AdhocRelaunch, we have access to the original run and
|
|
||||||
* we can pull the related relaunch URL by knowing the original Adhoc runs ID.
|
|
||||||
*
|
|
||||||
* #AdhocRelaunch Step 2: If we got passwords back, add them
|
|
||||||
* The relaunch URL gives us back the passwords we need to prompt for (if any).
|
|
||||||
* We'll go to step 3 if there are passwords, and step 4 if not.
|
|
||||||
*
|
|
||||||
* #AdhocRelaunch Step 3: PromptForPasswords and the CreateLaunchDialog
|
|
||||||
*
|
|
||||||
* #AdhocRelaunch Step 5: StartAdhocRun
|
|
||||||
*
|
|
||||||
* #AdhocRelaunch Step 6: LaunchJob and navigate to the standard out page.
|
|
||||||
|
|
||||||
* **If you are
|
|
||||||
* 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',
|
|
||||||
'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','$stateParams', 'LaunchJob',
|
|
||||||
'PromptForPasswords', 'Rest', 'GetBasePath', 'Alert', 'ProcessErrors',
|
|
||||||
'Wait', 'Empty', 'CreateLaunchDialog', '$state',
|
|
||||||
function ($location, $stateParams, LaunchJob, PromptForPasswords,
|
|
||||||
Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, CreateLaunchDialog, $state) {
|
|
||||||
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.removeStartAdhocRun) {
|
|
||||||
scope.removeStartAdhocRun();
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.removeStartAdhocRun = scope.$on('StartAdhocRun', function() {
|
|
||||||
var password,
|
|
||||||
postData={};
|
|
||||||
for (password in scope.passwords) {
|
|
||||||
postData[scope.passwords[password]] = scope[
|
|
||||||
scope.passwords[password]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
// Re-launch the adhoc job
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.post(postData)
|
|
||||||
.success(function (data) {
|
|
||||||
Wait('stop');
|
|
||||||
if($location.path().replace(/^\//, '').split('/')[0] !== 'jobs') {
|
|
||||||
$state.go('adHocJobStdout', {id: data.id});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: 'Failed to launch adhoc command. POST ' +
|
|
||||||
'returned status: ' + status });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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 });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2016 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:ApiModel
|
|
||||||
* @description Helper functions to convert singular/plural versions of our models to the opposite
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ApiModelHelper', ['Utilities'])
|
|
||||||
.factory('ModelToBasePathKey', [
|
|
||||||
function () {
|
|
||||||
return function (model) {
|
|
||||||
// This function takes in the singular model string and returns the key needed
|
|
||||||
// to get the base path from $rootScope/local storage.
|
|
||||||
|
|
||||||
var basePathKey;
|
|
||||||
|
|
||||||
switch(model) {
|
|
||||||
case 'project':
|
|
||||||
basePathKey = 'projects';
|
|
||||||
break;
|
|
||||||
case 'inventory':
|
|
||||||
basePathKey = 'inventory';
|
|
||||||
break;
|
|
||||||
case 'job_template':
|
|
||||||
basePathKey = 'job_templates';
|
|
||||||
break;
|
|
||||||
case 'credential':
|
|
||||||
basePathKey = 'credentials';
|
|
||||||
break;
|
|
||||||
case 'user':
|
|
||||||
basePathKey = 'users';
|
|
||||||
break;
|
|
||||||
case 'team':
|
|
||||||
basePathKey = 'teams';
|
|
||||||
break;
|
|
||||||
case 'notification_template':
|
|
||||||
basePathKey = 'notification_templates';
|
|
||||||
break;
|
|
||||||
case 'organization':
|
|
||||||
basePathKey = 'organizations';
|
|
||||||
break;
|
|
||||||
case 'management_job':
|
|
||||||
basePathKey = 'management_jobs';
|
|
||||||
break;
|
|
||||||
case 'custom_inventory_script':
|
|
||||||
basePathKey = 'inventory_scripts';
|
|
||||||
break;
|
|
||||||
case 'workflow_job_template':
|
|
||||||
basePathKey = 'workflow_job_templates';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return basePathKey;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Children
|
|
||||||
* @descriptionUsed in job_events to expand/collapse children by setting the
|
|
||||||
* 'show' attribute of each job_event in the set of job_events.
|
|
||||||
* See the filter in job_events.js list.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ChildrenHelper', ['RestServices', 'Utilities'])
|
|
||||||
.factory('ToggleChildren', ['$location', 'Store', function ($location, Store) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
list = params.list,
|
|
||||||
id = params.id,
|
|
||||||
set = scope[list.name],
|
|
||||||
clicked,
|
|
||||||
//base = $location.path().replace(/^\//, '').split('/')[0],
|
|
||||||
path = $location.path(),
|
|
||||||
local_child_store;
|
|
||||||
|
|
||||||
function updateExpand(key, expand) {
|
|
||||||
var found = false;
|
|
||||||
local_child_store.every(function(child, i) {
|
|
||||||
if (child.key === key) {
|
|
||||||
local_child_store[i].expand = expand;
|
|
||||||
found = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (!found) {
|
|
||||||
local_child_store.push({ key: key, expand: expand });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateShow(key, show) {
|
|
||||||
var found = false;
|
|
||||||
local_child_store.every(function(child, i) {
|
|
||||||
if (child.key === key) {
|
|
||||||
local_child_store[i].show = show;
|
|
||||||
found = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (!found) {
|
|
||||||
local_child_store.push({ key: key, show: show });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function expand(node) {
|
|
||||||
var i, has_children = false;
|
|
||||||
for (i = node + 1; i < set.length; i++) {
|
|
||||||
if (set[i].parent === set[node].id) {
|
|
||||||
updateShow(set[i].key, true);
|
|
||||||
set[i].show = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set[node].ngicon = (has_children) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-minus-square-o node-toggle';
|
|
||||||
}
|
|
||||||
|
|
||||||
function collapse(node) {
|
|
||||||
var i, has_children = false;
|
|
||||||
for (i = node + 1; i < set.length; i++) {
|
|
||||||
if (set[i].parent === set[node].id) {
|
|
||||||
set[i].show = false;
|
|
||||||
has_children = true;
|
|
||||||
updateShow(set[i].key, false);
|
|
||||||
if (set[i].related.children) {
|
|
||||||
collapse(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set[node].ngicon = (has_children) ? 'fa fa-plus-square-o node-toggle' : 'fa fa-square-o node-toggle';
|
|
||||||
}
|
|
||||||
|
|
||||||
local_child_store = Store(path + '_children');
|
|
||||||
if (!local_child_store) {
|
|
||||||
local_child_store = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan the array list and find the clicked element
|
|
||||||
set.every(function(row, i) {
|
|
||||||
if (row.id === id) {
|
|
||||||
clicked = i;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expand or collapse children based on clicked element's icon
|
|
||||||
if (/plus-square-o/.test(set[clicked].ngicon)) {
|
|
||||||
// Expand: lookup and display children
|
|
||||||
expand(clicked);
|
|
||||||
updateExpand(set[clicked].key, true);
|
|
||||||
} else if (/minus-square-o/.test(set[clicked].ngicon)) {
|
|
||||||
collapse(clicked);
|
|
||||||
updateExpand(set[clicked].key, false);
|
|
||||||
}
|
|
||||||
Store(path + '_children', local_child_store);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,432 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Credentials
|
|
||||||
* @description Functions shared amongst Credential related controllers
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('CredentialsHelper', ['Utilities'])
|
|
||||||
|
|
||||||
.factory('KindChange', ['Empty', 'i18n',
|
|
||||||
function (Empty, i18n) {
|
|
||||||
return function (params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
reset = params.reset,
|
|
||||||
collapse, id;
|
|
||||||
|
|
||||||
$('.popover').each(function() {
|
|
||||||
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
$('.tooltip').each( function() {
|
|
||||||
// close any lingering tool tipss
|
|
||||||
$(this).hide();
|
|
||||||
});
|
|
||||||
// Put things in a default state
|
|
||||||
scope.usernameLabel = i18n._('Username');
|
|
||||||
scope.aws_required = false;
|
|
||||||
scope.email_required = false;
|
|
||||||
scope.rackspace_required = false;
|
|
||||||
scope.sshKeyDataLabel = i18n._('Private Key');
|
|
||||||
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
|
|
||||||
scope.key_required = false; // JT -- doing the same for key and project
|
|
||||||
scope.project_required = false;
|
|
||||||
scope.subscription_required = false;
|
|
||||||
scope.key_description = i18n.sprintf(i18n._("Paste the contents of the SSH private key file.%s or click to close%s"), "<div class=\"popover-footer\"><span class=\"key\">Esc</span>", "</div>");
|
|
||||||
scope.host_required = false;
|
|
||||||
scope.password_required = false;
|
|
||||||
scope.hostLabel = '';
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
|
|
||||||
$('.popover').each(function() {
|
|
||||||
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
$('.tooltip').each( function() {
|
|
||||||
// close any lingering tool tipss
|
|
||||||
$(this).hide();
|
|
||||||
});
|
|
||||||
// Put things in a default state
|
|
||||||
scope.usernameLabel = i18n._('Username');
|
|
||||||
scope.aws_required = false;
|
|
||||||
scope.email_required = false;
|
|
||||||
scope.rackspace_required = false;
|
|
||||||
scope.sshKeyDataLabel = i18n._('Private Key');
|
|
||||||
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
|
|
||||||
scope.key_required = false; // JT -- doing the same for key and project
|
|
||||||
scope.project_required = false;
|
|
||||||
scope.domain_required = false;
|
|
||||||
scope.subscription_required = false;
|
|
||||||
scope.key_description = i18n._("Paste the contents of the SSH private key file.");
|
|
||||||
scope.host_required = false;
|
|
||||||
scope.password_required = false;
|
|
||||||
scope.hostLabel = '';
|
|
||||||
scope.projectLabel = '';
|
|
||||||
scope.domainLabel = '';
|
|
||||||
scope.project_required = false;
|
|
||||||
scope.passwordLabel = i18n._('Password (API Key)');
|
|
||||||
scope.projectPopOver = "<p>" + i18n._("The project value") + "</p>";
|
|
||||||
scope.hostPopOver = "<p>" + i18n._("The host value") + "</p>";
|
|
||||||
scope.ssh_key_data_api_error = '';
|
|
||||||
|
|
||||||
if (!Empty(scope.kind)) {
|
|
||||||
// Apply kind specific settings
|
|
||||||
switch (scope.kind.value) {
|
|
||||||
case 'aws':
|
|
||||||
scope.aws_required = true;
|
|
||||||
break;
|
|
||||||
case 'rax':
|
|
||||||
scope.rackspace_required = true;
|
|
||||||
scope.username_required = true;
|
|
||||||
break;
|
|
||||||
case 'ssh':
|
|
||||||
scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
|
|
||||||
scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
|
|
||||||
scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
|
|
||||||
break;
|
|
||||||
case 'scm':
|
|
||||||
scope.sshKeyDataLabel = i18n._('SCM Private Key');
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
break;
|
|
||||||
case 'gce':
|
|
||||||
scope.usernameLabel = i18n._('Service Account Email Address');
|
|
||||||
scope.sshKeyDataLabel = i18n._('RSA Private Key');
|
|
||||||
scope.email_required = true;
|
|
||||||
scope.key_required = true;
|
|
||||||
scope.project_required = true;
|
|
||||||
scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
|
|
||||||
scope.projectLabel = i18n._("Project");
|
|
||||||
scope.project_required = false;
|
|
||||||
scope.projectPopOver = "<p>" + i18n._("The Project ID is the " +
|
|
||||||
"GCE assigned identification. It is constructed as " +
|
|
||||||
"two words followed by a three digit number. Such " +
|
|
||||||
"as: ") + "</p><p>adjective-noun-000</p>";
|
|
||||||
break;
|
|
||||||
case 'azure':
|
|
||||||
scope.sshKeyDataLabel = i18n._('Management Certificate');
|
|
||||||
scope.subscription_required = true;
|
|
||||||
scope.key_required = true;
|
|
||||||
scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
|
|
||||||
break;
|
|
||||||
case 'azure_rm':
|
|
||||||
scope.usernameLabel = i18n._("Username");
|
|
||||||
scope.subscription_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.azure_rm_required = true;
|
|
||||||
break;
|
|
||||||
case 'vmware':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.hostLabel = "vCenter Host";
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
|
|
||||||
break;
|
|
||||||
case 'openstack':
|
|
||||||
scope.hostLabel = i18n._("Host (Authentication URL)");
|
|
||||||
scope.projectLabel = i18n._("Project (Tenant Name)");
|
|
||||||
scope.domainLabel = i18n._("Domain Name");
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.project_required = true;
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.projectPopOver = "<p>" + i18n._("This is the tenant name. " +
|
|
||||||
" This value is usually the same " +
|
|
||||||
" as the username.") + "</p>";
|
|
||||||
scope.hostPopOver = "<p>" + i18n._("The host to authenticate with.") +
|
|
||||||
"<br />" + i18n.sprintf(i18n._("For example, %s"), "https://openstack.business.com/v2.0/");
|
|
||||||
break;
|
|
||||||
case 'satellite6':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.hostLabel = i18n._("Satellite 6 URL");
|
|
||||||
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL which corresponds to your %s" +
|
|
||||||
"Red Hat Satellite 6 server. %s" +
|
|
||||||
"For example, %s"), "<br />", "<br />", "https://satellite.example.org");
|
|
||||||
break;
|
|
||||||
case 'cloudforms':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.hostLabel = i18n._("CloudForms URL");
|
|
||||||
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL for the virtual machine which %s" +
|
|
||||||
"corresponds to your CloudForm instance. %s" +
|
|
||||||
"For example, %s"), "<br />", "<br />", "https://cloudforms.example.org");
|
|
||||||
break;
|
|
||||||
case 'net':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = false;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.sshKeyDataLabel = i18n._('SSH Key');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset all the field values related to Kind.
|
|
||||||
if (reset) {
|
|
||||||
scope.access_key = null;
|
|
||||||
scope.secret_key = null;
|
|
||||||
scope.api_key = null;
|
|
||||||
scope.username = null;
|
|
||||||
scope.password = null;
|
|
||||||
scope.password_confirm = null;
|
|
||||||
scope.ssh_key_data = null;
|
|
||||||
scope.ssh_key_unlock = null;
|
|
||||||
scope.ssh_key_unlock_confirm = null;
|
|
||||||
scope.become_username = null;
|
|
||||||
scope.become_password = null;
|
|
||||||
scope.authorize = false;
|
|
||||||
scope.authorize_password = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapse or open help widget based on whether scm value is selected
|
|
||||||
collapse = $('#credential_kind').parent().find('.panel-collapse').first();
|
|
||||||
id = collapse.attr('id');
|
|
||||||
if (!Empty(scope.kind) && scope.kind.value !== '') {
|
|
||||||
if ($('#' + id + '-icon').hasClass('icon-minus')) {
|
|
||||||
scope.accordionToggle('#' + id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($('#' + id + '-icon').hasClass('icon-plus')) {
|
|
||||||
scope.accordionToggle('#' + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('BecomeMethodChange', ['Empty', 'i18n',
|
|
||||||
function (Empty, i18n) {
|
|
||||||
return function (params) {
|
|
||||||
console.log('become method has changed');
|
|
||||||
var scope = params.scope;
|
|
||||||
|
|
||||||
if (!Empty(scope.kind)) {
|
|
||||||
// Apply kind specific settings
|
|
||||||
switch (scope.kind.value) {
|
|
||||||
case 'aws':
|
|
||||||
scope.aws_required = true;
|
|
||||||
break;
|
|
||||||
case 'rax':
|
|
||||||
scope.rackspace_required = true;
|
|
||||||
scope.username_required = true;
|
|
||||||
break;
|
|
||||||
case 'ssh':
|
|
||||||
scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
|
|
||||||
scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
|
|
||||||
scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
|
|
||||||
break;
|
|
||||||
case 'scm':
|
|
||||||
scope.sshKeyDataLabel = i18n._('SCM Private Key');
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
break;
|
|
||||||
case 'gce':
|
|
||||||
scope.usernameLabel = i18n._('Service Account Email Address');
|
|
||||||
scope.sshKeyDataLabel = i18n._('RSA Private Key');
|
|
||||||
scope.email_required = true;
|
|
||||||
scope.key_required = true;
|
|
||||||
scope.project_required = true;
|
|
||||||
scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
|
|
||||||
scope.projectLabel = i18n._("Project");
|
|
||||||
scope.project_required = false;
|
|
||||||
scope.projectPopOver = "<p>" + i18n._("The Project ID is the " +
|
|
||||||
"GCE assigned identification. It is constructed as " +
|
|
||||||
"two words followed by a three digit number. Such " +
|
|
||||||
"as: ") + "</p><p>adjective-noun-000</p>";
|
|
||||||
break;
|
|
||||||
case 'azure':
|
|
||||||
scope.sshKeyDataLabel = i18n._('Management Certificate');
|
|
||||||
scope.subscription_required = true;
|
|
||||||
scope.key_required = true;
|
|
||||||
scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
|
|
||||||
break;
|
|
||||||
case 'azure_rm':
|
|
||||||
scope.usernameLabel = i18n._("Username");
|
|
||||||
scope.subscription_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.azure_rm_required = true;
|
|
||||||
break;
|
|
||||||
case 'vmware':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.hostLabel = "vCenter Host";
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
|
|
||||||
break;
|
|
||||||
case 'openstack':
|
|
||||||
scope.hostLabel = i18n._("Host (Authentication URL)");
|
|
||||||
scope.projectLabel = i18n._("Project (Tenant Name)");
|
|
||||||
scope.domainLabel = i18n._("Domain Name");
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.project_required = true;
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.projectPopOver = "<p>" + i18n._("This is the tenant name. " +
|
|
||||||
" This value is usually the same " +
|
|
||||||
" as the username.") + "</p>";
|
|
||||||
scope.hostPopOver = "<p>" + i18n._("The host to authenticate with.") +
|
|
||||||
"<br />" + i18n.sprintf(i18n._("For example, %s"), "https://openstack.business.com/v2.0/");
|
|
||||||
break;
|
|
||||||
case 'satellite6':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.hostLabel = i18n._("Satellite 6 URL");
|
|
||||||
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL which corresponds to your %s" +
|
|
||||||
"Red Hat Satellite 6 server. %s" +
|
|
||||||
"For example, %s"), "<br />", "<br />", "https://satellite.example.org");
|
|
||||||
break;
|
|
||||||
case 'cloudforms':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = true;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.host_required = true;
|
|
||||||
scope.hostLabel = i18n._("CloudForms URL");
|
|
||||||
scope.hostPopOver = i18n.sprintf(i18n._("Enter the URL for the virtual machine which %s" +
|
|
||||||
"corresponds to your CloudForm instance. %s" +
|
|
||||||
"For example, %s"), "<br />", "<br />", "https://cloudforms.example.org");
|
|
||||||
break;
|
|
||||||
case 'net':
|
|
||||||
scope.username_required = true;
|
|
||||||
scope.password_required = false;
|
|
||||||
scope.passwordLabel = i18n._('Password');
|
|
||||||
scope.sshKeyDataLabel = i18n._('SSH Key');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
.factory('OwnerChange', [
|
|
||||||
function () {
|
|
||||||
return function (params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
owner = scope.owner;
|
|
||||||
if (owner === 'team') {
|
|
||||||
scope.team_required = true;
|
|
||||||
scope.user_required = false;
|
|
||||||
scope.user = null;
|
|
||||||
scope.user_username = null;
|
|
||||||
} else {
|
|
||||||
scope.team_required = false;
|
|
||||||
scope.user_required = true;
|
|
||||||
scope.team = null;
|
|
||||||
scope.team_name = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('FormSave', ['$rootScope', '$location', 'Alert', 'Rest', 'ProcessErrors', 'Empty', 'GetBasePath', 'CredentialForm', 'ReturnToCaller', 'Wait', '$state', 'i18n',
|
|
||||||
function ($rootScope, $location, Alert, Rest, ProcessErrors, Empty, GetBasePath, CredentialForm, ReturnToCaller, Wait, $state, i18n) {
|
|
||||||
return function (params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
mode = params.mode,
|
|
||||||
form = CredentialForm,
|
|
||||||
data = {}, fld, url;
|
|
||||||
|
|
||||||
for (fld in form.fields) {
|
|
||||||
if (fld !== 'access_key' && fld !== 'secret_key' && fld !== 'ssh_username' &&
|
|
||||||
fld !== 'ssh_password') {
|
|
||||||
if (fld === "organization" && !scope[fld]) {
|
|
||||||
data.user = $rootScope.current_user.id;
|
|
||||||
} else if (scope[fld] === null) {
|
|
||||||
data[fld] = "";
|
|
||||||
} else {
|
|
||||||
data[fld] = scope[fld];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.kind = scope.kind.value;
|
|
||||||
if (scope.become_method === null || typeof scope.become_method === 'undefined') {
|
|
||||||
data.become_method = "";
|
|
||||||
data.become_username = "";
|
|
||||||
data.become_password = "";
|
|
||||||
} else {
|
|
||||||
data.become_method = (scope.become_method.value) ? scope.become_method.value : "";
|
|
||||||
}
|
|
||||||
switch (data.kind) {
|
|
||||||
case 'ssh':
|
|
||||||
data.password = scope.ssh_password;
|
|
||||||
break;
|
|
||||||
case 'aws':
|
|
||||||
data.username = scope.access_key;
|
|
||||||
data.password = scope.secret_key;
|
|
||||||
break;
|
|
||||||
case 'rax':
|
|
||||||
data.password = scope.api_key;
|
|
||||||
break;
|
|
||||||
case 'gce':
|
|
||||||
data.username = scope.email_address;
|
|
||||||
data.project = scope.project;
|
|
||||||
break;
|
|
||||||
case 'azure':
|
|
||||||
data.username = scope.subscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
if (mode === 'add') {
|
|
||||||
url = GetBasePath("credentials");
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.post(data)
|
|
||||||
.success(function (data) {
|
|
||||||
scope.addedItem = data.id;
|
|
||||||
Wait('stop');
|
|
||||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
|
||||||
if (base === 'credentials') {
|
|
||||||
$state.go('credentials.edit', {credential_id: data.id}, {reload: true});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ReturnToCaller(1);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
Wait('stop');
|
|
||||||
// TODO: hopefully this conditional error handling will to away in a future version of tower. The reason why we cannot
|
|
||||||
// simply pass this error to ProcessErrors is because it will actually match the form element 'ssh_key_unlock' and show
|
|
||||||
// the error there. The ssh_key_unlock field is not shown when the kind of credential is gce/azure and as a result the
|
|
||||||
// error is never shown. In the future, the API will hopefully either behave or respond differently.
|
|
||||||
if(status && status === 400 && data && data.ssh_key_unlock && (scope.kind.value === 'gce' || scope.kind.value === 'azure')) {
|
|
||||||
scope.ssh_key_data_api_error = i18n._("Encrypted credentials are not supported.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ProcessErrors(scope, data, status, form, {
|
|
||||||
hdr: i18n._('Error!'),
|
|
||||||
msg: i18n._('Failed to create new Credential. POST status: ') + status
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
url = GetBasePath('credentials') + scope.id + '/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.put(data)
|
|
||||||
.success(function () {
|
|
||||||
Wait('stop');
|
|
||||||
$state.go($state.current, {}, {reload: true});
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
Wait('stop');
|
|
||||||
ProcessErrors(scope, data, status, form, {
|
|
||||||
hdr: i18n._('Error!'),
|
|
||||||
msg: i18n._('Failed to update Credential. PUT status: ') + status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Events
|
|
||||||
* @description EventView - show the job_events form in a modal dialog
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefinition', 'JobEventsFormDefinition'])
|
|
||||||
|
|
||||||
.factory('EventView', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'GenerateForm',
|
|
||||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'JobEventDataForm', 'Empty', 'JobEventsForm',
|
|
||||||
function ($rootScope, $location, $log, $stateParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
|
|
||||||
FormatDate, JobEventDataForm, Empty, JobEventsForm) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var event_id = params.event_id,
|
|
||||||
generator = GenerateForm,
|
|
||||||
form = angular.copy(JobEventsForm),
|
|
||||||
scope,
|
|
||||||
defaultUrl = GetBasePath('base') + 'job_events/' + event_id + '/';
|
|
||||||
|
|
||||||
// Retrieve detail record and prepopulate the form
|
|
||||||
Rest.setUrl(defaultUrl);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
var i, n, fld, rows, txt, cDate;
|
|
||||||
|
|
||||||
// If event_data is not available, remove fields that depend on it
|
|
||||||
if ($.isEmptyObject(data.event_data) || !data.event_data.res || typeof data.event_data.res === 'string') {
|
|
||||||
for (fld in form.fields) {
|
|
||||||
switch (fld) {
|
|
||||||
case 'start':
|
|
||||||
case 'end':
|
|
||||||
case 'delta':
|
|
||||||
case 'msg':
|
|
||||||
case 'stdout':
|
|
||||||
case 'stderr':
|
|
||||||
case 'msg':
|
|
||||||
case 'results':
|
|
||||||
case 'module_name':
|
|
||||||
case 'module_args':
|
|
||||||
case 'rc':
|
|
||||||
delete form.fields[fld];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($.isEmptyObject(data.event_data) || !data.event_data.res || typeof data.event_data.res !== 'string') {
|
|
||||||
delete form.fields.traceback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove remaining form fields that do not have a corresponding data value
|
|
||||||
for (fld in form.fields) {
|
|
||||||
switch (fld) {
|
|
||||||
case 'start':
|
|
||||||
case 'end':
|
|
||||||
case 'delta':
|
|
||||||
case 'msg':
|
|
||||||
case 'stdout':
|
|
||||||
case 'stderr':
|
|
||||||
case 'msg':
|
|
||||||
case 'rc':
|
|
||||||
if (data.event_data && data.event_data.res && Empty(data.event_data.res[fld])) {
|
|
||||||
delete form.fields[fld];
|
|
||||||
} else {
|
|
||||||
if (form.fields[fld].type === 'textarea') {
|
|
||||||
n = data.event_data.res[fld].match(/\n/g);
|
|
||||||
rows = (n) ? n.length : 1;
|
|
||||||
rows = (rows > 10) ? 10 : rows;
|
|
||||||
rows = (rows < 3) ? 3 : rows;
|
|
||||||
form.fields[fld].rows = rows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'results':
|
|
||||||
if (data.event_data && data.event_data.res && data.event_data.res[fld] === undefined) {
|
|
||||||
// not defined
|
|
||||||
delete form.fields[fld];
|
|
||||||
} else if (!Array.isArray(data.event_data.res[fld]) || data.event_data.res[fld].length === 0) {
|
|
||||||
// defined, but empty
|
|
||||||
delete form.fields[fld];
|
|
||||||
} else {
|
|
||||||
// defined and not empty, so attempt to size the textarea field
|
|
||||||
txt = '';
|
|
||||||
for (i = 0; i < data.event_data.res[fld].length; i++) {
|
|
||||||
txt += data.event_data.res[fld][i];
|
|
||||||
}
|
|
||||||
if (txt === '') {
|
|
||||||
// there's an array, but the actual text is empty
|
|
||||||
delete form.fields[fld];
|
|
||||||
} else {
|
|
||||||
n = txt.match(/\n/g);
|
|
||||||
rows = (n) ? n.length : 1;
|
|
||||||
rows = (rows > 10) ? 10 : rows;
|
|
||||||
rows = (rows < 3) ? 3 : rows;
|
|
||||||
form.fields[fld].rows = rows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'module_name':
|
|
||||||
case 'module_args':
|
|
||||||
if (data.event_data && data.event_data.res) {
|
|
||||||
if (data.event_data.res.invocation === undefined ||
|
|
||||||
data.event_data.res.invocation[fld] === undefined) {
|
|
||||||
delete form.fields[fld];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the form
|
|
||||||
scope = generator.inject(form, {
|
|
||||||
mode: 'edit',
|
|
||||||
modal: true,
|
|
||||||
related: false
|
|
||||||
});
|
|
||||||
generator.reset();
|
|
||||||
scope.formModalAction = function () {
|
|
||||||
$('#form-modal').modal("hide");
|
|
||||||
};
|
|
||||||
scope.formModalActionLabel = 'OK';
|
|
||||||
scope.formModalCancelShow = false;
|
|
||||||
scope.formModalInfo = 'View JSON';
|
|
||||||
$('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none');
|
|
||||||
$('#form-modal').addClass('skinny-modal');
|
|
||||||
scope.formModalHeader = data.event_display.replace(/^\u00a0*/g, '');
|
|
||||||
|
|
||||||
// Respond to View JSON button
|
|
||||||
scope.formModalInfoAction = function () {
|
|
||||||
var generator = GenerateForm,
|
|
||||||
scope = generator.inject(JobEventDataForm, {
|
|
||||||
mode: 'edit',
|
|
||||||
modal: true,
|
|
||||||
related: false,
|
|
||||||
modal_selector: '#form-modal2',
|
|
||||||
modal_body_id: 'form-modal2-body',
|
|
||||||
modal_title_id: 'formModal2Header'
|
|
||||||
});
|
|
||||||
generator.reset();
|
|
||||||
scope.formModal2Header = data.event_display.replace(/^\u00a0*/g, '');
|
|
||||||
scope.event_data = JSON.stringify(data.event_data, null, '\t');
|
|
||||||
scope.formModal2ActionLabel = 'OK';
|
|
||||||
scope.formModal2CancelShow = false;
|
|
||||||
scope.formModal2Info = false;
|
|
||||||
scope.formModalInfo = 'View JSON';
|
|
||||||
scope.formModal2Action = function () {
|
|
||||||
$('#form-modal2').modal("hide");
|
|
||||||
};
|
|
||||||
$('#form-modal2 .btn-success').removeClass('btn-success').addClass('btn-none');
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof data.event_data.res === 'string') {
|
|
||||||
scope.traceback = data.event_data.res;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (fld in form.fields) {
|
|
||||||
switch (fld) {
|
|
||||||
case 'status':
|
|
||||||
if (data.failed) {
|
|
||||||
scope.status = 'error';
|
|
||||||
} else if (data.changed) {
|
|
||||||
scope.status = 'changed';
|
|
||||||
} else {
|
|
||||||
scope.status = 'success';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'created':
|
|
||||||
cDate = new Date(data.created);
|
|
||||||
scope.created = FormatDate(cDate);
|
|
||||||
break;
|
|
||||||
case 'host':
|
|
||||||
if (data.summary_fields && data.summary_fields.host) {
|
|
||||||
scope.host = data.summary_fields.host.name;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'id':
|
|
||||||
case 'task':
|
|
||||||
case 'play':
|
|
||||||
scope[fld] = data[fld];
|
|
||||||
break;
|
|
||||||
case 'start':
|
|
||||||
case 'end':
|
|
||||||
if (data.event_data && data.event_data.res && !Empty(data.event_data.res[fld])) {
|
|
||||||
scope[fld] = data.event_data.res[fld];
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'results':
|
|
||||||
if (Array.isArray(data.event_data.res[fld]) && data.event_data.res[fld].length > 0) {
|
|
||||||
txt = '';
|
|
||||||
for (i = 0; i < data.event_data.res[fld].length; i++) {
|
|
||||||
txt += data.event_data.res[fld][i];
|
|
||||||
}
|
|
||||||
if (txt !== '') {
|
|
||||||
scope[fld] = txt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'msg':
|
|
||||||
case 'stdout':
|
|
||||||
case 'stderr':
|
|
||||||
case 'delta':
|
|
||||||
case 'rc':
|
|
||||||
if (data.event_data && data.event_data.res && data.event_data.res[fld] !== undefined) {
|
|
||||||
scope[fld] = data.event_data.res[fld];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'module_name':
|
|
||||||
case 'module_args':
|
|
||||||
if (data.event_data.res && data.event_data.res.invocation) {
|
|
||||||
scope[fld] = data.event_data.res.invocation[fld];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scope.$$phase) {
|
|
||||||
scope.$digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
$('#form-modal').modal("hide");
|
|
||||||
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to retrieve event: ' + event_id + '. GET status: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,406 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/* jshint loopfunc: true */
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Hosts
|
|
||||||
* @description Routines that handle host add/edit/delete on the Inventory detail page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import listGenerator from '../shared/list-generator/main';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'HostListDefinition',
|
|
||||||
listGenerator.name, 'HostsHelper',
|
|
||||||
'InventoryHelper', 'InventoryFormDefinition', 'SelectionHelper',
|
|
||||||
'HostGroupsFormDefinition', 'VariablesHelper', 'ModalDialog', 'StandardOutHelper',
|
|
||||||
'GroupListDefinition'
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('SetEnabledMsg', [ function() {
|
|
||||||
return function(host) {
|
|
||||||
if (host.has_inventory_sources) {
|
|
||||||
// Inventory sync managed, so not clickable
|
|
||||||
host.enabledToolTip = (host.enabled) ? 'Host is available' : 'Host is not available';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Clickable
|
|
||||||
host.enabledToolTip = (host.enabled) ? 'Host is available. Click to toggle.' : 'Host is not available. Click to toggle.';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('SetHostStatus', ['SetEnabledMsg', function(SetEnabledMsg) {
|
|
||||||
return function(host) {
|
|
||||||
// Set status related fields on a host object
|
|
||||||
host.activeFailuresLink = '/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + host.inventory +
|
|
||||||
'&host_name=' + encodeURI(host.name);
|
|
||||||
if (host.has_active_failures === true) {
|
|
||||||
host.badgeToolTip = 'Most recent job failed. Click to view jobs.';
|
|
||||||
host.active_failures = 'failed';
|
|
||||||
}
|
|
||||||
else if (host.has_active_failures === false && host.last_job === null) {
|
|
||||||
host.has_active_failures = 'none';
|
|
||||||
host.badgeToolTip = "No job data available.";
|
|
||||||
host.active_failures = 'n/a';
|
|
||||||
}
|
|
||||||
else if (host.has_active_failures === false && host.last_job !== null) {
|
|
||||||
host.badgeToolTip = "Most recent job successful. Click to view jobs.";
|
|
||||||
host.active_failures = 'success';
|
|
||||||
}
|
|
||||||
|
|
||||||
host.enabled_flag = host.enabled;
|
|
||||||
SetEnabledMsg(host);
|
|
||||||
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('SetStatus', ['$filter', 'SetEnabledMsg', 'Empty', function($filter, SetEnabledMsg, Empty) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
host = params.host,
|
|
||||||
i, html, title;
|
|
||||||
|
|
||||||
function ellipsis(a) {
|
|
||||||
if (a.length > 25) {
|
|
||||||
return a.substr(0,25) + '...';
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
function noRecentJobs() {
|
|
||||||
title = 'No job data';
|
|
||||||
html = "<p>No recent job data available for this host.</p>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMsg(host) {
|
|
||||||
var j, job, jobs;
|
|
||||||
|
|
||||||
if (host.has_active_failures === true || (host.has_active_failures === false && host.last_job !== null)) {
|
|
||||||
if (host.has_active_failures === true) {
|
|
||||||
host.badgeToolTip = 'Most recent job failed. Click to view jobs.';
|
|
||||||
host.active_failures = 'error';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
host.badgeToolTip = "Most recent job successful. Click to view jobs.";
|
|
||||||
host.active_failures = 'successful';
|
|
||||||
}
|
|
||||||
if (host.summary_fields.recent_jobs.length > 0) {
|
|
||||||
// build html table of job status info
|
|
||||||
jobs = host.summary_fields.recent_jobs.sort(
|
|
||||||
function(a,b) {
|
|
||||||
// reverse numerical order
|
|
||||||
return -1 * (a - b);
|
|
||||||
});
|
|
||||||
title = "Recent Jobs";
|
|
||||||
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
|
||||||
html += "<thead>\n";
|
|
||||||
html += "<tr>\n";
|
|
||||||
html += "<th>Status</th>\n";
|
|
||||||
html += "<th>Finished</th>\n";
|
|
||||||
html += "<th>Name</th>\n";
|
|
||||||
html += "</tr>\n";
|
|
||||||
html += "</thead>\n";
|
|
||||||
html += "<tbody>\n";
|
|
||||||
for (j=0; j < jobs.length; j++) {
|
|
||||||
job = jobs[j];
|
|
||||||
html += "<tr>\n";
|
|
||||||
|
|
||||||
// SmartStatus-tooltips are named --success whereas icon-job uses successful
|
|
||||||
var iconStatus = (job.status === 'successful') ? 'success' : 'failed';
|
|
||||||
|
|
||||||
html += "<td><a href=\"#/jobs/" + job.id + "\"><i class=\"fa DashboardList-status SmartStatus-tooltip--" + iconStatus + " icon-job-" +
|
|
||||||
job.status + "\"></i></a></td>\n";
|
|
||||||
|
|
||||||
html += "<td>" + ($filter('longDate')(job.finished)).replace(/ /,'<br />') + "</td>\n";
|
|
||||||
|
|
||||||
html += "<td class=\"break\"><a href=\"#/jobs/" + job.id + "\" " +
|
|
||||||
"aw-tool-tip=\"" + job.status.charAt(0).toUpperCase() + job.status.slice(1) +
|
|
||||||
". Click for details\" data-placement=\"top\">" + ellipsis(job.name) + "</a></td>\n";
|
|
||||||
|
|
||||||
html += "</tr>\n";
|
|
||||||
}
|
|
||||||
html += "</tbody>\n";
|
|
||||||
html += "</table>\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
noRecentJobs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (host.has_active_failures === false && host.last_job === null) {
|
|
||||||
host.badgeToolTip = "No job data available.";
|
|
||||||
host.active_failures = 'none';
|
|
||||||
noRecentJobs();
|
|
||||||
}
|
|
||||||
host.job_status_html = html;
|
|
||||||
host.job_status_title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Empty(host)) {
|
|
||||||
// update single host
|
|
||||||
setMsg(host);
|
|
||||||
SetEnabledMsg(host);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// update all hosts
|
|
||||||
for (i=0; i < scope.hosts.length; i++) {
|
|
||||||
setMsg(scope.hosts[i]);
|
|
||||||
SetEnabledMsg(scope.hosts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('HostsReload', [ '$stateParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'Wait',
|
|
||||||
'SetHostStatus', 'SetStatus', 'ApplyEllipsis',
|
|
||||||
function($stateParams, Empty, InventoryHosts, GetBasePath, Wait, SetHostStatus, SetStatus,
|
|
||||||
ApplyEllipsis) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
parent_scope = params.parent_scope;
|
|
||||||
|
|
||||||
if (scope.removeHostsReloadPostRefresh) {
|
|
||||||
scope.removeHostsReloadPostRefresh();
|
|
||||||
}
|
|
||||||
scope.removeHostsReloadPostRefresh = scope.$on('PostRefresh', function(e, set) {
|
|
||||||
if (set === 'hosts') {
|
|
||||||
for (var i=0; i < scope.hosts.length; i++) {
|
|
||||||
//Set tooltip for host enabled flag
|
|
||||||
scope.hosts[i].enabled_flag = scope.hosts[i].enabled;
|
|
||||||
}
|
|
||||||
SetStatus({ scope: scope });
|
|
||||||
setTimeout(function() { ApplyEllipsis('#hosts_table .host-name a'); }, 2500);
|
|
||||||
Wait('stop');
|
|
||||||
if (parent_scope) {
|
|
||||||
parent_scope.$emit('HostReloadComplete');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList',
|
|
||||||
function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
var host_id = params.host_id,
|
|
||||||
group_scope = params.group_scope,
|
|
||||||
parent_scope = params.host_scope,
|
|
||||||
parent_group = group_scope.selected_group_id,
|
|
||||||
scope = parent_scope.$new(),
|
|
||||||
buttonSet, url, host;
|
|
||||||
|
|
||||||
buttonSet = [{
|
|
||||||
label: "Cancel",
|
|
||||||
onClick: function() {
|
|
||||||
scope.cancel();
|
|
||||||
},
|
|
||||||
icon: "fa-times",
|
|
||||||
"class": "btn btn-default",
|
|
||||||
"id": "host-copy-cancel-button"
|
|
||||||
},{
|
|
||||||
label: "OK",
|
|
||||||
onClick: function() {
|
|
||||||
scope.performCopy();
|
|
||||||
},
|
|
||||||
icon: "fa-check",
|
|
||||||
"class": "btn btn-primary",
|
|
||||||
"id": "host-copy-ok-button"
|
|
||||||
}];
|
|
||||||
|
|
||||||
if (scope.removeHostCopyPostRefresh) {
|
|
||||||
scope.removeHostCopyPostRefresh();
|
|
||||||
}
|
|
||||||
scope.removeHostCopyPostRefresh = scope.$on('PostRefresh', function() {
|
|
||||||
scope.copy_groups.forEach(function(row, i) {
|
|
||||||
scope.copy_groups[i].checked = '0';
|
|
||||||
});
|
|
||||||
Wait('stop');
|
|
||||||
$('#host-copy-dialog').dialog('open');
|
|
||||||
$('#host-copy-ok-button').attr('disabled','disabled');
|
|
||||||
|
|
||||||
// prevent backspace from navigation when not in input or textarea field
|
|
||||||
$(document).on("keydown", function (e) {
|
|
||||||
if (e.which === 8 && !$(e.target).is('input[type="text"], textarea')) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeHostCopyDialogReady) {
|
|
||||||
scope.removeHostCopyDialogReady();
|
|
||||||
}
|
|
||||||
scope.removeCopyDialogReady = scope.$on('HostCopyDialogReady', function() {
|
|
||||||
GenerateList.inject(GroupList, {
|
|
||||||
mode: 'lookup',
|
|
||||||
id: 'copy-host-select-container',
|
|
||||||
scope: scope
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeShowDialog) {
|
|
||||||
scope.removeShowDialog();
|
|
||||||
}
|
|
||||||
scope.removeShowDialog = scope.$on('ShowDialog', function() {
|
|
||||||
var d;
|
|
||||||
scope.name = host.name;
|
|
||||||
scope.copy_choice = "copy";
|
|
||||||
d = angular.element(document.getElementById('host-copy-dialog'));
|
|
||||||
$compile(d)(scope);
|
|
||||||
CreateDialog({
|
|
||||||
id: 'host-copy-dialog',
|
|
||||||
scope: scope,
|
|
||||||
buttons: buttonSet,
|
|
||||||
width: 650,
|
|
||||||
height: 650,
|
|
||||||
minWidth: 600,
|
|
||||||
title: 'Copy or Move Host',
|
|
||||||
callback: 'HostCopyDialogReady',
|
|
||||||
onClose: function() {
|
|
||||||
scope.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
url = GetBasePath('hosts') + host_id + '/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function(data) {
|
|
||||||
host = data;
|
|
||||||
scope.$emit('ShowDialog');
|
|
||||||
})
|
|
||||||
.error(function(data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status });
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
scope.cancel = function() {
|
|
||||||
$(document).off("keydown");
|
|
||||||
try {
|
|
||||||
$('#host-copy-dialog').dialog('close');
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
scope.$destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope['toggle_' + GroupList.iterator] = function (id) {
|
|
||||||
var count = 0,
|
|
||||||
list = GroupList;
|
|
||||||
scope[list.name].forEach( function(row, i) {
|
|
||||||
if (row.id === id) {
|
|
||||||
if (row.checked) {
|
|
||||||
scope[list.name][i].success_class = 'success';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope[list.name][i].success_class = '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope[list.name][i].checked = 0;
|
|
||||||
scope[list.name][i].success_class = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Check if any rows are checked
|
|
||||||
scope[list.name].forEach(function(row) {
|
|
||||||
if (row.checked) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (count === 0) {
|
|
||||||
$('#host-copy-ok-button').attr('disabled','disabled');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#host-copy-ok-button').removeAttr('disabled');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.performCopy = function() {
|
|
||||||
var list = GroupList,
|
|
||||||
target,
|
|
||||||
url;
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
if (scope.use_root_group) {
|
|
||||||
target = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope[list.name].every(function(row) {
|
|
||||||
if (row.checked === 1) {
|
|
||||||
target = row;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.copy_choice === 'move') {
|
|
||||||
// Respond to move
|
|
||||||
|
|
||||||
// disassociate the host from the original parent
|
|
||||||
if (scope.removeHostRemove) {
|
|
||||||
scope.removeHostRemove();
|
|
||||||
}
|
|
||||||
scope.removeHostRemove = scope.$on('RemoveHost', function () {
|
|
||||||
if (parent_group > 0) {
|
|
||||||
// Only remove a host from a parent when the parent is a group and not the inventory root
|
|
||||||
url = GetBasePath('groups') + parent_group + '/hosts/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.post({ id: host.id, disassociate: 1 })
|
|
||||||
.success(function () {
|
|
||||||
scope.cancel();
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to remove ' + host.name + ' from group ' + parent_group + '. POST returned: ' + status });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
scope.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// add the new host to the target
|
|
||||||
url = GetBasePath('groups') + target.id + '/hosts/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.post(host)
|
|
||||||
.success(function () {
|
|
||||||
scope.$emit('RemoveHost');
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Respond to copy by adding the new host to the target
|
|
||||||
url = GetBasePath('groups') + target.id + '/hosts/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.post(host)
|
|
||||||
.success(function () {
|
|
||||||
scope.cancel();
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2016 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
|
|
||||||
'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog', 'FormGenerator', 'JobVarsPromptFormDefinition'])
|
|
||||||
|
|
||||||
.factory('CreateLaunchDialog', ['$compile', 'CreateDialog', 'Wait', 'ParseTypeChange',
|
|
||||||
function($compile, CreateDialog, Wait, ParseTypeChange) {
|
|
||||||
return function(params) {
|
|
||||||
var buttons,
|
|
||||||
scope = params.scope,
|
|
||||||
html = params.html,
|
|
||||||
// job_launch_data = {},
|
|
||||||
callback = params.callback || 'PlaybookLaunchFinished',
|
|
||||||
// url = params.url,
|
|
||||||
e;
|
|
||||||
|
|
||||||
// html+='<br>job_launch_form.$valid = {{job_launch_form.$valid}}<br>';
|
|
||||||
html+='</form>';
|
|
||||||
$('#password-modal').empty().html(html);
|
|
||||||
$('#password-modal').find('#job_extra_vars').before(scope.helpContainer);
|
|
||||||
e = angular.element(document.getElementById('password-modal'));
|
|
||||||
$compile(e)(scope);
|
|
||||||
|
|
||||||
if(scope.prompt_for_vars===true){
|
|
||||||
ParseTypeChange({ scope: scope, field_id: 'job_extra_vars' , variable: "extra_vars"});
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons = [{
|
|
||||||
label: "Cancel",
|
|
||||||
onClick: function() {
|
|
||||||
$('#password-modal').dialog('close');
|
|
||||||
// scope.$emit('CancelJob');
|
|
||||||
// scope.$destroy();
|
|
||||||
},
|
|
||||||
icon: "fa-times",
|
|
||||||
"class": "btn btn-default",
|
|
||||||
"id": "password-cancel-button"
|
|
||||||
},{
|
|
||||||
label: "Launch",
|
|
||||||
onClick: function() {
|
|
||||||
scope.$emit(callback);
|
|
||||||
$('#password-modal').dialog('close');
|
|
||||||
},
|
|
||||||
icon: "fa-check",
|
|
||||||
"class": "btn btn-primary",
|
|
||||||
"id": "password-accept-button"
|
|
||||||
}];
|
|
||||||
|
|
||||||
CreateDialog({
|
|
||||||
id: 'password-modal',
|
|
||||||
scope: scope,
|
|
||||||
buttons: buttons,
|
|
||||||
width: 620,
|
|
||||||
height: 700, //(scope.passwords.length > 1) ? 700 : 500,
|
|
||||||
minWidth: 500,
|
|
||||||
title: 'Launch Configuration',
|
|
||||||
callback: 'DialogReady',
|
|
||||||
onOpen: function(){
|
|
||||||
Wait('stop');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeDialogReady) {
|
|
||||||
scope.removeDialogReady();
|
|
||||||
}
|
|
||||||
scope.removeDialogReady = scope.$on('DialogReady', function() {
|
|
||||||
$('#password-modal').dialog('open');
|
|
||||||
$('#password-accept-button').attr('ng-disabled', 'job_launch_form.$invalid' );
|
|
||||||
e = angular.element(document.getElementById('password-accept-button'));
|
|
||||||
$compile(e)(scope);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('PromptForPasswords', ['CredentialForm',
|
|
||||||
function(CredentialForm) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
callback = params.callback || 'PasswordsAccepted',
|
|
||||||
url = params.url,
|
|
||||||
form = CredentialForm,
|
|
||||||
fld, field,
|
|
||||||
html=params.html || "";
|
|
||||||
|
|
||||||
scope.passwords = params.passwords;
|
|
||||||
|
|
||||||
html += "<div class=\"alert alert-info\">Launching this job requires the passwords listed below. Enter and confirm each password before continuing.</div>\n";
|
|
||||||
|
|
||||||
scope.passwords.forEach(function(password) {
|
|
||||||
// Prompt for password
|
|
||||||
field = form.fields[password];
|
|
||||||
fld = password;
|
|
||||||
scope[fld] = '';
|
|
||||||
html += "<div class=\"form-group prepend-asterisk\">\n";
|
|
||||||
html += "<label for=\"" + fld + "\">" + field.label + "</label>\n";
|
|
||||||
html += "<input type=\"password\" ";
|
|
||||||
html += "ng-model=\"" + fld + '" ';
|
|
||||||
html += 'name="' + fld + '" ';
|
|
||||||
html += "class=\"password-field form-control input-sm\" ";
|
|
||||||
html += (field.associated) ? "ng-change=\"clearPWConfirm('" + field.associated + "')\" " : "";
|
|
||||||
html += "required ";
|
|
||||||
html += " >";
|
|
||||||
// Add error messages
|
|
||||||
html += "<div class=\"error\" ng-show=\"job_launch_form." + fld + ".$dirty && " +
|
|
||||||
"job_launch_form." + fld + ".$error.required\">Please enter a password.</div>\n";
|
|
||||||
html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
|
|
||||||
html += "</div>\n";
|
|
||||||
|
|
||||||
// Add the related confirm field
|
|
||||||
if (field.associated) {
|
|
||||||
fld = field.associated;
|
|
||||||
field = form.fields[field.associated];
|
|
||||||
scope[fld] = '';
|
|
||||||
html += "<div class=\"form-group prepend-asterisk\">\n";
|
|
||||||
html += "<label for=\"" + fld + "\"> " + field.label + "</label>\n";
|
|
||||||
html += "<input type=\"password\" ";
|
|
||||||
html += "ng-model=\"" + fld + '" ';
|
|
||||||
html += 'name="' + fld + '" ';
|
|
||||||
html += "class=\"form-control input-sm\" ";
|
|
||||||
html += "ng-change=\"checkStatus()\" ";
|
|
||||||
html += "required ";
|
|
||||||
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
|
|
||||||
html += "/>";
|
|
||||||
// Add error messages
|
|
||||||
html += "<div class=\"error\" ng-show=\"job_launch_form." + fld + ".$dirty && " +
|
|
||||||
"job_launch_form." + fld + ".$error.required\">Please confirm the password.</span>\n";
|
|
||||||
html += (field.awPassMatch) ? "<span class=\"error\" ng-show=\"job_launch_form." + fld +
|
|
||||||
".$error.awpassmatch\">This value does not match the password you entered previously. Please confirm that password.</div>\n" : "";
|
|
||||||
html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
|
|
||||||
html += "</div>\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.$emit(callback, html, url);
|
|
||||||
|
|
||||||
// Password change
|
|
||||||
scope.clearPWConfirm = function (fld) {
|
|
||||||
// If password value changes, make sure password_confirm must be re-entered
|
|
||||||
scope[fld] = '';
|
|
||||||
scope.job_launch_form[fld].$setValidity('awpassmatch', false);
|
|
||||||
scope.checkStatus();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.checkStatus = function() {
|
|
||||||
if (!scope.job_launch_form.$invalid) {
|
|
||||||
$('#password-accept-button').removeAttr('disabled');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#password-accept-button').attr({ "disabled": "disabled" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('CheckPasswords', ['Rest', 'GetBasePath', 'ProcessErrors', 'Empty',
|
|
||||||
function(Rest, GetBasePath, ProcessErrors, Empty) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
callback = params.callback,
|
|
||||||
credential = params.credential;
|
|
||||||
|
|
||||||
var passwords = [];
|
|
||||||
if (!Empty(credential)) {
|
|
||||||
Rest.setUrl(GetBasePath('credentials')+credential);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
if(data.kind === "ssh"){
|
|
||||||
if(data.password === "ASK" ){
|
|
||||||
passwords.push("ssh_password");
|
|
||||||
}
|
|
||||||
if(data.ssh_key_unlock === "ASK"){
|
|
||||||
passwords.push("ssh_key_unlock");
|
|
||||||
}
|
|
||||||
if(data.become_password === "ASK"){
|
|
||||||
passwords.push("become_password");
|
|
||||||
}
|
|
||||||
if(data.vault_password === "ASK"){
|
|
||||||
passwords.push("vault_password");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.$emit(callback, passwords);
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to get job template details. GET returned status: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
// Submit SCM Update request
|
|
||||||
.factory('ProjectUpdate', ['PromptForPasswords', 'LaunchJob', 'Rest', '$location', 'GetBasePath', 'ProcessErrors', 'Alert', 'Wait',
|
|
||||||
function (PromptForPasswords, LaunchJob, Rest, $location, GetBasePath, ProcessErrors, Alert, Wait) {
|
|
||||||
return function (params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
project_id = params.project_id,
|
|
||||||
url = GetBasePath('projects') + project_id + '/update/',
|
|
||||||
project;
|
|
||||||
|
|
||||||
if (scope.removeUpdateSubmitted) {
|
|
||||||
scope.removeUpdateSubmitted();
|
|
||||||
}
|
|
||||||
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function() {
|
|
||||||
// Refresh the project list after update request submitted
|
|
||||||
Wait('stop');
|
|
||||||
if (/\d$/.test($location.path())) {
|
|
||||||
//Request submitted from projects/N page. Navigate back to the list so user can see status
|
|
||||||
$location.path('/projects');
|
|
||||||
}
|
|
||||||
if (scope.socketStatus === 'error') {
|
|
||||||
Alert('Update Started', '<div>The request to start the SCM update process was submitted. ' +
|
|
||||||
'To monitor the update status, refresh the page by clicking the <i class="fa fa-refresh"></i> button.</div>', 'alert-info', null, null, null, null, true);
|
|
||||||
if (scope.refresh) {
|
|
||||||
scope.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removePromptForPasswords) {
|
|
||||||
scope.removePromptForPasswords();
|
|
||||||
}
|
|
||||||
scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
|
|
||||||
PromptForPasswords({ scope: scope, passwords: project.passwords_needed_to_update, callback: 'StartTheUpdate' });
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeStartTheUpdate) {
|
|
||||||
scope.removeStartTheUpdate();
|
|
||||||
}
|
|
||||||
scope.removeStartTheUpdate = scope.$on('StartTheUpdate', function(e, passwords) {
|
|
||||||
LaunchJob({ scope: scope, url: url, passwords: passwords, callback: 'UpdateSubmitted' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check to see if we have permission to perform the update and if any passwords are needed
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
project = data;
|
|
||||||
if (project.can_update) {
|
|
||||||
if (project.passwords_needed_to_updated) {
|
|
||||||
Wait('stop');
|
|
||||||
scope.$emit('PromptForPasswords');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.$emit('StartTheUpdate', {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Alert('Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
|
|
||||||
'alert-danger');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to lookup project ' + url + ' GET returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// Submit Inventory Update request
|
|
||||||
.factory('InventoryUpdate', ['PromptForPasswords', 'LaunchJob', 'Rest', 'GetBasePath', 'ProcessErrors', 'Alert', 'Wait',
|
|
||||||
function (PromptForPasswords, LaunchJob, Rest, GetBasePath, ProcessErrors, Alert, Wait) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
url = params.url,
|
|
||||||
inventory_source;
|
|
||||||
|
|
||||||
if (scope.removeUpdateSubmitted) {
|
|
||||||
scope.removeUpdateSubmitted();
|
|
||||||
}
|
|
||||||
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function () {
|
|
||||||
Wait('stop');
|
|
||||||
if (scope.socketStatus === 'error') {
|
|
||||||
Alert('Sync Started', '<div>The request to start the inventory sync process was submitted. ' +
|
|
||||||
'To monitor the status refresh the page by clicking the <i class="fa fa-refresh"></i> button.</div>', 'alert-info', null, null, null, null, true);
|
|
||||||
if (scope.refreshGroups) {
|
|
||||||
// inventory detail page
|
|
||||||
scope.refreshGroups();
|
|
||||||
}
|
|
||||||
else if (scope.refresh) {
|
|
||||||
scope.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removePromptForPasswords) {
|
|
||||||
scope.removePromptForPasswords();
|
|
||||||
}
|
|
||||||
scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
|
|
||||||
PromptForPasswords({ scope: scope, passwords: inventory_source.passwords_needed_to_update, callback: 'StartTheUpdate' });
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeStartTheUpdate) {
|
|
||||||
scope.removeStartTheUpdate();
|
|
||||||
}
|
|
||||||
scope.removeStartTheUpdate = scope.$on('StartTheUpdate', function(e, passwords) {
|
|
||||||
LaunchJob({ scope: scope, url: url, passwords: passwords, callback: 'UpdateSubmitted' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check to see if we have permission to perform the update and if any passwords are needed
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
inventory_source = data;
|
|
||||||
if (data.can_update) {
|
|
||||||
if (data.passwords_needed_to_update) {
|
|
||||||
Wait('stop');
|
|
||||||
scope.$emit('PromptForPasswords');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.$emit('StartTheUpdate', {});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Wait('stop');
|
|
||||||
Alert('Permission Denied', 'You do not have access to run the inventory sync. Please contact your system administrator.',
|
|
||||||
'alert-danger');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to get inventory source ' + url + ' GET returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:JobTemplatesHelper
|
|
||||||
* @description Routines shared by job related controllers
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('JobTemplatesHelper', ['Utilities'])
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add bits to $scope for handling callback url help
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
.factory('CallbackHelpInit', ['$location', 'GetBasePath', 'Rest', 'JobTemplateForm', 'GenerateForm', '$stateParams', 'ProcessErrors', 'ParseTypeChange',
|
|
||||||
'ParseVariableString', 'Empty', 'InventoryList', 'CredentialList','ProjectList', 'Wait',
|
|
||||||
function($location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors, ParseTypeChange,
|
|
||||||
ParseVariableString, Empty, InventoryList, CredentialList, ProjectList, Wait) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
defaultUrl = GetBasePath('job_templates'),
|
|
||||||
// generator = GenerateForm,
|
|
||||||
form = JobTemplateForm(),
|
|
||||||
// loadingFinishedCount = 0,
|
|
||||||
// base = $location.path().replace(/^\//, '').split('/')[0],
|
|
||||||
master = {},
|
|
||||||
id = $stateParams.job_template_id;
|
|
||||||
// checkSCMStatus, getPlaybooks, callback,
|
|
||||||
// choicesCount = 0;
|
|
||||||
|
|
||||||
CredentialList = _.cloneDeep(CredentialList);
|
|
||||||
|
|
||||||
// The form uses awPopOverWatch directive to 'watch' scope.callback_help for changes. Each time the
|
|
||||||
// popover is activated, a function checks the value of scope.callback_help before constructing the content.
|
|
||||||
scope.setCallbackHelp = function() {
|
|
||||||
scope.callback_help = "<p>With a provisioning callback URL and a host config key a host can contact Tower and request a configuration update using this job " +
|
|
||||||
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
|
|
||||||
"<pre>curl --data \"host_config_key=" + scope.example_config_key + "\" " +
|
|
||||||
scope.callback_server_path + GetBasePath('job_templates') + scope.example_template_id + "/callback/</pre>\n" +
|
|
||||||
"<p>Note the requesting host must be defined in the inventory associated with the job template. If Tower fails to " +
|
|
||||||
"locate the host, the request will be denied.</p>" +
|
|
||||||
"<p>Successful requests create an entry on the Jobs page, where results and history can be viewed.</p>";
|
|
||||||
};
|
|
||||||
|
|
||||||
// The md5 helper emits NewMD5Generated whenever a new key is available
|
|
||||||
if (scope.removeNewMD5Generated) {
|
|
||||||
scope.removeNewMD5Generated();
|
|
||||||
}
|
|
||||||
scope.removeNewMD5Generated = scope.$on('NewMD5Generated', function() {
|
|
||||||
scope.configKeyChange();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fired when user enters a key value
|
|
||||||
scope.configKeyChange = function() {
|
|
||||||
scope.example_config_key = scope.host_config_key;
|
|
||||||
scope.setCallbackHelp();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set initial values and construct help text
|
|
||||||
scope.callback_server_path = $location.protocol() + '://' + $location.host() + (($location.port()) ? ':' + $location.port() : '');
|
|
||||||
scope.example_config_key = '5a8ec154832b780b9bdef1061764ae5a';
|
|
||||||
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
|
|
||||||
Rest.setUrl(defaultUrl + id);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
scope.job_template_obj = data;
|
|
||||||
scope.name = data.name;
|
|
||||||
var fld, i;
|
|
||||||
for (fld in form.fields) {
|
|
||||||
if (fld !== 'variables' && fld !== 'survey' && data[fld] !== null && data[fld] !== undefined) {
|
|
||||||
if (form.fields[fld].type === 'select') {
|
|
||||||
if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) {
|
|
||||||
for (i = 0; i < scope[fld + '_options'].length; i++) {
|
|
||||||
if (data[fld] === scope[fld + '_options'][i].value) {
|
|
||||||
scope[fld] = scope[fld + '_options'][i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope[fld] = data[fld];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope[fld] = data[fld];
|
|
||||||
if(!Empty(data.summary_fields.survey)) {
|
|
||||||
scope.survey_exists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
master[fld] = scope[fld];
|
|
||||||
}
|
|
||||||
if (fld === 'variables') {
|
|
||||||
// Parse extra_vars, converting to YAML.
|
|
||||||
scope.variables = ParseVariableString(data.extra_vars);
|
|
||||||
master.variables = scope.variables;
|
|
||||||
}
|
|
||||||
if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
|
|
||||||
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
|
||||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
|
||||||
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
|
||||||
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
|
|
||||||
}
|
|
||||||
if (form.fields[fld].type === 'checkbox_group') {
|
|
||||||
for(var j=0; j<form.fields[fld].fields.length; j++) {
|
|
||||||
scope[form.fields[fld].fields[j].name] = data[form.fields[fld].fields[j].name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Wait('stop');
|
|
||||||
scope.url = data.url;
|
|
||||||
|
|
||||||
scope.survey_enabled = data.survey_enabled;
|
|
||||||
|
|
||||||
scope.ask_variables_on_launch = (data.ask_variables_on_launch) ? true : false;
|
|
||||||
master.ask_variables_on_launch = scope.ask_variables_on_launch;
|
|
||||||
|
|
||||||
scope.ask_limit_on_launch = (data.ask_limit_on_launch) ? true : false;
|
|
||||||
master.ask_limit_on_launch = scope.ask_limit_on_launch;
|
|
||||||
|
|
||||||
scope.ask_tags_on_launch = (data.ask_tags_on_launch) ? true : false;
|
|
||||||
master.ask_tags_on_launch = scope.ask_tags_on_launch;
|
|
||||||
|
|
||||||
scope.ask_skip_tags_on_launch = (data.ask_skip_tags_on_launch) ? true : false;
|
|
||||||
master.ask_skip_tags_on_launch = scope.ask_skip_tags_on_launch;
|
|
||||||
|
|
||||||
scope.ask_job_type_on_launch = (data.ask_job_type_on_launch) ? true : false;
|
|
||||||
master.ask_job_type_on_launch = scope.ask_job_type_on_launch;
|
|
||||||
|
|
||||||
scope.ask_inventory_on_launch = (data.ask_inventory_on_launch) ? true : false;
|
|
||||||
master.ask_inventory_on_launch = scope.ask_inventory_on_launch;
|
|
||||||
|
|
||||||
scope.ask_credential_on_launch = (data.ask_credential_on_launch) ? true : false;
|
|
||||||
master.ask_credential_on_launch = scope.ask_credential_on_launch;
|
|
||||||
|
|
||||||
if (data.host_config_key) {
|
|
||||||
scope.example_config_key = data.host_config_key;
|
|
||||||
}
|
|
||||||
scope.example_template_id = id;
|
|
||||||
scope.setCallbackHelp();
|
|
||||||
|
|
||||||
scope.callback_url = scope.callback_server_path + ((data.related.callback) ? data.related.callback :
|
|
||||||
GetBasePath('job_templates') + id + '/callback/');
|
|
||||||
master.callback_url = scope.callback_url;
|
|
||||||
|
|
||||||
scope.can_edit = data.summary_fields.user_capabilities.edit;
|
|
||||||
|
|
||||||
if (scope.job_type.value === "scan" && (!scope.project || scope.project === "") && (!scope.playbook || scope.playbook === "")) {
|
|
||||||
scope.resetProjectToDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.$emit('jobTemplateLoaded', data.related.cloud_credential, master);
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, form, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: 'Failed to retrieve job template: ' + $stateParams.id + '. GET status: ' + status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,308 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Jobs
|
|
||||||
* @description routines shared by job related controllers
|
|
||||||
*/
|
|
||||||
|
|
||||||
import listGenerator from '../shared/list-generator/main';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper', 'GeneratorHelpers',
|
|
||||||
'JobSubmissionHelper', 'StandardOutHelper', 'AdhocHelper', listGenerator.name])
|
|
||||||
|
|
||||||
.factory('RelaunchJob', ['RelaunchInventory', 'RelaunchPlaybook', 'RelaunchSCM', 'RelaunchAdhoc',
|
|
||||||
function(RelaunchInventory, RelaunchPlaybook, RelaunchSCM, RelaunchAdhoc) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
type = params.type,
|
|
||||||
name = params.name;
|
|
||||||
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' || type === 'workflow_job') {
|
|
||||||
RelaunchPlaybook({ scope: scope, id: id, name: name, job_type: type });
|
|
||||||
}
|
|
||||||
else if (type === 'project_update') {
|
|
||||||
RelaunchSCM({ scope: scope, id: id });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('JobStatusToolTip', [
|
|
||||||
function () {
|
|
||||||
return function (status) {
|
|
||||||
var toolTip;
|
|
||||||
switch (status) {
|
|
||||||
case 'successful':
|
|
||||||
case 'success':
|
|
||||||
toolTip = 'There were no failed tasks.';
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
toolTip = 'Some tasks encountered errors.';
|
|
||||||
break;
|
|
||||||
case 'canceled':
|
|
||||||
toolTip = 'Stopped by user request.';
|
|
||||||
break;
|
|
||||||
case 'new':
|
|
||||||
toolTip = 'In queue, waiting on task manager.';
|
|
||||||
break;
|
|
||||||
case 'waiting':
|
|
||||||
toolTip = 'SCM Update or Inventory Update is executing.';
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
toolTip = 'Not in queue, waiting on task manager.';
|
|
||||||
break;
|
|
||||||
case 'running':
|
|
||||||
toolTip = 'Playbook tasks executing.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return toolTip;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('JobsListUpdate', [function() {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
parent_scope = params.parent_scope,
|
|
||||||
list = params.list;
|
|
||||||
|
|
||||||
scope[list.name].forEach(function(item, item_idx) {
|
|
||||||
var fld, field,
|
|
||||||
itm = scope[list.name][item_idx];
|
|
||||||
|
|
||||||
//if (item.type === 'inventory_update') {
|
|
||||||
// itm.name = itm.name.replace(/^.*?:/,'').replace(/^: /,'');
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Set the item type label
|
|
||||||
if (list.fields.type) {
|
|
||||||
parent_scope.type_choices.forEach(function(choice) {
|
|
||||||
if (choice.value === item.type) {
|
|
||||||
itm.type_label = choice.label;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Set the job status label
|
|
||||||
parent_scope.status_choices.forEach(function(status) {
|
|
||||||
if (status.value === item.status) {
|
|
||||||
itm.status_label = status.label;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (list.name === 'completed_jobs' || list.name === 'running_jobs') {
|
|
||||||
itm.status_tip = itm.status_label + '. Click for details.';
|
|
||||||
}
|
|
||||||
else if (list.name === 'queued_jobs') {
|
|
||||||
itm.status_tip = 'Pending';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy summary_field values
|
|
||||||
for (field in list.fields) {
|
|
||||||
fld = list.fields[field];
|
|
||||||
if (fld.sourceModel) {
|
|
||||||
if (itm.summary_fields[fld.sourceModel]) {
|
|
||||||
itm[field] = itm.summary_fields[fld.sourceModel][fld.sourceField];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('DeleteJob', ['$state', 'Find', 'GetBasePath', 'Rest', 'Wait',
|
|
||||||
'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n',
|
|
||||||
function($state, Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert,
|
|
||||||
$filter, i18n){
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
job = params.job,
|
|
||||||
callback = params.callback,
|
|
||||||
action, jobs, url, action_label, hdr;
|
|
||||||
|
|
||||||
if (!job) {
|
|
||||||
if (scope.completed_jobs) {
|
|
||||||
jobs = scope.completed_jobs;
|
|
||||||
}
|
|
||||||
else if (scope.running_jobs) {
|
|
||||||
jobs = scope.running_jobs;
|
|
||||||
}
|
|
||||||
else if (scope.queued_jobs) {
|
|
||||||
jobs = scope.queued_jobs;
|
|
||||||
}
|
|
||||||
else if (scope.all_jobs) {
|
|
||||||
jobs = scope.all_jobs;
|
|
||||||
}
|
|
||||||
else if (scope.jobs) {
|
|
||||||
jobs = scope.jobs;
|
|
||||||
}
|
|
||||||
job = Find({list: jobs, key: 'id', val: id });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
|
||||||
url = job.related.cancel;
|
|
||||||
action_label = 'cancel';
|
|
||||||
hdr = i18n._('Cancel');
|
|
||||||
} else {
|
|
||||||
url = job.url;
|
|
||||||
action_label = 'delete';
|
|
||||||
hdr = i18n._('Delete');
|
|
||||||
}
|
|
||||||
|
|
||||||
action = function () {
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(url);
|
|
||||||
if (action_label === 'cancel') {
|
|
||||||
Rest.post()
|
|
||||||
.success(function () {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback, action_label);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$state.reload();
|
|
||||||
Wait('stop');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function(obj, status) {
|
|
||||||
Wait('stop');
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
if (status === 403) {
|
|
||||||
Alert('Error', obj.detail);
|
|
||||||
}
|
|
||||||
// Ignore the error. The job most likely already finished.
|
|
||||||
// ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
|
||||||
// ' failed. POST returned status: ' + status });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Rest.destroy()
|
|
||||||
.success(function () {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback, action_label);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$state.reload();
|
|
||||||
Wait('stop');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (obj, status) {
|
|
||||||
Wait('stop');
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
if (status === 403) {
|
|
||||||
Alert('Error', obj.detail);
|
|
||||||
}
|
|
||||||
// Ignore the error. The job most likely already finished.
|
|
||||||
//ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
|
||||||
// ' failed. DELETE returned status: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (scope.removeCancelNotAllowed) {
|
|
||||||
scope.removeCancelNotAllowed();
|
|
||||||
}
|
|
||||||
scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() {
|
|
||||||
Wait('stop');
|
|
||||||
Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scope.removeCancelJob) {
|
|
||||||
scope.removeCancelJob();
|
|
||||||
}
|
|
||||||
scope.removeCancelJob = scope.$on('CancelJob', function() {
|
|
||||||
var cancelBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Submit the request to cancel?") + "</div>";
|
|
||||||
var deleteBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Are you sure you want to delete the job below?") + "</div><div class=\"Prompt-bodyTarget\">#" + id + " " + $filter('sanitize')(job.name) + "</div>";
|
|
||||||
Prompt({
|
|
||||||
hdr: hdr,
|
|
||||||
body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody,
|
|
||||||
action: action,
|
|
||||||
actionText: (action_label === 'cancel' || job.status === 'new') ? "OK" : "DELETE"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (action_label === 'cancel') {
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function(data) {
|
|
||||||
if (data.can_cancel) {
|
|
||||||
scope.$emit('CancelJob');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.$emit('CancelNotAllowed');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function(data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
|
||||||
' failed. GET returned: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.$emit('CancelJob');
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('RelaunchInventory', ['Find', 'Wait', 'Rest', 'InventoryUpdate', 'ProcessErrors', 'GetBasePath',
|
|
||||||
function(Find, Wait, Rest, InventoryUpdate, ProcessErrors, GetBasePath) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
url = GetBasePath('inventory_sources') + id + '/';
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
InventoryUpdate({
|
|
||||||
scope: scope,
|
|
||||||
url: data.related.update,
|
|
||||||
group_name: data.summary_fields.group.name,
|
|
||||||
group_source: data.source,
|
|
||||||
tree_id: null,
|
|
||||||
group_id: data.group
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' +
|
|
||||||
url + ' GET returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('RelaunchPlaybook', ['InitiatePlaybookRun', function(InitiatePlaybookRun) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
job_type = params.job_type;
|
|
||||||
InitiatePlaybookRun({ scope: scope, id: id, relaunch: true, job_type: job_type });
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('RelaunchSCM', ['ProjectUpdate', function(ProjectUpdate) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
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 });
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Parse
|
|
||||||
* @description
|
|
||||||
* Show the CodeMirror variable editor and allow
|
|
||||||
* toggle between JSON and YAML
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'codemirror/lib/codemirror.js';
|
|
||||||
import 'codemirror/mode/javascript/javascript.js';
|
|
||||||
import 'codemirror/mode/yaml/yaml.js';
|
|
||||||
import 'codemirror/addon/lint/lint.js';
|
|
||||||
import 'angular-codemirror/lib/yaml-lint.js';
|
|
||||||
import 'codemirror/addon/edit/closebrackets.js';
|
|
||||||
import 'codemirror/addon/edit/matchbrackets.js';
|
|
||||||
import 'codemirror/addon/selection/active-line.js';
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule'])
|
|
||||||
.factory('ParseTypeChange', ['Alert', 'AngularCodeMirror',
|
|
||||||
function (Alert, AngularCodeMirror) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
field_id = params.field_id,
|
|
||||||
fld = (params.variable) ? params.variable : 'variables',
|
|
||||||
pfld = (params.parse_variable) ? params.parse_variable : 'parseType',
|
|
||||||
onReady = params.onReady,
|
|
||||||
onChange = params.onChange,
|
|
||||||
readOnly = params.readOnly;
|
|
||||||
|
|
||||||
function removeField(fld) {
|
|
||||||
//set our model to the last change in CodeMirror and then destroy CodeMirror
|
|
||||||
scope[fld] = scope[fld + 'codeMirror'].getValue();
|
|
||||||
$('#cm-' + fld + '-container > .CodeMirror').empty().remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createField(onChange, onReady, fld) {
|
|
||||||
//hide the textarea and show a fresh CodeMirror with the current mode (json or yaml)
|
|
||||||
|
|
||||||
scope[fld + 'codeMirror'] = AngularCodeMirror(readOnly);
|
|
||||||
scope[fld + 'codeMirror'].addModes(global.$AnsibleConfig.variable_edit_modes);
|
|
||||||
scope[fld + 'codeMirror'].showTextArea({
|
|
||||||
scope: scope,
|
|
||||||
model: fld,
|
|
||||||
element: field_id,
|
|
||||||
lineNumbers: true,
|
|
||||||
mode: scope[pfld],
|
|
||||||
onReady: onReady,
|
|
||||||
onChange: onChange
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide the textarea and show a CodeMirror editor
|
|
||||||
createField(onChange, onReady, fld);
|
|
||||||
|
|
||||||
|
|
||||||
// Toggle displayed variable string between JSON and YAML
|
|
||||||
scope.parseTypeChange = function(model, fld) {
|
|
||||||
var json_obj;
|
|
||||||
if (scope[model] === 'json') {
|
|
||||||
// converting yaml to json
|
|
||||||
try {
|
|
||||||
removeField(fld);
|
|
||||||
json_obj = jsyaml.load(scope[fld]);
|
|
||||||
if ($.isEmptyObject(json_obj)) {
|
|
||||||
scope[fld] = "{}";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope[fld] = JSON.stringify(json_obj, null, " ");
|
|
||||||
}
|
|
||||||
createField(onReady, onChange, fld);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Alert('Parse Error', 'Failed to parse valid YAML. ' + e.message);
|
|
||||||
setTimeout( function() { scope.$apply( function() { scope[model] = 'yaml'; createField(onReady, onChange, fld); }); }, 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// convert json to yaml
|
|
||||||
try {
|
|
||||||
removeField(fld);
|
|
||||||
json_obj = JSON.parse(scope[fld]);
|
|
||||||
if ($.isEmptyObject(json_obj)) {
|
|
||||||
scope[fld] = '---';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope[fld] = jsyaml.safeDump(json_obj);
|
|
||||||
}
|
|
||||||
createField(onReady, onChange, fld);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Alert('Parse Error', 'Failed to parse valid JSON. ' + e.message);
|
|
||||||
setTimeout( function() { scope.$apply( function() { scope[model] = 'json'; createField(onReady, onChange, fld); }); }, 500 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:ProjectPath
|
|
||||||
* @description
|
|
||||||
* Use GetProjectPath({ scope: <scope>, master: <master obj> }) to
|
|
||||||
* load scope.project_local_paths (array of options for drop-down) and
|
|
||||||
* scope.base_dir (readonly field).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ProjectPathHelper', ['RestServices', 'Utilities'])
|
|
||||||
.factory('GetProjectPath', ['Alert', 'Rest', 'GetBasePath', 'ProcessErrors',
|
|
||||||
function (Alert, Rest, GetBasePath, ProcessErrors) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
master = params.master;
|
|
||||||
|
|
||||||
function arraySort(data) {
|
|
||||||
//Sort nodes by name
|
|
||||||
var i, j, names = [],
|
|
||||||
newData = [];
|
|
||||||
for (i = 0; i < data.length; i++) {
|
|
||||||
names.push(data[i].value);
|
|
||||||
}
|
|
||||||
names.sort();
|
|
||||||
for (j = 0; j < names.length; j++) {
|
|
||||||
for (i = 0; i < data.length; i++) {
|
|
||||||
if (data[i].value === names[j]) {
|
|
||||||
newData.push(data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.showMissingPlaybooksAlert = false;
|
|
||||||
|
|
||||||
Rest.setUrl(GetBasePath('config'));
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
var opts = [], i;
|
|
||||||
if (data.project_local_paths) {
|
|
||||||
for (i = 0; i < data.project_local_paths.length; i++) {
|
|
||||||
opts.push({
|
|
||||||
label: data.project_local_paths[i],
|
|
||||||
value: data.project_local_paths[i]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (scope.local_path) {
|
|
||||||
// List only includes paths not assigned to projects, so add the
|
|
||||||
// path assigned to the current project.
|
|
||||||
opts.push({
|
|
||||||
label: scope.local_path,
|
|
||||||
value: scope.local_path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
scope.project_local_paths = arraySort(opts);
|
|
||||||
if (scope.local_path) {
|
|
||||||
for (i = 0; scope.project_local_paths.length; i++) {
|
|
||||||
if (scope.project_local_paths[i].value === scope.local_path) {
|
|
||||||
scope.local_path = scope.project_local_paths[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scope.base_dir = data.project_base_dir;
|
|
||||||
master.local_path = scope.local_path;
|
|
||||||
master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get
|
|
||||||
// wiped out on form reset.
|
|
||||||
if (opts.length === 0) {
|
|
||||||
// trigger display of alert block when scm_type == manual
|
|
||||||
scope.showMissingPlaybooksAlert = true;
|
|
||||||
}
|
|
||||||
scope.$emit('pathsReady');
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to access API config. GET status: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Projects
|
|
||||||
* @description
|
|
||||||
* Use GetProjectPath({ scope: <scope>, master: <master obj> }) to
|
|
||||||
* load scope.project_local_paths (array of options for drop-down) and
|
|
||||||
* scope.base_dir (readonly field).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('ProjectsHelper', ['RestServices', 'Utilities', 'ProjectStatusDefinition', 'ProjectFormDefinition'])
|
|
||||||
|
|
||||||
.factory('GetProjectIcon', [ function() {
|
|
||||||
return function(status) {
|
|
||||||
var result = '';
|
|
||||||
switch (status) {
|
|
||||||
case 'n/a':
|
|
||||||
case 'ok':
|
|
||||||
case 'never updated':
|
|
||||||
result = 'none';
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
case 'waiting':
|
|
||||||
case 'new':
|
|
||||||
result = 'none';
|
|
||||||
break;
|
|
||||||
case 'updating':
|
|
||||||
case 'running':
|
|
||||||
result = 'running';
|
|
||||||
break;
|
|
||||||
case 'successful':
|
|
||||||
result = 'success';
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
case 'missing':
|
|
||||||
case 'canceled':
|
|
||||||
result = 'error';
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('GetProjectToolTip', ['i18n', function(i18n) {
|
|
||||||
return function(status) {
|
|
||||||
var result = '';
|
|
||||||
switch (status) {
|
|
||||||
case 'n/a':
|
|
||||||
case 'ok':
|
|
||||||
case 'never updated':
|
|
||||||
result = i18n._('No SCM updates have run for this project');
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
case 'waiting':
|
|
||||||
case 'new':
|
|
||||||
result = i18n._('Queued. Click for details');
|
|
||||||
break;
|
|
||||||
case 'updating':
|
|
||||||
case 'running':
|
|
||||||
result = i18n._('Running! Click for details');
|
|
||||||
break;
|
|
||||||
case 'successful':
|
|
||||||
result = i18n._('Success! Click for details');
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
result = i18n._('Failed. Click for details');
|
|
||||||
break;
|
|
||||||
case 'missing':
|
|
||||||
result = i18n._('Missing. Click for details');
|
|
||||||
break;
|
|
||||||
case 'canceled':
|
|
||||||
result = i18n._('Canceled. Click for details');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,506 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Schedules
|
|
||||||
* @description
|
|
||||||
* Schedules Helper
|
|
||||||
*
|
|
||||||
* Display the scheduler widget in a dialog
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import listGenerator from '../shared/list-generator/main';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', listGenerator.name, 'ModalDialog',
|
|
||||||
'GeneratorHelpers'])
|
|
||||||
|
|
||||||
.factory('EditSchedule', ['SchedulerInit', '$rootScope', 'Wait', 'Rest',
|
|
||||||
'ProcessErrors', 'GetBasePath', 'SchedulePost', '$state',
|
|
||||||
function(SchedulerInit, $rootScope, Wait, Rest, ProcessErrors,
|
|
||||||
GetBasePath, SchedulePost, $state) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
callback = params.callback,
|
|
||||||
schedule, scheduler,
|
|
||||||
url = GetBasePath('schedules') + id + '/';
|
|
||||||
|
|
||||||
delete scope.isFactCleanup;
|
|
||||||
delete scope.cleanupJob;
|
|
||||||
|
|
||||||
function setGranularity(){
|
|
||||||
var a,b, prompt_for_days,
|
|
||||||
keep_unit,
|
|
||||||
granularity,
|
|
||||||
granularity_keep_unit;
|
|
||||||
|
|
||||||
if(scope.cleanupJob){
|
|
||||||
scope.schedulerPurgeDays = Number(schedule.extra_data.days);
|
|
||||||
// scope.scheduler_form.schedulerPurgeDays.$setViewValue( Number(schedule.extra_data.days));
|
|
||||||
}
|
|
||||||
else if(scope.isFactCleanup){
|
|
||||||
scope.keep_unit_choices = [{
|
|
||||||
"label" : "Days",
|
|
||||||
"value" : "d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Weeks",
|
|
||||||
"value" : "w"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label" : "Years",
|
|
||||||
"value" : "y"
|
|
||||||
}];
|
|
||||||
scope.granularity_keep_unit_choices = [{
|
|
||||||
"label" : "Days",
|
|
||||||
"value" : "d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Weeks",
|
|
||||||
"value" : "w"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label" : "Years",
|
|
||||||
"value" : "y"
|
|
||||||
}];
|
|
||||||
// the API returns something like 20w or 1y
|
|
||||||
a = schedule.extra_data.older_than; // "20y"
|
|
||||||
b = schedule.extra_data.granularity; // "1w"
|
|
||||||
prompt_for_days = Number(_.initial(a,1).join('')); // 20
|
|
||||||
keep_unit = _.last(a); // "y"
|
|
||||||
granularity = Number(_.initial(b,1).join('')); // 1
|
|
||||||
granularity_keep_unit = _.last(b); // "w"
|
|
||||||
|
|
||||||
scope.keep_amount = prompt_for_days;
|
|
||||||
scope.granularity_keep_amount = granularity;
|
|
||||||
scope.keep_unit = _.find(scope.keep_unit_choices, function(i){
|
|
||||||
return i.value === keep_unit;
|
|
||||||
});
|
|
||||||
scope.granularity_keep_unit =_.find(scope.granularity_keep_unit_choices, function(i){
|
|
||||||
return i.value === granularity_keep_unit;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.removeScheduleFound) {
|
|
||||||
scope.removeScheduleFound();
|
|
||||||
}
|
|
||||||
scope.removeScheduleFound = scope.$on('ScheduleFound', function() {
|
|
||||||
$('#form-container').empty();
|
|
||||||
scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false });
|
|
||||||
scheduler.inject('form-container', false);
|
|
||||||
scheduler.injectDetail('occurrences', false);
|
|
||||||
|
|
||||||
if (!/DTSTART/.test(schedule.rrule)) {
|
|
||||||
schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z');
|
|
||||||
}
|
|
||||||
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
|
|
||||||
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
|
|
||||||
scope.$on("htmlDetailReady", function() {
|
|
||||||
scheduler.setRRule(schedule.rrule);
|
|
||||||
scheduler.setName(schedule.name);
|
|
||||||
$rootScope.$broadcast("ScheduleFormCreated", scope);
|
|
||||||
});
|
|
||||||
scope.showRRuleDetail = false;
|
|
||||||
|
|
||||||
scheduler.setRRule(schedule.rrule);
|
|
||||||
scheduler.setName(schedule.name);
|
|
||||||
if(scope.isFactCleanup || scope.cleanupJob){
|
|
||||||
setGranularity();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (scope.removeScheduleSaved) {
|
|
||||||
scope.removeScheduleSaved();
|
|
||||||
}
|
|
||||||
scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) {
|
|
||||||
Wait('stop');
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback, data);
|
|
||||||
}
|
|
||||||
$state.go("^");
|
|
||||||
});
|
|
||||||
scope.saveSchedule = function() {
|
|
||||||
schedule.extra_data = scope.extraVars;
|
|
||||||
SchedulePost({
|
|
||||||
scope: scope,
|
|
||||||
url: url,
|
|
||||||
scheduler: scheduler,
|
|
||||||
callback: 'ScheduleSaved',
|
|
||||||
mode: 'edit',
|
|
||||||
schedule: schedule
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
// Get the existing record
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function(data) {
|
|
||||||
schedule = data;
|
|
||||||
try {
|
|
||||||
schedule.extra_data = JSON.parse(schedule.extra_data);
|
|
||||||
} catch(e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
scope.extraVars = data.extra_data === '' ? '---' : '---\n' + jsyaml.safeDump(data.extra_data);
|
|
||||||
|
|
||||||
if(schedule.extra_data.hasOwnProperty('granularity')){
|
|
||||||
scope.isFactCleanup = true;
|
|
||||||
}
|
|
||||||
if (schedule.extra_data.hasOwnProperty('days')){
|
|
||||||
scope.cleanupJob = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.schedule_obj = data;
|
|
||||||
|
|
||||||
scope.$emit('ScheduleFound');
|
|
||||||
})
|
|
||||||
.error(function(data,status){
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('AddSchedule', ['$location', '$rootScope', '$stateParams',
|
|
||||||
'SchedulerInit', 'Wait', 'GetBasePath', 'Empty', 'SchedulePost', '$state', 'Rest', 'ProcessErrors',
|
|
||||||
function($location, $rootScope, $stateParams, SchedulerInit,
|
|
||||||
Wait, GetBasePath, Empty, SchedulePost, $state, Rest,
|
|
||||||
ProcessErrors) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
callback= params.callback,
|
|
||||||
base = params.base || $location.path().replace(/^\//, '').split('/')[0],
|
|
||||||
url = params.url || null,
|
|
||||||
scheduler,
|
|
||||||
job_type;
|
|
||||||
|
|
||||||
job_type = scope.parentObject.job_type;
|
|
||||||
if (!Empty($stateParams.id) && base !== 'system_job_templates' && base !== 'inventories' && !url) {
|
|
||||||
url = GetBasePath(base) + $stateParams.id + '/schedules/';
|
|
||||||
}
|
|
||||||
else if(base === "inventories"){
|
|
||||||
if (!params.url){
|
|
||||||
url = GetBasePath('groups') + $stateParams.id + '/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get().
|
|
||||||
then(function (data) {
|
|
||||||
url = data.data.related.inventory_source + 'schedules/';
|
|
||||||
}).catch(function (response) {
|
|
||||||
ProcessErrors(null, response.data, response.status, null, {
|
|
||||||
hdr: 'Error!',
|
|
||||||
msg: 'Failed to get inventory group info. GET returned status: ' +
|
|
||||||
response.status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
url = params.url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (base === 'system_job_templates') {
|
|
||||||
url = GetBasePath(base) + $stateParams.id + '/schedules/';
|
|
||||||
if(job_type === "cleanup_facts"){
|
|
||||||
scope.isFactCleanup = true;
|
|
||||||
scope.keep_unit_choices = [{
|
|
||||||
"label" : "Days",
|
|
||||||
"value" : "d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Weeks",
|
|
||||||
"value" : "w"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label" : "Years",
|
|
||||||
"value" : "y"
|
|
||||||
}];
|
|
||||||
scope.granularity_keep_unit_choices = [{
|
|
||||||
"label" : "Days",
|
|
||||||
"value" : "d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Weeks",
|
|
||||||
"value" : "w"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label" : "Years",
|
|
||||||
"value" : "y"
|
|
||||||
}];
|
|
||||||
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
|
|
||||||
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
|
|
||||||
scope.keep_unit = scope.keep_unit_choices[0];
|
|
||||||
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.cleanupJob = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
$('#form-container').empty();
|
|
||||||
scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false });
|
|
||||||
if(scope.schedulerUTCTime) {
|
|
||||||
// The UTC time is already set
|
|
||||||
scope.processSchedulerEndDt();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// We need to wait for it to be set by angular-scheduler because the following function depends
|
|
||||||
// on it
|
|
||||||
var schedulerUTCTimeWatcher = scope.$watch('schedulerUTCTime', function(newVal) {
|
|
||||||
if(newVal) {
|
|
||||||
// Remove the watcher
|
|
||||||
schedulerUTCTimeWatcher();
|
|
||||||
scope.processSchedulerEndDt();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
scheduler.inject('form-container', false);
|
|
||||||
scheduler.injectDetail('occurrences', false);
|
|
||||||
scheduler.clear();
|
|
||||||
scope.$on("htmlDetailReady", function() {
|
|
||||||
$rootScope.$broadcast("ScheduleFormCreated", scope);
|
|
||||||
});
|
|
||||||
scope.showRRuleDetail = false;
|
|
||||||
|
|
||||||
if (scope.removeScheduleSaved) {
|
|
||||||
scope.removeScheduleSaved();
|
|
||||||
}
|
|
||||||
scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) {
|
|
||||||
Wait('stop');
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback, data);
|
|
||||||
}
|
|
||||||
$state.go("^", null, {reload: true});
|
|
||||||
});
|
|
||||||
scope.saveSchedule = function() {
|
|
||||||
SchedulePost({
|
|
||||||
scope: scope,
|
|
||||||
url: url,
|
|
||||||
scheduler: scheduler,
|
|
||||||
callback: 'ScheduleSaved',
|
|
||||||
mode: 'add'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$('#scheduler-tabs li a').on('shown.bs.tab', function(e) {
|
|
||||||
if ($(e.target).text() === 'Details') {
|
|
||||||
if (!scheduler.isValid()) {
|
|
||||||
$('#scheduler-tabs a:first').tab('show');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('SchedulePost', ['Rest', 'ProcessErrors', 'RRuleToAPI', 'Wait',
|
|
||||||
function(Rest, ProcessErrors, RRuleToAPI, Wait) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
url = params.url,
|
|
||||||
scheduler = params.scheduler,
|
|
||||||
mode = params.mode,
|
|
||||||
schedule = (params.schedule) ? params.schedule : {},
|
|
||||||
callback = params.callback,
|
|
||||||
newSchedule, rrule, extra_vars;
|
|
||||||
if (scheduler.isValid()) {
|
|
||||||
Wait('start');
|
|
||||||
newSchedule = scheduler.getValue();
|
|
||||||
rrule = scheduler.getRRule();
|
|
||||||
schedule.name = newSchedule.name;
|
|
||||||
schedule.rrule = RRuleToAPI(rrule.toString());
|
|
||||||
schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText();
|
|
||||||
|
|
||||||
if (scope.isFactCleanup) {
|
|
||||||
extra_vars = {
|
|
||||||
"older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value,
|
|
||||||
"granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value
|
|
||||||
};
|
|
||||||
schedule.extra_data = JSON.stringify(extra_vars);
|
|
||||||
} else if (scope.cleanupJob) {
|
|
||||||
extra_vars = {
|
|
||||||
"days" : scope.scheduler_form.schedulerPurgeDays.$viewValue
|
|
||||||
};
|
|
||||||
schedule.extra_data = JSON.stringify(extra_vars);
|
|
||||||
}
|
|
||||||
else if(scope.extraVars){
|
|
||||||
schedule.extra_data = scope.parseType === 'yaml' ?
|
|
||||||
(scope.extraVars === '---' ? "" : jsyaml.safeLoad(scope.extraVars)) : scope.extraVars;
|
|
||||||
}
|
|
||||||
Rest.setUrl(url);
|
|
||||||
if (mode === 'add') {
|
|
||||||
Rest.post(schedule)
|
|
||||||
.success(function(){
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Wait('stop');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function(data, status){
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'POST to ' + url + ' returned: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Rest.put(schedule)
|
|
||||||
.success(function(){
|
|
||||||
if (callback) {
|
|
||||||
scope.$emit(callback, schedule);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Wait('stop');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function(data, status){
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'POST to ' + url + ' returned: ' + status });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flip a schedule's enable flag
|
|
||||||
*
|
|
||||||
* ToggleSchedule({
|
|
||||||
* scope: scope,
|
|
||||||
* id: schedule.id to update
|
|
||||||
* callback: scope.$emit label to call when update completes
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
.factory('ToggleSchedule', ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest', '$state',
|
|
||||||
function(Wait, GetBasePath, ProcessErrors, Rest, $state) {
|
|
||||||
return function(params) {
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
url = GetBasePath('schedules') + id +'/';
|
|
||||||
|
|
||||||
// Perform the update
|
|
||||||
if (scope.removeScheduleFound) {
|
|
||||||
scope.removeScheduleFound();
|
|
||||||
}
|
|
||||||
scope.removeScheduleFound = scope.$on('ScheduleFound', function(e, data) {
|
|
||||||
data.enabled = (data.enabled) ? false : true;
|
|
||||||
Rest.put(data)
|
|
||||||
.success( function() {
|
|
||||||
Wait('stop');
|
|
||||||
$state.go('.', null, {reload: true});
|
|
||||||
})
|
|
||||||
.error( function(data, status) {
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to update schedule ' + id + ' PUT returned: ' + status });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
// Get the schedule
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function(data) {
|
|
||||||
scope.$emit('ScheduleFound', data);
|
|
||||||
})
|
|
||||||
.error(function(data,status){
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a schedule. Prompts user to confirm delete
|
|
||||||
*
|
|
||||||
* DeleteSchedule({
|
|
||||||
* scope: $scope containing list of schedules
|
|
||||||
* id: id of schedule to delete
|
|
||||||
* callback: $scope.$emit label to call when delete is completed
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
.factory('DeleteSchedule', ['GetBasePath','Rest', 'Wait', '$state',
|
|
||||||
'ProcessErrors', 'Prompt', 'Find', '$location', '$filter',
|
|
||||||
function(GetBasePath, Rest, Wait, $state, ProcessErrors, Prompt, Find,
|
|
||||||
$location, $filter) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
id = params.id,
|
|
||||||
callback = params.callback,
|
|
||||||
action, schedule, list, url, hdr;
|
|
||||||
|
|
||||||
if (scope.schedules) {
|
|
||||||
list = scope.schedules;
|
|
||||||
}
|
|
||||||
else if (scope.scheduled_jobs) {
|
|
||||||
list = scope.scheduled_jobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
url = GetBasePath('schedules') + id + '/';
|
|
||||||
schedule = Find({list: list, key: 'id', val: id });
|
|
||||||
hdr = 'Delete Schedule';
|
|
||||||
|
|
||||||
action = function () {
|
|
||||||
Wait('start');
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.destroy()
|
|
||||||
.success(function () {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
scope.$emit(callback, id);
|
|
||||||
if (new RegExp('/' + id + '$').test($location.$$url)) {
|
|
||||||
$location.url($location.url().replace(/[/][0-9]+$/, "")); // go to list view
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$state.go('.', null, {reload: true});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
try {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
|
||||||
' failed. DELETE returned: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Prompt({
|
|
||||||
hdr: hdr,
|
|
||||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the schedule below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(schedule.name) + '</div>',
|
|
||||||
action: action,
|
|
||||||
actionText: 'DELETE',
|
|
||||||
backdrop: false
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert rrule string to an API agreeable format
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
.factory('RRuleToAPI', [ function() {
|
|
||||||
return function(rrule) {
|
|
||||||
var response;
|
|
||||||
response = rrule.replace(/(^.*(?=DTSTART))(DTSTART=.*?;)(.*$)/, function(str, p1, p2, p3) {
|
|
||||||
return p2.replace(/\;/,'').replace(/=/,':') + ' ' + 'RRULE:' + p1 + p3;
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Selection
|
|
||||||
* @description
|
|
||||||
* SelectionHelper
|
|
||||||
* Used in list controllers where the list might also be used as a selection list.
|
|
||||||
*
|
|
||||||
* SelectionInit( {
|
|
||||||
* scope: <list scope>,
|
|
||||||
* list: <list object>
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('SelectionHelper', ['Utilities', 'RestServices'])
|
|
||||||
|
|
||||||
.factory('SelectionInit', ['Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'Wait',
|
|
||||||
function (Rest, Alert, ProcessErrors, ReturnToCaller, Wait) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
list = params.list,
|
|
||||||
target_url = params.url,
|
|
||||||
returnToCaller = params.returnToCaller,
|
|
||||||
selected;
|
|
||||||
|
|
||||||
if (params.selected !== undefined) {
|
|
||||||
selected = params.selected;
|
|
||||||
} else {
|
|
||||||
selected = []; //array of selected row IDs
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.formModalActionDisabled = true;
|
|
||||||
scope.disableSelectBtn = true;
|
|
||||||
|
|
||||||
// toggle row selection
|
|
||||||
scope['toggle_' + list.iterator] = function (id, ischeckbox) {
|
|
||||||
var i, j, found;
|
|
||||||
for (i = 0; i < scope[list.name].length; i++) {
|
|
||||||
if (scope[list.name][i].id === id) {
|
|
||||||
var control = scope[list.name][i];
|
|
||||||
if (ischeckbox && control.checked) {
|
|
||||||
scope[list.name][i].success_class = 'success';
|
|
||||||
// add selected object to the array
|
|
||||||
found = false;
|
|
||||||
for (j = 0; j < selected.length; j++) {
|
|
||||||
if (selected[j].id === id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
selected.push(scope[list.name][i]);
|
|
||||||
}
|
|
||||||
} else if (ischeckbox) {
|
|
||||||
scope[list.name][i].success_class = '';
|
|
||||||
|
|
||||||
// remove selected object from the array
|
|
||||||
for (j = 0; j < selected.length; j++) {
|
|
||||||
if (selected[j].id === id) {
|
|
||||||
selected.splice(j, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (selected.length > 0) {
|
|
||||||
scope.formModalActionDisabled = false;
|
|
||||||
scope.disableSelectBtn = false;
|
|
||||||
} else {
|
|
||||||
scope.formModalActionDisabled = true;
|
|
||||||
scope.disableSelectBtn = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the selections
|
|
||||||
scope.finishSelection = function () {
|
|
||||||
Rest.setUrl(target_url);
|
|
||||||
|
|
||||||
var queue = [], j;
|
|
||||||
|
|
||||||
scope.formModalActionDisabled = true;
|
|
||||||
scope.disableSelectBtn = true;
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
function finished() {
|
|
||||||
selected = [];
|
|
||||||
if (returnToCaller !== undefined) {
|
|
||||||
ReturnToCaller(returnToCaller);
|
|
||||||
} else {
|
|
||||||
$('#form-modal').modal('hide');
|
|
||||||
scope.$emit('modalClosed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postIt(data) {
|
|
||||||
Rest.post(data)
|
|
||||||
.success(function (data, status) {
|
|
||||||
queue.push({ result: 'success', data: data, status: status });
|
|
||||||
scope.$emit('callFinished');
|
|
||||||
})
|
|
||||||
.error(function (data, status, headers) {
|
|
||||||
queue.push({ result: 'error', data: data, status: status, headers: headers });
|
|
||||||
scope.$emit('callFinished');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.callFinishedRemove) {
|
|
||||||
scope.callFinishedRemove();
|
|
||||||
}
|
|
||||||
scope.callFinishedRemove = scope.$on('callFinished', function () {
|
|
||||||
// We call the API for each selected item. We need to hang out until all the api
|
|
||||||
// calls are finished.
|
|
||||||
var i, errors=0;
|
|
||||||
if (queue.length === selected.length) {
|
|
||||||
Wait('stop');
|
|
||||||
for (i = 0; i < queue.length; i++) {
|
|
||||||
if (queue[i].result === 'error') {
|
|
||||||
ProcessErrors(scope, queue[i].data, queue[i].status, null, { hdr: 'POST Failure',
|
|
||||||
msg: 'Failed to add ' + list.iterator + '. POST returned status: ' + queue[i].status });
|
|
||||||
errors++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (errors === 0) {
|
|
||||||
finished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (selected.length > 0) {
|
|
||||||
for (j = 0; j < selected.length; j++) {
|
|
||||||
postIt(selected[j]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
finished();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.formModalAction = scope.finishSelection;
|
|
||||||
|
|
||||||
// Initialize our data set after a refresh (page change or search)
|
|
||||||
if (scope.SelectPostRefreshRemove) {
|
|
||||||
scope.SelectPostRefreshRemove();
|
|
||||||
}
|
|
||||||
scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function () {
|
|
||||||
var i, j, found;
|
|
||||||
if (scope[list.name]) {
|
|
||||||
for (i = 0; i < scope[list.name].length; i++) {
|
|
||||||
found = false;
|
|
||||||
for (j = 0; j < selected.length; j++) {
|
|
||||||
if (selected[j].id === scope[list.name][i].id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
scope[list.name][i].checked = '1';
|
|
||||||
scope[list.name][i].success_class = 'success';
|
|
||||||
} else {
|
|
||||||
scope[list.name][i].checked = '0';
|
|
||||||
scope[list.name][i].success_class = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Users
|
|
||||||
* @description
|
|
||||||
* UserHelper
|
|
||||||
* Routines shared amongst the user controllers
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('UserHelper', ['UserFormDefinition'])
|
|
||||||
.factory('ResetForm', ['UserForm',
|
|
||||||
function (UserForm) {
|
|
||||||
return function () {
|
|
||||||
// Restore form to default conditions. Run before applying LDAP configuration.
|
|
||||||
// LDAP may manage some or all of these fields in which case the user cannot
|
|
||||||
// make changes to their values in AWX.
|
|
||||||
|
|
||||||
UserForm.fields.first_name.readonly = false;
|
|
||||||
UserForm.fields.first_name.editRequired = true;
|
|
||||||
UserForm.fields.last_name.readonly = false;
|
|
||||||
UserForm.fields.last_name.editRequired = true;
|
|
||||||
UserForm.fields.email.readonly = false;
|
|
||||||
UserForm.fields.email.editRequired = true;
|
|
||||||
UserForm.fields.organization.awRequiredWhen = {
|
|
||||||
reqExpression: "orgrequired",
|
|
||||||
init: true
|
|
||||||
};
|
|
||||||
UserForm.fields.organization.readonly = false;
|
|
||||||
UserForm.fields.username.awRequiredWhen = {
|
|
||||||
reqExpression: "not_ldap_user",
|
|
||||||
init: true
|
|
||||||
};
|
|
||||||
UserForm.fields.username.readonly = false;
|
|
||||||
UserForm.fields.password.editRequired = false;
|
|
||||||
UserForm.fields.password.addRrequired = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Variables
|
|
||||||
* @description
|
|
||||||
* VariablesHelper
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('VariablesHelper', ['Utilities'])
|
|
||||||
|
|
||||||
/**
|
|
||||||
variables: string containing YAML or JSON | a JSON object.
|
|
||||||
|
|
||||||
If JSON string, convert to JSON object and run through jsyaml.safeDump() to create a YAML document. If YAML,
|
|
||||||
will attempt to load via jsyaml.safeLoad() and return a YAML document using jsyaml.safeDump(). In all cases
|
|
||||||
a YAML document is returned.
|
|
||||||
**/
|
|
||||||
.factory('ParseVariableString', ['$log', 'ProcessErrors', 'SortVariables', function ($log, ProcessErrors, SortVariables) {
|
|
||||||
return function (variables) {
|
|
||||||
var result = "---", json_obj;
|
|
||||||
if (typeof variables === 'string') {
|
|
||||||
if (variables === "{}" || variables === "null" || variables === "" || variables === "\"\"") {
|
|
||||||
// String is empty, return ---
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
json_obj = JSON.parse(variables);
|
|
||||||
json_obj = SortVariables(json_obj);
|
|
||||||
result = jsyaml.safeDump(json_obj);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
$log.debug('Attempt to parse extra_vars as JSON failed. Check that the variables parse as yaml. Set the raw string as the result.');
|
|
||||||
try {
|
|
||||||
// do safeLoad, which well error if not valid yaml
|
|
||||||
json_obj = jsyaml.safeLoad(variables);
|
|
||||||
// but just send the variables
|
|
||||||
result = variables;
|
|
||||||
}
|
|
||||||
catch(e2) {
|
|
||||||
ProcessErrors(null, variables, e2.message, null, { hdr: 'Error!',
|
|
||||||
msg: 'Attempts to parse variables as JSON and YAML failed. Last attempt returned: ' + e2.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ($.isEmptyObject(variables) || variables === null) {
|
|
||||||
// Empty object, return ---
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// convert object to yaml
|
|
||||||
try {
|
|
||||||
json_obj = SortVariables(variables);
|
|
||||||
result = jsyaml.safeDump(json_obj);
|
|
||||||
// result = variables;
|
|
||||||
}
|
|
||||||
catch(e3) {
|
|
||||||
ProcessErrors(null, variables, e3.message, null, { hdr: 'Error!',
|
|
||||||
msg: 'Attempt to convert JSON object to YAML document failed: ' + e3.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
/**
|
|
||||||
parseType: 'json' | 'yaml'
|
|
||||||
variables: string containing JSON or YAML
|
|
||||||
stringify: optional, boolean
|
|
||||||
|
|
||||||
Parse the given string according to the parseType to a JSON object. If stringify true,
|
|
||||||
stringify the object and return the string. Otherwise, return the JSON object.
|
|
||||||
|
|
||||||
**/
|
|
||||||
.factory('ToJSON', ['$log', 'ProcessErrors', function($log, ProcessErrors) {
|
|
||||||
return function(parseType, variables, stringify, reviver) {
|
|
||||||
var json_data,
|
|
||||||
result,
|
|
||||||
tmp;
|
|
||||||
// bracketVar,
|
|
||||||
// key,
|
|
||||||
// lines, i, newVars = [];
|
|
||||||
if (parseType === 'json') {
|
|
||||||
try {
|
|
||||||
// perform a check to see if the user cleared the field completly
|
|
||||||
if(variables.trim() === "" || variables.trim() === "{" || variables.trim() === "}" ){
|
|
||||||
variables = "{}";
|
|
||||||
}
|
|
||||||
//parse a JSON string
|
|
||||||
if (reviver) {
|
|
||||||
json_data = JSON.parse(variables, reviver);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
json_data = JSON.parse(variables);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
json_data = {};
|
|
||||||
$log.error('Failed to parse JSON string. Parser returned: ' + e.message);
|
|
||||||
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to parse JSON string. Parser returned: ' + e.message });
|
|
||||||
throw 'Parse error. Failed to parse variables.';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
if(variables.trim() === "" || variables.trim() === "-" || variables.trim() === "--"){
|
|
||||||
variables = '---';
|
|
||||||
}
|
|
||||||
json_data = jsyaml.safeLoad(variables);
|
|
||||||
if(json_data!==null){
|
|
||||||
// unparsing just to make sure no weird characters are included.
|
|
||||||
tmp = jsyaml.dump(json_data);
|
|
||||||
if(tmp.indexOf('[object Object]')!==-1){
|
|
||||||
throw "Failed to parse YAML string. Parser returned' + key + ' : ' +value + '.' ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
json_data = undefined; // {};
|
|
||||||
// $log.error('Failed to parse YAML string. Parser returned undefined');
|
|
||||||
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to parse YAML string. Parser returned undefined'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure our JSON is actually an object
|
|
||||||
if (typeof json_data !== 'object') {
|
|
||||||
ProcessErrors(null, variables, null, null, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to parse variables. Attempted to parse ' + parseType + '. Parser did not return an object.' });
|
|
||||||
// setTimeout( function() {
|
|
||||||
throw 'Parse error. Failed to parse variables.';
|
|
||||||
// }, 1000);
|
|
||||||
}
|
|
||||||
result = json_data;
|
|
||||||
if (stringify) {
|
|
||||||
if(json_data === undefined){
|
|
||||||
result = undefined;
|
|
||||||
}
|
|
||||||
else if ($.isEmptyObject(json_data)) {
|
|
||||||
result = "";
|
|
||||||
} else {
|
|
||||||
// utilize the parsing to get here
|
|
||||||
// but send the raw variable string
|
|
||||||
result = variables;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('SortVariables', [ function() {
|
|
||||||
return function(variableObj) {
|
|
||||||
var newObj;
|
|
||||||
function sortIt(objToSort) {
|
|
||||||
var i,
|
|
||||||
keys = Object.keys(objToSort),
|
|
||||||
newObj = {};
|
|
||||||
keys = keys.sort();
|
|
||||||
for (i=0; i < keys.length; i++) {
|
|
||||||
if (typeof objToSort[keys[i]] === 'object' && objToSort[keys[i]] !== null && !Array.isArray(objToSort[keys[i]])) {
|
|
||||||
newObj[keys[i]] = sortIt(objToSort[keys[i]]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newObj[keys[i]] = objToSort[keys[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newObj;
|
|
||||||
}
|
|
||||||
newObj = sortIt(variableObj);
|
|
||||||
return newObj;
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:api-defaults
|
|
||||||
* @description this could use more discussion
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('APIDefaults', ['RestServices', 'Utilities'])
|
|
||||||
.factory('GetAPIDefaults', ['Alert', 'Rest', '$rootScope',
|
|
||||||
function (Alert, Rest, $rootScope) {
|
|
||||||
return function (key) {
|
|
||||||
|
|
||||||
//Reload a related collection on pagination or search change
|
|
||||||
|
|
||||||
var result = {}, cnt = 0, url;
|
|
||||||
|
|
||||||
function lookup(key) {
|
|
||||||
var id, result = {};
|
|
||||||
for (id in $rootScope.apiDefaults) {
|
|
||||||
if (id === key || id.iterator === key) {
|
|
||||||
result[id] = $rootScope.apiDefaults[id];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function wait() {
|
|
||||||
if ($.isEmptyObject(result) && cnt < 5) {
|
|
||||||
cnt++;
|
|
||||||
setTimeout(1000, wait());
|
|
||||||
} else if (result.status === 'success') {
|
|
||||||
return lookup(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($rootScope.apiDefaults === null || $rootScope.apiDefaults === undefined) {
|
|
||||||
url = '/api/v1/';
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
var id, defaults = data;
|
|
||||||
for (id in defaults) {
|
|
||||||
switch (id) {
|
|
||||||
case 'organizations':
|
|
||||||
defaults[id].iterator = 'organization';
|
|
||||||
break;
|
|
||||||
case 'jobs':
|
|
||||||
defaults[id].iterator = 'job';
|
|
||||||
break;
|
|
||||||
case 'users':
|
|
||||||
defaults[id].iterator = 'user';
|
|
||||||
break;
|
|
||||||
case 'teams':
|
|
||||||
defaults[id].iterator = 'team';
|
|
||||||
break;
|
|
||||||
case 'hosts':
|
|
||||||
defaults[id].iterator = 'host';
|
|
||||||
break;
|
|
||||||
case 'groups':
|
|
||||||
defaults[id].iterator = 'group';
|
|
||||||
break;
|
|
||||||
case 'projects':
|
|
||||||
defaults[id].iterator = 'project';
|
|
||||||
break;
|
|
||||||
case 'inventories':
|
|
||||||
defaults[id].iterator = 'inventory';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rootScope.apiDefaults = defaults;
|
|
||||||
result = {
|
|
||||||
status: 'success'
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
result = {
|
|
||||||
status: 'error',
|
|
||||||
msg: 'Call to ' + url + ' failed. GET returned status: ' + status
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return wait();
|
|
||||||
} else {
|
|
||||||
return lookup(key);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:Inventory
|
|
||||||
* @description InventoryHelper
|
|
||||||
* Routines for building the tree. Everything related to the tree is here except
|
|
||||||
* for the menu piece. The routine for building the menu is in InventoriesEdit controller
|
|
||||||
* (controllers/Inventories.js)
|
|
||||||
*/
|
|
||||||
|
|
||||||
import listGenerator from '../shared/list-generator/main';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('InventoryHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', listGenerator.name,
|
|
||||||
'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper', 'VariablesHelper',
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'OrganizationList',
|
|
||||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON',
|
|
||||||
function (InventoryForm, Rest, Alert, ProcessErrors, OrganizationList, GetBasePath, ParseTypeChange, Wait,
|
|
||||||
ToJSON) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
// Save inventory property modifications
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
form = InventoryForm,
|
|
||||||
defaultUrl = GetBasePath('inventory'),
|
|
||||||
fld, json_data, data;
|
|
||||||
|
|
||||||
Wait('start');
|
|
||||||
|
|
||||||
// Make sure we have valid variable data
|
|
||||||
json_data = ToJSON(scope.parseType, scope.variables);
|
|
||||||
|
|
||||||
data = {};
|
|
||||||
for (fld in form.fields) {
|
|
||||||
if (fld !== 'variables') {
|
|
||||||
if (form.fields[fld].realName) {
|
|
||||||
data[form.fields[fld].realName] = scope[fld];
|
|
||||||
} else {
|
|
||||||
data[fld] = scope[fld];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.removeUpdateInventoryVariables) {
|
|
||||||
scope.removeUpdateInventoryVariables();
|
|
||||||
}
|
|
||||||
scope.removeUpdateInventoryVariables = scope.$on('UpdateInventoryVariables', function(e, data) {
|
|
||||||
Rest.setUrl(data.related.variable_data);
|
|
||||||
Rest.put(json_data)
|
|
||||||
.success(function () {
|
|
||||||
Wait('stop');
|
|
||||||
scope.$emit('InventorySaved');
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to update inventory varaibles. PUT returned status: ' + status
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Rest.setUrl(defaultUrl + scope.inventory_id + '/');
|
|
||||||
Rest.put(data)
|
|
||||||
.success(function (data) {
|
|
||||||
if (scope.variables) {
|
|
||||||
scope.$emit('UpdateInventoryVariables', data);
|
|
||||||
} else {
|
|
||||||
scope.$emit('InventorySaved');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.error(function (data, status) {
|
|
||||||
ProcessErrors(scope, data, status, form, { hdr: 'Error!',
|
|
||||||
msg: 'Failed to update inventory. PUT returned status: ' + status });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:md5
|
|
||||||
* @description
|
|
||||||
* Run md5Setup({ scope: , master:, check_field:, default_val: })
|
|
||||||
* to initialize md5 fields (checkbox and text field).
|
|
||||||
* discussion
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('md5Helper', ['RestServices', 'Utilities', require('angular-md5')])
|
|
||||||
.factory('md5Setup', ['md5', function (md5) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
master = params.master,
|
|
||||||
check_field = params.check_field,
|
|
||||||
default_val = params.default_val;
|
|
||||||
|
|
||||||
scope[check_field] = default_val;
|
|
||||||
master[check_field] = default_val;
|
|
||||||
|
|
||||||
scope.genMD5 = function (fld) {
|
|
||||||
var now = new Date();
|
|
||||||
scope[fld] = md5.createHash('AnsibleWorks' + now.getTime());
|
|
||||||
scope.$emit('NewMD5Generated');
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.toggleCallback = function (fld) {
|
|
||||||
if (scope.allow_callbacks === false) {
|
|
||||||
scope[fld] = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.selectAll = function (fld) {
|
|
||||||
$('input[name="' + fld + '"]').focus().select();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:teams
|
|
||||||
* @description
|
|
||||||
* TeamHelper
|
|
||||||
* Routines shared amongst the team controllers
|
|
||||||
*/
|
|
||||||
|
|
||||||
import listGenerator from '../shared/list-generator/main';
|
|
||||||
|
|
||||||
export default
|
|
||||||
angular.module('TeamHelper', ['RestServices', 'Utilities', 'OrganizationListDefinition', listGenerator.name
|
|
||||||
])
|
|
||||||
.factory('SetTeamListeners', ['Alert', 'Rest',
|
|
||||||
function (Alert, Rest) {
|
|
||||||
return function (params) {
|
|
||||||
|
|
||||||
var scope = params.scope,
|
|
||||||
set = params.set;
|
|
||||||
|
|
||||||
// Listeners to perform lookups after main inventory list loads
|
|
||||||
|
|
||||||
scope.$on('TeamResultFound', function (e, results, lookup_results) {
|
|
||||||
var i, j, key, property;
|
|
||||||
if (lookup_results.length === results.length) {
|
|
||||||
key = 'organization';
|
|
||||||
property = 'organization_name';
|
|
||||||
for (i = 0; i < results.length; i++) {
|
|
||||||
for (j = 0; j < lookup_results.length; j++) {
|
|
||||||
if (results[i][key] === lookup_results[j].id) {
|
|
||||||
results[i][property] = lookup_results[j].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scope[set] = results;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.$on('TeamRefreshFinished', function (e, results) {
|
|
||||||
// Loop through the result set (sent to us by the search helper) and
|
|
||||||
// lookup the id and name of each organization. After each lookup
|
|
||||||
// completes, call resultFound.
|
|
||||||
|
|
||||||
var i, lookup_results = [], url;
|
|
||||||
|
|
||||||
function getOrganization(url) {
|
|
||||||
Rest.setUrl(url);
|
|
||||||
Rest.get()
|
|
||||||
.success(function (data) {
|
|
||||||
lookup_results.push({ id: data.id, value: data.name });
|
|
||||||
scope.$emit('TeamResultFound', results, lookup_results);
|
|
||||||
})
|
|
||||||
.error(function () {
|
|
||||||
lookup_results.push({ id: 'error' });
|
|
||||||
scope.$emit('TeamResultFound', results, lookup_results);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < results.length; i++) {
|
|
||||||
url = '/api/v1/organizations/' + results[i].organization + '/';
|
|
||||||
getOrganization(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import JobTemplatesListDirective from './job-templates-list.directive';
|
import JobTemplatesListDirective from './job-templates-list.directive';
|
||||||
import systemStatus from '../../../../smart-status/main';
|
import systemStatus from '../../../../smart-status/main';
|
||||||
import jobSubmissionHelper from '../../../../helpers/JobSubmission';
|
|
||||||
|
|
||||||
export default angular.module('JobTemplatesList', [systemStatus.name, jobSubmissionHelper.name])
|
export default angular.module('JobTemplatesList', [systemStatus.name])
|
||||||
.directive('jobTemplatesList', JobTemplatesListDirective);
|
.directive('jobTemplatesList', JobTemplatesListDirective);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
export default
|
||||||
|
function GetHostsStatusMsg() {
|
||||||
|
return function(params) {
|
||||||
|
var active_failures = params.active_failures,
|
||||||
|
total_hosts = params.total_hosts,
|
||||||
|
tip, failures, html_class;
|
||||||
|
|
||||||
|
// Return values for use on host status indicator
|
||||||
|
|
||||||
|
if (active_failures > 0) {
|
||||||
|
tip = total_hosts + ((total_hosts === 1) ? ' host' : ' hosts') + '. ' + active_failures + ' with failed jobs.';
|
||||||
|
html_class = 'error';
|
||||||
|
failures = true;
|
||||||
|
} else {
|
||||||
|
failures = false;
|
||||||
|
if (total_hosts === 0) {
|
||||||
|
// no hosts
|
||||||
|
tip = "Contains 0 hosts.";
|
||||||
|
html_class = 'none';
|
||||||
|
} else {
|
||||||
|
// many hosts with 0 failures
|
||||||
|
tip = total_hosts + ((total_hosts === 1) ? ' host' : ' hosts') + '. No job failures';
|
||||||
|
html_class = 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tooltip: tip,
|
||||||
|
failures: failures,
|
||||||
|
'class': html_class
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
export default
|
||||||
|
function GetSourceTypeOptions(Rest, ProcessErrors, GetBasePath) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
variable = params.variable;
|
||||||
|
|
||||||
|
if (scope[variable] === undefined) {
|
||||||
|
scope[variable] = [];
|
||||||
|
Rest.setUrl(GetBasePath('inventory_sources'));
|
||||||
|
Rest.options()
|
||||||
|
.success(function (data) {
|
||||||
|
var i, choices = data.actions.GET.source.choices;
|
||||||
|
for (i = 0; i < choices.length; i++) {
|
||||||
|
if (choices[i][0] !== 'file') {
|
||||||
|
scope[variable].push({
|
||||||
|
label: choices[i][1],
|
||||||
|
value: choices[i][0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.cloudCredentialRequired = false;
|
||||||
|
scope.$emit('sourceTypeOptionsReady');
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to retrieve options for inventory_sources.source. OPTIONS status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSourceTypeOptions.$inject =
|
||||||
|
[ 'Rest',
|
||||||
|
'ProcessErrors',
|
||||||
|
'GetBasePath'
|
||||||
|
];
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
export default
|
||||||
|
function GetSyncStatusMsg(Empty) {
|
||||||
|
return function(params) {
|
||||||
|
var status = params.status,
|
||||||
|
source = params.source,
|
||||||
|
has_inventory_sources = params.has_inventory_sources,
|
||||||
|
launch_class = '',
|
||||||
|
launch_tip = 'Start sync process',
|
||||||
|
schedule_tip = 'Schedule future inventory syncs',
|
||||||
|
stat, stat_class, status_tip;
|
||||||
|
|
||||||
|
stat = status;
|
||||||
|
stat_class = stat;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'never updated':
|
||||||
|
stat = 'never';
|
||||||
|
stat_class = 'na';
|
||||||
|
status_tip = 'Sync not performed. Click <i class="fa fa-refresh"></i> to start it now.';
|
||||||
|
break;
|
||||||
|
case 'none':
|
||||||
|
case 'ok':
|
||||||
|
case '':
|
||||||
|
launch_class = 'btn-disabled';
|
||||||
|
stat = 'n/a';
|
||||||
|
stat_class = 'na';
|
||||||
|
status_tip = 'Cloud source not configured. Click <i class="fa fa-pencil"></i> to update.';
|
||||||
|
launch_tip = 'Cloud source not configured.';
|
||||||
|
break;
|
||||||
|
case 'canceled':
|
||||||
|
status_tip = 'Sync canceled. Click to view log.';
|
||||||
|
break;
|
||||||
|
case 'failed':
|
||||||
|
status_tip = 'Sync failed. Click to view log.';
|
||||||
|
break;
|
||||||
|
case 'successful':
|
||||||
|
status_tip = 'Sync completed. Click to view log.';
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
status_tip = 'Sync pending.';
|
||||||
|
launch_class = "btn-disabled";
|
||||||
|
launch_tip = "Sync pending";
|
||||||
|
break;
|
||||||
|
case 'updating':
|
||||||
|
case 'running':
|
||||||
|
launch_class = "btn-disabled";
|
||||||
|
launch_tip = "Sync running";
|
||||||
|
status_tip = "Sync running. Click to view log.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_inventory_sources && Empty(source)) {
|
||||||
|
// parent has a source, therefore this group should not have a source
|
||||||
|
launch_class = "btn-disabled";
|
||||||
|
status_tip = 'Managed by an external cloud source.';
|
||||||
|
launch_tip = 'Can only be updated by running a sync on the parent group.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_inventory_sources === false && Empty(source)) {
|
||||||
|
launch_class = 'btn-disabled';
|
||||||
|
status_tip = 'Cloud source not configured. Click <i class="fa fa-pencil"></i> to update.';
|
||||||
|
launch_tip = 'Cloud source not configured.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"class": stat_class,
|
||||||
|
"tooltip": status_tip,
|
||||||
|
"status": stat,
|
||||||
|
"launch_class": launch_class,
|
||||||
|
"launch_tip": launch_tip,
|
||||||
|
"schedule_tip": schedule_tip
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSyncStatusMsg.$inject =
|
||||||
|
[ 'Empty' ];
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
export default
|
||||||
|
function GroupsCancelUpdate(Empty, Rest, ProcessErrors, Alert, Wait, Find) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
group = params.group;
|
||||||
|
|
||||||
|
if (scope.removeCancelUpdate) {
|
||||||
|
scope.removeCancelUpdate();
|
||||||
|
}
|
||||||
|
scope.removeCancelUpdate = scope.$on('CancelUpdate', function (e, url) {
|
||||||
|
// Cancel the update process
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post()
|
||||||
|
.success(function () {
|
||||||
|
Wait('stop');
|
||||||
|
//Alert('Inventory Sync Cancelled', 'Request to cancel the sync process was submitted to the task manger. ' +
|
||||||
|
// 'Click the <i class="fa fa-refresh fa-lg"></i> button to monitor the status.', 'alert-info');
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Call to ' + url + ' failed. POST status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeCheckCancel) {
|
||||||
|
scope.removeCheckCancel();
|
||||||
|
}
|
||||||
|
scope.removeCheckCancel = scope.$on('CheckCancel', function (e, last_update, current_update) {
|
||||||
|
// Check that we have access to cancelling an update
|
||||||
|
var url = (current_update) ? current_update : last_update;
|
||||||
|
url += 'cancel/';
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
if (data.can_cancel) {
|
||||||
|
scope.$emit('CancelUpdate', url);
|
||||||
|
//} else {
|
||||||
|
// Wait('stop');
|
||||||
|
// Alert('Cancel Inventory Sync', 'The sync process completed. Click the <i class="fa fa-refresh fa-lg"></i> button to view ' +
|
||||||
|
// 'the latest status.', 'alert-info');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Call to ' + url + ' failed. GET status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel the update process
|
||||||
|
if (Empty(group)) {
|
||||||
|
group = Find({ list: scope.groups, key: 'id', val: id });
|
||||||
|
scope.selected_group_id = group.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group && (group.status === 'running' || group.status === 'pending')) {
|
||||||
|
// We found the group, and there is a running update
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(group.related.inventory_source);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
scope.$emit('CheckCancel', data.related.last_update, data.related.current_update);
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsCancelUpdate.$inject =
|
||||||
|
[ 'Empty', 'Rest', 'ProcessErrors',
|
||||||
|
'Alert', 'Wait', 'Find'
|
||||||
|
];
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
export default
|
||||||
|
function ViewUpdateStatus($state, Rest, ProcessErrors, GetBasePath, Alert, Wait, Empty, Find) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
group_id = params.group_id,
|
||||||
|
group = Find({ list: scope.groups, key: 'id', val: group_id });
|
||||||
|
|
||||||
|
if (scope.removeSourceReady) {
|
||||||
|
scope.removeSourceReady();
|
||||||
|
}
|
||||||
|
scope.removeSourceReady = scope.$on('SourceReady', function(e, source) {
|
||||||
|
|
||||||
|
// Get the ID from the correct summary field
|
||||||
|
var update_id = (source.summary_fields.current_update) ? source.summary_fields.current_update.id : source.summary_fields.last_update.id;
|
||||||
|
|
||||||
|
$state.go('inventorySyncStdout', {id: update_id});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (group) {
|
||||||
|
if (Empty(group.source)) {
|
||||||
|
// do nothing
|
||||||
|
} else if (Empty(group.status) || group.status === "never updated") {
|
||||||
|
Alert('No Status Available', '<div>An inventory sync has not been performed for the selected group. Start the process by ' +
|
||||||
|
'clicking the <i class="fa fa-refresh"></i> button.</div>', 'alert-info', null, null, null, null, true);
|
||||||
|
} else {
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(group.related.inventory_source);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
scope.$emit('SourceReady', data);
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to retrieve inventory source: ' + group.related.inventory_source +
|
||||||
|
' GET returned status: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewUpdateStatus.$inject =
|
||||||
|
[ '$state', 'Rest', 'ProcessErrors',
|
||||||
|
'GetBasePath', 'Alert', 'Wait', 'Empty', 'Find'
|
||||||
|
];
|
||||||
@@ -6,8 +6,18 @@
|
|||||||
|
|
||||||
import GroupAddController from './groups-add.controller';
|
import GroupAddController from './groups-add.controller';
|
||||||
import GroupEditController from './groups-edit.controller';
|
import GroupEditController from './groups-edit.controller';
|
||||||
|
import GetHostsStatusMsg from './factories/get-hosts-status-msg.factory';
|
||||||
|
import GetSourceTypeOptions from './factories/get-source-type-options.factory';
|
||||||
|
import GetSyncStatusMsg from './factories/get-sync-status-msg.factory';
|
||||||
|
import GroupsCancelUpdate from './factories/groups-cancel-update.factory';
|
||||||
|
import ViewUpdateStatus from './factories/view-update-status.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('manageGroups', [])
|
angular.module('manageGroups', [])
|
||||||
|
.factory('GetHostsStatusMsg', GetHostsStatusMsg)
|
||||||
|
.factory('GetSourceTypeOptions', GetSourceTypeOptions)
|
||||||
|
.factory('GetSyncStatusMsg', GetSyncStatusMsg)
|
||||||
|
.factory('GroupsCancelUpdate', GroupsCancelUpdate)
|
||||||
|
.factory('ViewUpdateStatus', ViewUpdateStatus)
|
||||||
.controller('GroupAddController', GroupAddController)
|
.controller('GroupAddController', GroupAddController)
|
||||||
.controller('GroupEditController', GroupEditController);
|
.controller('GroupEditController', GroupEditController);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export default
|
||||||
|
function SetEnabledMsg() {
|
||||||
|
return function(host) {
|
||||||
|
if (host.has_inventory_sources) {
|
||||||
|
// Inventory sync managed, so not clickable
|
||||||
|
host.enabledToolTip = (host.enabled) ? 'Host is available' : 'Host is not available';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Clickable
|
||||||
|
host.enabledToolTip = (host.enabled) ? 'Host is available. Click to toggle.' : 'Host is not available. Click to toggle.';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
export default
|
||||||
|
function SetStatus($filter, SetEnabledMsg, Empty) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
host = params.host,
|
||||||
|
i, html, title;
|
||||||
|
|
||||||
|
function ellipsis(a) {
|
||||||
|
if (a.length > 25) {
|
||||||
|
return a.substr(0,25) + '...';
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function noRecentJobs() {
|
||||||
|
title = 'No job data';
|
||||||
|
html = "<p>No recent job data available for this host.</p>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMsg(host) {
|
||||||
|
var j, job, jobs;
|
||||||
|
|
||||||
|
if (host.has_active_failures === true || (host.has_active_failures === false && host.last_job !== null)) {
|
||||||
|
if (host.has_active_failures === true) {
|
||||||
|
host.badgeToolTip = 'Most recent job failed. Click to view jobs.';
|
||||||
|
host.active_failures = 'error';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
host.badgeToolTip = "Most recent job successful. Click to view jobs.";
|
||||||
|
host.active_failures = 'successful';
|
||||||
|
}
|
||||||
|
if (host.summary_fields.recent_jobs.length > 0) {
|
||||||
|
// build html table of job status info
|
||||||
|
jobs = host.summary_fields.recent_jobs.sort(
|
||||||
|
function(a,b) {
|
||||||
|
// reverse numerical order
|
||||||
|
return -1 * (a - b);
|
||||||
|
});
|
||||||
|
title = "Recent Jobs";
|
||||||
|
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||||
|
html += "<thead>\n";
|
||||||
|
html += "<tr>\n";
|
||||||
|
html += "<th>Status</th>\n";
|
||||||
|
html += "<th>Finished</th>\n";
|
||||||
|
html += "<th>Name</th>\n";
|
||||||
|
html += "</tr>\n";
|
||||||
|
html += "</thead>\n";
|
||||||
|
html += "<tbody>\n";
|
||||||
|
for (j=0; j < jobs.length; j++) {
|
||||||
|
job = jobs[j];
|
||||||
|
html += "<tr>\n";
|
||||||
|
|
||||||
|
// SmartStatus-tooltips are named --success whereas icon-job uses successful
|
||||||
|
var iconStatus = (job.status === 'successful') ? 'success' : 'failed';
|
||||||
|
|
||||||
|
html += "<td><a href=\"#/jobs/" + job.id + "\"><i class=\"fa DashboardList-status SmartStatus-tooltip--" + iconStatus + " icon-job-" +
|
||||||
|
job.status + "\"></i></a></td>\n";
|
||||||
|
|
||||||
|
html += "<td>" + ($filter('longDate')(job.finished)).replace(/ /,'<br />') + "</td>\n";
|
||||||
|
|
||||||
|
html += "<td class=\"break\"><a href=\"#/jobs/" + job.id + "\" " +
|
||||||
|
"aw-tool-tip=\"" + job.status.charAt(0).toUpperCase() + job.status.slice(1) +
|
||||||
|
". Click for details\" data-placement=\"top\">" + ellipsis(job.name) + "</a></td>\n";
|
||||||
|
|
||||||
|
html += "</tr>\n";
|
||||||
|
}
|
||||||
|
html += "</tbody>\n";
|
||||||
|
html += "</table>\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
noRecentJobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (host.has_active_failures === false && host.last_job === null) {
|
||||||
|
host.badgeToolTip = "No job data available.";
|
||||||
|
host.active_failures = 'none';
|
||||||
|
noRecentJobs();
|
||||||
|
}
|
||||||
|
host.job_status_html = html;
|
||||||
|
host.job_status_title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Empty(host)) {
|
||||||
|
// update single host
|
||||||
|
setMsg(host);
|
||||||
|
SetEnabledMsg(host);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// update all hosts
|
||||||
|
for (i=0; i < scope.hosts.length; i++) {
|
||||||
|
setMsg(scope.hosts[i]);
|
||||||
|
SetEnabledMsg(scope.hosts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStatus.$inject =
|
||||||
|
[ '$filter',
|
||||||
|
'SetEnabledMsg',
|
||||||
|
'Empty'
|
||||||
|
];
|
||||||
@@ -6,8 +6,12 @@
|
|||||||
|
|
||||||
import HostsAddController from './hosts-add.controller';
|
import HostsAddController from './hosts-add.controller';
|
||||||
import HostsEditController from './hosts-edit.controller';
|
import HostsEditController from './hosts-edit.controller';
|
||||||
|
import SetStatus from './factories/set-status.factory';
|
||||||
|
import SetEnabledMsg from './factories/set-enabled-msg.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('manageHosts', [])
|
angular.module('manageHosts', [])
|
||||||
|
.factory('SetStatus', SetStatus)
|
||||||
|
.factory('SetEnabledMsg', SetEnabledMsg)
|
||||||
.controller('HostsAddController', HostsAddController)
|
.controller('HostsAddController', HostsAddController)
|
||||||
.controller('HostEditController', HostsEditController);
|
.controller('HostEditController', HostsEditController);
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name helpers.function:Adhoc
|
||||||
|
* @description These routines are shared by adhoc command related controllers.
|
||||||
|
* The content here is very similar to the JobSubmission helper, and in fact,
|
||||||
|
* certain services are pulled from that helper. This leads to an important
|
||||||
|
* point: if you need to create functionality that is shared between the command
|
||||||
|
* and playbook run process, put that code in the JobSubmission helper and make
|
||||||
|
* it into a reusable step (by specifying a callback parameter in the factory).
|
||||||
|
* For a good example of this, please see how the AdhocLaunch factory in this
|
||||||
|
* file utilizes the CheckPasswords factory from the JobSubmission helper.
|
||||||
|
*
|
||||||
|
* #AdhocRelaunch Step 1: preparing the GET to ad_hoc_commands/n/relaunch
|
||||||
|
* The adhoc relaunch process is called from the JobSubmission helper. It is a
|
||||||
|
* separate process from the initial adhoc run becuase of the way the API
|
||||||
|
* endpoints work. For AdhocRelaunch, we have access to the original run and
|
||||||
|
* we can pull the related relaunch URL by knowing the original Adhoc runs ID.
|
||||||
|
*
|
||||||
|
* #AdhocRelaunch Step 2: If we got passwords back, add them
|
||||||
|
* The relaunch URL gives us back the passwords we need to prompt for (if any).
|
||||||
|
* We'll go to step 3 if there are passwords, and step 4 if not.
|
||||||
|
*
|
||||||
|
* #AdhocRelaunch Step 3: PromptForPasswords and the CreateLaunchDialog
|
||||||
|
*
|
||||||
|
* #AdhocRelaunch Step 5: StartAdhocRun
|
||||||
|
*
|
||||||
|
* #AdhocRelaunch Step 6: LaunchJob and navigate to the standard out page.
|
||||||
|
|
||||||
|
* **If you are
|
||||||
|
* 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
|
||||||
|
function AdhocRun($location, $stateParams, LaunchJob, PromptForPasswords,
|
||||||
|
Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, CreateLaunchDialog, $state) {
|
||||||
|
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.removeStartAdhocRun) {
|
||||||
|
scope.removeStartAdhocRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.removeStartAdhocRun = scope.$on('StartAdhocRun', function() {
|
||||||
|
var password,
|
||||||
|
postData={};
|
||||||
|
for (password in scope.passwords) {
|
||||||
|
postData[scope.passwords[password]] = scope[
|
||||||
|
scope.passwords[password]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Re-launch the adhoc job
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.post(postData)
|
||||||
|
.success(function (data) {
|
||||||
|
Wait('stop');
|
||||||
|
if($location.path().replace(/^\//, '').split('/')[0] !== 'jobs') {
|
||||||
|
$state.go('adHocJobStdout', {id: data.id});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to launch adhoc command. POST ' +
|
||||||
|
'returned status: ' + status });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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 });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AdhocRun.$inject =
|
||||||
|
[ '$location','$stateParams', 'LaunchJob',
|
||||||
|
'PromptForPasswords', 'Rest', 'GetBasePath', 'Alert', 'ProcessErrors',
|
||||||
|
'Wait', 'Empty', 'CreateLaunchDialog', '$state'
|
||||||
|
];
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
export default
|
||||||
|
function CheckPasswords(Rest, GetBasePath, ProcessErrors, Empty) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
callback = params.callback,
|
||||||
|
credential = params.credential;
|
||||||
|
|
||||||
|
var passwords = [];
|
||||||
|
if (!Empty(credential)) {
|
||||||
|
Rest.setUrl(GetBasePath('credentials')+credential);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
if(data.kind === "ssh"){
|
||||||
|
if(data.password === "ASK" ){
|
||||||
|
passwords.push("ssh_password");
|
||||||
|
}
|
||||||
|
if(data.ssh_key_unlock === "ASK"){
|
||||||
|
passwords.push("ssh_key_unlock");
|
||||||
|
}
|
||||||
|
if(data.become_password === "ASK"){
|
||||||
|
passwords.push("become_password");
|
||||||
|
}
|
||||||
|
if(data.vault_password === "ASK"){
|
||||||
|
passwords.push("vault_password");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.$emit(callback, passwords);
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to get job template details. GET returned status: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckPasswords.$inject =
|
||||||
|
[ 'Rest',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
'Empty'
|
||||||
|
];
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
export default
|
||||||
|
function CreateLaunchDialog($compile, CreateDialog, Wait, ParseTypeChange) {
|
||||||
|
return function(params) {
|
||||||
|
var buttons,
|
||||||
|
scope = params.scope,
|
||||||
|
html = params.html,
|
||||||
|
// job_launch_data = {},
|
||||||
|
callback = params.callback || 'PlaybookLaunchFinished',
|
||||||
|
// url = params.url,
|
||||||
|
e;
|
||||||
|
|
||||||
|
// html+='<br>job_launch_form.$valid = {{job_launch_form.$valid}}<br>';
|
||||||
|
html+='</form>';
|
||||||
|
$('#password-modal').empty().html(html);
|
||||||
|
$('#password-modal').find('#job_extra_vars').before(scope.helpContainer);
|
||||||
|
e = angular.element(document.getElementById('password-modal'));
|
||||||
|
$compile(e)(scope);
|
||||||
|
|
||||||
|
if(scope.prompt_for_vars===true){
|
||||||
|
ParseTypeChange({ scope: scope, field_id: 'job_extra_vars' , variable: "extra_vars"});
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons = [{
|
||||||
|
label: "Cancel",
|
||||||
|
onClick: function() {
|
||||||
|
$('#password-modal').dialog('close');
|
||||||
|
// scope.$emit('CancelJob');
|
||||||
|
// scope.$destroy();
|
||||||
|
},
|
||||||
|
icon: "fa-times",
|
||||||
|
"class": "btn btn-default",
|
||||||
|
"id": "password-cancel-button"
|
||||||
|
},{
|
||||||
|
label: "Launch",
|
||||||
|
onClick: function() {
|
||||||
|
scope.$emit(callback);
|
||||||
|
$('#password-modal').dialog('close');
|
||||||
|
},
|
||||||
|
icon: "fa-check",
|
||||||
|
"class": "btn btn-primary",
|
||||||
|
"id": "password-accept-button"
|
||||||
|
}];
|
||||||
|
|
||||||
|
CreateDialog({
|
||||||
|
id: 'password-modal',
|
||||||
|
scope: scope,
|
||||||
|
buttons: buttons,
|
||||||
|
width: 620,
|
||||||
|
height: 700, //(scope.passwords.length > 1) ? 700 : 500,
|
||||||
|
minWidth: 500,
|
||||||
|
title: 'Launch Configuration',
|
||||||
|
callback: 'DialogReady',
|
||||||
|
onOpen: function(){
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeDialogReady) {
|
||||||
|
scope.removeDialogReady();
|
||||||
|
}
|
||||||
|
scope.removeDialogReady = scope.$on('DialogReady', function() {
|
||||||
|
$('#password-modal').dialog('open');
|
||||||
|
$('#password-accept-button').attr('ng-disabled', 'job_launch_form.$invalid' );
|
||||||
|
e = angular.element(document.getElementById('password-accept-button'));
|
||||||
|
$compile(e)(scope);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateLaunchDialog.$inject =
|
||||||
|
[ '$compile',
|
||||||
|
'CreateDialog',
|
||||||
|
'Wait',
|
||||||
|
'ParseTypeChange'
|
||||||
|
];
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
export default
|
||||||
|
function InventoryUpdate(PromptForPasswords, LaunchJob, Rest, GetBasePath, ProcessErrors, Alert, Wait) {
|
||||||
|
return function (params) {
|
||||||
|
|
||||||
|
var scope = params.scope,
|
||||||
|
url = params.url,
|
||||||
|
inventory_source;
|
||||||
|
|
||||||
|
if (scope.removeUpdateSubmitted) {
|
||||||
|
scope.removeUpdateSubmitted();
|
||||||
|
}
|
||||||
|
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function () {
|
||||||
|
Wait('stop');
|
||||||
|
if (scope.socketStatus === 'error') {
|
||||||
|
Alert('Sync Started', '<div>The request to start the inventory sync process was submitted. ' +
|
||||||
|
'To monitor the status refresh the page by clicking the <i class="fa fa-refresh"></i> button.</div>', 'alert-info', null, null, null, null, true);
|
||||||
|
if (scope.refreshGroups) {
|
||||||
|
// inventory detail page
|
||||||
|
scope.refreshGroups();
|
||||||
|
}
|
||||||
|
else if (scope.refresh) {
|
||||||
|
scope.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removePromptForPasswords) {
|
||||||
|
scope.removePromptForPasswords();
|
||||||
|
}
|
||||||
|
scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
|
||||||
|
PromptForPasswords({ scope: scope, passwords: inventory_source.passwords_needed_to_update, callback: 'StartTheUpdate' });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeStartTheUpdate) {
|
||||||
|
scope.removeStartTheUpdate();
|
||||||
|
}
|
||||||
|
scope.removeStartTheUpdate = scope.$on('StartTheUpdate', function(e, passwords) {
|
||||||
|
LaunchJob({ scope: scope, url: url, passwords: passwords, callback: 'UpdateSubmitted' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check to see if we have permission to perform the update and if any passwords are needed
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
inventory_source = data;
|
||||||
|
if (data.can_update) {
|
||||||
|
if (data.passwords_needed_to_update) {
|
||||||
|
Wait('stop');
|
||||||
|
scope.$emit('PromptForPasswords');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.$emit('StartTheUpdate', {});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Wait('stop');
|
||||||
|
Alert('Permission Denied', 'You do not have access to run the inventory sync. Please contact your system administrator.',
|
||||||
|
'alert-danger');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to get inventory source ' + url + ' GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryUpdate.$inject =
|
||||||
|
[ 'PromptForPasswords',
|
||||||
|
'LaunchJob',
|
||||||
|
'Rest',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
'Alert',
|
||||||
|
'Wait'
|
||||||
|
];
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
export default
|
||||||
|
function ProjectUpdate(PromptForPasswords, LaunchJob, Rest, $location, GetBasePath, ProcessErrors, Alert, Wait) {
|
||||||
|
return function (params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
project_id = params.project_id,
|
||||||
|
url = GetBasePath('projects') + project_id + '/update/',
|
||||||
|
project;
|
||||||
|
|
||||||
|
if (scope.removeUpdateSubmitted) {
|
||||||
|
scope.removeUpdateSubmitted();
|
||||||
|
}
|
||||||
|
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function() {
|
||||||
|
// Refresh the project list after update request submitted
|
||||||
|
Wait('stop');
|
||||||
|
if (/\d$/.test($location.path())) {
|
||||||
|
//Request submitted from projects/N page. Navigate back to the list so user can see status
|
||||||
|
$location.path('/projects');
|
||||||
|
}
|
||||||
|
if (scope.socketStatus === 'error') {
|
||||||
|
Alert('Update Started', '<div>The request to start the SCM update process was submitted. ' +
|
||||||
|
'To monitor the update status, refresh the page by clicking the <i class="fa fa-refresh"></i> button.</div>', 'alert-info', null, null, null, null, true);
|
||||||
|
if (scope.refresh) {
|
||||||
|
scope.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removePromptForPasswords) {
|
||||||
|
scope.removePromptForPasswords();
|
||||||
|
}
|
||||||
|
scope.removePromptForPasswords = scope.$on('PromptForPasswords', function() {
|
||||||
|
PromptForPasswords({ scope: scope, passwords: project.passwords_needed_to_update, callback: 'StartTheUpdate' });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeStartTheUpdate) {
|
||||||
|
scope.removeStartTheUpdate();
|
||||||
|
}
|
||||||
|
scope.removeStartTheUpdate = scope.$on('StartTheUpdate', function(e, passwords) {
|
||||||
|
LaunchJob({ scope: scope, url: url, passwords: passwords, callback: 'UpdateSubmitted' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check to see if we have permission to perform the update and if any passwords are needed
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
project = data;
|
||||||
|
if (project.can_update) {
|
||||||
|
if (project.passwords_needed_to_updated) {
|
||||||
|
Wait('stop');
|
||||||
|
scope.$emit('PromptForPasswords');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.$emit('StartTheUpdate', {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Alert('Permission Denied', 'You do not have access to update this project. Please contact your system administrator.',
|
||||||
|
'alert-danger');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to lookup project ' + url + ' GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectUpdate.$inject =
|
||||||
|
[ 'PromptForPasswords',
|
||||||
|
'LaunchJob',
|
||||||
|
'Rest',
|
||||||
|
'$location',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
'Alert',
|
||||||
|
'Wait'
|
||||||
|
];
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
export default
|
||||||
|
function PromptForPasswords(CredentialForm) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
callback = params.callback || 'PasswordsAccepted',
|
||||||
|
url = params.url,
|
||||||
|
form = CredentialForm,
|
||||||
|
fld, field,
|
||||||
|
html=params.html || "";
|
||||||
|
|
||||||
|
scope.passwords = params.passwords;
|
||||||
|
|
||||||
|
html += "<div class=\"alert alert-info\">Launching this job requires the passwords listed below. Enter and confirm each password before continuing.</div>\n";
|
||||||
|
|
||||||
|
scope.passwords.forEach(function(password) {
|
||||||
|
// Prompt for password
|
||||||
|
field = form.fields[password];
|
||||||
|
fld = password;
|
||||||
|
scope[fld] = '';
|
||||||
|
html += "<div class=\"form-group prepend-asterisk\">\n";
|
||||||
|
html += "<label for=\"" + fld + "\">" + field.label + "</label>\n";
|
||||||
|
html += "<input type=\"password\" ";
|
||||||
|
html += "ng-model=\"" + fld + '" ';
|
||||||
|
html += 'name="' + fld + '" ';
|
||||||
|
html += "class=\"password-field form-control input-sm\" ";
|
||||||
|
html += (field.associated) ? "ng-change=\"clearPWConfirm('" + field.associated + "')\" " : "";
|
||||||
|
html += "required ";
|
||||||
|
html += " >";
|
||||||
|
// Add error messages
|
||||||
|
html += "<div class=\"error\" ng-show=\"job_launch_form." + fld + ".$dirty && " +
|
||||||
|
"job_launch_form." + fld + ".$error.required\">Please enter a password.</div>\n";
|
||||||
|
html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
|
||||||
|
html += "</div>\n";
|
||||||
|
|
||||||
|
// Add the related confirm field
|
||||||
|
if (field.associated) {
|
||||||
|
fld = field.associated;
|
||||||
|
field = form.fields[field.associated];
|
||||||
|
scope[fld] = '';
|
||||||
|
html += "<div class=\"form-group prepend-asterisk\">\n";
|
||||||
|
html += "<label for=\"" + fld + "\"> " + field.label + "</label>\n";
|
||||||
|
html += "<input type=\"password\" ";
|
||||||
|
html += "ng-model=\"" + fld + '" ';
|
||||||
|
html += 'name="' + fld + '" ';
|
||||||
|
html += "class=\"form-control input-sm\" ";
|
||||||
|
html += "ng-change=\"checkStatus()\" ";
|
||||||
|
html += "required ";
|
||||||
|
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
|
||||||
|
html += "/>";
|
||||||
|
// Add error messages
|
||||||
|
html += "<div class=\"error\" ng-show=\"job_launch_form." + fld + ".$dirty && " +
|
||||||
|
"job_launch_form." + fld + ".$error.required\">Please confirm the password.</span>\n";
|
||||||
|
html += (field.awPassMatch) ? "<span class=\"error\" ng-show=\"job_launch_form." + fld +
|
||||||
|
".$error.awpassmatch\">This value does not match the password you entered previously. Please confirm that password.</div>\n" : "";
|
||||||
|
html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
|
||||||
|
html += "</div>\n";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$emit(callback, html, url);
|
||||||
|
|
||||||
|
// Password change
|
||||||
|
scope.clearPWConfirm = function (fld) {
|
||||||
|
// If password value changes, make sure password_confirm must be re-entered
|
||||||
|
scope[fld] = '';
|
||||||
|
scope.job_launch_form[fld].$setValidity('awpassmatch', false);
|
||||||
|
scope.checkStatus();
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.checkStatus = function() {
|
||||||
|
if (!scope.job_launch_form.$invalid) {
|
||||||
|
$('#password-accept-button').removeAttr('disabled');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#password-accept-button').attr({ "disabled": "disabled" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PromptForPasswords.$inject =
|
||||||
|
[ 'CredentialForm' ];
|
||||||
@@ -7,6 +7,12 @@
|
|||||||
import InitiatePlaybookRun from './job-submission-factories/initiateplaybookrun.factory';
|
import InitiatePlaybookRun from './job-submission-factories/initiateplaybookrun.factory';
|
||||||
import LaunchJob from './job-submission-factories/launchjob.factory';
|
import LaunchJob from './job-submission-factories/launchjob.factory';
|
||||||
import GetSurveyQuestions from './job-submission-factories/getsurveyquestions.factory';
|
import GetSurveyQuestions from './job-submission-factories/getsurveyquestions.factory';
|
||||||
|
import AdhocRun from './job-submission-factories/adhoc-run.factory.js';
|
||||||
|
import CheckPasswords from './job-submission-factories/check-passwords.factory';
|
||||||
|
import CreateLaunchDialog from './job-submission-factories/create-launch-dialog.factory';
|
||||||
|
import InventoryUpdate from './job-submission-factories/inventory-update.factory';
|
||||||
|
import ProjectUpdate from './job-submission-factories/project-update.factory';
|
||||||
|
import PromptForPasswords from './job-submission-factories/prompt-for-passwords.factory';
|
||||||
import submitJob from './job-submission.directive';
|
import submitJob from './job-submission.directive';
|
||||||
import credentialList from './lists/credential/job-sub-cred-list.directive';
|
import credentialList from './lists/credential/job-sub-cred-list.directive';
|
||||||
import inventoryList from './lists/inventory/job-sub-inv-list.directive';
|
import inventoryList from './lists/inventory/job-sub-inv-list.directive';
|
||||||
@@ -16,6 +22,12 @@ export default
|
|||||||
.factory('InitiatePlaybookRun', InitiatePlaybookRun)
|
.factory('InitiatePlaybookRun', InitiatePlaybookRun)
|
||||||
.factory('LaunchJob', LaunchJob)
|
.factory('LaunchJob', LaunchJob)
|
||||||
.factory('GetSurveyQuestions', GetSurveyQuestions)
|
.factory('GetSurveyQuestions', GetSurveyQuestions)
|
||||||
|
.factory('AdhocRun', AdhocRun)
|
||||||
|
.factory('CheckPasswords', CheckPasswords)
|
||||||
|
.factory('CreateLaunchDialog', CreateLaunchDialog)
|
||||||
|
.factory('InventoryUpdate', InventoryUpdate)
|
||||||
|
.factory('ProjectUpdate', ProjectUpdate)
|
||||||
|
.factory('PromptForPasswords', PromptForPasswords)
|
||||||
.directive('submitJob', submitJob)
|
.directive('submitJob', submitJob)
|
||||||
.directive('jobSubCredList', credentialList)
|
.directive('jobSubCredList', credentialList)
|
||||||
.directive('jobSubInvList', inventoryList);
|
.directive('jobSubInvList', inventoryList);
|
||||||
|
|||||||
137
awx/ui/client/src/jobs/factories/delete-job.factory.js
Normal file
137
awx/ui/client/src/jobs/factories/delete-job.factory.js
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
export default
|
||||||
|
function DeleteJob($state, Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert,
|
||||||
|
$filter, i18n) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
job = params.job,
|
||||||
|
callback = params.callback,
|
||||||
|
action, jobs, url, action_label, hdr;
|
||||||
|
|
||||||
|
if (!job) {
|
||||||
|
if (scope.completed_jobs) {
|
||||||
|
jobs = scope.completed_jobs;
|
||||||
|
}
|
||||||
|
else if (scope.running_jobs) {
|
||||||
|
jobs = scope.running_jobs;
|
||||||
|
}
|
||||||
|
else if (scope.queued_jobs) {
|
||||||
|
jobs = scope.queued_jobs;
|
||||||
|
}
|
||||||
|
else if (scope.all_jobs) {
|
||||||
|
jobs = scope.all_jobs;
|
||||||
|
}
|
||||||
|
else if (scope.jobs) {
|
||||||
|
jobs = scope.jobs;
|
||||||
|
}
|
||||||
|
job = Find({list: jobs, key: 'id', val: id });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
||||||
|
url = job.related.cancel;
|
||||||
|
action_label = 'cancel';
|
||||||
|
hdr = i18n._('Cancel');
|
||||||
|
} else {
|
||||||
|
url = job.url;
|
||||||
|
action_label = 'delete';
|
||||||
|
hdr = i18n._('Delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
action = function () {
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
if (action_label === 'cancel') {
|
||||||
|
Rest.post()
|
||||||
|
.success(function () {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, action_label);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$state.reload();
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(obj, status) {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
if (status === 403) {
|
||||||
|
Alert('Error', obj.detail);
|
||||||
|
}
|
||||||
|
// Ignore the error. The job most likely already finished.
|
||||||
|
// ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||||
|
// ' failed. POST returned status: ' + status });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Rest.destroy()
|
||||||
|
.success(function () {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, action_label);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$state.reload();
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (obj, status) {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
if (status === 403) {
|
||||||
|
Alert('Error', obj.detail);
|
||||||
|
}
|
||||||
|
// Ignore the error. The job most likely already finished.
|
||||||
|
//ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||||
|
// ' failed. DELETE returned status: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (scope.removeCancelNotAllowed) {
|
||||||
|
scope.removeCancelNotAllowed();
|
||||||
|
}
|
||||||
|
scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() {
|
||||||
|
Wait('stop');
|
||||||
|
Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeCancelJob) {
|
||||||
|
scope.removeCancelJob();
|
||||||
|
}
|
||||||
|
scope.removeCancelJob = scope.$on('CancelJob', function() {
|
||||||
|
var cancelBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Submit the request to cancel?") + "</div>";
|
||||||
|
var deleteBody = "<div class=\"Prompt-bodyQuery\">" + i18n._("Are you sure you want to delete the job below?") + "</div><div class=\"Prompt-bodyTarget\">#" + id + " " + $filter('sanitize')(job.name) + "</div>";
|
||||||
|
Prompt({
|
||||||
|
hdr: hdr,
|
||||||
|
body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody,
|
||||||
|
action: action,
|
||||||
|
actionText: (action_label === 'cancel' || job.status === 'new') ? "OK" : "DELETE"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (action_label === 'cancel') {
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
if (data.can_cancel) {
|
||||||
|
scope.$emit('CancelJob');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.$emit('CancelNotAllowed');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||||
|
' failed. GET returned: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.$emit('CancelJob');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteJob.$inject =
|
||||||
|
[ '$state', 'Find', 'GetBasePath', 'Rest', 'Wait',
|
||||||
|
'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n'
|
||||||
|
];
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
export default
|
||||||
|
function JobStatusToolTip() {
|
||||||
|
return function(status) {
|
||||||
|
var toolTip;
|
||||||
|
switch (status) {
|
||||||
|
case 'successful':
|
||||||
|
case 'success':
|
||||||
|
toolTip = 'There were no failed tasks.';
|
||||||
|
break;
|
||||||
|
case 'failed':
|
||||||
|
toolTip = 'Some tasks encountered errors.';
|
||||||
|
break;
|
||||||
|
case 'canceled':
|
||||||
|
toolTip = 'Stopped by user request.';
|
||||||
|
break;
|
||||||
|
case 'new':
|
||||||
|
toolTip = 'In queue, waiting on task manager.';
|
||||||
|
break;
|
||||||
|
case 'waiting':
|
||||||
|
toolTip = 'SCM Update or Inventory Update is executing.';
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
toolTip = 'Not in queue, waiting on task manager.';
|
||||||
|
break;
|
||||||
|
case 'running':
|
||||||
|
toolTip = 'Playbook tasks executing.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return toolTip;
|
||||||
|
};
|
||||||
|
}
|
||||||
49
awx/ui/client/src/jobs/factories/jobs-list-update.factory.js
Normal file
49
awx/ui/client/src/jobs/factories/jobs-list-update.factory.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
export default
|
||||||
|
function JobsListUpdate() {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
parent_scope = params.parent_scope,
|
||||||
|
list = params.list;
|
||||||
|
|
||||||
|
scope[list.name].forEach(function(item, item_idx) {
|
||||||
|
var fld, field,
|
||||||
|
itm = scope[list.name][item_idx];
|
||||||
|
|
||||||
|
//if (item.type === 'inventory_update') {
|
||||||
|
// itm.name = itm.name.replace(/^.*?:/,'').replace(/^: /,'');
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Set the item type label
|
||||||
|
if (list.fields.type) {
|
||||||
|
parent_scope.type_choices.forEach(function(choice) {
|
||||||
|
if (choice.value === item.type) {
|
||||||
|
itm.type_label = choice.label;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Set the job status label
|
||||||
|
parent_scope.status_choices.forEach(function(status) {
|
||||||
|
if (status.value === item.status) {
|
||||||
|
itm.status_label = status.label;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (list.name === 'completed_jobs' || list.name === 'running_jobs') {
|
||||||
|
itm.status_tip = itm.status_label + '. Click for details.';
|
||||||
|
}
|
||||||
|
else if (list.name === 'queued_jobs') {
|
||||||
|
itm.status_tip = 'Pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy summary_field values
|
||||||
|
for (field in list.fields) {
|
||||||
|
fld = list.fields[field];
|
||||||
|
if (fld.sourceModel) {
|
||||||
|
if (itm.summary_fields[fld.sourceModel]) {
|
||||||
|
itm[field] = itm.summary_fields[fld.sourceModel][fld.sourceField];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
11
awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js
Normal file
11
awx/ui/client/src/jobs/factories/relaunch-adhoc.factory.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default
|
||||||
|
function RelaunchAdhoc(AdhocRun) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id;
|
||||||
|
AdhocRun({ scope: scope, project_id: id, relaunch: true });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RelaunchAdhoc.$inject =
|
||||||
|
[ 'AdhocRun' ];
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export default
|
||||||
|
function RelaunchInventory(Find, Wait, Rest, InventoryUpdate, ProcessErrors, GetBasePath) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
url = GetBasePath('inventory_sources') + id + '/';
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
InventoryUpdate({
|
||||||
|
scope: scope,
|
||||||
|
url: data.related.update,
|
||||||
|
group_name: data.summary_fields.group.name,
|
||||||
|
group_source: data.source,
|
||||||
|
tree_id: null,
|
||||||
|
group_id: data.group
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' +
|
||||||
|
url + ' GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RelaunchInventory.$inject =
|
||||||
|
[ 'Find', 'Wait', 'Rest',
|
||||||
|
'InventoryUpdate', 'ProcessErrors', 'GetBasePath'
|
||||||
|
];
|
||||||
28
awx/ui/client/src/jobs/factories/relaunch-job.factory.js
Normal file
28
awx/ui/client/src/jobs/factories/relaunch-job.factory.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
export default
|
||||||
|
function RelaunchJob(RelaunchInventory, RelaunchPlaybook, RelaunchSCM, RelaunchAdhoc) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
type = params.type,
|
||||||
|
name = params.name;
|
||||||
|
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' || type === 'workflow_job') {
|
||||||
|
RelaunchPlaybook({ scope: scope, id: id, name: name, job_type: type });
|
||||||
|
}
|
||||||
|
else if (type === 'project_update') {
|
||||||
|
RelaunchSCM({ scope: scope, id: id });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RelaunchJob.$inject =
|
||||||
|
[ 'RelaunchInventory',
|
||||||
|
'RelaunchPlaybook',
|
||||||
|
'RelaunchSCM',
|
||||||
|
'RelaunchAdhoc'
|
||||||
|
];
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export default
|
||||||
|
function RelaunchPlaybook(InitiatePlaybookRun) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
job_type = params.job_type;
|
||||||
|
InitiatePlaybookRun({ scope: scope, id: id, relaunch: true, job_type: job_type });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RelaunchPlaybook.$inject =
|
||||||
|
[ 'InitiatePlaybookRun' ];
|
||||||
11
awx/ui/client/src/jobs/factories/relaunch-scm.factory.js
Normal file
11
awx/ui/client/src/jobs/factories/relaunch-scm.factory.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default
|
||||||
|
function RelaunchSCM(ProjectUpdate) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id;
|
||||||
|
ProjectUpdate({ scope: scope, project_id: id });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RelaunchSCM.$inject =
|
||||||
|
[ 'ProjectUpdate' ];
|
||||||
@@ -6,10 +6,26 @@
|
|||||||
|
|
||||||
import jobsList from './jobs-list.controller';
|
import jobsList from './jobs-list.controller';
|
||||||
import jobsRoute from './jobs.route';
|
import jobsRoute from './jobs.route';
|
||||||
|
import DeleteJob from './factories/delete-job.factory';
|
||||||
|
import JobStatusToolTip from './factories/job-status-tool-tip.factory';
|
||||||
|
import JobsListUpdate from './factories/jobs-list-update.factory';
|
||||||
|
import RelaunchAdhoc from './factories/relaunch-adhoc.factory';
|
||||||
|
import RelaunchInventory from './factories/relaunch-inventory.factory';
|
||||||
|
import RelaunchJob from './factories/relaunch-job.factory';
|
||||||
|
import RelaunchPlaybook from './factories/relaunch-playbook.factory';
|
||||||
|
import RelaunchSCM from './factories/relaunch-scm.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('JobsModule', [])
|
angular.module('JobsModule', [])
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(jobsRoute);
|
$stateExtender.addState(jobsRoute);
|
||||||
}])
|
}])
|
||||||
.controller('JobsList', jobsList);
|
.controller('JobsList', jobsList)
|
||||||
|
.factory('DeleteJob', DeleteJob)
|
||||||
|
.factory('JobStatusToolTip', JobStatusToolTip)
|
||||||
|
.factory('JobsListUpdate', JobsListUpdate)
|
||||||
|
.factory('RelaunchAdhoc', RelaunchAdhoc)
|
||||||
|
.factory('RelaunchInventory', RelaunchInventory)
|
||||||
|
.factory('RelaunchJob', RelaunchJob)
|
||||||
|
.factory('RelaunchPlaybook', RelaunchPlaybook)
|
||||||
|
.factory('RelaunchSCM', RelaunchSCM);
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default ['$scope', '$rootScope', 'ProcessErrors', 'GetBasePath', 'generateList',
|
export default ['$scope', '$rootScope', 'ProcessErrors', 'GetBasePath', 'generateList',
|
||||||
'SelectionInit', 'templateUrl', '$state', 'Rest', '$q', 'Wait', '$window', 'QuerySet', 'UserList',
|
'templateUrl', '$state', 'Rest', '$q', 'Wait', '$window', 'QuerySet', 'UserList',
|
||||||
function($scope, $rootScope, ProcessErrors, GetBasePath, generateList,
|
function($scope, $rootScope, ProcessErrors, GetBasePath, generateList,
|
||||||
SelectionInit, templateUrl, $state, Rest, $q, Wait, $window, qs, UserList) {
|
templateUrl, $state, Rest, $q, Wait, $window, qs, UserList) {
|
||||||
$scope.$on("linkLists", function() {
|
$scope.$on("linkLists", function() {
|
||||||
|
|
||||||
if ($state.current.name.split(".")[1] === "users") {
|
if ($state.current.name.split(".")[1] === "users") {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
export default ['$scope', '$rootScope', '$location', '$log',
|
export default ['$scope', '$rootScope', '$location', '$log',
|
||||||
'$stateParams', 'Rest', 'Alert', 'Prompt',
|
'$stateParams', 'Rest', 'Alert', 'Prompt',
|
||||||
'ReturnToCaller', 'ClearScope', 'OrgProjectList', 'OrgProjectDataset',
|
'ReturnToCaller', 'ClearScope', 'OrgProjectList', 'OrgProjectDataset',
|
||||||
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'ProjectUpdate',
|
'ProcessErrors', 'GetBasePath', 'ProjectUpdate',
|
||||||
'Wait', 'GetChoices', 'Empty', 'Find', 'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state',
|
'Wait', 'GetChoices', 'Empty', 'Find', 'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state',
|
||||||
function($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, Prompt,
|
function($scope, $rootScope, $location, $log, $stateParams, Rest, Alert, Prompt,
|
||||||
ReturnToCaller, ClearScope, OrgProjectList, Dataset,
|
ReturnToCaller, ClearScope, OrgProjectList, Dataset,
|
||||||
ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate,
|
ProcessErrors, GetBasePath, ProjectUpdate,
|
||||||
Wait, GetChoices, Empty, Find, GetProjectIcon, GetProjectToolTip, $filter, $state) {
|
Wait, GetChoices, Empty, Find, GetProjectIcon, GetProjectToolTip, $filter, $state) {
|
||||||
|
|
||||||
var list = OrgProjectList,
|
var list = OrgProjectList,
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
export default ['$scope', '$rootScope', '$location', '$log', '$stateParams', 'OrgTeamList',
|
export default ['$scope', '$rootScope', '$location', '$log', '$stateParams', 'OrgTeamList',
|
||||||
'Rest', 'Alert', 'Prompt', 'OrgTeamsDataset', 'ReturnToCaller', 'ClearScope',
|
'Rest', 'Alert', 'Prompt', 'OrgTeamsDataset', 'ReturnToCaller', 'ClearScope',
|
||||||
'ProcessErrors', 'SetTeamListeners', 'GetBasePath',
|
'ProcessErrors', 'GetBasePath',
|
||||||
'SelectionInit', 'Wait', '$state',
|
'Wait', '$state',
|
||||||
function($scope, $rootScope, $location, $log, $stateParams, OrgTeamList,
|
function($scope, $rootScope, $location, $log, $stateParams, OrgTeamList,
|
||||||
Rest, Alert, Prompt, Dataset, ReturnToCaller, ClearScope,
|
Rest, Alert, Prompt, Dataset, ReturnToCaller, ClearScope,
|
||||||
ProcessErrors, SetTeamListeners, GetBasePath,
|
ProcessErrors, GetBasePath,
|
||||||
SelectionInit, Wait, $state) {
|
Wait, $state) {
|
||||||
|
|
||||||
var list = OrgTeamList,
|
var list = OrgTeamList,
|
||||||
orgBase = GetBasePath('organizations');
|
orgBase = GetBasePath('organizations');
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export default
|
||||||
|
function GetProjectIcon() {
|
||||||
|
return function(status) {
|
||||||
|
var result = '';
|
||||||
|
switch (status) {
|
||||||
|
case 'n/a':
|
||||||
|
case 'ok':
|
||||||
|
case 'never updated':
|
||||||
|
result = 'none';
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
case 'waiting':
|
||||||
|
case 'new':
|
||||||
|
result = 'none';
|
||||||
|
break;
|
||||||
|
case 'updating':
|
||||||
|
case 'running':
|
||||||
|
result = 'running';
|
||||||
|
break;
|
||||||
|
case 'successful':
|
||||||
|
result = 'success';
|
||||||
|
break;
|
||||||
|
case 'failed':
|
||||||
|
case 'missing':
|
||||||
|
case 'canceled':
|
||||||
|
result = 'error';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
export default
|
||||||
|
function GetProjectPath(Alert, Rest, GetBasePath, ProcessErrors) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
master = params.master;
|
||||||
|
|
||||||
|
function arraySort(data) {
|
||||||
|
//Sort nodes by name
|
||||||
|
var i, j, names = [],
|
||||||
|
newData = [];
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
names.push(data[i].value);
|
||||||
|
}
|
||||||
|
names.sort();
|
||||||
|
for (j = 0; j < names.length; j++) {
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].value === names[j]) {
|
||||||
|
newData.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.showMissingPlaybooksAlert = false;
|
||||||
|
|
||||||
|
Rest.setUrl(GetBasePath('config'));
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
var opts = [], i;
|
||||||
|
if (data.project_local_paths) {
|
||||||
|
for (i = 0; i < data.project_local_paths.length; i++) {
|
||||||
|
opts.push({
|
||||||
|
label: data.project_local_paths[i],
|
||||||
|
value: data.project_local_paths[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scope.local_path) {
|
||||||
|
// List only includes paths not assigned to projects, so add the
|
||||||
|
// path assigned to the current project.
|
||||||
|
opts.push({
|
||||||
|
label: scope.local_path,
|
||||||
|
value: scope.local_path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scope.project_local_paths = arraySort(opts);
|
||||||
|
if (scope.local_path) {
|
||||||
|
for (i = 0; scope.project_local_paths.length; i++) {
|
||||||
|
if (scope.project_local_paths[i].value === scope.local_path) {
|
||||||
|
scope.local_path = scope.project_local_paths[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope.base_dir = data.project_base_dir;
|
||||||
|
master.local_path = scope.local_path;
|
||||||
|
master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get
|
||||||
|
// wiped out on form reset.
|
||||||
|
if (opts.length === 0) {
|
||||||
|
// trigger display of alert block when scm_type == manual
|
||||||
|
scope.showMissingPlaybooksAlert = true;
|
||||||
|
}
|
||||||
|
scope.$emit('pathsReady');
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to access API config. GET status: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetProjectPath.$inject =
|
||||||
|
[ 'Alert',
|
||||||
|
'Rest',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors'
|
||||||
|
];
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
export default
|
||||||
|
function GetProjectToolTip(i18n) {
|
||||||
|
return function(status) {
|
||||||
|
var result = '';
|
||||||
|
switch (status) {
|
||||||
|
case 'n/a':
|
||||||
|
case 'ok':
|
||||||
|
case 'never updated':
|
||||||
|
result = i18n._('No SCM updates have run for this project');
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
case 'waiting':
|
||||||
|
case 'new':
|
||||||
|
result = i18n._('Queued. Click for details');
|
||||||
|
break;
|
||||||
|
case 'updating':
|
||||||
|
case 'running':
|
||||||
|
result = i18n._('Running! Click for details');
|
||||||
|
break;
|
||||||
|
case 'successful':
|
||||||
|
result = i18n._('Success! Click for details');
|
||||||
|
break;
|
||||||
|
case 'failed':
|
||||||
|
result = i18n._('Failed. Click for details');
|
||||||
|
break;
|
||||||
|
case 'missing':
|
||||||
|
result = i18n._('Missing. Click for details');
|
||||||
|
break;
|
||||||
|
case 'canceled':
|
||||||
|
result = i18n._('Canceled. Click for details');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetProjectToolTip.$inject =
|
||||||
|
[ 'i18n' ];
|
||||||
@@ -8,12 +8,18 @@ import ProjectsList from './list/projects-list.controller';
|
|||||||
import ProjectsAdd from './add/projects-add.controller';
|
import ProjectsAdd from './add/projects-add.controller';
|
||||||
import ProjectsEdit from './edit/projects-edit.controller';
|
import ProjectsEdit from './edit/projects-edit.controller';
|
||||||
import { N_ } from '../i18n';
|
import { N_ } from '../i18n';
|
||||||
|
import GetProjectPath from './factories/get-project-path.factory';
|
||||||
|
import GetProjectIcon from './factories/get-project-icon.factory';
|
||||||
|
import GetProjectToolTip from './factories/get-project-tool-tip.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('Projects', [])
|
angular.module('Projects', [])
|
||||||
.controller('ProjectsList', ProjectsList)
|
.controller('ProjectsList', ProjectsList)
|
||||||
.controller('ProjectsAdd', ProjectsAdd)
|
.controller('ProjectsAdd', ProjectsAdd)
|
||||||
.controller('ProjectsEdit', ProjectsEdit)
|
.controller('ProjectsEdit', ProjectsEdit)
|
||||||
|
.factory('GetProjectPath', GetProjectPath)
|
||||||
|
.factory('GetProjectIcon', GetProjectIcon)
|
||||||
|
.factory('GetProjectToolTip', GetProjectToolTip)
|
||||||
.config(['$stateProvider', 'stateDefinitionsProvider',
|
.config(['$stateProvider', 'stateDefinitionsProvider',
|
||||||
function($stateProvider, stateDefinitionsProvider) {
|
function($stateProvider, stateDefinitionsProvider) {
|
||||||
let stateDefinitions = stateDefinitionsProvider.$get();
|
let stateDefinitions = stateDefinitionsProvider.$get();
|
||||||
|
|||||||
135
awx/ui/client/src/scheduler/factories/add-schedule.factory.js
Normal file
135
awx/ui/client/src/scheduler/factories/add-schedule.factory.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
export default
|
||||||
|
function AddSchedule($location, $rootScope, $stateParams, SchedulerInit,
|
||||||
|
Wait, GetBasePath, Empty, SchedulePost, $state, Rest,
|
||||||
|
ProcessErrors) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
callback= params.callback,
|
||||||
|
base = params.base || $location.path().replace(/^\//, '').split('/')[0],
|
||||||
|
url = params.url || null,
|
||||||
|
scheduler,
|
||||||
|
job_type;
|
||||||
|
|
||||||
|
job_type = scope.parentObject.job_type;
|
||||||
|
if (!Empty($stateParams.id) && base !== 'system_job_templates' && base !== 'inventories' && !url) {
|
||||||
|
url = GetBasePath(base) + $stateParams.id + '/schedules/';
|
||||||
|
}
|
||||||
|
else if(base === "inventories"){
|
||||||
|
if (!params.url){
|
||||||
|
url = GetBasePath('groups') + $stateParams.id + '/';
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get().
|
||||||
|
then(function (data) {
|
||||||
|
url = data.data.related.inventory_source + 'schedules/';
|
||||||
|
}).catch(function (response) {
|
||||||
|
ProcessErrors(null, response.data, response.status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to get inventory group info. GET returned status: ' +
|
||||||
|
response.status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url = params.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (base === 'system_job_templates') {
|
||||||
|
url = GetBasePath(base) + $stateParams.id + '/schedules/';
|
||||||
|
if(job_type === "cleanup_facts"){
|
||||||
|
scope.isFactCleanup = true;
|
||||||
|
scope.keep_unit_choices = [{
|
||||||
|
"label" : "Days",
|
||||||
|
"value" : "d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Weeks",
|
||||||
|
"value" : "w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label" : "Years",
|
||||||
|
"value" : "y"
|
||||||
|
}];
|
||||||
|
scope.granularity_keep_unit_choices = [{
|
||||||
|
"label" : "Days",
|
||||||
|
"value" : "d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Weeks",
|
||||||
|
"value" : "w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label" : "Years",
|
||||||
|
"value" : "y"
|
||||||
|
}];
|
||||||
|
scope.prompt_for_days_facts_form.keep_amount.$setViewValue(30);
|
||||||
|
scope.prompt_for_days_facts_form.granularity_keep_amount.$setViewValue(1);
|
||||||
|
scope.keep_unit = scope.keep_unit_choices[0];
|
||||||
|
scope.granularity_keep_unit = scope.granularity_keep_unit_choices[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.cleanupJob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
$('#form-container').empty();
|
||||||
|
scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false });
|
||||||
|
if(scope.schedulerUTCTime) {
|
||||||
|
// The UTC time is already set
|
||||||
|
scope.processSchedulerEndDt();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We need to wait for it to be set by angular-scheduler because the following function depends
|
||||||
|
// on it
|
||||||
|
var schedulerUTCTimeWatcher = scope.$watch('schedulerUTCTime', function(newVal) {
|
||||||
|
if(newVal) {
|
||||||
|
// Remove the watcher
|
||||||
|
schedulerUTCTimeWatcher();
|
||||||
|
scope.processSchedulerEndDt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
scheduler.inject('form-container', false);
|
||||||
|
scheduler.injectDetail('occurrences', false);
|
||||||
|
scheduler.clear();
|
||||||
|
scope.$on("htmlDetailReady", function() {
|
||||||
|
$rootScope.$broadcast("ScheduleFormCreated", scope);
|
||||||
|
});
|
||||||
|
scope.showRRuleDetail = false;
|
||||||
|
|
||||||
|
if (scope.removeScheduleSaved) {
|
||||||
|
scope.removeScheduleSaved();
|
||||||
|
}
|
||||||
|
scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) {
|
||||||
|
Wait('stop');
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, data);
|
||||||
|
}
|
||||||
|
$state.go("^", null, {reload: true});
|
||||||
|
});
|
||||||
|
scope.saveSchedule = function() {
|
||||||
|
SchedulePost({
|
||||||
|
scope: scope,
|
||||||
|
url: url,
|
||||||
|
scheduler: scheduler,
|
||||||
|
callback: 'ScheduleSaved',
|
||||||
|
mode: 'add'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#scheduler-tabs li a').on('shown.bs.tab', function(e) {
|
||||||
|
if ($(e.target).text() === 'Details') {
|
||||||
|
if (!scheduler.isValid()) {
|
||||||
|
$('#scheduler-tabs a:first').tab('show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
AddSchedule.$inject =
|
||||||
|
[ '$location', '$rootScope', '$stateParams',
|
||||||
|
'SchedulerInit', 'Wait', 'GetBasePath',
|
||||||
|
'Empty', 'SchedulePost', '$state',
|
||||||
|
'Rest', 'ProcessErrors'
|
||||||
|
];
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
export default
|
||||||
|
function DeleteSchedule(GetBasePath, Rest, Wait, $state,
|
||||||
|
ProcessErrors, Prompt, Find, $location, $filter) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
callback = params.callback,
|
||||||
|
action, schedule, list, url, hdr;
|
||||||
|
|
||||||
|
if (scope.schedules) {
|
||||||
|
list = scope.schedules;
|
||||||
|
}
|
||||||
|
else if (scope.scheduled_jobs) {
|
||||||
|
list = scope.scheduled_jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = GetBasePath('schedules') + id + '/';
|
||||||
|
schedule = Find({list: list, key: 'id', val: id });
|
||||||
|
hdr = 'Delete Schedule';
|
||||||
|
|
||||||
|
action = function () {
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.destroy()
|
||||||
|
.success(function () {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
scope.$emit(callback, id);
|
||||||
|
if (new RegExp('/' + id + '$').test($location.$$url)) {
|
||||||
|
$location.url($location.url().replace(/[/][0-9]+$/, "")); // go to list view
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$state.go('.', null, {reload: true});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
try {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
|
||||||
|
' failed. DELETE returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Prompt({
|
||||||
|
hdr: hdr,
|
||||||
|
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the schedule below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(schedule.name) + '</div>',
|
||||||
|
action: action,
|
||||||
|
actionText: 'DELETE',
|
||||||
|
backdrop: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteSchedule.$inject =
|
||||||
|
[ 'GetBasePath','Rest', 'Wait', '$state',
|
||||||
|
'ProcessErrors', 'Prompt', 'Find', '$location',
|
||||||
|
'$filter'
|
||||||
|
];
|
||||||
154
awx/ui/client/src/scheduler/factories/edit-schedule.factory.js
Normal file
154
awx/ui/client/src/scheduler/factories/edit-schedule.factory.js
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
export default
|
||||||
|
function EditSchedule(SchedulerInit, $rootScope, Wait, Rest, ProcessErrors,
|
||||||
|
GetBasePath, SchedulePost, $state) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
callback = params.callback,
|
||||||
|
schedule, scheduler,
|
||||||
|
url = GetBasePath('schedules') + id + '/';
|
||||||
|
|
||||||
|
delete scope.isFactCleanup;
|
||||||
|
delete scope.cleanupJob;
|
||||||
|
|
||||||
|
function setGranularity(){
|
||||||
|
var a,b, prompt_for_days,
|
||||||
|
keep_unit,
|
||||||
|
granularity,
|
||||||
|
granularity_keep_unit;
|
||||||
|
|
||||||
|
if(scope.cleanupJob){
|
||||||
|
scope.schedulerPurgeDays = Number(schedule.extra_data.days);
|
||||||
|
// scope.scheduler_form.schedulerPurgeDays.$setViewValue( Number(schedule.extra_data.days));
|
||||||
|
}
|
||||||
|
else if(scope.isFactCleanup){
|
||||||
|
scope.keep_unit_choices = [{
|
||||||
|
"label" : "Days",
|
||||||
|
"value" : "d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Weeks",
|
||||||
|
"value" : "w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label" : "Years",
|
||||||
|
"value" : "y"
|
||||||
|
}];
|
||||||
|
scope.granularity_keep_unit_choices = [{
|
||||||
|
"label" : "Days",
|
||||||
|
"value" : "d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Weeks",
|
||||||
|
"value" : "w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label" : "Years",
|
||||||
|
"value" : "y"
|
||||||
|
}];
|
||||||
|
// the API returns something like 20w or 1y
|
||||||
|
a = schedule.extra_data.older_than; // "20y"
|
||||||
|
b = schedule.extra_data.granularity; // "1w"
|
||||||
|
prompt_for_days = Number(_.initial(a,1).join('')); // 20
|
||||||
|
keep_unit = _.last(a); // "y"
|
||||||
|
granularity = Number(_.initial(b,1).join('')); // 1
|
||||||
|
granularity_keep_unit = _.last(b); // "w"
|
||||||
|
|
||||||
|
scope.keep_amount = prompt_for_days;
|
||||||
|
scope.granularity_keep_amount = granularity;
|
||||||
|
scope.keep_unit = _.find(scope.keep_unit_choices, function(i){
|
||||||
|
return i.value === keep_unit;
|
||||||
|
});
|
||||||
|
scope.granularity_keep_unit =_.find(scope.granularity_keep_unit_choices, function(i){
|
||||||
|
return i.value === granularity_keep_unit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scope.removeScheduleFound) {
|
||||||
|
scope.removeScheduleFound();
|
||||||
|
}
|
||||||
|
scope.removeScheduleFound = scope.$on('ScheduleFound', function() {
|
||||||
|
$('#form-container').empty();
|
||||||
|
scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false });
|
||||||
|
scheduler.inject('form-container', false);
|
||||||
|
scheduler.injectDetail('occurrences', false);
|
||||||
|
|
||||||
|
if (!/DTSTART/.test(schedule.rrule)) {
|
||||||
|
schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z');
|
||||||
|
}
|
||||||
|
schedule.rrule = schedule.rrule.replace(/ RRULE:/,';');
|
||||||
|
schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART=');
|
||||||
|
scope.$on("htmlDetailReady", function() {
|
||||||
|
scheduler.setRRule(schedule.rrule);
|
||||||
|
scheduler.setName(schedule.name);
|
||||||
|
$rootScope.$broadcast("ScheduleFormCreated", scope);
|
||||||
|
});
|
||||||
|
scope.showRRuleDetail = false;
|
||||||
|
|
||||||
|
scheduler.setRRule(schedule.rrule);
|
||||||
|
scheduler.setName(schedule.name);
|
||||||
|
if(scope.isFactCleanup || scope.cleanupJob){
|
||||||
|
setGranularity();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (scope.removeScheduleSaved) {
|
||||||
|
scope.removeScheduleSaved();
|
||||||
|
}
|
||||||
|
scope.removeScheduleSaved = scope.$on('ScheduleSaved', function(e, data) {
|
||||||
|
Wait('stop');
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, data);
|
||||||
|
}
|
||||||
|
$state.go("^");
|
||||||
|
});
|
||||||
|
scope.saveSchedule = function() {
|
||||||
|
schedule.extra_data = scope.extraVars;
|
||||||
|
SchedulePost({
|
||||||
|
scope: scope,
|
||||||
|
url: url,
|
||||||
|
scheduler: scheduler,
|
||||||
|
callback: 'ScheduleSaved',
|
||||||
|
mode: 'edit',
|
||||||
|
schedule: schedule
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
|
||||||
|
// Get the existing record
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
schedule = data;
|
||||||
|
try {
|
||||||
|
schedule.extra_data = JSON.parse(schedule.extra_data);
|
||||||
|
} catch(e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
scope.extraVars = data.extra_data === '' ? '---' : '---\n' + jsyaml.safeDump(data.extra_data);
|
||||||
|
|
||||||
|
if(schedule.extra_data.hasOwnProperty('granularity')){
|
||||||
|
scope.isFactCleanup = true;
|
||||||
|
}
|
||||||
|
if (schedule.extra_data.hasOwnProperty('days')){
|
||||||
|
scope.cleanupJob = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.schedule_obj = data;
|
||||||
|
|
||||||
|
scope.$emit('ScheduleFound');
|
||||||
|
})
|
||||||
|
.error(function(data,status){
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
EditSchedule.$inject =
|
||||||
|
[ 'SchedulerInit', '$rootScope', 'Wait', 'Rest',
|
||||||
|
'ProcessErrors', 'GetBasePath', 'SchedulePost', '$state'
|
||||||
|
];
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export default
|
||||||
|
function RRuleToAPI() {
|
||||||
|
return function(rrule) {
|
||||||
|
var response;
|
||||||
|
response = rrule.replace(/(^.*(?=DTSTART))(DTSTART=.*?;)(.*$)/, function(str, p1, p2, p3) {
|
||||||
|
return p2.replace(/\;/,'').replace(/=/,':') + ' ' + 'RRULE:' + p1 + p3;
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
export default
|
||||||
|
function SchedulePost(Rest, ProcessErrors, RRuleToAPI, Wait) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
url = params.url,
|
||||||
|
scheduler = params.scheduler,
|
||||||
|
mode = params.mode,
|
||||||
|
schedule = (params.schedule) ? params.schedule : {},
|
||||||
|
callback = params.callback,
|
||||||
|
newSchedule, rrule, extra_vars;
|
||||||
|
if (scheduler.isValid()) {
|
||||||
|
Wait('start');
|
||||||
|
newSchedule = scheduler.getValue();
|
||||||
|
rrule = scheduler.getRRule();
|
||||||
|
schedule.name = newSchedule.name;
|
||||||
|
schedule.rrule = RRuleToAPI(rrule.toString());
|
||||||
|
schedule.description = (/error/.test(rrule.toText())) ? '' : rrule.toText();
|
||||||
|
|
||||||
|
if (scope.isFactCleanup) {
|
||||||
|
extra_vars = {
|
||||||
|
"older_than": scope.scheduler_form.keep_amount.$viewValue + scope.scheduler_form.keep_unit.$viewValue.value,
|
||||||
|
"granularity": scope.scheduler_form.granularity_keep_amount.$viewValue + scope.scheduler_form.granularity_keep_unit.$viewValue.value
|
||||||
|
};
|
||||||
|
schedule.extra_data = JSON.stringify(extra_vars);
|
||||||
|
} else if (scope.cleanupJob) {
|
||||||
|
extra_vars = {
|
||||||
|
"days" : scope.scheduler_form.schedulerPurgeDays.$viewValue
|
||||||
|
};
|
||||||
|
schedule.extra_data = JSON.stringify(extra_vars);
|
||||||
|
}
|
||||||
|
else if(scope.extraVars){
|
||||||
|
schedule.extra_data = scope.parseType === 'yaml' ?
|
||||||
|
(scope.extraVars === '---' ? "" : jsyaml.safeLoad(scope.extraVars)) : scope.extraVars;
|
||||||
|
}
|
||||||
|
Rest.setUrl(url);
|
||||||
|
if (mode === 'add') {
|
||||||
|
Rest.post(schedule)
|
||||||
|
.success(function(){
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(data, status){
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'POST to ' + url + ' returned: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Rest.put(schedule)
|
||||||
|
.success(function(){
|
||||||
|
if (callback) {
|
||||||
|
scope.$emit(callback, schedule);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Wait('stop');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(data, status){
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'POST to ' + url + ' returned: ' + status });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SchedulePost.$inject =
|
||||||
|
[ 'Rest',
|
||||||
|
'ProcessErrors',
|
||||||
|
'RRuleToAPI',
|
||||||
|
'Wait'
|
||||||
|
];
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
export default
|
||||||
|
function ToggleSchedule(Wait, GetBasePath, ProcessErrors, Rest, $state) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
id = params.id,
|
||||||
|
url = GetBasePath('schedules') + id +'/';
|
||||||
|
|
||||||
|
// Perform the update
|
||||||
|
if (scope.removeScheduleFound) {
|
||||||
|
scope.removeScheduleFound();
|
||||||
|
}
|
||||||
|
scope.removeScheduleFound = scope.$on('ScheduleFound', function(e, data) {
|
||||||
|
data.enabled = (data.enabled) ? false : true;
|
||||||
|
Rest.put(data)
|
||||||
|
.success( function() {
|
||||||
|
Wait('stop');
|
||||||
|
$state.go('.', null, {reload: true});
|
||||||
|
})
|
||||||
|
.error( function(data, status) {
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to update schedule ' + id + ' PUT returned: ' + status });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Wait('start');
|
||||||
|
|
||||||
|
// Get the schedule
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
scope.$emit('ScheduleFound', data);
|
||||||
|
})
|
||||||
|
.error(function(data,status){
|
||||||
|
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ToggleSchedule.$inject =
|
||||||
|
[ 'Wait',
|
||||||
|
'GetBasePath',
|
||||||
|
'ProcessErrors',
|
||||||
|
'Rest',
|
||||||
|
'$state'
|
||||||
|
];
|
||||||
@@ -10,12 +10,24 @@ import editController from './schedulerEdit.controller';
|
|||||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||||
import schedulerDatePicker from './schedulerDatePicker.directive';
|
import schedulerDatePicker from './schedulerDatePicker.directive';
|
||||||
import { N_ } from '../i18n';
|
import { N_ } from '../i18n';
|
||||||
|
import AddSchedule from './factories/add-schedule.factory';
|
||||||
|
import DeleteSchedule from './factories/delete-schedule.factory';
|
||||||
|
import EditSchedule from './factories/edit-schedule.factory';
|
||||||
|
import RRuleToAPI from './factories/r-rule-to-api.factory';
|
||||||
|
import SchedulePost from './factories/schedule-post.factory';
|
||||||
|
import ToggleSchedule from './factories/toggle-schedule.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('scheduler', [])
|
angular.module('scheduler', [])
|
||||||
.controller('schedulerListController', listController)
|
.controller('schedulerListController', listController)
|
||||||
.controller('schedulerAddController', addController)
|
.controller('schedulerAddController', addController)
|
||||||
.controller('schedulerEditController', editController)
|
.controller('schedulerEditController', editController)
|
||||||
|
.factory('AddSchedule', AddSchedule)
|
||||||
|
.factory('DeleteSchedule', DeleteSchedule)
|
||||||
|
.factory('EditSchedule', EditSchedule)
|
||||||
|
.factory('RRuleToAPI', RRuleToAPI)
|
||||||
|
.factory('SchedulePost', SchedulePost)
|
||||||
|
.factory('ToggleSchedule', ToggleSchedule)
|
||||||
.directive('schedulerDatePicker', schedulerDatePicker)
|
.directive('schedulerDatePicker', schedulerDatePicker)
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
// Inventory sync schedule states registered in: awx/ui/client/src/inventories/manage/groups/main.js
|
// Inventory sync schedule states registered in: awx/ui/client/src/inventories/manage/groups/main.js
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
angular.module('ModalDialog', ['Utilities'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
|
angular.module('AWDirectives', ['RestServices', 'Utilities'])
|
||||||
|
|
||||||
// awpassmatch: Add to password_confirm field. Will test if value
|
// awpassmatch: Add to password_confirm field. Will test if value
|
||||||
// matches that of 'input[name="password"]'
|
// matches that of 'input[name="password"]'
|
||||||
|
|||||||
@@ -1,30 +1,5 @@
|
|||||||
/*************************************************
|
|
||||||
* Copyright (c) 2015 Ansible, Inc.
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
|
||||||
*************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name helpers.function:LoadConfig
|
|
||||||
* @description Attempts to load local_config.js. If not found, loads config.js. Then evaluates the loaded
|
|
||||||
* javascript, putting the result in $AnsibleConfig.
|
|
||||||
* LoadConfigHelper
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*jshint evil:true */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('LoadConfigHelper', ['Utilities'])
|
function LoadConfig($log, $rootScope, $http, $location, GetBasePath, ProcessErrors, Rest, Store) {
|
||||||
|
|
||||||
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location', 'GetBasePath',
|
|
||||||
'ProcessErrors', 'Rest', 'Store',
|
|
||||||
function($log, $rootScope, $http, $location, GetBasePath, ProcessErrors, Rest, Store) {
|
|
||||||
return function() {
|
return function() {
|
||||||
|
|
||||||
// These ettings used to be found in config.js, hardcoded now.
|
// These ettings used to be found in config.js, hardcoded now.
|
||||||
@@ -105,4 +80,8 @@ angular.module('LoadConfigHelper', ['Utilities'])
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
]);
|
|
||||||
|
LoadConfig.$inject =
|
||||||
|
[ '$log', '$rootScope', '$http', '$location',
|
||||||
|
'GetBasePath', 'ProcessErrors', 'Rest', 'Store'
|
||||||
|
];
|
||||||
5
awx/ui/client/src/shared/load-config/main.js
Normal file
5
awx/ui/client/src/shared/load-config/main.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import LoadConfig from './load-config.factory';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('loadconfig', [])
|
||||||
|
.factory('LoadConfig', LoadConfig);
|
||||||
@@ -20,6 +20,10 @@ import templateUrl from './template-url/main';
|
|||||||
import RestServices from '../rest/main';
|
import RestServices from '../rest/main';
|
||||||
import stateDefinitions from './stateDefinitions.factory';
|
import stateDefinitions from './stateDefinitions.factory';
|
||||||
import apiLoader from './api-loader';
|
import apiLoader from './api-loader';
|
||||||
|
import variables from './variables/main';
|
||||||
|
import parse from './parse/main';
|
||||||
|
import loadconfig from './load-config/main';
|
||||||
|
import Modal from './Modal';
|
||||||
import 'angular-duration-format';
|
import 'angular-duration-format';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@@ -36,6 +40,10 @@ angular.module('shared', [listGenerator.name,
|
|||||||
templateUrl.name,
|
templateUrl.name,
|
||||||
RestServices.name,
|
RestServices.name,
|
||||||
apiLoader.name,
|
apiLoader.name,
|
||||||
|
variables.name,
|
||||||
|
parse.name,
|
||||||
|
loadconfig.name,
|
||||||
|
Modal.name,
|
||||||
require('angular-cookies'),
|
require('angular-cookies'),
|
||||||
'angular-duration-format'
|
'angular-duration-format'
|
||||||
])
|
])
|
||||||
|
|||||||
5
awx/ui/client/src/shared/parse/main.js
Normal file
5
awx/ui/client/src/shared/parse/main.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import ParseTypeChange from './parse-type-change.factory';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('parse', [])
|
||||||
|
.factory('ParseTypeChange', ParseTypeChange);
|
||||||
93
awx/ui/client/src/shared/parse/parse-type-change.factory.js
Normal file
93
awx/ui/client/src/shared/parse/parse-type-change.factory.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import 'codemirror/lib/codemirror.js';
|
||||||
|
import 'codemirror/mode/javascript/javascript.js';
|
||||||
|
import 'codemirror/mode/yaml/yaml.js';
|
||||||
|
import 'codemirror/addon/lint/lint.js';
|
||||||
|
import 'angular-codemirror/lib/yaml-lint.js';
|
||||||
|
import 'codemirror/addon/edit/closebrackets.js';
|
||||||
|
import 'codemirror/addon/edit/matchbrackets.js';
|
||||||
|
import 'codemirror/addon/selection/active-line.js';
|
||||||
|
|
||||||
|
export default
|
||||||
|
function ParseTypeChange(Alert, AngularCodeMirror) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
field_id = params.field_id,
|
||||||
|
fld = (params.variable) ? params.variable : 'variables',
|
||||||
|
pfld = (params.parse_variable) ? params.parse_variable : 'parseType',
|
||||||
|
onReady = params.onReady,
|
||||||
|
onChange = params.onChange,
|
||||||
|
readOnly = params.readOnly;
|
||||||
|
|
||||||
|
function removeField(fld) {
|
||||||
|
//set our model to the last change in CodeMirror and then destroy CodeMirror
|
||||||
|
scope[fld] = scope[fld + 'codeMirror'].getValue();
|
||||||
|
$('#cm-' + fld + '-container > .CodeMirror').empty().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createField(onChange, onReady, fld) {
|
||||||
|
//hide the textarea and show a fresh CodeMirror with the current mode (json or yaml)
|
||||||
|
|
||||||
|
scope[fld + 'codeMirror'] = AngularCodeMirror(readOnly);
|
||||||
|
scope[fld + 'codeMirror'].addModes(global.$AnsibleConfig.variable_edit_modes);
|
||||||
|
scope[fld + 'codeMirror'].showTextArea({
|
||||||
|
scope: scope,
|
||||||
|
model: fld,
|
||||||
|
element: field_id,
|
||||||
|
lineNumbers: true,
|
||||||
|
mode: scope[pfld],
|
||||||
|
onReady: onReady,
|
||||||
|
onChange: onChange
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide the textarea and show a CodeMirror editor
|
||||||
|
createField(onChange, onReady, fld);
|
||||||
|
|
||||||
|
|
||||||
|
// Toggle displayed variable string between JSON and YAML
|
||||||
|
scope.parseTypeChange = function(model, fld) {
|
||||||
|
var json_obj;
|
||||||
|
if (scope[model] === 'json') {
|
||||||
|
// converting yaml to json
|
||||||
|
try {
|
||||||
|
removeField(fld);
|
||||||
|
json_obj = jsyaml.load(scope[fld]);
|
||||||
|
if ($.isEmptyObject(json_obj)) {
|
||||||
|
scope[fld] = "{}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope[fld] = JSON.stringify(json_obj, null, " ");
|
||||||
|
}
|
||||||
|
createField(onReady, onChange, fld);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Alert('Parse Error', 'Failed to parse valid YAML. ' + e.message);
|
||||||
|
setTimeout( function() { scope.$apply( function() { scope[model] = 'yaml'; createField(onReady, onChange, fld); }); }, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// convert json to yaml
|
||||||
|
try {
|
||||||
|
removeField(fld);
|
||||||
|
json_obj = JSON.parse(scope[fld]);
|
||||||
|
if ($.isEmptyObject(json_obj)) {
|
||||||
|
scope[fld] = '---';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope[fld] = jsyaml.safeDump(json_obj);
|
||||||
|
}
|
||||||
|
createField(onReady, onChange, fld);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Alert('Parse Error', 'Failed to parse valid JSON. ' + e.message);
|
||||||
|
setTimeout( function() { scope.$apply( function() { scope[model] = 'json'; createField(onReady, onChange, fld); }); }, 500 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseTypeChange.$inject =
|
||||||
|
[ 'Alert',
|
||||||
|
'AngularCodeMirror'
|
||||||
|
];
|
||||||
9
awx/ui/client/src/shared/variables/main.js
Normal file
9
awx/ui/client/src/shared/variables/main.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import ParseVariableString from './parse-variable-string.factory';
|
||||||
|
import SortVariables from './sort-variables.factory';
|
||||||
|
import ToJSON from './to-json.factory';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('variables', [])
|
||||||
|
.factory('ParseVariableString', ParseVariableString)
|
||||||
|
.factory('SortVariables', SortVariables)
|
||||||
|
.factory('ToJSON', ToJSON);
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export default
|
||||||
|
function ParseVariableString($log, ProcessErrors, SortVariables) {
|
||||||
|
return function (variables) {
|
||||||
|
var result = "---", json_obj;
|
||||||
|
if (typeof variables === 'string') {
|
||||||
|
if (variables === "{}" || variables === "null" || variables === "" || variables === "\"\"") {
|
||||||
|
// String is empty, return ---
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
json_obj = JSON.parse(variables);
|
||||||
|
json_obj = SortVariables(json_obj);
|
||||||
|
result = jsyaml.safeDump(json_obj);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
$log.debug('Attempt to parse extra_vars as JSON failed. Check that the variables parse as yaml. Set the raw string as the result.');
|
||||||
|
try {
|
||||||
|
// do safeLoad, which well error if not valid yaml
|
||||||
|
json_obj = jsyaml.safeLoad(variables);
|
||||||
|
// but just send the variables
|
||||||
|
result = variables;
|
||||||
|
}
|
||||||
|
catch(e2) {
|
||||||
|
ProcessErrors(null, variables, e2.message, null, { hdr: 'Error!',
|
||||||
|
msg: 'Attempts to parse variables as JSON and YAML failed. Last attempt returned: ' + e2.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($.isEmptyObject(variables) || variables === null) {
|
||||||
|
// Empty object, return ---
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// convert object to yaml
|
||||||
|
try {
|
||||||
|
json_obj = SortVariables(variables);
|
||||||
|
result = jsyaml.safeDump(json_obj);
|
||||||
|
// result = variables;
|
||||||
|
}
|
||||||
|
catch(e3) {
|
||||||
|
ProcessErrors(null, variables, e3.message, null, { hdr: 'Error!',
|
||||||
|
msg: 'Attempt to convert JSON object to YAML document failed: ' + e3.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseVariableString.$inject =
|
||||||
|
[ '$log',
|
||||||
|
'ProcessErrors',
|
||||||
|
'SortVariables'
|
||||||
|
];
|
||||||
23
awx/ui/client/src/shared/variables/sort-variables.factory.js
Normal file
23
awx/ui/client/src/shared/variables/sort-variables.factory.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export default
|
||||||
|
function SortVariables() {
|
||||||
|
return function(variableObj) {
|
||||||
|
var newObj;
|
||||||
|
function sortIt(objToSort) {
|
||||||
|
var i,
|
||||||
|
keys = Object.keys(objToSort),
|
||||||
|
newObj = {};
|
||||||
|
keys = keys.sort();
|
||||||
|
for (i=0; i < keys.length; i++) {
|
||||||
|
if (typeof objToSort[keys[i]] === 'object' && objToSort[keys[i]] !== null && !Array.isArray(objToSort[keys[i]])) {
|
||||||
|
newObj[keys[i]] = sortIt(objToSort[keys[i]]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newObj[keys[i]] = objToSort[keys[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
newObj = sortIt(variableObj);
|
||||||
|
return newObj;
|
||||||
|
};
|
||||||
|
}
|
||||||
80
awx/ui/client/src/shared/variables/to-json.factory.js
Normal file
80
awx/ui/client/src/shared/variables/to-json.factory.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
export default
|
||||||
|
function ToJSON($log, ProcessErrors) {
|
||||||
|
return function(parseType, variables, stringify, reviver) {
|
||||||
|
var json_data,
|
||||||
|
result,
|
||||||
|
tmp;
|
||||||
|
// bracketVar,
|
||||||
|
// key,
|
||||||
|
// lines, i, newVars = [];
|
||||||
|
if (parseType === 'json') {
|
||||||
|
try {
|
||||||
|
// perform a check to see if the user cleared the field completly
|
||||||
|
if(variables.trim() === "" || variables.trim() === "{" || variables.trim() === "}" ){
|
||||||
|
variables = "{}";
|
||||||
|
}
|
||||||
|
//parse a JSON string
|
||||||
|
if (reviver) {
|
||||||
|
json_data = JSON.parse(variables, reviver);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
json_data = JSON.parse(variables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
json_data = {};
|
||||||
|
$log.error('Failed to parse JSON string. Parser returned: ' + e.message);
|
||||||
|
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to parse JSON string. Parser returned: ' + e.message });
|
||||||
|
throw 'Parse error. Failed to parse variables.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if(variables.trim() === "" || variables.trim() === "-" || variables.trim() === "--"){
|
||||||
|
variables = '---';
|
||||||
|
}
|
||||||
|
json_data = jsyaml.safeLoad(variables);
|
||||||
|
if(json_data!==null){
|
||||||
|
// unparsing just to make sure no weird characters are included.
|
||||||
|
tmp = jsyaml.dump(json_data);
|
||||||
|
if(tmp.indexOf('[object Object]')!==-1){
|
||||||
|
throw "Failed to parse YAML string. Parser returned' + key + ' : ' +value + '.' ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
json_data = undefined; // {};
|
||||||
|
// $log.error('Failed to parse YAML string. Parser returned undefined');
|
||||||
|
ProcessErrors(null, variables, e.message, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to parse YAML string. Parser returned undefined'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure our JSON is actually an object
|
||||||
|
if (typeof json_data !== 'object') {
|
||||||
|
ProcessErrors(null, variables, null, null, { hdr: 'Error!',
|
||||||
|
msg: 'Failed to parse variables. Attempted to parse ' + parseType + '. Parser did not return an object.' });
|
||||||
|
// setTimeout( function() {
|
||||||
|
throw 'Parse error. Failed to parse variables.';
|
||||||
|
// }, 1000);
|
||||||
|
}
|
||||||
|
result = json_data;
|
||||||
|
if (stringify) {
|
||||||
|
if(json_data === undefined){
|
||||||
|
result = undefined;
|
||||||
|
}
|
||||||
|
else if ($.isEmptyObject(json_data)) {
|
||||||
|
result = "";
|
||||||
|
} else {
|
||||||
|
// utilize the parsing to get here
|
||||||
|
// but send the raw variable string
|
||||||
|
result = variables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ToJSON.$inject =
|
||||||
|
[ '$log',
|
||||||
|
'ProcessErrors'
|
||||||
|
];
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
export default
|
||||||
|
function CallbackHelpInit($location, GetBasePath, Rest, JobTemplateForm, GenerateForm, $stateParams, ProcessErrors, ParseTypeChange,
|
||||||
|
ParseVariableString, Empty, InventoryList, CredentialList, ProjectList, Wait) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
defaultUrl = GetBasePath('job_templates'),
|
||||||
|
// generator = GenerateForm,
|
||||||
|
form = JobTemplateForm(),
|
||||||
|
// loadingFinishedCount = 0,
|
||||||
|
// base = $location.path().replace(/^\//, '').split('/')[0],
|
||||||
|
master = {},
|
||||||
|
id = $stateParams.job_template_id;
|
||||||
|
// checkSCMStatus, getPlaybooks, callback,
|
||||||
|
// choicesCount = 0;
|
||||||
|
|
||||||
|
CredentialList = _.cloneDeep(CredentialList);
|
||||||
|
|
||||||
|
// The form uses awPopOverWatch directive to 'watch' scope.callback_help for changes. Each time the
|
||||||
|
// popover is activated, a function checks the value of scope.callback_help before constructing the content.
|
||||||
|
scope.setCallbackHelp = function() {
|
||||||
|
scope.callback_help = "<p>With a provisioning callback URL and a host config key a host can contact Tower and request a configuration update using this job " +
|
||||||
|
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
|
||||||
|
"<pre>curl --data \"host_config_key=" + scope.example_config_key + "\" " +
|
||||||
|
scope.callback_server_path + GetBasePath('job_templates') + scope.example_template_id + "/callback/</pre>\n" +
|
||||||
|
"<p>Note the requesting host must be defined in the inventory associated with the job template. If Tower fails to " +
|
||||||
|
"locate the host, the request will be denied.</p>" +
|
||||||
|
"<p>Successful requests create an entry on the Jobs page, where results and history can be viewed.</p>";
|
||||||
|
};
|
||||||
|
|
||||||
|
// The md5 helper emits NewMD5Generated whenever a new key is available
|
||||||
|
if (scope.removeNewMD5Generated) {
|
||||||
|
scope.removeNewMD5Generated();
|
||||||
|
}
|
||||||
|
scope.removeNewMD5Generated = scope.$on('NewMD5Generated', function() {
|
||||||
|
scope.configKeyChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fired when user enters a key value
|
||||||
|
scope.configKeyChange = function() {
|
||||||
|
scope.example_config_key = scope.host_config_key;
|
||||||
|
scope.setCallbackHelp();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set initial values and construct help text
|
||||||
|
scope.callback_server_path = $location.protocol() + '://' + $location.host() + (($location.port()) ? ':' + $location.port() : '');
|
||||||
|
scope.example_config_key = '5a8ec154832b780b9bdef1061764ae5a';
|
||||||
|
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
|
||||||
|
Rest.setUrl(defaultUrl + id);
|
||||||
|
Rest.get()
|
||||||
|
.success(function (data) {
|
||||||
|
scope.job_template_obj = data;
|
||||||
|
scope.name = data.name;
|
||||||
|
var fld, i;
|
||||||
|
for (fld in form.fields) {
|
||||||
|
if (fld !== 'variables' && fld !== 'survey' && data[fld] !== null && data[fld] !== undefined) {
|
||||||
|
if (form.fields[fld].type === 'select') {
|
||||||
|
if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) {
|
||||||
|
for (i = 0; i < scope[fld + '_options'].length; i++) {
|
||||||
|
if (data[fld] === scope[fld + '_options'][i].value) {
|
||||||
|
scope[fld] = scope[fld + '_options'][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope[fld] = data[fld];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope[fld] = data[fld];
|
||||||
|
if(!Empty(data.summary_fields.survey)) {
|
||||||
|
scope.survey_exists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
master[fld] = scope[fld];
|
||||||
|
}
|
||||||
|
if (fld === 'variables') {
|
||||||
|
// Parse extra_vars, converting to YAML.
|
||||||
|
scope.variables = ParseVariableString(data.extra_vars);
|
||||||
|
master.variables = scope.variables;
|
||||||
|
}
|
||||||
|
if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
|
||||||
|
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||||
|
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||||
|
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||||
|
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
|
||||||
|
}
|
||||||
|
if (form.fields[fld].type === 'checkbox_group') {
|
||||||
|
for(var j=0; j<form.fields[fld].fields.length; j++) {
|
||||||
|
scope[form.fields[fld].fields[j].name] = data[form.fields[fld].fields[j].name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Wait('stop');
|
||||||
|
scope.url = data.url;
|
||||||
|
|
||||||
|
scope.survey_enabled = data.survey_enabled;
|
||||||
|
|
||||||
|
scope.ask_variables_on_launch = (data.ask_variables_on_launch) ? true : false;
|
||||||
|
master.ask_variables_on_launch = scope.ask_variables_on_launch;
|
||||||
|
|
||||||
|
scope.ask_limit_on_launch = (data.ask_limit_on_launch) ? true : false;
|
||||||
|
master.ask_limit_on_launch = scope.ask_limit_on_launch;
|
||||||
|
|
||||||
|
scope.ask_tags_on_launch = (data.ask_tags_on_launch) ? true : false;
|
||||||
|
master.ask_tags_on_launch = scope.ask_tags_on_launch;
|
||||||
|
|
||||||
|
scope.ask_skip_tags_on_launch = (data.ask_skip_tags_on_launch) ? true : false;
|
||||||
|
master.ask_skip_tags_on_launch = scope.ask_skip_tags_on_launch;
|
||||||
|
|
||||||
|
scope.ask_job_type_on_launch = (data.ask_job_type_on_launch) ? true : false;
|
||||||
|
master.ask_job_type_on_launch = scope.ask_job_type_on_launch;
|
||||||
|
|
||||||
|
scope.ask_inventory_on_launch = (data.ask_inventory_on_launch) ? true : false;
|
||||||
|
master.ask_inventory_on_launch = scope.ask_inventory_on_launch;
|
||||||
|
|
||||||
|
scope.ask_credential_on_launch = (data.ask_credential_on_launch) ? true : false;
|
||||||
|
master.ask_credential_on_launch = scope.ask_credential_on_launch;
|
||||||
|
|
||||||
|
if (data.host_config_key) {
|
||||||
|
scope.example_config_key = data.host_config_key;
|
||||||
|
}
|
||||||
|
scope.example_template_id = id;
|
||||||
|
scope.setCallbackHelp();
|
||||||
|
|
||||||
|
scope.callback_url = scope.callback_server_path + ((data.related.callback) ? data.related.callback :
|
||||||
|
GetBasePath('job_templates') + id + '/callback/');
|
||||||
|
master.callback_url = scope.callback_url;
|
||||||
|
|
||||||
|
scope.can_edit = data.summary_fields.user_capabilities.edit;
|
||||||
|
|
||||||
|
if (scope.job_type.value === "scan" && (!scope.project || scope.project === "") && (!scope.playbook || scope.playbook === "")) {
|
||||||
|
scope.resetProjectToDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.$emit('jobTemplateLoaded', data.related.cloud_credential, master);
|
||||||
|
})
|
||||||
|
.error(function (data, status) {
|
||||||
|
ProcessErrors(scope, data, status, form, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: 'Failed to retrieve job template: ' + $stateParams.id + '. GET status: ' + status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackHelpInit.$inject =
|
||||||
|
[ '$location', 'GetBasePath', 'Rest', 'JobTemplateForm', 'GenerateForm',
|
||||||
|
'$stateParams', 'ProcessErrors', 'ParseTypeChange', 'ParseVariableString',
|
||||||
|
'Empty', 'InventoryList', 'CredentialList','ProjectList', 'Wait'
|
||||||
|
];
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export default
|
||||||
|
function md5Setup(md5) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope,
|
||||||
|
master = params.master,
|
||||||
|
check_field = params.check_field,
|
||||||
|
default_val = params.default_val;
|
||||||
|
|
||||||
|
scope[check_field] = default_val;
|
||||||
|
master[check_field] = default_val;
|
||||||
|
|
||||||
|
scope.genMD5 = function (fld) {
|
||||||
|
var now = new Date();
|
||||||
|
scope[fld] = md5.createHash('AnsibleWorks' + now.getTime());
|
||||||
|
scope.$emit('NewMD5Generated');
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.toggleCallback = function (fld) {
|
||||||
|
if (scope.allow_callbacks === false) {
|
||||||
|
scope[fld] = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.selectAll = function (fld) {
|
||||||
|
$('input[name="' + fld + '"]').focus().select();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
md5Setup.$inject = [ 'md5' ];
|
||||||
@@ -18,6 +18,8 @@ import workflowControls from './workflows/workflow-controls/main';
|
|||||||
import templatesListRoute from './list/templates-list.route';
|
import templatesListRoute from './list/templates-list.route';
|
||||||
import workflowService from './workflows/workflow.service';
|
import workflowService from './workflows/workflow.service';
|
||||||
import templateCopyService from './copy-template/template-copy.service';
|
import templateCopyService from './copy-template/template-copy.service';
|
||||||
|
import CallbackHelpInit from './job_templates/factories/callback-help-init.factory';
|
||||||
|
import md5Setup from './job_templates/factories/md-5-setup.factory';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesAdd.name,
|
angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesAdd.name,
|
||||||
@@ -27,6 +29,8 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA
|
|||||||
.service('TemplatesService', templatesService)
|
.service('TemplatesService', templatesService)
|
||||||
.service('WorkflowService', workflowService)
|
.service('WorkflowService', workflowService)
|
||||||
.service('TemplateCopyService', templateCopyService)
|
.service('TemplateCopyService', templateCopyService)
|
||||||
|
.factory('CallbackHelpInit', CallbackHelpInit)
|
||||||
|
.factory('md5Setup', md5Setup)
|
||||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||||
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
|
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ const user_type_options = [
|
|||||||
|
|
||||||
export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'GenerateForm',
|
export default ['$scope', '$rootScope', '$stateParams', 'UserForm', 'GenerateForm',
|
||||||
'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
|
'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
|
||||||
'ResetForm', 'Wait', 'CreateSelect2', '$state', '$location', 'i18n',
|
'Wait', 'CreateSelect2', '$state', '$location', 'i18n',
|
||||||
function($scope, $rootScope, $stateParams, UserForm,
|
function($scope, $rootScope, $stateParams, UserForm,
|
||||||
GenerateForm, Rest, Alert, ProcessErrors, ReturnToCaller, ClearScope,
|
GenerateForm, Rest, Alert, ProcessErrors, ReturnToCaller, ClearScope,
|
||||||
GetBasePath, ResetForm, Wait, CreateSelect2, $state, $location, i18n) {
|
GetBasePath, Wait, CreateSelect2, $state, $location, i18n) {
|
||||||
|
|
||||||
ClearScope();
|
ClearScope();
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ const user_type_options = [
|
|||||||
|
|
||||||
export default ['$scope', '$rootScope', '$location',
|
export default ['$scope', '$rootScope', '$location',
|
||||||
'$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath',
|
'$stateParams', 'UserForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath',
|
||||||
'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n',
|
'Wait', 'CreateSelect2', '$state', 'i18n',
|
||||||
function($scope, $rootScope, $location,
|
function($scope, $rootScope, $location,
|
||||||
$stateParams, UserForm, Rest, ProcessErrors,
|
$stateParams, UserForm, Rest, ProcessErrors,
|
||||||
ClearScope, GetBasePath, ResetForm, Wait, CreateSelect2, $state, i18n) {
|
ClearScope, GetBasePath, Wait, CreateSelect2, $state, i18n) {
|
||||||
|
|
||||||
for (var i = 0; i < user_type_options.length; i++) {
|
for (var i = 0; i < user_type_options.length; i++) {
|
||||||
user_type_options[i].label = i18n._(user_type_options[i].label);
|
user_type_options[i].label = i18n._(user_type_options[i].label);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ describe('Controller: workflowResults', () => {
|
|||||||
let $controller;
|
let $controller;
|
||||||
let workflowResults;
|
let workflowResults;
|
||||||
let $rootScope;
|
let $rootScope;
|
||||||
let ParseVariableString;
|
|
||||||
let workflowResultsService;
|
let workflowResultsService;
|
||||||
let $interval;
|
let $interval;
|
||||||
|
|
||||||
@@ -17,8 +16,6 @@ describe('Controller: workflowResults', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(angular.mock.module('VariablesHelper'));
|
|
||||||
|
|
||||||
beforeEach(angular.mock.module('workflowResults', ($provide) => {
|
beforeEach(angular.mock.module('workflowResults', ($provide) => {
|
||||||
['PromptDialog', 'Prompt', 'Wait', 'Rest', '$state', 'ProcessErrors',
|
['PromptDialog', 'Prompt', 'Wait', 'Rest', '$state', 'ProcessErrors',
|
||||||
'InitiatePlaybookRun', 'jobLabels', 'workflowNodes', 'count',
|
'InitiatePlaybookRun', 'jobLabels', 'workflowNodes', 'count',
|
||||||
@@ -30,8 +27,9 @@ describe('Controller: workflowResults', () => {
|
|||||||
$provide.value('workflowData', workflow_job_json);
|
$provide.value('workflowData', workflow_job_json);
|
||||||
$provide.value('workflowDataOptions', workflow_job_options_json);
|
$provide.value('workflowDataOptions', workflow_job_options_json);
|
||||||
$provide.value('ParseTypeChange', function() {});
|
$provide.value('ParseTypeChange', function() {});
|
||||||
|
$provide.value('ParseVariableString', function() {});
|
||||||
$provide.value('i18n', { '_': (a) => { return a; } });
|
$provide.value('i18n', { '_': (a) => { return a; } });
|
||||||
$provide.provider('$stateProvider', { '$get': function() { return function() {} } });
|
$provide.provider('$stateProvider', { '$get': function() { return function() {}; } });
|
||||||
$provide.service('WorkflowService', function($q) {
|
$provide.service('WorkflowService', function($q) {
|
||||||
return {
|
return {
|
||||||
buildTree: function() {
|
buildTree: function() {
|
||||||
@@ -39,14 +37,13 @@ describe('Controller: workflowResults', () => {
|
|||||||
deferred.resolve(treeData);
|
deferred.resolve(treeData);
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(angular.mock.inject(function(_$controller_, _$rootScope_, _ParseVariableString_, _workflowResultsService_, _$interval_){
|
beforeEach(angular.mock.inject(function(_$controller_, _$rootScope_, _workflowResultsService_, _$interval_){
|
||||||
$controller = _$controller_;
|
$controller = _$controller_;
|
||||||
$rootScope = _$rootScope_;
|
$rootScope = _$rootScope_;
|
||||||
ParseVariableString = _ParseVariableString_;
|
|
||||||
workflowResultsService = _workflowResultsService_;
|
workflowResultsService = _workflowResultsService_;
|
||||||
$interval = _$interval_;
|
$interval = _$interval_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user