mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 23:07:42 -02:30
Polish split jobs API info & add fields to UI
*clarify help text and squash migrations *adds new internal_limit field to Job model for faster reference *if field is non-blank, populate shard params in summary_fields *add summary information to UI job/wfj details, JT selector
This commit is contained in:
@@ -3199,6 +3199,13 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
|
|||||||
|
|
||||||
def get_summary_fields(self, obj):
|
def get_summary_fields(self, obj):
|
||||||
summary_fields = super(JobSerializer, self).get_summary_fields(obj)
|
summary_fields = super(JobSerializer, self).get_summary_fields(obj)
|
||||||
|
if obj.internal_limit:
|
||||||
|
summary_fields['internal_limit'] = {}
|
||||||
|
if obj.internal_limit.startswith('shard'):
|
||||||
|
offset, step = Inventory.parse_shard_params(obj.internal_limit)
|
||||||
|
summary_fields['internal_limit']['shard'] = {'offset': offset, 'step': step}
|
||||||
|
else:
|
||||||
|
summary_fields['internal_limit']['unknown'] = self.internal_limit
|
||||||
all_creds = []
|
all_creds = []
|
||||||
# Organize credential data into multitude of deprecated fields
|
# Organize credential data into multitude of deprecated fields
|
||||||
# TODO: remove most of this as v1 is removed
|
# TODO: remove most of this as v1 is removed
|
||||||
|
|||||||
@@ -2914,11 +2914,13 @@ class JobTemplateLaunch(RetrieveAPIView):
|
|||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data['job'] = new_job.id
|
|
||||||
data['ignored_fields'] = self.sanitize_for_response(ignored_fields)
|
|
||||||
if isinstance(new_job, WorkflowJob):
|
if isinstance(new_job, WorkflowJob):
|
||||||
|
data['workflow_job'] = new_job.id
|
||||||
|
data['ignored_fields'] = self.sanitize_for_response(ignored_fields)
|
||||||
data.update(WorkflowJobSerializer(new_job, context=self.get_serializer_context()).to_representation(new_job))
|
data.update(WorkflowJobSerializer(new_job, context=self.get_serializer_context()).to_representation(new_job))
|
||||||
else:
|
else:
|
||||||
|
data['job'] = new_job.id
|
||||||
|
data['ignored_fields'] = self.sanitize_for_response(ignored_fields)
|
||||||
data.update(JobSerializer(new_job, context=self.get_serializer_context()).to_representation(new_job))
|
data.update(JobSerializer(new_job, context=self.get_serializer_context()).to_representation(new_job))
|
||||||
headers = {'Location': new_job.get_absolute_url(request)}
|
headers = {'Location': new_job.get_absolute_url(request)}
|
||||||
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|||||||
37
awx/main/migrations/0048_v330_split_jobs.py
Normal file
37
awx/main/migrations/0048_v330_split_jobs.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.11 on 2018-09-13 15:55
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import awx.main.utils.polymorphic
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0047_v330_activitystream_instance'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='jobtemplate',
|
||||||
|
name='job_shard_count',
|
||||||
|
field=models.IntegerField(blank=True, default=0, help_text='The number of jobs to split into at runtime. Will cause the Job Template to launch a workflow if value is non-zero.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='workflowjob',
|
||||||
|
name='job_template',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sharded_jobs', to='main.JobTemplate'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='unifiedjob',
|
||||||
|
name='unified_job_template',
|
||||||
|
field=models.ForeignKey(default=None, editable=False, null=True, on_delete=awx.main.utils.polymorphic.SET_NULL, related_name='unifiedjob_unified_jobs', to='main.UnifiedJobTemplate'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='job',
|
||||||
|
name='internal_limit',
|
||||||
|
field=models.CharField(default=b'', editable=False, max_length=1024),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.11 on 2018-08-14 13:43
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('main', '0047_v330_activitystream_instance'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='jobtemplate',
|
|
||||||
name='job_shard_count',
|
|
||||||
field=models.IntegerField(blank=True,
|
|
||||||
default=0,
|
|
||||||
help_text='The number of jobs to split into at runtime. Will cause the Job Template to launch a workflow.'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.11 on 2018-08-14 16:04
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('main', '0048_v340_split_jobs'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='workflowjob',
|
|
||||||
name='job_template',
|
|
||||||
field=models.ForeignKey(blank=True,
|
|
||||||
default=None,
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.SET_NULL,
|
|
||||||
related_name='sharded_jobs', to='main.JobTemplate'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.11 on 2018-09-10 17:41
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import awx.main.utils.polymorphic
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('main', '0049_v340_add_job_template'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='unifiedjob',
|
|
||||||
name='unified_job_template',
|
|
||||||
field=models.ForeignKey(default=None, editable=False, null=True, on_delete=awx.main.utils.polymorphic.SET_NULL, related_name='unifiedjob_unified_jobs', to='main.UnifiedJobTemplate'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -280,7 +280,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
|||||||
job_shard_count = models.IntegerField(
|
job_shard_count = models.IntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
default=0,
|
default=0,
|
||||||
help_text=_("The number of jobs to split into at runtime. Will cause the Job Template to launch a workflow."),
|
help_text=_("The number of jobs to split into at runtime. "
|
||||||
|
"Will cause the Job Template to launch a workflow if value is non-zero."),
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_role = ImplicitRoleField(
|
admin_role = ImplicitRoleField(
|
||||||
@@ -301,7 +302,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _get_unified_job_field_names(cls):
|
def _get_unified_job_field_names(cls):
|
||||||
return set(f.name for f in JobOptions._meta.fields) | set(
|
return set(f.name for f in JobOptions._meta.fields) | set(
|
||||||
['name', 'description', 'schedule', 'survey_passwords', 'labels', 'credentials']
|
['name', 'description', 'schedule', 'survey_passwords', 'labels', 'credentials', 'internal_limit']
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -327,10 +328,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
|||||||
return self.create_unified_job(**kwargs)
|
return self.create_unified_job(**kwargs)
|
||||||
|
|
||||||
def create_unified_job(self, **kwargs):
|
def create_unified_job(self, **kwargs):
|
||||||
split_event = bool(
|
prevent_sharding = kwargs.pop('_prevent_sharding', False)
|
||||||
self.job_shard_count > 1 and
|
split_event = bool(self.job_shard_count > 1 and (not prevent_sharding))
|
||||||
not kwargs.pop('_prevent_sharding', False)
|
|
||||||
)
|
|
||||||
if split_event:
|
if split_event:
|
||||||
# A sharded Job Template will generate a WorkflowJob rather than a Job
|
# A sharded Job Template will generate a WorkflowJob rather than a Job
|
||||||
from awx.main.models.workflow import WorkflowJobTemplate, WorkflowJobNode
|
from awx.main.models.workflow import WorkflowJobTemplate, WorkflowJobNode
|
||||||
@@ -532,6 +531,11 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
|||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
help_text=_('The SCM Refresh task used to make sure the playbooks were available for the job run'),
|
help_text=_('The SCM Refresh task used to make sure the playbooks were available for the job run'),
|
||||||
)
|
)
|
||||||
|
internal_limit = models.CharField(
|
||||||
|
max_length=1024,
|
||||||
|
default='',
|
||||||
|
editable=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _get_parent_field_name(self):
|
def _get_parent_field_name(self):
|
||||||
@@ -575,6 +579,13 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
|||||||
def event_class(self):
|
def event_class(self):
|
||||||
return JobEvent
|
return JobEvent
|
||||||
|
|
||||||
|
def copy_unified_job(self, **new_prompts):
|
||||||
|
new_prompts['_prevent_sharding'] = True
|
||||||
|
if self.internal_limit:
|
||||||
|
new_prompts.setdefault('_eager_fields', {})
|
||||||
|
new_prompts['_eager_fields']['internal_limit'] = self.internal_limit # oddball, not from JT or prompts
|
||||||
|
return super(Job, self).copy_unified_job(**new_prompts)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ask_diff_mode_on_launch(self):
|
def ask_diff_mode_on_launch(self):
|
||||||
if self.job_template is not None:
|
if self.job_template is not None:
|
||||||
|
|||||||
@@ -259,6 +259,10 @@ class WorkflowJobNode(WorkflowNodeBase):
|
|||||||
shard_str
|
shard_str
|
||||||
)
|
)
|
||||||
data['_eager_fields']['allow_simultaneous'] = True
|
data['_eager_fields']['allow_simultaneous'] = True
|
||||||
|
data['_eager_fields']['internal_limit'] = 'shard{0}of{1}'.format(
|
||||||
|
self.ancestor_artifacts['job_shard'],
|
||||||
|
self.workflow_job.workflow_job_nodes.count()
|
||||||
|
)
|
||||||
data['_prevent_sharding'] = True
|
data['_prevent_sharding'] = True
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -314,7 +318,7 @@ class WorkflowJobOptions(BaseModel):
|
|||||||
|
|
||||||
def create_relaunch_workflow_job(self):
|
def create_relaunch_workflow_job(self):
|
||||||
new_workflow_job = self.copy_unified_job()
|
new_workflow_job = self.copy_unified_job()
|
||||||
if self.workflow_job_template is None:
|
if self.unified_job_template_id is None:
|
||||||
new_workflow_job.copy_nodes_from_original(original=self)
|
new_workflow_job.copy_nodes_from_original(original=self)
|
||||||
return new_workflow_job
|
return new_workflow_job
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ class TaskManager():
|
|||||||
logger.debug(six.text_type("Dependent {} couldn't be scheduled on graph, waiting for next cycle").format(task.log_format))
|
logger.debug(six.text_type("Dependent {} couldn't be scheduled on graph, waiting for next cycle").format(task.log_format))
|
||||||
|
|
||||||
def process_pending_tasks(self, pending_tasks):
|
def process_pending_tasks(self, pending_tasks):
|
||||||
running_workflow_templates = set([wf.workflow_job_template_id for wf in self.get_running_workflow_jobs()])
|
running_workflow_templates = set([wf.unified_job_template_id for wf in self.get_running_workflow_jobs()])
|
||||||
for task in pending_tasks:
|
for task in pending_tasks:
|
||||||
self.process_dependencies(task, self.generate_dependencies(task))
|
self.process_dependencies(task, self.generate_dependencies(task))
|
||||||
if self.is_job_blocked(task):
|
if self.is_job_blocked(task):
|
||||||
@@ -429,12 +429,12 @@ class TaskManager():
|
|||||||
found_acceptable_queue = False
|
found_acceptable_queue = False
|
||||||
idle_instance_that_fits = None
|
idle_instance_that_fits = None
|
||||||
if isinstance(task, WorkflowJob):
|
if isinstance(task, WorkflowJob):
|
||||||
if task.workflow_job_template_id in running_workflow_templates:
|
if task.unified_job_template_id in running_workflow_templates:
|
||||||
if not task.allow_simultaneous:
|
if not task.allow_simultaneous:
|
||||||
logger.debug(six.text_type("{} is blocked from running, workflow already running").format(task.log_format))
|
logger.debug(six.text_type("{} is blocked from running, workflow already running").format(task.log_format))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
running_workflow_templates.add(task.workflow_job_template_id)
|
running_workflow_templates.add(task.unified_job_template_id)
|
||||||
self.start_task(task, None, task.get_jobs_fail_chain(), None)
|
self.start_task(task, None, task.get_jobs_fail_chain(), None)
|
||||||
continue
|
continue
|
||||||
for rampart_group in preferred_instance_groups:
|
for rampart_group in preferred_instance_groups:
|
||||||
|
|||||||
@@ -825,15 +825,9 @@ class BaseTask(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def build_inventory(self, instance, **kwargs):
|
def build_inventory(self, instance, **kwargs):
|
||||||
workflow_job = instance.get_workflow_job()
|
script_data = instance.inventory.get_script_data(
|
||||||
if workflow_job and workflow_job.job_template_id:
|
hostvars=True, subset=getattr(instance, 'internal_limit', '')
|
||||||
shard_address = 'shard{0}of{1}'.format(
|
)
|
||||||
instance.unified_job_node.ancestor_artifacts['job_shard'],
|
|
||||||
workflow_job.workflow_job_nodes.count()
|
|
||||||
)
|
|
||||||
script_data = instance.inventory.get_script_data(hostvars=True, subset=shard_address)
|
|
||||||
else:
|
|
||||||
script_data = instance.inventory.get_script_data(hostvars=True)
|
|
||||||
json_data = json.dumps(script_data)
|
json_data = json.dumps(script_data)
|
||||||
handle, path = tempfile.mkstemp(dir=kwargs.get('private_data_dir', None))
|
handle, path = tempfile.mkstemp(dir=kwargs.get('private_data_dir', None))
|
||||||
f = os.fdopen(handle, 'w')
|
f = os.fdopen(handle, 'w')
|
||||||
|
|||||||
@@ -126,6 +126,26 @@ function getSourceWorkflowJobDetails () {
|
|||||||
return { link, tooltip };
|
return { link, tooltip };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getShardDetails () {
|
||||||
|
const internalLimitDetails = resource.model.get('summary_fields.internal_limit');
|
||||||
|
|
||||||
|
if (!internalLimitDetails) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shardDetails = resource.model.get('summary_fields.internal_limit.shard');
|
||||||
|
|
||||||
|
if (!shardDetails) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = strings.get('labels.SHARD_DETAILS');
|
||||||
|
const offset = `${shardDetails.offset} of ${shardDetails.step} shards`;
|
||||||
|
const tooltip = strings.get('tooltips.SHARD_DETAILS');
|
||||||
|
|
||||||
|
return { label, offset, tooltip };
|
||||||
|
}
|
||||||
|
|
||||||
function getJobTemplateDetails () {
|
function getJobTemplateDetails () {
|
||||||
const jobTemplate = resource.model.get('summary_fields.job_template');
|
const jobTemplate = resource.model.get('summary_fields.job_template');
|
||||||
|
|
||||||
@@ -671,6 +691,7 @@ function JobDetailsController (
|
|||||||
vm.jobType = getJobTypeDetails();
|
vm.jobType = getJobTypeDetails();
|
||||||
vm.jobTemplate = getJobTemplateDetails();
|
vm.jobTemplate = getJobTemplateDetails();
|
||||||
vm.sourceWorkflowJob = getSourceWorkflowJobDetails();
|
vm.sourceWorkflowJob = getSourceWorkflowJobDetails();
|
||||||
|
vm.shardDetails = getShardDetails();
|
||||||
vm.inventory = getInventoryDetails();
|
vm.inventory = getInventoryDetails();
|
||||||
vm.project = getProjectDetails();
|
vm.project = getProjectDetails();
|
||||||
vm.projectUpdate = getProjectUpdateDetails();
|
vm.projectUpdate = getProjectUpdateDetails();
|
||||||
|
|||||||
@@ -151,6 +151,12 @@
|
|||||||
<div class="JobResults-resultRowText">{{ vm.jobType.value }}</div>
|
<div class="JobResults-resultRowText">{{ vm.jobType.value }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- SHAAAAAARD -->
|
||||||
|
<div class="JobResults-resultRow" ng-if="vm.shardDetails">
|
||||||
|
<label class="JobResults-resultRowLabel">{{ vm.shardDetails.label }}</label>
|
||||||
|
<div class="JobResults-resultRowText">{{ vm.shardDetails.offset }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- LAUNCHED BY DETAIL -->
|
<!-- LAUNCHED BY DETAIL -->
|
||||||
<div class="JobResults-resultRow" ng-if="vm.launchedBy">
|
<div class="JobResults-resultRow" ng-if="vm.launchedBy">
|
||||||
<label class="JobResults-resultRowLabel">{{ vm.launchedBy.label }}</label>
|
<label class="JobResults-resultRowLabel">{{ vm.launchedBy.label }}</label>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ function OutputStrings (BaseString) {
|
|||||||
EXTRA_VARS: t.s('Read-only view of extra variables added to the job template'),
|
EXTRA_VARS: t.s('Read-only view of extra variables added to the job template'),
|
||||||
INVENTORY: t.s('View the Inventory'),
|
INVENTORY: t.s('View the Inventory'),
|
||||||
JOB_TEMPLATE: t.s('View the Job Template'),
|
JOB_TEMPLATE: t.s('View the Job Template'),
|
||||||
|
SHARD_DETAILS: t.s('Job is one of several shards from a JT that splits on inventory'),
|
||||||
PROJECT: t.s('View the Project'),
|
PROJECT: t.s('View the Project'),
|
||||||
PROJECT_UPDATE: t.s('View Project checkout results'),
|
PROJECT_UPDATE: t.s('View Project checkout results'),
|
||||||
SCHEDULE: t.s('View the Schedule'),
|
SCHEDULE: t.s('View the Schedule'),
|
||||||
@@ -55,6 +56,7 @@ function OutputStrings (BaseString) {
|
|||||||
JOB_EXPLANATION: t.s('Explanation'),
|
JOB_EXPLANATION: t.s('Explanation'),
|
||||||
JOB_TAGS: t.s('Job Tags'),
|
JOB_TAGS: t.s('Job Tags'),
|
||||||
JOB_TEMPLATE: t.s('Job Template'),
|
JOB_TEMPLATE: t.s('Job Template'),
|
||||||
|
SHARD_DETAILS: t.s('Shard Details'),
|
||||||
JOB_TYPE: t.s('Job Type'),
|
JOB_TYPE: t.s('Job Type'),
|
||||||
LABELS: t.s('Labels'),
|
LABELS: t.s('Labels'),
|
||||||
LAUNCHED_BY: t.s('Launched By'),
|
LAUNCHED_BY: t.s('Launched By'),
|
||||||
|
|||||||
@@ -271,6 +271,20 @@ function(NotificationsList, i18n) {
|
|||||||
},
|
},
|
||||||
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
||||||
},
|
},
|
||||||
|
job_shard_count: {
|
||||||
|
label: i18n._('Number of job shards to use'),
|
||||||
|
type: 'number',
|
||||||
|
integer: true,
|
||||||
|
min: 0,
|
||||||
|
spinner: true,
|
||||||
|
// 'class': "input-small",
|
||||||
|
// toggleSource: 'diff_mode',
|
||||||
|
dataTitle: i18n._('Job Shard Count'),
|
||||||
|
dataPlacement: 'right',
|
||||||
|
dataContainer: 'body',
|
||||||
|
awPopOver: "<p>" + i18n._("If non-zero, split into multiple jobs that run on mutually exclusive slices of the inventory.") + "</p>",
|
||||||
|
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)'
|
||||||
|
},
|
||||||
checkbox_group: {
|
checkbox_group: {
|
||||||
label: i18n._('Options'),
|
label: i18n._('Options'),
|
||||||
type: 'checkbox_group',
|
type: 'checkbox_group',
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
|||||||
DELETE: i18n._('Delete'),
|
DELETE: i18n._('Delete'),
|
||||||
EDIT_USER: i18n._('Edit the user'),
|
EDIT_USER: i18n._('Edit the user'),
|
||||||
EDIT_WORKFLOW: i18n._('Edit the workflow job template'),
|
EDIT_WORKFLOW: i18n._('Edit the workflow job template'),
|
||||||
|
EDIT_SHARD_TEMPLATE: i18n._('Edit the shard job template'),
|
||||||
EDIT_SCHEDULE: i18n._('Edit the schedule'),
|
EDIT_SCHEDULE: i18n._('Edit the schedule'),
|
||||||
TOGGLE_STDOUT_FULLSCREEN: i18n._('Expand Output'),
|
TOGGLE_STDOUT_FULLSCREEN: i18n._('Expand Output'),
|
||||||
STATUS: '' // re-assigned elsewhere
|
STATUS: '' // re-assigned elsewhere
|
||||||
@@ -49,6 +50,7 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
|||||||
STARTED: i18n._('Started'),
|
STARTED: i18n._('Started'),
|
||||||
FINISHED: i18n._('Finished'),
|
FINISHED: i18n._('Finished'),
|
||||||
LABELS: i18n._('Labels'),
|
LABELS: i18n._('Labels'),
|
||||||
|
SHARD_TEMPLATE: i18n._('Shard Template'),
|
||||||
STATUS: i18n._('Status')
|
STATUS: i18n._('Status')
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
@@ -109,6 +111,11 @@ export default ['workflowData', 'workflowResultsService', 'workflowDataOptions',
|
|||||||
$scope.workflow_job_template_link = `/#/templates/workflow_job_template/${$scope.workflow.summary_fields.workflow_job_template.id}`;
|
$scope.workflow_job_template_link = `/#/templates/workflow_job_template/${$scope.workflow.summary_fields.workflow_job_template.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(workflowData.summary_fields && workflowData.summary_fields.job_template &&
|
||||||
|
workflowData.summary_fields.job_template.id){
|
||||||
|
$scope.shard_job_template_link = `/#/templates/job_template/${$scope.workflow.summary_fields.job_template.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
// turn related api browser routes into front end routes
|
// turn related api browser routes into front end routes
|
||||||
getLinks();
|
getLinks();
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- SHAAAAAARD -->
|
||||||
|
<div class="WorkflowResults-resultRow"
|
||||||
|
ng-show="workflow.summary_fields.job_template.name">
|
||||||
|
<label
|
||||||
|
class="WorkflowResults-resultRowLabel">
|
||||||
|
{{ strings.labels.SHARD_TEMPLATE }}
|
||||||
|
</label>
|
||||||
|
<div class="WorkflowResults-resultRowText">
|
||||||
|
<a href="{{ shard_job_template_link }}"
|
||||||
|
aw-tool-tip="{{ strings.tooltips.EDIT_SHARD_TEMPLATE }}"
|
||||||
|
data-placement="top">
|
||||||
|
{{ workflow.summary_fields.job_template.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- EXTRA VARIABLES DETAIL -->
|
<!-- EXTRA VARIABLES DETAIL -->
|
||||||
<at-code-mirror
|
<at-code-mirror
|
||||||
ng-if="variables"
|
ng-if="variables"
|
||||||
|
|||||||
Reference in New Issue
Block a user