multi VM: Fix p2p deployment
[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 from conf import settings
30
31
32 class LoaderServant(object):
33     """Class implements basic dynamic import operations.
34     """
35     _class_name = None
36     _path = None
37     _interface = None
38
39     def __init__(self, path, class_name, interface):
40         """LoaderServant constructor
41
42         Intializes all data needed for import operations.
43
44         Attributes:
45             path: path to directory which contains implementations derived from
46                     interface.
47             class_name: Class name which will be returned in get_class
48                         method, if such definition exists in directory
49                         represented by path,
50             interface: interface type. Every object which doesn't
51                         implement this particular interface will be
52                         filtered out.
53         """
54         self._class_name = class_name
55         self._path = path
56         self._interface = interface
57
58     def get_class(self):
59         """Returns class type based on parameters passed in __init__.
60
61         :return: Type of the found class.
62             None if class hasn't been found
63         """
64
65         return self.load_module(path=self._path,
66                                 interface=self._interface,
67                                 class_name=self._class_name)
68
69     def get_classes(self):
70         """Returns all classes in path derived from interface
71
72         :return: Dictionary with following data:
73             - key: String representing class name,
74             - value: Class type.
75         """
76         return self.load_modules(path=self._path,
77                                  interface=self._interface)
78
79     def get_classes_printable(self):
80         """Returns all classes derived from _interface found in path
81
82         :return: String - list of classes in printable format.
83         """
84
85         out = self.load_modules(path=self._path,
86                                 interface=self._interface)
87         results = []
88
89         for (name, mod) in list(out.items()):
90             desc = (mod.__doc__ or 'No description').strip().split('\n')[0]
91             results.append((name, desc))
92
93         header = 'Classes derived from: ' + self._interface.__name__
94         output = [header + '\n' + '=' * len(header) + '\n']
95
96         for (name, desc) in results:
97             output.append('* %-18s%s' % ('%s:' % name, desc))
98
99             output.append('')
100
101         output.append('')
102
103         return '\n'.join(output)
104
105     @staticmethod
106     def load_module(path, interface, class_name):
107         """Imports everything from given path and returns class type
108
109         This is based on following conditions:
110             - Class is derived from interface,
111             - Class type name matches class_name.
112
113         :return: Type of the found class.
114             None if class hasn't been found
115         """
116
117         results = LoaderServant.load_modules(
118             path=path, interface=interface)
119
120         if class_name in results:
121             logging.info(
122                 "Class found: " + class_name + ".")
123             return results.get(class_name)
124
125         return None
126
127     @staticmethod
128     def load_modules(path, interface):
129         """Returns dictionary of class name/class type found in path
130
131         This is based on following conditions:
132             - classes found under path are derived from interface.
133             - class is not interface itself.
134
135         :return: Dictionary with following data:
136             - key: String representing class name,
137             - value: Class type.
138         """
139         result = {}
140
141         for _, mod in LoaderServant._load_all_modules(path):
142             # find all classes derived from given interface, but suppress
143             # interface itself and any abstract class starting with iface name
144             gens = dict((k, v) for (k, v) in list(mod.__dict__.items())
145                         if type(v) == type and
146                         issubclass(v, interface) and
147                         not k.startswith(interface.__name__))
148             if gens:
149                 for (genname, gen) in list(gens.items()):
150                     result[genname] = gen
151         return result
152
153     @staticmethod
154     def _load_all_modules(path):
155         """Load all modules from ``path`` directory.
156
157         This is based on the design used by OFTest:
158             https://github.com/floodlight/oftest/blob/master/oft
159
160         :param path: Path to a folder of modules.
161
162         :return: List of modules in a folder.
163         """
164         mods = []
165
166         for root, _, filenames in os.walk(path):
167             # Iterate over each python file
168             for filename in fnmatch.filter(filenames, '[!.]*.py'):
169                 modname = os.path.splitext(os.path.basename(filename))[0]
170
171                 # skip module load if it is excluded by configuration
172                 if modname in settings.getValue('EXCLUDE_MODULES'):
173                     continue
174
175                 try:
176                     if modname in sys.modules:
177                         mod = sys.modules[modname]
178                     else:
179                         mod = imp.load_module(
180                             modname, *imp.find_module(modname, [root]))
181                 except ImportError:
182                     logging.error('Could not import file ' + filename)
183                     raise
184
185                 mods.append((modname, mod))
186
187         return mods