diff --git a/packageship/packageship/application/apps/package/function/install_depend.py b/packageship/packageship/application/apps/package/function/install_depend.py new file mode 100644 index 0000000000000000000000000000000000000000..212ab5734ba7bdb8a4a5375e7ec8700107b8892d --- /dev/null +++ b/packageship/packageship/application/apps/package/function/install_depend.py @@ -0,0 +1,133 @@ +''' + Querying for install dependencies + Querying packages install depend for those package can be installed +''' +from packageship.libs.log import Log +from .searchdb import SearchDB +from .constants import ResponseCode +from .constants import ListNode + +LOGGER = Log(__name__) + + +class InstallDepend(): + ''' + Description: quert install depend of package + changeLog: + ''' + + def __init__(self, db_list): + ''' + :param db_list: A list of Database name to show the priority + ''' + self.binary_dict = DictionaryOperations() + self.search_list = [] + + self.db_list = db_list + self.search_db = SearchDB(db_list) + + def install_depend_result(self, binary_list, history_dicts=None): + ''' + Description: init result dict and determint the loop end point + :param binary_list: A list of binary rpm package name + history_dicts: record the searching install depend history, + defualt is None + :return binary_dict.dictionary: + {binary_name: [ + src, + dbname, + version, + [ + parent_node_package_name + 'install' + ] + ]} + ''' + if not self.search_db.db_object_dict: + return ResponseCode.DIS_CONNECTION_DB, None + if not binary_list: + response_code = ResponseCode.INPUT_NONE, + return response_code, None + for binary in binary_list: + if binary: + self.search_list.append(binary) + else: + LOGGER.logger.warning("There is a NONE in input value:" + str(binary_list)) + while self.search_list: + response_code = self.query_install(history_dicts) + return response_code, self.binary_dict.dictionary if response_code != \ + ResponseCode.DIS_CONNECTION_DB else None + + def query_install(self, history_dicts): + """ + Description: query a package install depend and append to result + :param history_dicts + :return response_code + changeLog: + """ + response_code, result_list = self.search_db.get_install_depend(self.search_list) + for search in self.search_list: + if search not in self.binary_dict.dictionary: + self.binary_dict.init_key(key=search, parent_node=[]) + self.search_list.clear() + if result_list: + for result, dbname in result_list: + if not self.binary_dict.dictionary[result.search_name][ListNode.PARENT_LIST]: + self.binary_dict.init_key(key=result.search_name, + src=result.search_src_name, + version=result.search_version, + dbname=dbname) + else: + self.binary_dict.update_value(key=result.search_name, + src=result.search_src_name, + version=result.search_version, + dbname=dbname) + + if result.depend_name: + if result.depend_name in self.binary_dict.dictionary: + self.binary_dict.update_value(key=result.depend_name, + parent_node=[result.search_name, 'install']) + elif history_dicts is not None and result.depend_name in history_dicts: + self.binary_dict.init_key( + key=result.depend_name, + src=history_dicts[result.depend_name][ListNode.SOURCE_NAME], + version=history_dicts[result.depend_name][ListNode.VERSION], + dbname=None, + parent_node=[[result.search_name, 'install']] + ) + else: + self.binary_dict.init_key(key=result.depend_name, + parent_node=[[result.search_name, 'install']]) + self.search_list.append(result.depend_name) + return response_code + +class DictionaryOperations(): + ''' + Related to dictionary operations, creating dictionary, append dictionary + ''' + + def __init__(self): + + self.dictionary = dict() + + def init_key(self, key, src=None, version=None, dbname=None, parent_node=None): + ''' + Creating dictionary + ''' + if dbname: + self.dictionary[key] = [src, version, dbname, [['root', None]]] + else: + self.dictionary[key] = [src, version, dbname, parent_node] + + def update_value(self, key, src=None, version=None, dbname=None, parent_node=None): + ''' + append dictionary + ''' + if src: + self.dictionary[key][ListNode.SOURCE_NAME] = src + if version: + self.dictionary[key][ListNode.VERSION] = version + if dbname: + self.dictionary[key][ListNode.DBNAME] = dbname + if parent_node: + self.dictionary[key][ListNode.PARENT_LIST].append(parent_node) diff --git a/packageship/packageship/application/apps/package/function/searchdb.py b/packageship/packageship/application/apps/package/function/searchdb.py index 374456f880d8dc201c55029a43475c3b8b49aa01..0128db138d953222b0d47312f758859205b7a81c 100644 --- a/packageship/packageship/application/apps/package/function/searchdb.py +++ b/packageship/packageship/application/apps/package/function/searchdb.py @@ -1,13 +1,132 @@ """ A set for all query databases function """ +from collections import namedtuple import yaml from flask import current_app +from sqlalchemy import text +from sqlalchemy.exc import SQLAlchemyError, DisconnectionError +from sqlalchemy.sql import literal_column +from packageship.libs.dbutils import DBHelper +from packageship.libs.log import Log +from packageship.application.models.package import bin_pack from packageship.libs.exception import ContentNoneException, Error from packageship.system_config import DATABASE_SUCCESS_FILE +from .constants import ResponseCode +LOGGER = Log(__name__) + +class SearchDB(): + """ + Description: query in database + changeLog: + """ + def __new__(cls, *args, **kwargs): + # pylint: disable=w0613 + if not hasattr(cls, "_instance"): + cls._instance = super(SearchDB, cls).__new__(cls) + return cls._instance + + def __init__(self, db_list): + self.db_object_dict = dict() + for db_name in db_list: + try: + with DBHelper(db_name=db_name) as data_base: + self.db_object_dict[db_name] = data_base + except DisconnectionError as connection_error: + current_app.logger.error(connection_error) + + def get_install_depend(self, binary_list): + """ + Description: get a package install depend from database: + binary_name -> binary_id -> requires_set -> requires_id_set -> provides_set + -> install_depend_binary_id_key_list -> install_depend_binary_name_list + :param binary_lsit: a list of binary package name + :return install depend list + changeLog: + """ + if not self.db_object_dict: + return ResponseCode.DIS_CONNECTION_DB, None + + if None in binary_list: + binary_list.remove(None) + search_set = set(binary_list) + result_list = [] + get_list = [] + if not search_set: + return ResponseCode.INPUT_NONE, None + for db_name, data_base in self.db_object_dict.items(): + try: + name_in = literal_column('name').in_(search_set) + sql_com = text(""" + SELECT DISTINCT + bin_pack.NAME AS depend_name, + bin_pack.version AS depend_version, + s2.NAME AS depend_src_name, + bin.NAME AS search_name, + s1.`name` AS search_src_name, + s1.version AS search_version + FROM + ( SELECT id, NAME,srcIDkey FROM bin_pack WHERE {} ) bin + LEFT JOIN pack_requires ON bin.id = pack_requires.binIDkey + LEFT JOIN pack_provides ON pack_provides.id = pack_requires.depProIDkey + LEFT JOIN bin_pack ON bin_pack.id = pack_provides.binIDkey + LEFT JOIN src_pack s1 ON s1.id = bin.srcIDkey + LEFT JOIN src_pack s2 ON s2.id = bin_pack.srcIDkey;""".format(name_in)) + install_set = data_base.session. \ + execute(sql_com, {'name_{}'.format(i): v + for i, v in enumerate(search_set, 1)}).fetchall() + if install_set: + # find search_name in db_name + # depend_name's db_name will be found in next loop + for result in install_set: + result_list.append((result, db_name)) + get_list.append(result.search_name) + get_set = set(get_list) + get_list.clear() + search_set.symmetric_difference_update(get_set) + if not search_set: + return ResponseCode.SUCCESS, result_list + else: + continue + except AttributeError as error_msg: + LOGGER.logger.error(error_msg) + except SQLAlchemyError as error_msg: + LOGGER.logger.error(error_msg) + return_tuple = namedtuple('return_tuple', + 'depend_name depend_version depend_src_name \ + search_name search_src_name search_version') + for binary_name in search_set: + result_list.append((return_tuple(None, None, None, + binary_name, None, None), 'NOT FOUND')) + return ResponseCode.SUCCESS, result_list + + def get_src_name(self, binary_name): + """ + Description: get a package source name from database: + bianry_name ->binary_source_name -> source_name + input: search package's name, database preority list + return: database name, source name + changeLog: + """ + for db_name, data_base in self.db_object_dict.items(): + try: + bin_obj = data_base.session.query(bin_pack).filter_by( + name=binary_name + ).first() + source_name = bin_obj.src_pack.name + source_version = bin_obj.src_pack.version + if source_name is not None: + return ResponseCode.SUCCESS, db_name, \ + source_name, source_version + except AttributeError as error_msg: + LOGGER.logger.error(error_msg) + except SQLAlchemyError as error_msg: + LOGGER.logger.error(error_msg) + return ResponseCode.DIS_CONNECTION_DB, None + return ResponseCode.PACK_NAME_NOT_FOUND, None, None, None def db_priority(): """ diff --git a/packageship/packageship/application/apps/package/view.py b/packageship/packageship/application/apps/package/view.py index a67c41c855b9f79643f57c1ef6079e32b5484aec..795427b2fb18ece19bd11762bd2c3e2298291630 100644 --- a/packageship/packageship/application/apps/package/view.py +++ b/packageship/packageship/application/apps/package/view.py @@ -8,17 +8,6 @@ from flask import current_app from flask_restful import Resource from sqlalchemy.exc import DisconnectionError -from packageship.application.apps.package.function.constants import ResponseCode -from packageship.application.apps.package.function.packages import get_packages -from packageship.application.apps.package.function.packages import update_single_package -from packageship.application.apps.package.function.packages import update_maintaniner_info -from packageship.application.apps.package.function.packages import get_single_package -from packageship.application.apps.package.function.searchdb import db_priority -from packageship.application.apps.package.serialize import PackagesSchema -from packageship.application.apps.package.serialize import GetpackSchema -from packageship.application.apps.package.serialize import PutpackSchema -from packageship.application.apps.package.serialize import DeletedbSchema -from packageship.application.apps.package.serialize import InitSystemSchema from packageship.application.initsystem.data_import import InitDataBase from packageship.libs.configutils.readconfig import ReadConfig from packageship.libs.exception import Error @@ -26,9 +15,24 @@ from packageship.libs.exception import ContentNoneException from packageship.libs.exception import DataMergeException from packageship.libs.log import Log from packageship.system_config import DATABASE_SUCCESS_FILE +from .function.constants import ResponseCode +from .function.packages import get_packages +from .function.packages import update_single_package +from .function.packages import update_maintaniner_info +from .function.packages import get_single_package +from .function.searchdb import db_priority +from .serialize import PackagesSchema +from .serialize import GetpackSchema +from .serialize import PutpackSchema +from .serialize import DeletedbSchema +from .serialize import InitSystemSchema + +from .function.install_depend import InstallDepend as installdepend +from .serialize import InstallDependSchema +from .serialize import have_err_db_name LOGGER = Log(__name__) - +#pylint: disable = no-self-use class Packages(Resource): ''' @@ -212,7 +216,7 @@ class InstallDepend(Resource): changeLog: ''' - def post(self, *args, **kwargs): + def post(self): ''' Description: Query a package's install depend(support querying in one or more databases) @@ -220,22 +224,66 @@ class InstallDepend(Resource): binaryName dbPreority:the array for database preority return: - resultList[ - result[ - binaryName: binary package name - srcName: the source package name for - that binary packge - dbName: - type: install install or build, which - depend on the function - parentNode: the binary package name which is - the install depend for binaryName + resultDict{ + binary_name: //binary package name + [ + src, //the source package name for + that binary packge + dbname, + version, + [ + parent_node, //the binary package name which is + the install depend for binaryName + type //install install or build, which + depend on the function + ] ] - ] + } exception: changeLog: ''' - pass + schema = InstallDependSchema() + + data = request.get_json() + validate_err = schema.validate(data) + if validate_err: + return jsonify( + ResponseCode.response_json(ResponseCode.PARAM_ERROR) + ) + pkg_name = data.get("binaryName") + + db_pri = db_priority() + if not db_pri: + return jsonify( + ResponseCode.response_json( + ResponseCode.FILE_NOT_FIND_ERROR + ) + ) + + db_list = data.get("db_list") if data.get("db_list") \ + else db_pri + + if not all([pkg_name, db_list]): + return jsonify( + ResponseCode.response_json(ResponseCode.PARAM_ERROR) + ) + + if have_err_db_name(db_list, db_pri): + return jsonify( + ResponseCode.response_json(ResponseCode.DB_NAME_ERROR) + ) + + response_code, install_dict = \ + installdepend(db_list).install_depend_result([pkg_name]) + + if not install_dict: + return jsonify( + ResponseCode.response_json(response_code) + ) + + return jsonify( + ResponseCode.response_json(ResponseCode.SUCCESS, data=install_dict) + ) class BuildDepend(Resource): @@ -330,7 +378,8 @@ class BeDepend(Resource): srcName: dbName: type: beinstall or bebuild, which depend on the function - childNode: the binary package name which is the be built/installed depend for binaryName + childNode: the binary package name which is the be built/installed + depend for binaryName ] ] exception: diff --git a/packageship/packageship/system_config.py b/packageship/packageship/system_config.py index b26d7598e97e89bc0a60ad890ae92035a25a4e94..8db61286c71e54c52710cb058498052915af731e 100644 --- a/packageship/packageship/system_config.py +++ b/packageship/packageship/system_config.py @@ -23,7 +23,7 @@ SYS_CONFIG_PATH = os.path.join('/', 'etc', 'pkgship', 'package.ini') # BASE_PATH, 'application', 'initsystem', 'import_success_databse.yaml') DATABASE_SUCCESS_FILE = os.path.join( - '/', 'var', 'run', 'import_success_databse.yaml') + '/', 'var', 'run', 'import_success_database.yaml') # If the path of the imported database is not specified in the configuration file, the # configuration in the system is used by default