# vim: set fileencoding=utf-8
# Pavel Odvody <podvody@redhat.com>
#
# HICA - Host integrated container applications
#
# MIT License (C) 2015

import os
from base.hica_base import *
import subprocess
_container_lib_location = '/external_libs'

class IntrospectRuntimeInjector(HicaInjector):

  def get_description(self):
    return "Runs a binary ({0}) which makes a white list into the container".format(self.labels.get_value('io.hica.introspect_runtime'))

  def get_config_key(self):
    return "io.hica.introspect_runtime"

  def get_injected_args(self):
    return (("--introspect-runtime", HicaValueType.PATH, ""),
             ("--introspect-runtime-whitelist", HicaValueType.STRING, ""))

  def _get_runtime(self):
    return dict(self.labels.query(''))['io.hica.introspect_runtime']

  def _get_whitelist(self):
    return dict(self.labels.query(''))['io.hica.introspect_runtime.whitelist'].split(':')

  def _run_introspection(self, runtime='', whitelist=[], verbose=False):
    """ Figure out which objects are opened by a test binary and are matched by the white list. 

    :param runtime: The binary to run. 
    :type runtime: str
    :param whitelist: A list of regular expressions describing acceptable library names
    :type whitelist: [str]    
    """
    found_objects = set()
    try:
      # Retrieve list of successfully opened objects
      p = subprocess.Popen("strace {} 2>&1| grep open | grep -v ENOENT | cut -d '\"'  -f 2 | sort | uniq -c | awk '{{print $2}}\n'".format(runtime), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
      (stdout, stderr) = p.communicate()
      opened_objects = stdout.split()
      
      # filter opened objects through white list.
      for obj in opened_objects:
        for wl in whitelist:
          m = re.match('.*' + wl + '[\..*]?', obj)
          if m:
            found_objects.add(obj)
            if verbose:
              print("Found whitelisted {} at path {}".format(wl, obj))
            continue
    except Exception as e:
      print e

    return found_objects

  def __get_container_path(self, host_path):
    """ A simple helper function to determine the path of a host library
    inside the container

    :param host_path: The path of the library on the host
    :type host_path: str
    """
    libname = os.path.split(host_path)[1]
    return os.path.join(_container_lib_location, libname)

  def __remove_duplicates(self, library):
    libset = set()
    unique_libs = []
    for lib in library:
      unique_libs.append(lib)

  def inject_config(self, config, from_args):
    """
    :param config:
    :type config: list
    :param from_args:
    :type from_args: dict
    """
    # First get required values from labelStore
    runtime = self._get_runtime()
    whitelist = self._get_whitelist()
    #Run introspection on the libraries to retrieve list of libraries to link
    found_libraries = self._run_introspection(runtime, whitelist, verbose=True)
    container_path_set=set()
    for library in found_libraries:

      #disallow duplicate library targets
      cpath = self.__get_container_path(library)
      if cpath in container_path_set:
        continue
      container_path_set.add(cpath)

      config.append("--volume={0}:{1}".format(library, cpath))
    
    config.extend(["-e","LD_LIBRARY_PATH={0}".format(_container_lib_location)])
    config.extend(["-e","LIBGL_DRIVERS_PATH={0}".format(_container_lib_location)])

@@ -718,37 +718,32 @@
 lf):%0A    return 
-dict(
 self.labels.quer
@@ -734,35 +734,34 @@
 self.labels.
-query(''))%5B
+get_value(
 'io.hica.int
@@ -776,17 +776,17 @@
 runtime'
-%5D
+)
 %0A%0A  def 
@@ -822,13 +822,8 @@
 urn 
-dict(
 self
@@ -834,19 +834,18 @@
 els.
-query(''))%5B
+get_value(
 'io.
@@ -878,17 +878,17 @@
 itelist'
-%5D
+)
 .split('
@@ -1347,17 +1347,22 @@
 s%0A      
-p
+strace
  = subpr
@@ -1377,172 +1377,35 @@
 pen(
-%22
+%5B'
 strace
- %7B%7D 2%3E&1%7C grep open %7C grep -v ENOENT %7C cut -d '%5C%22'  -f 2 %7C sort %7C uniq -c %7C awk '%7B%7Bprint $2%7D%7D%5Cn'%22.format(runtime), shell=True, stderr=subprocess.PIPE, stdout
+', runtime%5D, stderr
 =sub
@@ -1425,22 +1425,17 @@
 %0A      (
-stdout
+_
 , stderr
@@ -1438,17 +1438,22 @@
 derr) = 
-p
+strace
 .communi
@@ -1487,22 +1487,252 @@
  = s
-tdout.split()%0A
+et()%0A      for line in stderr.split('%5Cn'):%0A          if 'open' in line and 'ENOENT' not in line:%0A              start = line.index('%22')%0A              end = line.index('%22', start + 1)%0A              opened_objects.add(line%5Bstart + 1:end%5D)%0A        
     

Cleanup the code in introspect runtime