3 •vh_rã@sDddlmZmZmZddlZddlZddlZddlZddlZddl Z ddl m Z m Z m Z mZmZddlmZdZejjedƒZejjedƒZdZejjedƒZd Zd Zd Zd Zed ed eded iZeeeegZdZ ej!e"ƒZ#dd„Z$Gdd„de%ƒZ&Gdd„de%ƒZ'Gdd„de%ƒZ(ej)dddddddgƒZ*d%d d!„Z+d&d#d$„Z,dS)'é)Úprint_functionÚdivisionÚabsolute_importN)Ú system_exitÚ create_dirÚ create_fileÚ make_utf8Úwrite_to_file_utf8)Úugettextz/etc/rhsm/syspurposezsyspurpose.jsonzvalid_fields.jsonz/var/lib/rhsm/cacheÚroleÚaddonsÚservice_level_agreementÚusageZaddOnsZ serviceLevelZ unsupportedcCs`d|kr\d|dkr2|dd|dd<|dd=d|dkr\|dd|dd<|dd=|S)zæ Try to solve conflicts in keys - Server returns key "roles", but it should be "role" - Server returns key "support_level", but service_level_agreement is used in syspurpose.json :return: modified dictionary ÚsystemPurposeAttributesZrolesr Z support_levelr ©)Údatarrú/usr/lib/python3.6/files.pyÚpost_process_received_data?s    rc@sbeZdZdZddd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dd„Z ddd„Z e ddd„ƒZ dS)ÚSyspurposeStorez9 Represents and maintains a json syspurpose file FcCs||_i|_||_dS)N)ÚpathÚcontentsÚraise_on_error)ÚselfrrrrrÚ__init__VszSyspurposeStore.__init__cCsòy.tj|jddd��}tj|ƒ|_dSQRXWn¾tk rhtjj|jƒrdt tj t dƒj |jƒƒdSt k rì}zj|jtjkr¤|j r¤t tjt dƒj |jƒƒ|jtjkrÒ|j rÒtjd j |j|d �ƒdS|jrÜ|‚WYdd}~XnXdS) aN Opens & reads the contents of the store's file based on the 'path' provided to the constructor, and stores them on this object. If the user doesn't have access rights to the file, the program exits. :return: False if the contents of the file were empty, or the file doesn't exist; otherwise, nothing. Úrzutf-8)ÚencodingTNz)rr7r8ZorgrrrÚsetÄs  zSyspurposeStore.setNc CsH|s8tj|jddd��}t||jƒ|jƒWdQRXn t||jƒdS)zE Write the current contents to the file at self.path Úwzutf-8)rN)rrrr rÚflush)rÚfpr-rrrÚwriteÓs  zSyspurposeStore.writecCs0|||d�}tj|tjƒs$|jƒn|jƒ|S)aL Read the file represented by path. If the file does not exist it is created. :param path: The path on the file system to read, should be a json file :param raise_on_error: When it is set to True, then exceptions are raised as expected. :return: new SyspurposeStore with the contents read in )r)r#ÚaccessÚW_OKr1r/)ÚclsrrZ new_storerrrÚreadÞs   zSyspurposeStore.read)F)N)F)Ú__name__Ú __module__Ú __qualname__Ú__doc__rr/r1r:r<r;r@rDÚ classmethodrHrrrrrQs   rc@seZdZdZdd„ZdS)Ú SyncResultza A container class for the results of a sync operation performed by a SyncedStore class. cCs||_||_||_||_dS)N)ÚresultÚremote_changedÚ local_changedÚcached_changed)rrOrPrQrRrrrrõszSyncResult.__init__N)rIrJrKrLrrrrrrNðsrNc@sÜeZdZdZeZeZd2dd„Zdd„Z dd „Z d d „Z d d „Z dd„Z d3dd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zd d!„Zd"d#„Zd$d%„Zd&d'„Zd(d)„Zd*d+„Zed,d-„ƒZed.d/„ƒZd0d1„Z dS)4Ú SyncedStorezz Stores values in a local file backed by a cache which is then synced with another source of the same values. NFcCsx||_|jjdƒd|_|j|_|j|_d|_|jƒ|_ d|_ |j ƒ|_ d|_ ||_||_|dkrn|jƒ|_nd|_dS)a8 Initialization of SyncedStore :param uep: object representing connection to candlepin server :param on_changed: optional callback method called, during three-way merge :param consumer_uuid: UUID of consumer :param use_valid_fields: if valid fields are considered ú/éNFTéÿÿÿÿ)ÚuepÚPATHÚsplitÚfilenamerÚ CACHE_PATHÚ cache_pathZ local_fileÚget_local_contentsÚlocal_contentsZ cache_fileÚget_cached_contentsÚcache_contentsÚchangedÚ on_changedÚ consumer_uuidÚget_valid_fieldsÚ valid_fields)rrWrbrcZuse_valid_fieldsrrrrs   zSyncedStore.__init__cCs|S)Nr)rrrrÚ __enter__szSyncedStore.__enter__cCs |jƒdS)N)Úfinish)rÚexc_typeZexc_valZexc_tbrrrÚ__exit__szSyncedStore.__exit__cCs|jr|jƒdS)z{ When local content was changed, then try to synchronize local content with remote server :return: N)raÚsync)rrrrrg"szSyncedStore.finishcsâtjdƒy*|jr2|jjdƒ r2tjdƒ|jƒSWn6tk rj}ztjdj|d�ƒ|jƒSd}~XnX|jƒ}|jƒ}|j ƒ}|j |||d�‰‡fdd „ˆDƒ}t ˆ|ˆkpº|j ˆƒ|j |ƒ|jˆƒƒ}tjd ƒd |_|S) z� Try to synchronize local content with remote server :return: instance of SyncResult holding result of synchronization z(Attempting to sync syspurpose content...Ú syspurposez9Server does not support syspurpose, syncing only locally.zDFailed to detect whether the server has syspurpose capability: {err})ÚerrN)ÚlocalÚremoteÚbasecsi|]}ˆ|rˆ||“qSrr)Ú.0r7)rOrrú Dsz$SyncedStore.sync..z#Successfully synced system purpose.F)r,ÚdebugrWÚhas_capabilityÚ_sync_local_onlyÚ Exceptionr'Úget_remote_contentsr]r_ÚmergerNÚ update_remoteÚ update_localÚ update_cachera)rrlZremote_contentsr^Zcached_contentsZ local_resultZ sync_resultr)rOrrj*s2      zSyncedStore.synccCs|j|jƒƒ}t|jd|dƒS)NF)ryr]rNr^)rZ local_updatedrrrrtTszSyncedStore._sync_local_onlycCst||||jd�}|S)zÉ Do three-way merge :param local: dictionary with local values (syspyrpose.json) :param remote: dictionary with values from server :param base: :return: )rmrornÚ on_change)Úthree_way_mergerb)rrmrnrorOrrrrwXs  zSyncedStore.mergec Csbytjtj|jddd�ƒ|_Wn<tjtt fk rZt j d|jƒ|j iƒi|_YnX|jS)zl Try to load local content from file :return: dictionary with system purpose values rzutf-8)rz+Unable to read local system purpose at "%s") r r!rrrr^r#rr"ÚIOErrorr,rrry)rrrrr]hs  zSyncedStore.get_local_contentscCs€|jdks|jdkr"tjdƒiS|jjdƒsÚLOCAL_TO_REMOTE)rZconsumerrOÚattrr8rrrrvus      zSyncedStore.get_remote_contentsc Csly(tjtj|jddd�ƒ|_tjdƒWn<tt j t fk rdtjd|j ƒi|_|j iƒYnX|jS)zy Try to load cached server response from the file :return: dictionary with system purpose values rzutf-8)rz-Successfully read cached syspurpose contents.z2Unable to read cached syspurpose contents at '%s'.)r r!rrr\r`r,rrr"r#rr}rrz)rrrrr_�szSyncedStore.get_cached_contentscCs||_|jƒdS)z¦ Rewrite local content with new data and write data to file syspurpose.json :param data: new dictionary with local data :return: None N)r^Ú _write_local)rrrrrry›szSyncedStore.update_localcCs|j|j|jƒdS)zD Write local data to the file :return: None N)Ú _update_filerr^)rrrrr�¤szSyncedStore._write_localcCs||_|jƒdS)N)r`Ú _write_cache)rrrrrrz«szSyncedStore.update_cachecCs|j|j|jƒdS)z; Write cache to file :return: None N)r‚r\r`)rrrrrƒ¯szSyncedStore._write_cachecCs||jdks|jdkr"tjdƒdS|jtƒ}|jj|j|jtƒpBd|dk rN|ng|jtƒp\d|jt ƒphdd�tjdƒdS)NzmFailed to update remote syspurpose on the server: no available connection, or the consumer is not registered.Fr=)r r Z service_levelrz5Successfully updated remote syspurpose on the server.T) rWrcr,rrr>ÚADDONSZupdateConsumerÚROLEÚ SERVICE_LEVELÚUSAGE)rrr rrrrx¶s     zSyncedStore.update_remotecCsž|jdk rš||jkrf||j|kršttdƒj||d�ƒx`|j|D]}t|ƒdkrDtd|ƒqDWn4ttdƒj|d�ƒx|jjƒD]}td|ƒq†WdS)z¸ Check validity of provided key and value of it is included in valid fields :param key: provided key :param value: provided value :return: None NzaWarning: Provided value "{val}" is not included in the list of valid values for attribute {attr}:)Úvalr€rz - %szHWarning: Provided key "{key}" is not included in the list of valid keys:)r7)reÚprintr&r'ÚlenÚkeys)rr7r8Z valid_valueZ valid_keyrrrÚ_check_key_value_validityÇs      z%SyncedStore._check_key_value_validityc Csöt|ƒ}t|ƒ}y„|j|}|dk rr?rar,rrr�)rr7r8rrrr;,s    zSyncedStore.unsetcCs–t|ƒ}t|ƒ}t|jj|dƒƒ}||j|<||ks<|dkrb|j||ƒd|_tjd||fƒn tjdƒ||kpz|dk|_|jdkr�|jƒ|jS)zù Set a key (syspurpose parameter) to value :param key: The parameter of the syspurpose file to set :type key: str :param value: The value to set that parameter to :return: Whether any change was made NTzSetting value '%s' to key '%s'.z#NOT Setting value '%s' to key '%s'.)rr^r>rŒrar,rrr�)rr7r8r9rrrr@Is     zSyncedStore.setcCshtjj|ƒsdtjd|ƒytj|ddd�Wn4tk rb}ztjd||fƒWYdd}~XnXdS)zr Try to create missing directory :param dir_path: path to directory :return: None zTrying to create directory: %siíT)ÚmodeÚexist_okz)Unable to create directory: %s, error: %sN)r#rÚisdirr,rrÚmakedirsruZwarning)Zdir_pathrlrrrÚ_create_missing_dirgs  zSyncedStore._create_missing_dircCs’|jtƒ|jtƒytj|ddd�}Wn.tk rV}z|jdkrF‚WYdd}~Xn*Xt||ƒ|jƒ|j ƒt j d|ƒt j d|ƒdS)a Write the contents of data to file in the first mode we can (effectively to create or update the file) :param path: The string path to the file location we should update :param data: The data to write to the file :return: None zw+zutf-8)réNz/Successfully updated syspurpose values at '%s'.z+Failed to update syspurpose values at '%s'.) r‘ÚUSER_SYSPURPOSE_DIRÚ CACHE_DIRrrr(r)r rBÚcloser,rr)rGrrr-r.rrrr‚vs    zSyncedStore._update_filecCs”d}|jdk r�|jdk r�|jj|jƒ}d|kr�|d}y|jj|ƒ}Wn0tk rv}ztjd|ƒWYdd}~XnXd|kr�t|ƒ}|d}|S)z† Try to get valid fields from server using current owner (organization) :return: Dictionary with valid fields Nr7z*Unable to get valid fields from server: %sr)rWrcZgetOwnerZgetOwnerSyspurposeValidFieldsrur,rrr)rreZ current_ownerZ owner_keyZresponserlrrrrd’s zSyncedStore.get_valid_fields)NNF)NNN)!rIrJrKrLÚUSER_SYSPURPOSErXÚCACHED_SYSPURPOSEr[rrfrirgrjrtrwr]rvr_ryr�rzrƒrxrŒr:r<r;r@Ú staticmethodr‘rMr‚rdrrrrrSüs4 *   +"  rSÚ DiffChanger7Úprevious_valueÚ new_valueÚsourceÚin_baseÚ in_resultrnc Cs®tjdƒi}|pi}|pi}|p$i}|dkr4|}n|dkrB|}ntdƒ‚|dkrZdd„}t|jƒƒt|jƒƒBt|jƒƒB}�x(|D�]}t|||dd�} t|||d d�} | pº| oº| tk} d } | | krø| d krÞtjd |ƒ|} ||krö||||<nv| d k�r,tjd |ƒd} ||k�rn||||<nB| �s<| tk�rn| d k�rTtjd|ƒd} ||k�rn||||<| r†|j|ƒ} t|| | |j|ƒ||k||kd�}||ƒq†W|S)aõ Performs a three-way merge on the local and remote dictionaries with a given base. :param local: The dictionary of the current local values :param base: The dictionary with the values we've last seen :param remote: The dictionary with "their" values :param on_conflict: Either "remote" or "local" or None. If "remote", the remote changes will win any conflict. If "local", the local changes will win any conflict. If anything else, an error will be thrown. :param on_change: This is an optional function which will be given each change as it is detected. :return: The dictionary of values as merged between the three provided dictionaries. zAttempting a three-way merge...rnrmzAkeyword argument "on_conflict" must be either "remote" or "local"NcSs|S)Nr)ZchangerrrÚËsz!three_way_merge..)roÚotherr7rœÚserverroTzLThree way merge conflict: both local and remote values changed for key '%s'.z7Three way merge: remote value was changed for key '%s'.z6Three way merge: local value was changed for key '%s'.)r7rœršr›r�rž) r,rrr"r@r‹Údetect_changedÚ UNSUPPORTEDr>r™)rmrornZ on_conflictr{rOÚwinnerZall_keysr7rQrPrarœÚoriginalZdiffrrrr|°sT $        r|r¡cCsœ|pi}|pi}||kr$|dkr$tS|j|ƒ}|j|ƒ}||krP|dkrPt|ƒSt|ƒtkrxt|ƒtkrxt|ƒt|ƒkS|dkr”|dkr”|dkr”dS||kS)aX Detect the type of change that has occurred between base and other for a given key. :param base: The dictionary of values we are starting with :param other: The dictionary of now current values :param key: The key that we are interested in knowing how it changed :param source: An optional string which indicates where the "other" values came from. Used to make decisions which are one sided. (i.e. only applicable for changes from the server side). :return: True if there was a change, false if there was no change :rtype: bool rmr¡Nr=F)r£r>ÚboolÚtyper3Úsorted)ror r7rœZbase_valZ other_valrrrr¢òs   r¢)rnN)r¡)-Z __future__rrrÚ collectionsZloggingr r#r)rZsyspurpose.utilsrrrrr Zsyspurpose.i18nr r&r“rÚjoinr–Z VALID_FIELDSr”r—r…r„r†r‡rr~r£Z getLoggerrIr,rÚobjectrrNrSÚ namedtupler™r|r¢rrrrÚsJ     1 B