diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..8f62c3cd2d7b99e025c70f9a6a54b5701f0d49b0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+# CI artifacts
+archives
+src/oai_rules_result*
+*.html
diff --git a/.gitmodules b/.gitmodules
index 56724854b677cb1ab0b52c09f690aee564cfb519..d18b52ca55a0860c931156de756b7c28c22d9775 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,9 @@
 [submodule "src/oai-cn5g-common-src"]
 	path = src/oai-cn5g-common-src
 	url = https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-common-src.git
-[submodule "build/build-common"]
-	path = build/build-common
+[submodule "build/common-build"]
+	path = build/common-build
 	url = https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-common-build.git
 [submodule "ci-scripts/common-ci"]
-	path = ci-scripts/common-ci
+	path = ci-scripts/common
 	url = https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-common-ci.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df9c533055dc5fa278b3921e2176af395882589d..e656860b44d6abbe9777eb719b33e65e9c73b4fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # RELEASE NOTES: #
 
+## v1.5.1 -- May 2023 ##
+
+* Add HTTP/2 support
+* Code Refactoring for:
+  * Logging mechanism (runtime log level selection)
+  * Installation / build scripts
+  * Continuous Integration scripts
+* Published image on Docker-Hub is using now Ubuntu-20 as base image
+  * We will soon obsolete the build system for Ubuntu18.04
+
 ## v1.5.0 -- January 2023 ##
 
 * Initial release
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ef94fc8e6bb3f5d63d0eea2907eb2e5e8bbbb876..2548db239e35ec061b6391d2d84ced76f1909eac 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,6 +15,19 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
       * The Continuous Integration will reject your pull request.
    - All pull requests SHALL have **`develop`** branch as target branch.
 
+## Synchronizing GIT sub-modules ##
+
+We are using nested GIT submodules. To synchronize them, the 2 most important commands to know are :
+
+1. `git submodule deinit --force .`
+2. `git submodule update --init --recursive`
+
+If you have non-tracked files or modified files within git submodules, these commands may not work.
+
+Use the `--verbose` option to see the execution of each command.
+
+If the synchronization fails, you may need to go into the path of the failing git-submodule(s) and clean the workspace from non-tracked/modified files.
+
 ## Coding Styles ##
 
 We are using `clang-format` as formatting tool on the C/C++ code.
diff --git a/build/build-common b/build/build-common
deleted file mode 160000
index 7ef996a4873ce487fb36da11325ae028974cc0df..0000000000000000000000000000000000000000
--- a/build/build-common
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7ef996a4873ce487fb36da11325ae028974cc0df
diff --git a/build/common-build b/build/common-build
new file mode 160000
index 0000000000000000000000000000000000000000..63ebefe554a739ba1c686d5c2d70946b4af471c0
--- /dev/null
+++ b/build/common-build
@@ -0,0 +1 @@
+Subproject commit 63ebefe554a739ba1c686d5c2d70946b4af471c0
diff --git a/build/scripts/build_helper.pcf b/build/scripts/build_helper.pcf
index 117bc6069864c9916c5a44841f26b840fa8520d7..35f8e2ae7b9875971c7aa6fbe8c06a15cac3d383 100644
--- a/build/scripts/build_helper.pcf
+++ b/build/scripts/build_helper.pcf
@@ -29,342 +29,13 @@
 ################################
 SCRIPT=$(readlink -f ${BASH_SOURCE})
 THIS_SCRIPT_PATH=`dirname $SCRIPT`
-source $THIS_SCRIPT_PATH/../build-common/installation/build_helper
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_spdlog_from_git() {
-  echo "Starting to install spdlog"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install spdlog ? <y/N> " prompt
-    OPTION=""
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    GIT_URL=https://github.com/gabime/spdlog.git
-    echo "Install spdlog from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading spdlog"
-    if [[ $OPTION =~ -[yY](es)* ]]
-    then
-      $SUDO rm -rf spdlog
-    fi
-
-    git clone $GIT_URL
-    cd spdlog && git checkout v1.11.0
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-
-    mkdir _build && cd _build
-    $CMAKE -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DSPDLOG_FMT_EXTERNAL=ON -DSPDLOG_BUILD_SHARED=ON ..
-    make
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    popd
-    rm -Rf $OPENAIRCN_DIR/build/ext/spdlog/_build
-  fi
-  echo "spdlog installation complete"
-  return 0
-}
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_fmt_from_git() {
-  echo "Starting to install fmt"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install fmt? <y/N> " prompt
-    OPTION=""
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    GIT_URL=https://github.com/fmtlib/fmt/
-    echo "Install fmt from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading fmt"
-    if [[ $OPTION =~ -[yY](es)* ]]
-    then
-      $SUDO rm -rf fmt
-    fi
-
-    git clone $GIT_URL
-    cd fmt && git checkout 9.0.0
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-
-    mkdir _build && cd _build
-    $CMAKE -G "Unix Makefiles" -DFMT_TEST=OFF -DBUILD_SHARED_LIBS=TRUE ..
-
-    make
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    popd
-    rm -Rf $OPENAIRCN_DIR/build/ext/fmt/_build
-  fi
-  echo "fmt installation complete"
-  return 0
-}
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_pistache_from_git() {
-  echo "Starting to install pistache"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install Pistache ? <y/N> " prompt
-    OPTION="-y"
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    GIT_URL=https://github.com/oktal/pistache.git
-    echo "Install Pistache from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading Pistache"
-    if [[ $OPTION =~ -[yY](es)* ]]
-    then
-      $SUDO rm -rf pistache
-    fi
-
-    git clone $GIT_URL
-    cd pistache && git checkout e18ed9baeb2145af6f9ea41246cf48054ffd9907
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    mkdir _build && cd _build
-    $CMAKE -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release \
-        -DPISTACHE_BUILD_EXAMPLES=false \
-        -DPISTACHE_BUILD_TESTS=false \
-        -DPISTACHE_BUILD_DOCS=false \
-        ..
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    make -j $(nproc)
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    popd
-    rm -Rf $OPENAIRCN_DIR/build/ext/pistache/_build
-  fi
-  echo "pistache installation complete"
-  return 0
-}
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_nlohmann_from_git() {
-  echo "Starting to install Nlohmann Json"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install Nlohmann Json ? <y/N> " prompt
-    OPTION=""
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    GIT_URL=https://github.com/nlohmann/json.git
-    echo "Install Nlohmann Json from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading Nlohmann"
-    if [[ $OPTION =~ -[yY](es)* ]]
-    then
-      $SUDO rm -rf json
-    fi
-
-    git clone $GIT_URL
-    cd json && git checkout -f v3.10.3
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    mkdir _build && cd _build
-    $CMAKE -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DJSON_BuildTests=OFF ..
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    make
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    popd
-    rm -Rf $OPENAIRCN_DIR/build/ext/json/_build
-  fi
-  echo "Nlohmann Json installation complete"
-  return 0
-}
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_nghttp2_from_git() {
-  echo "Starting to install nghttp2"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install nghttp2 ? <y/N> " prompt
-    OPTION=""
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    if [[ $OS_DISTRO == "ubuntu" ]]; then
-      PACKAGE_LIST="\
-      g++ \
-      $CMAKE \
-      binutils \
-      autoconf \
-      automake \
-      autotools-dev \
-      libtool \
-      pkg-config \
-      zlib1g-dev \
-      libcunit1-dev \
-      libssl-dev \
-      libxml2-dev libev-dev libevent-dev libjansson-dev libc-ares-dev \
-      libjemalloc-dev libsystemd-dev python3-dev python-setuptools"
-    elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then
-      PACKAGE_LIST="\
-      gcc-c++ \
-      binutils-devel \
-      autoconf \
-      automake \
-      $CMAKE \
-      make \
-      libtool \
-      pkg-config \
-      zlib-devel \
-      CUnit-devel \
-      openssl-devel \
-      libxml2-devel libev-devel libevent-devel jansson-devel c-ares-devel \
-      jemalloc-devel systemd-devel python3-Cython python3-devel python3-setuptools"
-    else
-      echo_fatal "$OS_DISTRO is not a supported distribution."
-    fi
-    echo "Install build tools"
-    $SUDO $INSTALLER install $OPTION $PACKAGE_LIST
-    ret=$?;[[ $ret -ne 0 ]] && return $ret
-
-    GIT_URL=https://github.com/nghttp2/nghttp2.git
-    echo "Install nghttp2 from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading nghttp2"
-    if [[ $OPTION =~ [yY](es)* ]]
-    then
-      $SUDO rm -rf nghttp2
-    fi
-
-    git clone $GIT_URL
-    cd nghttp2
-    git checkout 43ba3125932c1d56addaeded2b7f62637af255cd
-    git submodule update --init
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    autoreconf -i
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    automake
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    autoconf
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    ./configure --enable-asio-lib --enable-lib-only
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    make -j $(nproc)
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO ldconfig
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    git clean -x -d -ff .
-    popd
-  fi
-  echo "nghttp2 installation complete"
-  return 0
-}
-
-#-------------------------------------------------------------------------------
-#arg1 is force (0 or 1) (no interactive script)
-#arg2 is debug (0 or 1) (install debug libraries)
-install_libyaml_cpp_from_git() {
-  # For Ubuntu 2x and RHEL we should install from repo.
-  echo "Starting to install libyaml_cpp"
-  if [ $1 -eq 0 ]; then
-    read -p "Do you want to install yaml_cpp ? <y/N> " prompt
-    OPTION=""
-  else
-    prompt='y'
-    OPTION="-y"
-  fi
-  if [ $2 -eq 0 ]; then
-    debug=0
-  else
-    debug=1
-  fi
-
-
-  if [[ $prompt =~ [yY](es)* ]]
-  then
-    GIT_URL=https://github.com/jbeder/yaml-cpp.git
-    echo "Install yaml-cpp from $GIT_URL"
-    pushd $OPENAIRCN_DIR/build/ext
-    echo "Downloading yaml-cpp"
-    if [[ $OPTION =~ -[yY](es)* ]]
-    then
-      $SUDO rm -rf yaml-cpp
-    fi
-
-    git clone $GIT_URL
-    cd yaml-cpp && git checkout master
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    mkdir _build && cd _build
-    $CMAKE -G "Unix Makefiles" -DYAML_BUILD_SHARED_LIBS=on -DYAML_CPP_BUILD_TESTS=off ..
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    make
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    $SUDO make install
-    ret=$?;[[ $ret -ne 0 ]] && popd && return $ret
-    popd
-    rm -Rf $OPENAIRCN_DIR/build/ext/yaml-cpp/_build
-  fi
-  echo "yaml-cpp installation complete"
-  return 0
-}
-
-
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.fmt
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.spdlog
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.pistache
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.nlohmann
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.nghttp2
+source $THIS_SCRIPT_PATH/../common-build/installation/build_helper.yamlcpp
 
 #-------------------------------------------------------------------------------
 #arg1 is force (0 or 1) (no interactive script)
@@ -515,6 +186,8 @@ check_install_pcf_deps() {
   install_nghttp2_from_git $1 $2
   ret=$?;[[ $ret -ne 0 ]] && return $ret
 
+  # latest usage of yaml-cpp suggests that we should be using 0.7+ version
+  # To be sure, let's install from source all the time for the moment
   if [[ $OS_DISTRO == "ubuntu" ]]; then
     case "$(get_distribution_release)" in
       "ubuntu18.04")
@@ -522,14 +195,22 @@ check_install_pcf_deps() {
         ret=$?;[[ $ret -ne 0 ]] && return $ret
         ;;
       "ubuntu20.04")
-        $SUDO $INSTALLER install $OPTION libyaml-cpp0.6 libyaml-cpp-dev
+        # definitely, 0.6.2 as PPA installed is not enough
+        install_libyaml_cpp_from_git $1 $2
+        ret=$?;[[ $ret -ne 0 ]] && return $ret
         ;;
       "ubuntu22.04")
-        $SUDO $INSTALLER install $OPTION libyaml-cpp0.7 libyaml-cpp-dev
+        # not sure about 0.7.0
+        #$SUDO $INSTALLER install $OPTION libyaml-cpp0.7 libyaml-cpp-dev
+        install_libyaml_cpp_from_git $1 $2
+        ret=$?;[[ $ret -ne 0 ]] && return $ret
         ;;
     esac
   elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then
-    $SUDO $INSTALLER install $OPTION yaml-cpp yaml-cpp-devel
+    # definitely, 0.6.2 as PPA installed is not enough
+    # $SUDO $INSTALLER install $OPTION yaml-cpp yaml-cpp-devel
+    install_libyaml_cpp_from_git $1 $2
+    ret=$?;[[ $ret -ne 0 ]] && return $ret
   fi
 
   $SUDO ldconfig
diff --git a/build/scripts/build_pcf b/build/scripts/build_pcf
index f75b66725d935b373e13998995d75e6954989e57..1ea70b09f332a3364171734b39592e6e85f6491e 100755
--- a/build/scripts/build_pcf
+++ b/build/scripts/build_pcf
@@ -32,15 +32,25 @@ THIS_SCRIPT_PATH=$(dirname $(readlink -f $0))
 
 ## initialize git submodules
 pushd ${THIS_SCRIPT_PATH}/../..
-git submodule update --init --recursive
+if [ -d .git ] && [ -f .gitmodules ]; then
+  echo "Synchronizing the Git Sub-Modules"
+  git submodule status | while read -r line ; do
+    moduleName=`echo $line | awk '{print $2}'`
+    gitReference=`echo $line | awk '{print $3}'`
+    if [[ -z "$gitReference" ]]; then
+      echo "$moduleName looks empty!"
+      git submodule update --init --recursive $moduleName
+    fi
+  done
+  git submodule --quiet foreach 'echo "${path} is synchronized or in edition"'
+fi
 popd
 
 INSTALL_DIR=/usr/local/bin
 ################################
 # include helper functions
 ################################
-source $THIS_SCRIPT_PATH/build_helper.pcf
-
+source ${THIS_SCRIPT_PATH}/build_helper.pcf
 
 function help()
 {
@@ -80,7 +90,9 @@ function main()
       -b | --build-type)
         list_include_item "Debug Release RelWithDebInfo MinSizeRel" $2
         [[ $? -ne 0 ]] && echo_error "Build type $2 not recognized" && return $?
+        echo ${cmake_args}
         cmake_args="$cmake_args -DCMAKE_BUILD_TYPE=$2"
+        echo ${cmake_args}
         list_include_item "Debug" $2
         [[ $? -ne 0 ]] && debug=1
         shift 2;
diff --git a/ci-scripts/Jenkinsfile-GitLab-Docker b/ci-scripts/Jenkinsfile-GitLab-Docker
index 488a50db31fb65d58352f36f8e6c4a90558779fb..6fc29df85401c26702e1b49aa979579dc4e5cd36 100644
--- a/ci-scripts/Jenkinsfile-GitLab-Docker
+++ b/ci-scripts/Jenkinsfile-GitLab-Docker
@@ -213,7 +213,7 @@ pipeline {
               script {
                 sh "sudo podman image prune --force"
                 sh "rm -Rf ./etc-pki-entitlement ./rhsm-conf ./rhsm-ca"
-                stash includes: 'archives/pcf_podman_image_build.log', name: 'podmanBuildLog'
+                stash allowEmpty: true, includes: 'archives/pcf_podman_image_build.log', name: 'podmanBuildLog'
               }
             }
           }
@@ -228,7 +228,8 @@ pipeline {
                   // It's a different agent from main one.
                   prepareWorkspaceMergeCase()
                   // Moving to focal and cppcheck 1.90 and a dockerfile approach
-                  sh 'docker build --target pcf-cppcheck --tag pcf-cppcheck:test --file ci-scripts/docker/Dockerfile.ci.cppcheck . > archives/cppcheck_install.log 2>&1'
+                  sh 'sed -i -e "s@nfName@pcf@" ci-scripts/common/docker/Dockerfile.ci.cppcheck'
+                  sh 'docker build --target pcf-cppcheck --tag pcf-cppcheck:test --file ci-scripts/common/docker/Dockerfile.ci.cppcheck . > archives/cppcheck_install.log 2>&1'
                   sh 'docker run --name pcf-ci-cppcheck --entrypoint /bin/true pcf-cppcheck:test'
                   sh 'docker cp pcf-ci-cppcheck:/home/cppcheck.xml archives'
                   sh 'docker cp pcf-ci-cppcheck:/home/cppcheck_build.log archives'
@@ -247,7 +248,7 @@ pipeline {
             }
             cleanup {
               script {
-                stash includes: 'archives/cppcheck*.*', name: 'cppcheckLogs'
+                stash allowEmpty: true, includes: 'archives/cppcheck*.*', name: 'cppcheckLogs'
                 // no need to keep the cppcheck container
                 sh 'docker rm -f pcf-ci-cppcheck || true'
                 sh 'docker rmi pcf-cppcheck:test || true'
@@ -264,10 +265,11 @@ pipeline {
                 gitlabCommitStatus(name: "Code Formatting Checker") {
                   // It's a different agent from main one.
                   prepareWorkspaceMergeCase()
+                  sh 'sed -i -e "s@nfName@pcf@" ci-scripts/common/docker/Dockerfile.ci.clang-format'
                   if ("MERGE".equals(env.gitlabActionType)) {
-                    sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/docker/Dockerfile.ci.clang-format --build-arg MERGE_REQUEST_CHECK=True --build-arg SOURCE_BRANCH=' + env.gitlabSourceBranch + ' --build-arg TARGET_BRANCH=' + env.gitlabTargetBranch + ' . > archives/clang_format_install.log 2>&1'
+                    sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/common/docker/Dockerfile.ci.clang-format --build-arg MERGE_REQUEST_CHECK=True --build-arg SOURCE_BRANCH=' + env.gitlabSourceBranch + ' --build-arg TARGET_BRANCH=' + env.gitlabTargetBranch + ' . > archives/clang_format_install.log 2>&1'
                   } else {
-                    sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/docker/Dockerfile.ci.clang-format . > archives/clang_format_install.log 2>&1'
+                    sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/common/docker/Dockerfile.ci.clang-format . > archives/clang_format_install.log 2>&1'
                   }
                   sh 'docker run --name pcf-ci-clang-format --entrypoint /bin/true pcf-clang-format-check:test'
                   sh 'docker cp pcf-ci-clang-format:/home/src/oai_rules_result.txt src'
@@ -283,7 +285,7 @@ pipeline {
           post {
             cleanup {
               script {
-                stash includes: 'src/oai_rules_result*.txt', name: 'formatCheckLogs'
+                stash allowEmpty: true, includes: 'src/oai_rules_result*.txt, archives/clang_format_install.log', name: 'formatCheckLogs'
                 sh 'docker rm -f pcf-ci-clang-format || true'
                 sh 'docker rmi pcf-clang-format-check:test || true'
               }
@@ -317,8 +319,7 @@ pipeline {
                 if (localStatus.resultIsBetterOrEqualTo('SUCCESS')) {
                   echo "Tutorials Test Job is OK"
                 } else {
-                  echo "Tutorials Test Job is KO"
-                  sh "ci-scripts/fail.sh"
+                  error "Tutorials Test Job is KO"
                 }
               }
             }
@@ -389,9 +390,9 @@ pipeline {
 
         // Generating the HTML report(s)
         if ("MERGE".equals(env.gitlabActionType)) {
-          sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${env.gitlabSourceBranch} --git_src_commit=${env.gitlabMergeRequestLastCommit} --git_pull_request=True --git_target_branch=${env.gitlabTargetBranch} --git_target_commit=${GIT_COMMIT}"
+          sh "./ci-scripts/generateHtmlReport.py --job-name ${JOB_NAME} --build-id ${BUILD_ID} --build-url ${BUILD_URL} --git-url ${GIT_URL} --git-src-branch ${env.gitlabSourceBranch} --git-src-commit ${env.gitlabMergeRequestLastCommit} --git-merge-request --git-dst-branch ${env.gitlabTargetBranch} --git-dst-commit ${GIT_COMMIT}"
         } else {
-          sh "python3 ci-scripts/generateHtmlReport.py --job_name=${JOB_NAME} --job_id=${BUILD_ID} --job_url=${BUILD_URL} --git_url=${GIT_URL} --git_src_branch=${GIT_BRANCH} --git_src_commit=${GIT_COMMIT}"
+          sh "./ci-scripts/generateHtmlReport.py --job-name ${JOB_NAME} --build-id ${BUILD_ID} --build-url ${BUILD_URL} --git-url ${GIT_URL} --git-src-branch ${GIT_BRANCH} --git-src-commit ${GIT_COMMIT}"
         }
         listOfFiles = sh returnStdout: true, script: 'ls test_results*.html'
         String[] htmlFiles = listOfFiles.split("\\n")
@@ -427,6 +428,8 @@ OAI CI Team''',
 
 def prepareWorkspaceMergeCase () {
   sh "git clean -x -d -f > /dev/null 2>&1"
+  sh "git submodule foreach --recursive 'git clean -x -d -ff' > /dev/null 2>&1"
+  sh "git submodule deinit --force --all > /dev/null 2>&1"
   if ("MERGE".equals(env.gitlabActionType)) {
     sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
   }
diff --git a/ci-scripts/checkCodingFormattingRules.sh b/ci-scripts/checkCodingFormattingRules.sh
deleted file mode 100755
index 2e0d0d496861d0a53197129b3d410071288b93f9..0000000000000000000000000000000000000000
--- a/ci-scripts/checkCodingFormattingRules.sh
+++ /dev/null
@@ -1,208 +0,0 @@
-#!/bin/bash
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-
-function usage {
-    echo "OAI Coding / Formatting Guideline Check script"
-    echo "   Original Author: Raphael Defosseux"
-    echo ""
-    echo "   Requirement: clang-format / git shall be installed"
-    echo ""
-    echo "   By default (no options) the complete repository will be checked"
-    echo "   In case of merge/pull request, provided source and target branch,"
-    echo "   the script will check only the modified files"
-    echo ""
-    echo "Usage:"
-    echo "------"
-    echo "    checkCodingFormattingRules.sh [OPTIONS]"
-    echo ""
-    echo "Options:"
-    echo "--------"
-    echo "    --src-branch #### OR -sb ####"
-    echo "    Specify the source branch of the merge request."
-    echo ""
-    echo "    --target-branch #### OR -tb ####"
-    echo "    Specify the target branch of the merge request (usually develop)."
-    echo ""
-    echo "    --help OR -h"
-    echo "    Print this help message."
-    echo ""
-}
-
-if [ $# -ne 4 ] && [ $# -ne 1 ] && [ $# -ne 0 ]
-then
-    echo "Syntax Error: not the correct number of arguments"
-    echo ""
-    usage
-    exit 1
-fi
-
-cd src
-
-if [ $# -eq 0 ]
-then
-    echo " ---- Checking the whole repository ----"
-    echo ""
-    if [ -f oai_rules_result.txt ]
-    then
-        rm -f oai_rules_result.txt
-    fi
-    if [ -f oai_rules_result_list.txt ]
-    then
-        rm -f oai_rules_result_list.txt
-    fi
-    EXTENSION_LIST=("h" "hpp" "c" "cpp")
-    NB_TO_FORMAT=0
-    NB_TOTAL=0
-    for EXTENSION in ${EXTENSION_LIST[@]}
-    do
-        echo "Checking for all files with .${EXTENSION} extension"
-        FILE_LIST=`tree -n --noreport -i -f -P *.${EXTENSION} | sed -e 's#^\./##' | grep "\.${EXTENSION}"`
-        for FILE_TO_CHECK in ${FILE_LIST[@]}
-        do
-            TO_FORMAT=`clang-format -output-replacements-xml ${FILE_TO_CHECK} 2>&1 | grep -v replacements | grep -c replacement`
-            NB_TOTAL=$((NB_TOTAL + 1))
-            if [ $TO_FORMAT -ne 0 ]
-            then
-                NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
-                # In case of full repo, being silent
-                #echo "src/$FILE_TO_CHECK"
-                echo "src/$FILE_TO_CHECK" >> ./oai_rules_result_list.txt
-            fi
-        done
-    done
-    echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
-    echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
-    echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
-    exit 0
-fi
-
-checker=0
-while [[ $# -gt 0 ]]
-do
-key="$1"
-
-case $key in
-    -h|--help)
-    shift
-    usage
-    exit 0
-    ;;
-    -sb|--src-branch)
-    SOURCE_BRANCH="$2"
-    let "checker|=0x1"
-    shift
-    shift
-    ;;
-    -tb|--target-branch)
-    TARGET_BRANCH="$2"
-    let "checker|=0x2"
-    shift
-    shift
-    ;;
-    *)
-    echo "Syntax Error: unknown option: $key"
-    echo ""
-    usage
-    exit 1
-esac
-
-done
-
-
-if [ $checker -ne 3 ]
-then
-    echo "Source Branch is    : $SOURCE_BRANCH"
-    echo "Target Branch is    : $TARGET_BRANCH"
-    echo ""
-    echo "Syntax Error: missing option"
-    echo ""
-    usage
-    exit 1
-fi
-
-# When running in a container, in /home folder
-IS_CONTAINER=`egrep -c "docker|kubepods|podman|buildah|libpod" /proc/self/cgroup || true`
-if [ $IS_CONTAINER -ne 0 ]
-then
-    if [ $PWD = "/home/src" ]
-    then
-       git config --global --add safe.directory /home
-    fi
-fi
-
-# Merge request scenario
-
-MERGE_COMMMIT=`git log -n1 --pretty=format:%H`
-if [ -f .git/refs/remotes/origin/$TARGET_BRANCH ]
-then
-    TARGET_INIT_COMMIT=`cat .git/refs/remotes/origin/$TARGET_BRANCH`
-else
-    TARGET_INIT_COMMIT=`git log -n1 --pretty=format:%H origin/$TARGET_BRANCH`
-fi
-
-echo " ---- Checking the modified files by the merge request ----"
-echo ""
-echo "Source Branch is    : $SOURCE_BRANCH"
-echo "Target Branch is    : $TARGET_BRANCH"
-echo "Merged Commit is    : $MERGE_COMMMIT"
-echo "Target Init   is    : $TARGET_INIT_COMMIT"
-echo ""
-echo " ----------------------------------------------------------"
-echo ""
-
-# Retrieve the list of modified files since the latest develop commit
-MODIFIED_FILES=`git log $TARGET_INIT_COMMIT..$MERGE_COMMMIT --oneline --name-status | egrep "^M|^A" | sed -e "s@^M\t*@@" -e "s@^A\t*@@" | sort | uniq`
-NB_TO_FORMAT=0
-NB_TOTAL=0
-
-if [ -f oai_rules_result.txt ]
-then
-    rm -f oai_rules_result.txt
-fi
-if [ -f oai_rules_result_list.txt ]
-then
-    rm -f oai_rules_result_list.txt
-fi
-for FULLFILE in $MODIFIED_FILES
-do
-    filename=$(basename -- "$FULLFILE")
-    EXT="${filename##*.}"
-    if [ $EXT = "c" ] || [ $EXT = "h" ] || [ $EXT = "cpp" ] || [ $EXT = "hpp" ]
-    then
-        SRC_FILE=`echo $FULLFILE | sed -e "s#src/##"`
-        TO_FORMAT=`clang-format -output-replacements-xml ${SRC_FILE} 2>&1 | grep -v replacements | grep -c replacement`
-        NB_TOTAL=$((NB_TOTAL + 1))
-        if [ $TO_FORMAT -ne 0 ]
-        then
-            NB_TO_FORMAT=$((NB_TO_FORMAT + 1))
-            echo $FULLFILE
-            echo $FULLFILE >> ./oai_rules_result_list.txt
-        fi
-    fi
-done
-echo ""
-echo " ----------------------------------------------------------"
-echo "Nb Files that do NOT follow OAI rules: $NB_TO_FORMAT over $NB_TOTAL checked!"
-echo "NB_FILES_FAILING_CHECK=$NB_TO_FORMAT" > ./oai_rules_result.txt
-echo "NB_FILES_CHECKED=$NB_TOTAL" >> ./oai_rules_result.txt
-
-if [ $NB_TO_FORMAT -ne 0 ]; then exit -1; else exit 0; fi
diff --git a/ci-scripts/common b/ci-scripts/common
new file mode 160000
index 0000000000000000000000000000000000000000..b9ca1e67d3e4b4fee5075d90e98015698ab1f0a1
--- /dev/null
+++ b/ci-scripts/common
@@ -0,0 +1 @@
+Subproject commit b9ca1e67d3e4b4fee5075d90e98015698ab1f0a1
diff --git a/ci-scripts/common-ci b/ci-scripts/common-ci
deleted file mode 160000
index c458e9dd6a5cbce1306680d4289012b8f493ff85..0000000000000000000000000000000000000000
--- a/ci-scripts/common-ci
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c458e9dd6a5cbce1306680d4289012b8f493ff85
diff --git a/ci-scripts/docker/Dockerfile.ci.clang-format b/ci-scripts/docker/Dockerfile.ci.clang-format
deleted file mode 100644
index a99a9dca38dead38b66e0a9422cc8119d0249d2d..0000000000000000000000000000000000000000
--- a/ci-scripts/docker/Dockerfile.ci.clang-format
+++ /dev/null
@@ -1,38 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-#
-FROM ubuntu:focal as pcf-clang-format-check
-ARG MERGE_REQUEST_CHECK
-ARG SOURCE_BRANCH
-ARG TARGET_BRANCH
-
-RUN apt-get update && \
-    apt-get upgrade -y && \
-    apt-get install --yes \
-      git \
-      tree \
-      clang-format-9
-
-WORKDIR /home
-COPY . .
-
-RUN /bin/bash -c "if [[ -v MERGE_REQUEST_CHECK ]]; then ./ci-scripts/checkCodingFormattingRules.sh --src-branch $SOURCE_BRANCH --target-branch $TARGET_BRANCH; else ./ci-scripts/checkCodingFormattingRules.sh; fi"
diff --git a/ci-scripts/docker/Dockerfile.ci.cppcheck b/ci-scripts/docker/Dockerfile.ci.cppcheck
deleted file mode 100644
index 84fc920f772e4e78c23433aedbf38c12fb59adc1..0000000000000000000000000000000000000000
--- a/ci-scripts/docker/Dockerfile.ci.cppcheck
+++ /dev/null
@@ -1,36 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-#
-FROM ubuntu:focal as pcf-cppcheck
-
-RUN apt-get update && \
-    apt-get upgrade -y && \
-    apt-get install --yes cppcheck
-
-WORKDIR /home
-COPY . .
-
-RUN cppcheck --enable=warning --force \
-      --xml --xml-version=2 \
-      --suppressions-list=ci-scripts/cppcheck_suppressions.list src \
-      2> cppcheck.xml \
-      1> cppcheck_build.log
diff --git a/ci-scripts/docker/Dockerfile.ci.ubuntu b/ci-scripts/docker/Dockerfile.ci.ubuntu
deleted file mode 100644
index c1b4d47eec831cd9854c36412d27cb197a735b6f..0000000000000000000000000000000000000000
--- a/ci-scripts/docker/Dockerfile.ci.ubuntu
+++ /dev/null
@@ -1,123 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-#
-# Dockerfile for the Open-Air-Interface PCF service
-#   Valid for Ubuntu-18.04 (bionic)
-#
-#---------------------------------------------------------------------
-
-#---------------------------------------------------------------------
-# BUILDER IMAGE
-#---------------------------------------------------------------------
-ARG BASE_IMAGE=ubuntu:bionic
-FROM oai-pcf-base:latest as oai-pcf-builder
-
-RUN rm -Rf /openair-pcf
-# Copying source code
-WORKDIR /openair-pcf
-COPY . /openair-pcf
-
-# Building PCF
-WORKDIR /openair-pcf/build/scripts
-RUN ldconfig && \
-    cp -Rf /openair-pcf-ext-ref /openair-pcf/build/ext && \
-    ./build_pcf --clean --Verbose --build-type Release --jobs && \
-    ldd /openair-pcf/build/pcf/build/pcf && \
-    mv /openair-pcf/build/pcf/build/pcf /openair-pcf/build/pcf/build/oai_pcf
-
-#---------------------------------------------------------------------
-# TARGET IMAGE
-#---------------------------------------------------------------------
-FROM $BASE_IMAGE as oai-pcf
-ENV DEBIAN_FRONTEND=noninteractive
-ENV TZ=Europe/Paris
-# We install some debug tools for the moment in addition of mandatory libraries
-RUN apt-get update && \
-    DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
-    DEBIAN_FRONTEND=noninteractive apt-get install --yes \
-      python3 \
-      python3-jinja2 \
-      psmisc \
-      net-tools \
-      tzdata \
-      bc \
-      openssl \
-# Ubuntu 18 --> libasan4
-# Ubuntu 20 --> libasan5
-# Ubuntu 22 --> libasan6
-#      libasan? \
-# Ubuntu 18 --> boost62/65 -> will remove both
-# Ubuntu 20 --> boost67/71 -> will remove 67
-# Ubuntu 22 --> boost74
-      libboost-filesystem1.??.0 \
-      libcurl?-gnutls \
-      librtmp1 \
-      libpsl5 \
-# Ubuntu 18 --> won't install anything
-# Ubuntu 20 --> libyaml-cpp 0.6
-# Ubuntu 22 --> libyaml-cpp 0.7
-      libyaml-cpp0.? \
-  && rm -rf /var/lib/apt/lists/* \
-       /lib/x86_64-linux-gnu/libboost_system.so.1.6*.0 \
-       /lib/x86_64-linux-gnu/ibboost_filesystem.so.1.6*.0
-
-# Copying executable and generated libraries
-WORKDIR /openair-pcf/bin
-COPY --from=oai-pcf-builder \
-    /openair-pcf/build/pcf/build/oai_pcf \
-    /openair-pcf/scripts/healthcheck.sh \
-    ./
-
-WORKDIR /usr/local/lib/
-COPY --from=oai-pcf-builder \
-# Copying only the packages built from source
-    /usr/local/lib/libpistache.so \
-    /usr/local/lib/libnghttp2.so.14 \
-    /usr/local/lib/libnghttp2_asio.so.1 \
-    /usr/local/lib/libyaml-cpp.so.0.? \
-    /usr/local/lib/libspdlog.so \
-    /usr/local/lib/libfmt.so \
-# Ubuntu 18 --> boost 67 will be copied
-    /usr/lib/libboost_system.so.1.* \
-    /usr/lib/libboost_filesystem.so.1.* \
-    ./
-
-RUN ldconfig && \
-    ldd /openair-pcf/bin/oai_pcf
-
-# Copying template configuration files
-# The configuration folder will be flat
-WORKDIR /openair-pcf/etc
-COPY --from=oai-pcf-builder \
-    /openair-pcf/etc/config.yaml \
-    ./
-
-WORKDIR /openair-pcf
-
-EXPOSE 80/tcp 8080/tcp
-# healthcheck
-HEALTHCHECK --interval=10s \
-            --timeout=15s \
-            --retries=6 \
-    CMD /openair-pcf/bin/healthcheck.sh
-
-CMD ["/openair-pcf/bin/oai_pcf", "-c", "/openair-pcf/etc/config.yaml", "-o"]
diff --git a/ci-scripts/generateHtmlReport.py b/ci-scripts/generateHtmlReport.py
old mode 100644
new mode 100755
index 72badb995373726d68a084a40ef019271cbd821a..a268b04d3cc99b9c4e520dba8087b3bb3d487654
--- a/ci-scripts/generateHtmlReport.py
+++ b/ci-scripts/generateHtmlReport.py
@@ -1,940 +1,89 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *	  http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *	  contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-
-import glob
+#!/usr/bin/env python3
+"""
+Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The OpenAirInterface Software Alliance licenses this file to You under
+the OAI Public License, Version 1.1  (the "License"); you may not use this file
+except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.openairinterface.org/?page_id=698
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+------------------------------------------------------------------------------
+For more information about the OpenAirInterface (OAI) Software Alliance:
+  contact@openairinterface.org
+---------------------------------------------------------------------
+"""
+
+#import argparse
 import os
 import re
-import sys
-import subprocess
-
-class HtmlReport():
-	def __init__(self):
-		self.job_name = ''
-		self.job_id = ''
-		self.job_url = ''
-		self.job_start_time = 'TEMPLATE_TIME'
-		self.git_url = ''
-		self.git_src_branch = ''
-		self.git_src_commit = ''
-		self.git_src_commit_msg = None
-		self.git_pull_request = False
-		self.git_target_branch = ''
-		self.git_target_commit = ''
-		self.nb_warnings = 0
-		self.warning_rows = ''
-
-	def generate(self):
-		cwd = os.getcwd()
-		self.file = open(cwd + '/test_results_oai_pcf.html', 'w')
-		self.generateHeader()
-
-		self.coding_formatting_log()
-
-		self.analyze_sca_log()
-
-		self.buildSummaryHeader()
-		self.initialGitSetup()
-		self.installLibsPackagesRow()
-		self.buildCompileRows()
-		self.copyToTargetImage()
-		self.copyConfToolsToTargetImage()
-		self.imageSizeRow()
-		self.buildSummaryFooter()
-
-		self.generateFooter()
-		self.file.close()
-
-	def generateHeader(self):
-		# HTML Header
-		self.file.write('<!DOCTYPE html>\n')
-		self.file.write('<html class="no-js" lang="en-US">\n')
-		self.file.write('<head>\n')
-		self.file.write('  <meta name="viewport" content="width=device-width, initial-scale=1">\n')
-		self.file.write('  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">\n')
-		self.file.write('  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>\n')
-		self.file.write('  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>\n')
-		self.file.write('  <title>OAI Core Network Test Results for ' + self.job_name + ' job build #' + self.job_id + '</title>\n')
-		self.file.write('</head>\n')
-		self.file.write('<body><div class="container">\n')
-		self.file.write('  <table width = "100%" style="border-collapse: collapse; border: none;">\n')
-		self.file.write('   <tr style="border-collapse: collapse; border: none;">\n')
-		self.file.write('     <td style="border-collapse: collapse; border: none;">\n')
-		self.file.write('       <a href="http://www.openairinterface.org/">\n')
-		self.file.write('          <img src="http://www.openairinterface.org/wp-content/uploads/2016/03/cropped-oai_final_logo2.png" alt="" border="none" height=50 width=150>\n')
-		self.file.write('          </img>\n')
-		self.file.write('        </a>\n')
-		self.file.write('      </td>\n')
-		self.file.write('      <td style="border-collapse: collapse; border: none; vertical-align: center;">\n')
-		self.file.write('        <b><font size = "6">Job Summary -- Job: ' + self.job_name + ' -- Build-ID: <a href="' + self.job_url + '">' + self.job_id + '</a></font></b>\n')
-		self.file.write('      </td>\n')
-		self.file.write('    </tr>\n')
-		self.file.write('  </table>\n')
-		self.file.write('  <br>\n')
-
-		# Build Info Summary
-		buildSummary = ''
-		buildSummary += '  <table class="table-bordered" width = "80%" align = "center" border = "1">\n'
-		buildSummary += '    <tr>\n'
-		buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-time"></span> Build Start Time</td>\n'
-		#date_formatted = re.sub('\..*', '', self.created)
-		buildSummary += '      <td>' + self.job_start_time + '</td>\n'
-		buildSummary += '    </tr>\n'
-		buildSummary += '    <tr>\n'
-		buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-wrench"></span> Build Trigger</td>\n'
-		if self.git_pull_request:
-			buildSummary += '      <td>Pull Request</td>\n'
-		else:
-			buildSummary += '      <td>Push Event</td>\n'
-		buildSummary += '    </tr>\n'
-		buildSummary += '    <tr>\n'
-		buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-cloud-upload"></span> GIT Repository</td>\n'
-		buildSummary += '      <td><a href="' + self.git_url + '">' + self.git_url + '</a></td>\n'
-		buildSummary += '    </tr>\n'
-		if self.git_pull_request:
-			buildSummary += '	 <tr>\n'
-			buildSummary += '	   <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-log-out"></span> Merge Request URL</td>\n'
-			buildSummary += '	   <td><a href="TEMPLATE_MERGE_REQUEST_LINK">TEMPLATE_MERGE_REQUEST_LINK</a></td>\n'
-			buildSummary += '	 </tr>\n'
-			buildSummary += '	 <tr>\n'
-			buildSummary += '	   <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-header"></span> Merge Request Title</td>\n'
-			buildSummary += '	   <td>TEMPLATE_MERGE_REQUEST_TEMPLATE</td>\n'
-			buildSummary += '	 </tr>\n'
-			buildSummary += '	 <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-log-out"></span> Source Branch</td>\n'
-			buildSummary += '      <td>' + self.git_src_branch + '</td>\n'
-			buildSummary += '    </tr>\n'
-			buildSummary += '    <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-tag"></span> Source Commit ID</td>\n'
-			buildSummary += '      <td>' + self.git_src_commit + '</td>\n'
-			buildSummary += '    </tr>\n'
-			if (self.git_src_commit_msg is not None):
-				buildSummary += '    <tr>\n'
-				buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-comment"></span> Source Commit Message</td>\n'
-				buildSummary += '      <td>' + self.git_src_commit_msg + '</td>\n'
-				buildSummary += '    </tr>\n'
-			buildSummary += '    <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-log-in"></span> Target Branch</td>\n'
-			buildSummary += '      <td>' + self.git_target_branch + '</td>\n'
-			buildSummary += '    </tr>\n'
-			buildSummary += '    <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-tag"></span> Target Commit ID</td>\n'
-			buildSummary += '      <td>' + self.git_target_commit + '</td>\n'
-			buildSummary += '    </tr>\n'
-		else:
-			buildSummary += '    <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-tree-deciduous"></span> Branch</td>\n'
-			buildSummary += '      <td>' + self.git_src_branch + '</td>\n'
-			buildSummary += '    </tr>\n'
-			buildSummary += '    <tr>\n'
-			buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-tag"></span> Commit ID</td>\n'
-			buildSummary += '      <td>' + self.git_src_commit + '</td>\n'
-			buildSummary += '    </tr>\n'
-			if (self.git_src_commit_msg is not None):
-				buildSummary += '    <tr>\n'
-				buildSummary += '      <td bgcolor="lightcyan" > <span class="glyphicon glyphicon-comment"></span> Commit Message</td>\n'
-				buildSummary += '      <td>' + self.git_src_commit_msg + '</td>\n'
-				buildSummary += '    </tr>\n'
-		buildSummary += '  </table>\n'
-		buildSummary += '  <br>\n'
-		self.file.write(buildSummary)
-
-		cwd = os.getcwd()
-		for reportFile in glob.glob('./*results_oai_*.html'):
-			if reportFile == './test_results_oai_pcf.html':
-				continue
-			newEpcReport = open(cwd + '/' + str(reportFile) + '.new', 'w')
-			buildSummaryDone = True
-			with open(cwd + '/' + str(reportFile), 'r') as originalEpcReport:
-				for line in originalEpcReport:
-					result = re.search('Deployment Summary', line)
-					if (result is not None) and buildSummaryDone:
-						newEpcReport.write(buildSummary)
-						buildSummaryDone = False
-					newEpcReport.write(line)
-				originalEpcReport.close()
-			newEpcReport.close()
-			os.rename(cwd + '/' + str(reportFile) + '.new', cwd + '/' + str(reportFile))
-
-	def generateFooter(self):
-		self.file.write('  <div class="well well-lg">End of Build Report -- Copyright <span class="glyphicon glyphicon-copyright-mark"></span> 2020 <a href="http://www.openairinterface.org/">OpenAirInterface</a>. All Rights Reserved.</div>\n')
-		self.file.write('</div></body>\n')
-		self.file.write('</html>\n')
-
-	def coding_formatting_log(self):
-		cwd = os.getcwd()
-		self.file.write('  <h2>OAI Coding / Formatting Guidelines Check</h2>\n')
-		if os.path.isfile(cwd + '/src/oai_rules_result.txt'):
-			cmd = 'grep NB_FILES_FAILING_CHECK ' + cwd + '/src/oai_rules_result.txt | sed -e "s#NB_FILES_FAILING_CHECK=##"'
-			nb_fail = subprocess.check_output(cmd, shell=True, universal_newlines=True)
-			cmd = 'grep NB_FILES_CHECKED ' + cwd + '/src/oai_rules_result.txt | sed -e "s#NB_FILES_CHECKED=##"'
-			nb_total = subprocess.check_output(cmd, shell=True, universal_newlines=True)
-			if int(nb_fail.strip()) == 0:
-				self.file.write('  <div class="alert alert-success">\n')
-				if self.git_pull_request:
-					self.file.write('    <strong>All modified files in Pull-Request follow OAI rules. <span class="glyphicon glyphicon-ok-circle"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
-				else:
-					self.file.write('    <strong>All files in repository follow OAI rules. <span class="glyphicon glyphicon-ok-circle"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
-				self.file.write('  </div>\n')
-			else:
-				self.file.write('  <div class="alert alert-danger">\n')
-				if self.git_pull_request:
-					self.file.write('    <strong>' + nb_fail.strip() + ' modified files in Pull-Request DO NOT follow OAI rules. <span class="glyphicon glyphicon-warning-sign"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
-				else:
-					self.file.write('    <strong>' + nb_fail.strip() + ' files in repository DO NOT follow OAI rules. <span class="glyphicon glyphicon-warning-sign"></span> -> (' + nb_total.strip() + ' were checked)</strong>\n')
-				self.file.write('  </div>\n')
-
-				if os.path.isfile(cwd + '/src/oai_rules_result_list.txt'):
-					self.file.write('  <button data-toggle="collapse" data-target="#oai-formatting-details">More details on formatting check</button>\n')
-					self.file.write('  <div id="oai-formatting-details" class="collapse">\n')
-					self.file.write('  <p>Please apply the following command to this(ese) file(s): </p>\n')
-					self.file.write('  <p style="margin-left: 30px"><strong><code>cd src && clang-format -i filename(s)</code></strong></p>\n')
-					self.file.write('  <table class="table-bordered" width = "60%" align = "center" border = 1>\n')
-					self.file.write('    <tr><th bgcolor = "lightcyan" >Filename</th></tr>\n')
-					with open(cwd + '/src/oai_rules_result_list.txt', 'r') as filelist:
-						for line in filelist:
-							self.file.write('    <tr><td>' + line.strip() + '</td></tr>\n')
-						filelist.close()
-					self.file.write('  </table>\n')
-					self.file.write('  </div>\n')
-		else:
-			self.file.write('  <div class="alert alert-danger">\n')
-			self.file.write('     <strong>Was NOT performed (with CLANG-FORMAT tool). <span class="glyphicon glyphicon-ban-circle"></span></strong>\n')
-			self.file.write('  </div>\n')
-
-		self.file.write('  <br>\n')
-
-	def analyze_sca_log(self):
-		cwd = os.getcwd()
-		if os.path.isfile(cwd + '/archives/cppcheck_build.log'):
-			self.file.write('  <h2>Static Code Analysis</h2>\n')
-		if os.path.isfile(cwd + '/archives/cppcheck.xml'):
-			nb_errors = 0
-			nb_warnings = 0
-			nb_uninitvar = 0
-			nb_uninitStructMember = 0
-			nb_memleak = 0
-			nb_doubleFree = 0
-			nb_resourceLeak = 0
-			nb_nullPointer = 0
-			nb_arrayIndexOutOfBounds = 0
-			nb_bufferAccessOutOfBounds = 0
-			nb_unknownEvaluationOrder = 0
-			with open(cwd + '/archives/cppcheck.xml', 'r') as xmlfile:
-				for line in xmlfile:
-					result = re.search('severity="warning"', line)
-					if result is not None:
-						nb_warnings += 1
-					result = re.search('severity="error"', line)
-					if result is not None:
-						nb_errors += 1
-						result = re.search('uninitvar', line)
-						if result is not None:
-							nb_uninitvar += 1
-						result = re.search('uninitStructMember', line)
-						if result is not None:
-							nb_uninitStructMember += 1
-						result = re.search('memleak', line)
-						if result is not None:
-							nb_memleak += 1
-						result = re.search('doubleFree', line)
-						if result is not None:
-							nb_doubleFree += 1
-						result = re.search('resourceLeak', line)
-						if result is not None:
-							nb_resourceLeak += 1
-						result = re.search('nullPointer', line)
-						if result is not None:
-							nb_nullPointer += 1
-						result = re.search('arrayIndexOutOfBounds', line)
-						if result is not None:
-							nb_arrayIndexOutOfBounds += 1
-						result = re.search('bufferAccessOutOfBounds', line)
-						if result is not None:
-							nb_bufferAccessOutOfBounds += 1
-						result = re.search('unknownEvaluationOrder', line)
-						if result is not None:
-							nb_unknownEvaluationOrder += 1
-				xmlfile.close()
-			if (nb_errors == 0) and (nb_warnings == 0):
-				self.file.write('   <div class="alert alert-success">\n')
-				self.file.write('     <strong>CPPCHECK found NO error and NO warning <span class="glyphicon glyphicon-ok-circle"></span></strong>\n')
-				self.file.write('   </div>\n')
-			elif (nb_errors == 0):
-				self.file.write('   <div class="alert alert-warning">\n')
-				self.file.write('     <strong>CPPCHECK found NO error and ' + str(nb_warnings) + ' warnings <span class="glyphicon glyphicon-warning-sign"></span></strong>\n')
-				self.file.write('   </div>\n')
-			else:
-				self.file.write('   <div class="alert alert-danger">\n')
-				self.file.write('     <strong>CPPCHECK found ' +  str(nb_errors) + ' errors and ' + str(nb_warnings) + ' warnings <span class="glyphicon glyphicon-ban-circle"></span></strong>\n')
-				self.file.write('   </div>\n')
-			if (nb_errors > 0) or (nb_warnings > 0):
-				self.file.write('   <button data-toggle="collapse" data-target="#oai-cppcheck-details">More details on CPPCHECK results</button>\n')
-				self.file.write('   <div id="oai-cppcheck-details" class="collapse">\n')
-				self.file.write('   <br>\n')
-				self.file.write('   <table class="table-bordered" width = "80%" align = "center" border = "1">\n')
-				self.file.write('     <tr bgcolor = "#33CCFF" >\n')
-				self.file.write('   	<th>Error / Warning Type</th>\n')
-				self.file.write('   	<th>Nb Errors</th>\n')
-				self.file.write('   	<th>Nb Warnings</th>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('   	<td>Uninitialized variable</td>\n')
-				self.file.write('   	<td>' + str(nb_uninitvar) + '</td>\n')
-				self.file.write('   	<td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Uninitialized struct member</td>\n')
-				self.file.write('       <td>' + str(nb_uninitStructMember) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Memory leak</td>\n')
-				self.file.write('       <td>' + str(nb_memleak) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Memory is freed twice</td>\n')
-				self.file.write('       <td>' + str(nb_doubleFree) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Resource leak</td>\n')
-				self.file.write('       <td>' + str(nb_resourceLeak) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Possible null pointer dereference</td>\n')
-				self.file.write('       <td>' + str(nb_nullPointer) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Array access  out of bounds</td>\n')
-				self.file.write('       <td>' + str(nb_arrayIndexOutOfBounds) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('   	<td>Buffer is accessed out of bounds</td>\n')
-				self.file.write('   	<td>' + str(nb_bufferAccessOutOfBounds) + '</td>\n')
-				self.file.write('   	<td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Expression depends on order of evaluation of side effects</td>\n')
-				self.file.write('       <td>' + str(nb_unknownEvaluationOrder) + '</td>\n')
-				self.file.write('       <td>N/A</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr>\n')
-				self.file.write('       <td>Others</td>\n')
-				nb_others = nb_uninitvar + nb_uninitStructMember + nb_memleak + nb_doubleFree + nb_resourceLeak + nb_nullPointer + nb_arrayIndexOutOfBounds + nb_arrayIndexOutOfBounds + nb_bufferAccessOutOfBounds + nb_unknownEvaluationOrder
-				nb_others = nb_errors - nb_others
-				self.file.write('   	<td>' + str(nb_others) + '</td>\n')
-				self.file.write('   	<td>' + str(nb_warnings) + '</td>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('     <tr bgcolor = "#33CCFF" >\n')
-				self.file.write('   	<th>Total</th>\n')
-				self.file.write('   	<th>' + str(nb_errors) + '</th>\n')
-				self.file.write('   	<th>' + str(nb_warnings) + '</th>\n')
-				self.file.write('     </tr>\n')
-				self.file.write('   </table>\n')
-				self.file.write('   <br>\n')
-				self.file.write('   <p>Full details in artifact (cppcheck.xml) </p>\n')
-				self.file.write('   <p style="margin-left: 30px">Graphical Interface tool : <strong><code>cppcheck-gui -l cppcheck.xml</code></strong></p>\n')
-				self.file.write('   <br>\n')
-				self.file.write('   </div>\n')
-		else:
-			self.file.write('  <div class="alert alert-danger">\n')
-			self.file.write('     <strong>Was NOT performed (with CPPCHECK tool). <span class="glyphicon glyphicon-ban-circle"></span></strong>\n')
-			self.file.write('  </div>\n')
-
-	def buildSummaryHeader(self):
-		self.file.write('  <h2>Docker / Podman Image Build Summary</h2>\n')
-		self.file.write('  <table class="table-bordered" width = "100%" align = "center" border = "1">\n')
-		self.file.write('     <tr bgcolor="#33CCFF" >\n')
-		self.file.write('   	<th>Stage Name</th>\n')
-		self.file.write('   	<th>Image Kind</th>\n')
-		self.file.write('		<th>OAI PCF <font color="Gold">Ubuntu18</font> Image</th>\n')
-		self.file.write('		<th>OAI PCF <font color="Gold">RHEL8</font> Image</th>\n')
-		self.file.write('     </tr>\n')
-
-	def buildSummaryFooter(self):
-		self.file.write('  </table>\n')
-		self.file.write('  <br>\n')
-		if self.nb_warnings > 0:
-			self.file.write('  <h3>Compilation Warnings Details</h3>\n')
-			self.file.write('  <button data-toggle="collapse" data-target="#oai-compilation-details">Details for Compilation Errors and Warnings </button>\n')
-			self.file.write('  <div id="oai-compilation-details" class="collapse">\n')
-			self.file.write('  <table class="table-bordered">\n')
-			self.file.write('    <tr bgcolor = "#33CCFF" >\n')
-			self.file.write('      <th>File</th>\n')
-			self.file.write('      <th>Line Number</th>\n')
-			self.file.write('      <th>Status</th>\n')
-			self.file.write('      <th>Message</th>\n')
-			self.file.write(self.warning_rows)
-			self.file.write('    </tr>\n')
-			self.file.write('  </table>\n')
-			self.file.write('  </div>\n')
-
-	def initialGitSetup(self):
-		self.file.write('    <tr>\n')
-		self.file.write('      <td bgcolor="lightcyan" >Initial Git Setup</td>\n')
-		self.analyze_docker_build_git_part('PCF')
-		self.file.write('	 </tr>\n')
-
-	def analyze_docker_build_git_part(self, nfType):
-		if nfType != 'PCF':
-			self.file.write('      <td>N/A</td>\n')
-			self.file.write('      <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		self.file.write('      <td>Builder Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				status = False
-				section_start_pattern = 'git config --global http'
-				section_end_pattern = 'WORKDIR /openair-pcf/build/scripts'
-				section_status = False
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-							status = True
-					logfile.close()
-
-				if status:
-					cell_msg = '      <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:\n'
-					cell_msg += ' -- All Git Operations went successfully</b></pre></td>\n'
-				else:
-					cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO::\n'
-					cell_msg += ' -- Some Git Operations went WRONG</b></pre></td>\n'
-			else:
-				cell_msg = '      <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
-
-			self.file.write(cell_msg)
-
-	def installLibsPackagesRow(self):
-		self.file.write('    <tr>\n')
-		self.file.write('      <td bgcolor="lightcyan" >SW libs and packages Installation</td>\n')
-		self.analyze_install_log('PCF')
-		self.file.write('	 </tr>\n')
-
-	def analyze_install_log(self, nfType):
-		if nfType != 'PCF':
-			self.file.write('      <td>N/A</td>\n')
-			self.file.write('      <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		self.file.write('      <td>Builder Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				status = False
-				section_start_pattern = 'build_pcf --install-deps --force'
-				section_end_pattern = 'build_pcf --clean --Verbose --build-type Release --jobs'
-				section_status = False
-				package_install = False
-				spdlog_build_start = False
-				spdlog_build_status = False
-				pistache_build_start = False
-				pistache_build_status = False
-				json_build_start = False
-				json_build_status = False
-				nghttp2_build_start = False
-				nghttp2_build_status = False
-				base_image = False
-				build_stage_id = 'NotAcorrectBuildStageId'
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						# old method
-						result = re.search('FROM oai-pcf-base:latest', line)
-						if result is not None:
-							base_image = True
-						# new method --> buildx may cache this stage
-						result = re.search('^#([0-9]+).* RUN ./build_pcf --install-deps', line)
-						if result is not None:
-							build_stage_id = result.group(1)
-						result = re.search(f'^#{build_stage_id} CACHED', line)
-						if result is not None:
-							base_image = True
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-						if section_status:
-							result = re.search('PCF deps installation successful', line)
-							if result is not None:
-								status = True
-							result = re.search('distro libs installation complete', line)
-							if result is not None:
-								package_install = True
-							result = re.search('Starting to install spdlog', line)
-							if result is not None:
-								spdlog_build_start = True
-							result = re.search('spdlog installation complete', line)
-							if result is not None and spdlog_build_start:
-								spdlog_build_status = True
-							result = re.search('Starting to install pistache', line)
-							if result is not None:
-								pistache_build_start = True
-							result = re.search('pistache installation complete', line)
-							if result is not None and pistache_build_start:
-								pistache_build_status = True
-							result = re.search('Starting to install Nlohmann Json', line)
-							if result is not None:
-								json_build_start = True
-							result = re.search('Nlohmann Json installation complete', line)
-							if result is not None and json_build_start:
-								json_build_status = True
-							result = re.search('Starting to install nghttp2', line)
-							if result is not None:
-								nghttp2_build_start = True
-							result = re.search('nghttp2 installation complete', line)
-							if result is not None and nghttp2_build_start:
-								nghttp2_build_status = True
-					logfile.close()
-				if base_image:
-					cell_msg = '      <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'N/A:\n'
-				elif status:
-					cell_msg = '	  <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:\n'
-				else:
-					cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-				cell_msg += ' -- build_pcf --install-deps --force\n'
-				if base_image:
-					cell_msg += '   ** Packages Installation: N/A\n'
-				elif package_install:
-					cell_msg += '   ** Packages Installation: OK\n'
-				else:
-					cell_msg += '   ** Packages Installation: KO\n'
-				if base_image:
-					cell_msg += '   ** spdlog Installation: N/A\n'
-				elif spdlog_build_status:
-					cell_msg += '   ** spdlog Installation: OK\n'
-				else:
-					cell_msg += '   ** spdlog Installation: KO\n'
-				if base_image:
-					cell_msg += '   ** pistache Installation: N/A\n'
-				elif pistache_build_status:
-					cell_msg += '   ** pistache Installation: OK\n'
-				else:
-					cell_msg += '   ** pistache Installation: KO\n'
-				if base_image:
-					cell_msg += '   ** Nlohmann Json Installation: N/A\n'
-				elif json_build_status:
-					cell_msg += '   ** Nlohmann Json Installation: OK\n'
-				else:
-					cell_msg += '   ** Nlohmann Json Installation: KO\n'
-				if base_image:
-					cell_msg += '   ** nghttp2 Installation: N/A\n'
-				elif nghttp2_build_status:
-					cell_msg += '   ** nghttp2 Installation: OK\n'
-				else:
-					cell_msg += '   ** nghttp2 Installation: KO\n'
-				cell_msg += '</b></pre></td>\n'
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
 
-			self.file.write(cell_msg)
+from common.python.pipeline_args_parse import (
+    _parse_args,
+)
 
-	def buildCompileRows(self):
-		self.file.write('	 <tr>\n')
-		self.file.write('	   <td rowspan=2 bgcolor="lightcyan" >cNF Compile / Build</td>\n')
-		self.analyze_build_log('PCF', True)
-		self.file.write('	 </tr>\n')
-		self.file.write('	 <tr>\n')
-		self.analyze_compile_log('PCF', True)
-		self.file.write('	 </tr>\n')
+from common.python.generate_html import (
+    generate_header,
+    generate_footer,
+    generate_git_info,
+)
 
-	def analyze_build_log(self, nfType, imageKind):
-		if nfType != 'PCF':
-			if imageKind:
-				self.file.write('      <td>N/A</td>\n')
-			self.file.write('	   <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
+from common.python.code_format_checker import (
+    coding_formatting_log_check,
+)
 
-		if imageKind:
-			self.file.write('      <td>Builder Image</td>\n')
+from common.python.static_code_analysis import (
+    analyze_sca_log_check,
+)
 
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				status = False
-				if nfType == 'PCF':
-					section_start_pattern = 'build_pcf --clean --Verbose --build-type Release --jobs'
-					section_end_pattern = 'FROM .* as oai-pcf$'
-					pass_pattern = 'pcf installed'
-				section_status = False
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-						if section_status:
-							result = re.search(pass_pattern, line)
-							if result is not None:
-								status = True
-					logfile.close()
-				if status:
-					cell_msg = '	  <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:\n'
-				else:
-					cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-				cell_msg += ' -- ' + section_start_pattern + '</b></pre></td>\n'
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
+from common.python.building_report import (
+    build_summary,
+)
 
-			self.file.write(cell_msg)
+REPORT_NAME = 'test_results_oai_pcf.html'
 
-	def analyze_compile_log(self, nfType, imageKind):
-		if nfType != 'PCF':
-			if imageKind:
-				self.file.write('      <td>N/A</td>\n')
-			self.file.write('	   <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		if imageKind:
-			self.file.write('      <td>Builder Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			nb_errors = 0
-			nb_warnings = 0
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				if nfType == 'PCF':
-					section_start_pattern = 'build_pcf --clean --Verbose --build-type Release --jobs'
-					section_end_pattern = 'FROM .* as oai-pcf$'
-				section_status = False
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-						if section_status:
-							result = re.search('error:', line)
-							if result is not None:
-								nb_errors += 1
-							result = re.search('warning:', line)
-							if result is not None:
-								correctLine = re.sub("^.*/openair-pcf","/openair-pcf",line.strip())
-								wordsList = correctLine.split(None,2)
-								filename = re.sub(":[0-9]*:[0-9]*:","", wordsList[0])
-								linenumber = re.sub(filename + ':',"", wordsList[0])
-								linenumber = re.sub(':[0-9]*:',"", linenumber)
-								error_warning_status = re.sub(':',"", wordsList[1])
-								error_warning_msg = re.sub('^.*' + error_warning_status + ':', '', correctLine)
-								nb_warnings += 1
-								self.warning_rows += '<tr><td>' + filename + '</td><td>' + linenumber + '</td><td>' + error_warning_status + '</td><td>' + error_warning_msg + '</td></tr>\n'
-					logfile.close()
-				if nb_warnings == 0 and nb_errors == 0:
-					cell_msg = '	   <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-				elif nb_warnings < 20 and nb_errors == 0:
-					cell_msg = '	   <td bgcolor="Orange"><pre style="border:none; background-color:Orange"><b>'
-				else:
-					cell_msg = '	   <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				if nb_errors > 0:
-					cell_msg += str(nb_errors) + ' errors found in compile log\n'
-				cell_msg += str(nb_warnings) + ' warnings found in compile log</b></pre></td>\n'
-				self.nb_warnings = nb_warnings
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
-
-			self.file.write(cell_msg)
-
-	def copyToTargetImage(self):
-		self.file.write('	 <tr>\n')
-		self.file.write('	   <td bgcolor="lightcyan" >SW libs Installation / Copy from Builder</td>\n')
-		self.analyze_copy_log('PCF')
-		self.file.write('	 </tr>\n')
-
-	def analyze_copy_log(self, nfType):
-		if nfType != 'PCF':
-			self.file.write('      <td>N/A</td>\n')
-			self.file.write('	   <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		self.file.write('      <td>Target Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				section_start_pattern = 'COPY --from=oai-pcf-builder */openair-pcf/build/pcf/build/oai_pcf'
-				section_end_pattern = 'COPY --from=oai-pcf-builder */openair-pcf/etc/pcf.conf '
-				section_status = False
-				status = False
-				noPbInLDD = True
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search('not found', line)
-						if result is not None and section_status:
-							noPbInLDD = False
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-							status = True
-					logfile.close()
-				if status and noPbInLDD:
-					cell_msg = '	   <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:\n'
-				elif not noPbInLDD:
-					cell_msg = '	   <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-					cell_msg += '  Some libraries were not copied from builder image\n'
-				else:
-					cell_msg = '	   <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-				cell_msg += '</b></pre></td>\n'
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
-
-			self.file.write(cell_msg)
-
-	def copyConfToolsToTargetImage(self):
-		self.file.write('	 <tr>\n')
-		self.file.write('	   <td bgcolor="lightcyan" >Copy Template Conf / Tools from Builder</td>\n')
-		self.analyze_copy_conf_tool_log('PCF')
-		self.file.write('	 </tr>\n')
-
-	def analyze_copy_conf_tool_log(self, nfType):
-		if nfType != 'PCF':
-			self.file.write('      <td>N/A</td>\n')
-			self.file.write('	   <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		self.file.write('      <td>Target Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				section_start_pattern = 'WORKDIR /openair-pcf/etc'
-				if variant == 'docker':
-					section_end_pattern = 'naming to docker.io/library/oai-pcf:'
-				else:
-					section_end_pattern = 'COMMIT oai-pcf:'
-				section_status = False
-				status = False
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(section_start_pattern, line)
-						if result is not None:
-							section_status = True
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-							status = True
-					logfile.close()
-				if status:
-					cell_msg = '	   <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:\n'
-				else:
-					cell_msg = '	   <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-				cell_msg += '</b></pre></td>\n'
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
-
-			self.file.write(cell_msg)
-
-	def imageSizeRow(self):
-		self.file.write('	 <tr>\n')
-		self.file.write('	   <td bgcolor="lightcyan" >Image Size</td>\n')
-		self.analyze_image_size_log('PCF', True)
-		self.file.write('	 </tr>\n')
-
-	def analyze_image_size_log(self, nfType, imageKind):
-		if nfType != 'PCF':
-			if imageKind:
-				self.file.write('      <td>N/A</td>\n')
-			self.file.write('	   <td colspan="2">Wrong NF Type for this Report</td>\n')
-			return
-
-		if imageKind:
-			self.file.write('      <td>Target Image</td>\n')
-
-		cwd = os.getcwd()
-		variants = ['docker', 'podman']
-		for variant in variants:
-			logFileName = 'pcf_' + variant + '_image_build.log'
-			if os.path.isfile(cwd + '/archives/' + logFileName):
-				if nfType == 'PCF':
-					if variant == 'docker':
-						section_start_pattern = 'naming to docker.io/library/oai-pcf:'
-						section_end_pattern = 'OAI-PCF DOCKER IMAGE BUILD'
-					else:
-						section_start_pattern = 'COMMIT oai-pcf:'
-						section_end_pattern = 'OAI-PCF PODMAN RHEL8 IMAGE BUILD'
-				section_status = False
-				status = False
-				imageTag = 'notAcorrectTagForTheMoment'
-				with open(cwd + '/archives/' + logFileName, 'r') as logfile:
-					for line in logfile:
-						result = re.search(f'{section_start_pattern}([0-9a-zA-Z\-\_\.]+)', line)
-						if result is not None:
-							section_status = True
-							imageTag = result.group(1)
-						result = re.search(section_end_pattern, line)
-						if result is not None:
-							section_status = False
-						if section_status:
-							if nfType == 'PCF':
-								result = re.search(f'oai-pcf *{imageTag}', line)
-							if result is not None and not status:
-								result = re.search('ago  *([0-9A-Z ]+)', line)
-								if result is not None:
-									size = result.group(1)
-									if variant == 'docker':
-										size = re.sub('MB', ' MB', size)
-									status = True
-					logfile.close()
-				if status:
-					cell_msg = '	   <td bgcolor="LimeGreen"><pre style="border:none; background-color:LimeGreen"><b>'
-					cell_msg += 'OK:  ' + size + '\n'
-				else:
-					cell_msg = '	   <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-					cell_msg += 'KO:\n'
-				cell_msg += '</b></pre></td>\n'
-			else:
-				cell_msg = '	  <td bgcolor="Tomato"><pre style="border:none; background-color:Tomato"><b>'
-				cell_msg += 'KO: logfile (' + logFileName + ') not found</b></pre></td>\n'
-
-			self.file.write(cell_msg)
-
-def Usage():
-	print('----------------------------------------------------------------------------------------------------------------------')
-	print('generateHtmlReport.py')
-	print('   Generate an HTML report for the Jenkins pipeline on openair-pcf.')
-	print('----------------------------------------------------------------------------------------------------------------------')
-	print('Usage: python3 generateHtmlReport.py [options]')
-	print('  --help  Show this help.')
-	print('---------------------------------------------------------------------------------------------- Mandatory Options -----')
-	print('  --job_name=[Jenkins Job name]')
-	print('  --job_id=[Jenkins Job Build ID]')
-	print('  --job_url=[Jenkins Job Build URL]')
-	print('  --git_url=[Git Repository URL]')
-	print('  --git_src_branch=[Git Source Branch Name]')
-	print('  --git_src_commit=[Git Source Commit SHA-ONE]')
-	print('----------------------------------------------------------------------------------------------- Optional Options -----')
-	print('  --git_pull_request=True')
-	print('  --git_target_branch=[Git Target Branch Name]')
-	print('  --git_target_commit=[Git Target Commit SHA-ONE]')
-
-#--------------------------------------------------------------------------------------------------------
-#
-# Start of main
-#
-#--------------------------------------------------------------------------------------------------------
-
-argvs = sys.argv
-argc = len(argvs)
-
-HTML = HtmlReport()
-
-while len(argvs) > 1:
-	myArgv = argvs.pop(1)
-	if re.match('^\-\-help$', myArgv, re.IGNORECASE):
-		Usage()
-		sys.exit(0)
-	elif re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-job_name=(.+)$', myArgv, re.IGNORECASE)
-		HTML.job_name = matchReg.group(1)
-	elif re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-job_id=(.+)$', myArgv, re.IGNORECASE)
-		HTML.job_id = matchReg.group(1)
-	elif re.match('^\-\-job_url=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-job_url=(.+)$', myArgv, re.IGNORECASE)
-		HTML.job_url = matchReg.group(1)
-	elif re.match('^\-\-git_url=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-git_url=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_url = matchReg.group(1)
-	elif re.match('^\-\-git_src_branch=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-git_src_branch=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_src_branch = matchReg.group(1)
-	elif re.match('^\-\-git_src_commit=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-git_src_commit=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_src_commit = matchReg.group(1)
-	elif re.match('^\-\-git_src_commit_msg=(.+)$', myArgv, re.IGNORECASE):
-		# Not Mandatory
-		matchReg = re.match('^\-\-git_src_commit_msg=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_src_commit_msg = matchReg.group(1)
-	elif re.match('^\-\-git_pull_request=(.+)$', myArgv, re.IGNORECASE):
-		# Can be silent: would be false!
-		matchReg = re.match('^\-\-git_pull_request=(.+)$', myArgv, re.IGNORECASE)
-		if matchReg.group(1) == 'true' or matchReg.group(1) == 'True':
-			HTML.git_pull_request = True
-	elif re.match('^\-\-git_target_branch=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-git_target_branch=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_target_branch = matchReg.group(1)
-	elif re.match('^\-\-git_target_commit=(.+)$', myArgv, re.IGNORECASE):
-		matchReg = re.match('^\-\-git_target_commit=(.+)$', myArgv, re.IGNORECASE)
-		HTML.git_target_commit = matchReg.group(1)
-	else:
-		sys.exit('Invalid Parameter: ' + myArgv)
-
-if HTML.job_name == '' or HTML.job_id == '' or HTML.job_url == '':
-	sys.exit('Missing Parameter in job description')
-
-if HTML.git_url == '' or HTML.git_src_branch == '' or HTML.git_src_commit == '':
-	sys.exit('Missing Parameter in Git Repository description')
-
-if HTML.git_pull_request:
-	if HTML.git_target_commit == '' or HTML.git_target_branch == '':
-		 sys.exit('Missing Parameter in Git Pull Request Repository description')
-
-HTML.generate()
+class HtmlReport():
+    def __init__(self):
+        pass
+
+    def generate(self, args):
+        cwd = os.getcwd()
+        with open(os.path.join(cwd, REPORT_NAME), 'w') as wfile:
+            wfile.write(generate_header(args))
+            wfile.write(generate_git_info(args))
+            wfile.write(build_summary(args, 'pcf', '20', '8'))
+            wfile.write(coding_formatting_log_check(args))
+            wfile.write(analyze_sca_log_check())
+            wfile.write(generate_footer())
+
+    def appendToTestReports(self, args):
+        gitInfo = generate_git_info(args)
+        cwd = os.getcwd()
+        for reportFile in os.listdir(cwd):
+            if reportFile.endswith('.html') and re.search('results_oai_cn5g_', reportFile) is not None:
+                newFile = ''
+                gitInfoAppended = False
+                with open(os.path.join(cwd, reportFile), 'r') as rfile:
+                    for line in rfile:
+                        if re.search('<h2>', line) is not None and not gitInfoAppended:
+                            gitInfoAppended = True
+                            newFile += gitInfo
+                        newFile += line
+                with open(os.path.join(cwd, reportFile), 'w') as wfile:
+                    wfile.write(newFile)
+
+if __name__ == '__main__':
+    # Parse the arguments
+    args = _parse_args()
+
+    # Generate report
+    HTML = HtmlReport()
+    HTML.generate(args)
+    HTML.appendToTestReports(args)
diff --git a/ci-scripts/openshift/oai-pcf-bc.yaml b/ci-scripts/openshift/oai-pcf-bc.yaml
deleted file mode 100644
index e13dbf6705fd85cf09e5a3a32ffbcdd9edae018c..0000000000000000000000000000000000000000
--- a/ci-scripts/openshift/oai-pcf-bc.yaml
+++ /dev/null
@@ -1,50 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-#
----
-kind: BuildConfig
-apiVersion: build.openshift.io/v1
-metadata:
-    name: "oai-pcf"
-    namespace: oaicicd-core
-spec:
-    runPolicy: "Serial"
-    source:
-        type: "Binary"
-        secrets:
-          - secret:
-              name: etc-pki-entitlement
-            destinationDir: etc-pki-entitlement
-        configMaps:
-          - configMap:
-              name: rhsm-conf
-            destinationDir: rhsm-conf
-          - configMap:
-              name: rhsm-ca
-            destinationDir: rhsm-ca
-    strategy:
-        dockerStrategy:
-            dockerfilePath: "docker/Dockerfile.pcf.rhel8"
-    output:
-        to:
-            kind: "ImageStreamTag"
-            name: "oai-pcf:develop"
diff --git a/ci-scripts/openshift/oai-pcf-is.yaml b/ci-scripts/openshift/oai-pcf-is.yaml
deleted file mode 100644
index d6465da9c3afe775d1fa1d0072b80a4399daff97..0000000000000000000000000000000000000000
--- a/ci-scripts/openshift/oai-pcf-is.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-#---------------------------------------------------------------------
-#
-apiVersion: image.openshift.io/v1
-kind: ImageStream
-metadata:
-    name: oai-pcf
-    namespace: oaicicd-core
-spec:
-  lookupPolicy:
-    local: true
-status:
-    tag: develop
diff --git a/docker/Dockerfile.pcf.rhel8 b/docker/Dockerfile.pcf.rhel8
index f1342ff9d89d0ce0d513497bba4be78756989de8..915e6511aed219a5aa1b67753e6615309f33c9ab 100644
--- a/docker/Dockerfile.pcf.rhel8
+++ b/docker/Dockerfile.pcf.rhel8
@@ -122,9 +122,7 @@ COPY --from=oai-pcf-builder \
     /usr/local/lib/libnghttp2_asio.so.1 \
     /usr/local/lib64/libspdlog.so \
     /usr/local/lib64/libfmt.so \
-# libyaml-cpp is installed via epel-8
-# and epel-8 cannot be installed on ubi-minimal
-    /lib64/libyaml-cpp.so.0.6 \
+    /usr/local/lib64/libyaml-cpp.so.0.* \
     /usr/lib64/
 
 RUN ldconfig && \
diff --git a/docker/Dockerfile.pcf.ubuntu b/docker/Dockerfile.pcf.ubuntu
index 03a6fc02b35fc58a443edbff96471d12db4f8f64..245787b77b1681091abc6f4b0887dfb39ef605ef 100644
--- a/docker/Dockerfile.pcf.ubuntu
+++ b/docker/Dockerfile.pcf.ubuntu
@@ -31,7 +31,7 @@
 #---------------------------------------------------------------------
 # BASE IMAGE
 #---------------------------------------------------------------------
-ARG BASE_IMAGE=ubuntu:bionic
+ARG BASE_IMAGE=ubuntu:focal
 FROM $BASE_IMAGE as oai-pcf-base
 
 ENV DEBIAN_FRONTEND=noninteractive
@@ -51,7 +51,7 @@ RUN git config --global https.postBuffer 123289600 && \
 # Copying source code
 WORKDIR /openair-pcf
 COPY ./build/scripts /openair-pcf/build/scripts/
-COPY ./build/build-common /openair-pcf/build/build-common/
+COPY ./build/common-build /openair-pcf/build/common-build/
 COPY ./build/pcf/CMakeLists.txt /openair-pcf/build/pcf/CMakeLists.txt
 COPY ./build/cmake_modules /openair-pcf/build/cmake_modules/
 
@@ -100,10 +100,6 @@ RUN apt-get update && \
       libcurl?-gnutls \
       librtmp1 \
       libpsl5 \
-# Ubuntu 18 --> won't install anything
-# Ubuntu 20 --> libyaml-cpp 0.6
-# Ubuntu 22 --> libyaml-cpp 0.7
-      libyaml-cpp0.? \
   && rm -rf /var/lib/apt/lists/* \
        /lib/x86_64-linux-gnu/libboost_system.so.1.6*.0 \
        /lib/x86_64-linux-gnu/ibboost_filesystem.so.1.6*.0
diff --git a/src/api-server/CMakeLists.txt b/src/api-server/CMakeLists.txt
index cb8a4aa7a36fd2a8c3a0d1cd8e750d5ac0e8744b..29e978bc08e7b3b36407a2bbf2eae75c083d2c55 100644
--- a/src/api-server/CMakeLists.txt
+++ b/src/api-server/CMakeLists.txt
@@ -24,6 +24,7 @@ set(PCF_API_SERVER_DIR "${SRC_TOP_DIR}/api-server")
 include_directories(${PCF_API_SERVER_DIR}/api)
 include_directories(${PCF_API_SERVER_DIR}/impl)
 include_directories(${PCF_API_SERVER_DIR}/model)
+include_directories(${PCF_API_SERVER_DIR}/handler)
 include_directories(${PCF_API_SERVER_DIR}/)
 include_directories(${SRC_TOP_DIR}/pcf_app)
 include_directories(${SRC_TOP_DIR}/common)
@@ -34,9 +35,11 @@ include_directories(${SRC_TOP_DIR}/${MOUNTED_COMMON}/config)
 file(GLOB PCF_API_SERVER_src_files
     ${PCF_API_SERVER_DIR}/pcf-api-server.cpp
     ${PCF_API_SERVER_DIR}/pcf-http2-server.cpp
+    ${PCF_API_SERVER_DIR}/api_defs.cpp
     ${PCF_API_SERVER_DIR}/model/*.cpp
     ${PCF_API_SERVER_DIR}/api/*.cpp
     ${PCF_API_SERVER_DIR}/impl/*.cpp
+    ${PCF_API_SERVER_DIR}/handler/*.cpp
 )
  
 add_library(PCF_API STATIC
diff --git a/src/api-server/api/IndividualSMPolicyDocumentApi.cpp b/src/api-server/api/IndividualSMPolicyDocumentApi.cpp
index aec7bf6dcf5d8e2a336bebbc5d5f9d7f58014789..87d3d4a5a7d36db47dc214d95e336c863a2abc43 100644
--- a/src/api-server/api/IndividualSMPolicyDocumentApi.cpp
+++ b/src/api-server/api/IndividualSMPolicyDocumentApi.cpp
@@ -13,17 +13,14 @@
 
 #include "IndividualSMPolicyDocumentApi.h"
 #include "Helpers.h"
+#include "api_defs.h"
+#include "pcf_config.hpp"
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::model::common::helpers;
 using namespace oai::pcf::model;
 
-const std::string IndividualSMPolicyDocumentApi::base =
-    "/npcf-smpolicycontrol/v1";
-
 IndividualSMPolicyDocumentApi::IndividualSMPolicyDocumentApi(
     const std::shared_ptr<Pistache::Rest::Router>& rtr)
     : router(rtr) {}
@@ -34,17 +31,16 @@ void IndividualSMPolicyDocumentApi::init() {
 
 void IndividualSMPolicyDocumentApi::setupRoutes() {
   using namespace Pistache::Rest;
-
   Routes::Post(
-      *router, base + "/sm-policies/:smPolicyId/delete",
+      *router, sm_policies::get_route() + "/:smPolicyId/delete",
       Routes::bind(
           &IndividualSMPolicyDocumentApi::delete_sm_policy_handler, this));
   Routes::Get(
-      *router, base + "/sm-policies/:smPolicyId",
+      *router, sm_policies::get_route() + "/:smPolicyId",
       Routes::bind(
           &IndividualSMPolicyDocumentApi::get_sm_policy_handler, this));
   Routes::Post(
-      *router, base + "/sm-policies/:smPolicyId/update",
+      *router, sm_policies::get_route() + "/:smPolicyId/update",
       Routes::bind(
           &IndividualSMPolicyDocumentApi::update_sm_policy_handler, this));
 
@@ -183,6 +179,4 @@ void IndividualSMPolicyDocumentApi::
       Pistache::Http::Code::Not_Found, "The requested method does not exist");
 }
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
+}  // namespace oai::pcf::api
diff --git a/src/api-server/api/IndividualSMPolicyDocumentApi.h b/src/api-server/api/IndividualSMPolicyDocumentApi.h
old mode 100755
new mode 100644
index fde978c08596ccdaf5cce4614534f85120260f09..8c85924d0b4490cced1a87c5a22045b4e2b9272c
--- a/src/api-server/api/IndividualSMPolicyDocumentApi.h
+++ b/src/api-server/api/IndividualSMPolicyDocumentApi.h
@@ -33,9 +33,7 @@
 #include "SmPolicyUpdateContextData.h"
 #include <string>
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::pcf::model;
 
@@ -46,8 +44,6 @@ class IndividualSMPolicyDocumentApi {
   virtual ~IndividualSMPolicyDocumentApi() = default;
   void init();
 
-  static const std::string base;
-
  private:
   void setupRoutes();
 
@@ -121,8 +117,6 @@ class IndividualSMPolicyDocumentApi {
       Pistache::Http::ResponseWriter& response) = 0;
 };
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
+}  // namespace oai::pcf::api
 
 #endif /* IndividualSMPolicyDocumentApi_H_ */
diff --git a/src/api-server/api/SMPoliciesCollectionApi.cpp b/src/api-server/api/SMPoliciesCollectionApi.cpp
index f4c39fbcfe1b146154f068e4cc5ddd137bb32f8d..a72272587e664362d7cb7770db34e9b1166e68ea 100644
--- a/src/api-server/api/SMPoliciesCollectionApi.cpp
+++ b/src/api-server/api/SMPoliciesCollectionApi.cpp
@@ -12,6 +12,8 @@
  */
 
 #include "SMPoliciesCollectionApi.h"
+#include "api_defs.h"
+#include "pcf_config.hpp"
 
 namespace oai {
 namespace pcf {
@@ -20,8 +22,6 @@ namespace api {
 using namespace oai::model::common::helpers;
 using namespace oai::pcf::model;
 
-const std::string SMPoliciesCollectionApi::base = "/npcf-smpolicycontrol/v1";
-
 SMPoliciesCollectionApi::SMPoliciesCollectionApi(
     const std::shared_ptr<Pistache::Rest::Router>& rtr)
     : router(rtr) {}
@@ -34,7 +34,7 @@ void SMPoliciesCollectionApi::setupRoutes() {
   using namespace Pistache::Rest;
 
   Routes::Post(
-      *router, base + "/sm-policies",
+      *router, sm_policies::get_route(),
       Routes::bind(&SMPoliciesCollectionApi::create_sm_policy_handler, this));
 
   // Default handler, called when a route is not found
diff --git a/src/api-server/api/SMPoliciesCollectionApi.h b/src/api-server/api/SMPoliciesCollectionApi.h
index b59d17953a81d6e5e64de62ab6fe202d9c11a80e..49c79e0218e8aab38c75b7487e4c37b16f1970b9 100755
--- a/src/api-server/api/SMPoliciesCollectionApi.h
+++ b/src/api-server/api/SMPoliciesCollectionApi.h
@@ -46,8 +46,6 @@ class SMPoliciesCollectionApi {
   virtual ~SMPoliciesCollectionApi() = default;
   void init();
 
-  static const std::string base;
-
  private:
   void setupRoutes();
 
diff --git a/src/api-server/api_defs.cpp b/src/api-server/api_defs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84e1a517dc893042912e884a23c0ad7b5aacbfcf
--- /dev/null
+++ b/src/api-server/api_defs.cpp
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file api_defs.h
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#include "api_defs.h"
+
+#include "pcf_config.hpp"
+
+extern std::unique_ptr<oai::pcf::config::pcf_config> pcf_cfg;
+
+namespace oai::pcf::api {
+
+std::string sm_policies::get_route() {
+  return API_BASE + pcf_cfg->sbi.get_api_version() + sm_policies::CREATE_ROUTE;
+}
+
+}  // namespace oai::pcf::api
diff --git a/src/api-server/api_defs.h b/src/api-server/api_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..72858e5615e2bb3c14cc5f83a9df6896f635d431
--- /dev/null
+++ b/src/api-server/api_defs.h
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file api_defs.h
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#pragma once
+#include <string>
+
+namespace oai::pcf::api {
+class sm_policies {
+ public:
+  static inline const std::string API_NAME     = "npcf-smpolicycontrol";
+  static inline const std::string API_BASE     = "/" + API_NAME + "/";
+  static inline const std::string CREATE_ROUTE = "/sm-policies";
+
+  static std::string get_route();
+};
+
+}  // namespace oai::pcf::api
\ No newline at end of file
diff --git a/src/api-server/handler/api_response.h b/src/api-server/handler/api_response.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a5597ef7d38e186248404b12ee5004b34589a53
--- /dev/null
+++ b/src/api-server/handler/api_response.h
@@ -0,0 +1,45 @@
+
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file sm_policies_collection_api_handler.cpp
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#pragma once
+
+#include "3gpp_29.500.h"
+
+#include <pistache/http_headers.h>
+
+namespace oai::pcf::api {
+
+struct api_response {
+  http_status_code_e status_code;
+  Pistache::Http::Header::Collection headers;
+  std::string body;
+};
+
+}  // namespace oai::pcf::api
\ No newline at end of file
diff --git a/src/api-server/handler/individual_sm_policy_document_api_handler.cpp b/src/api-server/handler/individual_sm_policy_document_api_handler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a7cbfb3383882a3f5754851f5b60eb29bd18a58
--- /dev/null
+++ b/src/api-server/handler/individual_sm_policy_document_api_handler.cpp
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file individual_sm_policy_document_api_handler.cpp
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#include "individual_sm_policy_document_api_handler.h"
+#include "ProblemDetails.h"
+
+namespace oai::pcf::api {
+
+using namespace oai::pcf::model;
+using namespace oai::model::common;
+using namespace oai::pcf::app::sm_policy;
+
+api_response individual_sm_policy_document_api_handler::delete_sm_policy(
+    const std::string& sm_policy_id,
+    const SmPolicyDeleteData& sm_policy_delete_data) {
+  api_response response;
+
+  ProblemDetails problem_details;
+  std::string problem_description;
+  std::string content_type = "application/problem+json";
+  nlohmann::json json_data;
+  http_status_code_e http_code;
+  status_code res = m_smpc_service->delete_sm_policy_handler(
+      sm_policy_id, sm_policy_delete_data, problem_description);
+
+  switch (res) {
+    case status_code::OK:
+      http_code = http_status_code_e::HTTP_STATUS_CODE_204_NO_CONTENT;
+      break;
+
+    case status_code::NOT_FOUND:
+      problem_details.setDetail(problem_description);
+      // This is not defined in the standard
+      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
+      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
+      break;
+
+    default:
+      problem_details.setDetail("Internal Service Error: Unknown return code.");
+      problem_details.setCause("INTERNAL_ERROR");
+      http_code =
+          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
+  }
+  response.status_code = http_code;
+  if (res != status_code::OK) {
+    response.headers.add<Pistache::Http::Header::ContentType>(
+        Pistache::Http::Mime::MediaType(content_type));
+    to_json(json_data, problem_details);
+    response.body = json_data.dump();
+  }
+  return response;
+}
+
+api_response individual_sm_policy_document_api_handler::get_sm_policy(
+    const std::string& sm_policy_id) {
+  api_response response;
+  ProblemDetails problem_details;
+  std::string problem_description;
+  std::string content_type = "application/problem+json";
+  nlohmann::json json_data;
+  http_status_code_e http_code;
+
+  SmPolicyControl sm_policy_control;
+
+  status_code res = m_smpc_service->get_sm_policy_handler(
+      sm_policy_id, sm_policy_control, problem_description);
+
+  switch (res) {
+    case status_code::OK:
+      http_code    = http_status_code_e::HTTP_STATUS_CODE_200_OK;
+      content_type = "application/json";
+      break;
+
+    case status_code::NOT_FOUND:
+      problem_details.setDetail(problem_description);
+      // This is not defined in the standard
+      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
+      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
+      break;
+
+    default:
+      problem_details.setDetail("Internal Service Error: Unknown return code.");
+      problem_details.setCause("INTERNAL_ERROR");
+      http_code =
+          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
+  }
+
+  if (res == status_code::OK) {
+    to_json(json_data, sm_policy_control);
+  } else {
+    to_json(json_data, problem_details);
+  }
+  response.status_code = http_code;
+  response.body        = json_data.dump();
+  response.headers.add<Pistache::Http::Header::ContentType>(
+      Pistache::Http::Mime::MediaType(content_type));
+
+  return response;
+}
+
+api_response individual_sm_policy_document_api_handler::update_sm_policy(
+    const std::string& sm_policy_id,
+    const SmPolicyUpdateContextData& sm_policy_update_context_data) {
+  api_response response;
+  ProblemDetails problem_details;
+  std::string problem_description;
+  std::string content_type = "application/problem+json";
+  nlohmann::json json_data;
+  http_status_code_e http_code;
+
+  SmPolicyDecision decision_update;
+
+  status_code res = m_smpc_service->update_sm_policy_handler(
+      sm_policy_id, sm_policy_update_context_data, decision_update,
+      problem_description);
+
+  problem_details.setDetail(problem_description);
+
+  switch (res) {
+    case status_code::OK:
+      content_type = "application/json";
+      http_code    = http_status_code_e::HTTP_STATUS_CODE_200_OK;
+      break;
+    case status_code::INVALID_PARAMETERS:
+      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
+      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
+      break;
+    case status_code::CONTEXT_DENIED:
+      // should map to 403 but not defined in standard for this request
+      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
+      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
+      break;
+    case status_code::NOT_FOUND:
+      // TODO This is not defined in the standard, but this scenario is missing
+      // we could map it to the 400 Bad request but that is somehow misleading
+      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
+      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
+      break;
+    default:
+      problem_details.setCause("INTERNAL_ERROR");
+      http_code =
+          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
+  }
+
+  if (res == status_code::OK) {
+    to_json(json_data, decision_update);
+  } else {
+    to_json(json_data, problem_details);
+  }
+
+  response.headers.add<Pistache::Http::Header::ContentType>(
+      Pistache::Http::Mime::MediaType(content_type));
+  response.body        = json_data.dump();
+  response.status_code = http_code;
+  return response;
+}
+}  // namespace oai::pcf::api
diff --git a/src/api-server/handler/individual_sm_policy_document_api_handler.h b/src/api-server/handler/individual_sm_policy_document_api_handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..644f8b069dd6063eb02a389a294cbad41aec1a37
--- /dev/null
+++ b/src/api-server/handler/individual_sm_policy_document_api_handler.h
@@ -0,0 +1,79 @@
+
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file individual_sm_policy_document_api_handler.cpp
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#pragma once
+
+#include "3gpp_29.500.h"
+#include "api_response.h"
+#include "pcf_sm_policy_control.hpp"
+#include "SmPolicyDeleteData.h"
+#include "SmPolicyUpdateContextData.h"
+
+namespace oai::pcf::api {
+
+class individual_sm_policy_document_api_handler {
+ public:
+  explicit individual_sm_policy_document_api_handler(
+      const std::shared_ptr<oai::pcf::app::pcf_smpc>& pcf_smpc) {
+    m_smpc_service = pcf_smpc;
+  }
+  /**
+   * Delete SM Policy based on ID
+   * @param sm_policy_id
+   * @param sm_policy_delete_data
+   * @return api_response
+   */
+  api_response delete_sm_policy(
+      const std::string& sm_policy_id,
+      const oai::pcf::model::SmPolicyDeleteData& sm_policy_delete_data);
+
+  /**
+   * Get SM Policy by ID
+   * @param sm_policy_id
+   * @return api_response
+   */
+  api_response get_sm_policy(const std::string& sm_policy_id);
+
+  /**
+   * Update SM Policy by ID and mandatory update context data
+   * @param sm_policy_id
+   * @param smPolicyUpdateContextData
+   * @return
+   */
+  api_response update_sm_policy(
+      const std::string& sm_policy_id,
+      const oai::pcf::model::SmPolicyUpdateContextData&
+          smPolicyUpdateContextData);
+
+ private:
+  std::shared_ptr<oai::pcf::app::pcf_smpc> m_smpc_service;
+};
+
+}  // namespace oai::pcf::api
\ No newline at end of file
diff --git a/src/api-server/handler/sm_policies_collection_api_handler.cpp b/src/api-server/handler/sm_policies_collection_api_handler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b212d1ecc5f561984dcfef0b90ff0523174aab6d
--- /dev/null
+++ b/src/api-server/handler/sm_policies_collection_api_handler.cpp
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file sm_policies_collection_api_handler.cpp
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#include "api_response.h"
+#include "sm_policies_collection_api_handler.h"
+
+#include "ProblemDetails.h"
+#include "SmPolicyDecision.h"
+#include "SMPoliciesCollectionApi.h"
+#include "logger.hpp"
+#include "api_defs.h"
+#include "pcf_config.hpp"
+
+namespace oai::pcf::api {
+
+using namespace oai::pcf::model;
+using namespace oai::model::common;
+using namespace oai::pcf::app::sm_policy;
+
+api_response sm_policies_collection_api_handler::create_sm_policy(
+    const SmPolicyContextData& sm_policy_context_data) {
+  http_status_code_e http_code;
+  std::string cause;
+  ProblemDetails problem_details;
+  SmPolicyDecision decision;
+  std::string details_string;
+  std::string association_id;
+  std::string location;
+  std::string content_type = "application/problem+json";
+  api_response response;
+
+  status_code res = m_smpc_service->create_sm_policy_handler(
+      sm_policy_context_data, decision, association_id, details_string);
+  nlohmann::json json_data;
+
+  switch (res) {
+    case status_code::CREATED:
+      http_code = http_status_code_e::HTTP_STATUS_CODE_201_CREATED;
+      location  = m_address + sm_policies::get_route() + "/" + association_id;
+      content_type = "application/json";
+      break;
+
+    case status_code::USER_UNKOWN:
+      problem_details.setCause("USER_UNKOWN");
+      problem_details.setDetail(details_string);
+      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
+      break;
+
+    case status_code::INVALID_PARAMETERS:
+      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
+      problem_details.setDetail(details_string);
+      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
+      break;
+
+    case status_code::CONTEXT_DENIED:
+      problem_details.setCause("POLICY_CONTEXT_DENIED");
+      problem_details.setDetail(details_string);
+      http_code = http_status_code_e::HTTP_STATUS_CODE_403_FORBIDDEN;
+      break;
+
+    default:
+      Logger::pcf_app().error("Unknown error code");
+      http_code =
+          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
+      problem_details.setCause("INTERNAL_ERROR");
+      problem_details.setDetail("Internal Service Error: Unknown return code.");
+  }
+
+  if (http_code != http_status_code_e::HTTP_STATUS_CODE_201_CREATED) {
+    to_json(json_data, problem_details);
+  } else {
+    to_json(json_data, decision);
+    response.headers.add<Pistache::Http::Header::Location>(location);
+  }
+  response.headers.add<Pistache::Http::Header::ContentType>(
+      Pistache::Http::Mime::MediaType(content_type));
+  response.body        = json_data.dump();
+  response.status_code = http_code;
+  return response;
+}
+
+}  // namespace oai::pcf::api
diff --git a/src/api-server/handler/sm_policies_collection_api_handler.h b/src/api-server/handler/sm_policies_collection_api_handler.h
new file mode 100644
index 0000000000000000000000000000000000000000..8f9bd8c4acdebc884103d6b7d2c3a8e3f05cc600
--- /dev/null
+++ b/src/api-server/handler/sm_policies_collection_api_handler.h
@@ -0,0 +1,62 @@
+
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*! \file sm_policies_collection_api_handler.h
+ \brief
+ \author  Stefan Spettel
+ \company phine.tech
+ \date 2023
+ \email: stefan.spettel@phine.tech
+ */
+
+#pragma once
+
+#include "3gpp_29.500.h"
+#include "api_response.h"
+#include "SmPolicyContextData.h"
+#include "pcf_sm_policy_control.hpp"
+
+namespace oai::pcf::api {
+
+class sm_policies_collection_api_handler {
+ public:
+  sm_policies_collection_api_handler(
+      const std::shared_ptr<oai::pcf::app::pcf_smpc>& pcf_smpc,
+      const std::string& address) {
+    m_address      = address;
+    m_smpc_service = pcf_smpc;
+  }
+
+  /**
+   * Create SM Policy
+   * @param sm_policy_context_data SM context data
+   * @return api_response with SmPolicyDecision
+   */
+  api_response create_sm_policy(
+      const model::SmPolicyContextData& sm_policy_context_data);
+
+ private:
+  std::string m_address;
+  std::shared_ptr<oai::pcf::app::pcf_smpc> m_smpc_service;
+};
+
+}  // namespace oai::pcf::api
\ No newline at end of file
diff --git a/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.cpp b/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.cpp
index 9f210733a0db3acd98478a516eb36aa1c53c6d34..347b548ac06b6d2f3568e4d32c47b5d758525ad5 100644
--- a/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.cpp
+++ b/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.cpp
@@ -12,12 +12,9 @@
  */
 
 #include "IndividualSMPolicyDocumentApiImpl.h"
-#include "sm_policy/pcf_smpc_status_code.hpp"
 #include "3gpp_29.500.h"
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::pcf::model;
 using namespace oai::pcf::app;
@@ -26,153 +23,36 @@ using namespace oai::model::common;
 
 IndividualSMPolicyDocumentApiImpl::IndividualSMPolicyDocumentApiImpl(
     const std::shared_ptr<Pistache::Rest::Router>& rtr,
-    std::shared_ptr<pcf_smpc> smpc_service, std::string address)
+    const std::shared_ptr<pcf_smpc>& smpc_service, const std::string&)
     : IndividualSMPolicyDocumentApi(rtr) {
-  m_address      = address;
-  m_smpc_service = smpc_service;
+  m_api_handler =
+      std::make_shared<individual_sm_policy_document_api_handler>(smpc_service);
 }
 
 void IndividualSMPolicyDocumentApiImpl::delete_sm_policy(
     const std::string& smPolicyId, const SmPolicyDeleteData& smPolicyDeleteData,
     Pistache::Http::ResponseWriter& response) {
-  ProblemDetails problem_details;
-  std::string problem_description;
-  std::string content_type = "application/problem+json";
-  nlohmann::json json_data;
-  http_status_code_e http_code;
-  status_code res = m_smpc_service->delete_sm_policy_handler(
-      smPolicyId, smPolicyDeleteData, problem_description);
-
-  switch (res) {
-    case status_code::OK:
-      http_code = http_status_code_e::HTTP_STATUS_CODE_204_NO_CONTENT;
-      break;
-
-    case status_code::NOT_FOUND:
-      problem_details.setDetail(problem_description);
-      // This is not defined in the standard
-      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
-      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
-      break;
-
-    default:
-      problem_details.setDetail("Internal Service Error: Unknown return code.");
-      problem_details.setCause("INTERNAL_ERROR");
-      http_code =
-          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
-  }
-
-  if (res == status_code::OK) {
-    response.send(Pistache::Http::Code(http_code));
-  } else {
-    response.headers().add<Pistache::Http::Header::ContentType>(
-        Pistache::Http::Mime::MediaType(content_type));
-    to_json(json_data, problem_details);
-    response.send(Pistache::Http::Code(http_code), json_data.dump().c_str());
-  }
+  api_response resp =
+      m_api_handler->delete_sm_policy(smPolicyId, smPolicyDeleteData);
+  response.headers() = resp.headers;
+  response.send(Pistache::Http::Code(resp.status_code), resp.body);
 }
 
 void IndividualSMPolicyDocumentApiImpl::get_sm_policy(
     const std::string& smPolicyId, Pistache::Http::ResponseWriter& response) {
-  ProblemDetails problem_details;
-  std::string problem_description;
-  std::string content_type = "application/problem+json";
-  nlohmann::json json_data;
-  http_status_code_e http_code;
-
-  SmPolicyControl sm_policy_control;
-
-  status_code res = m_smpc_service->get_sm_policy_handler(
-      smPolicyId, sm_policy_control, problem_description);
-
-  switch (res) {
-    case status_code::OK:
-      http_code    = http_status_code_e::HTTP_STATUS_CODE_200_OK;
-      content_type = "application/json";
-      break;
-
-    case status_code::NOT_FOUND:
-      problem_details.setDetail(problem_description);
-      // This is not defined in the standard
-      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
-      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
-      break;
-
-    default:
-      problem_details.setDetail("Internal Service Error: Unknown return code.");
-      problem_details.setCause("INTERNAL_ERROR");
-      http_code =
-          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
-  }
-
-  if (res == status_code::OK) {
-    to_json(json_data, sm_policy_control);
-  } else {
-    to_json(json_data, problem_details);
-  }
-
-  response.headers().add<Pistache::Http::Header::ContentType>(
-      Pistache::Http::Mime::MediaType(content_type));
-
-  response.send(Pistache::Http::Code(http_code), json_data.dump().c_str());
+  api_response resp  = m_api_handler->get_sm_policy(smPolicyId);
+  response.headers() = resp.headers;
+  response.send(Pistache::Http::Code(resp.status_code), resp.body);
 }
 
 void IndividualSMPolicyDocumentApiImpl::update_sm_policy(
     const std::string& smPolicyId,
     const SmPolicyUpdateContextData& smPolicyUpdateContextData,
     Pistache::Http::ResponseWriter& response) {
-  ProblemDetails problem_details;
-  std::string problem_description = "";
-  std::string content_type        = "application/problem+json";
-  nlohmann::json json_data;
-  http_status_code_e http_code;
-
-  SmPolicyDecision decision_update;
-
-  status_code res = m_smpc_service->update_sm_policy_handler(
-      smPolicyId, smPolicyUpdateContextData, decision_update,
-      problem_description);
-
-  problem_details.setDetail(problem_description);
-
-  switch (res) {
-    case status_code::OK:
-      content_type = "application/json";
-      http_code    = http_status_code_e::HTTP_STATUS_CODE_200_OK;
-      break;
-    case status_code::INVALID_PARAMETERS:
-      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
-      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
-      break;
-    case status_code::CONTEXT_DENIED:
-      // should map to 403 but not defined in standard for this request
-      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
-      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
-      break;
-    case status_code::NOT_FOUND:
-      // TODO This is not defined in the standard, but this scenario is missig
-      // we could map it to the 400 Bad request but that is somehow misleading
-      problem_details.setCause("SM_POLICY_ID_NOT_FOUND");
-      http_code = http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND;
-      break;
-    default:
-      problem_details.setCause("INTERNAL_ERROR");
-      http_code =
-          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
-  }
-
-  if (res == status_code::OK) {
-    to_json(json_data, decision_update);
-  } else {
-    to_json(json_data, problem_details);
-  }
-
-  response.headers().add<Pistache::Http::Header::ContentType>(
-      Pistache::Http::Mime::MediaType(content_type));
-
-  response.send(Pistache::Http::Code(http_code), json_data.dump().c_str());
+  api_response resp =
+      m_api_handler->update_sm_policy(smPolicyId, smPolicyUpdateContextData);
+  response.headers() = resp.headers;
+  response.send(Pistache::Http::Code(resp.status_code), resp.body);
 }
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
+}  // namespace oai::pcf::api
diff --git a/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.h b/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.h
old mode 100755
new mode 100644
index ffc1da101c987b461a82c7b5d19383f19914f795..b957acd8a04f62f7ff44e6f8f39b40add67713c4
--- a/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.h
+++ b/src/api-server/impl/IndividualSMPolicyDocumentApiImpl.h
@@ -17,8 +17,7 @@
  *
  */
 
-#ifndef INDIVIDUAL_SM_POLICY_DOCUMENT_API_IMPL_H_
-#define INDIVIDUAL_SM_POLICY_DOCUMENT_API_IMPL_H_
+#pragma once
 
 #include <pistache/endpoint.h>
 #include <pistache/http.h>
@@ -36,10 +35,9 @@
 #include <string>
 
 #include "pcf_app.hpp"
+#include "individual_sm_policy_document_api_handler.h"
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::pcf::model;
 using namespace pcf;
@@ -49,28 +47,25 @@ class IndividualSMPolicyDocumentApiImpl
  public:
   explicit IndividualSMPolicyDocumentApiImpl(
       const std::shared_ptr<Pistache::Rest::Router>& rtr,
-      const std::shared_ptr<pcf::app::pcf_smpc> smpc_service,
-      std::string m_address);
+      const std::shared_ptr<pcf::app::pcf_smpc>& smpc_service,
+      const std::string& m_address);
   ~IndividualSMPolicyDocumentApiImpl() override = default;
 
   void delete_sm_policy(
       const std::string& smPolicyId,
       const SmPolicyDeleteData& smPolicyDeleteData,
-      Pistache::Http::ResponseWriter& response);
+      Pistache::Http::ResponseWriter& response) override;
   void get_sm_policy(
-      const std::string& smPolicyId, Pistache::Http::ResponseWriter& response);
+      const std::string& smPolicyId,
+      Pistache::Http::ResponseWriter& response) override;
   void update_sm_policy(
       const std::string& smPolicyId,
       const SmPolicyUpdateContextData& smPolicyUpdateContextData,
-      Pistache::Http::ResponseWriter& response);
+      Pistache::Http::ResponseWriter& response) override;
 
  private:
   std::string m_address;
-  std::shared_ptr<app::pcf_smpc> m_smpc_service;
+  std::shared_ptr<individual_sm_policy_document_api_handler> m_api_handler;
 };
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
-
-#endif
+}  // namespace oai::pcf::api
diff --git a/src/api-server/impl/SMPoliciesCollectionApiImpl.cpp b/src/api-server/impl/SMPoliciesCollectionApiImpl.cpp
index a57058c9fd15cd2c8c41a04d009803db2d29eeeb..bc99da85d61222d855d0f3cf306cd77d90c4ab53 100644
--- a/src/api-server/impl/SMPoliciesCollectionApiImpl.cpp
+++ b/src/api-server/impl/SMPoliciesCollectionApiImpl.cpp
@@ -12,13 +12,10 @@
  */
 
 #include "SMPoliciesCollectionApiImpl.h"
-#include "sm_policy/pcf_smpc_status_code.hpp"
 #include "3gpp_29.500.h"
 #include "logger.hpp"
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::pcf::model;
 using namespace oai::pcf::app;
@@ -27,74 +24,19 @@ using namespace oai::model::common;
 
 SMPoliciesCollectionApiImpl::SMPoliciesCollectionApiImpl(
     const std::shared_ptr<Pistache::Rest::Router>& rtr,
-    const std::shared_ptr<pcf_smpc> smpc_service, std::string address)
+    const std::shared_ptr<pcf_smpc>& smpc_service, const std::string& address)
     : SMPoliciesCollectionApi(rtr) {
-  this->m_address    = address;
-  this->smpc_service = smpc_service;
+  m_api_handler = std::make_shared<sm_policies_collection_api_handler>(
+      sm_policies_collection_api_handler(smpc_service, address));
 }
 
 void SMPoliciesCollectionApiImpl::create_sm_policy(
     const SmPolicyContextData& smPolicyContextData,
     Pistache::Http::ResponseWriter& response) {
-  http_status_code_e http_code;
-  std::string cause;
-  ProblemDetails problem_details;
-  SmPolicyDecision decision;
-  std::string details_string = "";
-  std::string association_id = "";
-  std::string location       = "";
-  std::string content_type   = "application/problem+json";
+  api_response api_resp = m_api_handler->create_sm_policy(smPolicyContextData);
+  response.headers()    = api_resp.headers;
 
-  status_code res = smpc_service->create_sm_policy_handler(
-      smPolicyContextData, decision, association_id, details_string);
-  nlohmann::json json_data;
-
-  switch (res) {
-    case status_code::CREATED:
-      http_code    = http_status_code_e::HTTP_STATUS_CODE_201_CREATED;
-      location     = m_address + base + "/sm-policies/" + association_id;
-      content_type = "application/json";
-      break;
-
-    case status_code::USER_UNKOWN:
-      problem_details.setCause("USER_UNKOWN");
-      problem_details.setDetail(details_string);
-      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
-      break;
-
-    case status_code::INVALID_PARAMETERS:
-      problem_details.setCause("ERROR_INITIAL_PARAMETERS");
-      problem_details.setDetail(details_string);
-      http_code = http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST;
-      break;
-
-    case status_code::CONTEXT_DENIED:
-      problem_details.setCause("POLICY_CONTEXT_DENIED");
-      problem_details.setDetail(details_string);
-      http_code = http_status_code_e::HTTP_STATUS_CODE_403_FORBIDDEN;
-      break;
-
-    default:
-      Logger::pcf_app().error("Unknown error code");
-      http_code =
-          http_status_code_e::HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR;
-      problem_details.setCause("INTERNAL_ERROR");
-      problem_details.setDetail("Internal Service Error: Unknown return code.");
-  }
-
-  if (http_code != http_status_code_e::HTTP_STATUS_CODE_201_CREATED) {
-    to_json(json_data, problem_details);
-  } else {
-    to_json(json_data, decision);
-    response.headers().add<Pistache::Http::Header::Location>(location);
-  }
-
-  response.headers().add<Pistache::Http::Header::ContentType>(
-      Pistache::Http::Mime::MediaType(content_type));
-
-  response.send(Pistache::Http::Code(http_code), json_data.dump().c_str());
+  response.send(Pistache::Http::Code(api_resp.status_code), api_resp.body);
 }
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
+}  // namespace oai::pcf::api
diff --git a/src/api-server/impl/SMPoliciesCollectionApiImpl.h b/src/api-server/impl/SMPoliciesCollectionApiImpl.h
old mode 100755
new mode 100644
index cdb357a076efc413cf302200dd824afa03d5594d..74ac948a50bb888b86529a0947f2115340e3eb07
--- a/src/api-server/impl/SMPoliciesCollectionApiImpl.h
+++ b/src/api-server/impl/SMPoliciesCollectionApiImpl.h
@@ -17,27 +17,25 @@
  *
  */
 
-#ifndef SM_POLICIES_COLLECTION_API_IMPL_H_
-#define SM_POLICIES_COLLECTION_API_IMPL_H_
+#pragma once
 
 #include <pistache/endpoint.h>
 #include <pistache/http.h>
 #include <pistache/router.h>
 #include <memory>
 #include <optional>
+#include <string>
 
 #include <SMPoliciesCollectionApi.h>
 
 #include "ProblemDetails.h"
 #include "SmPolicyContextData.h"
 #include "SmPolicyDecision.h"
-#include <string>
 
 #include "pcf_sm_policy_control.hpp"
+#include "sm_policies_collection_api_handler.h"
 
-namespace oai {
-namespace pcf {
-namespace api {
+namespace oai::pcf::api {
 
 using namespace oai::pcf::model;
 using namespace pcf;
@@ -47,20 +45,16 @@ class SMPoliciesCollectionApiImpl
  public:
   explicit SMPoliciesCollectionApiImpl(
       const std::shared_ptr<Pistache::Rest::Router>& rtr,
-      const std::shared_ptr<app::pcf_smpc> smpc_service, std::string address);
+      const std::shared_ptr<app::pcf_smpc>& smpc_service,
+      const std::string& address);
   ~SMPoliciesCollectionApiImpl() override = default;
 
   void create_sm_policy(
       const SmPolicyContextData& smPolicyContextData,
-      Pistache::Http::ResponseWriter& response);
+      Pistache::Http::ResponseWriter& response) override;
 
  private:
-  std::string m_address;
-  std::shared_ptr<app::pcf_smpc> smpc_service;
+  std::shared_ptr<sm_policies_collection_api_handler> m_api_handler;
 };
 
-}  // namespace api
-}  // namespace pcf
-}  // namespace oai
-
-#endif
+}  // namespace oai::pcf::api
diff --git a/src/api-server/pcf-api-server.cpp b/src/api-server/pcf-api-server.cpp
index 809d2af5412a04cd339d997e71cc6b2b7f61c7a6..682b13e54cd1f38ed6b04db884ec2943d72feb9e 100644
--- a/src/api-server/pcf-api-server.cpp
+++ b/src/api-server/pcf-api-server.cpp
@@ -69,7 +69,7 @@ void setUpUnixSignals(std::vector<int> quitSignals) {
 }
 #endif
 
-// using namespace oai::pcf::api;
+using namespace oai::pcf::api;
 
 void PCFApiServer::init(size_t thr) {
   auto opts = Pistache::Http::Endpoint::options().threads(thr);
@@ -87,6 +87,7 @@ void PCFApiServer::start() {
   m_httpEndpoint->setHandler(m_router->handler());
   m_httpEndpoint->serve();
 }
+
 void PCFApiServer::shutdown() {
   m_httpEndpoint->shutdown();
   m_smPoliciesCollectionApi = nullptr;
diff --git a/src/api-server/pcf-api-server.hpp b/src/api-server/pcf-api-server.hpp
index 88b0a6714f43e128ecd1adffc4cef3b8d447d3d6..5bdd9fbc471ec57960283fa55085feb7cb93b889 100644
--- a/src/api-server/pcf-api-server.hpp
+++ b/src/api-server/pcf-api-server.hpp
@@ -31,8 +31,7 @@
  *      contact@openairinterface.org
  */
 
-#ifndef FILE_PCF_API_SERVER_SEEN
-#define FILE_PCF_API_SERVER_SEEN
+#pragma once
 
 //#include "IndividualSMPolicyDocumentApi.h"
 
@@ -50,6 +49,8 @@
 #include "SMPoliciesCollectionApiImpl.h"
 #include "IndividualSMPolicyDocumentApiImpl.h"
 
+namespace oai::pcf::api {
+
 class PCFApiServer {
  public:
   PCFApiServer(
@@ -84,5 +85,4 @@ class PCFApiServer {
   std::shared_ptr<oai::pcf::api::IndividualSMPolicyDocumentApiImpl>
       m_individualSmPolicyDocumentApi;
 };
-
-#endif
+}  // namespace oai::pcf::api
\ No newline at end of file
diff --git a/src/api-server/pcf-http2-server.cpp b/src/api-server/pcf-http2-server.cpp
index 1c7039a8603a60d29a6f3861b7536938ff70b671..efa3d35644b0dd0ac7b8bb557f59f38f540e6b15 100644
--- a/src/api-server/pcf-http2-server.cpp
+++ b/src/api-server/pcf-http2-server.cpp
@@ -21,9 +21,9 @@
 
 /*! \file pcf_http2-server.h
  \brief
- \author  Rohan Kharade
+ \author  Rohan Kharade, Stefan Spettel
  \company Openairinterface Software Allianse
- \date 2022
+ \date 2023
  \email: rohan.kharade@openairinterface.org
  */
 
@@ -37,13 +37,15 @@
 
 #include "3gpp_29.500.h"
 #include "logger.hpp"
-#include "pcf.h"
 #include "pcf_config.hpp"
+#include "api_defs.h"
+#include "SmPolicyContextData.h"
 
 using namespace nghttp2::asio_http2;
 using namespace nghttp2::asio_http2::server;
 using namespace oai::pcf::model;
 using namespace oai::pcf::config;
+using namespace oai::pcf::api;
 
 extern std::unique_ptr<pcf_config> pcf_cfg;
 
@@ -55,41 +57,152 @@ void pcf_http2_server::start() {
   std::string nfId           = {};
   std::string subscriptionID = {};
 
-  // Get list of supported APIs
+  // SM Policies Collection API
   server.handle(
-      "/", [&](const request& request, const response& /* response */) {
-        request.on_data([&](const uint8_t* /* data */, std::size_t /* len */) {
-          if (request.method().compare("GET") == 0) {
-            // this->get_api_list(response);
+      sm_policies::get_route(),
+      [&](const request& request, const response& response) {
+        if (request.method() != "POST") {
+          handle_method_not_exists(response, request);
+          return;
+        }
+        auto request_body = std::make_shared<std::stringstream>();
+
+        request.on_data(
+            [&, request_body](const uint8_t* data, std::size_t len) {
+              if (len > 0) {
+                std::copy(
+                    data, data + len,
+                    std::ostream_iterator<uint8_t>(*request_body));
+                return;
+              }
+              SmPolicyContextData context;
+              try {
+                nlohmann::json::parse(request_body->str()).get_to(context);
+                context.validate();
+                api_response resp =
+                    m_collection_api_handler->create_sm_policy(context);
+                auto h_map = convert_headers(resp);
+                response.write_head(
+                    static_cast<unsigned int>(resp.status_code), h_map);
+                response.end(resp.body);
+                return;
+              } catch (std::exception& e) {
+                handle_parsing_error(response, e);
+                return;
+              }
+            });
+      });
+
+  // Individual SM Policy
+  // We match for sm-policies/*
+  server.handle(
+      sm_policies::get_route() + "/",
+      [&](const request& request, const response& response) {
+        std::vector<std::string> split_result;
+        boost::split(split_result, request.uri().path, boost::is_any_of("/"));
+        bool is_update = false;
+        bool is_delete = false;
+        bool is_get    = false;
+        std::string policy_id;
+        if (split_result[split_result.size() - 1] == "delete") {
+          is_delete = true;
+          policy_id = split_result[split_result.size() - 2];
+        } else if (split_result[split_result.size() - 1] == "update") {
+          is_update = true;
+          policy_id = split_result[split_result.size() - 2];
+        } else {
+          is_get    = true;
+          policy_id = split_result[split_result.size() - 1];
+        }
+        if ((is_delete || is_update) && request.method() != "POST") {
+          handle_method_not_exists(response, request);
+          return;
+        }
+        if (is_get && request.method() != "GET") {
+          handle_method_not_exists(response, request);
+          return;
+        }
+        auto request_body = std::make_shared<std::stringstream>();
+
+        request.on_data([&, request_body, is_get, is_update, is_delete,
+                         policy_id](const uint8_t* data, std::size_t len) {
+          if (len > 0) {
+            std::copy(
+                data, data + len,
+                std::ostream_iterator<uint8_t>(*request_body));
+            return;
+          }
+          SmPolicyDeleteData delete_data;
+          SmPolicyUpdateContextData update_context_data;
+          api_response resp;
+          try {
+            if (is_update) {
+              nlohmann::json::parse(request_body->str())
+                  .get_to(update_context_data);
+              update_context_data.validate();
+              resp = m_individual_api_handler->update_sm_policy(
+                  policy_id, update_context_data);
+            } else if (is_delete) {
+              nlohmann::json::parse(request_body->str()).get_to(delete_data);
+              delete_data.validate();
+              resp = m_individual_api_handler->delete_sm_policy(
+                  policy_id, delete_data);
+            } else if (is_get) {
+              resp = m_individual_api_handler->get_sm_policy(policy_id);
+            }
+            auto h_map = convert_headers(resp);
+            response.write_head(
+                static_cast<unsigned int>(resp.status_code), h_map);
+            response.end(resp.body);
+            return;
+          } catch (std::exception& e) {
+            handle_parsing_error(response, e);
+            return;
           }
         });
       });
 
+  // Default Route
+  server.handle("/", [&](const request& request, const response& response) {
+    handle_method_not_exists(response, request);
+    return;
+  });
+
   if (server.listen_and_serve(ec, m_address, std::to_string(m_port))) {
-    std::cerr << "HTTP Server error: " << ec.message() << std::endl;
+    Logger::pcf_sbi().error("HTTP Server error: %s", ec.message());
   }
 }
 
-//------------------------------------------------------------------------------
-
-//------------------------------------------------------------------------------
-// void pcf_http2_server::get_api_list(const response &response) {
-//   int http_code = 0;
-//   nlohmann::json json_data = {};
-//   std::string content_type = "application/json";
-//   header_map h;
-//   h.emplace("content-type", header_value{content_type});
-//   if (pcf_cfg.get_api_list(json_data)) {
-//     http_code = HTTP_STATUS_CODE_200_OK;
-//     response.write_head(http_code, h);
-//     response.end(json_data.dump(4).c_str());
-//   } else {
-//     http_code = HTTP_STATUS_CODE_503_SERVICE_UNAVAILABLE;
-//     response.write_head(http_code, h);
-//     response.end();
-//   }
-// }
-//------------------------------------------------------------------------------
 void pcf_http2_server::stop() {
   server.stop();
 }
+
+void pcf_http2_server::handle_method_not_exists(
+    const response& response, const request& request) {
+  Logger::pcf_sbi().warn(
+      "Invalid route/method called: %s : %s", request.method(),
+      request.uri().path);
+  response.write_head(static_cast<unsigned int>(
+      http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND));
+  response.end("The requested method does not exist");
+}
+
+void pcf_http2_server::handle_parsing_error(
+    const response& response, const std::exception& ex) {
+  Logger::pcf_sbi().warn("Parsing error: %s", ex.what());
+  response.write_head(static_cast<unsigned int>(
+      http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST));
+  // for security reasons it is better to not give the internal exception to the
+  // user, we can also decide to change that
+  response.end("Could not parse JSON data");
+}
+
+header_map pcf_http2_server::convert_headers(const api_response& response) {
+  header_map h_map;
+  for (const auto& hdr : response.headers.list()) {
+    std::stringstream ss;
+    hdr->write(ss);
+    h_map.emplace(hdr->name(), header_value{ss.str(), false});
+  }
+  return h_map;
+}
diff --git a/src/api-server/pcf-http2-server.hpp b/src/api-server/pcf-http2-server.hpp
index 7350b0bd885af00358bb5c0ed13a4e47d91d8359..274d5a8eec3d4049158cdbc7655666f16770b5a6 100644
--- a/src/api-server/pcf-http2-server.hpp
+++ b/src/api-server/pcf-http2-server.hpp
@@ -21,14 +21,13 @@
 
 /*! \file pcf_http2-server.h
  \brief
- \author  Rohan Kharade
+ \author  Rohan Kharade, Stefan Spettel
  \company Openairinterface Software Allianse
  \date 2022
  \email: rohan.kharade@openairinterface.org
  */
 
-#ifndef FILE_PCF_HTTP2_SERVER_SEEN
-#define FILE_PCF_HTTP2_SERVER_SEEN
+#pragma once
 
 #include "conversions.hpp"
 #include "pcf.h"
@@ -38,16 +37,28 @@
 #include <nghttp2/asio_http2_server.h>
 #include "IndividualSMPolicyDocumentApiImpl.h"
 #include "SMPoliciesCollectionApiImpl.h"
+#include "sm_policies_collection_api_handler.h"
+#include "individual_sm_policy_document_api_handler.h"
+
+namespace oai::pcf::api {
 
 class pcf_http2_server {
  public:
   pcf_http2_server(
       const std::string& addr, uint32_t port,
       const std::unique_ptr<oai::pcf::app::pcf_app>& pcf_app_inst)
-      : m_address(addr),
-        m_port(port),
-        server(),
-        smpc_service(pcf_app_inst->get_pcf_smpc_service()){};
+      : m_address(addr), m_port(port), server() {
+    // TODO hardcode http string, how to handle https
+    std::string address = "http://" + addr + ":" + std::to_string(port);
+
+    m_collection_api_handler =
+        std::make_shared<sm_policies_collection_api_handler>(
+            pcf_app_inst->get_pcf_smpc_service(), address);
+
+    m_individual_api_handler =
+        std::make_shared<individual_sm_policy_document_api_handler>(
+            pcf_app_inst->get_pcf_smpc_service());
+  };
 
   void start();
   void init(size_t /* thr */) {}
@@ -63,7 +74,20 @@ class pcf_http2_server {
 
   nghttp2::asio_http2::server::http2 server;
 
-  std::shared_ptr<oai::pcf::app::pcf_smpc> smpc_service;
+  std::shared_ptr<sm_policies_collection_api_handler> m_collection_api_handler;
+  std::shared_ptr<individual_sm_policy_document_api_handler>
+      m_individual_api_handler;
+
+  static void handle_method_not_exists(
+      const nghttp2::asio_http2::server::response& response,
+      const nghttp2::asio_http2::server::request& request);
+
+  static void handle_parsing_error(
+      const nghttp2::asio_http2::server::response& response,
+      const std::exception& ex);
+
+  static nghttp2::asio_http2::header_map convert_headers(
+      const api_response& response);
 };
 
-#endif
+}  // namespace oai::pcf::api
diff --git a/src/common/pcf.h b/src/common/pcf.h
index 2bf2861e7ba561f13714c2f297f1f69df07695af..caa8722a5fc219c816046978309be1b2eba283eb 100644
--- a/src/common/pcf.h
+++ b/src/common/pcf.h
@@ -151,6 +151,4 @@ typedef struct nf_service_s {
 #define AMF_NUMBER_RETRIES 3
 #define UDM_NUMBER_RETRIES 3
 
-#define SM_POLICY_API_NAME "npcf-smpolicycontrol";
-
 #endif
diff --git a/src/oai_pcf/CMakeLists.txt b/src/oai_pcf/CMakeLists.txt
index 024b3012007046683dc14c9535f3de1f426b7640..2780ca581879ddeadf3c5abf31f7d9586508e056 100644
--- a/src/oai_pcf/CMakeLists.txt
+++ b/src/oai_pcf/CMakeLists.txt
@@ -278,6 +278,7 @@ include_directories(${SRC_TOP_DIR}/api-server)
 include_directories(${SRC_TOP_DIR}/api-server/api)
 include_directories(${SRC_TOP_DIR}/api-server/impl)
 include_directories(${SRC_TOP_DIR}/api-server/model)
+include_directories(${SRC_TOP_DIR}/api-server/handler)
 
 add_executable(pcf
   ${SRC_TOP_DIR}/oai_pcf/main.cpp
diff --git a/src/oai_pcf/main.cpp b/src/oai_pcf/main.cpp
index 922900d818daa9f3dd30c2471905e06f8fa780c0..46bef051e68e74f2ad61bdd89c5ac85f3b0b6d86 100644
--- a/src/oai_pcf/main.cpp
+++ b/src/oai_pcf/main.cpp
@@ -32,6 +32,7 @@ using namespace std;
 using namespace oai::pcf::app;
 using namespace oai::pcf::config;
 using namespace oai::utils;
+using namespace oai::pcf::api;
 
 using namespace oai::config;
 
diff --git a/src/pcf_app/pcf_nrf.cpp b/src/pcf_app/pcf_nrf.cpp
index 8c38b49102103fd5a12bef03286da913e260cc14..6e282a355946433bacd599758ce7416cfac5a696 100644
--- a/src/pcf_app/pcf_nrf.cpp
+++ b/src/pcf_app/pcf_nrf.cpp
@@ -34,6 +34,7 @@
 #include "pcf_config.hpp"
 #include "pcf_client.hpp"
 #include "Snssai.h"
+#include "api_defs.h"
 
 #include <boost/uuid/random_generator.hpp>
 #include <boost/uuid/uuid_io.hpp>
@@ -80,8 +81,8 @@ void pcf_nrf::generate_pcf_profile() {
 
   // NF services
   nf_service_t nf_service        = {};
-  nf_service.service_instance_id = SM_POLICY_API_NAME;
-  nf_service.service_name        = SM_POLICY_API_NAME;
+  nf_service.service_instance_id = oai::pcf::api::sm_policies::API_NAME;
+  nf_service.service_name        = oai::pcf::api::sm_policies::API_NAME;
   nf_service_version_t version   = {};
   version.api_version_in_uri     = pcf_cfg->local().get_sbi().get_api_version();
   version.api_full_version       = "1.0.0";  // TODO: to be updated