[Kimchi-devel] [PATCH 1/3 v3] Adding yumparser module

Aline Manera alinefm at linux.vnet.ibm.com
Tue Jun 9 14:00:54 UTC 2015



On 05/06/2015 17:17, Daniel Henrique Barboza wrote:
> This module contains:
>
> - functions to write and delete YUM repositories using the .repo files
> from the filesystem directly, without the need of an external API.
>
> - a function to return the software update package list by parsing
> the output of $yum check-update
>
> Signed-off-by: Daniel Henrique Barboza <dhbarboza82 at gmail.com>
> ---
>   src/kimchi/yumparser.py | 271 ++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 271 insertions(+)
>   create mode 100644 src/kimchi/yumparser.py
>
> diff --git a/src/kimchi/yumparser.py b/src/kimchi/yumparser.py
> new file mode 100644
> index 0000000..af0396b
> --- /dev/null
> +++ b/src/kimchi/yumparser.py
> @@ -0,0 +1,271 @@
> +#
> +# Project Kimchi
> +#
> +# Copyright IBM, Corp. 2015
> +#
> +# This library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +#
> +# This library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +# Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with this library; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
> +import subprocess
> +
> +from os import listdir
> +from os.path import isfile, splitext
> +
> +


> +class YumRepoObject(object):
> +
> +    def _set_base_URL(self, strvalue):
> +        self.baseurl = strvalue
> +
> +    def _set_enabled(self, strvalue):
> +        self.enabled = (strvalue == '1')
> +
> +    def _set_gpgcheck(self, strvalue):
> +        self.gpgcheck = (strvalue == '1')
> +
> +    def _set_gpgkey(self, strvalue):
> +        self.gpgkey = strvalue
> +
> +    def _set_metalink(self, strvalue):
> +        self.metalink = strvalue
> +
> +    def _set_mirrorlist(self, strvalue):
> +        self.mirrorlist = strvalue
> +
> +    def _set_name(self, strvalue):
> +        self.name = strvalue
> +

Those functions are very similar.
I suggest create only 2:

def _set_bool(self, key, strvalue):
     setattr(getattr(self, key), strvalue ==  '1' )

def _set_key(self, key, strvalue):
     setattr(getattr(self, key), strvalue)

And use them when needed.

You can also avoid the functions by doing:

def set_atttribute(self, key, value):
     if key in ['gpgcheck', 'enabled']:
         setattr(getattr(self, key), strvalue ==  '1' )
     else:
         setattr(getattr(self, key), strvalue)

> +    def __init__(self, repo_id, repofile):
> +        self.repo_id = repo_id
> +        self.name = None
> +        self.baseurl = None
> +        self.enabled = True
> +        self.gpgcheck = True
> +        self.gpgkey = None
> +        self.metalink = None
> +        self.mirrorlist = None
> +        self.repofile = repofile
> +        self.attr_setters = {'baseurl': self._set_base_URL,
> +                             'enabled': self._set_enabled,
> +                             'gpgcheck': self._set_gpgcheck,
> +                             'gpgkey': self._set_gpgkey,
> +                             'name': self._set_name,
> +                             'metalink': self._set_metalink,
> +                             'mirrorlist': self._set_mirrorlist}
> +
> +    def set_attribute(self, key, value):
> +        attr_setter = self.attr_setters.get(key)
> +        if attr_setter:
> +            return attr_setter(value)
> +
> +    def get_attribute_str(self, key):
> +        if key not in self.attr_setters.keys():
> +            return None

> +        if key == 'enabled' or key == 'gpgcheck':
> +            str_value = '1' if getattr(self, key) is True else '0'

As 'enabled' and 'gpgcheck' will have a different behavior we can create 
a constant to hold the keys:

BOOL_KEYS = ['enabled', 'gpgcheck']

if key in BOOL_KEYS:
     do

> +        else:
> +            str_value = getattr(self, key)
> +
> +        if str_value is None:
> +            return None
> +
> +        return key + '=' + str_value
> +
> +    def get_attributes(self):
> +        return self.attr_setters.keys()
> +
> +    def enable(self):
> +        self.enabled = True
> +
> +    def disable(self):
> +        self.enabled = False
> +
> +    def __str__(self):
> +        str_obj = '[' + self.repo_id + ']' + '\n'
> +        for key in self.get_attributes():
> +            if self.get_attribute_str(key) is not None:
> +                str_obj += self.get_attribute_str(key) + '\n'
> +        return str_obj
> +
> +
> +def get_repo_files():
> +    def _is_repository_file(f):
> +        _, f_extension = splitext(f)
> +        return isfile(f) and (f_extension == '.repo')
> +
> +    YUM_REPO_DIR = '/etc/yum.repos.d'
> +    return [YUM_REPO_DIR+'/'+f for f in listdir(YUM_REPO_DIR)
> +            if _is_repository_file(YUM_REPO_DIR+'/'+f)]
> +
> +
> +def _ignore_line_repo_file(line):
> +    return line.startswith("#") or '=' not in line
> +
> +
> +def _get_repos_from_file(repo_file):
> +    repos_from_file = {}
> +    current_repo = None
> +    current_repo_id = None
> +    with open(repo_file) as f:
> +        for line in f.readlines():
> +            line = line.strip()
> +            if line.startswith("["):
> +                if current_repo is not None:
> +                    repos_from_file[current_repo_id] = current_repo
> +                current_repo_id = line.strip('[]')
> +                current_repo = YumRepoObject(current_repo_id, repo_file)
> +                continue
> +            if _ignore_line_repo_file(line):
> +                continue
> +            key, value = line.split('=', 1)
> +            key = key.strip()
> +            value = value.strip()
> +            current_repo.set_attribute(key, value)
> +
> +        # add the last repo from file
> +        repos_from_file[current_repo_id] = current_repo
> +
> +    return repos_from_file
> +
> +
> +def get_yum_repositories():
> +    repo_files = get_repo_files()
> +    repos = {}
> +    for yum_repo in repo_files:
> +        repos.update(_get_repos_from_file(yum_repo))
> +
> +    return repos
> +
> +
> +def _retrieve_repo_line_index(data, repo):
> +    repo_entry = '[' + repo.repo_id + ']\n'
> +    try:
> +        repo_index = data.index(repo_entry)
> +    except:
> +        return None
> +    return repo_index
> +
> +
> +def _update_repo_file_data(data, repo, repo_index):
> +    remaining_repo_attrs = repo.get_attributes()
> +
> +    for i in range(repo_index + 1, len(data)):
> +        line = data[i].strip()
> +        if line.startswith('['):
> +            break
> +        if _ignore_line_repo_file(line):
> +            continue
> +        key, _ = line.split('=', 1)
> +        key = key.strip()
> +        attr_str = repo.get_attribute_str(key)
> +        if attr_str is None:
> +            continue
> +        remaining_repo_attrs.remove(key)
> +        data[i] = attr_str + '\n'
> +
> +    for attr in remaining_repo_attrs:
> +        attr_str = repo.get_attribute_str(attr)
> +        if attr_str is None:
> +            continue
> +        data.insert(repo_index+1, attr_str + '\n')
> +
> +    return data
> +
> +
> +def write_repo_to_file(repo):
> +    with open(repo.repofile) as f:
> +        data = f.readlines()
> +
> +    repo_index = _retrieve_repo_line_index(data, repo)
> +    if repo_index is None:
> +        return
> +
> +    data = _update_repo_file_data(data, repo, repo_index)
> +
> +    with open(repo.repofile, 'w') as f:
> +        f.writelines(data)
> +
> +
> +def _get_last_line_repo(data, repo_index):
> +    stop_delete_index = None
> +    for i in range(repo_index+1, len(data)):
> +        line = data[i].strip()
> +        if line.startswith('['):
> +            stop_delete_index = i - 1
> +            break
> +    if stop_delete_index is None:
> +        stop_delete_index = len(data) - 1
> +
> +    return stop_delete_index
> +
> +
> +def _remove_repo_file_data(data, repo_index):
> +    last_line_repo = _get_last_line_repo(data, repo_index)
> +    for i in range(last_line_repo, repo_index - 1, -1):
> +        data.pop(i)
> +    return data
> +
> +
> +def delete_repo_from_file(repo):
> +    with open(repo.repofile) as f:
> +        data = f.readlines()
> +
> +    repo_index = _retrieve_repo_line_index(data, repo)
> +    if repo_index is None:
> +        return
> +
> +    data = _remove_repo_file_data(data, repo_index)
> +
> +    with open(repo.repofile, 'w') as f:
> +        f.writelines(data)
> +
> +
> +class YumUpdatePackageObject(object):
> +
> +    def __init__(self, name, arch, version, repo):
> +        self.name = name
> +        self.arch = arch
> +        self.version = version
> +        self.ui_from_repo = repo
> +
> +
> +def _get_yum_checkupdate_output():
> +    cmd = ['yum', 'check-update', '-d0']
> +    yum_update_cmd = subprocess.Popen(cmd,
> +                                      stdout=subprocess.PIPE,
> +                                      stderr=subprocess.PIPE)
> +    out, error = yum_update_cmd.communicate()
> +    if error != '':
> +        return None
> +
> +    return out.split()
> +
> +
> +def get_yum_packages_list_update():
> +    yum_checkupdate_output = _get_yum_checkupdate_output()
> +    if yum_checkupdate_output is None:
> +        return None
> +
> +    packages = []
> +    index = 0
> +    while index < len(yum_checkupdate_output):
> +        name_arch = yum_checkupdate_output[index]
> +        index += 1
> +        version = yum_checkupdate_output[index]
> +        index += 1
> +        repo = yum_checkupdate_output[index]
> +        index += 1
> +        name, arch = name_arch.rsplit('.', 1)
> +        packages.append(YumUpdatePackageObject(name, arch, version, repo))
> +
> +    return packages




More information about the Kimchi-devel mailing list