3 if_r@sDddlmZmZmZddlZddlZddlZddlZddlZddl Z ddl m Z m Z m Z mZmZddlmZdZejjedZejjedZdZejjedZd Zd Zd Zd Zed ed eded iZeeeegZdZ ej!e"Z#ddZ$Gddde%Z&Gddde%Z'Gddde%Z(ej)dddddddgZ*d%d d!Z+d&d#d$Z,dS)')print_functiondivisionabsolute_importN) system_exit create_dir create_file make_utf8write_to_file_utf8)ugettextz/etc/rhsm/syspurposezsyspurpose.jsonzvalid_fields.jsonz/var/lib/rhsm/cacheroleaddonsservice_level_agreementusageZaddOnsZ 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.pypost_process_received_data?s    rc@sbeZdZdZdddZddZddZd d Zd d Zd dZ ddZ dddZ e dddZ dS)SyspurposeStorez9 Represents and maintains a json syspurpose file FcCs||_i|_||_dS)N)pathcontentsraise_on_error)selfrrrrr__init__VszSyspurposeStore.__init__cCsy.tj|jddd}tj||_dSQRXWntk rhtjj|jrdt tj t dj |jdSt k r}zj|jtjkr|j rt tjt dj |j|jtjkr|j rtjd 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)rr7r8Zorgrrrsets  zSyspurposeStore.setNc CsH|s8tj|jddd}t||j|jWdQRXn t||jdS)zE Write the current contents to the file at self.path wzutf-8)rN)rrrr rflush)rfpr-rrrwrites  zSyspurposeStore.writecCs0|||d}tj|tjs$|jn|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#accessW_OKr1r/)clsrrZ new_storerrrreads   zSyspurposeStore.read)F)N)F)__name__ __module__ __qualname____doc__rr/r1r:r<r;r@rD classmethodrHrrrrrQs   rc@seZdZdZddZdS) SyncResultza A container class for the results of a sync operation performed by a SyncedStore class. cCs||_||_||_||_dS)N)resultremote_changed local_changedcached_changed)rrOrPrQrRrrrrszSyncResult.__init__N)rIrJrKrLrrrrrrNsrNc@seZdZdZeZeZd2ddZddZ dd Z d d Z d d Z ddZ d3ddZddZddZddZddZddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zed,d-Zed.d/Zd0d1Z 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||_|jjdd|_|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)uepPATHsplitfilenamer CACHE_PATH cache_pathZ local_fileget_local_contentslocal_contentsZ cache_fileget_cached_contentscache_contentschanged on_changed consumer_uuidget_valid_fields valid_fields)rrWrbrcZuse_valid_fieldsrrrrs   zSyncedStore.__init__cCs|S)Nr)rrrr __enter__szSyncedStore.__enter__cCs |jdS)N)finish)rexc_typeZexc_valZexc_tbrrr__exit__szSyncedStore.__exit__cCs|jr|jdS)z{ When local content was changed, then try to synchronize local content with remote server :return: N)rasync)rrrrrg"szSyncedStore.finishcstjdy*|jr2|jjd r2tjd|jSWn6tk rj}ztjdj|d|jSd}~XnX|j}|j}|j }|j |||dfdd 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)localremotebasecsi|]}|r||qSrr).0r7)rOrr Dsz$SyncedStore.sync..z#Successfully synced system purpose.F)r,debugrWhas_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|dS)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 ii|_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"tjdiS|jjdsLOCAL_TO_REMOTE)rZconsumerrOattrr8rrrrvus      zSyncedStore.get_remote_contentsc Csly(tjtj|jddd|_tjdWn<tt j t fk rdtjd|j i|_|j iYnX|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||_|jdS)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)rrrrrryszSyncedStore.update_localcCs|j|j|jdS)zD Write local data to the file :return: None N) _update_filerr^)rrrrrszSyncedStore._write_localcCs||_|jdS)N)r` _write_cache)rrrrrrzszSyncedStore.update_cachecCs|j|j|jdS)z; Write cache to file :return: None N)rr\r`)rrrrrszSyncedStore._write_cachecCs||jdks|jdkr"tjddS|jt}|jj|j|jtpBd|dk rN|ng|jtp\d|jt phddtjddS)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>ADDONSZupdateConsumerROLE SERVICE_LEVELUSAGE)rrr rrrrxs     zSyncedStore.update_remotecCs|jdk r||jkrf||j|krttdj||dx`|j|D]}t|dkrDtd|qDWn4ttdj|dx|jjD]}td|qWdS)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}:)valrrz - %szHWarning: Provided key "{key}" is not included in the list of valid keys:)r7)reprintr&r'lenkeys)rr7r8Z valid_valueZ valid_keyrrr_check_key_value_validitys      z%SyncedStore._check_key_value_validityc Cst|}t|}y|j|}|dk rr?rar,rrr)rr7r8rrrr;,s    zSyncedStore.unsetcCst|}t|}t|jj|d}||j|<||ks<|dkrb|j||d|_tjd||fn 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>rrar,rrr)rr7r8r9rrrr@Is     zSyncedStore.setcCshtjj|sdtjd|ytj|dddWn4tk rb}ztjd||fWYdd}~XnXdS)zr Try to create missing directory :param dir_path: path to directory :return: None zTrying to create directory: %siT)modeexist_okz)Unable to create directory: %s, error: %sN)r#risdirr,rrmakedirsruZwarning)Zdir_pathrlrrr_create_missing_dirgs  zSyncedStore._create_missing_dircCs|jt|jtytj|ddd}Wn.tk rV}z|jdkrFWYdd}~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)rNz/Successfully updated syspurpose values at '%s'.z+Failed to update syspurpose values at '%s'.) rUSER_SYSPURPOSE_DIR CACHE_DIRrrr(r)r rBcloser,rr)rGrrr-r.rrrrvs    zSyncedStore._update_filecCsd}|jdk r|jdk r|jj|j}d|kr|d}y|jj|}Wn0tk rv}ztjd|WYdd}~XnXd|krt|}|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_keyZresponserlrrrrds zSyncedStore.get_valid_fields)NNF)NNN)!rIrJrKrLUSER_SYSPURPOSErXCACHED_SYSPURPOSEr[rrfrirgrjrtrwr]rvr_ryrrzrrxrr:r<r;r@ staticmethodrrMrrdrrrrrSs4 *   +"  rS DiffChanger7previous_value new_valuesourcein_base in_resultrnc Cstjdi}|pi}|pi}|p$i}|dkr4|}n|dkrB|}ntd|dkrZdd}t|jt|jBt|jB}x(|D]}t|||dd} t|||d d} | p| o| tk} d } | | kr| d krtjd ||} ||kr||||<nv| d kr,tjd |d} ||krn||||<nB| s<| tkrn| d krTtjd|d} ||krn||||<| r|j|} t|| | |j|||k||kd}||qW|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)Zchangerrrsz!three_way_merge..)rootherr7rserverroTzLThree 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'.)r7rrrrr) r,rrr"r@rdetect_changed UNSUPPORTEDr>r)rmrornZ on_conflictr{rOwinnerZall_keysr7rQrPraroriginalZdiffrrrr|sT $        r|rcCs|pi}|pi}||kr$|dkr$tS|j|}|j|}||krP|dkrPt|St|tkrxt|tkrxt|t|kS|dkr|dkr|dkrdS||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 rmrNr=F)rr>booltyper3sorted)rorr7rZbase_valZ other_valrrrrs   r)rnN)r)-Z __future__rrr collectionsZloggingr r#r)rZsyspurpose.utilsrrrrr Zsyspurpose.i18nr r&rrjoinrZ VALID_FIELDSrrrrrrrr~rZ getLoggerrIr,robjectrrNrS namedtuplerr|rrrrrsJ     1 B