check_dependencies: Import modules dynamically and find related debian packages

This way we don't need to manually add new imports here or drop unusued
ones. It also makes sure local imports in all our py files is correct.
For instance, running the script already caught an issue which is added
to this patch (osmo_ms_driver/__main__.py).

This new version of the script also allows specifying subsets of
features to skip when checking for dependencies. This way, for instance
somebody not willing to use a sispm powersupply can stil check all the
needed dependencies are fine.

This new tool will make it easier to slowly make some dependencies only
used by some object test classes optional (for instance, python-smpplib
if user doesn't want to run an ESME node).

It also allows to retrieve the required debian/manually installed
packages when run with "-p" option:
"""
Debian packages:
        libpython3.5-minimal:amd64
        python3-gi
        python3-six
        libpython3.5-stdlib:amd64
        python3-pygments
        python3-yaml
        python3-mako
        python3-numpy
        python3-markupsafe

Modules without debian package (pip or setuptools?):
        usb                  [dpkg-query: no path found matching pattern /usr/local/lib/python3.5/dist-packages/usb/_interop.py]
        pydbus               [dpkg-query: no path found matching pattern /usr/local/lib/python3.5/dist-packages/pydbus/proxy.py]
        smpplib              [dpkg-query: no path found matching pattern /usr/local/lib/python3.5/dist-packages/smpplib/command_codes.py]
        sispm                [dpkg-query: no path found matching pattern /usr/local/lib/python3.5/dist-packages/sispm/__init__.py]
"""

Change-Id: I29ddf8971837754abd930d847bd1036e8e510de6
This commit is contained in:
Pau Espin 2020-04-17 19:37:10 +02:00
parent 2b959580b9
commit 045245d5ae
2 changed files with 125 additions and 28 deletions

View File

@ -3,31 +3,128 @@
# just import all python3 modules used by osmo-gsm-tester to make sure they are
# installed.
from inspect import getframeinfo, stack
from mako.lookup import TemplateLookup
from mako.template import Template
import argparse
import contextlib
import copy
import difflib
import fcntl
import inspect
import io
import os
import pprint
import re
import subprocess
import sys
import tempfile
import time
import traceback
import yaml
import pydbus
import sqlite3
import sispm
import smpplib
import urllib.request
import xml.etree.ElementTree
import numpy
print('dependencies ok')
import os
import sys
import argparse
import pprint
import subprocess
feature_module_map = {
'powersupply_intellinet' : ['powersupply_intellinet'],
'powersupply_sispm' : ['powersupply_sispm'],
'rfemu_amarisoftctrl': ['rfemu_amarisoftctrl'],
'rfemu_minicircuits': ['rfemu_minicircuits'],
}
def skip_features_to_skip_modules(skip_features):
skip_obj_modules = []
for skip_feature in skip_features:
if skip_feature not in feature_module_map:
raise Exception('feature %s doesn\'t exist!' % skip_feature)
for skip_module in feature_module_map[skip_feature]:
skip_obj_modules.append(skip_module)
return skip_obj_modules
def import_runtime_dependencies():
# we don't have any right now, but in the future if we import a module during runtime (eg inside a function), then we need to place it here:
# import foobar
pass
def import_all_py_in_dir(rel_path, skip_modules=[]):
selfdir = os.path.dirname(os.path.abspath(__file__))
dir = os.path.join(selfdir, rel_path)
print('importing files in directory %s' % dir)
for entry in os.listdir(dir):
full_entry = os.path.join(selfdir, rel_path, entry)
if not os.path.isfile(full_entry):
if args.verbose:
print('skipping entry %s' % full_entry)
continue
if not full_entry.endswith('.py'):
if args.verbose:
print('skipping file %s' % full_entry)
continue
modulename = entry[:-3]
if modulename in skip_modules:
if args.verbose:
print('skipping module %s' % modulename)
continue
modulepath = rel_path.replace('/', '.') + '.' + modulename
print('importing %s' % modulepath)
__import__(modulepath, globals(), locals())
def get_module_names():
all_modules=sys.modules.items()
all_modules_filtered = {}
for mname, m in all_modules:
if not hasattr(m, '__file__'):
continue # skip built-in modules
if mname.startswith('_'):
continue # skip internal modules
if mname.startswith('src.osmo_') or 'osmo_gsm_tester' in mname or 'osmo_ms_driver' in mname:
continue # skip our own local modules
mname = mname.split('.')[0] # store only main module
if m not in all_modules_filtered.values():
all_modules_filtered[mname] = m
return all_modules_filtered
def print_deb_packages(modules):
packages_deb = []
modules_err = []
for mname, m in modules.items():
proc = subprocess.Popen(["dpkg", "-S", m.__file__], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
outs, errs = proc.communicate()
if args.verbose:
print('out: %s, err: %s' %(outs, errs))
if len(errs): # error -> package not found (installed through pip?)
modules_err.append((mname, errs.decode('utf-8')))
elif len(outs):
outs = outs.decode('utf-8')
outs = outs.split()[0].rstrip(':') # first part is debian package name
if not outs in packages_deb:
packages_deb.append(outs)
else:
print('WARNING: dpkg returns empty!')
print('Debian packages:')
for pkgname in packages_deb:
print("\t" + pkgname)
print()
print('Modules without debian package (pip or setuptools?):')
for mname, err in modules_err:
print("\t" + mname.ljust(20) + " [" + err.rstrip() +"]")
parser = argparse.ArgumentParser(epilog=__doc__, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-s', '--skip-feature', dest='skip_features', choices=feature_module_map.keys(), action='append',
help='''All osmo-gsm-tester features not used by the user running the script''')
parser.add_argument('-p', '--distro-packages', dest='distro_packages', action='store_true',
help='Print distro packages installing modules')
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
help='Print a lot more information')
args = parser.parse_args()
skip_obj_modules = skip_features_to_skip_modules(list(args.skip_features or []))
print('Skip checking modules: %r' % skip_obj_modules)
# We need to add it for cross-references between osmo_ms_driver and osmo_gsm_tester to work:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'src/'))
import_all_py_in_dir('src/osmo_ms_driver')
import_all_py_in_dir('src/osmo_gsm_tester/core')
import_all_py_in_dir('src/osmo_gsm_tester/obj', skip_obj_modules)
import_all_py_in_dir('src/osmo_gsm_tester')
import_runtime_dependencies()
print('Importing dependencies ok, all installed')
print('Retreiving list of imported modules...')
modules = get_module_names()
if args.verbose:
for mname, m in modules.items():
print('%s --> %s' %(mname, m.__file__))
if args.distro_packages:
print('Generating distro package list from imported module list...')
print_deb_packages(modules)

View File

@ -23,7 +23,7 @@ from .cdf import cdfs
from .starter import BinaryOptions, MobileTestStarter
from .test_support import imsi_ki_gen
from osmo_gsm_tester.core import log, util
from osmo_gsm_tester import ms_osmo_mobile
from osmo_gsm_tester.obj import ms_osmo_mobile
# System modules
from datetime import timedelta