Enable PVVP deployment for DPDK Vhost User and Vhost Cuse
[vswitchperf.git] / core / loader / loader_servant.py
1 # Copyright 2015 Intel Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 """Loader servant module used by Loader.
16
17 Module is inteded to be private to serve only Loader itself, nevertheless
18 some methods are exposed outside and can be used by any other clients:
19 - load_modules(self, path, interface)
20 - load_module(self, path, interface, class_name)
21 Those method are stateless static members.
22
23 """
24 import os
25 from os import sys
26 import imp
27 import fnmatch
28 import logging
29
30
31 class LoaderServant(object):
32     """Class implements basic dynamic import operations.
33     """
34     _class_name = None
35     _path = None
36     _interface = None
37
38     def __init__(self, path, class_name, interface):
39         """LoaderServant constructor
40
41         Intializes all data needed for import operations.
42
43         Attributes:
44             path: path to directory which contains implementations derived from
45                     interface.
46             class_name: Class name which will be returned in get_class
47                         method, if such definition exists in directory
48                         represented by path,
49             interface: interface type. Every object which doesn't
50                         implement this particular interface will be
51                         filtered out.
52         """
53         self._class_name = class_name
54         self._path = path
55         self._interface = interface
56
57     def get_class(self):
58         """Returns class type based on parameters passed in __init__.
59
60         :return: Type of the found class.
61             None if class hasn't been found
62         """
63
64         return self.load_module(path=self._path,
65                                 interface=self._interface,
66                                 class_name=self._class_name)
67
68     def get_classes(self):
69         """Returns all classes in path derived from interface
70
71         :return: Dictionary with following data:
72             - key: String representing class name,
73             - value: Class type.
74         """
75         return self.load_modules(path=self._path,
76                                  interface=self._interface)
77
78     def get_classes_printable(self):
79         """Returns all classes derived from _interface found in path
80
81         :return: String - list of classes in printable format.
82         """
83
84         out = self.load_modules(path=self._path,
85                                 interface=self._interface)
86         results = []
87
88         for (name, mod) in list(out.items()):
89             desc = (mod.__doc__ or 'No description').strip().split('\n')[0]
90             results.append((name, desc))
91
92         output = [
93             'Classes derived from: ' + self._interface.__name__ + '\n======\n']
94
95         for (name, desc) in results:
96             output.append('* %-18s%s' % ('%s:' % name, desc))
97
98             output.append('')
99
100         output.append('')
101
102         return '\n'.join(output)
103
104     @staticmethod
105     def load_module(path, interface, class_name):
106         """Imports everything from given path and returns class type
107
108         This is based on following conditions:
109             - Class is derived from interface,
110             - Class type name matches class_name.
111
112         :return: Type of the found class.
113             None if class hasn't been found
114         """
115
116         results = LoaderServant.load_modules(
117             path=path, interface=interface)
118
119         if class_name in results:
120             logging.info(
121                 "Class found: " + class_name + ".")
122             return results.get(class_name)
123
124         return None
125
126     @staticmethod
127     def load_modules(path, interface):
128         """Returns dictionary of class name/class type found in path
129
130         This is based on following conditions:
131             - classes found under path are derived from interface.
132             - class is not interface itself.
133
134         :return: Dictionary with following data:
135             - key: String representing class name,
136             - value: Class type.
137         """
138         result = {}
139
140         for _, mod in LoaderServant._load_all_modules(path):
141             # find all classes derived from given interface, but suppress
142             # interface itself and any abstract class starting with iface name
143             gens = dict((k, v) for (k, v) in list(mod.__dict__.items())
144                         if type(v) == type and
145                         issubclass(v, interface) and
146                         not k.startswith(interface.__name__))
147             if gens:
148                 for (genname, gen) in list(gens.items()):
149                     result[genname] = gen
150         return result
151
152     @staticmethod
153     def _load_all_modules(path):
154         """Load all modules from ``path`` directory.
155
156         This is based on the design used by OFTest:
157             https://github.com/floodlight/oftest/blob/master/oft
158
159         :param path: Path to a folder of modules.
160
161         :return: List of modules in a folder.
162         """
163         mods = []
164
165         for root, _, filenames in os.walk(path):
166             # Iterate over each python file
167             for filename in fnmatch.filter(filenames, '[!.]*.py'):
168                 modname = os.path.splitext(os.path.basename(filename))[0]
169
170                 try:
171                     if modname in sys.modules:
172                         mod = sys.modules[modname]
173                     else:
174                         mod = imp.load_module(
175                             modname, *imp.find_module(modname, [root]))
176                 except ImportError:
177                     logging.error('Could not import file ' + filename)
178                     raise
179
180                 mods.append((modname, mod))
181
182         return mods