a
    “I_k  ã                   @   sì   d dl mZmZ d dlZd dlZd dlZd dlmZmZm	Z	m
Z
mZ d dlZd dlmZ d dlmZ d dl mZmZ d dlmZ d dlZd dl mZ d d	lmZ d
d„ ZG dd„ de
ƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZdS )é    )Ú	provisionÚparamN)Úenv_loadparmÚcreate_test_ouÚBlackboxProcessErrorÚBlackboxTestCaseÚconnect_samdb)ÚSamDB)Úsystem_session)ÚLdbÚdn_from_dns_name)Úget_fsmo_roleowner)Úsites)Ú_dsdb_load_udv_v2c                 C   s(   t | tƒ |d}|jdg d¢tjddS )N)Úsession_infoÚlpzCN=Primary Domains)ZobjectClassÚsamAccountNameÚsecretzmsDS-KeyVersionNumberz(objectClass=kerberosSecret))ÚbaseÚattrsÚscopeÚ
expression)r   r
   ÚsearchÚldbÚSCOPE_SUBTREE)Úsecrets_pathr   Zsecrets_ldb© r   ú;/usr/lib/python3/dist-packages/samba/tests/domain_backup.pyÚget_prim_dom!   s    ür   c                       sÖ   e Zd Z‡ fdd„Zdd„ Zdd„ Zdd„ Zd	d
„ Zd2dd„Zdd„ Z	dd„ Z
d3dd„Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd „ Zd4d"d#„Zd$d%„ Zd&d'„ Zd(d)„ Zd*d+„ Zd,d-„ Zd5d.d/„Zd6d0d1„Z‡  ZS )7ÚDomainBackupBasec                    s‚   t t| ƒ ¡  tjd }dtjd tjd f | _td| ƒ| _d| _| 	¡ | _
d | _ddg| _tjd	 | _tjd
 | _d | _d S )NZ	DC_SERVERz-U%s%%%sZDC_USERNAMEZDC_PASSWORDz	ldap://%sZ
BACKUPSERVZsidForRestoreZ
backupDateÚDOMAINÚREALM)Úsuperr   ÚsetUpÚosÚenvironÚ	user_authr   r   Ú
new_serverÚupperÚserverÚbase_cmdÚbackup_markersÚrestore_domainÚrestore_realmÚbackend)Úselfr)   ©Ú	__class__r   r   r#   2   s    

ÿ

zDomainBackupBase.setUpc                 C   s   || _ |  jd| g7  _dS )z8Explicitly set the DB backend that the backup should usez--backend-store=N)r.   r*   )r/   r.   r   r   r   Úuse_backendC   s    zDomainBackupBase.use_backendc                 C   s.   t | ¡ ƒ}d| }||d| d| d| gS )NzCN=Configuration,%szCN=Schema,%szDC=DomainDnsZones,%szDC=ForestDnsZones,%s)ÚstrÚget_default_basedn)r/   ÚsamdbÚbasednZ	config_dnr   r   r   Úget_expected_partitionsH   s    
þz(DomainBackupBase.get_expected_partitionsc                 C   s`   |j dtjdgd}dd„ |d  d¡D ƒ}|  |¡}|D ] }|  ||v d|t|ƒf ¡ q:dS )	z?Asserts all expected partitions are present in the backup samdbÚ ZnamingContexts©r   r   r   c                 S   s   g | ]}t |ƒ‘qS r   ©r3   )Ú.0Úrr   r   r   Ú
<listcomp>S   ó    z>DomainBackupBase.assert_partitions_present.<locals>.<listcomp>r   z%s not in %sN)r   r   Ú
SCOPE_BASEÚgetr7   Ú
assertTruer3   )r/   r5   ÚresZ
actual_ncsÚexpected_ncsÚncr   r   r   Úassert_partitions_presentO   s    
ÿ

ÿz*DomainBackupBase.assert_partitions_presentc                 C   sX   | j  ¡ }|  |¡}|D ]:}d}t||ƒD ]}|t|jƒkr*d} qFq*|  |d¡ qdS )z>Asserts an replUpToDateVector entry exists for the original DCFTz"Couldn't find UDTV for original DCN)r   Zget_invocation_idr7   r   r3   Zsource_dsa_invocation_idrA   )r/   r5   Zorig_invoc_idrC   rD   ÚfoundZcursorr   r   r   Úassert_repl_uptodate_vector[   s    

z,DomainBackupBase.assert_repl_uptodate_vectorNc                 C   sd   d}|j | ¡ tj|d}d}|D ]}|t|jƒv r"d}q"|  |d| ¡ |r`|  t|ƒ|k¡ dS )z=Checks that the expected server is present in the restored DBz*(&(objectClass=Server)(serverReference=*))©r   r   FTzCould not find %s serverN)r   Úget_config_basednr   r   r3   ÚdnrA   Úlen)r/   r5   Zexpected_serverÚexpected_countZsearch_exprrB   Zserver_foundÚmsgr   r   r   Úassert_dcs_presenti   s    
þÿz#DomainBackupBase.assert_dcs_presentc                 C   s8   t j | jd¡}t j |¡s4t  |¡ |  tj|¡ |S )NZtree)	r$   ÚpathÚjoinÚtempdirÚexistsÚmkdirÚ
addCleanupÚshutilÚrmtree)r/   Úextract_dirr   r   r   Úrestore_dirz   s
    
zDomainBackupBase.restore_dirc                 C   s@   |   ¡ }t |¡}| |¡ W d  ƒ n1 s20    Y  dS )z@Untar the backup file's raw contents (i.e. not a proper restore)N)rX   ÚtarfileÚopenZ
extractall)r/   Úbackup_filerW   Ztfr   r   r   Úuntar_backup   s    zDomainBackupBase.untar_backupr   c           
      C   sÐ   |   ¡ }|  |¡ tj |  ¡ d¡}tj |d¡}tƒ }t|tƒ |d}|j	t
 |d¡t
j| jd}|  t|ƒd¡ | jD ]}|  |d  |¡d| ¡ qztj |d	¡}	t|	|ƒ}|  t|ƒ|¡ |  |¡ d
S )z@Creates a backup, untars the raw files, and sanity-checks the DBÚprivatezsam.ldb)Úurlr   r   ú@SAMBA_DSDBr9   é   r   z%s backup marker missingzsecrets.ldbN)Úcreate_backupr\   r$   rO   rP   rX   r   r	   r
   r   r   ÚDnr?   r+   ÚassertEqualrK   ÚassertIsNotNoner@   r   rE   )
r/   Úprimary_domain_secretsr[   Úprivate_dirZ
samdb_pathr   r5   rB   Úmarkerr   r   r   r   Ú_test_backup_untar‡   s&    
þ
ÿ
z#DomainBackupBase._test_backup_untarc                 C   s(   |   ¡ }|  |¡ |  ¡ }|  |¡ dS )z?Does a backup/restore, with specific checks of the resulting DBN©ra   Úrestore_backupÚcheck_restored_smbconfÚcheck_restored_database©r/   r[   r   r   r   r   Ú_test_backup_restore£   s    
z%DomainBackupBase._test_backup_restorec                 C   s2   | j dgd}|  |¡ |  ¡ }| j|dd dS )zADoes a backup/restore with secrets excluded from the resulting DBz--no-secrets)Ú
extra_argsF©Úexpect_secretsNri   rm   r   r   r   Ú_test_backup_restore_no_secretsª   s    
z0DomainBackupBase._test_backup_restore_no_secretsc                 C   s¦   d}t  | j| j ¡ |¡ |  t j| j| j ¡ |¡ |  ¡ }|  |d| g¡ |  ¡ }|  	|¡}d 
|| ¡ ¡}d 
| j¡}|j|tj|d}|  t|ƒdkd¡ dS )	z2Does a backup and restores into a non-default sitezTest-Site-For-Backupsz--site=zCN={0},CN=Sites,{1}z(&(objectClass=server)(cn={0}))rH   r`   z Failed to find new DC under siteN)r   Zcreate_siter   rI   rT   Zdelete_sitera   rj   rk   rl   Úformatr'   r   r   rA   rK   )r/   Zsitenamer[   r   Úrestored_ldbZsite_dnZmatch_serverrB   r   r   r   Ú_test_backup_restore_into_siteµ   s&    
ÿ
ÿ
ÿÿz/DomainBackupBase._test_backup_restore_into_sitec                 C   sb   t jd }tj|d}| ¡ D ]\}}| ||¡ qt j | jd¡}| 	d|¡ |  
t j|¡ |S )z9Creates a very basic smb.conf to pass to the restore toolZSMB_CONF_PATH©Zfilename_for_non_global_lpúsmb.confF)r$   r%   r   ÚLoadParmÚitemsÚsetrO   rP   rQ   ÚdumprT   Úremove)r/   ÚsettingsZtestenv_confZlocal_lpÚkeyÚvalZnew_smbconfr   r   r   Úcreate_smbconfÏ   s    
zDomainBackupBase.create_smbconfc              
   C   s”   |   ¡ }dddddœ}dddœ}| |¡ |  |¡}|  |d	| g¡ |  ¡ }|  |¡ | ¡ D ].\}}|  t| 	|¡ƒ|d
|| 	|¡f ¡ q`dS )z9Checks smb.conf values passed to the restore are retainedz/var/runZFOOBARZNOTMYDOMAINzNOT.MY.REALM)ústate directoryúnetbios nameÚ	workgroupÚrealmZ275Ú7)zdrs: max link synczprefork childrenz--configfile=z'%s' was '%s' in smb.confN)
ra   Úupdater€   rj   rk   rl   ry   rc   r3   r@   )r/   r[   r}   Zassert_settingsÚsmbconfr   r~   r   r   r   r   Ú_test_backup_restore_with_confä   s$    ýÿ


ÿz/DomainBackupBase._test_backup_restore_with_confc                 C   s¬   t j |  ¡ dd¡}tj|d}|  | d¡| j¡ |  | d¡| j	¡ |  | d¡| j
 ¡ ¡ t j |  ¡ d¡}|  | d¡|¡ t j |  ¡ d	¡}|  | d
¡|¡ |S )z=Sanity-check important smb.conf values are restored correctlyÚetcrw   rv   r‚   rƒ   r„   r]   zprivate dirÚstater   )r$   rO   rP   rX   r   rx   rc   r@   r'   r,   r-   r(   )r/   r‡   Úbkp_lprf   Z	state_dirr   r   r   rk   ÿ   s    z'DomainBackupBase.check_restored_smbconfTc           
      C   s  t  || d¡¡}t|j|ƒ}|  t|ƒd¡ |d  d¡}|  |¡ |  t|d ƒ| j	d ¡ |  |d  d¡¡ t
|jtƒ ||  ¡ d}|jt |d¡tj| jd	}|  t|ƒd¡ | jD ]}|  |d  |¡d
| ¡ q¾|j| ¡ tjddgd	}|  t|ƒd¡ |  |d  d¡¡ |  |d  d¡¡ |j| ¡ tjddgd	}|  t|ƒd¡ |  |d  d¡¡ |  |d  d¡¡ | jr¾|jdtjdgd	}t|d  d¡ƒ}	|  |	| j¡ |  |¡ | j|| j	dd |  || j	| j¡ | j||d |  |¡ |S )Nr„   r`   r   r   ú$r   )r^   r   r   Zcredentialsr_   r9   z%s backup-marker left behindZrepsFromZrepsToz
@PARTITIONZbackendStore)rL   rp   )r   Zprovision_paths_from_lpr@   r   Zsecretsrc   rK   rd   r3   r'   r	   r5   r
   Zget_credentialsr   r   rb   r?   r+   ZassertIsNoner4   rI   r.   rE   rN   Úassert_fsmo_rolesr)   Úassert_secretsrG   )
r/   r‹   rq   ÚpathsZbkp_pdZacnr5   rB   rg   r.   r   r   r   rl     sZ    

ÿþ
ÿ
þ
þ
ÿ

z(DomainBackupBase.check_restored_databasec                 C   sF   t | ¡ ƒ}d||f }|r0|  | d|¡¡ n|  t|jd|¡ dS )z8Asserts that a user has/doesn't have secrets as expectedzCN=%s,CN=users,%sZ
unicodePwdN)r3   r4   rd   Z	searchoneÚassertRaisesÚKeyError)r/   r5   Zusernamerq   r6   Zuser_dnr   r   r   Úassert_user_secretsF  s
    z$DomainBackupBase.assert_user_secretsc                 C   s$   g d¢}|D ]}|   |||¡ qdS )z?Check the user secrets in the restored DB match what's expected)ZaliceZbobZjaneN)r’   )r/   r5   rq   Z
test_usersÚuserr   r   r   rŽ   R  s    zDomainBackupBase.assert_secretsc           
      C   s¶   |  ¡ }t| ¡ ƒ}d| d| ¡  t| ¡ ƒd| |d| d| dœ}| ¡ D ]`\}}t|t 	||¡|ƒ}	|  
d |¡|	 ¡ v d||f ¡ |  
d |¡|	 ¡ vd	||f ¡ qPd
S )z2Asserts the expected server is the FSMO role ownerzCN=Infrastructure,zCN=Partitions,%szCN=RID Manager$,CN=System,z$CN=Infrastructure,DC=DomainDnsZones,z$CN=Infrastructure,DC=ForestDnsZones,)ZinfrastructureZnamingZschemaZridZpdcZ	domaindnsZ	forestdnszCN={0},zExpected %s to own FSMO role %sz%s found as FSMO %s role ownerN)Ú	domain_dnr   Zforest_dns_namerI   r3   Zget_schema_basednry   r   r   rb   rA   rs   Zextended_str)
r/   r5   r)   Zexclude_serverr”   Z	forest_dnZfsmosZrolerJ   Úownerr   r   r   r   Z  s(    

ø	
ÿÿ
þz"DomainBackupBase.assert_fsmo_rolesc                 C   s0   t  | j¡D ]}t j | j|¡}t |¡ qd S ©N)r$   ÚlistdirrQ   rO   rP   rU   rV   )r/   ÚfilenameÚfilepathr   r   r   Úcleanup_tempdiro  s    z DomainBackupBase.cleanup_tempdirc              
   C   sp   d  |¡}td| ƒ z|  d| ¡}W n: tyb } z"|  ¡  |  d| ¡ W Y d}~n
d}~0 0 t|ƒ dS )z,Executes a samba-tool backup/restore commandú zExecuting: samba-tool %szsamba-tool zError calling samba-tool: %sN)rP   ÚprintÚcheck_outputr   rš   Zfail)r/   ÚargsÚcmdÚoutÚer   r   r   Úrun_cmdt  s    
$zDomainBackupBase.run_cmdc                 C   sž   | j d| j g }|r||7 }|  |¡ g }t | j¡D ]"}| d¡r8| d¡r8| |¡ q8|  t	|ƒdkdt	|ƒ ¡ tj
 | j|d ¡}|  tj|¡ |S )z?Runs the backup cmd to produce a backup file for the testenv DCú--targetdir=zsamba-backup-z.tar.bz2r`   z"Domain backup created %u tar filesr   )r*   rQ   r¢   r$   r—   Ú
startswithÚendswithÚappendrA   rK   rO   rP   rT   r|   )r/   ro   rž   Z	tar_filesÚfnr[   r   r   r   ra   „  s    

ÿzDomainBackupBase.create_backupc                 C   sp   |   ¡ }dddd| d| d| j g}|r2||7 }|  |¡ |  | j¡ |  | j| j¡ |  | j| j| j¡ dS )z6Restores the samba directory files from a given backupÚdomainÚbackupZrestorez--backup-file=r£   z--newservername=N)rX   r'   r¢   rE   r   rN   r)   r   )r/   r[   ro   rW   rž   r   r   r   rj   ›  s    þ
zDomainBackupBase.restore_backup)N)r   )T)N)N)Ú__name__Ú
__module__Ú__qualname__r#   r2   r7   rE   rG   rN   rX   r\   rh   rn   rr   ru   r€   rˆ   rk   rl   r’   rŽ   r   rš   r¢   ra   rj   Ú__classcell__r   r   r0   r   r   0   s.   


7
r   c                       sD   e Zd Z‡ fdd„Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Z‡  Z	S )ÚDomainBackupOnlinec                    s*   t t| ƒ ¡  dddd| j | jg| _d S )Nr¨   r©   Zonlineú	--server=)r"   r®   r#   r)   r&   r*   ©r/   r0   r   r   r#   ¯  s    ÿzDomainBackupOnline.setUpc                 C   s   |   ¡  d S r–   ©rh   r°   r   r   r   Útest_backup_untarµ  s    z$DomainBackupOnline.test_backup_untarc                 C   s   |   d¡ |  ¡  d S ©NZtdb©r2   rn   r°   r   r   r   Útest_backup_restore¸  s    
z&DomainBackupOnline.test_backup_restorec                 C   s   |   d¡ |  ¡  d S ©NZmdb©r2   rˆ   r°   r   r   r   Útest_backup_restore_with_conf¼  s    
z0DomainBackupOnline.test_backup_restore_with_confc                 C   s   |   d¡ |  ¡  d S r³   ©r2   rr   r°   r   r   r   Útest_backup_restore_no_secretsÀ  s    
z1DomainBackupOnline.test_backup_restore_no_secretsc                 C   s   |   d¡ |  ¡  d S r¶   ©r2   ru   r°   r   r   r   Útest_backup_restore_into_siteÄ  s    
z0DomainBackupOnline.test_backup_restore_into_site)
rª   r«   r¬   r#   r²   rµ   r¸   rº   r¼   r­   r   r   r0   r   r®   ­  s   r®   c                       sj   e Zd Z‡ fdd„Zdd„ Zdd„ Zdd„ Zd	d
„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zd‡ fdd„	Z‡  ZS )ÚDomainBackupRenamec                    sZ   t t| ƒ ¡  d| _d| _d| _d| _ddd| j| jd| j | jg| _	|  j
d	g7  _
d S )
NZ
RENAMESERVZ	NEWDOMAINzrename.test.netzDC=rename,DC=test,DC=netr¨   r©   Úrenamer¯   ZbackupRename)r"   r½   r#   r'   r,   r-   Ú
new_basednr)   r&   r*   r+   r°   r0   r   r   r#   Ì  s    
þzDomainBackupRename.setUpc                 C   s   |   ¡  d S r–   r±   r°   r   r   r   r²   Ø  s    z$DomainBackupRename.test_backup_untarc                 C   s   |   d¡ |  ¡  d S r¶   r´   r°   r   r   r   rµ   Û  s    
z&DomainBackupRename.test_backup_restorec                 C   s   |   d¡ |  ¡  d S r³   r·   r°   r   r   r   r¸   ß  s    
z0DomainBackupRename.test_backup_restore_with_confc                 C   s   |   d¡ |  ¡  d S r¶   r¹   r°   r   r   r   rº   ã  s    
z1DomainBackupRename.test_backup_restore_no_secretsc                 C   s   |   d¡ |  ¡  d S r³   r»   r°   r   r   r   r¼   ç  s    
z0DomainBackupRename.test_backup_restore_into_sitec                 C   sX   d}dj || jtjd d}|  t| j|¡ dj |tjd | jd}|  t| j|¡ dS )z:Checks that rename commands with invalid args are rejectedz samba-tool domain backup rename z{cmd} {domain} {realm}r!   )rŸ   r¨   r„   r    N)rs   r,   r$   r%   r   r   r   r-   )r/   Z
rename_cmdZbad_cmdr   r   r   Útest_backup_invalid_argsë  s    þþz+DomainBackupRename.test_backup_invalid_argsc                 C   s<   t  ¡ }t  | j |¡|_t  |t j|¡||< | j  |¡ d S r–   )r   ZMessagerb   rJ   ZMessageElementZFLAG_MOD_ADDZmodify)r/   ÚattrÚsourceÚtargetÚmr   r   r   Úadd_linkû  s    zDomainBackupRename.add_linkc                 C   s†  t | jdƒ}|  | jj|dg¡ d| }| j |ddœ¡ d| }| j |ddœ¡ d}|  |||¡ d	t| j ¡ ƒ }| j |d
dœ¡ |  | jj|¡ |  |||¡ |  ¡ }|  	|¡ |  
¡ }|  |¡}t| j ¡ ƒ}	t |	d | j|¡}
t |	d | j|¡}t |	d | j|¡}|j|tj|gd}|  t|ƒdd¡ |  ||d v d¡ dd„ |d | D ƒ}|  |
|v ¡ |  ||v ¡ dS )z:Sanity-check that a rename handles one-way links correctlyZrename_testztree_delete:1zCN=link_src,%sZmsExchConfigurationContainer)rJ   ZobjectclasszOU=link_tgt,%sZorganizationalunitZaddressBookRoots2z?CN=testrename,CN=Servers,CN=Default-First-Site-Name,CN=Sites,%sr)   rŒ   r9   r`   z)Failed to find renamed link source objectr   zMissing link attributec                 S   s   g | ]}t |ƒ‘qS r   r:   )r;   Úxr   r   r   r=   ,  r>   z9DomainBackupRename.test_one_way_links.<locals>.<listcomp>N)r   r   rT   ÚdeleteÚaddrÅ   r3   rI   ra   rj   rk   rl   r4   ÚreÚsubr¿   r   r?   rc   rK   rA   )r/   Ztest_ouZsrc_dnZ	target_dnZ	link_attrZ	server_dnr[   r   rt   Z
old_basednZnew_target_dnZ
new_src_dnZnew_server_dnrB   Zlink_valuesr   r   r   Útest_one_way_links  sD    ÿÿ


ÿÿz%DomainBackupRename.test_one_way_linksTc                    s€  t t| ƒ}| ||¡}t| ¡ ƒ}|  || j¡ | ¡ }t 	t|ƒ¡}|j
|tjddgd| d}|  t|ƒdd¡ |  t|d  d¡ƒ| j¡ |  t|d  d¡ƒ| j¡ | j}	d| j| jf }
|j
|
tjd	gd
}|  t|ƒdd¡ d| j ¡ |	f }|  t|d  d	¡ƒ|¡ d|	|f }
|j
|
tjd}|  t|ƒdd¡ | ¡  ¡ }d|	|f }
|j
|
tjd}|  t|ƒdd¡ |S )NZnETBIOSNameZcnz	ncName=%s)r   r   r   r   r`   z*Looking up partition's NetBIOS name failedr   zCN=%s,OU=Domain Controllers,%sZdNSHostNamer9   z&Looking up new DC's dnsHostname failedz%s.%sz*DC=%s,CN=MicrosoftDNS,DC=DomainDnsZones,%s)r   r   z&Lookup of new domain's DNS zone failedz1DC=_msdcs.%s,CN=MicrosoftDNS,DC=ForestDnsZones,%s)r"   r½   rl   r3   r4   rc   r¿   Zget_partitions_dnr   Zbinary_encoder   ZSCOPE_ONELEVELrK   r@   r,   r-   r'   r?   ÚlowerZget_root_basednZget_linearized)r/   r   rq   Zcommon_testr5   r6   Zpartitions_dnZnc_namerB   r„   rJ   Zexpected_valZforestdnr0   r   r   rl   1  sJ    

þÿÿ
ÿÿÿz*DomainBackupRename.check_restored_database)T)rª   r«   r¬   r#   r²   rµ   r¸   rº   r¼   rÀ   rÅ   rË   rl   r­   r   r   r0   r   r½   É  s   0r½   c                       s<   e Zd Z‡ fdd„Zdd„ Zdd„ Zdd„ Zd	d
„ Z‡  ZS )ÚDomainBackupOfflinec                    s   t t| ƒ ¡  g d¢| _d S )N)r¨   r©   Zoffline)r"   rÍ   r#   r*   r°   r0   r   r   r#   _  s    zDomainBackupOffline.setUpc                 C   s   | j dd d S )Nr`   )re   r±   r°   r   r   r   r²   c  s    z%DomainBackupOffline.test_backup_untarc                 C   s   |   ¡  d S r–   )rˆ   r°   r   r   r   r¸   f  s    z1DomainBackupOffline.test_backup_restore_with_confc                 C   s   |   ¡  d S r–   )rn   r°   r   r   r   rµ   i  s    z'DomainBackupOffline.test_backup_restorec                 C   s   |   ¡  d S r–   )ru   r°   r   r   r   r¼   l  s    z1DomainBackupOffline.test_backup_restore_into_site)	rª   r«   r¬   r#   r²   r¸   rµ   r¼   r­   r   r   r0   r   rÍ   ]  s
   rÍ   )Zsambar   r   rY   r$   rU   Zsamba.testsr   r   r   r   r   r   Zsamba.samdbr	   Z
samba.authr
   r   r   Zsamba.netcmd.fsmor   rÉ   r   Z
samba.dsdbr   r   r   r®   r½   rÍ   r   r   r   r   Ú<module>   s*        