Skip to content

Commit b1736b8

Browse files
committed
feat: when generate a input/ouput actor if an argument is missing, try to find its opposite actor configuration
1 parent a14f67a commit b1736b8

File tree

2 files changed

+209
-52
lines changed

2 files changed

+209
-52
lines changed

powerapi/cli/tools.py

Lines changed: 76 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -159,72 +159,96 @@ def parse_argv(self):
159159
sys.exit()
160160

161161

162-
DB_FACTORY = {
163-
'mongodb': lambda db_config: MongoDB(db_config['uri'], db_config['db'], db_config['collection']),
164-
'csv': lambda db_config: CsvDB(current_path=os.getcwd() if 'directory' not in db_config else db_config['directory'],
165-
files=[] if 'files' not in db_config else db_config['files']),
166-
'influxdb': lambda db_config: InfluxDB(db_config['uri'], db_config['port'], db_config['db']),
167-
'opentsdb': lambda db_config: OpenTSDB(db_config['uri'], db_config['port'], db_config['metric_name']),
168-
}
162+
class Generator:
169163

164+
def __init__(self, component_group_name):
165+
self.component_group_name = component_group_name
170166

171-
MODEL_FACTORY = {
172-
'hwpc_report': HWPCModel(),
173-
'power_report': PowerModel(),
174-
}
167+
def generate(self, config):
168+
if self.component_group_name not in config:
169+
print('CLI error : no ' + self.component_group_name + ' specified', file=sys.stderr)
170+
sys.exit()
175171

172+
actors = {}
173+
174+
for component_name, component_config in config[self.component_group_name].items():
175+
try:
176+
actors[component_config['name']] = self._gen_actor(component_name, component_config, config)
177+
except KeyError as exn:
178+
msg = 'CLI error : argument ' + exn.args[0]
179+
msg += ' needed with --output ' + component_name
180+
print(msg, file=sys.stderr)
181+
sys.exit()
182+
183+
return actors
184+
185+
def _gen_actor(self, component_name, component_config, main_config):
186+
raise NotImplementedError()
176187

177-
def generate_pullers(config, report_filter):
178-
# default mode if no input are specified
179-
if 'input' not in config:
180-
print('CLI error : no input specified', file=sys.stderr)
181-
sys.exit()
182188

183-
pullers = {}
184-
for db_config in config['input']:
189+
class DBActorGenerator(Generator):
190+
191+
def __init__(self, component_group_name):
192+
Generator.__init__(self, component_group_name)
193+
self.model_factory = {
194+
'hwpc_report': HWPCModel(),
195+
'power_report': PowerModel(),
196+
}
197+
198+
self.db_factory = {
199+
'mongodb': lambda db_config: MongoDB(db_config['uri'], db_config['db'], db_config['collection']),
200+
'csv': lambda db_config: CsvDB(current_path=os.getcwd() if 'directory' not in db_config else db_config['directory'],
201+
files=[] if 'files' not in db_config else db_config['files']),
202+
'influxdb': lambda db_config: InfluxDB(db_config['uri'], db_config['port'], db_config['db']),
203+
'opentsdb': lambda db_config: OpenTSDB(db_config['uri'], db_config['port'], db_config['metric_name']),
204+
}
205+
206+
def _generate_db(self, db_name, db_config, main_config):
185207
try:
186-
factory = DB_FACTORY[db_config['type']]
187-
model = MODEL_FACTORY[db_config['model']]
188-
name = db_config['name']
189-
puller = PullerActor(name, factory(db_config), report_filter, model, stream_mode=config['stream'],
190-
level_logger=config['verbose'])
191-
pullers[name] = puller
208+
return self.db_factory[db_name](db_config)
192209
except KeyError as exn:
193-
msg = 'CLI error : argument ' + exn.args[0]
194-
msg += ' needed with --output ' + db_config['type']
195-
print(msg, file=sys.stderr)
196-
sys.exit()
210+
arg = exn.args[0]
197211

198-
return pullers
212+
# if an argument is missing, look if it exist in another component group
213+
for group_name, group in main_config.items():
214+
if group_name == self.component_group_name:
215+
pass
199216

217+
elif not isinstance(group, dict):
218+
pass
200219

201-
def generate_pushers(config):
202-
# default mode if no output are specified
203-
if 'output' not in config:
204-
print('CLI error : no output specified', file=sys.stderr)
205-
sys.exit()
220+
elif db_name not in group:
221+
pass
206222

207-
pushers = {}
223+
elif arg in group[db_name]:
224+
db_config[arg] = group[db_name][arg]
225+
return self._generate_db(db_name, db_config, main_config)
226+
raise exn
208227

209-
for db_config in config['output']:
210-
try:
211-
factory = DB_FACTORY[db_config['type']]
212-
model = MODEL_FACTORY[db_config['model']]
213-
name = db_config['name']
214-
pusher = PusherActor(name, model, factory(db_config), level_logger=config['verbose'])
228+
def _gen_actor(self, db_name, db_config, main_config):
229+
db = self._generate_db(db_name, db_config, main_config)
230+
model = self.model_factory[db_config['model']]
231+
name = db_config['name']
232+
return self._actor_factory(name, db, model, main_config['stream'], main_config['verbose'])
215233

216-
pushers[name] = pusher
234+
def _actor_factory(self, name, db, model, stream_mode, level_logger):
235+
raise NotImplementedError()
217236

218-
except KeyError as exn:
219-
msg = 'CLI error : '
220237

221-
if 'type' not in db_config:
222-
msg += 'output type not specified'
238+
class PullerGenerator(DBActorGenerator):
223239

224-
else:
225-
msg += 'argument ' + exn.args[0]
226-
msg += ' needed with --output ' + db_config['type']
227-
print(msg, file=sys.stderr)
228-
sys.exit()
240+
def __init__(self, report_filter):
241+
DBActorGenerator.__init__(self, 'input')
242+
self.report_filter = report_filter
243+
244+
def _actor_factory(self, name, db, model, stream_mode, level_logger):
245+
return PullerActor(name, db, self.report_filter, model, stream_mode, level_logger)
246+
247+
248+
class PusherGenerator(DBActorGenerator):
249+
250+
def __init__(self):
251+
DBActorGenerator.__init__(self, 'output')
229252

230-
return pushers
253+
def _actor_factory(self, name, db, model, stream_mode, level_logger):
254+
return PusherActor(name, model, db, level_logger)

tests/unit/cli/test_tools.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
Copyright (c) 2018, INRIA
3+
Copyright (c) 2018, University of Lille
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
"""
31+
32+
33+
import pytest
34+
import sys
35+
36+
from mock import Mock
37+
38+
from powerapi.cli.tools import PullerGenerator, PusherGenerator
39+
from powerapi.puller import PullerActor
40+
from powerapi.database import MongoDB
41+
42+
####################
43+
# PULLER GENERATOR #
44+
####################
45+
def test_no_input_specified():
46+
"""
47+
generate puller from an empty config dict
48+
49+
Test if the generate_puller function call sys.exit
50+
"""
51+
args = {}
52+
sys.exit = Mock()
53+
sys.exit.side_effect = Exception()
54+
55+
generator = PullerGenerator(None)
56+
57+
with pytest.raises(Exception):
58+
generator.generate(args)
59+
60+
assert sys.exit.called
61+
62+
63+
def test_generate_puller_from_simple_config():
64+
"""
65+
generate mongodb puller from this config :
66+
{ 'verbose': True, 'stream': True, 'input': {'mongodb': {'model': 'hwpc_report', 'name': 'toto', 'uri': 'titi', 'db': 'tata',
67+
'collection': 'tutu'}}}
68+
69+
Test if :
70+
- function return a dict containing one actor, its key is 'toto'
71+
- puller type is PullerActor
72+
- puller name is toto
73+
- puller database type is MongoDB
74+
- database uri is titi
75+
- database db is tata
76+
- database collection is tutu
77+
78+
"""
79+
args = {'verbose': True, 'stream': True, 'input': {'mongodb': {'model': 'hwpc_report', 'name': 'toto', 'uri': 'titi',
80+
'db': 'tata', 'collection': 'tutu'}}}
81+
generator = PullerGenerator(None)
82+
result = generator.generate(args)
83+
84+
assert len(result) == 1
85+
assert 'toto' in result
86+
puller = result['toto']
87+
assert isinstance(puller, PullerActor)
88+
assert puller.name == 'toto'
89+
90+
db = puller.state.database
91+
92+
assert isinstance(db, MongoDB)
93+
assert db.uri == 'titi'
94+
assert db.db_name == 'tata'
95+
assert db.collection_name == 'tutu'
96+
97+
98+
def test_generate_puller_and_pusher_with_same_database():
99+
"""
100+
generate mongodb puller and pusher from this config :
101+
{'verbose': True, 'stream': True, 'input': {'mongodb': {'model': 'hwpc_report', 'name': 'toto', 'uri': 'titi', 'db': 'tata',
102+
'collection': 'tutu'}},
103+
'output': {'mongodb': {'model': 'hwpc_report', 'name': 'hoho', 'collection': 'tete'}}}
104+
as the database have the same uri and same database name, this value are not specified on the configuration of the output
105+
106+
Test if :
107+
- puller and pusher database uri is titi
108+
- puller and pusher database db is tata
109+
- puller database collection is tutu
110+
- pusher database collection is tete
111+
112+
"""
113+
args = {'verbose': True, 'stream': True, 'input': {'mongodb': {'model': 'hwpc_report', 'name': 'toto', 'uri': 'titi',
114+
'db': 'tata', 'collection': 'tutu'}},
115+
'output': {'mongodb': {'model': 'hwpc_report', 'name': 'hoho', 'collection': 'tete'}}}
116+
117+
puller_generator = PullerGenerator(None)
118+
result = puller_generator.generate(args)
119+
120+
puller = result['toto']
121+
puller_db = puller.state.database
122+
123+
pusher_generator = PusherGenerator()
124+
result = pusher_generator.generate(args)
125+
126+
pusher = result['hoho']
127+
128+
pusher_db = pusher.state.database
129+
130+
assert pusher_db.uri == puller_db.uri
131+
assert pusher_db.db_name == puller_db.db_name
132+
assert pusher_db.collection_name == 'tete'
133+
assert puller_db.collection_name == 'tutu'

0 commit comments

Comments
 (0)