Bundle transport completeness check
Hello,
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.
Hi Denis,
Thank you for sharing your development, and congratulations on publishing your first blog on SAP Community.
I should point out that others have considered this problem before, and there have been blogs about their efforts, though admittedly they can be difficult to find with the site’s built-in search. I think you may be interested to look at these previous efforts and compare their work with your own:
Transport Checking Tool (Object Level) by Edwin Vleeshouwers
How-to check dependencies between transport requests by Nicolas Busson
And there is some built-in functionality that can do some of this, too, though I admit I’ve not configured it myself:
Import Queue Web UI – Performing Import Checks (requires component CTS Plug-In)
Still, I think your blog and your code sample could generate some useful discussion.
A couple of side notes: I adjusted the tags on your blog to show that it is related to Software Logistics, and specifically to Change Control and Transport, as well as to the NetWeaver Application Server ABAP. Come to think of it, it should probably have an ABAP Development tag on it as well, so I’ll add that.
Cheers,
Matt
Thank you, Matt.
Especially for pointing to plug-in. Will discuss with basis why still not using it 🙂
BR,
Denis
Dear Denis,
I found this report also very useful to perform the TR checks, sometimes I found bugs while executing, so I am not totally dependent on this report, but it has come into my first step to perform TR related checks.
Please check sap Note 2475591.
https://launchpad.support.sap.com/#/notes/2475591
Ignore if already known.
Regards