[Kimchi-devel] [PATCH] [WoK 9/9] Removing configobj, use file operations to edit conf files

Daniel Henrique Barboza danielhb at linux.vnet.ibm.com
Fri Jan 20 14:10:35 UTC 2017



On 01/20/2017 11:24 AM, Lucio Correia wrote:
> On 20/01/2017 10:15, dhbarboza82 at gmail.com wrote:
>> From: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
>>
>> Before the implementation of the /config/plugins API, WoK used
>> cherrypy.lib.reprconf.Parser to parse the .conf files of the
>> plug-ins. In the implementation of this API we have the need to
>> edit these config files, setting the 'enable = ' line from the WoK
>> section to either True or False. Using this same parser for writing
>> the files presented to be a problem because the writing process were
>> deleting the comments presented in the conf files. No option is
>> available to make the write() call of cherrypy.lib.reprconf.Parser
>> preserve the comments.
>>
>> To solve this, the implementation of /config/plugins introduced
>> configobj.ConfigObj, a python standard parser, to make this write
>> operation in the conf files. The write() call of this parser retains
>> the comments. So we ended up using cherrypy.lib.reprconf.Parser to parse
>> the conf files to the plug-ins and configobj.ConfigObj to edit/write the
>> 'enable =' option. It is worth mentioning that although
>> cherrypy.lib.reprconf.Parser implementation extends configobj.ConfigObj,
>> his write() call extends another class called
>> 'backports.configparser.RawConfigParser'. This explains why the write()
>> call from both parsers differs.
>>
>> A problem in this approach was detected when dealing with the conf
>> file of the Ginger plug-in. This is the relevant portion of the Ginger
>> conf file:
>>
>> [backup]
>> default_include = ['/etc', '/var/spool/cron']
>> default_exclude = ['/etc/init.d', '/etc/rc.d', '/etc/rc?.d']
>>
>> This declaration of default_include and default_exclude is invalid to
>> the configobj.ConfigObj parser:
>>
>>>>> import configobj
>>>>> parser = configobj.ConfigObj('ginger.conf')
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>>   File "/usr/lib/python2.7/site-packages/configobj.py", line 1229, in 
>> __init__
>>     self._load(infile, configspec)
>>   File "/usr/lib/python2.7/site-packages/configobj.py", line 1318, in 
>> _load
>>     raise error
>> configobj.ConfigObjError: Parsing failed with several errors.
>> First error at line 6.
>>>>>
>>
>> This resulted in the /enable and /disable plug-ins being broken when
>> dealing with Ginger. After experimentation with the ginger.conf format
>> no common ground was found between both parsers to proper handle this
>> list declaration of the Ginger file.
>>
>> One solution that was speculated was to get rid of
>> cherrypy.lib.reprconf.Parser altogether and use configobj.ConfigObj 
>> in all
>> cases, meaning that we would use it to parse the conf files of the 
>> plug-ins
>> too. At first only the Ginger conf file was going to be changed but then
>> we realized that ConfigObj parses boolean in a different fashion too.
>>
>> This is the parsing of the current kimchi.conf file using both parsers:
>>
>>>>> import configobj
>>>>> parser = configobj.ConfigObj('kimchi.conf')
>>>>> parser.dict()
>> {'kimchi': {'create_iso_pool': 'True', 'federation': 'False'}, 'wok': 
>> {'enable': 'True'}}
>>>>>
>>>>> from cherrypy.lib.reprconf import Parser
>>>>> Parser().dict_from_file('kimchi.conf')
>> {'kimchi': {'create_iso_pool': True, 'federation': False}, 'wok': 
>> {'enable': True}}
>>>>>
>>
>> cherrypy.lib.reprconf.Parser delivers boolean values in the dict,
>> ConfigObj delivers string values 'True' and 'False'. This means that the
>> change to use ConfigObj in the parsing of the conf files would need 
>> either:
>>
>> • change Kimchi and Gingerbase plug-ins to handle the now string values
>> 'True' or 'False' in its backend logic
>>
>> or
>>
>> • WoK would need to convert the string values to boolean before 
>> sending it
>> to the plug-ins. This can be annoying considering that the conf file
>> supports multiple sections and subsections.
>>
>> The saner solution this patch implements is to get rid of 
>> configobj.ConfigObj
>> to do the write of the conf files. This is now being manually made with
>> standard Python file operations. The file editing required in this 
>> feature
>> is too simple to warrant for all this hassle with two differents 
>> parsers,
>> different INI formats and so on.
>>
>> A unit test was edited to make the .conf file a little trickier to 
>> ensure
>> that our file operation is editing and writing the right attribute of 
>> the
>> conf file.
>>
>> Signed-off-by: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
>> ---
>>  src/wok/utils.py    | 22 ++++++++++++++++++----
>>  tests/test_utils.py |  5 +++++
>>  2 files changed, 23 insertions(+), 4 deletions(-)
>>
>> diff --git a/src/wok/utils.py b/src/wok/utils.py
>> index d841371..c2d4724 100644
>> --- a/src/wok/utils.py
>> +++ b/src/wok/utils.py
>> @@ -35,7 +35,6 @@ import traceback
>>  import xml.etree.ElementTree as ET
>>
>>  from cherrypy.lib.reprconf import Parser
>> -from configobj import ConfigObj
>>  from datetime import datetime, timedelta
>>  from multiprocessing import Process, Queue
>>  from threading import Timer
>> @@ -117,9 +116,24 @@ def set_plugin_state(name, state):
>>      if not plugin_conf:
>>          return
>>
>> -    config = ConfigObj(plugin_conf)
>> -    config['wok']['enable'] = str(state)
>> -    config.write()
>> +    config_contents = None
>> +
>> +    with open(plugin_conf, 'r') as f:
>> +        config_contents = f.readlines()
>> +
>> +    wok_section_found = False
>> +
>> +    for i in range(0, len(config_contents)):
>> +        if config_contents[i] == '[wok]\n':
>> +            wok_section_found = True
>> +            continue
>> +
>> +        if config_contents[i].startswith('enable =') and 
>> wok_section_found:
>> +            config_contents[i] = 'enable = %s\n' % str(state)
>> +            break
>> +
>> +    with open(plugin_conf, 'w') as f:
>> +        f.writelines(config_contents)
>>
>>
>>  def get_all_tabs():
>> diff --git a/tests/test_utils.py b/tests/test_utils.py
>> index c0e1009..6f1a783 100644
>> --- a/tests/test_utils.py
>> +++ b/tests/test_utils.py
>> @@ -76,12 +76,17 @@ class UtilsTests(unittest.TestCase):
>>
>>      def _get_fake_config_file_content(self, enable=True):
>>          return """\
>> +[a_random_section]
>> +# a random section for testing purposes
>> +enable = 1
>> +
>>  [wok]
>>  # Enable plugin on Wok server (values: True|False)
>>  enable = %s
>>
>>  [fakeplugin]
>>  # Yet another comment on this config file
>> +enable = 2
>>  very_interesting_option = True
>>  """ % str(enable)
>>
>>
> I believe we may have complaints about problems when user uses:
> enabled=1  (without spaces).
>
> This is supported by INI files and we should handle it as well.
>
>

I'll add support for this format in the v2



More information about the Kimchi-devel mailing list