Skip to content

Commit c543bde

Browse files
authored
Merge pull request #79 from altor/release_0.6.2
Release 0.6.2
2 parents 9953dc8 + b1736b8 commit c543bde

File tree

4 files changed

+223
-78
lines changed

4 files changed

+223
-78
lines changed

powerapi/cli/parser.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,10 @@ def subparse(self, token_list):
322322
parsed argument and the result of the parsing
323323
324324
"""
325-
# print(vars(self.actions))
326325
local_result = deepcopy(self.default_values)
327326
if token_list == []:
328327
return token_list, local_result
329328

330-
local_result['type'] = self.name
331-
332329
return self._parse(token_list, local_result)
333330

334331

@@ -453,11 +450,11 @@ def add_component_subparser(self, component_name, subparser, help_str=''):
453450
"""
454451
def _action(arg, val, args, acc):
455452
if arg not in acc:
456-
acc[arg] = []
453+
acc[arg] = {}
457454

458455
subparser = self.subparsers_group[arg].get_subparser(val)
459456
args, subparse_result = subparser.subparse(args)
460-
acc[arg].append(subparse_result)
457+
acc[arg][subparser.name] = subparse_result
461458
return args, acc
462459

463460
if component_name not in self.subparsers_group:

powerapi/cli/tools.py

Lines changed: 81 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -159,80 +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-
}
169-
170-
171-
MODEL_FACTORY = {
172-
'hwpc_report': HWPCModel(),
173-
'power_report': PowerModel(),
174-
}
175-
176-
177-
def generate_pullers(config, report_filter):
178-
# default mode if no input are specified
179-
if 'input' not in config:
180-
factory = DB_FACTORY['csv']
181-
model = HWPCModel()
182-
name = 'csv_puller'
183-
db_config = {'files': ['core.csv', 'rapl.csv', 'pcu.csv']}
184-
puller = PullerActor(name, factory(db_config), report_filter, model, stream_mode=config['stream'],
185-
level_logger=config['verbose'])
186-
return {name: puller}
187-
188-
pullers = {}
189-
for db_config in config['input']:
190-
try:
191-
factory = DB_FACTORY[db_config['type']]
192-
model = MODEL_FACTORY[db_config['model']]
193-
name = db_config['name']
194-
puller = PullerActor(name, factory(db_config), report_filter, model, stream_mode=config['stream'],
195-
level_logger=config['verbose'])
196-
pullers[name] = puller
197-
except KeyError as exn:
198-
msg = 'CLI error : argument ' + exn.args[0]
199-
msg += ' needed with --output ' + db_config['type']
200-
print(msg, file=sys.stderr)
162+
class Generator:
163+
164+
def __init__(self, component_group_name):
165+
self.component_group_name = component_group_name
166+
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)
201170
sys.exit()
202171

203-
return pullers
172+
actors = {}
204173

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()
205182

206-
def generate_pushers(config):
207-
# default mode if no output are specified
208-
if 'output' not in config:
209-
factory = DB_FACTORY['csv']
210-
model = PowerModel()
211-
name = 'csv_pusher'
212-
pusher = PusherActor(name, model, factory({}), level_logger=config['verbose'])
213-
return {name: pusher}
183+
return actors
214184

215-
pushers = {}
185+
def _gen_actor(self, component_name, component_config, main_config):
186+
raise NotImplementedError()
216187

217-
for db_config in config['output']:
218-
try:
219-
factory = DB_FACTORY[db_config['type']]
220-
model = MODEL_FACTORY[db_config['model']]
221-
name = db_config['name']
222-
pusher = PusherActor(name, model, factory(db_config), level_logger=config['verbose'])
223188

224-
pushers[name] = pusher
189+
class DBActorGenerator(Generator):
225190

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):
207+
try:
208+
return self.db_factory[db_name](db_config)
226209
except KeyError as exn:
227-
msg = 'CLI error : '
210+
arg = exn.args[0]
228211

229-
if 'type' not in db_config:
230-
msg += 'output type not specified'
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
231216

232-
else:
233-
msg += 'argument ' + exn.args[0]
234-
msg += ' needed with --output ' + db_config['type']
235-
print(msg, file=sys.stderr)
236-
sys.exit()
217+
elif not isinstance(group, dict):
218+
pass
219+
220+
elif db_name not in group:
221+
pass
222+
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
227+
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'])
233+
234+
def _actor_factory(self, name, db, model, stream_mode, level_logger):
235+
raise NotImplementedError()
236+
237+
238+
class PullerGenerator(DBActorGenerator):
239+
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')
237252

238-
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_parser.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def test_subparser():
200200
- "" : {}
201201
- "-z" : UnknowArgException(z)
202202
- "-a" : {a: True}
203-
- "-a --sub toto -b" : {a:True, sub: [{type : toto, b: True}]}
203+
- "-a --sub toto -b" : {a:True, sub: {'toto' : {b: True}}}
204204
- "-b" : BadContextException(b, [toto])
205205
206206
Parser description :
@@ -223,7 +223,7 @@ def test_subparser():
223223
check_parsing_result(parser, '-a', {'a': True})
224224

225225
check_parsing_result(parser, '-a --sub toto -b',
226-
{'a': True, 'sub': [{'type': 'toto', 'b': True}]})
226+
{'a': True, 'sub': {'toto': { 'b': True}}})
227227

228228
with pytest.raises(BadContextException):
229229
check_parsing_result(parser, '-b', None)
@@ -364,7 +364,7 @@ def test_add_component_subparser_with_two_name():
364364
subparser = ComponentSubParser('titi')
365365
subparser.add_argument('a', 'aaa', flag=True, action=store_true, default=False)
366366
parser.add_component_subparser('sub', subparser)
367-
check_parsing_result(parser, '--sub titi -a', {'sub': [{'type': 'titi', 'aaa': True}]})
367+
check_parsing_result(parser, '--sub titi -a', {'sub': {'titi': {'aaa': True}}})
368368

369369

370370
def test_parse_empty_string_default_value():
@@ -404,25 +404,24 @@ def test_component_subparser_normal_token_list(component_subparser):
404404
test component_subparser, parse a token list which contain only subparser
405405
argument [('a', '')].
406406
407-
must return return a dictionary {'type': 'test', 'a':''} as parse result
407+
must return return a dictionary {'a':''} as parse result
408408
and a empty token list
409409
410410
"""
411-
assert component_subparser.subparse([('a', '')]) == ([], {'type': 'test',
412-
'a': None})
411+
assert component_subparser.subparse([('a', '')]) == ([], {'a': None})
413412

414413

415414
def test_component_subparser_full_token_list(component_subparser):
416415
"""
417416
test component_subparser, parse a token list which contain subparser
418417
argument and arguments from other parser[('a', ''), ('b', '')].
419418
420-
must return return a dictionary {'type': 'test', 'a':''} as parse result
419+
must return return a dictionary {'a':''} as parse result
421420
and a token list that contains the unparsed arguments : [('b', '')].
422421
423422
"""
424423
assert component_subparser.subparse([('a', ''), ('b', '')]) == ([('b', '')],
425-
{'type': 'test', 'a': None})
424+
{'a': None})
426425

427426

428427
def test_subparser_empty_token_list_default_value():

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)