Launch dbus session for docker unit tests

Fix for openbmc/openbmc#1137

Change-Id: I9e8a41820c6b85bff3625af4ab7b488a66b39c65
Signed-off-by: Leonel Gonzalez <lgonzalez@us.ibm.com>
diff --git a/build-unit-test-docker.sh b/build-unit-test-docker.sh
index cc1d887..61b5430 100755
--- a/build-unit-test-docker.sh
+++ b/build-unit-test-docker.sh
@@ -63,7 +63,8 @@
     libssl-dev \
     sudo \
     wget \
-    git
+    git \
+    dbus
 
 RUN easy_install pip
 RUN pip install inflection
diff --git a/run-unit-test-docker.sh b/run-unit-test-docker.sh
index 7ebb0a9..283017b 100755
--- a/run-unit-test-docker.sh
+++ b/run-unit-test-docker.sh
@@ -4,6 +4,7 @@
 #
 # It uses a few variables which are part of Jenkins build job matrix:
 #   distro = fedora|ubuntu|ubuntu:14.04|ubuntu:16.04
+#   dbus_sys_config_file = <path of the dbus config file>
 #   WORKSPACE = <location of unit test execution script>
 
 # Trace bash processing. Set -e so when a step fails, we fail the build
@@ -16,6 +17,8 @@
 OBMC_BUILD_SCRIPTS="openbmc-build-scripts"
 UNIT_TEST_PY_DIR="scripts"
 UNIT_TEST_PY="unit-test.py"
+DBUS_UNIT_TEST_PY="dbus-unit-test.py"
+DBUS_SYS_CONFIG_FILE=${dbus_sys_config_file:-"/usr/share/dbus-1/system.conf"}
 
 # Timestamp for job
 echo "Unit test build started, $(date)"
@@ -47,17 +50,27 @@
 ${WORKSPACE}/${UNIT_TEST_PY}
 chmod a+x ${WORKSPACE}/${UNIT_TEST_PY}
 
+# Copy dbus unit test script into workspace
+cp ${WORKSPACE}/${OBMC_BUILD_SCRIPTS}/${UNIT_TEST_PY_DIR}/${DBUS_UNIT_TEST_PY} \
+${WORKSPACE}/${DBUS_UNIT_TEST_PY}
+chmod a+x ${WORKSPACE}/${DBUS_UNIT_TEST_PY}
+
 # Configure docker build
 cd ${WORKSPACE}/${OBMC_BUILD_SCRIPTS}
 echo "Building docker image with build-unit-test-docker.sh"
 ./build-unit-test-docker.sh ${DOCKER_IMG_NAME} ${DISTRO}
 
+# Unit test and parameters
+UNIT_TEST="${WORKSPACE}/${UNIT_TEST_PY},-w,${WORKSPACE},-p,${UNIT_TEST_PKG},-v"
+
 # Run the docker unit test container with the unit test execution script
 echo "Executing docker image"
 docker run --cap-add=sys_admin --rm=true \
+    --privileged=true \
     -w "${WORKSPACE}" -v "${WORKSPACE}":"${WORKSPACE}" \
     -t ${DOCKER_IMG_NAME} \
-    ${WORKSPACE}/${UNIT_TEST_PY} -w ${WORKSPACE} -p ${UNIT_TEST_PKG} -v
+    ${WORKSPACE}/${DBUS_UNIT_TEST_PY} -u ${UNIT_TEST} \
+    -f ${DBUS_SYS_CONFIG_FILE}
 
 # Timestamp for build
 echo "Unit test build completed, $(date)"
diff --git a/scripts/dbus-unit-test.py b/scripts/dbus-unit-test.py
new file mode 100755
index 0000000..a6ccb72
--- /dev/null
+++ b/scripts/dbus-unit-test.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+"""
+This script launches a dbus session, sets the DBUS_SESSION_BUS_ADDRESS
+and DBUS_STARTER_BUS_TYPE Eenvironment variables and puts the generated files
+in the dbus dir location passed as a paramter. It then runs the unit test
+script, and then cleans up the generated dbus files.
+"""
+
+from subprocess import check_call, check_output
+import os
+import sys
+import argparse
+import re
+import tempfile
+
+def launch_session_dbus(dbus_dir, dbus_config_file):
+    """
+    Launches a session debus using a modified config file and
+    sets the DBUS_SESSION_BUS_ADDRESS environment variable
+
+    Parameter descriptions:
+    dbus_dir            Directory location for generated files
+    dbus_config_file    File location of dbus sys config file
+    """
+    dbus_pid = os.path.join(dbus_dir,'pid')
+    dbus_socket = os.path.join(dbus_dir,'system_bus_socket')
+    dbus_local_conf = os.path.join(dbus_dir,'system-local.conf')
+    if os.path.isfile(dbus_pid):
+        os.remove(dbus_pid)
+    with open(dbus_config_file) as infile, \
+         open(dbus_local_conf, 'w') as outfile:
+        for line in infile:
+            line = re.sub('<type>.*</type>','<type>session</type>', \
+                          line, flags=re.DOTALL)
+            line = re.sub('<pidfile>.*</pidfile>', \
+                          '<pidfile>%s</pidfile>' % dbus_pid, \
+                          line, flags=re.DOTALL)
+            line = re.sub('<listen>.*</listen>', \
+                          '<listen>unix:path=%s</listen>' % dbus_socket, \
+                          line, flags=re.DOTALL)
+            line = re.sub('<deny','<allow', line)
+            outfile.write(line)
+    infile.close()
+    outfile.close()
+    command = ['dbus-daemon', '--config-file=%s' % dbus_local_conf, \
+               '--print-address']
+    out = check_output(command).splitlines()
+    os.environ['DBUS_SESSION_BUS_ADDRESS'] = out[0]
+    os.environ['DBUS_STARTER_BUS_TYPE'] = 'session'
+
+def dbus_cleanup(dbus_dir):
+    """
+    Kills the dbus session started by launch_session_dbus
+    and removes the generated files.
+
+    Parameter descriptions:
+    dbus_dir            Directory location of generated files
+    """
+
+    dbus_pid = os.path.join(dbus_dir,'pid')
+    dbus_socket = os.path.join(dbus_dir,'system_bus_socket')
+    dbus_local_conf = os.path.join(dbus_dir,'system-local.conf')
+    if os.path.isfile(dbus_pid):
+        dbus_pid = open(dbus_pid,'r').read().replace('\n','')
+        check_call(['kill', dbus_pid])
+    if os.path.isfile(dbus_local_conf):
+        os.remove(dbus_local_conf)
+    if os.path.exists(dbus_socket):
+        os.remove(dbus_socket)
+
+
+if __name__ == '__main__':
+
+    # Set command line arguments
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument("-f", "--dbussysconfigfile",
+                        dest="DBUS_SYS_CONFIG_FILE",
+                        required=True, help="Dbus sys config file location")
+    parser.add_argument("-u", "--unittestandparams",
+                        dest="UNIT_TEST",
+                        required=True, help="Unit test script and params \
+                        as comma delimited string")
+    args = parser.parse_args(sys.argv[1:])
+    DBUS_DIR = tempfile.mkdtemp()
+    DBUS_SYS_CONFIG_FILE = args.DBUS_SYS_CONFIG_FILE
+    UNIT_TEST = args.UNIT_TEST
+
+    launch_session_dbus(DBUS_DIR, DBUS_SYS_CONFIG_FILE)
+    check_call(UNIT_TEST.split(','), env=os.environ)
+    dbus_cleanup(DBUS_DIR)