Merge branch 'devel' of github.com:ansible/ansible-tower into merge-devel

This commit is contained in:
Akita Noek 2016-02-22 09:37:14 -05:00
commit 161f4f22cf
32 changed files with 2554 additions and 2018 deletions

View File

@ -1,5 +1,10 @@
import pytest
from django.core.urlresolvers import resolve
from django.utils.six.moves.urllib.parse import urlparse
from awx.main.models.organization import Organization
from awx.main.models.ha import Instance
from django.contrib.auth.models import User
from rest_framework.test import (
@ -105,13 +110,16 @@ def permissions():
@pytest.fixture
def post():
def rf(_cls, _user, _url, pk=None, kwargs={}, middleware=None):
view = _cls.as_view()
request = APIRequestFactory().post(_url, kwargs, format='json')
def rf(url, data, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().post(url, data, **kwargs)
if middleware:
middleware.process_request(request)
force_authenticate(request, user=_user)
response = view(request, pk=pk)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
@ -119,13 +127,101 @@ def post():
@pytest.fixture
def get():
def rf(_cls, _user, _url, pk=None, middleware=None):
view = _cls.as_view()
request = APIRequestFactory().get(_url, format='json')
def rf(url, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().get(url, **kwargs)
if middleware:
middleware.process_request(request)
force_authenticate(request, user=_user)
response = view(request, pk=pk)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
return rf
@pytest.fixture
def put():
def rf(url, data, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().put(url, data, **kwargs)
if middleware:
middleware.process_request(request)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
return rf
@pytest.fixture
def patch():
def rf(url, data, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().patch(url, data, **kwargs)
if middleware:
middleware.process_request(request)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
return rf
@pytest.fixture
def delete():
def rf(url, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().delete(url, **kwargs)
if middleware:
middleware.process_request(request)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
return rf
@pytest.fixture
def head():
def rf(url, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().head(url, **kwargs)
if middleware:
middleware.process_request(request)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response
return rf
@pytest.fixture
def options():
def rf(url, data, user=None, middleware=None, **kwargs):
view, view_args, view_kwargs = resolve(urlparse(url)[2])
if 'format' not in kwargs:
kwargs['format'] = 'json'
request = APIRequestFactory().options(url, data, **kwargs)
if middleware:
middleware.process_request(request)
if user:
force_authenticate(request, user=user)
response = view(request, *view_args, **view_kwargs)
if middleware:
middleware.process_response(request, response)
return response

View File

@ -1,11 +1,6 @@
import mock
import pytest
from awx.api.views import (
ActivityStreamList,
ActivityStreamDetail,
OrganizationList,
)
from awx.main.middleware import ActivityStreamMiddleware
from awx.main.models.activity_stream import ActivityStream
from django.core.urlresolvers import reverse
@ -17,7 +12,7 @@ def mock_feature_enabled(feature, bypass_database=None):
@pytest.mark.django_db
def test_get_activity_stream_list(monkeypatch, organization, get, user):
url = reverse('api:activity_stream_list')
response = get(ActivityStreamList, user('admin', True), url)
response = get(url, user('admin', True))
assert response.status_code == 200
@ -31,7 +26,7 @@ def test_basic_fields(monkeypatch, organization, get, user):
aspk = activity_stream.pk
url = reverse('api:activity_stream_detail', args=(aspk,))
response = get(ActivityStreamDetail, user('admin', True), url, pk=aspk)
response = get(url, user('admin', True))
assert response.status_code == 200
assert 'related' in response.data
@ -46,8 +41,9 @@ def test_middleware_actor_added(monkeypatch, post, get, user):
u = user('admin-poster', True)
url = reverse('api:organization_list')
response = post(OrganizationList, u, url,
kwargs=dict(name='test-org', description='test-desc'),
response = post(url,
dict(name='test-org', description='test-desc'),
u,
middleware=ActivityStreamMiddleware())
assert response.status_code == 201
@ -55,7 +51,7 @@ def test_middleware_actor_added(monkeypatch, post, get, user):
activity_stream = ActivityStream.objects.filter(organization__pk=org_id).first()
url = reverse('api:activity_stream_detail', args=(activity_stream.pk,))
response = get(ActivityStreamDetail, u, url, pk=activity_stream.pk)
response = get(url, u)
assert response.status_code == 200
assert response.data['summary_fields']['actor']['username'] == 'admin-poster'

View File

@ -549,7 +549,7 @@
word-break: break-all;
word-wrap: break-word;
padding: 9.5px;
ont-family: Fixed, monospace;
font-family: Fixed, monospace;
max-height: 200px;
}

View File

@ -31,6 +31,7 @@ import systemTracking from './system-tracking/main';
import inventoryScripts from './inventory-scripts/main';
import permissions from './permissions/main';
import managementJobs from './management-jobs/main';
import jobDetail from './job-detail/main';
// modules
import setupMenu from './setup-menu/main';
@ -43,8 +44,7 @@ import templateUrl from './shared/template-url/main';
import adhoc from './adhoc/main';
import login from './login/main';
import activityStream from './activity-stream/main';
import {JobDetailController} from './controllers/JobDetail';
import {JobStdoutController} from './controllers/JobStdout';
import standardOut from './standard-out/main';
import {JobTemplatesList, JobTemplatesAdd, JobTemplatesEdit} from './controllers/JobTemplates';
import {LicenseController} from './controllers/License';
import {ScheduleEditController} from './controllers/Schedules';
@ -95,6 +95,8 @@ var tower = angular.module('Tower', [
login.name,
activityStream.name,
footer.name,
jobDetail.name,
standardOut.name,
'templates',
'Utilities',
'LicenseHelper',
@ -293,83 +295,6 @@ var tower = angular.module('Tower', [
}
}).
state('jobDetail', {
url: '/jobs/:id',
templateUrl: urlPrefix + 'partials/job_detail.html',
controller: JobDetailController,
ncyBreadcrumb: {
parent: 'jobs',
label: "{{ job.id }} - {{ job.name }}"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
if (!$rootScope.event_socket) {
$rootScope.event_socket = Socket({
scope: $rootScope,
endpoint: "job_events"
});
$rootScope.event_socket.init();
return true;
} else {
return true;
}
}]
}
}).
state('jobsStdout', {
url: '/jobs/:id/stdout',
templateUrl: urlPrefix + 'partials/job_stdout.html',
controller: JobStdoutController,
ncyBreadcrumb: {
parent: 'jobDetail',
label: "STANDARD OUT"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
if (!$rootScope.event_socket) {
$rootScope.event_socket = Socket({
scope: $rootScope,
endpoint: "job_events"
});
$rootScope.event_socket.init();
return true;
} else {
return true;
}
}]
}
}).
state('adHocJobStdout', {
url: '/ad_hoc_commands/:id',
templateUrl: urlPrefix + 'partials/job_stdout_adhoc.html',
controller: JobStdoutController,
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
adhocEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
if (!$rootScope.adhoc_event_socket) {
$rootScope.adhoc_event_socket = Socket({
scope: $rootScope,
endpoint: "ad_hoc_command_events"
});
$rootScope.adhoc_event_socket.init();
return true;
} else {
return true;
}
}]
}
}).
state('jobTemplates', {
url: '/job_templates',
templateUrl: urlPrefix + 'partials/job_templates.html',
@ -1062,6 +987,9 @@ var tower = angular.module('Tower', [
$rootScope.$emit('JobStatusChange-jobs', data);
} else if (/\/jobs\/(\d)+\/stdout/.test(urlToCheck) ||
/\/ad_hoc_commands\/(\d)+/.test(urlToCheck)) {
// TODO: something will need to change here for stdout
$log.debug("sending status to standard out");
$rootScope.$emit('JobStatusChange-jobStdout', data);
} else if (/\/jobs\/(\d)+/.test(urlToCheck)) {

File diff suppressed because it is too large Load Diff

View File

@ -22,12 +22,11 @@ export default
* Initialize calling scope with all the bits required to support a jobs list
*
*/
.factory('JobsControllerInit', ['$location', 'Find', 'DeleteJob', 'RelaunchJob', 'LogViewer', '$window',
function($location, Find, DeleteJob, RelaunchJob, LogViewer, $window) {
.factory('JobsControllerInit', ['$state', 'Find', 'DeleteJob', 'RelaunchJob', 'LogViewer', '$window',
function($state, Find, DeleteJob, RelaunchJob, LogViewer, $window) {
return function(params) {
var scope = params.scope,
iterator = (params.iterator) ? params.iterator : scope.iterator;
//base = $location.path().replace(/^\//, '').split('/')[0];
scope.deleteJob = function(id) {
DeleteJob({ scope: scope, id: id });
@ -70,53 +69,39 @@ export default
};
scope.refreshJobs = function() {
// if (base !== 'jobs') {
scope.search(iterator);
// }
scope.search(iterator);
};
scope.viewJobLog = function(id) {
var list, job;
if (scope.completed_jobs) {
list = scope.completed_jobs;
}
else if (scope.running_jobs) {
list = scope.running_jobs;
}
else if (scope.queued_jobs) {
list = scope.queued_jobs;
}
else if (scope.jobs) {
list = scope.jobs;
}
else if(scope.all_jobs){
list = scope.all_jobs;
}
else if(scope.portal_jobs){
list=scope.portal_jobs;
}
job = Find({ list: list, key: 'id', val: id });
if (job.type === 'job') {
scope.viewJobDetails = function(job) {
var goToJobDetails = function(state) {
if(scope.$parent.portalMode===true){
$window.open('/#/jobs/' + job.id, '_blank');
var url = $state.href(state, {id: job.id});
$window.open(url, '_blank');
}
else {
$location.url('/jobs/' + job.id);
$state.go(state, {id: job.id});
}
} else if (job.type === 'ad_hoc_command') {
if(scope.$parent.portalMode===true){
$window.open('/#/ad_hoc_commands/' + job.id, '_blank');
}
else {
$location.url('/ad_hoc_commands/' + job.id);
}
} else {
LogViewer({
scope: scope,
url: job.url
});
}
switch(job.type) {
case 'job':
goToJobDetails('jobDetail');
break;
case 'ad_hoc_command':
goToJobDetails('adHocJobStdout');
break;
case 'system_job':
goToJobDetails('managementJobStdout');
break;
case 'project_update':
goToJobDetails('scmUpdateStdout');
break;
case 'inventory_update':
goToJobDetails('inventorySyncStdout');
break;
}
};
};
}

View File

@ -0,0 +1,58 @@
/** @define SetupItem */
@import '../shared/branding/colors.less';
@import '../shared/branding/colors.default.less';
.JobDetail-panelHeader{
height: 50px;
display: flex;
}
.JobDetail-panelHeaderText{
color: @default-interface-txt;
flex: 1 0 auto;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
text-transform: uppercase;
}
.JobDetail-panelHeaderText:hover{
color: @default-interface-txt;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
text-transform: uppercase;
}
.JobDetail-expandArrow{
color: @default-icon-hov;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
text-transform: uppercase;
margin-left: 10px;
}
.JobDetail-resultsDetails{
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.JobDetail-resultRow{
width: 50%;
display: flex;
}
.JobDetail-resultRow label{
color: @default-interface-txt;
font-size: 14px;
font-weight: normal!important;
flex: 1 0 auto;
}
.JobDetail-resultRowText{
width: 40%;
flex: 1 0 auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,25 @@
<div class="tab-pane" id="jobs-detail">
<div ng-cloak id="htmlTemplate">
<div class="row" style="position: relative;">
<div id="job-detail-container">
<div class="job_well">
<div class="row">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3">Status</label>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-5 job_status"><i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div>
<div class="col-lg-4 text-right JobDetails-status">
<a href="" id="play-help" aw-pop-over="Live event processing is now paused. Click here to resume." id="play-button-help" data-placement="left" ng-show="pauseLiveEvents" ><i class="fa fa-question"></i></a>
<a href="" ng-click="togglePlayButton()" id="play-button" class="btn btn-primary btn-xs" aw-tool-tip="Resume viewing live events" data-placement="top" ng-show="pauseLiveEvents" style="margin-right:25px;"><i class="fa fa-play"></i></a>
<a href="/#/jobs/{{ job_id }}/stdout" id="view-stdout-button" target="_blank" type="button" class="btn btn-primary btn-xs" aw-tool-tip="View standard out. Opens in new tab or window." data-placement="top"><i class="fa fa-external-link"></i></a>
<button type="button" class="btn btn-xs btn-primary ng-hide" ng-click="refresh()" id="refresh_btn" aw-tool-tip="Refresh the page" data-placement="top" ng-show="socketStatus == 'error'"
data-original-title="" title=""><i class="fa fa-refresh"></i></button>
<a href="" ng-click="deleteJob()" id="cancel-job-button" ng-show="job_status.status == 'running' || job_status.status=='pending' " type="button" class="btn btn-primary btn-xs" aw-tool-tip="Cancel" data-placement="top"><i class="fa fa-minus-circle"></i></a>
<a href="" ng-click="deleteJob()" id="delete-job-button" ng-hide="job_status.status == 'running' || job_status.status == 'pending' " type="button" class="btn btn-primary btn-xs" aw-tool-tip="Delete" data-placement="top"><i class="fa fa-trash-o"></i></a>
<a href="" ng-click="relaunchJob()" id="relaunch-job-button" type="button" class="btn btn-primary btn-xs" aw-tool-tip="Relaunch using the same parameters" data-placement="top"><i class="fa fa-rocket"></i></a>
<button type="button" id="summary-button" class="btn btn-primary btn-xs" ng-click="toggleSummary()" aw-tool-tip="View summary" data-placement="top"><i class="fa fa-arrow-circle-left"></i></button>
</div>
<div class="JobDetail-resultsContainer Panel">
<div class="JobDetail-panelHeader">
<a class="JobDetail-panelHeaderText" ng-show="lessStatus" href="" ng-click="toggleLessStatus()">
RESULTS<i class="JobDetail-expandArrow fa fa-caret-left"></i>
</a>
<a class="JobDetail-panelHeaderText" ng-show="!lessStatus" href="" ng-click="toggleLessStatus()">
RESULTS<i class="JobDetail-expandArrow fa fa-caret-down"></i>
</a>
<button id="submit-action" class="List-actionButton JobDetail-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Start a job using this template" data-original-title="" title=""><i class="fa fa-rocket"></i> </button>
<button id="delete-action" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJobTemplate(job_template.id, job_template.name)" aw-tool-tip="Delete template" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
</div>
<div class="form-horizontal" role="form" id="job-status-form">
<div class="form-group" ng-show="job_status.explanation">
<div class="form-horizontal JobDetail-resultsDetails" role="form" id="job-status-form">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Status</label>
<div class="JobDetail-resultRowText"><i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div>
</div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.explanation">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12">Explanation</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
ng-show="!previousTaskFailed" ng-bind-html="job_status.explanation"></div>
@ -44,117 +40,116 @@
</div>
</div>
<div class="form-group" ng-show="job_status.traceback">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.traceback">
<label class="col-lg-2 col-md-12 col-sm-12 col-xs-12">Results Traceback</label>
<div class="col-lg-10 col-md-12 col-sm-12 col-xs-12 job_status_traceback" ng-bind-html="job_status.traceback"></div>
<div class="JobDetail-resultRowText col-lg-10 col-md-12 col-sm-12 col-xs-12 job_status_traceback" ng-bind-html="job_status.traceback"></div>
</div>
<div class="form-group" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Timing</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-9">
<div ng-show="job_status.started" id="started-time">Started &nbsp;{{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</div>
<div ng-show="job_status.finished" id="finished-time">Finished &nbsp;{{ job_status.finished | date:'MM/dd/yy HH:mm:ss' }}</div>
<div ng-show="job_status.finished" id="elapsed-time">Elapsed &nbsp;{{ job_status.elapsed }}</div>
</div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Started</label>
<div class="JobDetail-resultRowText">{{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job_template_name">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Finished</label>
<div class="JobDetail-resultRowText">{{ job_status.finished | date:'MM/dd/yy HH:mm:ss' }}</div>
</div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Elapsed</label>
<div class="JobDetail-resultRowText">{{ job_status.elapsed }}</div>
</div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_template_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Template</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href="{{ job_template_url }}" aw-tool-tip="Edit the job template" data-placement="top">{{ job_template_name }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job_type">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_type">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Type</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ job_type }}</div>
<div class="JobDetail-resultRowText">{{ job_type }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="created_by">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="created_by">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href="{{ users_url }}" aw-tool-tip="Edit the User" data-placement="top">{{ created_by }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="scheduled_by">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="scheduled_by">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href aw-tool-tip="Edit the Schedule" data-placement="top" ng-click="editSchedule()">{{scheduled_by}}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="inventory_name">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="inventory_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Inventory</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href="{{ inventory_url }}" aw-tool-tip="Edit the inventory" data-placement="top">{{ inventory_name }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="project_name">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="project_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Project</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href="{{ project_url }}" aw-tool-tip="Edit the project" data-placement="top">{{ project_name }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job.playbook">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.playbook">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Playbook</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ job.playbook }}</div>
<div class="JobDetail-resultRowText">{{ job.playbook }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="credential_name">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="credential_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Machine Credential</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText JobDetail-resultRowText">
<a href="{{ credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ credential_name }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="cloud_credential_name">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="cloud_credential_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Cloud Credential</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<a href="{{ cloud_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ cloud_credential_name }}</a>
</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job.forks">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.forks">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Forks</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ job.forks }}</div>
<div class="JobDetail-resultRowText">{{ job.forks }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job.limit">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.limit">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Limit</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ job.limit }}</div>
<div class="JobDetail-resultRowText">{{ job.limit }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="verbosity">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="verbosity">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Verbosity</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ verbosity }}</div>
<div class="JobDetail-resultRowText">{{ verbosity }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="job.job_tags">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.job_tags">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Tags</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">{{ job.job_tags }}</div>
<div class="JobDetail-resultRowText">{{ job.job_tags }}</div>
</div>
<div class="form-group toggle-show" style="display:none;" ng-show="variables">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="variables">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Extra Variables</label>
<div class="col-lg-10- col-md-10 col-sm-10 col-xs-9">
<div class="JobDetail-resultRowText">
<div id="pre-formatted-variables">{{ variables }}</div>
<!-- <pre>{{ variables }}</pre> -->
</div>
</div>
<div class="row">
<div class="col-sm-12 more-or-less">
<a ng-show="lessStatus" href="" ng-click="toggleLessStatus()">more <i class="fa fa-angle-down"></i></a>
<a ng-show="!lessStatus" href="" ng-click="toggleLessStatus()">less <i class="fa fa-angle-up"></i></a>
</div>
</div>
<hr>
</div>
</div>
<!--- JobDetail-results---------------------------------------------->
<div id="job-detail-tables">
<div id="play-section" class="section">
@ -378,11 +373,11 @@
</div><!-- section -->
</div><!-- job-detail-tables -->
</div><!-- well -->
</div><!-- job-detail-container -->
<div id="job-summary-container">
<!-- <div id="job-summary-container"> -->
<div class="job_well">
<div id="summary-well-top-section">
<div id="hide-summary-button" style="display: hidden;">

View File

@ -0,0 +1,35 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../shared/template-url/template-url.factory';
export default {
name: 'jobDetail',
url: '/jobs/:id',
templateUrl: templateUrl('job-detail/job-detail'),
controller: 'JobDetailController',
ncyBreadcrumb: {
parent: 'jobs',
label: "{{ job.id }} - {{ job.name }}"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
if (!$rootScope.event_socket) {
$rootScope.event_socket = Socket({
scope: $rootScope,
endpoint: "job_events"
});
$rootScope.event_socket.init();
return true;
} else {
return true;
}
}]
}
};

View File

@ -0,0 +1,15 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import route from './job-detail.route';
import controller from './job-detail.controller';
export default
angular.module('jobDetail', [])
.controller('JobDetailController', controller)
.run(['$stateExtender', function($stateExtender) {
$stateExtender.addState(route);
}]);

View File

@ -1,34 +0,0 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/** @define StandardOutDetails */
// Some of these are left empty as a helpful measure so that you can see how the new
// SuitCSS styling should work. They can be removed once we've done more
// SuitCSS
.StandardOutDetails {
}
.StandardOutDetails-detailRow {
margin-bottom: 15px;
}
.StandardOutDetails-detailRow--closable {
display: none;
}
.StandardOutDetails-detailLabel {
}
.StandardOutDetails-detailContent {
}
.StandardOutDetails-closedToggle {
}
.StandardOutDetails-closedToggleLink {
}

View File

@ -25,7 +25,7 @@ export default
dataTitle: "{{ all_job.status_popover_title }}",
icon: 'icon-job-{{ all_job.status }}',
iconOnly: true,
ngClick:"viewJobLog(all_job.id)",
ngClick:"viewJobDetails(all_job)",
searchable: true,
searchType: 'select',
nosort: true,
@ -38,7 +38,7 @@ export default
},
id: {
label: 'ID',
ngClick:"viewJobLog(all_job.id)",
ngClick:"viewJobDetails(all_job)",
searchType: 'int',
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
awToolTip: "{{ all_job.status_tip }}",
@ -47,7 +47,7 @@ export default
name: {
label: 'Name',
columnClass: 'col-lg-3 col-md-3 col-sm-4 col-xs-6',
ngClick: "viewJobLog(all_job.id, all_job.nameHref)",
ngClick: "viewJobDetails(all_job)",
defaultSearchField: true,
awToolTip: "{{ all_job.name | sanitize }}",
dataPlacement: 'top'
@ -87,13 +87,6 @@ export default
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
stdout: {
mode: 'all',
href: '/#/jobs/{{ all_job.id }}/stdout',
awToolTip: 'View standard output',
dataPlacement: 'top',
ngShow: "all_job.type == 'job'"
},
submit: {
icon: 'icon-rocket',
mode: 'all',

View File

@ -27,7 +27,7 @@ export default
dataTitle: "{{ completed_job.status_popover_title }}",
icon: 'icon-job-{{ completed_job.status }}',
iconOnly: true,
ngClick:"viewJobLog(completed_job.id)",
ngClick:"viewJobDetails(completed_job)",
searchable: true,
searchType: 'select',
nosort: true,
@ -40,7 +40,7 @@ export default
},
id: {
label: 'ID',
ngClick:"viewJobLog(completed_job.id)",
ngClick:"viewJobDetails(completed_job)",
searchType: 'int',
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
awToolTip: "{{ completed_job.status_tip }}",
@ -49,7 +49,7 @@ export default
name: {
label: 'Name',
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
ngClick: "viewJobLog(completed_job.id, completed_job.nameHref)",
ngClick: "viewJobDetails(completed_job)",
defaultSearchField: true,
awToolTip: "{{ completed_job.name | sanitize }}",
dataPlacement: 'top'
@ -89,13 +89,6 @@ export default
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
stdout: {
mode: 'all',
href: '/#/jobs/{{ completed_job.id }}/stdout',
awToolTip: 'View standard output',
dataPlacement: 'top',
ngShow: "completed_job.type == 'job'"
},
submit: {
icon: 'icon-rocket',
mode: 'all',
@ -110,11 +103,5 @@ export default
awToolTip: 'Delete the job',
dataPlacement: 'top'
}
// job_details: {
// mode: 'all',
// ngClick: "viewJobLog(completed_job.id)",
// awToolTip: 'View job details',
// dataPlacement: 'top'
// }
}
});

View File

@ -20,7 +20,7 @@ export default
fields: {
id: {
label: 'ID',
ngClick:"viewJobLog(job.id)",
ngClick:"viewJobDetails(job)",
key: true,
desc: true,
searchType: 'int',
@ -36,7 +36,7 @@ export default
dataTitle: "{{ job.status_popover_title }}",
icon: 'icon-job-{{ job.status }}',
iconOnly: true,
ngClick:"viewJobLog(job.id)",
ngClick:"viewJobDetails(job)",
searchable: true,
nosort: true,
searchType: 'select',
@ -66,7 +66,7 @@ export default
name: {
label: 'Name',
columnClass: 'col-md-3 col-xs-5',
ngClick: "viewJobLog(job.id, job.nameHref)",
ngClick: "viewJobDetails(job)",
defaultSearchField: true
}
},
@ -74,13 +74,6 @@ export default
actions: { },
fieldActions: {
stdout: {
mode: 'all',
href: '/#/jobs/{{ job.id }}/stdout',
awToolTip: 'View standard output',
dataPlacement: 'top',
ngShow: "job.type == 'job'"
},
submit: {
mode: 'all',
icon: 'icon-rocket',

View File

@ -64,7 +64,7 @@ export default
job_details: {
mode: 'all',
ngClick: "viewJobLog(portal_job.id)",
ngClick: "viewJobDetails(portal_job)",
awToolTip: 'View job details',
dataPlacement: 'top'
}

View File

@ -1,38 +0,0 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="StandardOut-heading">
<div class="row StandardOut-breadcrumbs">
<div id="home-list-actions" class="list-actions pull-right col-md-12">
<button type="button" class="btn btn-xs btn-primary ng-hide" ng-click="refresh()" id="refresh_btn" aw-tool-tip="Refresh the page" data-placement="top" ng-show="socketStatus == 'error'" data-original-title="" title=""><i class="fa fa-refresh fa-lg"></i> </button></div>
</div>
<div class="row StandardOut-form">
<div class="col-md-12">
<div id="job-status"><label>Job Status</label> <i class="fa icon-job-{{ job.status }}"></i> {{ job.status }}</div>
</div>
</div>
</div>
<div class="panel panel-default job-stdout-panel StandardOut-panel">
<div class="panel-heading StandardOut-panelHeading">
<h3 class="panel-title">Standard Output
<a href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}" class="btn btn-primary btn-xs DownloadStandardOut DownloadStandardOut--onStandardOutPage" id="download-stdout-button" type="button" aw-tool-tip="Download standard out as a .txt file" data-placement="top" ng-show="job.status === 'cancelled' || job.status === 'failed' || job.status === 'error' || job.status === 'successful'">
<i class="fa fa-download DownloadStandardOut-icon DownloadStandardOut-icon--withText"></i>Download
</a>
</div>
<div class="panel-body stdout-panel-body StandardOut-panelBody">
<div id="pre-container" class="body_background
body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop"
scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,172 +0,0 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="row StandardOut-heading">
<div class="row StandardOut-breadcrumbs">
<div id="home-list-actions"
class="list-actions pull-right col-md-12">
<button type="button" class="btn btn-xs btn-primary ng-hide"
ng-click="refresh()" id="refresh_btn"
aw-tool-tip="Refresh the page"
data-placement="top" ng-show="socketStatus == 'error'"
data-original-title="" title="">
<i class="fa fa-refresh fa-lg"></i>
</button>
</div>
</div>
<div class="StandardOut-form form-horizontal StandardOutDetails"
role="form" id="job-status-form">
<div class="form-group StandardOutDetails-detailRow">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Status</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">
<i class="fa icon-job-{{ job.status }}"></i> {{ job.status }}
</div>
</div>
<div <div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="job.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Timing</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">
<div ng-show="job.started" id="started-time">
Started &nbsp;{{ job.started | date:'MM/dd/yy HH:mm:ss' }}
</div>
<div ng-show="job.finished" id="finished-time">
Finished &nbsp;{{ job.finished | date:'MM/dd/yy HH:mm:ss' }}
</div>
<div ng-show="job.finished" id="elapsed-time">
Elapsed &nbsp;{{ job.elapsed }} seconds
</div>
</div>
</div>
<div <div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="job.module_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Module Name</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">{{ job.module_name }}
</div>
</div>
<div <div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="job.module_args">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Module Args</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent mono-space">{{ job.module_args }}
</div>
</div>
<div <div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="inventory_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Inventory</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">
<a href="{{ inventory_url }}"
aw-tool-tip="The inventory this command ran on."
data-placement="top">{{ inventory_name }}</a>
</div>
</div>
<div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="credential_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Credential</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">
<a href="{{ credential_url }}"
aw-tool-tip="The credential used to run this command."
data-placement="top">{{ credential_name }}</a>
</div>
</div>
<div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="created_by">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailContent">Launched By</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-9
StandardOutDetails-detailContent">
<a href="/#/users/{{ created_by.id }}"
aw-tool-tip="The user who ran this command."
data-placement="top">{{ created_by.username }}</a>
</div>
</div>
<!-- since zero is a falsy value, you need ng-show such that
the number is >= 0 -->
<div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="forks >= 0">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Forks</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">{{ forks }}</div>
</div>
<div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="limit">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Limit</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">{{ limit }}</div>
</div>
<!-- since zero is a falsy value, you need ng-show such that
the number is >= 0 -->
<div class="form-group StandardOutDetails-detailRow
StandardOutDetails-detailRow--closable"
ng-show="verbosity >= 0">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-12
StandardOutDetails-detailLabel">Verbosity</label>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12
StandardOutDetails-detailContent">{{ verbosity }}</div>
</div>
<div class="form-group StandardOutDetails-closedToggle">
<a class="col-sm-12 StandardOutDetails-closedToggleLink"
ng-show="isClosed" href="javascript:;"
ng-click="toggleClosedStatus()"> more <i class="fa fa-angle-down"></i>
</a>
<a class="col-sm-12 StandardOutDetails-closedToggleLink"
ng-show="!isClosed" href="javascript:;"
ng-click="toggleClosedStatus()"> less <i class="fa fa-angle-up"></i>
</a>
</div>
</div>
</div>
<div class="panel panel-default StandardOut-panel">
<div class="panel-heading StandardOut-panelHeading">
<h3 class="panel-title">Standard Output
<a ng-href="/api/v1/ad_hoc_commands/{{ job.id }}/stdout?format=txt_download&token={{ token }}" class="btn btn-primary btn-xs DownloadStandardOut DownloadStandardOut--onStandardOutPage" id="download-stdout-button" type="button" aw-tool-tip="Download standard out as a .txt file" data-placement="top" ng-show="job.status === 'cancelled' || job.status === 'failed' || job.status === 'error' || job.status === 'successful'"><i class="fa fa-download DownloadStandardOut-icon DownloadStandardOut-icon--withText"></i>Download</a>
</h3>
</div>
<div class="panel-body stdout-panel-body StandardOut-panelBody">
<div id="pre-container" class="body_background
body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop"
scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -172,9 +172,6 @@ angular.module('GeneratorHelpers', [systemStatus.name])
case 'schedule':
icon = "fa-calendar";
break;
case 'stdout':
icon = "fa-external-link";
break;
case 'question_cancel':
icon = 'fa-times';
break;

View File

@ -0,0 +1,116 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="StandardOut-leftPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
RESULTS
</div>
<div class="StandardOut-details">
<div class="StandardOut-detailsRow" ng-show="job.module_name">
<div class="StandardOut-detailsLabel">Name</div>
<div class="StandardOut-detailsContent">{{ job.module_name }}</div>
</div>
<div class="StandardOut-detailsRow">
<div class="StandardOut-detailsLabel">STATUS</div>
<div class="StandardOut-detailsContent">
<i class="fa icon-job-{{ job.status }}"></i>
<span class="StandardOut-statusText">{{ job.status }}</span>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.started">
<div class="StandardOut-detailsLabel">STARTED</div>
<div class="StandardOut-detailsContent">
{{ job.started | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">FINISHED</div>
<div class="StandardOut-detailsContent">
{{ job.finished | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">ELAPSED</div>
<div class="StandardOut-detailsContent">
{{ job.elapsed }} seconds
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.module_args">
<div class="StandardOut-detailsLabel">Module Args</div>
<div class="StandardOut-detailsContent">{{ job.module_args }}</div>
</div>
<div class="StandardOut-detailsRow">
<div class="StandardOut-detailsLabel">Inventory</div>
<div class="StandardOut-detailsContent">
<a href="{{ inventory_url }}"
aw-tool-tip="The inventory this command ran on."
data-placement="top">{{ inventory_name }}</a>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="credential_name">
<div class="StandardOut-detailsLabel">Credential</div>
<div class="StandardOut-detailsContent">
<a href="{{ credential_url }}"
aw-tool-tip="The credential used to run this command."
data-placement="top">{{ credential_name }}</a>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="created_by">
<div class="StandardOut-detailsLabel">Launched By</div>
<div class="StandardOut-detailsContent">
<a href="/#/users/{{ created_by.id }}"
aw-tool-tip="The user who ran this command."
data-placement="top">{{ created_by.username }}</a>
</div>
</div>
<!-- since zero is a falsy value, you need ng-show such that
the number is >= 0 -->
<div class="StandardOut-detailsRow" ng-show="forks >= 0">
<div class="StandardOut-detailsLabel">Forks</div>
<div class="StandardOut-detailsContent">{{ forks }}</div>
</div>
<div class="StandardOut-detailsRow" ng-show="limit">
<div class="StandardOut-detailsLabel">Limit</div>
<div class="StandardOut-detailsContent">{{ limit }}</div>
</div>
<!-- since zero is a falsy value, you need ng-show such that
the number is >= 0 -->
<div class="StandardOut-detailsRow" ng-show="verbosity >= 0">
<div class="StandardOut-detailsLabel">Verbosity</div>
<div class="StandardOut-detailsContent">{{ verbosity }}</div>
</div>
</div>
</div>
</div>
<div class="StandardOut-rightPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
STANDARD OUT
</div>
<div class="StandardOut-consoleOutput">
<div id="pre-container" class="body_background body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop" scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,40 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'adHocJobStdout',
route: '/ad_hoc_commands/:id/stdout',
templateUrl: templateUrl('standard-out/adhoc/standard-out-adhoc'),
controller: 'JobStdoutController',
ncyBreadcrumb: {
parent: "jobs",
label: "{{ job.module_name }}"
},
data: {
jobType: 'ad_hoc_commands'
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
adhocEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
// if (!$rootScope.adhoc_event_socket) {
// $rootScope.adhoc_event_socket = Socket({
// scope: $rootScope,
// endpoint: "ad_hoc_command_events"
// });
// $rootScope.adhoc_event_socket.init();
// return true;
// } else {
// return true;
// }
return true;
}]
}
};

View File

@ -0,0 +1,130 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="StandardOut-leftPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
RESULTS
</div>
<div class="StandardOut-details">
<div class="StandardOut-detailsRow" ng-show="inventory_source_name">
<div class="StandardOut-detailsLabel">NAME</div>
<div class="StandardOut-detailsContent">
<a href="/#/home/groups/?id={{ group }}">
{{ inventory_source_name }}
</a>
</div>
</div>
<div class="StandardOut-detailsRow">
<div class="StandardOut-detailsLabel">STATUS</div>
<div class="StandardOut-detailsContent">
<i class="fa icon-job-{{ job.status }}"></i>
<span class="StandardOut-statusText">{{ job.status }}</span>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="{{job.license_error !== null}}">
<div class="StandardOut-detailsLabel">LICENSE ERROR</div>
<div class="StandardOut-detailsContent">
{{ job.license_error }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.started">
<div class="StandardOut-detailsLabel">STARTED</div>
<div class="StandardOut-detailsContent">
{{ job.started | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">FINISHED</div>
<div class="StandardOut-detailsContent">
{{ job.finished | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">ELAPSED</div>
<div class="StandardOut-detailsContent">
{{ job.elapsed }} seconds
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.launch_type">
<div class="StandardOut-detailsLabel">LAUNCH TYPE</div>
<div class="StandardOut-detailsContent">
{{ job.launch_type }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="credential_name">
<div class="StandardOut-detailsLabel">CREDENTIAL</div>
<div class="StandardOut-detailsContent">
<a ui-sref="credentials.edit({credential_id: credential})">
{{ credential_name }}
</a>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="inventory_source_name">
<div class="StandardOut-detailsLabel">GROUP</div>
<div class="StandardOut-detailsContent">
<a href="/#/home/groups/?id={{ group }}">
{{ inventory_source_name }}
</a>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="source">
<div class="StandardOut-detailsLabel">SOURCE</div>
<div class="StandardOut-detailsContent">
{{ source }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="source_regions">
<div class="StandardOut-detailsLabel">REGIONS</div>
<div class="StandardOut-detailsContent">
{{ source_regions }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="{{ job.overwrite !== null }}">
<div class="StandardOut-detailsLabel">OVERWRITE</div>
<div class="StandardOut-detailsContent">
{{ job.overwrite }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="{{ job.overwrite_vars !== null }}">
<div class="StandardOut-detailsLabel">OVERWRITE VARS</div>
<div class="StandardOut-detailsContent">
{{ job.overwrite_vars }}
</div>
</div>
</div>
</div>
</div>
<div class="StandardOut-rightPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
STANDARD OUT
</div>
<div class="StandardOut-consoleOutput">
<div id="pre-container" class="body_background body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop" scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,42 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
// TODO: figure out what this route should be - should it be inventory_sync?
export default {
name: 'inventorySyncStdout',
route: '/inventory_sync/:id/stdout',
templateUrl: templateUrl('standard-out/inventory-sync/standard-out-inventory-sync'),
controller: 'JobStdoutController',
ncyBreadcrumb: {
parent: "jobs",
label: "{{ inventory_source_name }}"
},
data: {
jobType: 'inventory_updates'
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
adhocEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
// if (!$rootScope.adhoc_event_socket) {
// $rootScope.adhoc_event_socket = Socket({
// scope: $rootScope,
// endpoint: "ad_hoc_command_events"
// });
// $rootScope.adhoc_event_socket.init();
// return true;
// } else {
// return true;
// }
return true;
}]
}
};

View File

@ -0,0 +1,20 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import stdoutAdhocRoute from './adhoc/standard-out-adhoc.route';
import stdoutManagementJobsRoute from './management-jobs/standard-out-management-jobs.route';
import stdoutInventorySyncRoute from './inventory-sync/standard-out-inventory-sync.route';
import stdoutScmUpdateRoute from './scm-update/standard-out-scm-update.route';
import {JobStdoutController} from './standard-out.controller';
export default angular.module('standardOut', [])
.controller('JobStdoutController', JobStdoutController)
.run(['$stateExtender', function($stateExtender) {
$stateExtender.addState(stdoutAdhocRoute);
$stateExtender.addState(stdoutManagementJobsRoute);
$stateExtender.addState(stdoutInventorySyncRoute);
$stateExtender.addState(stdoutScmUpdateRoute);
}]);

View File

@ -0,0 +1,82 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="StandardOut-leftPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
RESULTS
</div>
<div class="StandardOut-details">
<div class="StandardOut-detailsRow" ng-show="job.name">
<div class="StandardOut-detailsLabel">NAME</div>
<div class="StandardOut-detailsContent">{{ job.name }}</div>
</div>
<div class="StandardOut-detailsRow">
<div class="StandardOut-detailsLabel">STATUS</div>
<div class="StandardOut-detailsContent">
<i class="fa icon-job-{{ job.status }}"></i>
<span class="StandardOut-statusText">{{ job.status }}</span>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.started">
<div class="StandardOut-detailsLabel">STARTED</div>
<div class="StandardOut-detailsContent">
{{ job.started | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">FINISHED</div>
<div class="StandardOut-detailsContent">
{{ job.finished | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">ELAPSED</div>
<div class="StandardOut-detailsContent">
{{ job.elapsed }} seconds
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.launch_type">
<div class="StandardOut-detailsLabel">LAUNCH TYPE</div>
<div class="StandardOut-detailsContent">
{{ job.launch_type }}
</div>
</div>
<!-- TODO: figure out how to show the extra vars on different rows like the mockup -->
<div class="StandardOut-detailsRow" ng-show="job.extra_vars">
<div class="StandardOut-detailsLabel">EXTRA VARS</div>
<div class="StandardOut-detailsContent">
{{ job.extra_vars }}
</div>
</div>
</div>
</div>
</div>
<div class="StandardOut-rightPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
STANDARD OUT
</div>
<div class="StandardOut-consoleOutput">
<div id="pre-container" class="body_background body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop" scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,40 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
export default {
name: 'managementJobStdout',
route: '/management_jobs/:id/stdout',
templateUrl: templateUrl('standard-out/management-jobs/standard-out-management-jobs'),
controller: 'JobStdoutController',
ncyBreadcrumb: {
parent: "jobs",
label: "{{ job.name }}"
},
data: {
jobType: 'system_jobs'
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
adhocEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
// if (!$rootScope.adhoc_event_socket) {
// $rootScope.adhoc_event_socket = Socket({
// scope: $rootScope,
// endpoint: "ad_hoc_command_events"
// });
// $rootScope.adhoc_event_socket.init();
// return true;
// } else {
// return true;
// }
return true;
}]
}
};

View File

@ -0,0 +1,95 @@
<div class="tab-pane" id="jobs-stdout">
<div ng-cloak id="htmlTemplate">
<div class="StandardOut">
<div class="StandardOut-leftPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
RESULTS
</div>
<div class="StandardOut-details">
<div class="StandardOut-detailsRow" ng-show="project_name">
<div class="StandardOut-detailsLabel">NAME</div>
<div class="StandardOut-detailsContent">
<a ui-sref="projects.edit({id: job.project})">
{{ project_name }}
</a>
</div>
</div>
<div class="StandardOut-detailsRow">
<div class="StandardOut-detailsLabel">STATUS</div>
<div class="StandardOut-detailsContent">
<i class="fa icon-job-{{ job.status }}"></i>
<span class="StandardOut-statusText">{{ job.status }}</span>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.started">
<div class="StandardOut-detailsLabel">STARTED</div>
<div class="StandardOut-detailsContent">
{{ job.started | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">FINISHED</div>
<div class="StandardOut-detailsContent">
{{ job.finished | date:'MM/dd/yy HH:mm:ss' }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.finished">
<div class="StandardOut-detailsLabel">ELAPSED</div>
<div class="StandardOut-detailsContent">
{{ job.elapsed }} seconds
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="job.launch_type">
<div class="StandardOut-detailsLabel">LAUNCH TYPE</div>
<div class="StandardOut-detailsContent">
{{ job.launch_type }}
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="project_name">
<div class="StandardOut-detailsLabel">PROJECT</div>
<div class="StandardOut-detailsContent">
<a ui-sref="projects.edit({id: job.project})">
{{ project_name }}
</a>
</div>
</div>
<div class="StandardOut-detailsRow" ng-show="credential_name">
<div class="StandardOut-detailsLabel">CREDENTIAL</div>
<div class="StandardOut-detailsContent">
<a ui-sref="credentials.edit({credential_id: credential})">
{{ credential_name }}
</a>
</div>
</div>
</div>
</div>
</div>
<div class="StandardOut-rightPanel">
<div class="Panel">
<div class="StandardOut-panelHeader">
STANDARD OUT
</div>
<div class="StandardOut-consoleOutput">
<div id="pre-container" class="body_background body_foreground pre mono-space StandardOut-preContainer"
lr-infinite-scroll="stdOutScrollToTop" scroll-threshold="300" data-direction="up" time-threshold="500">
<div id="pre-container-content" class="StandardOut-preContent"></div>
</div>
<div class="scroll-spinner" id="stdoutMoreRowsBottom">
<i class="fa fa-cog fa-spin"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,42 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
// TODO: figure out what this route should be - should it be scm_update?
export default {
name: 'scmUpdateStdout',
route: '/scm_update/:id/stdout',
templateUrl: templateUrl('standard-out/scm-update/standard-out-scm-update'),
controller: 'JobStdoutController',
ncyBreadcrumb: {
parent: "jobs",
label: "{{ project_name }}"
},
data: {
jobType: 'project_updates'
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
adhocEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
// if (!$rootScope.adhoc_event_socket) {
// $rootScope.adhoc_event_socket = Socket({
// scope: $rootScope,
// endpoint: "ad_hoc_command_events"
// });
// $rootScope.adhoc_event_socket.init();
// return true;
// } else {
// return true;
// }
return true;
}]
}
};

View File

@ -0,0 +1,60 @@
@import "../shared/branding/colors.default.less";
/** @define StandardOut */
.StandardOut {
height: 100%;
display: flex;
flex-direction: row;
}
.StandardOut-leftPanel {
flex: 0 0 400px;
}
.StandardOut-rightPanel {
flex: 1 0;
margin-left: 20px;
}
.StandardOut-panelHeader {
color: @default-interface-txt;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
text-transform: uppercase;
}
.StandardOut-consoleOutput {
margin-top: 25px;
min-height: 200px;
background-color: @default-secondary-bg;
border-radius: 5px;
}
.StandardOut-details {
margin-top: 25px;
}
.StandardOut-detailsRow {
display: flex;
}
.StandardOut-detailsRow:not(:last-child) {
margin-bottom: 20px;
}
.StandardOut-detailsLabel {
width: 130px;
flex: 0 0 130px;
color: @default-interface-txt;
text-transform: uppercase;
}
.StandardOut-detailsContent {
flex: 1 0;
}
.StandardOut-statusText {
margin-left: 6px;
}

View File

@ -3,7 +3,7 @@
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name controllers.function:JobStdout
@ -11,11 +11,12 @@
*/
export function JobStdoutController ($location, $log, $rootScope, $scope, $compile, $stateParams, ClearScope, GetBasePath, Wait, Rest, ProcessErrors) {
export function JobStdoutController ($location, $log, $rootScope, $scope, $compile, $state, $stateParams, ClearScope, GetBasePath, Wait, Rest, ProcessErrors, ModelToBasePathKey, Empty, GetChoices, LookUpName) {
ClearScope();
var job_id = $stateParams.id,
jobType = $state.current.data.jobType,
api_complete = false,
stdout_url,
current_range,
@ -32,26 +33,27 @@ export function JobStdoutController ($location, $log, $rootScope, $scope, $compi
$scope.isClosed = true;
function openSockets() {
if (/\/jobs\/(\d)+\/stdout/.test($location.$$url)) {
$log.debug("socket watching on job_events-" + job_id);
$rootScope.event_socket.on("job_events-" + job_id, function() {
$log.debug("socket fired on job_events-" + job_id);
if (api_complete) {
event_queue++;
}
});
} else if (/\/ad_hoc_commands\/(\d)+/.test($location.$$url)) {
$log.debug("socket watching on ad_hoc_command_events-" + job_id);
$rootScope.adhoc_event_socket.on("ad_hoc_command_events-" + job_id, function() {
$log.debug("socket fired on ad_hoc_command_events-" + job_id);
if (api_complete) {
event_queue++;
}
});
}
}
openSockets();
// function openSockets() {
// if (/\/jobs\/(\d)+\/stdout/.test($location.$$url)) {
// $log.debug("socket watching on job_events-" + job_id);
// $rootScope.event_socket.on("job_events-" + job_id, function() {
// $log.debug("socket fired on job_events-" + job_id);
// if (api_complete) {
// event_queue++;
// }
// });
// } else if (/\/ad_hoc_commands\/(\d)+/.test($location.$$url)) {
// $log.debug("socket watching on ad_hoc_command_events-" + job_id);
// $rootScope.adhoc_event_socket.on("ad_hoc_command_events-" + job_id, function() {
// $log.debug("socket fired on ad_hoc_command_events-" + job_id);
// if (api_complete) {
// event_queue++;
// }
// });
// }
// }
//
// openSockets();
if ($rootScope.removeJobStatusChange) {
$rootScope.removeJobStatusChange();
@ -158,9 +160,7 @@ export function JobStdoutController ($location, $log, $rootScope, $scope, $compi
$(".StandardOut").height($("body").height() - 60);
// Note: could be ad_hoc_commands or jobs
var jobType = $location.path().replace(/^\//, '').split('/')[0];
Rest.setUrl(GetBasePath(jobType) + job_id + '/');
Rest.setUrl(GetBasePath('base') + jobType + '/' + job_id + '/');
Rest.get()
.success(function(data) {
$scope.job = data;
@ -182,13 +182,87 @@ export function JobStdoutController ($location, $log, $rootScope, $scope, $compi
$scope.verbosity = data.verbosity;
$scope.job_tags = data.job_tags;
stdout_url = data.related.stdout;
if (data.status === 'successful' || data.status === 'failed' || data.status === 'error' || data.status === 'canceled') {
live_event_processing = false;
if ($rootScope.jobStdOutInterval) {
window.clearInterval($rootScope.jobStdOutInterval);
// If we have a source then we have to go get the source choices from the server
if (!Empty(data.source)) {
if ($scope.removeChoicesReady) {
$scope.removeChoicesReady();
}
$scope.removeChoicesReady = $scope.$on('ChoicesReady', function() {
$scope.source_choices.every(function(e) {
if (e.value === data.source) {
$scope.source = e.label;
return false;
}
return true;
});
});
// GetChoices can be found in the helper: LogViewer.js
// It attaches the source choices to $scope.source_choices.
// Then, when the callback is fired, $scope.source is bound
// to the corresponding label.
GetChoices({
scope: $scope,
url: GetBasePath('inventory_sources'),
field: 'source',
variable: 'source_choices',
choice_name: 'choices',
callback: 'ChoicesReady'
});
}
// LookUpName can be found in the helper: LogViewer.js
// It attaches the name that it gets (based on the url)
// to the $scope variable defined by the attribute scope_var.
if (!Empty(data.credential)) {
LookUpName({
scope: $scope,
scope_var: 'credential',
url: GetBasePath('credentials') + data.credential + '/'
});
}
if (!Empty(data.inventory)) {
LookUpName({
scope: $scope,
scope_var: 'inventory',
url: GetBasePath('inventory') + data.inventory + '/'
});
}
if (!Empty(data.project)) {
LookUpName({
scope: $scope,
scope_var: 'project',
url: GetBasePath('projects') + data.project + '/'
});
}
if (!Empty(data.cloud_credential)) {
LookUpName({
scope: $scope,
scope_var: 'cloud_credential',
url: GetBasePath('credentials') + data.cloud_credential + '/'
});
}
if (!Empty(data.inventory_source)) {
LookUpName({
scope: $scope,
scope_var: 'inventory_source',
url: GetBasePath('inventory_sources') + data.inventory_source + '/'
});
}
// if (data.status === 'successful' || data.status === 'failed' || data.status === 'error' || data.status === 'canceled') {
// live_event_processing = false;
// if ($rootScope.jobStdOutInterval) {
// window.clearInterval($rootScope.jobStdOutInterval);
// }
// }
if(stdout_url) {
$scope.$emit('LoadStdout');
}
$scope.$emit('LoadStdout');
})
.error(function(data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
@ -197,11 +271,9 @@ export function JobStdoutController ($location, $log, $rootScope, $scope, $compi
$scope.refresh = function(){
if (loaded_sections.length === 0) { ////this if statement for refresh
$log.debug('calling LoadStdout');
$scope.$emit('LoadStdout');
}
else if (live_event_processing) {
$log.debug('calling getNextSection');
getNextSection();
}
};
@ -281,4 +353,4 @@ export function JobStdoutController ($location, $log, $rootScope, $scope, $compi
}
JobStdoutController.$inject = [ '$location', '$log', '$rootScope', '$scope', '$compile', '$stateParams', 'ClearScope', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors'];
JobStdoutController.$inject = [ '$location', '$log', '$rootScope', '$scope', '$compile', '$state', '$stateParams', 'ClearScope', 'GetBasePath', 'Wait', 'Rest', 'ProcessErrors', 'ModelToBasePathKey', 'Empty', 'GetChoices', 'LookUpName'];

View File

@ -1,47 +0,0 @@
/** @define StandardOut */
.StandardOut {
height: 100%;
display: flex;
flex-direction: column;
}
.StandardOut-header {
flex: initial;
}
.StandardOut-breadcrumbs {
padding-left: 15px;
}
.StandardOut-form {
padding-left: 15px;
}
.StandardOut-panel {
flex: 1 0 0;
display: flex;
flex-direction: column;
margin-bottom: -41px;
}
.StandardOut-panelHeading {
flex: initial;
}
.StandardOut-panelBody {
flex: 1 0 auto;
padding: 0;
position: relative;
}
.StandardOut-preContainer {
position: absolute;
height: 100%;
padding-left: 15px;
padding-right: 15px;
}
.StandardOut-preContent {
position: absolute;
}