Bundle transport completeness check
Being at QA person on the project is not only checking developments done. The main question is always how safe to transport certain transport requests (usually a bundle) to a certain system and ensure there are no collisions. The task is usually to check each object from a bundle and check its versions. Task is becoming more complex when there could be collisions between partial objects, for example somebody transporting entire class but there are changes for certain methods or sections only. All of those could lead to wrong queue and differences of the object versions through the landscape. That is why there was an idea to automate this check and below is how it was build. The expected result a report receiving a list of transport requests and a target system at start.
At the first step, we would need to build a list of objects to check from the list provided. The easiest way to read it directly from E071 table and remove duplicates. Then we would check each request for each object for collisions. In order to ensure transport consistency before the system specified I decided to add each system check. To do so we would need to read target system of each TR we are checking and get it’s configuration with function TR_GET_LIST_OF_TARGETS. Target system of request and its last change time could be found in E070 table.
Next step is to read object versions. After a little debugging, table VRSD has been discovered. The goal is to find the previous version transport request and ensure it has been transported to target system. This could be done simply by reading a version with a time and date lower that release time of current request we are checking. Therefore, after obtaining of TR we would need to read its transport history. I found that function STRF_READ_COFILE fits well for that. Function returns all actions performed with TR in each system. I guess function = “I” (import) as fact of request import. Report will check each TR like this, it should be transported or to be in a current bundle to avoid errors.
However, this is unfortunately not the only check to be done. We need to ensure that newer versions of objects have not transported yet to avoid overwriting by older ones. Actually, we already got everything to do it. Just check the history of TRs with higher version if its transported or not. If it is, the error should be shown.
The final question is how to handle partial objects. I attempted to do it via function TRINT_GET_PART_OBJECTS call to partial components of each object report checking. The idea is simply to check all this function returns or if it returns nothing to check the initial object itself.
Here is a report listing. Maybe it will be helpful.
report bundle_test. tables: e070, vrsd. ********************************** * use only in DEVELOPMENT system * ********************************** selection-screen begin of block main with frame title text-t00. select-options s_trkorr for e070-trkorr. parameters p_target type sy-sysid obligatory. parameters p_fnc like tstrfcofil-function default 'I' modif id 100. selection-screen end of block main. at selection-screen output. loop at screen. check screen-group1 = 100. screen-input = 0. modify screen. endloop. start-of-selection. types: begin of ts_obj , pgmid type e071-pgmid , object type e071-object , obj_name type e071-obj_name , end of ts_obj. types: begin of ts_cons , tr_target type sysname , targets type trsysclis , end of ts_cons . data: lt_list type table of e071 , lt_objs type table of ts_obj , lt_vers type table of vrsd , ls_e070 type e070 " request of the particular object , lt_e070 type table of e070 , lt_cons type hashed table of ts_cons with unique key tr_target , lt_cofi type tr_cofilines , lt_part type trwb1_t_object , lv_err type abap_bool . " get obj list select trkorr pgmid object obj_name from e071 into corresponding fields of table lt_list where trkorr in s_trkorr. if sy-subrc ne 0. message s001(00) with 'Nothing found.' display like 'E'. stop. endif. lt_objs = value #( for <ob> in lt_list ( pgmid = <ob>-pgmid object = <ob>-object obj_name = <ob>-obj_name ) ). sort lt_objs. delete adjacent duplicates from lt_objs comparing all fields. sort lt_list by pgmid object obj_name trkorr. data(lv_total) = lines( lt_objs ). loop at lt_objs assigning field-symbol(<obj>). call function 'PROGRESS_INDICATOR' exporting i_msgid = 'ZBC' i_msgno = '000' i_msgv1 = <obj>-pgmid i_msgv2 = <obj>-object i_msgv3 = <obj>-obj_name i_processed = sy-tabix i_total = lv_total. " are there object parts? refresh lt_part. call function 'TRINT_GET_PART_OBJECTS' exporting iv_hpgmid = <obj>-pgmid iv_hobject = <obj>-object iv_hobjname = <obj>-obj_name iv_hobjname_as_structure = abap_false tables et_part_objects = lt_part exceptions no_part_objects_possible = 1 syntax_table_entry_without_len = 2 header_object_name_too_short = 3 header_object_name_too_long = 4 hobjname_parameter_wrong = 5. if sy-subrc ne 0. insert value #( pgmid = <obj>-pgmid object = <obj>-object obj_struct = value #( ( substtype = 'CON' value = <obj>-obj_name ) ) ) into table lt_part. endif. " check request by request for mentioned object clear lv_err. write: / 'Checking object', <obj>-pgmid, <obj>-object, <obj>-obj_name. loop at lt_list assigning field-symbol(<list>) where pgmid = <obj>-pgmid and object = <obj>-object and obj_name = <obj>-obj_name. " get the request of the particular object select single * from e070 into ls_e070 where trkorr = <list>-trkorr. check sy-subrc eq 0. " read tms configuration read table lt_cons assigning field-symbol(<tms>) with key tr_target = ls_e070-tarsystem. if sy-subrc ne 0. insert value #( tr_target = ls_e070-tarsystem ) into table lt_cons assigning <tms>. call function 'TR_GET_LIST_OF_TARGETS' exporting iv_cons_target = ls_e070-tarsystem importing et_targets = <tms>-targets exceptions tce_config_error = 1. endif. " process all the partial objects loop at lt_part assigning field-symbol(<part>). loop at <part>-obj_struct assigning field-symbol(<str>) where substtype = 'CON'. " read versions of the object refresh lt_vers. select * from vrsd into table lt_vers where objtype = <part>-object and objname = <str>-value. check sy-subrc eq 0. sort lt_vers by datum descending zeit descending. " get previous version data lv_prev_version type sy-tabix. clear lv_prev_version. loop at lt_vers assigning field-symbol(<prev_vers>) where korrnum ne ls_e070-trkorr and korrnum is not initial and ( datum lt ls_e070-as4date or datum eq ls_e070-as4date and zeit lt ls_e070-as4time ). lv_prev_version = sy-tabix. " read request log refresh lt_cofi. call function 'STRF_READ_COFILE' exporting iv_trkorr = <prev_vers>-korrnum tables tt_cofi_lines = lt_cofi exceptions wrong_call = 1 no_info_found = 2 others = 3. if sy-subrc ne 0. write: / icon_yellow_light as icon, 'cant get history for the predecessor', <prev_vers>-korrnum. lv_err = abap_undefined. else. " check all the systems until requested loop at <tms>-targets assigning field-symbol(<target>). loop at lt_cofi assigning field-symbol(<cofi>) where tarsystem = <target>-sysname and tarclient = <target>-client and function = p_fnc. exit. endloop. if sy-subrc ne 0. " ok not imported yet, but may be in current bundle? read table lt_list transporting no fields with key trkorr = <prev_vers>-korrnum. if sy-subrc ne 0. write: / icon_red_light as icon, 'predecessor', <prev_vers>-korrnum, 'hasnt been imported into', <target>-sysname. lv_err = abap_true. endif. endif. if <target>-sysname = p_target. exit. endif. endloop. endif. exit. endloop. " now check if successors are transported if lv_err is initial. loop at lt_vers assigning field-symbol(<next_vers>) from 1 to ( lv_prev_version - 1 ). " if it's in bundle - then on read table lt_list transporting no fields with key trkorr = <next_vers>-korrnum. check sy-subrc ne 0. " read request log refresh lt_cofi. call function 'STRF_READ_COFILE' exporting iv_trkorr = <next_vers>-korrnum tables tt_cofi_lines = lt_cofi exceptions wrong_call = 1 no_info_found = 2 others = 3. if sy-subrc ne 0. " no logs -> good, not transported yet else. loop at <tms>-targets assigning <target>. loop at lt_cofi assigning <cofi> where tarsystem = <target>-sysname and tarclient = <target>-client and function = p_fnc. exit. endloop. if sy-subrc eq 0. " newer version already transported!!! write: / icon_red_light as icon, 'successor', <next_vers>-korrnum, 'has been already imported into', <target>-sysname. lv_err = abap_true. endif. if <target>-sysname = p_target. exit. endif. endloop. endif. endloop. endif. endloop. endloop. endloop. if lv_err eq abap_false. write: / icon_green_light as icon, 'seems to be ok'. endif. write: / sy-uline. endloop.