Skip to Content
Author's profile photo Denis Muzhzhukhin

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.

 

Assigned Tags

      3 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Matt Fraser
      Matt Fraser

      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

      Author's profile photo Denis Muzhzhukhin
      Denis Muzhzhukhin
      Blog Post Author

      Thank you, Matt.

      Especially for pointing to plug-in. Will discuss with basis why still not using it 🙂

      BR,

      Denis

      Author's profile photo Abhijit Barui
      Abhijit Barui

      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