mirror of
https://github.com/ansible/awx.git
synced 2026-02-27 07:56:06 -03:30
Merge branch 'release_3.1.0' into fixJobResults
This commit is contained in:
@@ -242,7 +242,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
queryset = queryset.filter(q)
|
queryset = queryset.filter(q)
|
||||||
queryset = queryset.filter(*args).distinct()
|
queryset = queryset.filter(*args).distinct()
|
||||||
return queryset
|
return queryset
|
||||||
except (FieldError, FieldDoesNotExist, ValueError) as e:
|
except (FieldError, FieldDoesNotExist, ValueError, TypeError) as e:
|
||||||
raise ParseError(e.args[0])
|
raise ParseError(e.args[0])
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
raise ParseError(e.messages)
|
raise ParseError(e.messages)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import uuid
|
|||||||
from ansible.utils.display import Display
|
from ansible.utils.display import Display
|
||||||
|
|
||||||
# Tower Display Callback
|
# Tower Display Callback
|
||||||
from tower_display_callback.events import event_context
|
from .events import event_context
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,75 @@ import base64
|
|||||||
import contextlib
|
import contextlib
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
# Kombu
|
||||||
|
from kombu import Connection, Exchange, Producer
|
||||||
|
|
||||||
__all__ = ['event_context']
|
__all__ = ['event_context']
|
||||||
|
|
||||||
|
|
||||||
|
class CallbackQueueEventDispatcher(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.callback_connection = os.getenv('CALLBACK_CONNECTION', None)
|
||||||
|
self.connection_queue = os.getenv('CALLBACK_QUEUE', '')
|
||||||
|
self.connection = None
|
||||||
|
self.exchange = None
|
||||||
|
self._init_logging()
|
||||||
|
|
||||||
|
def _init_logging(self):
|
||||||
|
try:
|
||||||
|
self.job_callback_debug = int(os.getenv('JOB_CALLBACK_DEBUG', '0'))
|
||||||
|
except ValueError:
|
||||||
|
self.job_callback_debug = 0
|
||||||
|
self.logger = logging.getLogger('awx.plugins.callback.job_event_callback')
|
||||||
|
if self.job_callback_debug >= 2:
|
||||||
|
self.logger.setLevel(logging.DEBUG)
|
||||||
|
elif self.job_callback_debug >= 1:
|
||||||
|
self.logger.setLevel(logging.INFO)
|
||||||
|
else:
|
||||||
|
self.logger.setLevel(logging.WARNING)
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
formatter = logging.Formatter('%(levelname)-8s %(process)-8d %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
self.logger.addHandler(handler)
|
||||||
|
self.logger.propagate = False
|
||||||
|
|
||||||
|
def dispatch(self, obj):
|
||||||
|
if not self.callback_connection or not self.connection_queue:
|
||||||
|
return
|
||||||
|
active_pid = os.getpid()
|
||||||
|
for retry_count in xrange(4):
|
||||||
|
try:
|
||||||
|
if not hasattr(self, 'connection_pid'):
|
||||||
|
self.connection_pid = active_pid
|
||||||
|
if self.connection_pid != active_pid:
|
||||||
|
self.connection = None
|
||||||
|
if self.connection is None:
|
||||||
|
self.connection = Connection(self.callback_connection)
|
||||||
|
self.exchange = Exchange(self.connection_queue, type='direct')
|
||||||
|
|
||||||
|
producer = Producer(self.connection)
|
||||||
|
producer.publish(obj,
|
||||||
|
serializer='json',
|
||||||
|
compression='bzip2',
|
||||||
|
exchange=self.exchange,
|
||||||
|
declare=[self.exchange],
|
||||||
|
routing_key=self.connection_queue)
|
||||||
|
return
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.info('Publish Job Event Exception: %r, retry=%d', e,
|
||||||
|
retry_count, exc_info=True)
|
||||||
|
retry_count += 1
|
||||||
|
if retry_count >= 3:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
class EventContext(object):
|
class EventContext(object):
|
||||||
'''
|
'''
|
||||||
Store global and local (per thread/process) data associated with callback
|
Store global and local (per thread/process) data associated with callback
|
||||||
@@ -38,6 +99,7 @@ class EventContext(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.display_lock = multiprocessing.RLock()
|
self.display_lock = multiprocessing.RLock()
|
||||||
|
self.dispatcher = CallbackQueueEventDispatcher()
|
||||||
|
|
||||||
def add_local(self, **kwargs):
|
def add_local(self, **kwargs):
|
||||||
if not hasattr(self, '_local'):
|
if not hasattr(self, '_local'):
|
||||||
@@ -111,7 +173,9 @@ class EventContext(object):
|
|||||||
if event_data.get(key, False):
|
if event_data.get(key, False):
|
||||||
event = key
|
event = key
|
||||||
break
|
break
|
||||||
|
max_res = int(os.getenv("MAX_EVENT_RES", 700000))
|
||||||
|
if event not in ('playbook_on_stats',) and "res" in event_data and len(str(event_data['res'])) > max_res:
|
||||||
|
event_data['res'] = {}
|
||||||
event_dict = dict(event=event, event_data=event_data)
|
event_dict = dict(event=event, event_data=event_data)
|
||||||
for key in event_data.keys():
|
for key in event_data.keys():
|
||||||
if key in ('job_id', 'ad_hoc_command_id', 'uuid', 'parent_uuid', 'created', 'artifact_data'):
|
if key in ('job_id', 'ad_hoc_command_id', 'uuid', 'parent_uuid', 'created', 'artifact_data'):
|
||||||
@@ -136,7 +200,9 @@ class EventContext(object):
|
|||||||
fileobj.flush()
|
fileobj.flush()
|
||||||
|
|
||||||
def dump_begin(self, fileobj):
|
def dump_begin(self, fileobj):
|
||||||
self.dump(fileobj, self.get_begin_dict())
|
begin_dict = self.get_begin_dict()
|
||||||
|
self.dispatcher.dispatch(begin_dict)
|
||||||
|
self.dump(fileobj, {'uuid': begin_dict['uuid']})
|
||||||
|
|
||||||
def dump_end(self, fileobj):
|
def dump_end(self, fileobj):
|
||||||
self.dump(fileobj, self.get_end_dict(), flush=True)
|
self.dump(fileobj, self.get_end_dict(), flush=True)
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ from ansible.plugins.callback import CallbackBase
|
|||||||
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule
|
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule
|
||||||
|
|
||||||
# Tower Display Callback
|
# Tower Display Callback
|
||||||
from tower_display_callback.events import event_context
|
from .events import event_context
|
||||||
from tower_display_callback.minimal import CallbackModule as MinimalCallbackModule
|
from .minimal import CallbackModule as MinimalCallbackModule
|
||||||
|
|
||||||
|
|
||||||
class BaseCallbackModule(CallbackBase):
|
class BaseCallbackModule(CallbackBase):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from channels import Group
|
|||||||
from channels.sessions import channel_session
|
from channels.sessions import channel_session
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from awx.main.models.organization import AuthToken
|
from awx.main.models.organization import AuthToken
|
||||||
|
|
||||||
|
|
||||||
@@ -86,4 +87,4 @@ def ws_receive(message):
|
|||||||
|
|
||||||
|
|
||||||
def emit_channel_notification(group, payload):
|
def emit_channel_notification(group, payload):
|
||||||
Group(group).send({"text": json.dumps(payload)})
|
Group(group).send({"text": json.dumps(payload, cls=DjangoJSONEncoder)})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ logger = logging.getLogger('awx.main.commands.run_callback_receiver')
|
|||||||
class CallbackBrokerWorker(ConsumerMixin):
|
class CallbackBrokerWorker(ConsumerMixin):
|
||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
self.partial_events = {}
|
||||||
|
|
||||||
def get_consumers(self, Consumer, channel):
|
def get_consumers(self, Consumer, channel):
|
||||||
return [Consumer(queues=[Queue(settings.CALLBACK_QUEUE,
|
return [Consumer(queues=[Queue(settings.CALLBACK_QUEUE,
|
||||||
@@ -31,18 +32,28 @@ class CallbackBrokerWorker(ConsumerMixin):
|
|||||||
|
|
||||||
def process_task(self, body, message):
|
def process_task(self, body, message):
|
||||||
try:
|
try:
|
||||||
if 'event' not in body:
|
|
||||||
raise Exception('Payload does not have an event')
|
|
||||||
if 'job_id' not in body and 'ad_hoc_command_id' not in body:
|
if 'job_id' not in body and 'ad_hoc_command_id' not in body:
|
||||||
raise Exception('Payload does not have a job_id or ad_hoc_command_id')
|
raise Exception('Payload does not have a job_id or ad_hoc_command_id')
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.info('Body: {}'.format(body))
|
logger.info('Body: {}'.format(body))
|
||||||
logger.info('Message: {}'.format(message))
|
logger.info('Message: {}'.format(message))
|
||||||
try:
|
try:
|
||||||
if 'job_id' in body:
|
# If event came directly from callback without counter/stdout,
|
||||||
JobEvent.create_from_data(**body)
|
# save it until the rest of the event arrives.
|
||||||
elif 'ad_hoc_command_id' in body:
|
if 'counter' not in body:
|
||||||
AdHocCommandEvent.create_from_data(**body)
|
if 'uuid' in body:
|
||||||
|
self.partial_events[body['uuid']] = body
|
||||||
|
# If event has counter, try to combine it with any event data
|
||||||
|
# already received for the same uuid, then create the actual
|
||||||
|
# job event record.
|
||||||
|
else:
|
||||||
|
if 'uuid' in body:
|
||||||
|
partial_event = self.partial_events.pop(body['uuid'], {})
|
||||||
|
body.update(partial_event)
|
||||||
|
if 'job_id' in body:
|
||||||
|
JobEvent.create_from_data(**body)
|
||||||
|
elif 'ad_hoc_command_id' in body:
|
||||||
|
AdHocCommandEvent.create_from_data(**body)
|
||||||
except DatabaseError as e:
|
except DatabaseError as e:
|
||||||
logger.error('Database Error Saving Job Event: {}'.format(e))
|
logger.error('Database Error Saving Job Event: {}'.format(e))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
@@ -1073,8 +1073,8 @@ class JobEvent(CreatedModifiedModel):
|
|||||||
from awx.main.models.inventory import Host
|
from awx.main.models.inventory import Host
|
||||||
hostnames = set()
|
hostnames = set()
|
||||||
try:
|
try:
|
||||||
for v in self.event_data.values():
|
for stat in ('changed', 'dark', 'failures', 'ok', 'processed', 'skipped'):
|
||||||
hostnames.update(v.keys())
|
hostnames.update(self.event_data.get(stat, {}).keys())
|
||||||
except AttributeError: # In case event_data or v isn't a dict.
|
except AttributeError: # In case event_data or v isn't a dict.
|
||||||
pass
|
pass
|
||||||
with ignore_inventory_computed_fields():
|
with ignore_inventory_computed_fields():
|
||||||
|
|||||||
@@ -809,6 +809,7 @@ class RunJob(BaseTask):
|
|||||||
env['REST_API_URL'] = settings.INTERNAL_API_URL
|
env['REST_API_URL'] = settings.INTERNAL_API_URL
|
||||||
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
env['REST_API_TOKEN'] = job.task_auth_token or ''
|
||||||
env['TOWER_HOST'] = settings.TOWER_URL_BASE
|
env['TOWER_HOST'] = settings.TOWER_URL_BASE
|
||||||
|
env['MAX_EVENT_RES'] = settings.MAX_EVENT_RES_DATA
|
||||||
env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE
|
env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE
|
||||||
env['CALLBACK_CONNECTION'] = settings.BROKER_URL
|
env['CALLBACK_CONNECTION'] = settings.BROKER_URL
|
||||||
if getattr(settings, 'JOB_CALLBACK_DEBUG', False):
|
if getattr(settings, 'JOB_CALLBACK_DEBUG', False):
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
|||||||
# Note: This setting may be overridden by database settings.
|
# Note: This setting may be overridden by database settings.
|
||||||
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
||||||
|
|
||||||
|
# The maximum size of the ansible callback event's res data structure
|
||||||
|
# beyond this limit and the value will be removed
|
||||||
|
MAX_EVENT_RES_DATA = 700000
|
||||||
|
|
||||||
# Note: This setting may be overridden by database settings.
|
# Note: This setting may be overridden by database settings.
|
||||||
EVENT_STDOUT_MAX_BYTES_DISPLAY = 1024
|
EVENT_STDOUT_MAX_BYTES_DISPLAY = 1024
|
||||||
|
|
||||||
@@ -522,17 +526,6 @@ ANSIBLE_FORCE_COLOR = True
|
|||||||
# the celery task.
|
# the celery task.
|
||||||
AWX_TASK_ENV = {}
|
AWX_TASK_ENV = {}
|
||||||
|
|
||||||
# Maximum number of job events processed by the callback receiver worker process
|
|
||||||
# before it recycles
|
|
||||||
JOB_EVENT_RECYCLE_THRESHOLD = 3000
|
|
||||||
|
|
||||||
# Number of workers used to proecess job events in parallel
|
|
||||||
JOB_EVENT_WORKERS = 4
|
|
||||||
|
|
||||||
# Maximum number of job events that can be waiting on a single worker queue before
|
|
||||||
# it can be skipped as too busy
|
|
||||||
JOB_EVENT_MAX_QUEUE_SIZE = 100
|
|
||||||
|
|
||||||
# Flag to enable/disable updating hosts M2M when saving job events.
|
# Flag to enable/disable updating hosts M2M when saving job events.
|
||||||
CAPTURE_JOB_EVENT_HOSTS = False
|
CAPTURE_JOB_EVENT_HOSTS = False
|
||||||
|
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ register(
|
|||||||
category=_('LDAP'),
|
category=_('LDAP'),
|
||||||
category_slug='ldap',
|
category_slug='ldap',
|
||||||
feature_required='ldap',
|
feature_required='ldap',
|
||||||
|
default='MemberDNGroupType',
|
||||||
)
|
)
|
||||||
|
|
||||||
register(
|
register(
|
||||||
|
|||||||
@@ -151,6 +151,9 @@ body .prettyprint .lit {
|
|||||||
body .prettyprint .str {
|
body .prettyprint .str {
|
||||||
color: #D9534F;
|
color: #D9534F;
|
||||||
}
|
}
|
||||||
|
body div.ansi_back {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
body .well.tab-content {
|
body .well.tab-content {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ export default [
|
|||||||
|
|
||||||
var dropdownOptions = [
|
var dropdownOptions = [
|
||||||
{label: i18n._('Azure AD'), value: 'azure'},
|
{label: i18n._('Azure AD'), value: 'azure'},
|
||||||
{label: i18n._('Github'), value: 'github'},
|
{label: i18n._('GitHub'), value: 'github'},
|
||||||
{label: i18n._('Github Org'), value: 'github_org'},
|
{label: i18n._('GitHub Org'), value: 'github_org'},
|
||||||
{label: i18n._('Github Team'), value: 'github_team'},
|
{label: i18n._('GithHub Team'), value: 'github_team'},
|
||||||
{label: i18n._('Google OAuth2'), value: 'google_oauth'},
|
{label: i18n._('Google OAuth2'), value: 'google_oauth'},
|
||||||
{label: i18n._('LDAP'), value: 'ldap'},
|
{label: i18n._('LDAP'), value: 'ldap'},
|
||||||
{label: i18n._('RADIUS'), value: 'radius'},
|
{label: i18n._('RADIUS'), value: 'radius'},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="tab-pane Configuration-container" id="configuration_edit">
|
<div class="tab-pane Configuration-container" id="configuration_edit">
|
||||||
<!-- <div ui-view="form"></div>
|
<div class="Form-nav--dropdownContainer">
|
||||||
<div ng-cloak id="htmlTemplate"> -->
|
<div class="Form-nav--dropdownLabel">Sub Category</div>
|
||||||
<div class="Form-nav--dropdown">
|
<div class="Form-nav--dropdown">
|
||||||
<select
|
<select
|
||||||
id="configure-dropdown-nav"
|
id="configure-dropdown-nav"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
ng-options="opt.value as opt.label for opt in authVm.dropdownOptions"
|
ng-options="opt.value as opt.label for opt in authVm.dropdownOptions"
|
||||||
ng-change="authVm.activeForm()">
|
ng-change="authVm.activeForm()">
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
|
|||||||
@@ -21,11 +21,26 @@
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Form-nav--dropdown {
|
.Form-nav--dropdownContainer {
|
||||||
width: 175px;
|
width: 285px;
|
||||||
margin-top: -52px;
|
margin-top: -52px;
|
||||||
margin-bottom: 22px;
|
margin-bottom: 22px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Form-nav--dropdown {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Form-nav--dropdownLabel {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Form-tabRow {
|
.Form-tabRow {
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ export default [
|
|||||||
'configurationLdapForm',
|
'configurationLdapForm',
|
||||||
'configurationRadiusForm',
|
'configurationRadiusForm',
|
||||||
'configurationSamlForm',
|
'configurationSamlForm',
|
||||||
|
'systemActivityStreamForm',
|
||||||
|
'systemLoggingForm',
|
||||||
|
'systemMiscForm',
|
||||||
'ConfigurationJobsForm',
|
'ConfigurationJobsForm',
|
||||||
'ConfigurationSystemForm',
|
|
||||||
'ConfigurationUiForm',
|
'ConfigurationUiForm',
|
||||||
function(
|
function(
|
||||||
$scope, $rootScope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
|
$scope, $rootScope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
|
||||||
@@ -33,8 +35,10 @@ export default [
|
|||||||
configurationLdapForm,
|
configurationLdapForm,
|
||||||
configurationRadiusForm,
|
configurationRadiusForm,
|
||||||
configurationSamlForm,
|
configurationSamlForm,
|
||||||
|
systemActivityStreamForm,
|
||||||
|
systemLoggingForm,
|
||||||
|
systemMiscForm,
|
||||||
ConfigurationJobsForm,
|
ConfigurationJobsForm,
|
||||||
ConfigurationSystemForm,
|
|
||||||
ConfigurationUiForm
|
ConfigurationUiForm
|
||||||
) {
|
) {
|
||||||
var vm = this;
|
var vm = this;
|
||||||
@@ -48,8 +52,10 @@ export default [
|
|||||||
'ldap': configurationLdapForm,
|
'ldap': configurationLdapForm,
|
||||||
'radius': configurationRadiusForm,
|
'radius': configurationRadiusForm,
|
||||||
'saml': configurationSamlForm,
|
'saml': configurationSamlForm,
|
||||||
|
'activity_stream': systemActivityStreamForm,
|
||||||
|
'logging': systemLoggingForm,
|
||||||
|
'misc': systemMiscForm,
|
||||||
'jobs': ConfigurationJobsForm,
|
'jobs': ConfigurationJobsForm,
|
||||||
'system': ConfigurationSystemForm,
|
|
||||||
'ui': ConfigurationUiForm
|
'ui': ConfigurationUiForm
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,19 +90,24 @@ export default [
|
|||||||
lastForm: '',
|
lastForm: '',
|
||||||
currentForm: '',
|
currentForm: '',
|
||||||
currentAuth: '',
|
currentAuth: '',
|
||||||
|
currentSystem: '',
|
||||||
setCurrent: function(form) {
|
setCurrent: function(form) {
|
||||||
this.lastForm = this.currentForm;
|
this.lastForm = this.currentForm;
|
||||||
this.currentForm = form;
|
this.currentForm = form;
|
||||||
},
|
},
|
||||||
setCurrentAuth: function(form) {
|
|
||||||
this.currentAuth = form;
|
|
||||||
this.setCurrent(this.currentAuth);
|
|
||||||
},
|
|
||||||
getCurrent: function() {
|
getCurrent: function() {
|
||||||
return this.currentForm;
|
return this.currentForm;
|
||||||
},
|
},
|
||||||
currentFormName: function() {
|
currentFormName: function() {
|
||||||
return 'configuration_' + this.currentForm + '_template_form';
|
return 'configuration_' + this.currentForm + '_template_form';
|
||||||
|
},
|
||||||
|
setCurrentAuth: function(form) {
|
||||||
|
this.currentAuth = form;
|
||||||
|
this.setCurrent(this.currentAuth);
|
||||||
|
},
|
||||||
|
setCurrentSystem: function(form) {
|
||||||
|
this.currentSystem = form;
|
||||||
|
this.setCurrent(this.currentSystem);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,6 +193,7 @@ export default [
|
|||||||
}
|
}
|
||||||
|
|
||||||
function active(setForm) {
|
function active(setForm) {
|
||||||
|
// Authentication and System's sub-module dropdowns handled first:
|
||||||
if (setForm === 'auth') {
|
if (setForm === 'auth') {
|
||||||
// Default to 'azure' on first load
|
// Default to 'azure' on first load
|
||||||
if (formTracker.currentAuth === '') {
|
if (formTracker.currentAuth === '') {
|
||||||
@@ -190,7 +202,15 @@ export default [
|
|||||||
// If returning to auth tab reset current form to previously viewed
|
// If returning to auth tab reset current form to previously viewed
|
||||||
formTracker.setCurrentAuth(formTracker.currentAuth);
|
formTracker.setCurrentAuth(formTracker.currentAuth);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (setForm === 'system') {
|
||||||
|
if (formTracker.currentSystem === '') {
|
||||||
|
formTracker.setCurrentSystem('misc');
|
||||||
|
} else {
|
||||||
|
// If returning to system tab reset current form to previously viewed
|
||||||
|
formTracker.setCurrentSystem(formTracker.currentSystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
formTracker.setCurrent(setForm);
|
formTracker.setCurrent(setForm);
|
||||||
}
|
}
|
||||||
vm.activeTab = setForm;
|
vm.activeTab = setForm;
|
||||||
|
|||||||
@@ -46,7 +46,19 @@
|
|||||||
},
|
},
|
||||||
AWX_PROOT_ENABLED: {
|
AWX_PROOT_ENABLED: {
|
||||||
type: 'toggleSwitch',
|
type: 'toggleSwitch',
|
||||||
}
|
},
|
||||||
|
DEFAULT_JOB_TIMEOUT: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'DEFAULT_JOB_TIMEOUT',
|
||||||
|
},
|
||||||
|
DEFAULT_INVENTORY_UPDATE_TIMEOUT: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'DEFAULT_INVENTORY_UPDATE_TIMEOUT',
|
||||||
|
},
|
||||||
|
DEFAULT_PROJECT_UPDATE_TIMEOUT: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'DEFAULT_PROJECT_UPDATE_TIMEOUT',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
buttons: {
|
buttons: {
|
||||||
|
|||||||
@@ -20,8 +20,12 @@ import configurationLdapForm from './auth-form/sub-forms/auth-ldap.form.js';
|
|||||||
import configurationRadiusForm from './auth-form/sub-forms/auth-radius.form.js';
|
import configurationRadiusForm from './auth-form/sub-forms/auth-radius.form.js';
|
||||||
import configurationSamlForm from './auth-form/sub-forms/auth-saml.form';
|
import configurationSamlForm from './auth-form/sub-forms/auth-saml.form';
|
||||||
|
|
||||||
|
//system sub-forms
|
||||||
|
import systemActivityStreamForm from './system-form/sub-forms/system-activity-stream.form.js';
|
||||||
|
import systemLoggingForm from './system-form/sub-forms/system-logging.form.js';
|
||||||
|
import systemMiscForm from './system-form/sub-forms/system-misc.form.js';
|
||||||
|
|
||||||
import configurationJobsForm from './jobs-form/configuration-jobs.form';
|
import configurationJobsForm from './jobs-form/configuration-jobs.form';
|
||||||
import configurationSystemForm from './system-form/configuration-system.form';
|
|
||||||
import configurationUiForm from './ui-form/configuration-ui.form';
|
import configurationUiForm from './ui-form/configuration-ui.form';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@@ -36,10 +40,15 @@ angular.module('configuration', [])
|
|||||||
.factory('configurationLdapForm', configurationLdapForm)
|
.factory('configurationLdapForm', configurationLdapForm)
|
||||||
.factory('configurationRadiusForm', configurationRadiusForm)
|
.factory('configurationRadiusForm', configurationRadiusForm)
|
||||||
.factory('configurationSamlForm', configurationSamlForm)
|
.factory('configurationSamlForm', configurationSamlForm)
|
||||||
|
//system forms
|
||||||
|
.factory('systemActivityStreamForm', systemActivityStreamForm)
|
||||||
|
.factory('systemLoggingForm', systemLoggingForm)
|
||||||
|
.factory('systemMiscForm', systemMiscForm)
|
||||||
|
|
||||||
//other forms
|
//other forms
|
||||||
.factory('ConfigurationJobsForm', configurationJobsForm)
|
.factory('ConfigurationJobsForm', configurationJobsForm)
|
||||||
.factory('ConfigurationSystemForm', configurationSystemForm)
|
|
||||||
.factory('ConfigurationUiForm', configurationUiForm)
|
.factory('ConfigurationUiForm', configurationUiForm)
|
||||||
|
|
||||||
//helpers and services
|
//helpers and services
|
||||||
.factory('ConfigurationUtils', ConfigurationUtils)
|
.factory('ConfigurationUtils', ConfigurationUtils)
|
||||||
.service('ConfigurationService', configurationService)
|
.service('ConfigurationService', configurationService)
|
||||||
|
|||||||
@@ -5,22 +5,120 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
'$rootScope', '$scope', '$state', 'AngularCodeMirror', 'Authorization', 'ConfigurationSystemForm', 'ConfigurationService',
|
'$rootScope', '$scope', '$state', '$stateParams',
|
||||||
'ConfigurationUtils', 'GenerateForm',
|
'AngularCodeMirror',
|
||||||
|
'systemActivityStreamForm',
|
||||||
|
'systemLoggingForm',
|
||||||
|
'systemMiscForm',
|
||||||
|
'ConfigurationService',
|
||||||
|
'ConfigurationUtils',
|
||||||
|
'CreateSelect2',
|
||||||
|
'GenerateForm',
|
||||||
|
'i18n',
|
||||||
function(
|
function(
|
||||||
$rootScope, $scope, $state, AngularCodeMirror, Authorization, ConfigurationSystemForm, ConfigurationService, ConfigurationUtils, GenerateForm
|
$rootScope, $scope, $state, $stateParams,
|
||||||
|
AngularCodeMirror,
|
||||||
|
systemActivityStreamForm,
|
||||||
|
systemLoggingForm,
|
||||||
|
systemMiscForm,
|
||||||
|
ConfigurationService,
|
||||||
|
ConfigurationUtils,
|
||||||
|
CreateSelect2,
|
||||||
|
GenerateForm,
|
||||||
|
i18n
|
||||||
) {
|
) {
|
||||||
var systemVm = this;
|
var systemVm = this;
|
||||||
var generator = GenerateForm;
|
|
||||||
var form = ConfigurationSystemForm;
|
|
||||||
var keys = _.keys(form.fields);
|
|
||||||
|
|
||||||
_.each(keys, function(key) {
|
var generator = GenerateForm;
|
||||||
addFieldInfo(form, key);
|
var formTracker = $scope.$parent.vm.formTracker;
|
||||||
|
var dropdownValue = 'misc';
|
||||||
|
var activeSystemForm = 'misc';
|
||||||
|
|
||||||
|
if ($stateParams.currentTab === 'system') {
|
||||||
|
formTracker.setCurrentSystem(activeSystemForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeForm = function() {
|
||||||
|
if(!$scope.$parent[formTracker.currentFormName()].$dirty) {
|
||||||
|
systemVm.activeSystemForm = systemVm.dropdownValue;
|
||||||
|
formTracker.setCurrentSystem(systemVm.activeSystemForm);
|
||||||
|
} else {
|
||||||
|
var msg = i18n._('You have unsaved changes. Would you like to proceed <strong>without</strong> saving?');
|
||||||
|
var title = i18n._('Warning: Unsaved Changes');
|
||||||
|
var buttons = [{
|
||||||
|
label: i18n._('Discard changes'),
|
||||||
|
"class": "btn Form-cancelButton",
|
||||||
|
"id": "formmodal-cancel-button",
|
||||||
|
onClick: function() {
|
||||||
|
$scope.$parent.vm.populateFromApi();
|
||||||
|
$scope.$parent[formTracker.currentFormName()].$setPristine();
|
||||||
|
systemVm.activeSystemForm = systemVm.dropdownValue;
|
||||||
|
formTracker.setCurrentSystem(systemVm.activeSystemForm);
|
||||||
|
$('#FormModal-dialog').dialog('close');
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: i18n._('Save changes'),
|
||||||
|
onClick: function() {
|
||||||
|
$scope.$parent.vm.formSave()
|
||||||
|
.then(function() {
|
||||||
|
$scope.$parent[formTracker.currentFormName()].$setPristine();
|
||||||
|
$scope.$parent.vm.populateFromApi();
|
||||||
|
systemVm.activeSystemForm = systemVm.dropdownValue;
|
||||||
|
formTracker.setCurrentSystem(systemVm.activeSystemForm);
|
||||||
|
$('#FormModal-dialog').dialog('close');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"class": "btn btn-primary",
|
||||||
|
"id": "formmodal-save-button"
|
||||||
|
}];
|
||||||
|
$scope.$parent.vm.triggerModal(msg, title, buttons);
|
||||||
|
}
|
||||||
|
formTracker.setCurrentSystem(systemVm.activeSystemForm);
|
||||||
|
};
|
||||||
|
|
||||||
|
var dropdownOptions = [
|
||||||
|
{label: i18n._('Misc. System'), value: 'misc'},
|
||||||
|
{label: i18n._('Activity Stream'), value: 'activity_stream'},
|
||||||
|
{label: i18n._('Logging'), value: 'logging'},
|
||||||
|
];
|
||||||
|
|
||||||
|
CreateSelect2({
|
||||||
|
element: '#system-configure-dropdown-nav',
|
||||||
|
multiple: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable the save button for system auditors
|
var systemForms = [{
|
||||||
form.buttons.save.disabled = $rootScope.user_is_system_auditor;
|
formDef: systemLoggingForm,
|
||||||
|
id: 'system-logging-form'
|
||||||
|
}, {
|
||||||
|
formDef: systemActivityStreamForm,
|
||||||
|
id: 'system-activity-stream-form'
|
||||||
|
}, {
|
||||||
|
formDef: systemMiscForm,
|
||||||
|
id: 'system-misc-form'
|
||||||
|
}];
|
||||||
|
|
||||||
|
var forms = _.pluck(systemForms, 'formDef');
|
||||||
|
_.each(forms, function(form) {
|
||||||
|
var keys = _.keys(form.fields);
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
if($scope.$parent.configDataResolve[key].type === 'choice') {
|
||||||
|
// Create options for dropdowns
|
||||||
|
var optionsGroup = key + '_options';
|
||||||
|
$scope.$parent[optionsGroup] = [];
|
||||||
|
_.each($scope.$parent.configDataResolve[key].choices, function(choice){
|
||||||
|
$scope.$parent[optionsGroup].push({
|
||||||
|
name: choice[0],
|
||||||
|
label: choice[1],
|
||||||
|
value: choice[0]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addFieldInfo(form, key);
|
||||||
|
});
|
||||||
|
// Disable the save button for system auditors
|
||||||
|
form.buttons.save.disabled = $rootScope.user_is_system_auditor;
|
||||||
|
});
|
||||||
|
|
||||||
function addFieldInfo(form, key) {
|
function addFieldInfo(form, key) {
|
||||||
_.extend(form.fields[key], {
|
_.extend(form.fields[key], {
|
||||||
@@ -29,21 +127,56 @@ export default [
|
|||||||
name: key,
|
name: key,
|
||||||
toggleSource: key,
|
toggleSource: key,
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
|
placeholder: ConfigurationUtils.formatPlaceholder($scope.$parent.configDataResolve[key].placeholder, key) || null,
|
||||||
dataTitle: $scope.$parent.configDataResolve[key].label,
|
dataTitle: $scope.$parent.configDataResolve[key].label,
|
||||||
required: $scope.$parent.configDataResolve[key].required,
|
required: $scope.$parent.configDataResolve[key].required,
|
||||||
ngDisabled: $rootScope.user_is_system_auditor
|
ngDisabled: $rootScope.user_is_system_auditor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.inject(form, {
|
$scope.$parent.parseType = 'json';
|
||||||
id: 'configure-system-form',
|
|
||||||
mode: 'edit',
|
_.each(systemForms, function(form) {
|
||||||
scope: $scope.$parent,
|
generator.inject(form.formDef, {
|
||||||
related: true
|
id: form.id,
|
||||||
|
mode: 'edit',
|
||||||
|
scope: $scope.$parent,
|
||||||
|
related: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var dropdownRendered = false;
|
||||||
|
|
||||||
|
$scope.$on('populated', function() {
|
||||||
|
|
||||||
|
var opts = [];
|
||||||
|
if($scope.$parent.LOG_AGGREGATOR_TYPE !== null) {
|
||||||
|
_.each(ConfigurationUtils.listToArray($scope.$parent.LOG_AGGREGATOR_TYPE), function(type) {
|
||||||
|
opts.push({
|
||||||
|
id: type,
|
||||||
|
text: type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!dropdownRendered) {
|
||||||
|
dropdownRendered = true;
|
||||||
|
CreateSelect2({
|
||||||
|
element: '#configuration_logging_template_LOG_AGGREGATOR_TYPE',
|
||||||
|
multiple: true,
|
||||||
|
placeholder: i18n._('Select types'),
|
||||||
|
opts: opts
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
angular.extend(systemVm, {
|
angular.extend(systemVm, {
|
||||||
|
activeForm: activeForm,
|
||||||
|
activeSystemForm: activeSystemForm,
|
||||||
|
dropdownOptions: dropdownOptions,
|
||||||
|
dropdownValue: dropdownValue,
|
||||||
|
systemForms: systemForms
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,9 +1,34 @@
|
|||||||
<div class="tab-pane Configuration-container">
|
<div class="tab-pane Configuration-container">
|
||||||
<!-- <div ui-view="form"></div>
|
<div class="Form-nav--dropdownContainer">
|
||||||
<div ng-cloak id="htmlTemplate"> -->
|
<div class="Form-nav--dropdownLabel">Sub Category</div>
|
||||||
|
<div class="Form-nav--dropdown">
|
||||||
|
<select
|
||||||
|
id="system-configure-dropdown-nav"
|
||||||
|
class="form-control"
|
||||||
|
ng-model="systemVm.dropdownValue"
|
||||||
|
ng-options="opt.value as opt.label for opt in systemVm.dropdownOptions"
|
||||||
|
ng-change="systemVm.activeForm()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div id="configure-system-form"></div>
|
<!-- <div id="configure-system-form"></div> -->
|
||||||
|
<div ng-show="systemVm.activeSystemForm === 'misc'">
|
||||||
|
<div id="system-misc-form">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-show="systemVm.activeSystemForm === 'activity_stream'">
|
||||||
|
<div id="system-activity-stream-form">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ng-show="systemVm.activeSystemForm === 'logging'">
|
||||||
|
<div id="system-logging-form">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
name: 'configuration_activity_stream_template',
|
||||||
|
showActions: true,
|
||||||
|
showHeader: false,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
ACTIVITY_STREAM_ENABLED: {
|
||||||
|
type: 'toggleSwitch',
|
||||||
|
},
|
||||||
|
ACTIVITY_STREAM_ENABLED_FOR_INVENTORY_SYNC: {
|
||||||
|
type: 'toggleSwitch'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buttons: {
|
||||||
|
reset: {
|
||||||
|
ngClick: 'vm.resetAllConfirm()',
|
||||||
|
label: i18n._('Reset All'),
|
||||||
|
class: 'Form-button--left Form-cancelButton'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
ngClick: 'vm.formCancel()',
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
ngClick: 'vm.formSave()',
|
||||||
|
ngDisabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
name: 'configuration_logging_template',
|
||||||
|
showActions: true,
|
||||||
|
showHeader: false,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
LOG_AGGREGATOR_HOST: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'LOG_AGGREGATOR_HOST'
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_PORT: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'LOG_AGGREGATOR_PORT'
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_TYPE: {
|
||||||
|
type: 'select',
|
||||||
|
reset: 'LOG_AGGREGATOR_TYPE',
|
||||||
|
ngOptions: 'type.label for type in LOG_AGGREGATOR_TYPE_options track by type.value',
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_USERNAME: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'LOG_AGGREGATOR_USERNAME'
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_PASSWORD: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'LOG_AGGREGATOR_PASSWORD'
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_LOGGERS: {
|
||||||
|
type: 'textarea',
|
||||||
|
reset: 'LOG_AGGREGATOR_PASSWORD'
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_INDIVIDUAL_FACTS: {
|
||||||
|
type: 'toggleSwitch',
|
||||||
|
},
|
||||||
|
LOG_AGGREGATOR_ENABLED: {
|
||||||
|
type: 'toggleSwitch',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buttons: {
|
||||||
|
reset: {
|
||||||
|
ngClick: 'vm.resetAllConfirm()',
|
||||||
|
label: i18n._('Reset All'),
|
||||||
|
class: 'Form-button--left Form-cancelButton'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
ngClick: 'vm.formCancel()',
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
ngClick: 'vm.formSave()',
|
||||||
|
ngDisabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
export default ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
showHeader: false,
|
||||||
|
name: 'configuration_misc_template',
|
||||||
|
showActions: true,
|
||||||
|
|
||||||
|
fields: {
|
||||||
|
TOWER_URL_BASE: {
|
||||||
|
type: 'text',
|
||||||
|
reset: 'TOWER_URL_BASE',
|
||||||
|
},
|
||||||
|
TOWER_ADMIN_ALERTS: {
|
||||||
|
type: 'toggleSwitch',
|
||||||
|
},
|
||||||
|
ORG_ADMINS_CAN_SEE_ALL_USERS: {
|
||||||
|
type: 'toggleSwitch',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
buttons: {
|
||||||
|
reset: {
|
||||||
|
ngClick: 'vm.resetAllConfirm()',
|
||||||
|
label: i18n._('Reset All'),
|
||||||
|
class: 'Form-button--left Form-cancelButton'
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
ngClick: 'vm.formCancel()',
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
ngClick: 'vm.formSave()',
|
||||||
|
ngDisabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -289,14 +289,12 @@ angular.module('CredentialsHelper', ['Utilities'])
|
|||||||
|
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||||
if (base === 'credentials') {
|
if (base === 'credentials') {
|
||||||
ReturnToCaller();
|
$state.go('credentials.edit', {credential_id: data.id}, {reload: true});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ReturnToCaller(1);
|
ReturnToCaller(1);
|
||||||
}
|
}
|
||||||
$state.go('credentials.edit', {credential_id: data.id}, {reload: true});
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.error(function (data, status) {
|
.error(function (data, status) {
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count'
|
|||||||
$scope.machine_credential_link = getTowerLink('credential');
|
$scope.machine_credential_link = getTowerLink('credential');
|
||||||
$scope.cloud_credential_link = getTowerLink('cloud_credential');
|
$scope.cloud_credential_link = getTowerLink('cloud_credential');
|
||||||
$scope.network_credential_link = getTowerLink('network_credential');
|
$scope.network_credential_link = getTowerLink('network_credential');
|
||||||
|
$scope.schedule_link = getTowerLink('schedule');
|
||||||
};
|
};
|
||||||
|
|
||||||
// uses options to set scope variables to their readable string
|
// uses options to set scope variables to their readable string
|
||||||
@@ -69,7 +70,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count'
|
|||||||
|
|
||||||
// turn related api browser routes into tower routes
|
// turn related api browser routes into tower routes
|
||||||
getTowerLinks();
|
getTowerLinks();
|
||||||
|
|
||||||
// the links below can't be set in getTowerLinks because the
|
// the links below can't be set in getTowerLinks because the
|
||||||
// links on the UI don't directly match the corresponding URL
|
// links on the UI don't directly match the corresponding URL
|
||||||
// on the API browser
|
// on the API browser
|
||||||
@@ -89,7 +90,9 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count'
|
|||||||
jobData.summary_fields.source_workflow_job.id){
|
jobData.summary_fields.source_workflow_job.id){
|
||||||
$scope.workflow_result_link = `/#/workflows/${jobData.summary_fields.source_workflow_job.id}`;
|
$scope.workflow_result_link = `/#/workflows/${jobData.summary_fields.source_workflow_job.id}`;
|
||||||
}
|
}
|
||||||
|
if(jobData.result_traceback) {
|
||||||
|
$scope.job.result_traceback = jobData.result_traceback.trim().split('\n').join('<br />');
|
||||||
|
}
|
||||||
// use options labels to manipulate display of details
|
// use options labels to manipulate display of details
|
||||||
getTowerLabels();
|
getTowerLabels();
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,19 @@
|
|||||||
<!-- LEFT PANE DETAILS GROUP -->
|
<!-- LEFT PANE DETAILS GROUP -->
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<!-- STATUS DETAIL -->
|
||||||
|
<div class="JobResults-resultRow">
|
||||||
|
<label class="JobResults-resultRowLabel">
|
||||||
|
Status
|
||||||
|
</label>
|
||||||
|
<div class="JobResults-resultRowText">
|
||||||
|
<i class="JobResults-statusResultIcon
|
||||||
|
fa
|
||||||
|
icon-job-{{ job.status }}">
|
||||||
|
</i> {{ status_label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- START TIME DETAIL -->
|
<!-- START TIME DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="JobResults-resultRow"
|
||||||
ng-show="job.started">
|
ng-show="job.started">
|
||||||
@@ -85,6 +98,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- EXPLANATION DETAIL -->
|
||||||
|
<div class="JobResults-resultRow"
|
||||||
|
ng-show="job.job_explanation">
|
||||||
|
<label class="JobResults-resultRowLabel">
|
||||||
|
Explanation
|
||||||
|
</label>
|
||||||
|
<div class="JobResults-resultRowText">
|
||||||
|
{{job.job_explanation}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RESULTS TRACEBACK DETAIL -->
|
||||||
|
<div class="JobResults-resultRow"
|
||||||
|
ng-show="job.result_traceback">
|
||||||
|
<label class="JobResults-resultRowLabel">
|
||||||
|
Results Traceback
|
||||||
|
</label>
|
||||||
|
<div class="JobResults-resultRowText"
|
||||||
|
ng-bind-html="job.result_traceback">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- TEMPLATE DETAIL -->
|
<!-- TEMPLATE DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="JobResults-resultRow"
|
||||||
ng-show="job.summary_fields.job_template.name">
|
ng-show="job.summary_fields.job_template.name">
|
||||||
@@ -135,6 +171,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- SCHEDULED BY DETAIL -->
|
||||||
|
<div class="JobResults-resultRow toggle-show"
|
||||||
|
ng-show="job.summary_fields.schedule.name">
|
||||||
|
<label
|
||||||
|
class="JobResults-resultRowLabel">
|
||||||
|
Launched By
|
||||||
|
</label>
|
||||||
|
<div class="JobResults-resultRowText">
|
||||||
|
<a href="{{ scheduled_by_link }}"
|
||||||
|
aw-tool-tip="Edit the Schedule"
|
||||||
|
data-placement="top">
|
||||||
|
{{ job.summary_fields.schedule.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- INVENTORY DETAIL -->
|
<!-- INVENTORY DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="JobResults-resultRow"
|
||||||
ng-show="job.summary_fields.inventory.name">
|
ng-show="job.summary_fields.inventory.name">
|
||||||
@@ -348,135 +400,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- STATUS DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
JobResults-resultRow
|
|
||||||
toggle-show">
|
|
||||||
<label
|
|
||||||
class="JobResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Status
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText
|
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9">
|
|
||||||
<i
|
|
||||||
class="JobResults-statusIcon--results
|
|
||||||
fa
|
|
||||||
icon-job-{{ job.status }}">
|
|
||||||
</i> {{ status_label }}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- SCHEDULED BY DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
JobResults-resultRow toggle-show"
|
|
||||||
ng-show="job.summary_fields.schedule_by.username">
|
|
||||||
<label
|
|
||||||
class="JobResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Launched By
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ scheduled_by_link }}"
|
|
||||||
aw-tool-tip="Edit the Schedule"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.scheduled_by.username }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- ELAPSED TIME DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
JobResults-resultRow toggle-show"
|
|
||||||
ng-show="job_status.started">
|
|
||||||
<label
|
|
||||||
class="JobResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Elapsed
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job_status.elapsed }}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- EXPLANATION DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
JobResults-resultRow
|
|
||||||
toggle-show"
|
|
||||||
ng-show="job_status.explanation">
|
|
||||||
<label
|
|
||||||
class="JobResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Explanation
|
|
||||||
</label> -->
|
|
||||||
|
|
||||||
<!-- PREVIOUS TASK SUCCEEDED -->
|
|
||||||
<!-- <div class="JobResults-resultRowText
|
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
|
||||||
job_status_explanation"
|
|
||||||
ng-show="!previousTaskFailed"
|
|
||||||
ng-bind-html="job_status.explanation">
|
|
||||||
<i
|
|
||||||
class="JobResults-statusIcon--results
|
|
||||||
fa
|
|
||||||
icon-job-{{ job_status.status }}">
|
|
||||||
</i> {{ job_status.status_label }}
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- PREVIOUS TASK FAILED -->
|
|
||||||
<!-- <div class="JobResults-resultRowText
|
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
|
||||||
job_status_explanation"
|
|
||||||
ng-show="previousTaskFailed">
|
|
||||||
Previous Task Failed
|
|
||||||
<a
|
|
||||||
href=""
|
|
||||||
id="explanation_help"
|
|
||||||
aw-pop-over="{{ task_detail }}"
|
|
||||||
aw-pop-over-watch="task_detail"
|
|
||||||
data-placement="bottom"
|
|
||||||
data-container="body"
|
|
||||||
class="help-link"
|
|
||||||
over-title="Failure Detail"
|
|
||||||
title=""
|
|
||||||
tabindex="-1">
|
|
||||||
<i class="fa fa-question-circle">
|
|
||||||
</i>
|
|
||||||
</a>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
||||||
<!-- RESULTS TRACEBACK DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
JobResults-resultRow
|
|
||||||
toggle-show" ng-show="job.result_traceback">
|
|
||||||
<label
|
|
||||||
class="JobResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-12
|
|
||||||
col-sm-12 col-xs-12">
|
|
||||||
Results Traceback
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText
|
|
||||||
col-lg-10 col-md-12 col-sm-12 col-xs-12
|
|
||||||
job_status_traceback"
|
|
||||||
ng-bind-html="job.result_traceback">
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -490,7 +413,10 @@
|
|||||||
<div class="StandardOut-panelHeader">
|
<div class="StandardOut-panelHeader">
|
||||||
<div class="StandardOut-panelHeaderText">
|
<div class="StandardOut-panelHeaderText">
|
||||||
<i class="JobResults-statusResultIcon
|
<i class="JobResults-statusResultIcon
|
||||||
fa icon-job-{{ job_status }}">
|
fa icon-job-{{ job_status }}"
|
||||||
|
aw-tool-tip="Job {{status_label}}"
|
||||||
|
aw-tip-placement="top"
|
||||||
|
data-original-title>
|
||||||
</i>
|
</i>
|
||||||
{{ job.name }}
|
{{ job.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,3 +105,11 @@
|
|||||||
.WorkflowChart-activeNode {
|
.WorkflowChart-activeNode {
|
||||||
fill: @default-link;
|
fill: @default-link;
|
||||||
}
|
}
|
||||||
|
.WorkflowChart-elapsedHolder {
|
||||||
|
background-color: @b7grey;
|
||||||
|
color: @default-bg;
|
||||||
|
height: 13px;
|
||||||
|
width: 39px;
|
||||||
|
padding: 1px 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ export default [ '$state',
|
|||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.attr("class", "WorkflowChart-startText")
|
.attr("class", "WorkflowChart-startText")
|
||||||
.text(function () { return "START"; })
|
.text(function () { return "START"; })
|
||||||
|
.attr("display", function() { return scope.mode === 'details' ? 'none' : null;})
|
||||||
.call(add_node);
|
.call(add_node);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -225,10 +226,10 @@ export default [ '$state',
|
|||||||
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
||||||
|
|
||||||
thisNode.append("text")
|
thisNode.append("text")
|
||||||
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 20 : nodeW / 2; })
|
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 20 : nodeW / 2; })
|
||||||
.attr("y", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 10 : nodeH / 2; })
|
.attr("y", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 10 : nodeH / 2; })
|
||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.attr("text-anchor", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? "inherit" : "middle"; })
|
.attr("text-anchor", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? "inherit" : "middle"; })
|
||||||
.attr("class", "WorkflowChart-defaultText WorkflowChart-nameText")
|
.attr("class", "WorkflowChart-defaultText WorkflowChart-nameText")
|
||||||
.text(function (d) {
|
.text(function (d) {
|
||||||
return (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? d.unifiedJobTemplate.name : "";
|
return (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? d.unifiedJobTemplate.name : "";
|
||||||
@@ -293,7 +294,7 @@ export default [ '$state',
|
|||||||
.attr("y", nodeH - 10)
|
.attr("y", nodeH - 10)
|
||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.attr("class", "WorkflowChart-detailsLink")
|
.attr("class", "WorkflowChart-detailsLink")
|
||||||
.style("display", function(d){ return d.job && d.job.jobStatus && d.job.unified_job_id ? null : "none"; })
|
.style("display", function(d){ return d.job && d.job.status && d.job.id ? null : "none"; })
|
||||||
.text(function () {
|
.text(function () {
|
||||||
return "DETAILS";
|
return "DETAILS";
|
||||||
})
|
})
|
||||||
@@ -388,7 +389,7 @@ export default [ '$state',
|
|||||||
let statusClass = "WorkflowChart-nodeStatus ";
|
let statusClass = "WorkflowChart-nodeStatus ";
|
||||||
|
|
||||||
if(d.job){
|
if(d.job){
|
||||||
switch(d.job.jobStatus) {
|
switch(d.job.status) {
|
||||||
case "pending":
|
case "pending":
|
||||||
statusClass = "workflowChart-nodeStatus--running";
|
statusClass = "workflowChart-nodeStatus--running";
|
||||||
break;
|
break;
|
||||||
@@ -404,15 +405,37 @@ export default [ '$state',
|
|||||||
case "failed":
|
case "failed":
|
||||||
statusClass = "workflowChart-nodeStatus--failed";
|
statusClass = "workflowChart-nodeStatus--failed";
|
||||||
break;
|
break;
|
||||||
|
case "error":
|
||||||
|
statusClass = "workflowChart-nodeStatus--failed";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusClass;
|
return statusClass;
|
||||||
})
|
})
|
||||||
.style("display", function(d) { return d.job && d.job.jobStatus ? null : "none"; })
|
.style("display", function(d) { return d.job && d.job.status ? null : "none"; })
|
||||||
.attr("cy", 10)
|
.attr("cy", 10)
|
||||||
.attr("cx", 10)
|
.attr("cx", 10)
|
||||||
.attr("r", 6);
|
.attr("r", 6);
|
||||||
|
|
||||||
|
thisNode.append("foreignObject")
|
||||||
|
.attr("x", 5)
|
||||||
|
.attr("y", 43)
|
||||||
|
.style("font-size","0.7em")
|
||||||
|
.attr("class", "WorkflowChart-elapsed")
|
||||||
|
.html(function (d) {
|
||||||
|
if(d.job && d.job.elapsed) {
|
||||||
|
let elapsedMs = d.job.elapsed * 1000;
|
||||||
|
let elapsedMoment = moment.duration(elapsedMs);
|
||||||
|
let paddedElapsedMoment = Math.floor(elapsedMoment.asHours()) < 10 ? "0" + Math.floor(elapsedMoment.asHours()) : Math.floor(elapsedMoment.asHours());
|
||||||
|
let elapsedString = paddedElapsedMoment + moment.utc(elapsedMs).format(":mm:ss");
|
||||||
|
return "<div class=\"WorkflowChart-elapsedHolder\"><span>" + elapsedString + "</span></div>";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.style("display", function(d) { return (d.job && d.job.elapsed) ? null : "none"; });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -608,7 +631,7 @@ export default [ '$state',
|
|||||||
let statusClass = "WorkflowChart-nodeStatus ";
|
let statusClass = "WorkflowChart-nodeStatus ";
|
||||||
|
|
||||||
if(d.job){
|
if(d.job){
|
||||||
switch(d.job.jobStatus) {
|
switch(d.job.status) {
|
||||||
case "pending":
|
case "pending":
|
||||||
statusClass += "workflowChart-nodeStatus--running";
|
statusClass += "workflowChart-nodeStatus--running";
|
||||||
break;
|
break;
|
||||||
@@ -624,17 +647,20 @@ export default [ '$state',
|
|||||||
case "failed":
|
case "failed":
|
||||||
statusClass += "workflowChart-nodeStatus--failed";
|
statusClass += "workflowChart-nodeStatus--failed";
|
||||||
break;
|
break;
|
||||||
|
case "error":
|
||||||
|
statusClass = "workflowChart-nodeStatus--failed";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusClass;
|
return statusClass;
|
||||||
})
|
})
|
||||||
.style("display", function(d) { return d.job && d.job.jobStatus ? null : "none"; })
|
.style("display", function(d) { return d.job && d.job.status ? null : "none"; })
|
||||||
.transition()
|
.transition()
|
||||||
.duration(0)
|
.duration(0)
|
||||||
.attr("r", 6)
|
.attr("r", 6)
|
||||||
.each(function(d) {
|
.each(function(d) {
|
||||||
if(d.job && d.job.jobStatus && (d.job.jobStatus === "pending" || d.job.jobStatus === "waiting" || d.job.jobStatus === "running")) {
|
if(d.job && d.job.status && (d.job.status === "pending" || d.job.status === "waiting" || d.job.status === "running")) {
|
||||||
// Pulse the circle
|
// Pulse the circle
|
||||||
var circle = d3.select(this);
|
var circle = d3.select(this);
|
||||||
(function repeat() {
|
(function repeat() {
|
||||||
@@ -651,15 +677,15 @@ export default [ '$state',
|
|||||||
});
|
});
|
||||||
|
|
||||||
t.selectAll(".WorkflowChart-nameText")
|
t.selectAll(".WorkflowChart-nameText")
|
||||||
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 20 : nodeW / 2; })
|
.attr("x", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 20 : nodeW / 2; })
|
||||||
.attr("y", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? 10 : nodeH / 2; })
|
.attr("y", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? 10 : nodeH / 2; })
|
||||||
.attr("text-anchor", function(d){ return (scope.mode === 'details' && d.job && d.job.jobStatus) ? "inherit" : "middle"; })
|
.attr("text-anchor", function(d){ return (scope.mode === 'details' && d.job && d.job.status) ? "inherit" : "middle"; })
|
||||||
.text(function (d) {
|
.text(function (d) {
|
||||||
return (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? wrap(d.unifiedJobTemplate.name) : "";
|
return (d.unifiedJobTemplate && d.unifiedJobTemplate.name) ? wrap(d.unifiedJobTemplate.name) : "";
|
||||||
});
|
});
|
||||||
|
|
||||||
t.selectAll(".WorkflowChart-detailsLink")
|
t.selectAll(".WorkflowChart-detailsLink")
|
||||||
.style("display", function(d){ return d.job && d.job.jobStatus && d.job.unified_job_id ? null : "none"; });
|
.style("display", function(d){ return d.job && d.job.status && d.job.id ? null : "none"; });
|
||||||
|
|
||||||
t.selectAll(".WorkflowChart-incompleteText")
|
t.selectAll(".WorkflowChart-incompleteText")
|
||||||
.style("display", function(d){ return d.unifiedJobTemplate || d.placeholder ? "none" : null; });
|
.style("display", function(d){ return d.unifiedJobTemplate || d.placeholder ? "none" : null; });
|
||||||
@@ -670,6 +696,9 @@ export default [ '$state',
|
|||||||
t.selectAll(".WorkflowChart-activeNode")
|
t.selectAll(".WorkflowChart-activeNode")
|
||||||
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
.style("display", function(d) { return d.isActiveEdit ? null : "none"; });
|
||||||
|
|
||||||
|
t.selectAll(".WorkflowChart-elapsed")
|
||||||
|
.style("display", function(d) { return (d.job && d.job.elapsed) ? null : "none"; });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_node() {
|
function add_node() {
|
||||||
@@ -722,15 +751,15 @@ export default [ '$state',
|
|||||||
d3.select(this).style("text-decoration", null);
|
d3.select(this).style("text-decoration", null);
|
||||||
});
|
});
|
||||||
this.on("click", function(d) {
|
this.on("click", function(d) {
|
||||||
if(d.job.unified_job_id && d.unifiedJobTemplate) {
|
if(d.job.id && d.unifiedJobTemplate) {
|
||||||
if(d.unifiedJobTemplate.unified_job_type === 'job') {
|
if(d.unifiedJobTemplate.unified_job_type === 'job') {
|
||||||
$state.go('jobDetail', {id: d.job.unified_job_id});
|
$state.go('jobDetail', {id: d.job.id});
|
||||||
}
|
}
|
||||||
else if(d.unifiedJobTemplate.unified_job_type === 'inventory_update') {
|
else if(d.unifiedJobTemplate.unified_job_type === 'inventory_update') {
|
||||||
$state.go('inventorySyncStdout', {id: d.job.unified_job_id});
|
$state.go('inventorySyncStdout', {id: d.job.id});
|
||||||
}
|
}
|
||||||
else if(d.unifiedJobTemplate.unified_job_type === 'project_update') {
|
else if(d.unifiedJobTemplate.unified_job_type === 'project_update') {
|
||||||
$state.go('scmUpdateStdout', {id: d.job.unified_job_id});
|
$state.go('scmUpdateStdout', {id: d.job.id});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -224,10 +224,7 @@ export default [function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(params.nodesObj[params.nodeId].summary_fields.job) {
|
if(params.nodesObj[params.nodeId].summary_fields.job) {
|
||||||
treeNode.job = {
|
treeNode.job = _.clone(params.nodesObj[params.nodeId].summary_fields.job);
|
||||||
jobStatus: params.nodesObj[params.nodeId].summary_fields.job.status,
|
|
||||||
unified_job_id: params.nodesObj[params.nodeId].summary_fields.job.id
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(params.nodesObj[params.nodeId].summary_fields.unified_job_template) {
|
if(params.nodesObj[params.nodeId].summary_fields.unified_job_template) {
|
||||||
@@ -282,8 +279,8 @@ export default [function(){
|
|||||||
|
|
||||||
if(matchingNode) {
|
if(matchingNode) {
|
||||||
matchingNode.job = {
|
matchingNode.job = {
|
||||||
jobStatus: params.status,
|
status: params.status,
|
||||||
unified_job_id: params.unified_job_id
|
id: params.unified_job_id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,19 @@
|
|||||||
<!-- LEFT PANE DETAILS GROUP -->
|
<!-- LEFT PANE DETAILS GROUP -->
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<!-- STATUS DETAIL -->
|
||||||
|
<div class="WorkflowResults-resultRow">
|
||||||
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
|
Status
|
||||||
|
</label>
|
||||||
|
<div class="WorkflowResults-resultRowText">
|
||||||
|
<i class="WorkflowResults-statusResultIcon
|
||||||
|
fa
|
||||||
|
icon-job-{{ workflow.status }}">
|
||||||
|
</i> {{ status_label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- START TIME DETAIL -->
|
<!-- START TIME DETAIL -->
|
||||||
<div class="WorkflowResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="workflow.started">
|
ng-show="workflow.started">
|
||||||
@@ -85,32 +98,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TEMPLATE DETAIL -->
|
|
||||||
<div class="WorkflowResults-resultRow"
|
|
||||||
ng-show="workflow.name">
|
|
||||||
<label class="WorkflowResults-resultRowLabel">
|
|
||||||
Template
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText">
|
|
||||||
<a href="{{ workflow_template_link }}"
|
|
||||||
aw-tool-tip="Edit the job template"
|
|
||||||
data-placement="top">
|
|
||||||
{{ workflow.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- JOB TYPE DETAIL -->
|
|
||||||
<div class="WorkflowResults-resultRow"
|
|
||||||
ng-show="workflow.type">
|
|
||||||
<label class="WorkflowResults-resultRowLabel">
|
|
||||||
Job Type
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText">
|
|
||||||
Workflow Job
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CREATED BY DETAIL -->
|
<!-- CREATED BY DETAIL -->
|
||||||
<div class="WorkflowResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="workflow.summary_fields.created_by.username">
|
ng-show="workflow.summary_fields.created_by.username">
|
||||||
@@ -180,7 +167,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- end of labels-->
|
<!-- end of labels-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -195,7 +182,10 @@
|
|||||||
<div class="StandardOut-panelHeader">
|
<div class="StandardOut-panelHeader">
|
||||||
<div class="StandardOut-panelHeaderText">
|
<div class="StandardOut-panelHeaderText">
|
||||||
<i class="WorkflowResults-statusResultIcon
|
<i class="WorkflowResults-statusResultIcon
|
||||||
fa icon-job-{{ workflow.status }}">
|
fa icon-job-{{ workflow.status }}"
|
||||||
|
aw-tool-tip="Job {{status_label}}"
|
||||||
|
aw-tip-placement="top"
|
||||||
|
data-original-title>
|
||||||
</i>
|
</i>
|
||||||
{{ workflow.name }}
|
{{ workflow.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -265,7 +255,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<workflow-chart tree-data="treeData.data" can-add-workflow-job-template="canAddWorkflowJobTemplate" mode="details" class="WorkflowMaker-chart"></workflow-chart>
|
<workflow-chart tree-data="treeData.data" workflow-zoomed="workflowZoomed(zoom)" can-add-workflow-job-template="canAddWorkflowJobTemplate" mode="details" class="WorkflowMaker-chart"></workflow-chart>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user