1
0
Fork 0
mirror of https://github.com/mmumshad/ansible-playable.git synced 2025-03-09 23:38:54 +00:00

Initial Commit

This commit is contained in:
Mumshad Mannambeth 2017-06-07 13:36:44 -04:00
commit c92f737237
273 changed files with 16964 additions and 0 deletions

View file

@ -0,0 +1,89 @@
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './designer.routes';
export class DesignerComponent {
/*@ngInject*/
constructor($scope,Projects,ansible) {
'ngInject';
$scope.selectedInventoryFileName = null;
/**
* Get list of projects from server
*/
$scope.getProjects = function(){
$scope.projects = Projects.resource.query(function(){
if($scope.projects.length){
$scope.selectedProjectID = localStorage.selectedProjectID || $scope.projects[0]._id;
$scope.projectSelected($scope.selectedProjectID)
}
})
};
/**
* On ProjectSelected - set selectedProjectID in cache
* @param projectID
*/
$scope.projectSelected = function(projectID){
localStorage.selectedProjectID = projectID;
$scope.selectedProject = Projects.resource.get({id: projectID},function(){
Projects.selectedProject = $scope.selectedProject;
$scope.listOfInventoryFiles();
$scope.$broadcast('projectLoaded');
})
};
/**
* Get List of inventory files in project root folder
*/
$scope.listOfInventoryFiles = function(){
var rolesTestFolder = null;
/*if(roleName){
rolesTestFolder = projectFolder + '/' + roleName + '/tests'
}*/
ansible.getInventoryList(function(response){
$scope.inventoryFiles = response.data;
console.log($scope.inventoryFiles);
Projects.selectedInventoryFileName = localStorage.selectedInventoryFileName || $scope.inventoryFiles[0];
localStorage.selectedInventoryFileName = $scope.inventoryFiles[0];
$scope.selectedInventoryFileName = localStorage.selectedInventoryFileName
},
function(response){
$scope.err_msg = response.data
},rolesTestFolder)
};
/**
* Set selected inventory file in local cache.
* @param selectedInventoryFileName - Selected inventory file name
*/
$scope.inventoryFileSelected = function(selectedInventoryFileName){
localStorage.selectedInventoryFileName = selectedInventoryFileName;
};
/**
* Main - Get Projects
*/
$scope.getProjects();
}
}
export default angular.module('webAppApp.designer', [uiRouter])
.config(routes)
.component('designer', {
template: require('./designer.html'),
controller: DesignerComponent,
controllerAs: 'designerCtrl'
})
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Component: DesignerComponent', function() {
// load the controller's module
beforeEach(module('webAppApp.designer'));
var DesignerComponent;
// Initialize the controller and a mock scope
beforeEach(inject(function($componentController) {
DesignerComponent = $componentController('designer', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,10 @@
.logconsole {
font:12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas';
color: #fff;
padding:5px;
margin:5px;
font-size:14px;
background: black;
}
.ace_editor { height: 800px; }

View file

@ -0,0 +1,55 @@
<div style="padding:15px;">
<div class="row">
<div class="col-md-2">
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">Projects</div>
<div class="panel-body">
<select class="form-control" ng-model="selectedProjectID" ng-change="projectSelected(selectedProjectID)" ng-options="project._id as project.name for project in projects">
</select>
<div ng-if="selectedProject.ansibleVersion" class="hint">
Ansible Version:{{selectedProject.ansibleVersion}}
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">Inventory Files</div>
<div class="panel-body">
<select class="form-control" ng-model="selectedInventoryFileName" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles" ng-change="inventoryFileSelected(selectedInventoryFileName)">
</select>
<div ng-if="selectedProject.ansibleVersion" class="hint">
An inventory file to work with
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">Menu</div>
<div class="panel-body">
<div class="btn-group-vertical" role="group" aria-label="...">
<button class="btn btn-default" ui-sref="designer.inventory">Inventory</button>
<button class="btn btn-default" ui-sref="designer.playbook">Playbooks</button>
<button class="btn btn-default" ui-sref="designer.roles">Roles</button>
<button class="btn btn-default" ui-sref="designer.file_browser">File Browser</button>
</div>
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
</div>
<div class="col-md-10">
<div ui-view></div>
</div>
</div>
</div>

View file

@ -0,0 +1,10 @@
'use strict';
export default function($stateProvider) {
'ngInject';
$stateProvider
.state('designer', {
url: '/designer',
template: '<designer></designer>'
});
}

View file

@ -0,0 +1,185 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" ng-click="cancel()">&times;</button>
<h4 class="modal-title">Play</h4>
</div>
<div class="modal-body">
<div class="row" id="PlaybookExecutionModal">
<!--<button class="btn btn-default" ng-click="view = (view === 'console' ? 'table' : 'console')">Toggle View</button>-->
<div style="background:black;color:lightgrey;width:100%;padding:20px;" ng-if="result" ng-show="view=='console'">
<p class="logconsole" ng-bind-html="result"></p>
</div>
<!--<div ng-if="ansibleOutputObject" ng-show="view=='table'" style="margin-top:20px;">
&lt;!&ndash;<div class="row" ng-repeat="resultItem in ansibleOutputResult">
<div class="col-md-1"><div class="btn btn-default">{{resultItem.type}}</div></div>
<div class="col-md-5"><div class="btn btn-primary">{{resultItem.name}}</div></div>
<div class="col-md-6"><div class="btn btn-info" ng-if="resultItem.host">{{resultItem.host}}</div></div>
<div class="col-md-offset-1 col-md-2"><div class="btn " ng-class="{'btn-success': resultItem.status == 'ok' || resultItem.status == 'changed', 'btn-danger': resultItem.status == 'fatal', 'btn-warning': resultItem.status == 'skipping'}" ng-if="resultItem.status">Status = {{resultItem.status}}</div></div>
<div class="col-md-3"><div class="btn btn-info" ng-if="resultItem.status_2.trim()">Status2 = {{resultItem.status_2}}</div></div>
<div class="col-md-3"><div class="btn" ng-if="resultItem.resultObject.changed.trim()" ng-class="{'btn-success': resultItem.resultObject.changed == true, 'btn-warning': resultItem.resultObject.changed == false}">Changed = {{resultItem.resultObject.changed}}</div></div>
<div class="col-md-3"><div class="btn" ng-if="resultItem.resultObject.failed" ng-class="{'btn-danger': resultItem.resultObject.failed}">Failed = {{resultItem.resultObject.failed}}</div></div>
<div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.msg" ><p style="background:grey;" class="logconsole">{{resultItem.resultObject.msg}}</p></div>
<div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.formattedStdErr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="resultItem.resultObject.formattedStdErr"></p></div>
<div class="col-md-offset-1 col-md-11" ng-if="resultItem.resultObject.formattedStdOut" ><p style="color:green;" class="logconsole" ng-bind-html="resultItem.resultObject.formattedStdOut"></p></div>
&lt;!&ndash;<div class="col-md-offset-1 col-md-11"><p class="logconsole">{{resultItem.resultString}}</p></div>&ndash;&gt;
<span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading && $last"></span>
</div>&ndash;&gt;
&lt;!&ndash;<div ng-repeat="(host, stats) in ansibleOutputObject.stats">
<span><b>Host : </b>{{host}}</span>
<span class="btn btn-default btn-info">OK <span class="badge">{{stats.ok}}</span></span>
<span class="btn btn-default" ng-class="{'btn-success': stats.changed}" >Changed <span class="badge">{{stats.changed}}</span></span>
<span class="btn btn-default" ng-class="{'btn-warning': stats.skipped}">Skipped <span class="badge">{{stats.skipped}}</span></span>
<span class="btn btn-default" ng-class="{'btn-danger': stats.unreachable}">Unreachable <span class="badge">{{stats.unreachable}}</span></span>
<span class="btn btn-default" ng-class="{'btn-danger': stats.failures}">Failures <span class="badge">{{stats.failures}}</span></span>
</div>&ndash;&gt;
<h3>Run Statistics</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Host</th>
<th>Ok</th>
<th>Changed</th>
<th>Skipped</th>
<th>Unreachable</th>
<th>Failures</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(host, stats) in ansibleOutputObject.stats">
<td>{{host}}</td>
<td>{{stats.ok}}</td>
<td><span ng-class="{'label label-success': stats.changed}">{{stats.changed}}</span></td>
<td><span ng-class="{'label label-warning': stats.skipped}">{{stats.skipped}}</span></td>
<td><span ng-class="{'label label-danger': stats.unreachable}">{{stats.unreachable}}</span></td>
<td><span ng-class="{'label label-danger': stats.failures}">{{stats.failures}}</span></td>
</tr>
</tbody>
</table>
</div>
<div class="row" ng-repeat="playObject in ansibleOutputObject.plays">
&lt;!&ndash;<div class="col-md-3"><div class="btn btn-default">Play - {{playObject.play.name}}</div></div>&ndash;&gt;
<div class="col-md-12">
<uib-accordion close-others="false">
<div uib-accordion-group class="panel-default" is-open="true" ng-repeat="taskObject in playObject.tasks">
<uib-accordion-heading>
Play - {{playObject.play.name}} - Tasks - {{taskObject.task.name}}
<span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading && $last"></span>
</uib-accordion-heading>
<div class="col-md-12" ng-repeat="(hostName, hostObject) in taskObject.hosts">
<div class="col-md-3"><b>Host :</b> {{hostName}}</div>
<div class="col-md-3"><b>Method :</b> {{hostObject.invocation && hostObject.invocation.module_name}}</div>
<div class="col-md-2"><div class="btn" ng-class="{'btn-success': hostObject.changed == true, 'btn-warning': hostObject.changed == false}" style="padding: 2px 5px">Changed <span class="badge">{{hostObject.changed}}</span></div></div>
<div class="col-md-2" ng-if="hostObject.skipped == true"><div class="btn" ng-class="{'btn-warning': hostObject.skipped == true}" style="padding: 2px 5px">Skipped <span class="badge">{{hostObject.skipped}}</span></div></div>
<div class="col-md-1"><div class="btn btn-default" ng-show="hostObject.module_stderr || hostObject.stdout || hostObject.stderr || hostObject.msg" ng-click="hostObject.showLogs = !hostObject.showLogs" style="padding: 2px 5px">Logs</div></div>
<div class="col-md-1"><div class="btn btn-warning" ng-show="hostObject.warnings.length" ng-click="hostObject.showWarnings = !hostObject.showWarnings" style="padding: 2px 5px">Warnings</div></div>
<div class="col-md-2"><div class="btn btn-danger" ng-show="hostObject.rc && hostObject.rc != 0">Return Code - {{hostObject.rc}}</div></div>
<div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.stdout" ><p style="color:grey;" class="logconsole" ng-bind-html="hostObject.stdout | replaceLineBreaks"></p></div>
<div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.msg" ><p style="color:grey;" class="logconsole" ng-bind-html="hostObject.msg | replaceLineBreaks"></p></div>
<div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.stderr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="hostObject.stderr | replaceLineBreaks"></p></div>
<div ng-show="hostObject.showLogs" class="col-md-12" ng-if="hostObject.module_stderr" ><p style="color:#cd616b;" class="logconsole" ng-bind-html="hostObject.module_stderr | replaceLineBreaks"></p></div>
<div ng-show="hostObject.showWarnings" class="col-md-12" ng-if="hostObject.warnings.length" ><p style="color:orangered;" class="logconsole" ng-bind-html="hostObject.warnings.join('\n') | replaceLineBreaks"></p></div>
</div>
</div>
</uib-accordion>
</div>
</div>
</div>-->
</div>
<div class="row" ng-if="!readOnly">
<div class="col-md-3">
<div class="input-group">
<span class="input-group-addon" >Inventory Files</span>
<!--<input ng-model="newPlay.hosts" type="text" class="form-control" placeholder="Playbook Name">-->
<select class="form-control" ng-model="selectedInventoryFile.value" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles" ng-disabled="!inventoryFiles">
</select>
</div>
</div>
<div class="col-md-9">
<btn class="checkbox-inline"><input type="checkbox" ng-true-value="'Check'" ng-false-value="'No_Check'" ng-model="check_mode.value">Check Mode</btn>
<btn class="checkbox-inline"><input type="checkbox" ng-true-value="'verbose'" ng-model="verbose.value">Verbose</btn>
<btn class="checkbox-inline"><input type="checkbox" ng-true-value="'verbose_detail'" ng-model="verbose_detail.value">Verbose Detail</btn>
<btn class="checkbox-inline"><input type="checkbox" ng-model="refreshLog">Refresh Logs</btn>
<btn class="checkbox-inline"><input type="checkbox" ng-model="additional_tags.show">Tags</btn>
<btn class="checkbox-inline"><input type="checkbox" ng-model="additional_tags.vars">Vars</btn>
</div>
<div class="clearfix"></div>
<div class="col-md-6" ng-show="additional_tags.show">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="tag in all_tags">
<td><input type="checkbox" ng-model="tag.selected">
</td>
<td>{{tag.name}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-6" ng-show="additional_tags.show">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Host</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="host in all_hosts">
<td><input type="checkbox" ng-model="host.selected">
</td>
<td>{{host.name}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div ng-show="additional_vars.show">
<complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
</complex-var>
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-success" type="button" ng-if="!readOnly" ng-disabled="!selectedInventoryFile.value || !readyForPlay" ng-click="Run()">Play <span class="fa fa-play" ng-if="!AnsiblePlayBookLoading"></span> <span class="fa fa-spinner fa-spin" ng-if="AnsiblePlayBookLoading"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,475 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function executionController($scope,$sce, $uibModalInstance, $timeout, ansi2html, ansible, tags, selectedProject, selectedPlaybook, selectedPlay, executionType, executionName, readOnly, runData, projectFolder, roleName) {
'ngInject';
$scope.view = 'console';
$scope.selectedInventoryFile = {value:null};
$scope.verbose_detail = {value:null};
$scope.verbose = {value:'verbose'};
$scope.check_mode = {
value: 'No_Check'
};
$scope.additional_tags = {show: false};
$scope.additional_vars = {show: false};
$scope.refreshLog = true;
$scope.all_tags = [];
$scope.all_hosts = [];
$scope.readOnly = readOnly;
$scope.readyForPlay = false;
/**
* Execute Ansible Playbook
*/
$scope.executeAnsiblePlayBook = function(){
$scope.AnsiblePlayBookLoading = true;
var reqBody = {};
//reqBody.inventory_file_contents = inventory_file_contents;
//reqBody.playbook_file_contents = yaml;
//reqBody.tags = tags || [];
reqBody.tags = [];
$scope.all_tags.map(tag => {
if(tag.selected){
if(tag.name)
reqBody.tags.push(tag.name.trim())
}
});
reqBody.limit_to_hosts = [];
$scope.all_hosts.map(host => {
if(host.selected){
if(host.name)
reqBody.limit_to_hosts.push(host.name.trim())
}
});
reqBody.verbose = $scope.verbose_detail.value || $scope.verbose.value;
reqBody.check_mode = $scope.check_mode.value;
reqBody.inventory_file_name = $scope.selectedInventoryFile.value;
if(roleName){
reqBody.inventory_file_name = roleName + '/tests/' + reqBody.inventory_file_name;
}
console.log("Check Mode = " + reqBody.check_mode);
reqBody.selectedPlaybook = selectedPlaybook.playbook;
reqBody.executionType = executionType;
reqBody.executionName = executionName;
reqBody.ansibleEngine = angular.copy(selectedProject.ansibleEngine);
// Override project folder for roles
if(projectFolder)
reqBody.ansibleEngine.projectFolder = projectFolder;
if(selectedPlay && selectedPlay.play)
reqBody.host = selectedPlay.play.hosts;
$scope.result = "Running...";
ansible.executeAnsiblePlayBook(reqBody,function(response){
//$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
$scope.refreshLog = true;
$scope.executionData = response.data;
setTimeout(function(){
$scope.refreshLogs();
},3000);
}, function(response){
if(response.data)
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
$scope.AnsiblePlayBookLoading = false;
console.log("error" + $scope.result)
}, 'PlaybookExecutionModal');
};
/*setTimeout(function(){
$scope.executeAnsiblePlayBook();
},200);*/
/**
* Get logs
*/
$scope.getLogs = function(){
ansible.getLogs($scope.executionData,function(successResponse) {
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(successResponse.data.replace('SCRIPT_FINISHED','')).replace(/\n/g, "<br>"));
if(successResponse.data.indexOf('SCRIPT_FINISHED') > -1){
$scope.refreshLog = false;
$scope.AnsiblePlayBookLoading = false;
}
$scope.processAnsibleOutput(successResponse.data)
});
};
/**
* Refersh Logs
*/
$scope.refreshLogs = function(){
if($scope.logRefreshTimer){
$timeout.cancel( $scope.logRefreshTimer );
}
$scope.getLogs();
$scope.logRefreshTimer = $timeout(
function(){
//$scope.getLogs(tile);
if($scope.refreshLog) {
$scope.refreshLogs();
}
},
10000
);
$scope.$on(
"$destroy",
function( event ) {
$timeout.cancel( $scope.logRefreshTimer );
}
);
};
/**
* Close the modal
*/
$scope.ok = function () {
$uibModalInstance.close(null);
};
/**
* Cancel modal
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
/**
* Run a random command on the server
* TODO: Remove this later.
* @param command
*/
$scope.runCommand = function(command){
command = command || $scope.command;
ansible.executeCommand( command,
function(response){
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>").replace(/ /g,"&nbsp;"));
}, function(response){
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
})
};
/**
* Run Ansible Playbook
* @constructor
*/
$scope.Run = function(){
$scope.executeAnsiblePlayBook();
};
/**
* This is used when viewing the logs from the Runs view
*/
if($scope.readOnly){
$scope.executionData = runData;
$scope.refreshLog = true;
$scope.refreshLogs()
}
/**
* Get List of inventory files
*/
$scope.listOfInventoryFiles = function(){
var rolesTestFolder = null;
if(roleName){
rolesTestFolder = projectFolder + '/' + roleName + '/tests'
}
ansible.getInventoryList(function(response){
$scope.inventoryFiles = response.data;
if($scope.inventoryFiles.length)
$scope.selectedInventoryFile = {value:$scope.inventoryFiles[0]};
/**
* Run Get Tags
*/
if(!readOnly)
$scope.getTags();
},
function(response){
/*$scope.err_msg = response.data;*/
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
$scope.view = 'console'
},rolesTestFolder)
};
$scope.listOfInventoryFiles();
/**
* Get List of Tags based on playbook and inventory file
*/
$scope.getTags = function(){
var inventory_file_name = $scope.selectedInventoryFile.value;
if(roleName){
inventory_file_name = roleName + '/tests/' + inventory_file_name;
}
var selectedPlaybookName = selectedPlaybook.playbook;
var ansibleEngine = angular.copy(selectedProject.ansibleEngine);
// Override project folder for roles
if(projectFolder)
ansibleEngine.projectFolder = projectFolder;
ansible.getTagList(selectedPlaybookName,inventory_file_name,ansibleEngine,
function(response){
console.log(response.data)
/*var re = /TAGS: \[(.*)\]/g;
var m;
var all_tags = []
while ((m = re.exec(response.data)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
if(m[1])
all_tags.push(m[1])
}
$scope.all_tags = all_tags.join(',').split(',');*/
if(!response.data.playbooks)return null;
var playbooks = response.data.playbooks;
$scope.all_hosts = [];
$scope.all_tags = [];
angular.forEach(playbooks, playbook => {
angular.forEach(playbook.plays, play => {
$scope.all_hosts = $scope.all_hosts.concat(play.hosts);
$scope.all_tags = $scope.all_tags.concat(play.tags);
angular.forEach(play.tasks, task => {
$scope.all_tags = $scope.all_tags.concat(task.tags);
})
})
});
// Get Unique List of tags
$scope.all_tags = Array.from(new Set($scope.all_tags));
// Get Unique List of hosts
$scope.all_hosts = Array.from(new Set($scope.all_hosts));
$scope.all_hosts = $scope.all_hosts.map(host => {return {name:host,selected:false}});
$scope.all_tags = $scope.all_tags.map(tag => {return {name:tag,selected:false}});
if(tags){
angular.forEach(tags, tag => {
var tag_found = false;
angular.forEach($scope.all_tags, (all_tag,index) => {
if(tag == all_tag.name){
tag_found = true;
all_tag.selected = true
}
});
if(!tag_found)
$scope.all_tags.push({name:tag,selected:true})
})
}
$scope.readyForPlay = true;
},
function(error){
//console.log(error.data)
//$scope.err_msg = error.data;
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(error.data).replace(/\n/g, "<br>"));
$scope.view = 'console'
})
};
/**
* Process Ansible Output and show graphically
* @param ansibleOutput
*/
$scope.processAnsibleOutput_old = function(ansibleOutput){
$scope.ansibleOutputResult = [];
//https://regex101.com/r/yD6lZ6/1
//var re = /(PLAY|TASK) \[(.*)\] (.*)\n(.*?):([^]*?)(?=TASK|PLAY)/gm;
//var re = /(PLAY|TASK) \[(.*)\] (.*)\n(?:(.*?)\s?(.*): \[(.*)\](.*)=> ([^]*?)(?=TASK|PLAY)|(.*?): \[(.*)\](.*)|(.*)|(?=TASK|PLAY))/gm
//var re = /(PLAY|TASK) \[(.*)\] (.*)\n(?:(.*?)\s?(.*): \[(.*)\](.*)=> ([^]*?)(?=\n\n)|(.*?): \[(.*)\](.*)|(.*)|(?=\n\n))/gm;
var re = /({[^]+})/g
var m;
while ((m = re.exec(ansibleOutput)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
var type = m[1]; //TASK,PLAY
var name = m[2]; // ansible-role-vra : debug delete instance
var status = m[5]; //ok, skipping, failed
var host = m[6]; //localhost , localhost -> localhost
var status_2 = m[7];
var result = m[8];
if(result){
//var result_object_string = result.replace(/({[^]+})[^]+/,"$1");
var result_object_string = result;
var resultObject = null;
try{
resultObject = JSON.parse(result_object_string);
//resultObject.formattedStdErr = resultObject.stderr.replace(/\\r\\n/g,'<br>').replace(/\\n/g, "<br>");
resultObject.formattedStdErr = resultObject.stderr.replace(/\n/g,'<br>');
resultObject.formattedStdOut = resultObject.stderr.replace(/\n/g,'<br>');
}catch(e){
console.log("Error converting ansible output result object to javascript")
}
}
$scope.ansibleOutputResult.push({
type:type,
name:name,
status:status,
host:host,
status_2:status_2,
resultString:result,
resultObject:resultObject
})
}
}
$scope.processAnsibleOutput = function(ansibleOutput){
$scope.ansibleOutputResult = [];
$scope.ansibleOutputObject = {
'plays' : [],
'stats' : {}
}
//var re = /(.*{[^]+}.*)/g;
var re = /--------BEGIN--------([^]+?)--------END--------/gm;
var m;
while ((m = re.exec(ansibleOutput)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
try{
//$scope.ansibleOutputObject = JSON.parse(m[1]);
var resultItem = JSON.parse(m[1]);
if('play' in resultItem){
$scope.ansibleOutputObject.plays.push(resultItem);
} else if('task' in resultItem){
var current_play = $scope.ansibleOutputObject.plays[$scope.ansibleOutputObject.plays.length-1]
var newTask = true;
angular.forEach(current_play.tasks, (task, index)=>{
if(task.task.id === resultItem.task.id){
newTask = false;
current_play.tasks[index] = resultItem
}
})
if(newTask)
current_play.tasks.push(resultItem);
} else if('stats' in resultItem){
$scope.ansibleOutputObject.stats = resultItem.stats;
}
}catch(e){
console.log("Error parsing ansible output" + e);
}
//var plays = $scope.ansibleOutputObject.plays;
}
console.log($scope.ansibleOutputObject);
/*while ((m = re.exec(ansibleOutput)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
var type = m[1]; //TASK,PLAY
var name = m[2]; // ansible-role-vra : debug delete instance
var status = m[5]; //ok, skipping, failed
var host = m[6]; //localhost , localhost -> localhost
var status_2 = m[7];
var result = m[8];
if(result){
//var result_object_string = result.replace(/({[^]+})[^]+/,"$1");
var result_object_string = result;
var resultObject = null;
try{
resultObject = JSON.parse(result_object_string);
//resultObject.formattedStdErr = resultObject.stderr.replace(/\\r\\n/g,'<br>').replace(/\\n/g, "<br>");
resultObject.formattedStdErr = resultObject.stderr.replace(/\n/g,'<br>');
resultObject.formattedStdOut = resultObject.stderr.replace(/\n/g,'<br>');
}catch(e){
console.log("Error converting ansible output result object to javascript")
}
}
$scope.ansibleOutputResult.push({
type:type,
name:name,
status:status,
host:host,
status_2:status_2,
resultString:result,
resultObject:resultObject
})
}*/
}
}
export default angular.module('webAppApp.execution', [])
.controller('ExecutionController', executionController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: ExecutionCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.execution'));
var ExecutionCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
ExecutionCtrl = $controller('ExecutionCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,104 @@
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './file_browser.routes';
export class FileBrowserComponent {
/*@ngInject*/
constructor($scope,ansible,editor) {
'ngInject';
$scope.treeOptions = {
nodeChildren: "children",
dirSelectable: true,
isLeaf: function (node) {
return !(node.type === 'directory');
},
injectClasses: {
ul: "a1",
li: "a2",
liSelected: "a7",
iExpanded: "a3",
iCollapsed: "a4",
iLeaf: "a5",
label: "a6",
labelSelected: "a8"
}
};
$scope.editContent = false;
$scope.selectedFile = {showSource: true};
var loadProjectFiles = function(){
ansible.getProjectFiles(function(response){
$scope.projectFiles = response.data;
},function(error){
});
};
$scope.$on('projectLoaded',function(){
loadProjectFiles();
});
if($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
loadProjectFiles();
}
/**
* Show selected item in the tree
* @param file
* @param parent
*/
$scope.showSelected = function (file, parent) {
if (file.children) {
$scope.selectedFile.content = JSON.stringify(file, null, '\t');
$scope.docType = 'json';
$scope.selectedFile.tasks = null;
return;
}
var command = 'cat "' + file.path + '"';
$scope.showSource = true;
$scope.markdownContent = '';
$scope.docType = 'text';
$scope.selectedFile.content = 'Loading..';
$scope.selectedFile.tasks = null;
$scope.selectedFileName = file.name;
$scope.selectedFilePath = file.path;
$scope.parentNode = parent;
ansible.executeCommand(command,
function (response) {
console.log(response.data)
editor.setContentAndType(response.data, file, $scope.selectedFile);
var parentDirectory = file.path.replace(/^(.+)\/(.+)\/([^/]+)$/, "$2");
if (parentDirectory == 'tasks') {
$scope.selectedFile.tasks = YAML.parse(response.data) || [];
}
if (parentDirectory == 'group_vars' || parentDirectory == 'host_vars') {
$scope.selectedFile.docType = 'yaml';
}
//$scope.selectedFile.content = response.data;
}, function (response) {
$scope.selectedFile.content = response.data;
})
};
}
}
export default angular.module('webAppApp.file_browser', [uiRouter])
.config(routes)
.component('fileBrowser', {
template: require('./file_browser.html'),
controller: FileBrowserComponent,
controllerAs: 'fileBrowserCtrl'
})
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Component: FileBrowserComponent', function() {
// load the controller's module
beforeEach(module('webAppApp.file_browser'));
var FileBrowserComponent;
// Initialize the controller and a mock scope
beforeEach(inject(function($componentController) {
FileBrowserComponent = $componentController('file_browser', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,33 @@
<div class="row">
<h3>File Browser</h3>
<div class="col-md-6">
<treecontrol ng-show="projectFiles.children" class="tree-classic"
tree-model="projectFiles.children"
options="treeOptions"
on-selection="showSelected(node, $parentNode)"
selected-node="selectedFile"
filter-expression="{name: '!.git'}">
{{node.name}}
</treecontrol>
</div>
<div class="col-md-6">
<button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && !selectedFile.showSource" ng-click="selectedFile.showSource = true">
Show Source
</button>
<button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && selectedFile.showSource" ng-click="selectedFile.showSource = false">
Hide Source
</button>
<!--{{docType}}-->
<!--{{selectedFile.showSource}}-->
<div ng-show="selectedFile.showSource" ng-readonly="!editContent"
ui-ace="{theme:'twilight',document:selectedFile.docType,mode:selectedFile.docType,onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedFile.content">
</div>
<div ng-show="!selectedFile.showSource" btf-markdown="selectedFile.markdownContent">
</div>
</div>
</div>

View file

@ -0,0 +1,10 @@
'use strict';
export default function($stateProvider) {
'ngInject';
$stateProvider
.state('designer.file_browser', {
url: '/file_browser',
template: '<file-browser></file-browser>'
});
}

View file

@ -0,0 +1,278 @@
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './inventory.routes';
export class InventoryComponent {
/*@ngInject*/
constructor($scope, $uibModal, ansible) {
'ngInject';
$scope.selectedInventory = {inventory: "", content: ""};
$scope.editInventory = {value: false};
$scope.selectedGroup = {group: null};
$scope.selectedHost = {host: null};
$scope.complexVar = {};
$scope.$on('projectLoaded', function () {
$scope.getInventorys()
});
//To fix a warning message in console
$scope.aceLoaded = function(_editor){
_editor.$blockScrolling = Infinity;
};
// --------------------------------------- PLAYBOOKS ----------------
$scope.getInventorys = function () {
ansible.getInventoryList(
function (response) {
$scope.inventorys = response.data;
},
function (response) {
console.log(response.data)
}
)
};
if ($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
$scope.getInventorys()
}
$scope.loadingModuleCode = false;
$scope.showInventoryCode = function (inventory_name) {
$scope.loadingModuleCode = true;
if (!inventory_name) {
$scope.selectedInventory.content = "Select a module";
return;
}
ansible.readInventory(inventory_name, function (response) {
$scope.loadingModuleCode = false;
$scope.selectedInventory.content = response.data.split("Stream :: close")[0];
$scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
$scope.inventory_data_json['name'] = inventory_name
});
};
$scope.$watch('selectedInventory.inventory', function (newValue, oldValue) {
if (newValue && newValue !== oldValue) {
$scope.selectedInventory.content = "Loading Code...";
$scope.showInventoryCode(newValue)
}
});
$scope.showCreatInventoryModal = function () {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/inventory/new_inventory/new_inventory.html',
controller: 'NewInventoryController',
size: 'md',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
selectedProject: function () {
return $scope.$parent.selectedProject
}
}
});
modalInstance.result.then(function () {
$scope.getInventorys();
}, function () {
});
};
$scope.editGroup = function (group) {
$scope.showCreateGroupModal(group);
};
$scope.showCreateGroupModal = function (editGroup) {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/inventory/new_group/new_group.html',
controller: 'NewGroupController',
size: 'lg',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
selectedProject: function () {
return $scope.$parent.selectedProject
},
editGroup: function () {
return editGroup
}
}
});
modalInstance.result.then(function (group) {
if(!editGroup)$scope.addGroup(group);
else $scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
$scope.saveInventory();
}, function () {
});
};
$scope.editHost = function (host) {
var hostMemberOfGroups = getHostMemberOfGroups(host);
$scope.showCreateHostModal({name: host, members: hostMemberOfGroups.join(',')});
};
$scope.showCreateHostModal = function (editHost) {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/inventory/new_host/new_host.html',
controller: 'NewHostController',
size: 'lg',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
selectedProject: function () {
return $scope.$parent.selectedProject
},
editHost: function () {
return editHost
}
}
});
modalInstance.result.then(function (host) {
$scope.addHost(host);
$scope.saveInventory();
}, function () {
});
};
$scope.saveInventory = function () {
$scope.saveInventoryLoading = true;
ansible.createInventory($scope.selectedInventory.inventory, $scope.selectedInventory.content,
function (response) {
$scope.saveInventoryLoading = false;
$scope.editInventory.value = false;
},
function (response) {
$scope.saveInventoryLoading = false;
$scope.err_msg = response.data;
})
};
$scope.deleteInventory = function () {
$scope.deleteInventoryLoading = true;
ansible.deleteInventory($scope.selectedInventory.inventory,
function (response) {
$scope.deleteInventoryLoading = false;
$scope.selectedInventory.inventory = "";
$scope.getInventorys();
},
function (response) {
$scope.deleteInventoryLoading = false;
$scope.err_msg = response.data;
})
};
$scope.addGroup = function (group) {
$scope.inventory_data_json.groups.push(group);
$scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
// To refresh All Hosts list
$scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content)
};
$scope.addHost = function (host) {
if ($scope.inventory_data_json.hosts.indexOf(host.name) < 0)
$scope.inventory_data_json.hosts.push(host.name);
var host_member_of_groups = host.members.split(',');
angular.forEach($scope.inventory_data_json.groups, function (group) {
if ((host_member_of_groups.indexOf(group.name)) > -1 && group.members.indexOf(host.name) < 0) {
group.members.push(host.name)
}
});
$scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
// To refresh All Hosts list
$scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content)
};
$scope.deleteGroup = function (index) {
$scope.inventory_data_json.groups.splice(index, 1);
$scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
// To refresh All Hosts list
$scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
$scope.saveInventory();
};
$scope.deleteHost = function (index, group) {
var hostname = $scope.inventory_data_json.hosts[index];
$scope.inventory_data_json.hosts.splice(index, 1);
angular.forEach($scope.inventory_data_json.groups, function (group) {
var memberIndex = group.members.indexOf(hostname)
if (memberIndex > -1) {
group.members.splice(memberIndex, 1)
}
});
$scope.selectedInventory.content = ansible.jsonToAnsibleInventoryIni($scope.inventory_data_json);
// To refresh All Hosts list
$scope.inventory_data_json = ansible.parseINIString($scope.selectedInventory.content);
$scope.saveInventory();
};
var getHostMemberOfGroups = function (host) {
var groups = [];
angular.forEach($scope.inventory_data_json.groups, function (group) {
var memberIndex = group.members.indexOf(host);
if (memberIndex > -1) {
groups.push(group.name)
}
});
return groups;
};
}
}
export default angular.module('webAppApp.inventory', [uiRouter])
.config(routes)
.component('inventory', {
template: require('./inventory.html'),
controller: InventoryComponent,
controllerAs: 'inventoryCtrl'
})
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Component: InventoryComponent', function() {
// load the controller's module
beforeEach(module('webAppApp.inventory'));
var InventoryComponent;
// Initialize the controller and a mock scope
beforeEach(inject(function($componentController) {
InventoryComponent = $componentController('inventory', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,154 @@
<div class="col-md-8">
<button class="btn btn-default" ng-click="showCreatInventoryModal()">Create Inventory <span class="fa fa-plus"></span></button>
<!--<button class="btn btn-default" ng-if="!editInventory.value" ng-click="editInventory.value = true">Edit <span ng-if="!saveInventoryLoading" class="fa fa-edit"></span></button>
<button class="btn btn-primary" ng-if="editInventory.value" ng-disabled="!selectedInventory.inventory" ng-click="saveInventory()">Save <span ng-if="!saveInventoryLoading" class="fa fa-save"></span><span ng-if="saveInventoryLoading" class="fa fa-spinner fa-spin"></span></button>-->
<button class="btn btn-danger" confirm="Are you sure you want to delete inventory? " ng-disabled="!selectedInventory.inventory" ng-click="deleteInventory()">Delete <span ng-if="!deleteInventoryLoading" class="fa fa-trash-o"></span><span ng-if="deleteInventoryLoading" class="fa fa-spinner fa-spin"></span></button>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="inventory in inventorys">
<td><input name="playGroup" type="radio" ng-model="selectedInventory.inventory" ng-value="inventory">
</td>
<td>{{inventory}}</td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">Groups - {{inventory_data_json.groups.length}}</div>
<div class="panel-body">
<button class="btn btn-default" ng-disabled="!selectedInventory.inventory" ng-click="showCreateGroupModal()">Create Group <span class="fa fa-plus"></span></button>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Members</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="group in inventory_data_json.groups">
<td><input name="groupsRadioGroup" type="radio" ng-model="selectedGroup.group" ng-value="group">
</td>
<td>{{group.name}}</td>
<td>{{group.members.length}}</td>
<td>
<div class="btn-group">
<label ng-show="group.type == 'userdefined'" class="btn btn-default btn-xs" ng-click="editGroup(group)" ><span class="fa fa-edit"></span></label>
<label ng-show="group.type == 'userdefined'" class="btn btn-danger btn-xs" confirm="Are you sure you want to delete this group?" ng-click="deleteGroup($index)" ><span class="fa fa-trash-o"></span></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="panel" ng-show="selectedGroup.group.members">
<div class="panel panel-default">
<div class="panel-heading">Group Members - {{selectedGroup.group.name}}</div>
<div class="panel-body">
<div class="table-responsive" >
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="host in selectedGroup.group.members">
<td><input name="hostsRadioGroup" type="radio" ng-model="selectedHost.host" ng-value="host">
</td>
<td>{{host}}</td>
<td>
<div class="btn-group">
<label class="btn btn-danger btn-xs" confirm="Are you sure you want to delete host and remove it from all groups?" ng-click="deleteHost($index)" ><span class="fa fa-trash-o"></span></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">Hosts - {{inventory_data_json.hosts.length}}</div>
<div class="panel-body">
<button class="btn btn-default" ng-disabled="!selectedInventory.inventory" ng-click="showCreateHostModal()">Create Host <span class="fa fa-plus"></span></button>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="host in inventory_data_json.hosts">
<td><input name="hostsRadioGroup" type="radio" ng-model="selectedHost.host" ng-value="host">
</td>
<td>{{host}}</td>
<td>
<div class="btn-group">
<label class="btn btn-default btn-xs" ng-click="editHost(host)" ><span class="fa fa-edit"></span></label>
<label class="btn btn-danger btn-xs" confirm="Are you sure you want to delete host and remove it from all groups?" ng-click="deleteHost($index)" ><span class="fa fa-trash-o"></span></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
</div>
</div>
</div>
<div class="col-md-4">
<div ng-readonly="!editInventory.value" ui-ace="{theme:'twilight',document:'INI',mode:'ini',onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedInventory.content">
</div>
</div>

View file

@ -0,0 +1,10 @@
'use strict';
export default function($stateProvider) {
'ngInject';
$stateProvider
.state('designer.inventory', {
url: '/inventory',
template: '<inventory></inventory>'
});
}

View file

@ -0,0 +1,82 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newGroupController($scope,$uibModalInstance, yamlFile, ansible, selectedProject, editGroup, YAML, $timeout) {
'ngInject';
$scope.newGroup = editGroup || {name:null};
$scope.variableViewType = {type:'YAML'};
$scope.complexVar = {};
$scope.complexVarString = {};
console.log("$scope.newGroup.name" + $scope.newGroup.name);
$scope.aceLoaded = function (_editor) {
_editor.$blockScrolling = Infinity;
};
if($scope.newGroup.members){
$scope.newGroup.members = $scope.newGroup.members.join(',');
}
if($scope.newGroup.name){
$scope.getGroupLoading = true;
ansible.getGroupVarsFile($scope.newGroup.name,
function(response){
$scope.getGroupLoading = false;
$scope.complexVarStringYaml = response.data;
$scope.complexVar = YAML.parse(response.data);
$timeout(function(){
$scope.$broadcast('membersUpdated')
},100);
},function(response){
$scope.getGroupLoading = false;
$scope.err_msg = response.data;
})
}
$scope.$watch('complexVar',function(){
$scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
$scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Group Variables - ' + $scope.newGroup.name);
}, true);
$scope.$watch('newGroup',function(){
$scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
$scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Group Variables - ' + $scope.newGroup.name);
}, true);
$scope.createGroup = function(){
$scope.createGroupLoading = true;
ansible.updateGroupVarsFile($scope.newGroup.name,$scope.complexVarStringYaml,
function(response){
$scope.createGroupLoading = false;
console.log("Success");
$scope.ok()
},function(response){
$scope.createGroupLoading = false;
$scope.err_msg = response.data;
});
};
$scope.ok = function () {
var resultGroup = {name:$scope.newGroup.name};
if($scope.newGroup.members){
resultGroup.members = $scope.newGroup.members.split(',');
$scope.newGroup.members = $scope.newGroup.members.split(',')
}
$uibModalInstance.close(resultGroup);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_group', [])
.controller('NewGroupController', newGroupController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewGroupCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_group'));
var NewGroupCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewGroupCtrl = $controller('NewGroupCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,45 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New Group</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Group Name</label>
<input type="text" ng-model="newGroup.name" class="form-control">
</p>
<p class="form-group">
<label>Group Members</label>
<input type="text" ng-model="newGroup.members" class="form-control" placeholder="Hosts list separated by coma">
</p>
<label> Group Variables </label>
<div class="row">
<div class="col-md-6">
<complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
</complex-var>
</div>
<div class="col-md-6">
<input name="typeRadioGroup" type="radio" ng-model="variableViewType.type" ng-value="'JSON'"> JSON
<input name="typeRadioGroup" type="radio" ng-model="variableViewType.type" ng-value="'YAML'"> YAML
<div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'yaml',mode:'yaml',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'YAML'" ng-model="complexVarStringYaml" style="max-height: 200px;">
</div>
<div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'json',mode:'json',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'JSON'" ng-model="complexVarString" style="max-height: 200px;">
</div>
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newGroup.name" ng-click="createGroup()">Create <span ng-if="createGroupLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,69 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newHostController($scope,$uibModalInstance, yamlFile, ansible, selectedProject, editHost, YAML, $timeout) {
$scope.newHost = editHost || {name:null};
$scope.variableViewType = {type:'YAML'};
$scope.complexVar = {};
$scope.complexVarString = {};
if($scope.newHost.name){
$scope.getHostLoading = true;
ansible.getHostVarsFile($scope.newHost.name,
function(response){
$scope.getHostLoading = false;
$scope.complexVarStringYaml = response.data;
$scope.complexVar = YAML.parse(response.data);
$timeout(function(){
$scope.$broadcast('membersUpdated')
},100);
},function(response){
$scope.getHostLoading = false;
$scope.err_msg = response.data;
})
}
$scope.$watch('complexVar',function(){
$scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
$scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Host Variables - ' + $scope.newHost.name);
}, true);
$scope.$watch('newHost',function(){
$scope.complexVarString = JSON.stringify($scope.complexVar, null, '\t');
$scope.complexVarStringYaml = yamlFile.jsonToYamlFile($scope.complexVar, 'Host Variables - ' + $scope.newHost.name);
}, true);
$scope.aceLoaded = function(_editor){
_editor.$blockScrolling = Infinity;
};
$scope.createHost = function(){
$scope.createHostLoading = true;
ansible.updateHostVarsFile($scope.newHost.name,$scope.complexVarStringYaml,
function(response){
$scope.createHostLoading = false;
console.log("Success");
$scope.ok()
},function(response){
$scope.createHostLoading = false;
$scope.err_msg = response.data;
});
};
$scope.ok = function () {
if(!$scope.newHost.members)$scope.newHost.members = 'Un grouped';
$uibModalInstance.close($scope.newHost);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_host', [])
.controller('NewHostController', newHostController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewHostCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_host'));
var NewHostCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewHostCtrl = $controller('NewHostCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,46 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New Host</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Host Name</label>
<input type="text" ng-model="newHost.name" class="form-control">
</p>
<p class="form-group">
<label>Member of Groups</label>
<input type="text" ng-model="newHost.members" class="form-control" placeholder="Hosts list separated by coma">
</p>
<label> Host Variables </label>
<div class="row">
<div class="col-md-6">
<complex-var members="complexVar" path="'Parent'" type="'object'" input-width="'400px'">
</complex-var>
</div>
<div class="col-md-6">
<input name="typeRadioHost" type="radio" ng-model="variableViewType.type" ng-value="'JSON'"> JSON
<input name="typeRadioHost" type="radio" ng-model="variableViewType.type" ng-value="'YAML'"> YAML
<div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'yaml',mode:'yaml',onChange:codeChanged}" ng-show="variableViewType.type == 'YAML'" ng-model="complexVarStringYaml" style="max-height: 200px;">
</div>
<div ng-readonly="true" ui-ace="{th1eme:'twilight',document:'json',mode:'json',onChange:codeChanged,onLoad:aceLoaded}" ng-show="variableViewType.type == 'JSON'" ng-model="complexVarString" style="max-height: 200px;">
</div>
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newHost.name" ng-click="createHost()">Create <span ng-if="createHostLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,40 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newInventoryController($scope,$uibModalInstance,ansible,selectedProject) {
$scope.newInventory = {name:null};
$scope.createInventoryLoading = false;
$scope.createInventory = function(){
if($scope.newInventory.name.match(/\./)){
$scope.err_msg = "Inventory files should not have extension"
return
}
$scope.createInventoryLoading = true;
ansible.createInventory($scope.newInventory.name,'# Inventory File - ' + $scope.newInventory.name,
function(response){
$scope.createInventoryLoading = false;
$scope.ok();
},
function(response){
$scope.createInventoryLoading = false;
$scope.err_msg = response.data;
})
};
$scope.ok = function () {
$uibModalInstance.close(null);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_inventory', [])
.controller('NewInventoryController', newInventoryController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewInventoryCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_inventory'));
var NewInventoryCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewInventoryCtrl = $controller('NewInventoryCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,21 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New Inventory</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Inventory Name</label>
<input type="text" ng-model="newInventory.name" class="form-control">
</p>
<div class="alert alert-info"> Inventory files should be named without an extension </div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newInventory.name" ng-click="createInventory()">Create <span ng-if="createInventoryLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,134 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newPlayController($scope, $uibModalInstance, ansible, plays, selectedPlayIndex) {
$scope.loading_msg = '';
$scope.title = "Create Play";
$scope.editMode = false;
$scope.editHostMode = false;
var selectedPlay;
if(selectedPlayIndex > -1){
selectedPlay = plays[selectedPlayIndex];
$scope.title = "Edit Play";
$scope.editMode = true;
if(selectedPlay && selectedPlay.tags)$scope.tags = selectedPlay.tags.join(',');
}
$scope.newPlay = selectedPlay || {};
$scope.newPlay_roles = $scope.newPlay.roles;
$scope.createPlayLoading = false;
$scope.createPlay = function () {
$scope.ok($scope.newPlay)
};
$scope.ok = function (newPlay) {
if($scope.tags)
newPlay.tags = $scope.tags.split(',');
if($scope.newPlay_roles && $scope.newPlay_roles.length){
var roles = [];
angular.forEach($scope.newPlay_roles,function(role){
roles.push(role.text)
});
newPlay.roles = roles;
}else if(newPlay.roles){
delete newPlay.roles;
}
$uibModalInstance.close(newPlay);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.getHostsFromInventory = function(){
var hosts = [];
angular.forEach($scope.inventory_data_json.hosts, function(host){
hosts.push({name:host})
});
angular.forEach($scope.inventory_data_json.groups, function(group){
if(group.name !== 'Un grouped')
hosts.push({name:group.name})
});
return hosts;
};
$scope.listOfInventoryFiles = function(){
$scope.loading_msg = 'Loading Inventory Files';
ansible.getInventoryList(function(response){
$scope.loading_msg = '';
$scope.inventoryFiles = response.data;
},
function(response){
$scope.loading_msg = '';
$scope.err_msg = response.data
})
};
$scope.listOfRoles = function(){
$scope.loading_msg = 'Loading Roles';
ansible.getRoleList(function(response){
$scope.loading_msg = '';
$scope.roleList = response.data;
},
function(response){
$scope.loading_msg = '';
$scope.err_msg = response.data
})
};
$scope.inventorySelected = function(selectedInventoryFile){
$scope.loading_msg = 'Loading Hosts';
ansible.readInventory(selectedInventoryFile,
function(response){
$scope.loading_msg = '';
$scope.inventory_data_json = ansible.parseINIString(response.data);
$scope.hosts = $scope.getHostsFromInventory();
},function(response){
$scope.loading_msg = '';
$scope.err_msg = response.data
})
};
$scope.getHostObject = function(hostname){
var result = $scope.hosts.filter(function(host){
return host.name == hostname
});
if(result.length){
return result[0]
}
};
$scope.listOfInventoryFiles();
$scope.listOfRoles();
$scope.loadTags = function(query){
if($scope.roleList){
var tempList = $scope.roleList.filter(function(role){
return role.indexOf(query) > -1
});
return tempList
}
}
}
export default angular.module('webAppApp.new_play', [])
.controller('NewPlayController', newPlayController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewPlayCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_play'));
var NewPlayCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewPlayCtrl = $controller('NewPlayCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,96 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<p class="form-group">
<label>Name</label>
<input ng-model="newPlay.name" type="text" class="form-control" placeholder="Play Name">
</p>
<p class="form-group">
<label>Tags</label>
<input ng-model="tags" type="text" class="form-control" placeholder="Tags separated by comma">
</p>
<p class="form-group" ng-show="!editMode || editHostMode">
<label>Inventory Files</label>
<select class="form-control" ng-disabled="loading_msg" ng-change="inventorySelected(selectedInventoryFile);" ng-model="selectedInventoryFile" ng-options="inventoryFile as inventoryFile for inventoryFile in inventoryFiles">
</select>
</p>
<p class="form-group" ng-show="!editMode || editHostMode">
<label>Hosts</label>
<select class="form-control" ng-disabled="loading_msg" ng-model="newPlay.hosts" ng-options="host.name as host.name for host in hosts">
</select>
</p>
<p class="form-group" ng-show="editMode && !editHostMode">
<label>Hosts </label>
<input ng-model="newPlay.hosts" ng-disabled="true" type="text" class="form-control" placeholder="Host Name">
</p>
<div class="input-group-btn" ng-show="editMode && !editHostMode">
<button class="btn btn-default" ng-click="editHostMode = true"> <span class="fa fa-edit"></span> Edit Hosts </button>
</div>
<label>Roles</label>
<tags-input ng-model="newPlay_roles" add-from-autocomplete-only="true" placeholder="Add roles to play">
<auto-complete source="loadTags($query)" min-length="0" load-on-empty="true" load-on-focus="true" load-on-down-arrow="true" ></auto-complete>
</tags-input>
</div>
<div class="col-md-6">
<h4 ng-if="selectedHostInfo.ip">IP - {{selectedHostInfo.ip}}</h4>
<h4 ng-if="selectedHostInfo.members">Members:</h4>
<ul class="list-group" ng-if="selectedHostInfo.members">
<li class="list-group-item" ng-repeat="member in selectedHostInfo.members.split(',')">{{member}}</li>
</ul>
<div ng-if="hostGroups">
<h4 >Member Of:</h4>
<ul class="list-group">
<li class="list-group-item" ng-repeat="group in hostGroups">{{group.name}}</li>
</ul>
</div>
<!--<div ui-ace="{theme:'twilight',document:'INI',mode:'ini',showGutter:false}" ng-model="hostInfo">
</div>-->
</div>
</div>
<div class="row" ng-if="selectedHostInfo">
<h4>Variables</h4>
<div class="input-group">
<span class="input-group-addon" >Groups</span>
<select class="form-control" ng-change="variableGroupSelected(selectedVariableGroup.value)" ng-model="selectedVariableGroup.value" ng-options="group.name as group.name for group in [selectedHostInfo].concat(hostGroups)">
</select>
</div>
<div ui-ace="{theme:'twilight',document:'INI',mode:'ini'}" ng-model="variableInfo">
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<div class="row">
<div class="col-md-3" style="text-align: left">
<span ng-show="loading_msg" class="fa fa-spin fa-spinner"></span> {{loading_msg}}
</div>
<div class="col-md-9" style="text-align: right">
<button class="btn btn-primary" ng-click="createPlay()">Save <span ng-if="createPlayLoading" class="fa fa-spinner fa-spin"></span> </button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,35 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newPlaybookController($scope,$uibModalInstance,ansible) {
$scope.newPlaybook = {name:null};
$scope.createPlaybookLoading = false;
$scope.createPlaybook = function(){
$scope.createPlaybookLoading = true;
ansible.createPlaybook($scope.newPlaybook.name + '.yml',"",
function(response){
$scope.createPlaybookLoading = false;
$scope.ok();
},
function(response){
$scope.createPlaybookLoading = false;
$scope.err_msg = response.data;
})
};
$scope.ok = function () {
$uibModalInstance.close(null);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_playbook', [])
.controller('NewPlaybookController', newPlaybookController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewPlaybookCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_playbook'));
var NewPlaybookCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewPlaybookCtrl = $controller('NewPlaybookCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,19 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">New Playbook</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Playbook Name</label>
<input type="text" ng-model="newPlaybook.name" class="form-control">
</p>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newPlaybook.name" ng-click="createPlaybook()">Create <span ng-if="createPlaybookLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,328 @@
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './playbook.routes';
export class PlaybookComponent {
/*@ngInject*/
constructor($scope,$uibModal,YAML,ansible,yamlFile) {
'ngInject';
$scope.isopen = {playbooks:true,plays:false,tasks:false};
$scope.selectedPlaybook = {playbook: "",content: ""};
$scope.selectedPlay = {play: ""};
$scope.showSaveButton = {};
$scope.loadingButtons = {};
$scope.editPlaybook = {value:false};
$scope.loadingModuleCode = false;
$scope.$on('projectLoaded',function(){
$scope.getPlaybooks()
});
//To fix a warning message in console
$scope.aceLoaded = function(_editor){
_editor.$blockScrolling = Infinity;
};
// --------------------------------------- PLAYBOOKS ----------------
$scope.getPlaybooks = function(){
ansible.getPlaybookList(
function(response){
$scope.playbooks = response.data;
},
function(response){
console.log(response.data)
}
)
};
if($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine){
$scope.getPlaybooks()
}
$scope.showPlaybookCode = function(playbook_name){
$scope.loadingModuleCode = true;
if(!playbook_name){
$scope.selectedPlaybook.content = "Select a module";
return;
}
ansible.readPlaybook(playbook_name,function(response) {
$scope.isopen.playbooks = true;
$scope.isopen.plays = true
$scope.loadingModuleCode = false;
$scope.selectedPlaybook.content = response.data.split("Stream :: close")[0];
$scope.getPlaysFromPlayBook($scope.selectedPlaybook.content);
});
};
$scope.$watch('selectedPlaybook.playbook',function(newValue,oldValue){
if(newValue && newValue !== oldValue){
$scope.selectedPlaybook.content = "Loading Code...";
$scope.showPlaybookCode(newValue);
}
});
$scope.$watch('selectedPlay.play',function(newValue,oldValue){
if(newValue && newValue !== oldValue){
$scope.selectedPlay.play.tasks = $scope.selectedPlay.play.tasks || [];
$scope.isopen.playbooks = false;
$scope.isopen.plays = false;
$scope.isopen.tasks = true;
$scope.isopen.roles = true;
}
});
$scope.showCreatePlaybookModal = function(){
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/playbook/new_playbook/new_playbook.html',
controller: 'NewPlaybookController',
size: 'md',
backdrop : 'static',
keyboard : false,
closeByEscape : false,
closeByDocument : false,
resolve: {
selectedProject: function(){
return $scope.$parent.selectedProject
}
}
});
modalInstance.result.then(function () {
$scope.getPlaybooks();
}, function () {
});
};
$scope.editPlaybookMethod = function(){
$scope.editPlaybook.value = true;
$scope.uneditedPlaybokContents = $scope.selectedPlaybook.content
};
$scope.cancelPlaybookChanges = function(){
$scope.editPlaybook.value = false;
$scope.selectedPlaybook.content = $scope.uneditedPlaybokContents
};
$scope.savePlaybook = function(buttonVariable){
console.log("Saving Playbook")
$scope.loadingButtons[buttonVariable] = true;
ansible.createPlaybook($scope.selectedPlaybook.playbook,$scope.selectedPlaybook.content,
function(response){
$scope.loadingButtons[buttonVariable] = false;
$scope.showSaveButton[buttonVariable] = false;
$scope.editPlaybook.value = false;
},
function(response){
$scope.loadingButtons[buttonVariable] = false;
$scope.showSaveButton[buttonVariable] = false;
$scope.err_msg = response.data;
})
};
$scope.deletePlaybook = function(){
$scope.deletePlaybookLoading = true;
ansible.deletePlaybook($scope.selectedPlaybook.playbook,
function(response){
$scope.deletePlaybookLoading = false;
$scope.selectedPlaybook.playbook = "";
$scope.getPlaybooks();
},
function(response){
$scope.deletePlaybookLoading = false;
$scope.err_msg = response.data;
})
};
//--------------- PLAY --------------
$scope.showCreatePlayModal = function(selectedPlayIndex){
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createPlayContent.html',*/
templateUrl: 'app/designer/playbook/new_play/new_play.html',
controller: 'NewPlayController',
size: 'lg',
backdrop : 'static',
keyboard : false,
closeByEscape : false,
closeByDocument : false,
resolve: {
selectedProject: function () {
return $scope.$parent.selectedProject;
},
plays: function () {
return $scope.plays;
},
selectedPlayIndex: function () {
return selectedPlayIndex;
}
}
});
modalInstance.result.then(
function (newPlay) {
if(selectedPlayIndex == null)
$scope.plays.push(newPlay);
$scope.clearEmptyTasks($scope.plays);
$scope.selectedPlaybook.content = yamlFile.jsonToYamlFile($scope.plays, 'Playbook file: ' + $scope.selectedPlaybook.playbook)
$scope.savePlaybook();
}, function () {
});
};
// FUNCTION - GET PLAYS FROM PLAYBOOK
$scope.getPlaysFromPlayBook = function(playbookYamlData){
$scope.plays = YAML.parse(playbookYamlData) || []
};
// FUNCTION - DELETE PLAY
$scope.deletePlay = function(index){
$scope.plays.splice(index,1);
$scope.selectedPlaybook.content = yamlFile.jsonToYamlFile($scope.plays, 'Playbook file: ' + $scope.selectedPlaybook.playbook)
$scope.savePlaybook();
$scope.selectedPlay = {play: ""};
};
// ------------------- EXECUTE PLAYBOOK MODAL -------------
$scope.executeAnsiblePlayBook = function(tags,executionType,executionName,selectedPlay){
console.log("Tags type" + typeof tags)
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/execution/executeModal.html',
controller: 'ExecutionController',
size: 'lg',
backdrop : 'static',
keyboard : false,
closeByEscape : false,
closeByDocument : false,
resolve: {
tags: function(){
return tags
},
selectedProject: function(){
return $scope.$parent.selectedProject
},
selectedPlaybook: function(){
return $scope.selectedPlaybook
},
selectedPlay: function(){
return selectedPlay
},
executionType: function(){
return executionType
},
executionName: function(){
return executionName
},
readOnly: function(){
return false
},
runData: function(){
return null
},
projectFolder: function(){
return null
},
roleName: function(){
return null
}
}
});
};
$scope.clearEmptyTasks = function(plays){
//Check for empty tasks list
angular.forEach(plays,function(play){
if((play.tasks && !play.tasks.length) || !play.tasks){
delete play.tasks
}
});
};
// ---------------------- TASKS -------------------
$scope.updatePlaybookFileContent = function(save,buttonVariable){
var playsCopy = angular.copy($scope.plays);
$scope.clearEmptyTasks(playsCopy);
$scope.selectedPlaybook.content = yamlFile.jsonToYamlFile(playsCopy, 'Playbook file: ' + $scope.selectedPlaybook.playbook)
if(save)
$scope.savePlaybook(buttonVariable);
};
$scope.moveUp = function(list,index,buttonVariable){
if(!$scope.preChangeData) $scope.preChangeData = angular.copy(list);
var temp = angular.copy(list[index]);
list[index] = list[index-1];
list[index-1] = temp;
$scope.updatePlaybookFileContent(false);
$scope.showSaveButton[buttonVariable] = true
};
$scope.cancelChange = function(buttonVariable){
if($scope.preChangeData){
$scope.plays = angular.copy($scope.preChangeData);
$scope.preChangeData = null
}
$scope.updatePlaybookFileContent(false);
$scope.showSaveButton[buttonVariable] = false
};
$scope.moveDown = function(list,index,buttonVariable){
if(!$scope.preChangeData) $scope.preChangeData = angular.copy(list);
var temp = angular.copy(list[index]);
list[index] = list[index+1];
list[index+1] = temp;
$scope.updatePlaybookFileContent(false);
$scope.showSaveButton[buttonVariable] = true
};
}
}
export default angular.module('webAppApp.playbook', [uiRouter])
.config(routes)
.component('playbook', {
template: require('./playbook.html'),
controller: PlaybookComponent,
controllerAs: 'playbookCtrl'
})
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Component: PlaybookComponent', function() {
// load the controller's module
beforeEach(module('webAppApp.playbook'));
var PlaybookComponent;
// Initialize the controller and a mock scope
beforeEach(inject(function($componentController) {
PlaybookComponent = $componentController('playbook', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,166 @@
<div class="col-md-6">
<uib-accordion close-others="false">
<div uib-accordion-group class="panel-default" is-open="isopen.playbooks">
<uib-accordion-heading>
Playbooks <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.playbooks, 'glyphicon-chevron-right': !isopen.playbooks}"></i>
</uib-accordion-heading>
<button class="btn btn-default" ng-click="showCreatePlaybookModal()">Create Playbook <span
class="fa fa-plus"></span></button>
<button class="btn btn-default" ng-if="!editPlaybook.value" ng-click="editPlaybookMethod()">Edit <span
ng-if="!showSaveButton.savePlaybookLoading" class="fa fa-edit"></span></button>
<button class="btn btn-primary" ng-if="editPlaybook.value" ng-disabled="!selectedPlaybook.playbook"
ng-click="savePlaybook('savePlaybookLoading')">Save <span ng-if="!showSaveButton.savePlaybookLoading"
class="fa fa-save"></span><span
ng-if="showSaveButton.savePlaybookLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-warning" ng-if="editPlaybook.value" confirm="Are you sure you want to discard the changes?"
ng-click="cancelPlaybookChanges()">Cancel <span class="fa fa-times-circle"></span></button>
<button class="btn btn-danger" confirm="Are you sure you want to delete playbook? "
ng-disabled="!selectedPlaybook.playbook" ng-click="deletePlaybook()">Delete <span
ng-show="!deletePlaybookLoading" class="fa fa-save"></span><span ng-show="deletePlaybookLoading"
class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-success" ng-disabled="!selectedPlaybook.playbook"
ng-click="executeAnsiblePlayBook(null,'Playbook')">Play <span class="fa fa-play"></span></button>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<!--<th>Actions</th>-->
</tr>
</thead>
<tbody>
<tr ng-repeat="playbook in playbooks">
<td><input name="playbookGroup" type="radio" ng-model="selectedPlaybook.playbook" ng-value="playbook">
</td>
<td>{{playbook}}</td>
<!--<td><div class="btn-group">
<label class="btn btn-default btn-sm" ng-click="showCreatePlayModal($index)" ><span class="fa fa-edit"></span></label>
<label class="btn btn-danger btn-sm" confirm="Are you sure you want to delete?" ng-click="deletePlay($index)" ><span class="fa fa-trash-o"></span></label>
<div style="display: inline-block" tooltip-enable="!play.tags" uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm" ng-disabled="!play.tags" ng-click="executeAnsiblePlayBook(play.tags,'Play',play.name,play)" ><span class="fa fa-play"></span></label></div>
</div></td>-->
</tr>
</tbody>
</table>
</div>
</div>
<div uib-accordion-group class="panel-default" is-open="isopen.plays" ng-show="plays">
<uib-accordion-heading>
Plays <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.plays, 'glyphicon-chevron-right': !isopen.plays}"></i>
</uib-accordion-heading>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<tr>
<th>Select</th>
<th>Name</th>
<!--<th>Tags</th>-->
<th>Hosts</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="play in plays">
<td><input name="playGroup" type="radio" ng-model="selectedPlay.play" ng-value="play">
</td>
<td>{{play.name}}</td>
<!--<td>{{play.tags.join(', ')}}</td>-->
<td>{{play.hosts}}</td>
<td>
<div class="btn-group">
<label class="btn btn-default btn-sm" ng-click="showCreatePlayModal($index)"><span
class="fa fa-edit"></span></label>
<label class="btn btn-danger btn-sm" confirm="Are you sure you want to delete?"
ng-click="deletePlay($index)"><span class="fa fa-trash-o"></span></label>
<div style="display: inline-block" tooltip-enable="!play.tags"
uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm"
ng-disabled="!play.tags"
ng-click="executeAnsiblePlayBook(play.tags,'Play',play.name,play)"><span
class="fa fa-play"></span></label></div>
<label class="btn btn-primary btn-sm" ng-disabled="$first"
ng-click="moveUp(plays,$index,'savePlayLoading')"><span class="fa fa-arrow-up"></span></label>
<label class="btn btn-primary btn-sm" ng-disabled="$last"
ng-click="moveDown(plays,$index,'savePlayLoading')"><span
class="fa fa-arrow-down"></span></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<button class="btn btn-default" ng-click="showCreatePlayModal()">Create <span class="fa fa-plus"></span></button>
<button class="btn btn-primary" ng-if="showSaveButton.savePlayLoading" ng-click="savePlaybook('savePlayLoading')">
Save <span ng-if="loadingButtons.savePlayLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-warning" ng-if="showSaveButton.savePlayLoading" ng-click="cancelChange('savePlayLoading')">
Cancel <span class="fa fa-times"></span></button>
</div>
<div uib-accordion-group class="panel-default" is-open="isopen.tasks" ng-show="selectedPlay.play">
<uib-accordion-heading>
Tasks <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
</uib-accordion-heading>
<tasks tasks-list="selectedPlay.play.tasks" update-playbook-file-content="updatePlaybookFileContent" selected-play="selectedPlay" save-playbook="savePlaybook" execute-ansible-play-book="executeAnsiblePlayBook"></tasks>
</div>
<div uib-accordion-group class="panel-default" is-open="isopen.roles" ng-show="selectedPlay.play.roles">
<uib-accordion-heading>
Roles <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
</uib-accordion-heading>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="role in selectedPlay.play.roles">
<td><input name="playGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
</td>
<td>{{role}}</td>
</tr>
</tbody>
</table>
</div>
<div class="panel">
<div class="panel panel-default">
<div class="panel-heading">File Browser</div>
<div class="panel-body">
<treecontrol ng-if="roleData.children" class="tree-classic"
tree-model="roleData.children"
options="treeOptions"
on-selection="showSelected(node)"
selected-node="node1"
filter-expression="{name: '!.git'}">
{{node.name}}
</treecontrol>
</div>
</div>
</div>
</div>
</uib-accordion>
</div>
<div class="col-md-6">
<div ng-readonly="!editPlaybook.value" ui-ace="{theme:'twilight',document:'YAML',mode:'yaml',onChange:codeChanged,onLoad:aceLoaded}"
ng-model="selectedPlaybook.content">
</div>
</div>

View file

@ -0,0 +1,10 @@
'use strict';
export default function($stateProvider) {
'ngInject';
$stateProvider
.state('designer.playbook', {
url: '/playbook',
template: '<playbook></playbook>'
});
}

View file

@ -0,0 +1,58 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newFileController($scope,$uibModalInstance,ansible,selectedDirectory,copyFile,selectedFileName) {
$scope.newFile = {name:null};
$scope.createFileLoading = false;
$scope.title = 'New File';
var parentDirectory = selectedDirectory;
// If copyFile use selectedFileName to create new role from
// else nullify selectedFileName
if(!copyFile){
selectedFileName = null;
}
else {
$scope.title = 'Copy File';
$scope.newFile.name = 'Copy of ' + selectedFileName;
}
/**
* Create/Copy File - Either a new role or copy an existing role
*/
$scope.createFile = function(){
$scope.createFileLoading = true;
ansible.createFile(parentDirectory + '/' + $scope.newFile.name,
function(response){
$scope.createFileLoading = false;
$scope.ok();
},
function(response){
$scope.createFileLoading = false;
$scope.err_msg = response.data;
},
selectedFileName
)
};
/**
* Close create/copy modal
*/
$scope.ok = function () {
$uibModalInstance.close(null);
};
/**
* Cancel modal
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_file', [])
.controller('NewFileController', newFileController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewFileCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_file'));
var NewFileCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewFileCtrl = $controller('NewFileCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,19 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>File Name</label>
<input type="text" ng-model="newFile.name" class="form-control">
</p>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newFile.name" ng-click="createFile()">Create <span ng-if="createFileLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,56 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newRoleController($scope,$uibModalInstance,ansible,selectedRoleName,copyRole) {
$scope.newRole = {name:null};
$scope.createRoleLoading = false;
$scope.title = 'New Role';
// If copyRole use selectedRoleName to create new role from
// else nullify selectedRoleName
if(!copyRole){
selectedRoleName = null;
}
else {
$scope.title = 'Copy Role';
$scope.newRole.name = 'Copy of ' + selectedRoleName;
}
/**
* Create/Copy Role - Either a new role or copy an existing role
*/
$scope.createRole = function(){
$scope.createRoleLoading = true;
ansible.createRole($scope.newRole.name,
function(response){
$scope.createRoleLoading = false;
$scope.ok();
},
function(response){
$scope.createRoleLoading = false;
$scope.err_msg = response.data;
},
selectedRoleName
)
};
/**
* Close create/copy modal
*/
$scope.ok = function () {
$uibModalInstance.close(null);
};
/**
* Cancel modal
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.new_role', [])
.controller('NewRoleController', newRoleController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewRoleCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_role'));
var NewRoleCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewRoleCtrl = $controller('NewRoleCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,19 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Role Name</label>
<input type="text" ng-model="newRole.name" class="form-control">
</p>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!newRole.name" ng-click="createRole()">Create <span ng-if="createRoleLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,463 @@
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './roles.routes';
export class RolesComponent {
/*@ngInject*/
constructor($scope, ansible, $uibModal, yamlFile, Projects, editor) {
'ngInject';
$scope.treeOptions = {
nodeChildren: "children",
dirSelectable: true,
isLeaf: function (node) {
return !(node.type === 'directory');
},
injectClasses: {
ul: "a1",
li: "a2",
liSelected: "a7",
iExpanded: "a3",
iCollapsed: "a4",
iLeaf: "a5",
label: "a6",
labelSelected: "a8"
}
};
$scope.isopen = {roles: true, filebrowser: false, tasks: false};
$scope.selectedRole = {role: "", tasks: null};
$scope.selectedFile = {showSource: true, markdownContent: true, content: ""};
$scope.editRole = {value: false};
$scope.showSaveFileButton = false;
$scope.$on('projectLoaded', function () {
$scope.getRoles()
});
$scope.aceLoaded = function (_editor) {
_editor.$blockScrolling = Infinity;
};
// --------------------------------------- PLAYBOOKS ----------------
$scope.getRoles = function () {
ansible.getRoleList(
function (response) {
$scope.roles = response.data;
if(localStorage.selectedRoleName)
$scope.selectedRole.role = localStorage.selectedRoleName
},
function (response) {
console.log(response.data)
}
)
};
var getRoleByName = function(roleName){
var result = null;
angular.forEach($scope.roles,function(role){
if(role.name == roleName){
result = role
}
});
return result;
};
if ($scope.$parent.selectedProject && $scope.$parent.selectedProject.ansibleEngine) {
$scope.getRoles()
}
$scope.loadingModuleCode = false;
$scope.markdownContent = "";
$scope.showRoleCode = function (role_name) {
$scope.loadingModuleCode = true;
$scope.markdownContent = '';
$scope.docType = 'text';
$scope.selectedFile.content = 'Loading Role Files..';
$scope.selectedRole.tasks = null;
$scope.roleData = null;
if (!role_name) {
$scope.selectedFile.content = "Select a module";
return;
}
ansible.getRoleFiles(role_name, function (response) {
$scope.loadingModuleCode = false;
$scope.selectedFile.content = JSON.stringify(response.data, null, '\t');
$scope.docType = 'json';
$scope.roleData = response.data;
});
};
$scope.$watch('selectedRole.role', function (newValue, oldValue) {
if (newValue && newValue !== oldValue) {
$scope.currentRole = newValue;
$scope.reloadRole();
//$scope.isopen.roles = false;
$scope.isopen.filebrowser = true;
localStorage.selectedRoleName = $scope.selectedRole.role;
}
});
$scope.reloadRole = function () {
$scope.selectedFile.content = "Loading Code...";
$scope.docType = 'txt';
$scope.showRoleCode($scope.currentRole);
};
/*var setDocType = function (data, file) {
if (typeof data == 'object') {
$scope.selectedFile.content = JSON.stringify(data, null, '\t');
} else {
$scope.selectedFile.content = data;
}
$scope.docType = editor.ui_ace_doctype_map[file.extension.replace('.', '')];
if ($scope.docType == 'markdown') {
$scope.markdownContent = $scope.selectedFile.content;
$scope.selectedFile.showSource = false;
}
};*/
/**
* Show selected item in the tree
* @param file
* @param parent
*/
$scope.showSelected = function (file, parent, decrypt) {
if($scope.editRole.value){
return
}
if (file.children) {
$scope.selectedFile.content = JSON.stringify(file, null, '\t');
$scope.docType = 'json';
$scope.selectedRole.tasks = null;
return;
}
$scope.selectedFile.content = 'Loading..';
var command = 'cat "' + file.path + '"';
$scope.encryptedFile = false;
if(decrypt){
command = 'ansible-vault view "' + file.path + '" --vault-password-file ~/.vault_pass.txt'
$scope.encryptedFile = true;
$scope.selectedFile.content = 'Loading Encrypted File..';
}
$scope.selectedFile.showSource = true;
$scope.markdownContent = '';
$scope.docType = 'text';
$scope.selectedRole.tasks = null;
$scope.selectedFileName = file.name;
$scope.selectedFilePath = file.path;
$scope.parentNode = parent;
ansible.executeCommand(command,
function (response) {
$scope.preChangeData = null;
editor.setContentAndType(response.data, file, $scope.selectedFile);
var parentDirectory = file.path.replace(/^(.+)\/(.+)\/([^/]+)$/, "$2");
if (parentDirectory == 'tasks') {
$scope.selectedRole.tasks = YAML.parse(response.data) || [];
$scope.isopen.tasks = true;
$scope.isopen.roles = false;
}
if(response.data.indexOf('ANSIBLE_VAULT') > -1){
editor.setContentAndType('Decrypting content...', file, $scope.selectedFile);
$scope.showSelected(file, parent, true);
}
}, function (response) {
$scope.selectedFile.content = response.data;
})
};
$scope.showCreateFileModal = function (selectedFile, copyFile) {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/roles/new_file/new_file.html',
controller: 'NewFileController',
size: 'md',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
copyFile: function () {
return copyFile
},
selectedDirectory: function () {
if (selectedFile.type == 'directory')
return selectedFile.path;
else return $scope.parentNode.path
},
selectedFileName: function () {
return selectedFile
}
}
});
modalInstance.result.then(function () {
//$scope.getRoles();
$scope.reloadRole();
}, function () {
});
};
$scope.showCreateRoleModal = function (copyRole) {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/roles/new_role/new_role.html',
controller: 'NewRoleController',
size: 'md',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
copyRole: function () {
return copyRole
},
selectedRoleName: function () {
return $scope.selectedRole.role
}
}
});
modalInstance.result.then(function () {
$scope.getRoles();
}, function () {
});
};
$scope.showSearchRoleModal = function () {
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/roles/search_role/search_role.html',
controller: 'SearchRoleController',
size: 'lg',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
selectedProject: function () {
return $scope.$parent.selectedProject
}
}
});
modalInstance.result.then(function () {
$scope.getRoles();
}, function () {
});
};
$scope.saveRole = function () {
$scope.saveRoleLoading = true;
ansible.createRole($scope.selectedRole.role, $scope.selectedFile.content,
function (response) {
$scope.saveRoleLoading = false;
$scope.editRole.value = false;
},
function (response) {
$scope.saveRoleLoading = false;
$scope.err_msg = response.data;
})
};
$scope.deleteRole = function () {
$scope.deleteRoleLoading = true;
ansible.deleteRole($scope.selectedRole.role,
function (response) {
$scope.deleteRoleLoading = false;
$scope.selectedRole.role = "";
$scope.selectedFile.content = "";
$scope.roleData = null;
$scope.getRoles();
},
function (response) {
$scope.deleteRoleLoading = false;
$scope.err_msg = response.data;
$scope.selectedFile.content = "";
$scope.roleData = null;
})
};
$scope.loadingButtons = {};
$scope.showSaveButton = {};
// ------------- PLAYBOOK ------------------
$scope.saveTasksFile = function (buttonStates) {
buttonStates = buttonStates || {};
buttonStates.loading = true;
var tasksFileContent = $scope.selectedFile.content;
ansible.createPlaybook($scope.selectedFilePath, tasksFileContent,
function (response) {
buttonStates.loading = false;
buttonStates.save = false;
},
function (response) {
buttonStates.loading = false;
buttonStates.save = false;
buttonStates.err_msg = false;
})
};
$scope.updatePlaybookFileContent = function (save, buttonStates, preChangedData) {
$scope.selectedRole.tasks = preChangedData || $scope.selectedRole.tasks;
$scope.selectedFile.content = yamlFile.jsonToYamlFile($scope.selectedRole.tasks, 'Tasks File: ' + $scope.selectedFileName);
if (save)
$scope.saveTasksFile(buttonStates);
};
$scope.editFile = function (selectedFile) {
if (selectedFile.type == 'directory')return;
if (!$scope.preChangeData){
console.log("No prechanged data, setting pre change data");
$scope.preChangeData = angular.copy($scope.selectedFile.content);
}
$scope.editRole.value = true;
$scope.showSaveFileButton = true;
};
$scope.cancelFileChanges = function (selectedFile) {
if ($scope.preChangeData) {
console.log("Replacing content with pre changed data");
$scope.selectedFile.content = angular.copy($scope.preChangeData);
$scope.preChangeData = null;
console.log("Clearing pre changed data")
}
$scope.editRole.value = false;
$scope.showSaveFileButton = false;
};
$scope.saveFile = function (selectedFile) {
$scope.showSaveFileButtonLoading = true;
$scope.preChangeData = null;
ansible.updateFile(selectedFile.path, $scope.selectedFile.content,
function (response) {
$scope.showSaveFileButtonLoading = false;
$scope.showSaveFileButton = false;
$scope.editRole.value = false;
}, function (error) {
$scope.showSaveFileButtonLoading = false;
$scope.err_msg = error.data;
})
};
$scope.deleteFile = function (selectedFile) {
ansible.deleteFile(selectedFile.path, function (response) {
$scope.reloadRole();
}, function (error) {
$scope.showSaveFileButtonLoading = false;
$scope.err_msg = error.data;
})
};
// ------------------- EXECUTE PLAYBOOK MODAL -------------
$scope.executeAnsiblePlayBook = function (tags, executionType, executionName, selectedPlay) {
console.log("Tags type" + typeof tags);
var projectRolesFolder = Projects.selectedProject.ansibleEngine.projectFolder + '/roles';
var rolesFolder = projectRolesFolder + '/' + $scope.selectedRole.role;
var roleName = $scope.selectedRole.role;
console.log("Projects Roles Folder" + projectRolesFolder);
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/execution/executeModal.html',
controller: 'ExecutionController',
size: 'lg',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
tags: function () {
return tags
},
selectedProject: function () {
return Projects.selectedProject
},
selectedPlaybook: function () {
return {playbook: $scope.selectedRole.role + '/tests/test.yml'};
},
selectedPlay: function () {
return selectedPlay
},
executionType: function () {
return executionType
},
executionName: function () {
return executionName
},
readOnly: function () {
return false
},
runData: function () {
return null
},
projectFolder: function () {
return projectRolesFolder
},
roleName: function () {
return roleName
}
}
});
};
}
}
export default angular.module('webAppApp.roles', [uiRouter])
.config(routes)
.component('roles', {
template: require('./roles.html'),
controller: RolesComponent,
controllerAs: 'rolesCtrl'
})
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Component: RolesComponent', function() {
// load the controller's module
beforeEach(module('webAppApp.roles'));
var RolesComponent;
// Initialize the controller and a mock scope
beforeEach(inject(function($componentController) {
RolesComponent = $componentController('roles', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

View file

@ -0,0 +1,110 @@
<div class="col-md-6">
<uib-accordion close-others="false">
<div uib-accordion-group class="panel-default" is-open="isopen.roles">
<uib-accordion-heading>
Roles <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.roles, 'glyphicon-chevron-right': !isopen.roles}"></i>
</uib-accordion-heading>
<button class="btn btn-default" ng-click="showCreateRoleModal()">Create Role <span class="fa fa-plus"></span>
</button>
<button class="btn btn-default" ng-click="showSearchRoleModal()">Import Role <span
class="fa fa-cloud-download"></span></button>
<button class="btn btn-default" ng-disabled="!selectedRole.role" ng-click="showCreateRoleModal(true)">Copy Role <span class="fa fa-copy"></span>
</button>
<!--<button class="btn btn-default" ng-if="!editRole.value" ng-disabled="!selectedRole.role" ng-click="editRole.value = true">Edit <span
ng-if="!saveRoleLoading" class="fa fa-edit"></span></button>
<button class="btn btn-primary" ng-if="editRole.value" ng-disabled="!selectedRole.role" ng-click="saveRole()">Save
<span ng-if="!saveRoleLoading" class="fa fa-save"></span><span ng-if="saveRoleLoading"
class="fa fa-spinner fa-spin"></span></button>-->
<button class="btn btn-danger" confirm="Are you sure you want to delete the role and all its contents? "
ng-disabled="!selectedRole.role" ng-click="deleteRole()">Delete <span ng-if="!deleteRoleLoading"
class="fa fa-save"></span><span
ng-if="deleteRoleLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-success"
ng-disabled="!selectedRole.role" ng-click="executeAnsiblePlayBook(null,'Role',selectedRole.role,null)">Test <span
class="fa fa-play"></span></button>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="role in roles">
<td><input name="playGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
</td>
<td>{{role}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div uib-accordion-group class="panel-default" is-open="isopen.filebrowser" ng-show="roleData">
<uib-accordion-heading>
File Browser {{selectedRole.role ? '-' + selectedRole.role : ''}} <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.filebrowser, 'glyphicon-chevron-right': !isopen.filebrowser}"></i>
</uib-accordion-heading>
<treecontrol ng-show="roleData.children" class="tree-classic"
tree-model="roleData.children"
options="treeOptions"
on-selection="showSelected(node, $parentNode)"
selected-node="selectedFile"
filter-expression="{name: '!.git'}">
{{node.name}}
</treecontrol>
<button class="btn btn-default" ng-click="showCreateFileModal(selectedFile)" ng-disabled="!(selectedFile.type === 'directory')">Create File <span class="fa fa-plus"></span>
</button>
<button class="btn btn-default" ng-show="!editRole.value" ng-disabled="!(selectedFile.type === 'file') || encryptedFile" ng-click="editFile(selectedFile)">Edit File <span class="fa fa-edit"></span>
</button>
<button class="btn btn-primary" ng-show="showSaveFileButton" ng-disabled="encryptedFile" ng-click="saveFile(selectedFile)">Save File <span ng-show="!showSaveFileButtonLoading" class="fa fa-save"></span><span ng-show="showSaveFileButtonLoading" class="fa fa-spin fa-spinner"></span>
</button>
<button class="btn btn-warning" ng-show="showSaveFileButton" confirm="Are you sure you want to cancel and discard the changes?" ng-click="cancelFileChanges(selectedFile)">Cancel <span class="fa fa-times"></span>
</button>
<button class="btn btn-danger" ng-disabled="!selectedFile.type" confirm="Are you sure you want to delete the selected {{selectedFile.type}}?" ng-click="deleteFile(selectedFile)">Delete <span class="fa fa-trash-o"></span>
</button>
</div>
<div uib-accordion-group class="panel-default" is-open="isopen.tasks" ng-show="selectedRole.tasks">
<uib-accordion-heading>
Tasks {{selectedRole.role ? '-' + selectedRole.role : ''}} <i class="pull-right glyphicon"
ng-class="{'glyphicon-chevron-down': isopen.tasks, 'glyphicon-chevron-right': !isopen.tasks}"></i>
</uib-accordion-heading>
<tasks tasks-list="selectedRole.tasks" update-playbook-file-content="updatePlaybookFileContent" selected-play="selectedPlay" selected-role="selectedRole" move-up="moveUp" move-down="moveDown" save-playbook="saveTasksFile" files="parentNode.children" execute-ansible-play-book="executeAnsiblePlayBook"></tasks>
</div>
</uib-accordion>
</div>
</div>
<div class="col-md-6">
<button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && !selectedFile.showSource" ng-click="selectedFile.showSource = true"> Show
Source
</button>
<button class="btn btn-default" ng-show="selectedFile.docType == 'markdown' && selectedFile.showSource" ng-click="selectedFile.showSource = false"> Hide
Source
</button>
<!--{{docType}}-->
<div ng-show="selectedFile.showSource" ng-readonly="!editRole.value"
ui-ace="{theme:'twilight',document:selectedFile.docType,mode:selectedFile.docType,onChange:codeChanged,onLoad:aceLoaded}" ng-model="selectedFile.content">
</div>
<div ng-show="!selectedFile.showSource" btf-markdown="selectedFile.markdownContent">
</div>
</div>

View file

@ -0,0 +1,10 @@
'use strict';
export default function($stateProvider) {
'ngInject';
$stateProvider
.state('designer.roles', {
url: '/roles',
template: '<roles></roles>'
});
}

View file

@ -0,0 +1,66 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function searchRoleController($scope,ansible,selectedProject,$uibModalInstance) {
$scope.searchText = '';
$scope.searchLoading = false;
$scope.selectedRole = {role:{}};
$scope.searchRoles = function(){
$scope.searchResult = [];
$scope.searchLoading = true;
ansible.searchRolesGalaxy($scope.searchText,
function(response){
$scope.searchLoading = false;
$scope.searchResult = $scope.searchResult.concat(response.data)
},function(response){
$scope.searchLoading = false;
$scope.err_msg = response.data
});
ansible.searchRolesGithub($scope.searchText,
function(response){
$scope.searchLoading = false;
$scope.searchResult = $scope.searchResult.concat(response.data)
},function(response){
$scope.searchLoading = false;
$scope.err_msg = response.data
})
};
$scope.importRole = function(role){
$scope.importLoading = true;
if(role.type === 'galaxy')role.url = role.name;
ansible.importRole(role.type,role.url,
function(response){
$scope.importLoading = false;
$scope.ok();
},function(response){
$scope.importLoading = false;
$scope.err_msg = response.data
});
};
$scope.ok = function () {
$uibModalInstance.close(null);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
export default angular.module('webAppApp.search_role', [])
.controller('SearchRoleController', searchRoleController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: SearchRoleCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.search_role'));
var SearchRoleCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
SearchRoleCtrl = $controller('SearchRoleCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,70 @@
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Search Role</h4>
</div>
<div class="modal-body">
<p class="form-group">
<label>Search</label>
<input type="text" ng-model="searchText" class="form-control">
</p>
<div class="row">
<div ng-repeat="role in searchResult">
<div class="clearfix" ng-if="($index % 3) == 0"></div>
<div class="col-md-4" >
<div class="panel">
<div class="panel panel-default">
<!--<div class="panel-heading"></div>-->
<div class="panel-body">
<span class="fa fa-spin fa-spinner fa-4x" ng-show="searchLoading"></span>
<h4>
<span class="fa fa-github fa-2x" ng-if="role.type == 'gitrepo'"></span>
<img src="assets/images/ansible_icon.png" ng-if="role.type == 'galaxy'">
{{role.name}}
</h4>
{{role.description}}
<br>
<button class="btn btn-success btn-sm" ng-click="importRole(role)"> <span class="fa fa-cloud-download"></span> Import</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!--<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="role in searchResult">
<td><input name="roleGroup" type="radio" ng-model="selectedRole.role" ng-value="role">
</td>
<td>{{role.name}}</td>
<td>{{role.description}}</td>
<td>{{role.type}}</td>
</tr>
</tbody>
</table>
</div>
{{selectedRole}}-->
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-disabled="!searchText" ng-click="searchRoles()">Search <span ng-if="!searchLoading" class="fa fa-search"></span> <span ng-if="searchLoading" class="fa fa-spinner fa-spin"></span></button>
<!--<button class="btn btn-primary" ng-disabled="!selectedRole.role.name" ng-click="importRole()">Import <span ng-if="importLoading" class="fa fa-spinner fa-spin"></span></button>-->
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>

View file

@ -0,0 +1,446 @@
'use strict';
const angular = require('angular');
/*@ngInject*/
export function newTaskController($window, $scope, $sce, $uibModal, ansi2html, ansible, $uibModalInstance, tasksList, selectedTaskIndex, copyTask, files, selectedPlay, selectedRole, $filter, Projects) {
var selectedTask;
/**
* Edit task - in case of edit task , selectedTaskIndex is not null.
* Set selectedTask to a copy of selected task to edit.
*/
if(selectedTaskIndex > -1 && tasksList){
if(copyTask){
selectedTask = angular.copy(tasksList[selectedTaskIndex]);
selectedTask.name = "Copy of " + selectedTask.name;
selectedTaskIndex = null;
}else{
selectedTask = tasksList[selectedTaskIndex]
}
}
/**
* List of files for include purpose
*/
if(files){
$scope.files = files;
}
$scope.getModuleDescriptionLoading = false;
$scope.modulesLoading = false;
$scope.modules = null;
$scope.singeLineModules = ["shell"];
$scope.showHelp = false;
$scope.newTask = {};
$scope.title = "New Task";
$scope.createTaskLoading = false;
/**
* Get Ansible Modules
* If Edit Task, get module description for selected task
*/
$scope.getAnsibleModules = function(){
$scope.modulesLoading = true;
ansible.getAnsibleModules(function(response){
$scope.modules = response;
$scope.modulesLoading = false;
if(selectedTask){
$scope.title = "Edit Task";
selectedTask = angular.copy(selectedTask);
$scope.newTask = selectedTask;
if(selectedTask.tags)$scope.newTask.tags = $scope.newTask.tags.join(',');
var module = $scope.getModuleFromTask(selectedTask);
$scope.getModuleDescription(module,true)
}
}, function(response){
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>").replace(/ /g,"&nbsp;"));
});
};
/**
* Get Module Description whenever a module is selected by the user
* @param module - Module Object
* @param override - Override variables in case of edit task
* @param refresh - Refresh module description from server. Don't display from cache
*/
$scope.getModuleDescription = function(module,override,refresh){
if(!module)return;
var module_copy = angular.copy(module);
$scope.getModuleDescriptionLoading = true;
var moduleName = module.name;
if($scope.singeLineModules.indexOf(moduleName) > -1){
module.singleLine = true;
}
$scope.detailHelp = "";
$scope.examples = "";
module.variables = [];
ansible.getAnsibleModuleDescription(moduleName,
function(response){
$scope.showHelp = true;
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response).replace(/\n/g, "<br>").replace(/ /g,"&nbsp;"));
$scope.detailHelp = response;
$scope.examples = response.substr(response.indexOf("#"));
//var re = /(^[-=] .*)/gm;
//var re = /(^[-=] .*)[^]*?(?:(\(Choices[^]+?\))?\s*(\[.*\])|(?=^[-=]|^EXAMPLES))/gm;
var re = /(^[-=] .*)([^]*?)(?:(\(Choices[^]+?\))?\s*(\[.*\])|(?=^[-=]|^EXAMPLES))/gm;
var m;
while ((m = re.exec($scope.detailHelp.split("EXAMPLES")[0]+"EXAMPLES")) !== null) {
//while ((m = re.exec($scope.detailHelp.split("#")[0])) !== null) {
//while ((m = re.exec($scope.detailHelp)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
var option_name = m[1];
var description = m[2];
var choices = m[3];
var default_value = m[4];
var breakup = option_name.split(" ");
var variable_name = breakup[1];
var mandatory = breakup[0] == "=";
var complex_value = {};
if(default_value)
default_value = default_value.replace(/\[Default: (.*)\]/,"$1");
if(default_value == 'None')
default_value = null
var variable_value = default_value || '';
if(choices)
choices = choices.replace(/\s+/g,"").replace(/\n\s+/g,"").replace(/\(Choices:(.*)\)/,"$1").split(",");
if(override && module_copy.variables){
var matching_variable = module_copy.variables.filter(function(item){
if(item.name == variable_name){
return true
}
});
if(matching_variable.length){
variable_value = matching_variable[0].value;
if(typeof variable_value=='object'){
complex_value = angular.copy(variable_value)
}
}
}
module.variables.push({name:variable_name,description:description,mandatory:mandatory,value:variable_value,complexValue:complex_value,choices:choices,default_value:default_value});
$scope.getModuleDescriptionLoading = false;
}
}, function(response){
$scope.result = $sce.trustAsHtml(ansi2html.toHtml(response.data).replace(/\n/g, "<br>"));
console.log(ansi2html.toHtml(response.data));
$scope.detailHelp = response.data;
$scope.getModuleDescriptionLoading = false;
},refresh)
};
/**
* Reload Module Description and variables. Ignore displaying from cache.
* Used when a custom module is updated and description and variables info need to be updated.
* @param module - module object
*/
$scope.reloadModuleDetails = function(module){
if(selectedTask){
$scope.getModuleDescription(module,true,true)
}else{
$scope.getModuleDescription(module,false,true)
}
};
/**
* Identify module from a given task object.
* @param task - Single task object containing task properties
* @returns {{}}
*/
$scope.getModuleFromTask = function(task){
var moduleObject = {};
$scope.local_action = false;
var task_properties = null;
var module = ansible.getModuleFromTask(task);
if(module === 'include'){
module = null;
task.tags = task.include.replace(/.*tags=(.*)/,"$1")
return;
}else if(module === 'local_action'){
$scope.local_action = true;
module = task.local_action.module;
task_properties = task.local_action;
delete task_properties.module;
}else{
task_properties = task[module];
}
angular.forEach($scope.modules, function(item,index) {
if(item.name == module){
moduleObject = item;
$scope.newTask.module = item;
}
});
if(!(moduleObject && moduleObject.name)){
$scope.err_msg = "Unable to find module " + module + " in Ansible controller";
return
}
//moduleObject.name = module;
moduleObject.variables = [];
if(typeof task_properties == "string"){
moduleObject.variables.push({'name':'free_form','value':task_properties});
var re = /\b(\w+)=\s*([^=]*\S)\b\s*(?=\w+=|$)/g;
var m;
while ((m = re.exec(task_properties)) !== null) {
if (m.index === re.lastIndex) {
re.lastIndex++;
}
// View your result using the m-variable.
// eg m[0] etc.
var k=m[1];
var v=m[2];
moduleObject.variables.push({'name':k,'value':v})
}
}else if(typeof task_properties == "object"){
angular.forEach(task_properties,function(value,key){
this.push({'name':key,'value':value,'complexValue':value})
},moduleObject.variables)
}
return moduleObject
};
/**
* Create Task - Creates new task object and set task variables.
* Push new task object to tasksList
* Close new task modal
*/
$scope.createTask = function(){
if(!$scope.newTask.module && !$scope.newTask.include){
$scope.err_msg = "Must select atleast one module or include statement";
return
}
$scope.createTaskLoading = true;
if(!tasksList){
tasksList = []
}
var taskObject = {name:$scope.newTask.name};
if($scope.newTask.include)
taskObject['include'] = $scope.newTask.include;
if($scope.newTask.tags)
taskObject['tags'] = $scope.newTask.tags.split(',');
if($scope.newTask.register)
taskObject['register'] = $scope.newTask.register;
if($scope.newTask.async){
taskObject['async'] = $scope.newTask.async;
if(!$scope.newTask.poll)
$scope.newTask.poll = 10;
taskObject['poll'] = $scope.newTask.poll;
}
var variablesObject = null;
if($scope.newTask.module){
if($scope.newTask.module.singleLine){
variablesObject = "";
//Add all mandatory variables first
angular.forEach($scope.newTask.module.variables.filter(function(item){
return item.mandatory
}),function(item){
if(item.name == 'free_form'){
variablesObject += item.value;
}else if(item.value){
variablesObject += " " + item.name + "=" + item.value;
}
});
//Add optional variables
angular.forEach($scope.newTask.module.variables.filter(function(item){
return !item.mandatory
}),function(item){
if(item.value != item.default_value){
if(item.name == 'free_form'){
variablesObject += item.value;
}else if(item.value){
variablesObject += " " + item.name + "=" + item.value;
}
}
});
}else{
variablesObject = {};
angular.forEach($scope.newTask.module.variables,function(item){
if((item.value || (item.isComplexVariable && item.complexValue)) && item.value != item.default_value){
if(item.isComplexVariable){
variablesObject[item.name] = item.complexValue;
}else{
variablesObject[item.name] = item.value;
}
}
});
}
taskObject[$scope.newTask.module.name] = variablesObject;
if($scope.local_action){
variablesObject.module = $scope.newTask.module.name;
taskObject['local_action'] = variablesObject;
}
}
if(selectedTaskIndex != null){
// If Edit Task
tasksList[selectedTaskIndex] = taskObject
}else{
// If New Task
tasksList.push(taskObject);
}
$uibModalInstance.close(taskObject);
};
/**
* Close modal
*/
$scope.ok = function () {
$uibModalInstance.close($scope.newTask);
};
/**
* Cancel modal
*/
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
/**
* Get host variables using Ansible Python API in the backend
*/
$scope.getHostVars = function(){
if(!(selectedPlay && selectedPlay.play && selectedPlay.play.hosts))return;
ansible.getVars(Projects.selectedInventoryFileName,selectedPlay.play.hosts,function(response){
console.log(response.data);
if(response.data.length)
$scope.hostvars = $filter('dictToKeyValueArray')(response.data[0]);
else $scope.err_msg = "Getting host variables - No variables returned" ;
},function(error){
console.log(error.data);
$scope.err_msg = "Getting host variables - " + error.data;
})
};
if(selectedPlay)
$scope.getHostVars();
$scope.getRoleVars = function(){
if(!(selectedRole && selectedRole.role))return;
ansible.getRoleVars(selectedRole.role,function(response){
console.log(response.data);
if(response.data)
$scope.hostvars = $filter('dictToKeyValueArray')(response.data);
else $scope.err_msg = "Getting host variables - No variables returned" ;
},function(error){
console.log(error.data);
$scope.err_msg = "Getting host variables - " + error.data;
})
};
if(selectedRole)
$scope.getRoleVars();
if(!$scope.modules){
$scope.getAnsibleModules();
}
$scope.showComplexVariable = function(variable){
variable.isComplexVariable = true;
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/modals/complex_var_modal/complexVariable.html',
controller: 'ComplexVarModalController',
size: 'sm',
backdrop: 'static',
keyboard: false,
closeByEscape: false,
closeByDocument: false,
resolve: {
path: function () {
return variable.name
},
hostvars: function(){
return $scope.hostvars
},
members: function(){
return variable.complexValue
}
}
});
modalInstance.result.then(function (selectedItem) {
variable.complexValue = selectedItem
}, function () {
});
}
}
export default angular.module('webAppApp.new_task', [])
.controller('NewTaskController', newTaskController)
.name;

View file

@ -0,0 +1,17 @@
'use strict';
describe('Controller: NewTaskCtrl', function() {
// load the controller's module
beforeEach(module('webAppApp.new_task'));
var NewTaskCtrl;
// Initialize the controller and a mock scope
beforeEach(inject(function($controller) {
NewTaskCtrl = $controller('NewTaskCtrl', {});
}));
it('should ...', function() {
expect(1).to.equal(1);
});
});

View file

@ -0,0 +1,134 @@
<!--<script type="text/ng-template" id="createTaskContent.html">-->
<!-- Modal content-->
<div class="modal-content" >
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()" data-dismiss="modal">&times;</button>
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body" id="TaskCreationModal">
<div class="row">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-addon" >Name</span>
<input ng-model="newTask.name" type="text" class="form-control" placeholder="Task Name">
</div>
<div class="input-group">
<span class="input-group-addon" >Include</span>
<input type="text" ng-model="newTask.include" uib-typeahead="file.name as file.name for file in files | filter: $viewValue" class="form-control">
</div>
<div class="input-group">
<span class="input-group-addon" >Module</span>
<input type="text" ng-disabled="!modules || newTask.include" ng-model="newTask.module" typeahead-on-select="getModuleDescription(newTask.module)" uib-typeahead="module as module.name for module in modules | filter: $viewValue" class="form-control">
</div>
<i ng-if="modulesLoading" class="fa fa-spinner fa-spin"></i>
<div class="input-group">
<span class="input-group-addon" >Tags</span>
<input ng-model="newTask.tags" type="text" class="form-control" placeholder="Tags separated by comma">
</div>
<div class="input-group">
<span class="input-group-addon" >Async (s)</span>
<input ng-model="newTask.async" type="text" class="form-control" placeholder="Max run time in seconds">
</div>
<div ng-if="newTask.async">
<div class="hint">{{newTask.async / 60 | number}} minutes</div>
<div class="input-group" >
<span class="input-group-addon" >Poll (s)</span>
<input ng-model="newTask.poll" type="text" class="form-control" placeholder="10" ng-value="10">
</div>
</div>
<div class="input-group">
<span class="input-group-addon" >Register Result</span>
<input ng-model="newTask.register" type="text" class="form-control" placeholder="Variable Name">
</div>
<div class="hint">Register results of to a variable</div>
<label class="checkbox-inline"><input type="checkbox" ng-model="newTask.module.singleLine">Single Line</label>
<span ng-if="getModuleDescriptionLoading" class="fa fa-spinner fa-spin"></span>
<div ng-if="newTask.module.variables">
<h4>Arguments</h4>
<div ng-repeat="variable in newTask.module.variables | orderBy : '-mandatory'">
<!--<div class="input-group" ng-show="!variable.isVariable && !variable.choices.length">
<span class="input-group-addon" >{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
<input ng-model="variable.value" type="text" class="form-control" required="{{variable.mandatory}}">
</div>-->
<!--Input Type - Default Typeahead - variable or string-->
<div class="input-group" ng-show="!variable.choices.length">
<span class="input-group-addon" uib-tooltip="{{variable.description}}">{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
<!--<select class="form-control" ng-model="variable.value" ng-options="('&quot;\{\{' + var.name + '\}\}&quot;') as (var.name + '-' + var.value) disable when var.disabled for var in hostvars">
</select>-->
<input type="text" ng-model="variable.value" uib-typeahead="('\{\{' + var.key + '\}\}') as (var.key + ' = ' + var.value) for var in hostvars | filter: $viewValue" class="form-control" placeholder="{{variable.description}}">
<span class="input-group-btn">
<button class="btn btn-secondary" ng-click="showComplexVariable(variable)" uib-tooltip="Click to configure Complex Variable">{ }</button>
</span>
</div>
<!--Input Type - Choice-->
<div class="input-group" ng-show="variable.choices.length">
<span class="input-group-addon" uib-tooltip="{{variable.description}}">{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
<select class="form-control" ng-model="variable.value" ng-options="choice for choice in variable.choices">
</select>
</div>
<!--<div class="input-group" ng-show="variable.isComplexVariable">
<span class="input-group-addon" >{{ variable.mandatory ? "*" : "" }} {{variable.name}}</span>
&lt;!&ndash;<json-tree json="variable.complexValue"></json-tree>&ndash;&gt;
<button class="btn btn-default" ng-click="showComplexVariable(variable)">Configure</button>
</div>-->
<!--<label class="checkbox-inline"><input type="checkbox" ng-model="variable.isVariable">Variable</label>-->
<label class="checkbox-inline" ng-show="variable.isVariable"><input type="checkbox" ng-model="variable.isComplexVariable">Complex Variable</label>
</div>
</div>
</div>
<div class="col-md-6">
<div ng-if="showHelp">
<h4>Help</h4>
<textarea rows="20" cols="60" ng-model="detailHelp" style="font-size: 13px"></textarea>
<h4>Examples</h4>
<textarea rows="20" cols="60" ng-model="examples" style="font-size: 13px"></textarea>
</div>
<!--{{newTask.module.variables}}-->
</div>
</div>
</div>
<div class="alert alert-danger" ng-if="err_msg">{{err_msg}}</div>
<div class="modal-footer">
<div class="row">
<div class="col-md-3" style="text-align: left">
<button class="btn btn-default" type="button" ng-click="reloadModuleDetails(newTask.module)">Reload Module <span class="fa fa-refresh"></span></button>
</div>
<div class="col-md-9" style="text-align: right">
<button class="btn btn-primary" ng-click="createTask()">Save <span ng-if="createTaskLoading" class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-default" type="button" ng-click="cancel()">Close</button>
</div>
</div>
</div>
</div>
<!--
</script>
-->

View file

View file

@ -0,0 +1,229 @@
'use strict';
const angular = require('angular');
export default angular.module('webAppApp.tasks', [])
.directive('tasks', function(ansible, $uibModal) {
return {
templateUrl: 'app/designer/tasks/tasks.html',
restrict: 'EA',
scope: {
tasksList: '=',
selectedPlay: '=',
savePlaybook: '&',
selectedRole: '=',
updatePlaybookFileContent: '&',
executeAnsiblePlayBook: '&',
files: '=' //List of files for include purpose
},
link: function (scope, element, attrs) {
scope.getModuleFromTask = ansible.getModuleFromTask;
scope.buttonStates = {loading:false,save:false,err_msg:false};
scope.tasksMetaData = [];
scope.$watch('tasksList',function(){
console.log('tasks list changed');
scope.tasksMetaData = [];
angular.forEach(scope.tasksList,function(task){
var taskModule = ansible.getModuleFromTask(task);
var taskName = task.name;
if(taskModule === 'include'){
taskName = task[taskModule].replace(/(.*yml) .*/,"$1")
}
scope.tasksMetaData.push({taskModule:taskModule,taskName:taskName,selected:false})
})
},true);
/**
* Detect when the user selects tasks.
* Enable play button if tasks are selected and has tags assigned
* Enable delete button if tasks are selected
*/
scope.$watch('tasksMetaData',function(newValue,oldValue){
scope.selectedTasksPlayButton = false;
scope.selectedTasksDeleteButton = false
if(!(scope.tasksMetaData))return;
var selectedTasks = scope.tasksMetaData.filter(item => item.selected);
var includeTasks = scope.tasksMetaData.filter(item => item.taskModule === 'include');
var selectedTasksWithoutTags = [];
/**
* Find selected tasks without any tags.
* If there are any play button will not be enabled
*/
angular.forEach(scope.tasksMetaData,function(item,index){
scope.tasksListItem = scope.tasksList[index];
if(!scope.tasksListItem.tags && item.selected){
selectedTasksWithoutTags.push(scope.tasksListItem)
}
});
console.log("selectedTasksWithoutTags=")
console.log(selectedTasksWithoutTags)
if(selectedTasks.length){
//if(!includeTasks.length && !selectedTasksWithoutTags.length){
if(!selectedTasksWithoutTags.length){
scope.selectedTasksPlayButton = true
}
scope.selectedTasksDeleteButton = true
}else{
scope.selectedTasksPlayButton = false;
scope.selectedTasksDeleteButton = false
}
},true);
//scope.moveUp = scope.moveUp();
//scope.moveDown = scope.moveDown();
scope.savePlaybook = scope.savePlaybook();
scope.updatePlaybookFileContent = scope.updatePlaybookFileContent();
scope.executeAnsiblePlayBook = scope.executeAnsiblePlayBook();
scope.showTaskModal = function(selectedTaskIndex, copyTask){
var modalInstance = $uibModal.open({
animation: true,
/*templateUrl: 'createTaskContent.html',*/
templateUrl: 'app/designer/tasks/new_task/new_task.html',
controller: 'NewTaskController',
size: 'lg',
backdrop : 'static',
keyboard : false,
closeByEscape : false,
closeByDocument : false,
resolve: {
selectedProject: function () {
return scope.$parent.selectedProject;
},
selectedPlay: function(){
return scope.selectedPlay
},
selectedRole: function(){
return scope.selectedRole
},
tasksList: function () {
return scope.tasksList;
},
selectedTaskIndex: function(){
return selectedTaskIndex
},
copyTask : function(){
return copyTask
},
//List of files for include purpose
files: function(){
return scope.files
}
}
});
modalInstance.result.then(
function (newTask) {
// if(!selectedTaskIndex)
// scope.tasksList.push(newTask);
scope.updatePlaybookFileContent(true);
//$scope.selectedPlay = {play: ""};
}, function () {
});
};
scope.deleteTask = function(index){
scope.tasksList.splice(index,1);
scope.updatePlaybookFileContent(true);
};
scope.deleteTasks = function(){
scope.tasksMetaData.filter(function(item, index){
if(item.selected){
scope.tasksList.splice(index,1);
}
});
scope.updatePlaybookFileContent(true);
};
scope.moveUp = function(list,index,buttonVariable){
if(!scope.preChangeData) scope.preChangeData = angular.copy(list);
var temp = angular.copy(list[index]);
list[index] = list[index-1];
list[index-1] = temp;
scope.updatePlaybookFileContent(false);
scope.buttonStates.save = true
};
scope.cancelChange = function(buttonVariable){
if(scope.preChangeData){
//scope.tasksList = angular.copy(scope.preChangeData);
scope.selectedPlay.play.tasks = angular.copy(scope.preChangeData);
scope.preChangeData = null
}
scope.updatePlaybookFileContent(false,null,scope.tasksList);
scope.buttonStates.save = false;
};
scope.moveDown = function(list,index,buttonVariable){
if(!scope.preChangeData) scope.preChangeData = angular.copy(list);
var temp = angular.copy(list[index]);
list[index] = list[index+1];
list[index+1] = temp;
scope.updatePlaybookFileContent(false);
scope.buttonStates.save = true;
};
scope.executeSelectedTasks = function(){
/*var selectedTasks = scope.tasksMetaData.map(function(item){return item.selected});*/
var selectedTags = [];
var selectedTaskNames = [];
/*if(selectedTasks.length){
selectedTags = selectedTasks.map(function(item){return item.tags});
selectedTaskNames = selectedTasks.map(function(item){return item.name})
}*/
angular.forEach(scope.tasksMetaData, function(item,index){
if(item.selected){
if(scope.tasksList[index].tags){
// As tags is an array and each task can have multiple tags
var task_tags = scope.tasksList[index].tags
if(typeof task_tags == 'object')
task_tags = task_tags[0] //task_tags.join(',')
selectedTags.push(task_tags);
selectedTaskNames.push(scope.tasksList[index].name)
}
}
});
if(selectedTags.length){
var play = scope.selectedPlay && scope.selectedPlay.play;
scope.executeAnsiblePlayBook(selectedTags,'Tasks',selectedTaskNames.join(","),play)
}
};
}
};
})
.name;

View file

@ -0,0 +1,20 @@
'use strict';
describe('Directive: tasks', function() {
// load the directive's module and view
beforeEach(module('webAppApp.tasks'));
beforeEach(module('app/designer/tasks/tasks.html'));
var element, scope;
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
}));
it('should make hidden element visible', inject(function($compile) {
element = angular.element('<tasks></tasks>');
element = $compile(element)(scope);
scope.$apply();
expect(element.text()).to.equal('this is the tasks directive');
}));
});

View file

@ -0,0 +1,53 @@
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Module</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="task in tasksList">
<td><input type="checkbox" ng-model="tasksMetaData[$index].selected">
</td>
<td>{{tasksMetaData[$index].taskName}}</td>
<td>{{tasksMetaData[$index].taskModule}}</td>
<td>
<div class="btn-group">
<label class="btn btn-default btn-sm" ng-click="showTaskModal($index)"><span
class="fa fa-edit"></span></label>
<!--<label class="btn btn-default btn-sm" ng-click="showTaskModal($index,true)"><span
class="fa fa-copy"></span></label>-->
<!--<label class="btn btn-danger btn-sm" ng-click="deleteTask($index)"
confirm="Are you sure you want to delete?"><span class="fa fa-trash"></span></label>-->
<!--<div style="display: inline-block" tooltip-enable="!task.tags"
uib-tooltip="Tag must be assigned to play individually"><label class="btn btn-success btn-sm"
ng-disabled="!task.tags"
ng-click="executeAnsiblePlayBook(task.tags,'Task',task.name, selectedPlay)"><span
class="fa fa-play"></span></label></div>-->
<label class="btn btn-primary btn-sm" ng-disabled="$first"
ng-click="moveUp(tasksList,$index,'saveTaskListLoading')"><span
class="fa fa-arrow-up"></span></label>
<label class="btn btn-primary btn-sm" ng-disabled="$last"
ng-click="moveDown(tasksList,$index,'saveTaskListLoading')"><span
class="fa fa-arrow-down"></span></label>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<button class="btn btn-default" data-toggle="modal" data-target="#createTaskModal" ng-click="showTaskModal()">
Create Task <span class="fa fa-plus"></span></button>
<button class="btn btn-success" data-toggle="modal" ng-disabled="!selectedTasksPlayButton" ng-if="!buttonStates.save"
ng-click="executeSelectedTasks()"> Play <span class="fa fa-play"></span></button>
<button class="btn btn-danger" data-toggle="modal" ng-disabled="!selectedTasksDeleteButton" ng-if="!buttonStates.save" confirm="Are you sure you want to delete the selected tasks?"
ng-click="deleteTasks()"> Delete <span class="fa fa-trash-o"></span></button>
<button class="btn btn-primary" ng-if="buttonStates.save"
ng-click="savePlaybook(buttonStates)">Save <span ng-if="buttonStates.loading"
class="fa fa-spinner fa-spin"></span></button>
<button class="btn btn-warning" ng-if="buttonStates.save"
ng-click="cancelChange(buttonStates)">Cancel <span class="fa fa-times"></span></button>