mirror of
https://github.com/ZwareBear/awx.git
synced 2026-05-13 01:38:37 -05:00
Implement WFJT prompting for limit & scm_branch
add feature to UI and awxkit restructure some details of create_unified_job for workflows to allow use of char_prompts hidden field avoid conflict with sliced jobs in char_prompts copy logic update developer docs update migration reference bump migration
This commit is contained in:
@@ -19,9 +19,14 @@ from functools import reduce, wraps
|
||||
from decimal import Decimal
|
||||
|
||||
# Django
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.functional import cached_property
|
||||
from django.db.models.fields.related import ForeignObjectRel, ManyToManyField
|
||||
from django.db.models.fields.related_descriptors import (
|
||||
ForwardManyToOneDescriptor,
|
||||
ManyToManyDescriptor
|
||||
)
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.models import Q
|
||||
|
||||
@@ -42,7 +47,7 @@ __all__ = ['get_object_or_400', 'camelcase_to_underscore', 'underscore_to_camelc
|
||||
'get_current_apps', 'set_current_apps',
|
||||
'extract_ansible_vars', 'get_search_fields', 'get_system_task_capacity', 'get_cpu_capacity', 'get_mem_capacity',
|
||||
'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict',
|
||||
'model_instance_diff', 'parse_yaml_or_json', 'RequireDebugTrueOrTest',
|
||||
'NullablePromptPsuedoField', 'model_instance_diff', 'parse_yaml_or_json', 'RequireDebugTrueOrTest',
|
||||
'has_model_field_prefetched', 'set_environ', 'IllegalArgumentError', 'get_custom_venv_choices', 'get_external_account',
|
||||
'task_manager_bulk_reschedule', 'schedule_task_manager', 'classproperty', 'create_temporary_fifo']
|
||||
|
||||
@@ -435,6 +440,39 @@ def model_to_dict(obj, serializer_mapping=None):
|
||||
return attr_d
|
||||
|
||||
|
||||
class CharPromptDescriptor:
|
||||
"""Class used for identifying nullable launch config fields from class
|
||||
ex. Schedule.limit
|
||||
"""
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
|
||||
|
||||
class NullablePromptPsuedoField:
|
||||
"""
|
||||
Interface for psuedo-property stored in `char_prompts` dict
|
||||
Used in LaunchTimeConfig and submodels, defined here to avoid circular imports
|
||||
"""
|
||||
def __init__(self, field_name):
|
||||
self.field_name = field_name
|
||||
|
||||
@cached_property
|
||||
def field_descriptor(self):
|
||||
return CharPromptDescriptor(self)
|
||||
|
||||
def __get__(self, instance, type=None):
|
||||
if instance is None:
|
||||
# for inspection on class itself
|
||||
return self.field_descriptor
|
||||
return instance.char_prompts.get(self.field_name, None)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if value in (None, {}):
|
||||
instance.char_prompts.pop(self.field_name, None)
|
||||
else:
|
||||
instance.char_prompts[self.field_name] = value
|
||||
|
||||
|
||||
def copy_model_by_class(obj1, Class2, fields, kwargs):
|
||||
'''
|
||||
Creates a new unsaved object of type Class2 using the fields from obj1
|
||||
@@ -442,9 +480,10 @@ def copy_model_by_class(obj1, Class2, fields, kwargs):
|
||||
'''
|
||||
create_kwargs = {}
|
||||
for field_name in fields:
|
||||
# Foreign keys can be specified as field_name or field_name_id.
|
||||
id_field_name = '%s_id' % field_name
|
||||
if hasattr(obj1, id_field_name):
|
||||
descriptor = getattr(Class2, field_name)
|
||||
if isinstance(descriptor, ForwardManyToOneDescriptor): # ForeignKey
|
||||
# Foreign keys can be specified as field_name or field_name_id.
|
||||
id_field_name = '%s_id' % field_name
|
||||
if field_name in kwargs:
|
||||
value = kwargs[field_name]
|
||||
elif id_field_name in kwargs:
|
||||
@@ -454,15 +493,29 @@ def copy_model_by_class(obj1, Class2, fields, kwargs):
|
||||
if hasattr(value, 'id'):
|
||||
value = value.id
|
||||
create_kwargs[id_field_name] = value
|
||||
elif isinstance(descriptor, CharPromptDescriptor):
|
||||
# difficult case of copying one launch config to another launch config
|
||||
new_val = None
|
||||
if field_name in kwargs:
|
||||
new_val = kwargs[field_name]
|
||||
elif hasattr(obj1, 'char_prompts'):
|
||||
if field_name in obj1.char_prompts:
|
||||
new_val = obj1.char_prompts[field_name]
|
||||
elif hasattr(obj1, field_name):
|
||||
# extremely rare case where a template spawns a launch config - sliced jobs
|
||||
new_val = getattr(obj1, field_name)
|
||||
if new_val is not None:
|
||||
create_kwargs.setdefault('char_prompts', {})
|
||||
create_kwargs['char_prompts'][field_name] = new_val
|
||||
elif isinstance(descriptor, ManyToManyDescriptor):
|
||||
continue # not coppied in this method
|
||||
elif field_name in kwargs:
|
||||
if field_name == 'extra_vars' and isinstance(kwargs[field_name], dict):
|
||||
create_kwargs[field_name] = json.dumps(kwargs['extra_vars'])
|
||||
elif not isinstance(Class2._meta.get_field(field_name), (ForeignObjectRel, ManyToManyField)):
|
||||
create_kwargs[field_name] = kwargs[field_name]
|
||||
elif hasattr(obj1, field_name):
|
||||
field_obj = obj1._meta.get_field(field_name)
|
||||
if not isinstance(field_obj, ManyToManyField):
|
||||
create_kwargs[field_name] = getattr(obj1, field_name)
|
||||
create_kwargs[field_name] = getattr(obj1, field_name)
|
||||
|
||||
# Apply class-specific extra processing for origination of unified jobs
|
||||
if hasattr(obj1, '_update_unified_job_kwargs') and obj1.__class__ != Class2:
|
||||
@@ -481,7 +534,10 @@ def copy_m2m_relationships(obj1, obj2, fields, kwargs=None):
|
||||
'''
|
||||
for field_name in fields:
|
||||
if hasattr(obj1, field_name):
|
||||
field_obj = obj1._meta.get_field(field_name)
|
||||
try:
|
||||
field_obj = obj1._meta.get_field(field_name)
|
||||
except FieldDoesNotExist:
|
||||
continue
|
||||
if isinstance(field_obj, ManyToManyField):
|
||||
# Many to Many can be specified as field_name
|
||||
src_field_value = getattr(obj1, field_name)
|
||||
|
||||
Reference in New Issue
Block a user