Skip to content

Commit 584b635

Browse files
committed
feat: add configuration with config file
1 parent 992b3cf commit 584b635

File tree

9 files changed

+152
-70
lines changed

9 files changed

+152
-70
lines changed

powerapi/cli/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,4 @@
2626
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2727
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2828
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29-
30-
29+
from .config_validator import ConfigValidator

powerapi/cli/config_validator.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) 2018, INRIA
2+
# Copyright (c) 2018, University of Lille
3+
# All rights reserved.
4+
5+
# Redistribution and use in source and binary forms, with or without
6+
# modification, are permitted provided that the following conditions are met:
7+
8+
# * Redistributions of source code must retain the above copyright notice, this
9+
# list of conditions and the following disclaimer.
10+
11+
# * Redistributions in binary form must reproduce the above copyright notice,
12+
# this list of conditions and the following disclaimer in the documentation
13+
# and/or other materials provided with the distribution.
14+
15+
# * Neither the name of the copyright holder nor the names of its
16+
# contributors may be used to endorse or promote products derived from
17+
# this software without specific prior written permission.
18+
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
from typing import Dict
30+
import logging
31+
32+
class ConfigValidator:
33+
@staticmethod
34+
def validate(config: Dict):
35+
36+
if 'verbose' not in config:
37+
config['verbose'] = logging.NOTSET
38+
if 'stream' not in config:
39+
config['stream'] = False
40+
if 'output' not in config:
41+
logging.error("no output configuration found")
42+
return False
43+
44+
for output_type in config['output']:
45+
output_config = config['output'][output_type]
46+
if 'model' not in output_config:
47+
output_config['model'] = 'HWPCReport'
48+
if 'name' not in output_config:
49+
output_config['name'] = 'default_pusher'
50+
51+
if 'input' not in config:
52+
logging.error("no input configuration found")
53+
return False
54+
55+
for input_type in config['input']:
56+
input_config = config['input'][input_type]
57+
if 'model' not in input_config:
58+
input_config['model'] = 'PowerReport'
59+
if 'name' not in input_config:
60+
input_config['name'] = 'default_puller'
61+
return True
62+

powerapi/cli/parser.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ def _action(arg, val, args, acc):
474474
args, subparse_result = subparser.subparse(args)
475475

476476
acc[arg][subparser.name] = subparse_result
477+
# acc[arg] = subparse_result
477478
return args, acc
478479

479480
if component_type not in self.subparsers_group:
@@ -512,14 +513,14 @@ def _action(arg, val, args, acc):
512513
raise NoNameSpecifiedForComponentException(component_type)
513514

514515
component_name = subparse_result['name']
516+
del subparse_result['name']
515517

516-
if subparser.name not in acc[arg]:
517-
acc[arg][subparser.name] = {}
518-
519-
if component_name in acc[arg][subparser.name]:
518+
if component_name in acc[arg]:
520519
raise ComponentAlreadyExistException(component_name)
521520

522-
acc[arg][subparser.name][component_name] = subparse_result
521+
acc[arg][component_name] = subparse_result
522+
acc[arg][component_name]['type'] = subparser.name
523+
523524
return args, acc
524525

525526
if 'name' not in subparser.actions:

powerapi/cli/tools.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def __init__(self):
125125
help_str='specify a database output : --db_output database_name ARG1 ARG2 ...')
126126

127127
subparser_prom_output = ComponentSubParser('prom')
128-
subparser_prom_output.add_argument('a', 'addr', help='specify server address')
128+
subparser_prom_output.add_argument('u', 'uri', help='specify server uri')
129129
subparser_prom_output.add_argument('p', 'port', help='specify server port', type=int)
130130
subparser_prom_output.add_argument('M', 'metric_name', help='speify metric name')
131131
subparser_prom_output.add_argument('d', 'metric_description', help='specify metric description', default='energy consumption')
@@ -138,7 +138,7 @@ def __init__(self):
138138
help_str='specify a database output : --db_output database_name ARG1 ARG2 ...')
139139

140140
subparser_direct_prom_output = ComponentSubParser('direct_prom')
141-
subparser_direct_prom_output.add_argument('a', 'addr', help='specify server address')
141+
subparser_direct_prom_output.add_argument('a', 'uri', help='specify server uri')
142142
subparser_direct_prom_output.add_argument('p', 'port', help='specify server port', type=int)
143143
subparser_direct_prom_output.add_argument('M', 'metric_name', help='speify metric name')
144144
subparser_direct_prom_output.add_argument('d', 'metric_description', help='specify metric description', default='energy consumption')
@@ -221,19 +221,19 @@ def generate(self, config):
221221

222222
actors = {}
223223

224-
for component_type, components_list in config[self.component_group_name].items():
225-
for component_name, component_config in components_list.items():
226-
try:
227-
actors[component_name] = self._gen_actor(component_type, component_config, config)
228-
except KeyError as exn:
229-
msg = 'CLI error : argument ' + exn.args[0]
230-
msg += ' needed with --output ' + component_type
231-
print(msg, file=sys.stderr)
232-
sys.exit()
224+
for component_name, component_config in config[self.component_group_name].items():
225+
component_type = component_config['type']
226+
try:
227+
actors[component_name] = self._gen_actor(component_type, component_config, config, component_name)
228+
except KeyError as exn:
229+
msg = 'CLI error : argument ' + exn.args[0]
230+
msg += ' needed with --output ' + component_type
231+
print(msg, file=sys.stderr)
232+
sys.exit()
233233

234234
return actors
235235

236-
def _gen_actor(self, component_name, component_config, main_config):
236+
def _gen_actor(self, component_type, component_config, main_config, component_name):
237237
raise NotImplementedError()
238238

239239

@@ -289,9 +289,9 @@ def __init__(self, component_group_name):
289289
files=[] if 'files' not in db_config else db_config['files']),
290290
'influxdb': lambda db_config: InfluxDB(db_config['uri'], db_config['port'], db_config['db']),
291291
'opentsdb': lambda db_config: OpenTSDB(db_config['uri'], db_config['port'], db_config['metric_name']),
292-
'prom': lambda db_config: PrometheusDB(db_config['port'], db_config['addr'], db_config['metric_name'],
292+
'prom': lambda db_config: PrometheusDB(db_config['port'], db_config['uri'], db_config['metric_name'],
293293
db_config['metric_description'], self.model_factory[db_config['model']], db_config['aggregation_period']),
294-
'direct_prom': lambda db_config: DirectPrometheusDB(db_config['port'], db_config['addr'], db_config['metric_name'],
294+
'direct_prom': lambda db_config: DirectPrometheusDB(db_config['port'], db_config['uri'], db_config['metric_name'],
295295
db_config['metric_description'], self.model_factory[db_config['model']]),
296296
'virtiofs': lambda db_config: VirtioFSDB(db_config['vm_name_regexp'], db_config['root_directory_name'], db_config['vm_directory_name_prefix'], db_config['vm_directory_name_suffix']),
297297
}
@@ -320,11 +320,10 @@ def _generate_db(self, db_name, db_config, main_config):
320320
return self.db_factory[db_name](db_config)
321321

322322

323-
def _gen_actor(self, db_name, db_config, main_config):
323+
def _gen_actor(self, db_name, db_config, main_config, actor_name):
324324
db = self._generate_db(db_name, db_config, main_config)
325325
model = self.model_factory[db_config['model']]
326-
name = db_config['name']
327-
return self._actor_factory(name, db, model, main_config['stream'], main_config['verbose'])
326+
return self._actor_factory(actor_name, db, model, main_config['stream'], main_config['verbose'])
328327

329328
def _actor_factory(self, name, db, model, stream_mode, level_logger):
330329
raise NotImplementedError()

tests/acceptation/test_simple_architecture_csv.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,12 @@ def test_run(files, supervisor):
132132

133133
config = {'verbose': LOG_LEVEL,
134134
'stream': False,
135-
'input': {'csv': {'puller' : {'files': FILES,
136-
'model': 'HWPCReport',
137-
'name': 'puller',
138-
}}},
139-
'output': {'csv': {'pusher': {'model': 'PowerReport', 'name': 'pusher', 'directory': ROOT_PATH}}}}
135+
'input': {'puller' : {'type': 'csv',
136+
'files': FILES,
137+
'model': 'HWPCReport',
138+
'name': 'puller',
139+
}},
140+
'output': {'pusher': {'type': 'csv', 'model': 'PowerReport', 'name': 'pusher', 'directory': ROOT_PATH}}}
140141

141142
# Pusher
142143
pusher_generator = PusherGenerator()

tests/acceptation/test_simple_architecture_with_libvirt_mapper.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,20 @@ def test_run(mocked_libvirt, mongo_database, supervisor):
106106
LOG_LEVEL = logging.DEBUG
107107
config = {'verbose': LOG_LEVEL,
108108
'stream': False,
109-
'input': {'mongodb': {'puller': {'uri': MONGO_URI,
110-
'db': MONGO_DATABASE_NAME,
111-
'collection': MONGO_INPUT_COLLECTION_NAME,
112-
'model': 'HWPCReport',
113-
'name': 'puller',
114-
}}},
115-
'output': {'mongodb': {'pusher': {'uri': MONGO_URI,
116-
'db': MONGO_DATABASE_NAME,
117-
'collection': MONGO_OUTPUT_COLLECTION_NAME,
118-
'model': 'PowerReport',
119-
'name': 'pusher'}}},
120-
'report_modifier': {'libvirt_mapper': {'uri': '',
121-
'domain_regexp': REGEXP}}}
109+
'input': {'puller': {'type': 'mongodb',
110+
'uri': MONGO_URI,
111+
'db': MONGO_DATABASE_NAME,
112+
'collection': MONGO_INPUT_COLLECTION_NAME,
113+
'model': 'HWPCReport',
114+
}},
115+
'output': {'pusher': {'type': 'mongodb',
116+
'uri': MONGO_URI,
117+
'db': MONGO_DATABASE_NAME,
118+
'collection': MONGO_OUTPUT_COLLECTION_NAME,
119+
'model': 'PowerReport',
120+
'name': 'pusher'}},
121+
'report_modifier': {'libvirt_mapper': {'uri': '',
122+
'domain_regexp': REGEXP}}}
122123

123124
# Pusher
124125
pusher_generator = PusherGenerator()

tests/integration/cli/test_generate_pusher.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ def test_generate_pusher_with_new_PowerReport_model_and_send_it_powerReport_must
7676
"""
7777

7878
config = {'verbose': True, 'stream': False,
79-
'output': {'influxdb': {'test_pusher': {'model': 'PowerReport', 'name': 'test_pusher', 'uri': INFLUX_URI, 'port': INFLUX_PORT,
80-
'db': INFLUX_DBNAME}}}}
79+
'output': {'test_pusher': {'type': 'influxdb', 'model': 'PowerReport', 'name': 'test_pusher', 'uri': INFLUX_URI, 'port': INFLUX_PORT,
80+
'db': INFLUX_DBNAME}}}
8181

8282
class PowerModelWithFormulaName(PowerModel):
8383

tests/unit/cli/test_parser.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,15 @@ def test_main_parser():
194194
check_parsing_result(parser, '-b', None)
195195

196196

197-
def test_subparser():
197+
def test_actor_subparser():
198198
"""
199199
test to parse strings with a parser and retrieve the following results :
200200
201201
- "" : {}
202202
- "-z" : UnknowArgException(z)
203203
- "-a" : {a: True}
204204
- "-a --sub toto -b" : NoNameSpecifiedForComponentException
205-
- "-a --sub toto -b --name titi" : {a:True, sub: { titi: {'toto' : {b: True}}}}
205+
- "-a --sub toto -b --name titi" : {a:True, sub: { titi: { 'type': 'toto', b: True}}}
206206
- "-b" : BadContextException(b, [toto])
207207
208208
Parser description :
@@ -228,13 +228,13 @@ def test_subparser():
228228
with pytest.raises(NoNameSpecifiedForComponentException):
229229
check_parsing_result(parser, '-a --sub toto -b', {})
230230

231-
check_parsing_result(parser, '-a --sub toto -b --name titi', {'a': True, 'sub': {'toto': {'titi': {'name': 'titi', 'b': True}}}})
231+
check_parsing_result(parser, '-a --sub toto -b --name titi', {'a': True, 'sub': {'titi': {'type': 'toto', 'b': True}}})
232232

233233
with pytest.raises(BadContextException):
234234
check_parsing_result(parser, '-b', None)
235235

236236

237-
def test_formula_subparser():
237+
def test_component_subparser():
238238
"""
239239
test to parse strings with a formula parser and retrieve the following results :
240240
- "" : {}
@@ -265,7 +265,7 @@ def test_create_two_component():
265265
--sub toto --name titi --sub toto -b --name tutu
266266
267267
test if the result is :
268-
{sub:{'toto' : {'titi': {'name': 'titi'}, 'tutu': {'name': 'tutu', 'b':False}}}}
268+
{sub:{'titi' : {'type': 'toto'}, 'tutu': {'type': 'toto', 'b':True}}}
269269
270270
"""
271271
parser = MainParser(help_arg=False)
@@ -275,7 +275,28 @@ def test_create_two_component():
275275
subparser.add_argument('n', 'name')
276276
parser.add_actor_subparser('sub', subparser)
277277

278-
check_parsing_result(parser, '--sub toto --name titi --sub toto -b --name tutu', {'sub': {'toto': {'titi': {'name': 'titi'}, 'tutu': {'name': 'tutu', 'b': True}}}})
278+
check_parsing_result(parser, '--sub toto --name titi --sub toto -b --name tutu', {'sub': {'titi': {'type': 'toto'}, 'tutu': {'type': 'toto', 'b': True}}})
279+
280+
def test_create_two_with_different_type_component():
281+
"""
282+
Create two component with different type with the following cli :
283+
--sub toto --name titi --sub tutu --name tete
284+
285+
test if the result is :
286+
{sub:{'titi' : {'type': 'toto'}, 'tete': {'type': 'tutu'}}}
287+
288+
"""
289+
parser = MainParser(help_arg=False)
290+
291+
subparser = ComponentSubParser('toto')
292+
subparser.add_argument('n', 'name')
293+
parser.add_actor_subparser('sub', subparser)
294+
295+
subparser = ComponentSubParser('tutu')
296+
subparser.add_argument('n', 'name')
297+
parser.add_actor_subparser('sub', subparser)
298+
299+
check_parsing_result(parser, '--sub toto --name titi --sub tutu --name tete', {'sub': {'titi': {'type': 'toto'}, 'tete': {'type': 'tutu'}}})
279300

280301

281302
def test_create_component_that_already_exist():
@@ -284,8 +305,6 @@ def test_create_component_that_already_exist():
284305
--sub toto --name titi --sub toto --name titi
285306
286307
test if an ComponentAlreadyExistException is raised
287-
288-
289308
"""
290309
parser = MainParser(help_arg=False)
291310

@@ -420,7 +439,7 @@ def test_add_component_subparser_that_aldready_exists():
420439
parser.add_component_subparser('toto', subparser)
421440
subparser2 = ComponentSubParser('titi')
422441
subparser2.add_argument('n', 'name')
423-
442+
424443
with pytest.raises(AlreadyAddedArgumentException):
425444
parser.add_component_subparser('toto', subparser2)
426445

@@ -435,10 +454,10 @@ def test_add_component_subparser_with_two_name():
435454
subparser.add_argument('a', 'aaa', flag=True, action=store_true, default=False)
436455
subparser.add_argument('n', 'name')
437456
parser.add_actor_subparser('sub', subparser)
438-
check_parsing_result(parser, '--sub titi -a --name tutu', {'sub': {'titi': {'tutu': {'aaa': True, 'name': 'tutu'}}}})
457+
check_parsing_result(parser, '--sub titi -a --name tutu', {'sub': {'tutu': {'aaa': True, 'type': 'titi'}}})
439458

440459

441-
def test_add_component_subparser_that_aldready_exists():
460+
def test_add_component_subparser_that_aldready_exists2():
442461
"""
443462
Add a component_subparser with no argument 'name'
444463
test if a SubParserWithoutNameArgumentException is raised

0 commit comments

Comments
 (0)