data_import.py 28.5 KB
Newer Older
1
#!/usr/bin/python3
魔箭胖胖's avatar
魔箭胖胖 已提交
2 3 4 5 6
"""
Description: Initialization of data import
            Import the data in the sqlite database into the mysql database
Class: InitDataBase,MysqlDatabaseOperations,SqliteDatabaseOperations
"""
7 8 9 10 11 12 13 14
import os
import pathlib
import yaml
from sqlalchemy.exc import SQLAlchemyError, InternalError
from packageship.libs.dbutils.sqlalchemy_helper import DBHelper
from packageship.libs.exception import ContentNoneException
from packageship.libs.exception import DatabaseRepeatException
from packageship.libs.exception import Error
15
from packageship.libs.exception import ConfigurationException
16 17
from packageship.libs.configutils.readconfig import ReadConfig
from packageship.libs.log import Log
G
gongzt 已提交
18 19 20 21 22
from packageship.application.models.package import SrcPack
from packageship.application.models.package import BinPack
from packageship.application.models.package import BinRequires
from packageship.application.models.package import SrcRequires
from packageship.application.models.package import BinProvides
23
from packageship.application.models.package import BinFiles
G
gongzt 已提交
24
from packageship.application.models.package import Packages
25
from packageship import system_config
26 27 28 29 30

LOGGER = Log(__name__)


class InitDataBase():
魔箭胖胖's avatar
魔箭胖胖 已提交
31
    """
G
gongzt 已提交
32 33
    Description: Database initialization, generate multiple databases and data
                 based on configuration files
34

35 36 37 38
    Attributes:
        config_file_path: configuration file path
        config_file_datas: initialize the configuration content of the database
        db_type: type of database
魔箭胖胖's avatar
魔箭胖胖 已提交
39
    """
40 41

    def __init__(self, config_file_path=None):
魔箭胖胖's avatar
魔箭胖胖 已提交
42 43
        """
        Description: Class instance initialization
44

魔箭胖胖's avatar
魔箭胖胖 已提交
45 46 47
        Args:
            config_file_path: Configuration file path
        """
48 49 50 51 52
        self.config_file_path = config_file_path

        if self.config_file_path:
            # yaml configuration file content
            self.config_file_datas = self.__read_config_file()
53 54
        self._read_config = ReadConfig(system_config.SYS_CONFIG_PATH)
        self.db_type = 'sqlite'
55 56
        self.sql = None
        self._database = None
G
gongzt 已提交
57
        self._sqlite_db = None
58 59 60 61 62
        self._database_engine = {
            'sqlite': SqliteDatabaseOperations,
            'mysql': MysqlDatabaseOperations
        }
        self.database_name = None
63
        self._tables = ['src_pack', 'bin_pack',
64
                        'bin_requires', 'src_requires', 'bin_provides', 'bin_files']
65 66
        # Create life cycle related databases and tables
        if not self.create_database(db_name='lifecycle',
67 68
                                    tables=['packages_issue',
                                            'packages_maintainer'],
69 70 71
                                    storage=True):
            raise SQLAlchemyError(
                'Failed to create the specified database and table:lifecycle')
72 73

    def __read_config_file(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
74
        """
75 76
        Read the contents of the configuration file load each
        node data in the yaml configuration file as a list to return
77 78 79 80 81 82

        Returns:
            Initialize the contents of the database configuration file
        Raises:
            FileNotFoundError: The specified file does not exist
            TypeError: Wrong type of data
魔箭胖胖's avatar
魔箭胖胖 已提交
83
        """
84 85 86

        if not os.path.exists(self.config_file_path):
            raise FileNotFoundError(
87 88
                'system initialization configuration file \
                    does not exist: %s' % self.config_file_path)
89 90
        # load yaml configuration file
        with open(self.config_file_path, 'r', encoding='utf-8') as file_context:
91 92 93 94
            try:
                init_database_config = yaml.load(
                    file_context.read(), Loader=yaml.FullLoader)
            except yaml.YAMLError as yaml_error:
95 96 97

                raise ConfigurationException(' '.join("The format of the yaml configuration\
                     file is wrong please check and try again:{0}".format(yaml_error).split()))
98

99
            if init_database_config is None:
100
                raise ConfigurationException(
101 102
                    'The content of the database initialization configuration file cannot be empty')
            if not isinstance(init_database_config, list):
103
                raise ConfigurationException(
104 105 106 107
                    ' '.join('The format of the initial database configuration file\
                        is incorrect.When multiple databases need to be initialized, \
                        it needs to be configured in the form of multiple \
                        nodes:{}'.format(self.config_file_path).split()))
108 109
            for config_item in init_database_config:
                if not isinstance(config_item, dict):
110 111 112 113
                    raise ConfigurationException(' '.join('The format of the initial database\
                        configuration file is incorrect, and the value in a single node should\
                        be presented in the form of key - val pairs: \
                        {}'.format(self.config_file_path).split()))
114 115 116
            return init_database_config

    def init_data(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
117
        """
118
        Initialization of the database
119 120 121

        Raises:
            IOError: An error occurred while deleting the database information file
魔箭胖胖's avatar
魔箭胖胖 已提交
122
        """
123 124
        if getattr(self, 'config_file_datas', None) is None or \
                self.config_file_datas is None:
125 126
            raise ContentNoneException('The content of the database initialization \
                configuration file is empty')
127 128 129 130 131 132 133 134

        if self.__exists_repeat_database():
            raise DatabaseRepeatException(
                'There is a duplicate database configuration')
        if not InitDataBase.delete_settings_file():
            raise IOError(
                'An error occurred while deleting the database configuration file')

135 136
        for database_config in self.config_file_datas:
            if not database_config.get('dbname'):
137 138
                LOGGER.logger.error(
                    'The database name in the database initialization configuration file is empty')
139
                continue
140
            priority = database_config.get('priority')
141
            if not isinstance(priority, int) or priority < 0 or priority > 100:
142 143
                LOGGER.logger.error('The priority value type in the database initialization \
                    configuration file is incorrect')
144
                continue
145 146 147 148 149
            lifecycle_status_val = database_config.get('lifecycle')
            if lifecycle_status_val not in ('enable', 'disable'):
                LOGGER.logger.error('The status value of the life cycle in the initialization\
                    configuration file can only be enable or disable')
                continue
150
            # Initialization data
151
            self._init_data(database_config)
152

153
    def _create_database(self, db_name, tables, storage=False):
魔箭胖胖's avatar
魔箭胖胖 已提交
154
        """
155 156
        create related databases

157
        Args:
158
            database_config: Initialize the configuration content of the database_config
159 160 161 162
        Returns:
            The generated mysql database or sqlite database
        Raises:
            SQLAlchemyError: Abnormal database operation
魔箭胖胖's avatar
魔箭胖胖 已提交
163
        """
164 165 166 167 168 169 170 171
        _database_engine = self._database_engine.get(self.db_type)
        if not _database_engine:
            raise Error('The database engine is set incorrectly, \
            currently only the following engines are supported: %s '
                        % '、'.join(self._database_engine.keys()))
        _create_table_result = _database_engine(
            db_name=db_name, tables=tables, storage=storage).create_database(self)
        return _create_table_result
172

173
    def _init_data(self, database_config):
魔箭胖胖's avatar
魔箭胖胖 已提交
174
        """
175 176
        data initialization operation

177 178 179 180 181 182 183 184 185
        Args:
            database: Initialize the configuration content of the database
        Returns:

        Raises:
            ContentNoneException: Exception with empty content
            TypeError: Data type error
            SQLAlchemyError: Abnormal database operation
            IOError: An error occurred while deleting the database information file
魔箭胖胖's avatar
魔箭胖胖 已提交
186
        """
187

188 189
        try:
            # 1. create a database and related tables in the database
190 191 192 193 194 195 196
            _db_name = database_config.get('dbname')
            _create_database_result = self._create_database(
                _db_name, self._tables)
            if not _create_database_result:
                raise SQLAlchemyError(
                    'Failed to create the specified database and table:%s'
                    % database_config['dbname'])
197
            # 2. get the data of binary packages and source packages
198 199
            src_db_file = database_config.get('src_db_file')
            bin_db_file = database_config.get('bin_db_file')
200

201
            if src_db_file is None or bin_db_file is None:
202
                raise ContentNoneException(
203 204
                    'The path to the sqlite file in the database initialization configuration \
                    is incorrect ')
Y
Yiru Wang Mac 已提交
205
            if not os.path.exists(src_db_file) or not os.path.exists(bin_db_file):
206 207
                raise FileNotFoundError("sqlite file {src} or {bin} does not exist, please \
                    check and try again".format(src=src_db_file, bin=bin_db_file))
208
            # 3. Obtain temporary source package files and binary package files
209 210
            if self.__save_data(database_config,
                                self.database_name):
211 212
                # Update the configuration file of the database
                database_content = {
213 214
                    'database_name': _db_name,
                    'priority': database_config.get('priority'),
215 216 217 218 219 220 221
                }
                InitDataBase.__updata_settings_file(
                    database_content=database_content)

        except (SQLAlchemyError, ContentNoneException, TypeError,
                Error, FileNotFoundError) as error_msg:
            LOGGER.logger.error(error_msg)
222
            # Delete the specified database
223
            self.__del_database(_db_name)
224

225
    def __del_database(self, db_name):
226
        try:
227 228 229
            _database_engine = self._database_engine.get(self.db_type)
            del_result = _database_engine(db_name=db_name).drop_database()
            return del_result
230 231
        except (IOError, Error) as exception_msg:
            LOGGER.logger.error(exception_msg)
232
            return False
233

234 235
    @staticmethod
    def __columns(cursor):
魔箭胖胖's avatar
魔箭胖胖 已提交
236
        """
237 238 239
        functional description:Returns all the column names
        queried by the current cursor

240
        Args:
241
            cursor: Cursor
242
        Returns:
243
            The first columns
244 245
        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
246
        """
247
        return [col[0] for col in cursor.description]
248

249
    def __get_data(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
250
        """
251 252
        According to different sql statements, query related table data

253
        Args:
254

255 256 257 258
        Returns:

        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
259
        """
260 261 262 263 264 265 266 267 268 269
        if self.sql is None:
            return None
        try:
            src_packages_data = self._database.session.execute(self.sql)
            columns = InitDataBase.__columns(
                src_packages_data.cursor)
            return [dict(zip(columns, row)) for row in src_packages_data.fetchall()]
        except SQLAlchemyError as sql_error:
            LOGGER.logger.error(sql_error)
            return None
270

271
    def __save_data(self, database_config, db_name):
魔箭胖胖's avatar
魔箭胖胖 已提交
272
        """
273 274
        integration of multiple data files

275
        Args:
276 277
            src_package_paths: Source package database file
            bin_package_paths: Binary package database file
278
        Returns:
279
            Path of the generated temporary database file
280 281
        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
282
        """
283 284 285 286
        src_db_file = database_config.get('src_db_file')
        bin_db_file = database_config.get('bin_db_file')
        table_name = database_config.get('dbname')
        lifecycle_status_val = database_config.get('lifecycle')
287
        try:
288
            with DBHelper(db_name=src_db_file, db_type='sqlite:///', complete_route_db=True) \
289 290 291
                    as database:
                self._database = database
                # Save data related to source package
292 293
                self._save_src_packages(
                    db_name, table_name, lifecycle_status_val)
294 295
                self._save_src_requires(db_name)

296
            with DBHelper(db_name=bin_db_file, db_type='sqlite:///', complete_route_db=True)\
297 298 299 300 301 302
                    as database:
                self._database = database
                # Save binary package related data
                self._save_bin_packages(db_name)
                self._save_bin_requires(db_name)
                self._save_bin_provides(db_name)
303
                self._save_bin_files(db_name)
304 305
        except (SQLAlchemyError, ContentNoneException) as sql_error:
            LOGGER.logger.error(sql_error)
306
            self.__del_database(db_name)
307 308 309
            return False
        else:
            return True
310

G
gongzt 已提交
311
    def _save_src_packages(self, db_name, table_name, lifecycle_status_val):
魔箭胖胖's avatar
魔箭胖胖 已提交
312
        """
313 314
        Save the source package data

315
        Args:
316
            db_name: Saved database name
317 318 319 320
        Returns:

        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
321
        """
322 323 324 325 326 327 328 329
        # Query all source packages
        self.sql = " select * from packages "
        packages_datas = self.__get_data()
        if packages_datas is None:
            raise ContentNoneException(
                '{db_name}:There is no relevant data in the source \
                    package provided '.format(db_name=db_name))
        with DBHelper(db_name=db_name) as database:
G
gongzt 已提交
330
            database.batch_add(packages_datas, SrcPack)
331 332
        if lifecycle_status_val == 'enable':
            InitDataBase._storage_packages(table_name, packages_datas)
333

334 335
    @staticmethod
    def _storage_packages(table_name, package_data):
336 337 338 339
        """
            Bulk storage of source code package data
        """
        add_packages = []
G
gongzt 已提交
340
        cls_model = Packages.package_meta(table_name)
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
        pkg_keys = ('name', 'url', 'rpm_license', 'version',
                    'release', 'summary', 'description')
        with DBHelper(db_name="lifecycle") as database:
            if table_name not in database.engine.table_names():
                database.create_table([table_name])
            # Query data that already exists in the database
            exist_packages_dict = dict()
            for pkg in database.session.query(cls_model).all():
                exist_packages_dict[pkg.name] = pkg
            _packages = []
            for pkg in package_data:
                _package_dict = {key: pkg[key] for key in pkg_keys}
                _packages.append(_package_dict)

            # Combine all package data, save or update
            for package_item in _packages:
                package_model = exist_packages_dict.get(package_item['name'])
                if package_model:
                    for key, val in package_item.items():
                        setattr(package_model, key, val)
                else:
                    add_packages.append(package_item)

            if add_packages:
                database.batch_add(add_packages, cls_model)
G
gongzt 已提交
366
                database.session.commit()
367

368
    def _save_src_requires(self, db_name):
魔箭胖胖's avatar
魔箭胖胖 已提交
369
        """
370 371
        Save the dependent package data of the source package

372
        Args:
373
            db_name:Name database
374 375 376 377
        Returns:

        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
378
        """
379 380 381 382 383 384 385
        # Query all source packages
        self.sql = " select * from requires "
        requires_datas = self.__get_data()
        if requires_datas is None:
            raise ContentNoneException('{db_name}: The package data that the source package \
                depends on is empty'.format(db_name=db_name))
        with DBHelper(db_name=db_name) as database:
G
gongzt 已提交
386
            database.batch_add(requires_datas, SrcRequires)
魔箭胖胖's avatar
魔箭胖胖 已提交
387

388
    def _save_bin_packages(self, db_name):
魔箭胖胖's avatar
魔箭胖胖 已提交
389
        """
390 391
        Save binary package data

392
        Args:
393
            db_name:Name database
394 395 396 397
        Returns:

        Raises:

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
        """
        self.sql = " select * from packages "
        bin_packaegs = self.__get_data()
        if bin_packaegs is None:
            raise ContentNoneException(
                '{db_name}:There is no relevant data in the provided \
                     binary package '.format(db_name=db_name))
        for index, bin_package_item in enumerate(bin_packaegs):
            try:
                src_package_name = bin_package_item.get('rpm_sourcerpm').split(
                    '-' + bin_package_item.get('version'))[0]
            except AttributeError as exception_msg:
                src_package_name = None
                LOGGER.logger.warning(exception_msg)
            finally:
                bin_packaegs[index]['src_name'] = src_package_name
414

415
        with DBHelper(db_name=db_name) as database:
G
gongzt 已提交
416
            database.batch_add(bin_packaegs, BinPack)
417

418 419 420
    def _save_bin_requires(self, db_name):
        """
        Save the dependent package data of the binary package
421

422 423 424
        Args:
            db_name:Name database
        Returns:
425

426
        Raises:
427

428 429 430 431 432 433 434
        """
        self.sql = " select * from requires "
        requires_datas = self.__get_data()
        if requires_datas is None:
            raise ContentNoneException(
                '{db_name}:There is no relevant data in the provided binary \
                    dependency package'.format(db_name=db_name))
435

436
        with DBHelper(db_name=db_name) as database:
G
gongzt 已提交
437
            database.batch_add(requires_datas, BinRequires)
438

439
    def _save_bin_provides(self, db_name):
魔箭胖胖's avatar
魔箭胖胖 已提交
440
        """
441 442
        Save the component data provided by the binary package

443
        Args:
444
            db_name:Name database
445
        Returns:
446

447
        Raises:
448

魔箭胖胖's avatar
魔箭胖胖 已提交
449
        """
450 451 452 453 454 455
        self.sql = " select * from provides "
        provides_datas = self.__get_data()
        if provides_datas is None:
            raise ContentNoneException(
                '{db_name}:There is no relevant data in the provided \
                    binary component '.format(db_name=db_name))
456

457
        with DBHelper(db_name=db_name) as database:
G
gongzt 已提交
458
            database.batch_add(provides_datas, BinProvides)
459

460 461 462 463 464 465 466 467 468 469 470 471
    def _save_bin_files(self, db_name):

        self.sql = " select * from files "
        files_datas = self.__get_data()
        if files_datas is None:
            raise ContentNoneException(
                '{db_name}:There is no relevant binary file installation\
                    path data in the provided database '.format(db_name=db_name))

        with DBHelper(db_name=db_name) as database:
            database.batch_add(files_datas, BinFiles)

472
    def __exists_repeat_database(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
473
        """
474
        Determine if the same database name exists
475

476
        Returns:
477
            True if there are duplicate databases, false otherwise
478 479
        Raises:

魔箭胖胖's avatar
魔箭胖胖 已提交
480
        """
481 482
        db_names = [name.get('dbname')
                    for name in self.config_file_datas]
483

484 485
        if len(set(db_names)) != len(self.config_file_datas):
            return True
486

487
        return False
488 489 490

    @staticmethod
    def __updata_settings_file(**Kwargs):
魔箭胖胖's avatar
魔箭胖胖 已提交
491
        """
492 493
        update some configuration files related to the database in the system

494
        Args:
495
            **Kwargs: data related to configuration file nodes
496
            database_name: Name database
魔箭胖胖's avatar
魔箭胖胖 已提交
497 498
        Returns:

499 500 501
        Raises:
            FileNotFoundError: The specified file was not found
            IOError: File or network operation io abnormal
魔箭胖胖's avatar
魔箭胖胖 已提交
502
        """
503
        try:
504 505 506
            if not os.path.exists(system_config.DATABASE_FILE_INFO):
                pathlib.Path(system_config.DATABASE_FILE_INFO).touch()
            with open(system_config.DATABASE_FILE_INFO, 'a+', encoding='utf8') as file_context:
507 508 509 510 511 512 513 514 515 516 517 518 519 520
                setting_content = []
                if 'database_content' in Kwargs.keys():
                    content = Kwargs.get('database_content')
                    if content:
                        setting_content.append(content)
                yaml.dump(setting_content, file_context)

        except FileNotFoundError as not_found:
            LOGGER.logger.error(not_found)
        except IOError as exception_msg:
            LOGGER.logger.error(exception_msg)

    @staticmethod
    def delete_settings_file():
魔箭胖胖's avatar
魔箭胖胖 已提交
521
        """
522 523
        Delete the configuration file of the database

524 525 526 527 528 529
        Args:

        Returns:
            True if the deletion is successful, otherwise false
        Raises:
            IOError: File or network operation io abnormal
魔箭胖胖's avatar
魔箭胖胖 已提交
530
        """
531

532
        try:
533 534
            if os.path.exists(system_config.DATABASE_FILE_INFO):
                os.remove(system_config.DATABASE_FILE_INFO)
535 536 537 538 539 540 541
        except (IOError, Error) as exception_msg:
            LOGGER.logger.error(exception_msg)
            return False
        else:
            return True

    def delete_db(self, db_name):
魔箭胖胖's avatar
魔箭胖胖 已提交
542
        """
543 544
        delete the database

545 546 547 548 549 550
        Args:
            db_name: The name of the database
        Returns:

        Raises:
            IOError: File or network operation io abnormal
魔箭胖胖's avatar
魔箭胖胖 已提交
551
        """
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
        try:
            del_result = True
            file_read = open(
                system_config.DATABASE_FILE_INFO, 'r', encoding='utf-8')
            _databases = yaml.load(
                file_read.read(), Loader=yaml.FullLoader)
            for database in _databases:
                if database.get('database_name') == db_name:
                    _databases.remove(database)
            # Delete the successfully imported database configuration node
            with open(system_config.DATABASE_FILE_INFO, 'w+', encoding='utf-8') as file_context:
                yaml.safe_dump(_databases, file_context)
        except (IOError, Error) as del_config_error:
            LOGGER.logger.error(del_config_error)
            del_result = False
        finally:
            file_read.close()
569 570

        if del_result:
571
            del_result = self.__del_database(db_name)
572 573

        return del_result
574

575 576 577 578 579 580 581 582 583 584 585 586
    def create_database(self, db_name, tables=None, storage=True):
        """
            Create databases and tables related to the package life cycle

            Args:
                db_name: The name of the database
                tables: Table to be created
        """
        _create_database_result = self._create_database(
            db_name, tables, storage)
        return _create_database_result

587 588

class MysqlDatabaseOperations():
魔箭胖胖's avatar
魔箭胖胖 已提交
589
    """
590 591
    Related to database operations, creating databases, creating tables

592 593 594 595
    Attributes:
        db_name: The name of the database
        create_database_sql: SQL statement to create a database
        drop_database_sql: Delete the SQL statement of the database
魔箭胖胖's avatar
魔箭胖胖 已提交
596
    """
597

598
    def __init__(self, db_name, tables=None, storage=False):
魔箭胖胖's avatar
魔箭胖胖 已提交
599
        """
600 601
        Class instance initialization

魔箭胖胖's avatar
魔箭胖胖 已提交
602 603 604
        Args:
            db_name: Database name
        """
605 606 607 608 609
        self.db_name = db_name
        self.create_database_sql = ''' CREATE DATABASE if not exists `{db_name}` \
                                    DEFAULT CHARACTER SET utf8mb4; '''.format(db_name=self.db_name)
        self.drop_database_sql = '''drop DATABASE if exists `{db_name}` '''.format(
            db_name=self.db_name)
610
        self.tables = tables
611
        self.storage = storage
612

613
    def create_database(self, init_db):
魔箭胖胖's avatar
魔箭胖胖 已提交
614
        """
615
        create a mysql database
616 617 618 619 620

        Returns:
            True if successful, otherwise false
        Raises:
            SQLAlchemyError: An exception occurred while creating the database
魔箭胖胖's avatar
魔箭胖胖 已提交
621
        """
622 623 624
        _create_success = True
        if isinstance(init_db, InitDataBase):
            init_db.database_name = self.db_name
625 626 627 628
        with DBHelper(db_name='mysql') as data_base:

            try:
                # create database
629
                if not self.storage:
630
                    data_base.session.execute(self.drop_database_sql)
631
                data_base.session.execute(self.create_database_sql)
632 633 634
            except InternalError as internal_error:
                LOGGER.logger.info(internal_error)
            except SQLAlchemyError as exception_msg:
635 636
                LOGGER.logger.error(exception_msg)
                return False
637 638 639
        if self.tables:
            _create_success = self.__create_tables()
        return _create_success
640

641
    def drop_database(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
642
        """
643 644
        Delete the database according to the specified name

645 646 647
        Args:
            db_name: The name of the database to be deleted
        Returns:
魔箭胖胖's avatar
魔箭胖胖 已提交
648
            True if successful, otherwise false
649 650
        Raises:
            SQLAlchemyError: An exception occurred while creating the database
魔箭胖胖's avatar
魔箭胖胖 已提交
651
        """
652
        if self.db_name is None:
653 654 655 656
            raise IOError(
                "The name of the database to be deleted cannot be empty")
        with DBHelper(db_name='mysql') as data_base:
            drop_database = '''  drop DATABASE if exists `{db_name}` '''.format(
657
                db_name=self.db_name)
658 659 660 661 662 663 664 665 666
            try:
                data_base.session.execute(drop_database)
            except SQLAlchemyError as exception_msg:
                LOGGER.logger.error(exception_msg)
                return False
            else:
                return True

    def __create_tables(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
667
        """
668
        Create the specified data table
魔箭胖胖's avatar
魔箭胖胖 已提交
669 670 671 672 673 674

        Returns:
            True if successful, otherwise false
        Raises:
            SQLAlchemyError: An exception occurred while creating the database
        """
675 676
        try:
            with DBHelper(db_name=self.db_name) as database:
677
                if self.tables:
678 679 680
                    _tables = list(set(self.tables).difference(
                        set(database.engine.table_names())))
                    database.create_table(_tables)
681 682 683 684 685 686 687 688 689

        except SQLAlchemyError as exception_msg:
            LOGGER.logger.error(exception_msg)
            return False
        else:
            return True


class SqliteDatabaseOperations():
魔箭胖胖's avatar
魔箭胖胖 已提交
690
    """
691 692
    sqlite database related operations

693 694 695
    Attributes:
        db_name: Name database
        database_file_folder: Database folder path
魔箭胖胖's avatar
魔箭胖胖 已提交
696
    """
697

698
    def __init__(self, db_name, tables=None, storage=False, ** kwargs):
魔箭胖胖's avatar
魔箭胖胖 已提交
699
        """
700 701
        Class instance initialization

魔箭胖胖's avatar
魔箭胖胖 已提交
702 703 704 705
        Args:
            db_name: Database name
            kwargs: data related to configuration file nodes
        """
706
        self.db_name = db_name
707
        self._read_config = ReadConfig(system_config.SYS_CONFIG_PATH)
708
        if getattr(kwargs, 'database_path', None) is None:
709
            self._database_file_path()
710 711
        else:
            self.database_file_folder = kwargs.get('database_path')
712
        self.tables = tables
713
        self.storage = storage
714

715
    def _database_file_path(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
716
        """
717 718
        Database file path

魔箭胖胖's avatar
魔箭胖胖 已提交
719 720 721 722 723
        Returns:

        Raises:
            IOError: File or network operation io abnormal
        """
724 725 726
        self.database_file_folder = self._read_config.get_system(
            'data_base_path')
        if not self.database_file_folder:
727
            self.database_file_folder = system_config.DATABASE_FOLDER_PATH
728 729 730 731 732 733 734 735

        if not os.path.exists(self.database_file_folder):
            try:
                os.makedirs(self.database_file_folder)
            except IOError as makedirs_error:
                LOGGER.logger.error(makedirs_error)
                self.database_file_folder = None

736
    def create_database(self, init_db):
魔箭胖胖's avatar
魔箭胖胖 已提交
737
        """
738
        create sqlite database and table
739 740 741 742 743 744 745

        Returns:
            After successful generation, return the database file address,
            otherwise return none
        Raises:
            FileNotFoundError: The specified folder path does not exist
            SQLAlchemyError: An error occurred while generating the database
魔箭胖胖's avatar
魔箭胖胖 已提交
746
        """
747
        _create_success = False
748 749 750 751 752 753
        if self.database_file_folder is None:
            raise FileNotFoundError('Database folder does not exist')

        _db_file = os.path.join(
            self.database_file_folder, self.db_name)

754
        if not self.storage and os.path.exists(_db_file + '.db'):
755 756
            os.remove(_db_file + '.db')

757
        # create a sqlite database
758 759 760 761 762 763 764 765 766 767 768 769 770
        with DBHelper(db_name=_db_file) as database:
            try:
                if self.tables:
                    _tables = list(set(self.tables).difference(
                        set(database.engine.table_names())))
                    database.create_table(_tables)
            except (SQLAlchemyError, InternalError) as create_table_err:
                LOGGER.logger.error(create_table_err)
                return _create_success
        if isinstance(init_db, InitDataBase):
            init_db.database_name = _db_file
            _create_success = True
        return _create_success
771 772

    def drop_database(self):
魔箭胖胖's avatar
魔箭胖胖 已提交
773
        """
774
        Delete the specified sqlite database
775 776

        Returns:
魔箭胖胖's avatar
魔箭胖胖 已提交
777
            Return true after successful deletion, otherwise return false
778 779
        Raises:
            IOError: An io exception occurred while deleting the specified database file
魔箭胖胖's avatar
魔箭胖胖 已提交
780
        """
781 782
        try:
            db_path = os.path.join(
783
                self.database_file_folder, self.db_name + '.db')
784 785 786 787 788 789 790
            if os.path.exists(db_path):
                os.remove(db_path)
        except IOError as exception_msg:
            LOGGER.logger.error(exception_msg)
            return False
        else:
            return True