o nnh@sddlmZddlZddlZddlZddlZddlZddlZddlZddl m Z m Z m Z m Z mZddlmZddlmZddlmZddlmZmZmZddlmZdd lmZdd lmZmZdd l m!Z"dd l#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)dd l*m+Z+ddl,m-Z-m.Z.m/Z/ddl0m1Z1ddl2m3Z3m4Z4m5Z5ddl6m7Z7m8Z8m9Z9m:Z:ddl;mZ>ddl?m@Z@ddlAmBZBddlCmDZDddlEmFZFmGZGmHZHmIZImJZJe@ZKddgZLeMefZNdZOePdZQePdZReSeTdZUd=ddZVddZWd d!ZXd"d#ZYd$d%ZZd&d'Z[d(d)Z\d*d+Z]d,d-Z^d.d/Z_Gd0d1d1eZ`Gd2d3d3eZad4d5Zbe^d6d7ZcGd8d9d9eZdGd:d;d;edZeGd) annotationsN)IteratorSequenceMapping MappingViewMutableMapping)contextmanager)Number) format_exc)TemplateSyntaxErrorUndefinedError SecurityError)FileSystemLoader)NativeEnvironment)ContextStrictUndefined) constants)AnsibleAssertionError AnsibleErrorAnsibleFilterErrorAnsibleLookupErrorAnsibleOptionsErrorAnsibleUndefinedVariable) string_types) to_nativeto_textto_bytes) is_sequence) filter_loader lookup_loader test_loader)AnsibleUndefinedansible_native_concatansible_eval_concatansible_concat)AnsibleJ2Template) AnsibleJ2Vars)Display)listify_lookup_plugin_termsNativeJinjaText)to_unsafe_textwrap_varAnsibleUnsafeTextAnsibleUnsafeBytesNativeJinjaUnsafeTextTemplargenerate_ansible_template_varsz#jinja2:)variable_begin block_begin comment_begin raw_begin) variable_end block_end comment_endraw_endc Cs|dur t|}nt|}z tt|jj}Wnttfy*t|j}Ynwt t dt |t j tj |t |t j |rMtt|ndd}|dur`ttj ||d<nt||d<tj}|jdddd}tt|ttj ||d<|S) N) template_host template_pathtemplate_mtime template_uidtemplate_run_datetemplate_destpathtemplate_fullpathz{{ template_host }}z{{ template_uid }}z{{ template_path }})hostuidfileansible_managed)rpwdgetpwuidosstatst_uidpw_nameKeyError TypeErrorr+unamedatetime fromtimestamppathgetmtimenowr,rabspathCDEFAULT_MANAGED_STRformattimestrftime localtime)rQfullpath dest_pathb_pathr> temp_varsmanaged_default managed_strraD/usr/local/lib/python3.10/dist-packages/ansible/template/__init__.pyr1Ks4   "cCsd|vrZ|j|vrZg}||}d}||D]=}|ddkr)d}||dq|ddkr9d}||dq|rM|ddkrM||ddd q||dqd |}|S) aDouble backslashes within jinja2 expressions A user may enter something like this in a playbook:: debug: msg: "Test Case 1\3; {{ test1_name | regex_replace('^(.*)_name$', '\1')}}" The string inside of the {{ gets interpreted multiple times First by yaml. Then by python. And finally by jinja2 as part of it's variable. Because it is processed by both python and jinja2, the backslash escaped characters get unescaped twice. This means that we'd normally have to use four backslashes to escape that. This is painful for playbook authors as they have to remember different rules for inside vs outside of a jinja2 expression (The backslashes outside of the "{{ }}" only get processed by yaml and python. So they only need to be escaped once). The following code fixes this by automatically performing the extra quoting of backslashes inside of a jinja2 expression. \Fr:r2Tr6stringz\\)variable_start_string preprocesslexappendreplacejoin)data jinja_envnew_datad2in_vartokenrararb_escape_backslashesps     rsc Cs|duri}z|t}Wn ttfyd}Ynw|s|r(|jd i|}n|}|r{|d}|tt|}||dd}|dD]4}d|vrTtd| |dd\}} | }t ||rqt ||t | qFtd|dqF||fS) NF r:,:zhfailed to parse jinja2 override '%s'. Did you use something different from colon as key-value separator?z8Could not find Jinja2 environment setting to override: ''ra) startswithJINJA2_OVERRIDErMAttributeErroroverlayfindlensplitrstriphasattrsetattrast literal_evaldisplaywarning) rm overridesrnhas_override_headerr{eollinepairkeyvalrararb_create_overlays2  rcCs2t|tr|j|j|jfD] }||vrdSq dS)aDetermines if a string looks like a template, by seeing if it contains a jinja2 start delimiter. Does not guarantee that the string is actually a template. This is different than ``is_template`` which is more strict. This method may return ``True`` on a string that is not templatable. Useful when guarding passing a string for templating, but when you want to allow the templating engine to make the final assessment which may result in ``TemplateSyntaxError``. TF) isinstancerblock_start_stringrgcomment_start_string)rmrnmarkerrararbis_possibly_templates rcCsd}d}d}||}t||sdSzC||D]:}|dtvr6|r*|ddkr*d}d}|ddd}q|dtvrR|ddd|krKWdS|rNqWdSqWdSty_YdSw)zThis function attempts to quickly detect whether a value is a jinja2 template. To do so, we look for the first 2 matching jinja2 tokens for start and end delimiters. NTFr:r4_r)rhrriJINJA2_BEGIN_TOKENSr~JINJA2_END_TOKENSr )rmrnfoundstartcommentrprrrararb is_templates2     rcCsVzt|}|d}||dkr|d8}||dks|d|WSty*|YSw)z Counts the number of newlines at the end of a string. This is used during the jinja2 templating to ensure the count matches the input, since some newlines may be thrown away during the templating. r:rt)r} IndexError)in_strijrararb_count_newlines_from_ends   rcCslddlm}t|tr|D]}t||q dSt|tr(|D]}t|qdSt||r4td|dS)Nr Undefinedz{0} is undefined)jinja2.runtimerrrrecursive_check_definedlistrrW)itemrrrrararbrs     rcCst|tpt|tpt|tS)zfHelper method to determine if something is an unrolled generator, iterator, or similar object )rrr RANGE_TYPE)valuerararb _is_rolleds rcfdd}t|S)zWrapper function, that intercepts the result of a templating and auto unrolls a generator, so that users are not required to explicitly use ``|list`` to unroll. cs"|i|}t|rt|S|SN)rrargskwargsretfuncrarbwrapper%sz!_unroll_iterator..wrapper functoolsupdate_wrapperrrrarrb_unroll_iterator s  rcr)zWrapper function, that intercepts the result of a filter and wraps it into NativeJinjaText which is then used in ``ansible_native_concat`` to indicate that it is a text which should not be passed into ``literal_eval``. cs|i|}t|Srr)rrrarbr4sz"_wrap_native_text..wrapperrrrarrb_wrap_native_text.s  rcsjeZdZdZeejjejjejjhZ fddZ fddZ ddZ dd Z fd d Zd d ZZS)AnsibleContexta$ A custom context, which intercepts resolve_or_missing() calls and sets a flag internally if any variable lookup returns an AnsibleUnsafe value. This flag is checked post-templating, and (when set) will result in the final templated result being wrapped in AnsibleUnsafe. cs tt|j|i|d|_dS)NF)superr__init__unsafeselfrr __class__rarbrHs zAnsibleContext.__init__csDt|dd|jvs||jvrt|dtj|g|Ri|S)N __qualname__z is not safely callable)getattr_disallowed_callablesr rcall)robjrrrrarbrLszAnsibleContext.callcCspt|tr|D] }|||rdSq dSt|tr,|D] }||r)dSqdSt|dddur6dSdS)a Our helper function, which will also recursively check dict and list entries due to the fact that they may be repr'd and contain a key or value which contains jinja2 syntax and would otherwise lose the AnsibleUnsafe value. T __UNSAFE__F)rdictkeys _is_unsaferr)rrrrrararbrQs    zAnsibleContext._is_unsafecCs.|dur|js||rd|_dSdSdSdSNT)rr)rrrararb_update_unsafeds  zAnsibleContext._update_unsafecstt||}|||Sr)rrresolve_or_missingr)rrrrrarbrhs z!AnsibleContext.resolve_or_missingcCsF|js|jS|js |jSt|jtr|j|jSt|jfi|jS)aReturn the complete context as a dict including the exported variables. For optimizations reasons this might not return an actual copy so be careful with using it. This is to prevent from running ``AnsibleJ2Vars`` through dict(): ``dict(self.parent, **self.vars)`` In Ansible this means that ALL variables would be templated in the process of re-creating the parent because ``AnsibleJ2Vars`` templates each variable in its ``__getitem__`` method. Instead we re-create the parent via ``AnsibleJ2Vars.add_locals`` that creates a new ``AnsibleJ2Vars`` copy without templating each variable. This will prevent unnecessarily templating unused variables in cases like setting a local variable and passing it to {% include %} in a template. Also see ``AnsibleJ2Template``and https://github.com/pallets/jinja/commit/d67f0fd4cc2a4af08f51f4466150d49da7798729 )varsparentrr& add_localsrrrararbget_allms zAnsibleContext.get_all)__name__ __module__r__doc__ frozensetr- _strip_unsafer.r/rrrrrrr __classcell__rararrbr;s   rcsHeZdZdZfddZddZddZdd Zd d Zd d Z Z S)JinjaPluginInterceptz Simulated dict class that loads Jinja2Plugins at request otherwise all plugins would need to be loaded a priori. NOTE: plugin_loader still loads all 'builtin/legacy' at start so only collection plugins are really at request. cs.tt|j|i|||_||_t|_dSr)rrr _pluginloader _delegateeset_seen_it)r delegatee pluginloaderrrrrarbrs zJinjaPluginIntercept.__init__c Cs2t|ts tdt|d}||jvr[|j|d}z|j|}Wn.tt fy:}z|}WYd}~nd}~wt yR}z t d|t |f|d}~ww|r[|j|j|<z|j|}Wnt y}z|j|td|t |pw|fdd}~ww|jjdkr|tjvrt|}|St|}|S)Nz$key must be a string, got %s insteadz)Unexpected plugin load (%s) exception: %szCould not load "%s": %srfilter)rr ValueErrortyperaddrgetrrL Exceptionrvvvvr j2_functionrremover rUSTRING_TYPE_FILTERSrr)rr original_excpluginerrararb __getitem__s>       z JinjaPluginIntercept.__getitem__cCs|j||Sr)r __setitem__)rrrrararbrsz JinjaPluginIntercept.__setitem__cCstr)NotImplementedError)rrrararb __delitem__sz JinjaPluginIntercept.__delitem__cC t|jSr)iterrrrararb__iter__ zJinjaPluginIntercept.__iter__cCrr)r}rrrararb__len__rzJinjaPluginIntercept.__len__) rrrrrrrrrrrrararrbrs  +rcCsXt|tr|D]}t|q |St|r!|D]}t|q|St|tr*t||S)zsRecursively find an undefined value in a nested data structure and properly raise the undefined exception. )rrvalues_fail_on_undefinedrrstr)rmrrrararbrs      rcCst|dur|SdS)aSA custom finalize function for jinja2, which prevents None from being returned. This avoids a string of ``"None"`` as ``None`` has no importance in YAML. The function is decorated with ``_unroll_iterator`` so that users are not required to explicitly use ``|list`` to unroll a generator. This only affects the scenario where the final result of templating is a generator, e.g. ``range``, ``dict.items()`` and so on. Filters which can produce a generator in the middle of a template are already wrapped with ``_unroll_generator`` in ``JinjaPluginIntercept``. Nrf)r)thingrararb_ansible_finalizes rcs0eZdZdZeZeZee Z fddZ Z S)AnsibleEnvironmentz Our custom environment, which simply allows us to override the class-level values for the Template and Context classes used by jinja2 internally. csDtj|i|t|jt|_t|jt|_d|_t|_ t |_ dSr) rrrfiltersrtestsr trim_blocksr! undefinedrfinalizerrrarbr s  zAnsibleEnvironment.__init__) rrrrr context_classr%template_class staticmethodr#concatrrrararrbrs rcs$eZdZeeZfddZZS)AnsibleNativeEnvironmentcs tj|i|tt|_dSr)rrrrrrrrarbrsz!AnsibleNativeEnvironment.__init__)rrrrr"rrrrararrbrsrc@seZdZdZd&ddZddZefddZd d Ze d d Z e j d d Z e ddZ  d'ddZddZeZd&ddZddZddZd(ddZddZd d!Zd&d"d#Z  d)d$d%ZeZdS)*r0zQ The main class for templating, with the main entry-point of template(). NcCs||_|dur in||_tj|_tjrtnt}||t |r"| ndd|_ ||j j _ |j|j jd<|j|j jd<|j jd<|j|j jd<|j|j jd<d|_||j tj|_dS)N.) extensionsloaderlookupqueryqrSundef)_loader_available_variablesrUDEFAULT_UNDEFINED_VAR_BEHAVIOR_fail_on_undefined_errorsDEFAULT_JINJA2_NATIVErr_get_extensionsr get_basedir environmentrenvironment_class_lookupglobals _query_lookup _now_datetime_make_undefined cur_context_compile_single_var jinja2_native)rr variablesrrararbr%s    zTemplar.__init__cCstd|j|jf|_dS)Nz^%s\s*(\w*)\s*%s$)recompilergvariable_end_string SINGLE_VAR)renvrararbrAszTemplar._compile_single_varc Kst|}|j|jjtt}|j|j||_|tu|_||jd}| D]\}}| ||}z |dur@t |||Wq+t yJYq+w|S)aVCreates a new copy of Templar with a new environment. :kwarg environment_class: Environment class used for creating a new environment. :kwarg \*\*kwargs: Optional arguments for the new environment that override existing environment attributes. :returns: Copy of Templar with updated environment. available_variables searchpathN) object__new____dict__updaterr0rrritemsrrrz) rrrnew_env new_templarmappingrrrrararbcopy_with_new_envDs&      zTemplar.copy_with_new_envcCs"g}tjrtjddd}|S)z Return jinja2 extensions to load. If some extensions are set via jinja_extensions in ansible.cfg, we try to load them with the jinja environment.  rfru)rUDEFAULT_JINJA2_EXTENSIONSrkr~)r jinja_extsrararbrhszTemplar._get_extensionscCs|jSr)r rrararbr"xszTemplar.available_variablescCs$t|ts tdt|||_dS)a  Sets the list of template variables this Templar instance will use to template things, so we don't have to pass them around between internal methods. We also clear the template cache here, as the variables are being changed. z8the type of 'variables' should be a Mapping but was a %sN)rrrrr )rrrararbr"|s  c ks||jjd}i}|D]'\}}|||j}zt||||<|dur+t|||Wqty5YqwdV|D]}|||j}t||||q;dS)a;Context manager used to set temporary templating context, without having to worry about resetting original values afterward Use a keyword that maps to the attr you are setting. Applies to ``self.environment`` by default, to set context on another object, it must be in ``mapping``. r!N)rrr(rrrrz)rrr+originalrrrrararbset_temporary_contexts(   zTemplar.set_temporary_contextFTc  sT|durgn|}| durtjdddt|dr|Sdur!j|r(|}t|trn|s5|Sj |} | rZ| d} | j vrZj | } t| t rS| S| durZt jSj|||d}j|St|rfdd |DSt|tri}|D]}||vrj||d ||<q||||<q|S|S) z Templates (possibly recursively) any given data as input. If convert_bare is set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') before being sent through the template engine. NzjThe `cache` option to `Templar.template` is no longer functional, and will be removed in a future release.2.18versionrr:)preserve_trailing_newlinesescape_backslashesfail_on_undefinedrdisable_lookups convert_datac s g|] }j|dqS)r5r7rr8)template).0vr8r7rr5rrarb s z$Templar.template..r:)r deprecatedrr _convert_bare_variablerrrrmatchgroupr NON_TEMPLATED_TYPESrUDEFAULT_NULL_REPRESENTATION do_templaterrrrrr;)rvariable convert_barer5r6r7rr9 static_varscacher8only_onevar_name resolved_valresultdkrar>rbr;sd             zTemplar.templatecCsxt|tr t||jSt|ttfr!|D] }||rdSqdSt|tr:|D]}||s6|||r9dSq(dS)z#lets us know if data has a templateTF)rrrrrtupler)rrmr=rPrararbrs    zTemplar.is_templatecCst|||j\}}t||Sr)rrr)rrmrr rararbrs zTemplar.is_possibly_templatecCsht|tr2d|v}|dddddd}|s!||jvr2|jj|vr2d|jj||jjfS|S)z Wraps a bare string, which may have an attribute portion (ie. foo.bar) in jinja2 variable braces so that it is evaluated properly. |rr[z%s%s%s)rrr~r rrgr)rrGcontains_filters first_partrararbrA s "zTemplar._convert_bare_variablecOs td|)NzHThe lookup `%s` was found, however lookups were disabled from templating)rrnamerrrararb _fail_lookups zTemplar._fail_lookupcCs:|rtjtjjjdd}ntj}|r||S|S)zUjinja2 global function to return current datetime, potentially formatted via strftimeN)tzinfo)rOrStimezoneutcrkrY)rr[fmtrSrararbrs   zTemplar._now_datetimecOs d|d<|j|g|Ri|S)z( wrapper for lookup, force wantlist trueTwantlist)rrVrararbr)szTemplar._query_lookupc Ostj||j|d}|durtd||dd}|dtj}|dd}t||ddd }z|j|fd |j i|} Wnt t fyN} zt | d} ~ wt y[} z| d} ~ wt y} z*d t| } |d krrt| n|d kr~tj| ddn| |rgndWYd} ~ Sd} ~ wty} z>d|t| t| f} |d krt| n|d krtj| ddntdttt| | d|rgndWYd} ~ Sd} ~ wwt| stjd|dt| d|ddd| rc|durc|jrd|j_|rt| Szt| dtrttd| } W| Std| } W| StyQt| t s:td|t!| dkrJt| d} Y| St| } Y| St"ybtd| } Y| Sw| S)N)rtemplarzlookup plugin (%s) not foundr]F allow_unsafeerrorsstrictT)termsr^r7rHrz0Lookup failed but the error is being ignored: %swarnignore)log_onlyzjAn unhandled exception occurred while running the lookup plugin '%s'. Error was a %s, original message: %sz&exception during Jinja2 execution: {0}orig_exczThe lookup plugin 'z&' was expected to return a list, got 'z' instead. The lookup plugin 'zM' needs to be changed to return a list. This will be an error in Ansible 2.18r2r3rruz-The lookup plugin '%s' did not return a list.r:)#rrr rpoprUDEFAULT_ALLOW_UNSAFE_LOOKUPSr(runr rr rrrrrrrrvvvrWr rr@rrr,rr*rlrMrr}rL) rrWrrinstancer]r_r` loop_termsranrmsgrararbr.s         zTemplar._lookupcCs2ddlm}|dust||s|dkrd}t|S)Nrrrfz*Mandatory variable has not been overridden)rrrr!)rhintrrararbrs zTemplar._make_undefinedc Cs|jr t|ts |St|}|dur|j}zt|||j\}} || |r,t|| }z| |} Wn@t t fyO} zt dt | t |f| dd} ~ wtys} zdt | vrft dt || d|WYd} ~ WSd} ~ ww|r|j| jd<| jd<| jd<t|| j} |j} | jj| _|js|st| _| j| dd |_| |j}zWz| |}t|jd d }|rt|}Wn=ty}z1d t |vrd t |}|dt |7}t||dtdt|t dt |t |f|dd}~wwW| |_n| |_wt|tr(|r(t|}||kr(|| j||7}|r(t|}|WSt yO} z|r:t| tdt| |WYd} ~ Sd} ~ wtyp} z|r[tdt| |WYd} ~ Sd} ~ ww)Nz6template error while templating string: %s. String: %srf recursionz.recursive loop detected in template string: %srrrT)sharedrFr!zIUnable to look up a name or access an attribute in template string (%s). zMMake sure your variable name does not contain invalid characters like '-': %sz5failing because of a type error, template data is: %sz5Unexpected templating type error occurred on (%s): %szIgnoring undefined failure: %s)!rrrrr rrrrs from_stringr SyntaxErrorrrrrXrr&rrrr$ new_contextroot_render_funcrr,rMrrdebugrnewline_sequencer )rrmr5r6r7rr8r9 data_newlinesmyenvtrjvarscached_contextrfresrteerrmsg res_newlinesrararbrFs              zTemplar.do_templater) FTTNNTNNF)FN)TTNNFF)rrrrrrrr,rpropertyr"setterrr1r;r templatablerrArXrrrrrF _do_templaterarararbr0 s8  $    O    T g)NN)g __future__rrrOrrHrFrrXcollections.abcrrrrr contextlibrnumbersr tracebackr jinja2.exceptionsr r r jinja2.loadersrjinja2.nativetypesrrrransiblerrUansible.errorsrrrrrransible.module_utils.sixr+ansible.module_utils.common.text.convertersrrr'ansible.module_utils.common.collectionsransible.plugins.loaderrrr ansible.template.native_helpersr!r"r#r$ansible.template.templater%ansible.template.varsr&ansible.utils.displayr'ansible.utils.listifyr(ansible.utils.native_jinjar*ansible.utils.unsafe_proxyr+r,r-r.r/r__all__boolrDryrrrrrangerr1rsrrrrrrrrrrrrrrr0rarararbsl                 %+!&  TN