mirror of
https://github.com/ansible/awx.git
synced 2026-02-24 22:46:01 -03:30
rename to slicing and schema tweaks
This commit is contained in:
@@ -221,17 +221,19 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin):
|
||||
return group_children_map
|
||||
|
||||
@staticmethod
|
||||
def parse_split_params(split_str):
|
||||
m = re.match(r"split(?P<offset>\d+)of(?P<step>\d+)", split_str)
|
||||
def parse_slice_params(slice_str):
|
||||
m = re.match(r"slice(?P<number>\d+)of(?P<step>\d+)", slice_str)
|
||||
if not m:
|
||||
raise ParseError(_('Could not parse subset as split specification.'))
|
||||
offset = int(m.group('offset'))
|
||||
raise ParseError(_('Could not parse subset as slice specification.'))
|
||||
number = int(m.group('number'))
|
||||
step = int(m.group('step'))
|
||||
if offset > step:
|
||||
raise ParseError(_('Split offset must be greater than total number of splits.'))
|
||||
return (offset, step)
|
||||
if number > step:
|
||||
raise ParseError(_('Slice number must be less than total number of slices.'))
|
||||
elif number < 1:
|
||||
raise ParseError(_('Slice number must be 1 or higher.'))
|
||||
return (number, step)
|
||||
|
||||
def get_script_data(self, hostvars=False, towervars=False, show_all=False, subset=None):
|
||||
def get_script_data(self, hostvars=False, towervars=False, show_all=False, slice_number=1, slice_count=1):
|
||||
hosts_kw = dict()
|
||||
if not show_all:
|
||||
hosts_kw['enabled'] = True
|
||||
@@ -239,14 +241,9 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin):
|
||||
if towervars:
|
||||
fetch_fields.append('enabled')
|
||||
hosts = self.hosts.filter(**hosts_kw).order_by('name').only(*fetch_fields)
|
||||
if subset:
|
||||
if not isinstance(subset, six.string_types):
|
||||
raise ParseError(_('Inventory subset argument must be a string.'))
|
||||
if subset.startswith('split'):
|
||||
offset, step = Inventory.parse_split_params(subset)
|
||||
hosts = hosts[offset::step]
|
||||
else:
|
||||
raise ParseError(_('Subset does not use any supported syntax.'))
|
||||
if slice_count > 1:
|
||||
offset = slice_number - 1
|
||||
hosts = hosts[offset::slice_count]
|
||||
|
||||
data = dict()
|
||||
all_group = data.setdefault('all', dict())
|
||||
|
||||
@@ -277,11 +277,11 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
||||
default=False,
|
||||
allows_field='credentials'
|
||||
)
|
||||
job_split_count = models.IntegerField(
|
||||
job_slice_count = models.PositiveIntegerField(
|
||||
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."),
|
||||
default=1,
|
||||
help_text=_("The number of jobs to slice into at runtime. "
|
||||
"Will cause the Job Template to launch a workflow if value is greater than 1."),
|
||||
)
|
||||
|
||||
admin_role = ImplicitRoleField(
|
||||
@@ -302,7 +302,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
||||
@classmethod
|
||||
def _get_unified_job_field_names(cls):
|
||||
return set(f.name for f in JobOptions._meta.fields) | set(
|
||||
['name', 'description', 'schedule', 'survey_passwords', 'labels', 'credentials', 'internal_limit']
|
||||
['name', 'description', 'schedule', 'survey_passwords', 'labels', 'credentials',
|
||||
'job_slice_number', 'job_slice_count']
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -328,13 +329,15 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
||||
return self.create_unified_job(**kwargs)
|
||||
|
||||
def create_unified_job(self, **kwargs):
|
||||
prevent_splitting = kwargs.pop('_prevent_splitting', False)
|
||||
split_event = bool(self.job_split_count > 1 and (not prevent_splitting))
|
||||
prevent_splitting = kwargs.pop('_prevent_slicing', False)
|
||||
split_event = bool(self.job_slice_count > 1 and (not prevent_splitting))
|
||||
if split_event:
|
||||
# A Split Job Template will generate a WorkflowJob rather than a Job
|
||||
from awx.main.models.workflow import WorkflowJobTemplate, WorkflowJobNode
|
||||
kwargs['_unified_job_class'] = WorkflowJobTemplate._get_unified_job_class()
|
||||
kwargs['_parent_field_name'] = "job_template"
|
||||
kwargs.setdefault('_eager_fields', {})
|
||||
kwargs['_eager_fields']['is_sliced_job'] = True
|
||||
job = super(JobTemplate, self).create_unified_job(**kwargs)
|
||||
if split_event:
|
||||
try:
|
||||
@@ -342,11 +345,11 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
||||
except JobLaunchConfig.DoesNotExist:
|
||||
wj_config = JobLaunchConfig()
|
||||
actual_inventory = wj_config.inventory if wj_config.inventory else self.inventory
|
||||
for idx in xrange(min(self.job_split_count,
|
||||
for idx in xrange(min(self.job_slice_count,
|
||||
actual_inventory.hosts.count())):
|
||||
create_kwargs = dict(workflow_job=job,
|
||||
unified_job_template=self,
|
||||
ancestor_artifacts=dict(job_split=idx))
|
||||
ancestor_artifacts=dict(job_split=idx + 1))
|
||||
WorkflowJobNode.objects.create(**create_kwargs)
|
||||
return job
|
||||
|
||||
@@ -531,10 +534,17 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
||||
on_delete=models.SET_NULL,
|
||||
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,
|
||||
job_slice_number = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
default=0,
|
||||
help_text=_("If part of a sliced job, the ID of the inventory slice operated on. "
|
||||
"If not part of sliced job, parameter is not used."),
|
||||
)
|
||||
job_slice_count = models.PositiveIntegerField(
|
||||
blank=True,
|
||||
default=1,
|
||||
help_text=_("If ran as part of sliced jobs, the total number of slices. "
|
||||
"If 1, job is not part of a sliced job."),
|
||||
)
|
||||
|
||||
|
||||
@@ -580,10 +590,11 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
||||
return JobEvent
|
||||
|
||||
def copy_unified_job(self, **new_prompts):
|
||||
new_prompts['_prevent_splitting'] = 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
|
||||
# Needed for job slice relaunch consistency, do no re-spawn workflow job
|
||||
# target same slice as original job
|
||||
new_prompts['_prevent_slicing'] = True
|
||||
new_prompts.setdefault('_eager_fields', {})
|
||||
new_prompts['_eager_fields']['job_slice_number'] = self.job_slice_number
|
||||
return super(Job, self).copy_unified_job(**new_prompts)
|
||||
|
||||
@property
|
||||
|
||||
@@ -219,11 +219,13 @@ class WorkflowJobNode(WorkflowNodeBase):
|
||||
data.update(accepted_fields)
|
||||
# build ancestor artifacts, save them to node model for later
|
||||
aa_dict = {}
|
||||
is_root_node = True
|
||||
for parent_node in self.get_parent_nodes():
|
||||
is_root_node = False
|
||||
aa_dict.update(parent_node.ancestor_artifacts)
|
||||
if parent_node.job and hasattr(parent_node.job, 'artifacts'):
|
||||
aa_dict.update(parent_node.job.artifacts)
|
||||
if aa_dict:
|
||||
if aa_dict and not is_root_node:
|
||||
self.ancestor_artifacts = aa_dict
|
||||
self.save(update_fields=['ancestor_artifacts'])
|
||||
# process password list
|
||||
@@ -252,18 +254,12 @@ class WorkflowJobNode(WorkflowNodeBase):
|
||||
# ensure that unified jobs created by WorkflowJobs are marked
|
||||
data['_eager_fields'] = {'launch_type': 'workflow'}
|
||||
# Extra processing in the case that this is a split job
|
||||
if 'job_split' in self.ancestor_artifacts:
|
||||
if 'job_split' in self.ancestor_artifacts and is_root_node:
|
||||
split_str = six.text_type(self.ancestor_artifacts['job_split'] + 1)
|
||||
data['_eager_fields']['name'] = six.text_type("{} - {}").format(
|
||||
self.unified_job_template.name[:512 - len(split_str) - len(' - ')],
|
||||
split_str
|
||||
)
|
||||
data['_eager_fields']['allow_simultaneous'] = True
|
||||
data['_eager_fields']['internal_limit'] = 'split{0}of{1}'.format(
|
||||
self.ancestor_artifacts['job_split'],
|
||||
self.workflow_job.workflow_job_nodes.count()
|
||||
)
|
||||
data['_prevent_splitting'] = True
|
||||
data['_eager_fields']['job_slice_number'] = self.ancestor_artifacts['job_split']
|
||||
data['_eager_fields']['job_slice_count'] = self.workflow_job.workflow_job_nodes.count()
|
||||
data['_prevent_slicing'] = True
|
||||
return data
|
||||
|
||||
|
||||
@@ -459,11 +455,16 @@ class WorkflowJob(UnifiedJob, WorkflowJobOptions, SurveyJobMixin, JobNotificatio
|
||||
)
|
||||
job_template = models.ForeignKey(
|
||||
'JobTemplate',
|
||||
related_name='split_jobs',
|
||||
related_name='slice_workflow_jobs',
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
on_delete=models.SET_NULL,
|
||||
help_text=_("If automatically created for a sliced job run, the job template "
|
||||
"the workflow job was created from."),
|
||||
)
|
||||
is_sliced_job = models.BooleanField(
|
||||
default=False
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user