܍~c .ddlZddlZddlZddlZddlZddlZddlmZddl m Z m Z m Z m Z mZddlmZddlmZddlmZmZmZmZmZmZmZddlmZmZddlmZdd lm Z dd l!m"Z"m#Z#m$Z$m%Z%dd l&m'Z'dd l(m)Z)d Z*dgdgddgddgddgdgdggdddgd Z+ej,e-Z.ej/dZ0gdZ1ej2ej3zdzZ4Gdd ej5ej6!Z7d"e8d#e9fd$Z:d"e8fd%Z;dej<fd&Z=d'Z>d(e8d)ee7fd*Z? d/d-Z@d.ZAdS)0N)StringIO)AnyMappingMutableMappingOptionalType)importer)log)net persistencessh_utilsubp temp_utils type_utilsutil)LinuxNetworking Networking)hosts)+ALLOW_EC2_MIRRORS_ON_NON_AWS_INSTANCE_TYPES) activatorseni network_state renderers)parse_net_config_data)Rendererallalpinearchdebianubuntufreebsd dragonflygentoocosnetbsdopenbsd) almalinuxamazoncentos cloudlinux eurolinuxfedoramariner miraclelinux openEuler openmandrivaphotonrhelrocky virtuozzoopensusesles) rrrr!r#r%r&redhatsusez#^[a-z][a-z]-(?:[a-z]+-)+[0-9][a-z]$)chronyzsystemd-timesyncdntpntpdate-cdeZdZUdZdZdZdZdZdZdZ dgZ iZ e e ee effed <d ZeZeeed <d d ddZdZdZdZe ed<dZdedd fdZejdZdZ e!de"ee#j$fdZ%de&fdZ'de&fdZ(dZ)dTdZ*dTd Z+dTd!Z,d"Z-ejdUd#Z.ejd$Z/d%Z0dTd&Z1dUd'Z2dVd)Z3dVd*Z4d+Z5dWde6fd,Z7ejdTd-Z8ejd.Z9d/Z:d0Z;ejdTd1Zd4Z?d5Z@eAd6ZBd7ZCd8ZDe!d9ZEd:ZFd;ZGd<ZHd=ZId>ZJd?ZKd@ZLdAZMdWdBZNdCeOdDe6fdEZPdXdGZQdTdHZRdTdIZSdJZTdKe de fdLZUdMZVde fdNZWdYdPeOdQe dRe fdSZXd S)ZDistroz python3-pipz/usr/libz /etc/hostsz"/etc/sudoers.d/90-cloud-init-usersz /etc/hostnamez/usr/share/zoneinfoz root:rootservicerenderer_configsNnetworking_clsz-Hz-Pz-r)haltpoweroffrebootFz/etc/resolv.confosfamilycb||_||_||_||_dSN)_paths_cfgnamerA networking)selfrKcfgpathss Bi H   rRctj|jdd} tj|S#tj$rYdSwxYw)z=Return the configured network activator for this environment.)networkrNpriority)rget_cfg_by_pathrJrselect_activatorNoActivatorException)rMres rPnetwork_activatorzDistro.network_activators^' I0$   .AAA A.   44 s2AActj|jdd}tj|\}}t d||||j|}|S)N)rcrrdz-Selected renderer '%s' from priority list: %s)config) rrfrJrselectLOGdebugr@get)rMrerK render_clsrenderers rP _get_rendererzDistro._get_renderers' I/  %+X>>>j ;T8   :T%:%>%>t%D%DEEErRrqc0||dSrH)render_network_state)rMrrqs rP_write_network_statezDistro._write_network_states%%m44444rRctj|jt |}tj|st d|d||S)NzInvalid timezone z, no file found at )ospathjoin tz_zone_dirstrisfileIOError)rMtztz_files rP _find_tz_filezDistro._find_tz_files_',,t/R99w~~g&& '>@bb''J rRc8|j||SrH)rJro)rMopt_namedefaults rP get_optionzDistro.get_optionsy}}Xw///rRc||j|<dSrH)rJ)rMrvalues rP set_optionzDistro.set_options# (rRc|||}|||j||dSrH)_select_hostname_write_hostnamehostname_conf_fn_apply_hostname)rMhostnamefqdnwriteable_hostnames rP set_hostnamezDistro.set_hostnamesM!228TBB /1FGGG /00000rRctS)z?Wrapper to report whether this distro uses systemd or sysvinit.) uses_systemdrMs rPrzDistro.uses_systemds ~~rRctrHrY)rMcommandargspkgss rPpackage_commandzDistro.package_commandr]rRctrHrYrs rPupdate_package_sourceszDistro.update_package_sourcesr]rRcDtjd}|dvrdS|S)N)i386i486i586i686r)rwuname)rMrs rPget_primary_archzDistro.get_primary_archs'xzz!} 3 3 36 rRcz|dg}|s|}t||S)Npackage_mirrors)rr_get_arch_package_mirror_info)rMr mirror_infos rPrz$Distro._get_arch_package_mirror_infos?oo&7<<  +((**D,[$???rRcN||}t||S)N) data_sourcer)r_get_package_mirror_info)rMrr arch_infos rPget_package_mirror_infozDistro.get_package_mirror_infos366t<< '#    rRTc^||}|r||SdS)r_F)ra_bring_up_interfaces)rMr`bring_up dev_namess rP apply_networkzDistro.apply_networks:''11   8,,Y77 7urRc|j}td|dd|zdg}t j|}t j||d}|||S)r_ziapply_network_config is not currently implemented for distribution '%s'. Attempting to use apply_network z-# Converted from network_config for distro %sz4# Implementation of _write_network_config is needed.T)headerrender_hwaddressr) __class__rmwarningryrrrnetwork_state_to_enir)rM netconfigrdistrornscontentss rP"_apply_network_from_network_configz)Distro._apply_network_from_network_configs  F    ?&HF     0 ; ;+ v   !!(X!>>>rRc(tjSrH)r generate_fallback_configrs rPrzDistro.generate_fallback_configs+---rRc |}n'#t$r|||cYSwxYwt||}||||rUt d|j}|st ddS| |nt ddS)aApply the network config. If bring_up is True, attempt to bring up the passed in devices. If devices is None, attempt to bring up devices returned by _write_network_config. Returns True if any devices failed to come up, otherwise False. r)rqz/Bringing up newly configured network interfacesz>No network activator found, not bringing up network interfacesTz3Not bringing up newly configured network interfacesF) rrrZrrrurmrnrirbring_up_all_interfaces)rMrrrqrris rPapply_network_configzDistro.apply_network_configs  ))++HH"   ::H;     .i(KKK  !!-:::  M IIG H H H $ 6 $  )t  5 5m D D D D IIK L L Lus !;;ctrHrY)rMlocaleout_fns rP apply_localezDistro.apply_localer]rRctrHrY)rMr~s rP set_timezonezDistro.set_timezone"r]rRcdS)Nz 127.0.0.1rs rP_get_localhost_ipzDistro._get_localhost_ip&s{rRctrHrYrs rP get_localezDistro.get_locale)s!###rRctrHrY)rMfilenamers rP_read_hostnamezDistro._read_hostname,r]rRctrHrY)rMrrs rPrzDistro._write_hostname0r]rRctrHrYrs rP_read_system_hostnamezDistro._read_system_hostname4r]rRctd| tjd|gdS#tj$rt jtd|YdSwxYw)Nz2Non-persistently setting the system hostname to %srz;Failed to non-persistently adjust the system hostname to %s)rmrnrProcessExecutionErrorrlogexc)rMrs rPrzDistro._apply_hostname8s @(     Iz8, - - - - -)    KM       s5*A#"A#cVtj|jd|jr|r|S|s|S|S)Nprefer_fqdn_over_hostname)rget_cfg_option_boolrJ prefer_fqdn)rMrrs rPrzDistro._select_hostnameIsI  $ 68H      K KrRcg}|D]M}|tvr"td||t|N|S)Nz&No distributions found for osfamily {}) OSFAMILIES ValueErrorformatextend) family_listdistrosfamilys rPexpand_osfamilyzDistro.expand_osfamilyWse! / /FZ'' <CCFKK NN:f- . . . .rRc|}|||}|r5tj|r||}nd}|\}}g}|r||kr|||r ||kr||kr|||r&|r$||krtd||dStd|D}t d|t||D]E} | || #t$rtjtd|| YBwxYw||vr||dSdS)Nz6%s differs from %s, assuming user maintained hostname.cg|]}||Srr).0fs rP z*Distro.update_hostname..s999!q9A999rRz/Attempting to update hostname to %s in %s filesz!Failed to write hostname %s to %s)rrwrxexistsrrappendrminfosetrnlenrr}rrr) rMrrprev_hostname_fnapplying_hostname prev_hostnamesys_fn sys_hostname update_filesfns rPupdate_hostnamezDistro.update_hostnamebs$((488  !/? @ @ ! //0@AAMM M"&!;!;!=!=  2  9 9    0 1 1 1 ( M ) )lh.F.F    ' ' '  M lm.K.K HHH     F 99|999::  =         B $$Xr2222    >$- ( ( 4 !>!>??BB$$B%7333F))++LL**   7 LL4 2 2 2KKK" , ,! " u::??!&qJu::??$)!""IM)jD.@.@=00&+  7"9oo ""D(#3444x((((77E5zzQ XuQx8888Uq$ X66666  LzzH 2v0111 NN6R= ) ) ) ODM8+<+<+>+>U K K K K K K  L LrRcP|jstt|_|jS)z7Allow distro to determine the preferred ntp client list)_preferred_ntp_clientsrPREFERRED_NTP_CLIENTSrs rPpreferred_ntp_clientszDistro.preferred_ntp_clientss** F*./D*E*ED '**rRct)r_rY)rM device_names rP_bring_up_interfacezDistro._bring_up_interfaces!!rRcXd}|D]}||s|dz }|dkrdSdS)r_rrETF)r)rM device_names am_failedds rPrzDistro._bring_up_interfacessI   A++A.. Q >>4urRc,|dS)N default_user)rrs rPget_default_userzDistro.get_default_users~...rRc Ftj|rtd|dSd|vr|d}nd}d|g}d|g}tjr*|d|dddd d d d d dddd }dddd}dg}|d} | rt| tr| d} t| trt d|d| D} d | |d<|d} | r| | |rL| rJ| D]G} tj| s1|| td| |Hd|vrt|d|d<t%|D]\} } | |vrt| rrt| tr]||| | g| |vr||| dg_||| | g}| |vr8| r6||| ||| |ds|d r+|d!|d!n*|d"|d"td#| t+j||$dS#t,$r"}tjtd%||d}~wwxYw)&z Add a user to the system using standard GNU tools This should be overriden on distros where useradd is not desirable or not available. z!User %s already exists, skipping.N create_groupsTuseradd --extrausersz --commentz--homez--gidz--uidz--groupsz --passwordz--shellz --expiredatez --inactivez--selinux-user) gecoshomedir primary_groupuidgroupspasswdshell expiredateinactive selinux_userz--no-user-groupz--systemz --no-log-init) no_user_groupsystem no_log_initr(r',zDEPRECATED: The user %s has a 'groups' config value of type dict which is deprecated and will be removed in a future version of cloud-init. Use a comma-delimited string or array instead: group1,group2.c6g|]}|Srstrip)rgs rPrz#Distro.add_user..)s 000Aaggii000rRr%z created group '%s' for user '%s'r&REDACTEDno_create_homer.z-Mz-mzAdding user %s logstringzFailed to create user %s)ris_userrmrpopsystem_is_snappyrro isinstancer{splitdictrryis_group create_grouprnkeyssorteditemsrr Exceptionr)rMrKkwargsr  useradd_cmdlog_useradd_cmd useradd_opts useradd_flags redact_optsr'r%groupkeyvales rPadd_userzDistro.add_users <    HH8$ ? ? ? F f $ $"JJ77MM M $' $d+  " " 3   ~ . . .  " "> 2 2 2 !$ "($,   / *   j H%%  -&#&& +c**&$''  ? 10000F #xx//F8 "JJ77M - m,,,  OV O O O}U++O%%e,,,II@%NNN FKKMM ! !u ..F5Mv||~~.. ; ;HCl""s"z#s/C/C"""L$5s#;<<<+%%#**L,=z+JKKKK#**L,=s+CDDDD %%#%""=#5666&&}S'9::: ::& ' ' )6::h+?+? )   t $ $ $  " "4 ( ( ( (   t $ $ $  " "4 ( ( ( "D)))  Ik_ = = = = = =    K7 > > >G sM44 N >NN c |d}|dd}gd}|r|d||td| t j||d\}}td ||t j|}|d d } n/#t$r"} t jtd || d } ~ wwxYw| S) zD Add a snappy user to the system using snappy tools snapuserknownF)snapz create-userz--sudoerz--jsonz--knownzAdding snap user %sT)r8capturez snap create-user returned: %s:%susernameNzFailed to create snap user %s) rorrmrnrr load_jsonrDr) rMrKrErQrRcreate_user_cmdouterrjobjrUrNs rP add_snap_userzDistro.add_snap_user\s ::j)) 7E**GGG  .  " "9 - - -x((( '... ?DJS# II8#s C C C>#&&Dxx D11HH    Krvaluestuplertyper setup_user_keysDISABLE_USER_OPTSreplace)rMrKrErA cloud_keys redirect_userdisable_options rP create_userzDistro.create_userws*   %4%d55f55 5  d%%f%%% & ( (V4G-H ( OOD&)<"= > > > f $ $)@ $ OOD&"9$O G G G ::mT * * #   T " " " V  f~ %%dF6N;;;;5(( +  !F * */0D$$$ +vD$'' +DKKMM**!$c(:;; +KK1T  DDt99?D  $SYY 5 5 5 & ( ($;R@@J  L./ !'': ; !)!;!/!7!7!O!O!/!7!7!N!N( OOT>trRc>dd|gdd|gf} td|D}n3#t$r&}td|dd|Dd |d }~wwxYw tj|d S#t$r"}t jtd ||d }~wwxYw) zL Lock the password of a user, i.e., disable password logins r(z-lusermodz--lockc3NK|] }tj|d|V!dS)rN)rwhich)rtools rP z%Distro.lock_passwd..s5JJdja6I6IJtJJJJJJrRzUnable to lock user account 'z '. No tools available. Tried: cg|] }|d S)rr)rcs rPrz&Distro.lock_passwd..s(B(B(B!1(B(B(BrR.Nz&Failed to disable password for user %s)next StopIteration RuntimeErrorrrDrrrm)rMrK lock_toolscmdrNs rPr`zDistro.lock_passwds  t,y(D.IJ  JJ JJJJJCC   ,"&$$(B(Bz(B(B(B(B(BD     IcNNNNN    KEt L L LG s,& A!AAA00 B:BBc tjdd|gdS#t$r"}tjtd||d}~wwxYw)Nr(z--expirezFailed to set 'expire' for %s)rrDrrrm)rMuserrNs rP expire_passwdzDistro.expire_passwds]  IxT2 3 3 3 3 3    K A*A%%A*plist_inr_cdd|Ddz}dg|rdgngz}tj||dS)Nrc3JK|]\}}d||gVdS)rNry)rrKpasswords rPrwz"Distro.chpasswd..s7KKh4*++KKKKKKrRrr)ryr)rMrr_payloadrs rPrzDistro.chpasswdsd IIKK(KKK     l6tffB7 #wrR /etc/sudoerscd}d}tj|rtj|}d}d}|D]}|}tjd|}|s.| d}|sXtj |}||krd}n|s |sIddtj dd |zdg} d | }tj ||d nFdtj dd |zdg} d | }tj||td ||n/#t"$r"} tjtd || d} ~ wwxYwtj|ddS)NrFTz^[#|@]includedir\s+(.*)$rEz?# See sudoers(5) for more information on "#include" directives:rrz#includedir %sr zAdded '#includedir %s' to %szFailed to write %si)rwrxrrr splitlinesr3researchrKabspathrryr append_filermrnr}r ensure_dir) rMrx sudo_basesudoers_contents base_exists found_includeline include_match included_dirlinesrNs rPensure_sudo_dirzDistro.ensure_sudo_dirs 7>>) $ $ #~i88 K $//11  D::<?? ?))E""4 RW__Y77888w~~i((  ""H  499X+>+>FFFFF    C!BINNN   G44444    C!CYOOO s0$)E E;E66E;?F G F==Gc~d|g}tjr|d|sg}tj|rtd|n\ t j|td|n+#t$rtj td|YnwxYwt|dkrk|D]j}tj |std||3t jdd d ||gtd ||idSdS) Ngroupaddr"z(Skipping creation of existing group '%s'zCreated new group %szFailed to create group %srzCUnable to add group member '%s' to group '%s'; user does not exist.rsz-az-GzAdded user '%s' to group '%s') rr;rr?rmrrrrDrrr9)rMrKmembers group_add_cmdmembers rPr@zDistro.create_group^sx#T*  " " 1   0 0 0 G =   D KKBD I I I I D -(((/6666 D D D C!? ~~E *   )"'%%*   %   ! KK  s* A AA actionc *|j}|sd|vrdg}d|gd|gd|gd|gd|gd|gd|gd |gd }n|dg|dg|dg|dg|dg|dg|dg|d gd }t|t||z}tj|d S) z Perform the requested action on a service. This handles the common 'systemctl' and 'service' cases and may be overridden in subclasses as necessary. May raise ProcessExecutionError systemctlstopstartenabledisablerestartzreload-or-restartzreload-or-try-restartstatus)rrrrrreloadz try-reloadrT)rT)init_cmdrrr)rMrr?rcmdsrs rPmanage_servicezDistro.manage_services=      +"9"9#}H)!7+#W-%w/%w/.86@#W-  DD!&)!7+"G,#V,#Y/"I.& 2"H-  D8nntDL111yd++++rRc~|rtjdd||||gdSt)N localectlzset-x11-keymap)rrrZ)rMlayoutmodelvariantres rP set_keymapzDistro.set_keymapsY      ( I$      &'' 'rRctjd}tj|ds|Stj|jddS)NT) needs_exenoexecz cloud-initclouddir)rget_tmp_ancestorr has_mount_optrwrxry usr_lib_exec)rMtmp_dirs rPget_tmp_exec_pathzDistro.get_tmp_exec_pathsJ-===!'844 Nw||D-|ZHHHrRrrrcwdc v|rd|dnd}tjdd|d|dzd|zgfi|S) a` Perform a command as the requested user. Behaves like subp() Note: We pass `PATH` to the user env by using `env`. This could be probably simplified after bionic EOL by using `su --whitelist-environment=PATH ...`, more info on: https://lore.kernel.org/all/20180815110445.4qefy5zx5gfgbqly@ws.net.home/T/ zcd z && rsur<z-czenv PATH=$PATH r)rry)rMrrrrE directorys rPdo_asz Distro.do_assh(+2O#OOOO y--0A0AA        rRrH)NN)T)F)r)r)Y__name__ __module__ __qualname__pip_package_namerrrrrz default_ownerrr@rr{rr__annotations__rrrArrr_ci_pkl_versionrresolve_conf_fnrQrrWabcabstractmethodr\rapropertyrrNetworkActivatorrirrrrurrrrrrrrrrrrrboolrrrrrrrrrr staticmethodrrrrrrrrOr[rqr`rrfrrrrgr@rrrrrrrRrPr>r>Ssf$LH8M&'KM{H?Agc>#s(#;;<AAA!'6ND$666%)ddKKOK(OMMM<<< 4 4 4 4 4 4 $$$   8D1L,M#NX x    5H55550000$$$$1111  $$$$ $$$ @@@@         ????(...######J $$$$ $$$$$$ $$$$ $$$ $$$"   \?4?4?4B)L)L)LV++X+"""///sssj6YYYv&$  t    /%/%/%/%b####JHHHH@ ,S ,3 , , , ,D ( ( (I3IIII   T  3      rRr>) metaclassurltransformationscV tj|}n#t$rYdSwxYw|j}|dS|D]}||}||cS|}|jd||j}tj||S)a Apply transformations to a URL's hostname, return transformed URL. This is a separate function because unwrapping and rewrapping only the hostname portion of a URL is complex. :param url: The URL to operate on. :param transformations: A list of ``(str) -> Optional[str]`` functions, which will be applied in order to the hostname portion of the URL. If any function (regardless of ordering) returns None, ``url`` will be returned without any modification. :return: A string whose value is ``url`` with the hostname ``transformations`` applied, or ``None`` if ``url`` is unparseable. Nz{}:{})netloc) urllibparseurlsplitrrportr urlunsplit_replace)rrparts new_hostnametransformation new_netlocs rP&_apply_hostname_transformations_to_urlrs& %%c** tt>Lt)%~l33  JJJ J z^^J ;; < " "5>>>#D#D E EEs " 00cPtdzddfddg}t||S)aH Given a mirror URL, replace or remove any invalid URI characters. This performs the following actions on the URL's hostname: * Checks if it is an IP address, returning the URL immediately if it is * Converts it to its IDN form (see below for details) * Replaces any non-Letters/Digits/Hyphen (LDH) characters in it with hyphens * Removes any leading/trailing hyphens from each domain name label Before we replace any invalid domain name characters, we first need to ensure that any valid non-ASCII characters in the hostname will not be replaced, by ensuring the hostname is in its Internationalized domain name (IDN) representation (see RFC 5890). This conversion has to be applied to the whole hostname (rather than just the substitution variables), because the Punycode algorithm used by IDNA transcodes each part of the hostname as a whole string (rather than encoding individual characters). It cannot be applied to the whole URL, because (a) the Punycode algorithm expects to operate on domain names so doesn't output a valid URL, and (b) non-ASCII characters in non-hostname parts of the URL aren't encoded via Punycode. To put this in RFC 5890's terminology: before we remove or replace any characters from our domain name (which we do to ensure that each label is a valid LDH Label), we first ensure each label is in its A-label form. (Note that Python's builtin idna encoding is actually IDNA2003, not IDNA2008. This changes the specifics of how some characters are encoded to ASCII, but doesn't affect the logic here.) :param url: The URL to operate on. :return: A sanitized version of the URL, which will have been IDNA encoded if necessary, or ``None`` if the generated string is not a parseable URL. rzc2tj|rdn|SrH)r is_ip_addressrs rPz&_sanitize_mirror_url..0s!28!.3s 0077@@rRcFdfd|DS)Nrc3(K|] }|vr|ndV dSr<Nr)rryacceptable_charss rPrwz9_sanitize_mirror_url....5sC! ! 45&&&AAC! ! ! ! ! ! rRr)rrs rPrz&_sanitize_mirror_url..5s;! ! ! ! 9A! ! !   rRcfdd|dDS)Nrzc3@K|]}|dVdSrr2)rparts rPrwz9_sanitize_mirror_url....9s=" " $DJJsOO" " " " " " rR)ryr=rs rPrz&_sanitize_mirror_url..9s<" " (0s(;(;" " "   rR)LDH_ASCII_CHARSr)rrrs @rP_sanitize_mirror_urlr sZL', KJ A@      O" 2# G GGrRc|si}i}|rb|jr[|j|d<t|jr2|jdd}tr d|z|d<n|jdkrd|z|d<|r|jr |j|d<i}|diD] \}}|||< |d iD][\}}g} |D]?} | |z}n#t$rYwxYwt|}|| |@|| } | r| ||<\t d ||S) Navailability_zonerz%s ec2_regionec2regionfailsaferzfiltered distro mirror info: %s) r  _EC2_AZ_REmatchr platform_typerrorCKeyErrorr rrmrn) rr mirror_filtersubstr resultsrKmirror searchlistmirrorstmplfounds rPrrAs  E 8{4 8%0%B!"   K9 : : 8$6qt>KKK   G NrRrKrTctj|dtgdg\}}|std|d|dtj|d}t |d}|S)Nrr>z!No distribution found for distro z (searched )r)r find_moduler ImportError import_modulegetattr)rKlocs looked_locsmodclss rPfetchr*sy ,TB>H:NND+  ktt[[[ "     a ) )C #x C JrR /etc/timezone/etc/localtimec~tj|t|dz|r|rtj|}|stj|s,|rtj|t j ||ntj ||dS)Nr) rrr{rstriprwrxislinkrdel_filesymlinkcopy)r~rtz_conftz_localr/s rPset_etc_timezoner5s OGSWW^^--4555)G)))  )11 ) ( h''' Jw ) ) ) ) Igx ( ( ( FrRc tjd}tj|jS#t $rYdSwxYw)Nz/run/systemd/systemF)rwlstatstatS_ISDIRst_moderD)ress rPrrsKh,--|CK((( uus ,/ ==)Nr+r,)Brrwrr8string urllib.parseriortypingrrrrr cloudinitr r loggingr r r rrrrcloudinit.distros.networkingrrcloudinit.distros.parsersrcloudinit.featuresr cloudinit.netrrrrcloudinit.net.network_statercloudinit.net.rendererr ALL_DISTROSr getLoggerrrmcompilerr ascii_lettersdigitsrCloudInitPickleMixinABCMetar>r{rrr search_for_mirrorrrr*r5rrrRrPrPs9 ??????????????$$$$$$EDDDDDDD++++++JJJJJJCCCCCCCCCCCC======++++++ j H";'j{  1 6g!!RZ= > > JII&6<E E E E E [ -E E E E P(F(Fd(F(F(F(FV8Hc8H8H8H8Hx"1G0000f     V    9I    "rR