opensips 3.5的DB部署

        opensips 3.X的DB部署方式较之前版本有很大的不同。本文以opensips 3.5 为例,说明部署的过程。

       当OpenSIPS安装完成后,需要进一步做什么?最大的可能就是部署配套的DB。因为很多功能离不开它,比如用户鉴权、注册信息持久化、dialog信息维护,等等。

        你可以通过opensips-cli来部署db。在部署之前,你需要先安装它,如何安装请参考CentOS8安装opensips-cli-CSDN博客。本人

配置 OpenSIPS CLI

OpenSIPS CLI的配置信息,存储在配置文件里,用自己熟悉的文本编辑上期工具打开它,配置以下选项::

  • database_schema_path - (默认为 /usr/share/opensips/)
    把它的值设置为:[Install_Path]/share/opensips/
    其中[Install_Path]指向自己的opensips安装路径
  • database_url - 需要连接的DB URL
    如果未指定,在部署时会有交互提示)
  • database_name - (默认为o opensips)
    指定需要连接的数据库名字
  • database_modules - (缺省安装标准模块的表)
    指定需要安装DB的模块,可以指定ALL,把所有表都装上

更多配置信息,请参考:opensips-cli/docs/modules/database.md at master · OpenSIPS/opensips-cli · GitHub

注意: OpenSIPS CLI 按以下顺序查询配置文件: ~/.opensips-cli.cfg/etc/opensips-cli.cfg/etc/opensips/opensips-cli.cfg, 此外,你也可以通过-f参数指定自己的配置文件路径。

下面是我的配置文件模板

[default]
#log_level: DEBUG
log_level: INFO
prompt_name: opensips-cli
prompt_intro: Welcome to OpenSIPS Command Line Interface!
prompt_emptyline_repeat_cmd: False
history_file: ~/.opensips-cli.history
history_file_size: 1000
output_type: pretty-print
communication_type: fifo
fifo_file: /tmp/opensips_fifo
database_schema_path:/opt/payne/share/opensips/mysql
database_admin_url: mysql://root:opensipsdb@localhost
database_url: mysql://opensips:opensipsrw@localhost
database_name: opensips
database_modules: ALL

​​​​​​​创建数据库

I        准备好上述内容之后,你可以执行下面命令创建数据库:

opensips-cli -x database create

在这之后,如果因为增加模块需要添加新模块的关联表,比如说presence,只需要执行以下命令:

opensips-cli -x database add presence

当然,你也可以指定一个DB名,比如说opensips_test:

opensips-cli -x database create opensips_test

碰到问题:

AttributeError: 'str' object has no attribute '_execute_on_connection'

原因是用的sqlalchemy不支持string型参数,需要用text对象封装。

问题修改代码:

 opensipscli/db.py

        # all good - it's time to create the databaseif self.dialect == "postgresql":self.__conn.connection.connection.set_isolation_level(0)try:self.__conn.execute("CREATE DATABASE {}".format(self.db_name))self.__conn.connection.connection.set_isolation_level(1)except sqlalchemy.exc.OperationalError as se:logger.error("cannot create database: {}!".format(se))return Falseelif self.dialect != "sqlite":from sqlalchemy import textcreate_database_stmt = text("CREATE DATABASE {}".format(self.db_name))#self.__conn.execute("CREATE DATABASE {}".format(self.db_name))self.__conn.execute(create_database_stmt)

 

全部的SQL封装都需要修改。下面是修改后的db.py文件,修改后重装安装一下OpenSIPS CLI

#!/usr/bin/env python
##
## This file is part of OpenSIPS CLI
## (see https://github.com/OpenSIPS/opensips-cli).
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program 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 General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##from opensipscli.logger import logger
from opensipscli.config import cfg
import retry:import sqlalchemyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import Column, Date, Integer, String, Booleanfrom sqlalchemy.orm import sessionmaker, deferred# for now, we use our own make_url(), since Alchemy API is highly unstable#  (https://github.com/OpenSIPS/opensips-cli/issues/85)#from sqlalchemy.engine.url import make_urlsqlalchemy_available = Truelogger.debug("SQLAlchemy version: ", sqlalchemy.__version__)try:import sqlalchemy_utilsexcept ImportError:logger.debug("using embedded implementation of SQLAlchemy_Utils")# copied from SQLAlchemy_utils repositoryfrom opensipscli.libs import sqlalchemy_utils
except ImportError:logger.info("sqlalchemy not available!")sqlalchemy_available = FalseSUPPORTED_BACKENDS = ["mysql","postgresql","sqlite","oracle",
]"""
SQLAlchemy: Classes for ORM handling
"""
if sqlalchemy_available:Base = declarative_base()class Roles(Base):"""Postgres: Roles database"""__tablename__ = 'pg_roles'oid = Column(Integer, primary_key=True)rolname = Column(String)rolsuper = deferred(Column(Boolean), group='options')rolinherit = deferred(Column(Boolean), group='options')rolcreaterole = deferred(Column(Boolean), group='options')rolcreatedb = deferred(Column(Boolean), group='options')rolcanlogin = deferred(Column(Boolean), group='options')rolreplication = deferred(Column(Boolean), group='options')rolconnlimit = deferred(Column(Integer))rolpassword = Column(String)rolvaliduntil = deferred(Column(Date))rolbypassrls = deferred(Column(Boolean))rolconfig = deferred(Column(String))def __repr__(self):"""returns a string from an arbitrary object"""return self.shapeclass osdbError(Exception):"""OSDB: error handler"""passclass osdbConnectError(osdbError):"""OSDB: connecton error handler"""passclass osdbArgumentError(osdbError):"""OSDB: argument error handler"""passclass osdbNoSuchModuleError(osdbError):"""OSDB: module error handler"""passclass osdbModuleAlreadyExistsError(osdbError):"""OSDB: module error handler"""passclass osdbAccessDeniedError(osdbError):"""OSDB: module error handler"""passclass DBURL(object):def __init__(self, url):arr = url.split('://')self.drivername = arr[0].strip()if len(arr) != 2 or not self.drivername:raise Exception('Failed to parse RFC 1738 URL')self.username = Noneself.password = Noneself.host = Noneself.port = Noneself.database = Noneurl = arr[1].strip()if not url:returnarr = url.split('/')if len(arr) > 1:self.database = "/".join(arr[1:]).strip()url = arr[0].strip()arr = url.split('@')if len(arr) > 1:# handle user + passwordupass = arr[0].strip().split(':')self.username = upass[0].strip()if len(upass) > 1:self.password = ":".join(upass[1:]).strip()url = arr[1].strip()else:url = arr[0].strip()# handle host + portarr = url.strip().split(':')self.host = arr[0].strip()if len(arr) > 1:self.port = int(arr[1].strip())def __repr__(self):return "{}://{}{}{}{}{}{}".format(self.drivername,self.username or "",":***" if self.username != None and self.password != None else "","@" if self.username != None else "",self.host or "",":" + str(self.port) if self.port != None else "","/" + self.database if self.database != None else "")def __str__(self):return "{}://{}{}{}{}{}{}".format(self.drivername,self.username or "",":" + self.password if self.username != None and self.password != None else "","@" if self.username != None else "",self.host or "",":" + str(self.port) if self.port != None else "","/" + self.database if self.database != None else "")def make_url(url_string):return DBURL(url_string)class osdb(object):"""Class: object store database"""def __init__(self, db_url, db_name):"""constructor"""self.db_url = db_urlself.db_name = db_nameself.dialect = osdb.get_dialect(db_url)self.Session = sessionmaker()self.__engine = Noneself.__conn = None# TODO: do this only for SQLAlchemytry:if self.dialect == "postgresql":self.__engine = sqlalchemy.create_engine(db_url, isolation_level='AUTOCOMMIT')else:self.__engine = sqlalchemy.create_engine(db_url)logger.debug("connecting to %s", db_url)self.__conn = self.__engine.connect().\execution_options(autocommit=True)# connect the Session object to our engineself.Session.configure(bind=self.__engine)# instanciate the Session objectself.__session = self.Session()except sqlalchemy.exc.OperationalError as se:if self.dialect == "mysql":try:if int(se.args[0].split(",")[0].split("(")[2]) in [2006, # MySQL1698, # MariaDB "Access Denied"1044, # MariaDB "DB Access Denied"1045, # MariaDB "Access Denied (Using Password)"]:raise osdbAccessDeniedErrorexcept osdbAccessDeniedError:raiseexcept:logger.error("unexpected parsing exception")elif self.dialect == "postgresql" and \(("authentication" in se.args[0] and "failed" in se.args[0]) or \("no password supplied" in se.args[0])):raise osdbAccessDeniedErrorraise osdbError("unable to connect to the database")except sqlalchemy.exc.NoSuchModuleError:raise osdbError("cannot handle {} dialect".format(self.dialect))except sqlalchemy.exc.ArgumentError:raise osdbArgumentError("bad DB URL: {}".format(self.db_url))def alter_role(self, role_name, role_options=None, role_password=None):"""alter attributes of a role object"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")return Falseif not role_options is None:sqlcmd = "ALTER ROLE {} WITH {}".format(role_name, role_options)msg = "Alter role '{}' with options '{}'". \format(role_name, role_options, self.db_name)if not role_password is None:sqlcmd  += " PASSWORD '{}'".format(role_password)msg += " and password '********'"msg += " on database '{}'".format(self.db_name)try:result = self.__conn.execute(sqlcmd)if result:logger.info( "{} was successfull".format(msg))except:logger.error("%s failed", msg)return Falsereturndef connect(self, db_name=None):"""connect to database"""if db_name is not None:self.db_name = db_name# TODO: do this only for SQLAlchemytry:if self.dialect == "postgresql":self.db_url = self.set_url_db(self.db_url, self.db_name)if sqlalchemy_utils.database_exists(self.db_url) is True:engine = sqlalchemy.create_engine(self.db_url, isolation_level='AUTOCOMMIT')if self.__conn:self.__conn.close()self.__conn = engine.connect()# connect the Session object to our engineself.Session.configure(bind=self.__engine)# instanciate the Session objectself.session = self.Session()logger.debug("connected to database URL '%s'", self.db_url)elif self.dialect != "sqlite":from sqlalchemy import text#self.__conn.execute("USE {}".format(self.db_name))self.__conn.execute(text("USE {}".format(self.db_name)))except Exception as e:logger.error("failed to connect to %s", self.db_url)logger.error(e)return Falsereturn Truedef create(self, db_name=None):"""create a database object"""if db_name is None:db_name = self.db_name# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")logger.debug("Create Database '%s' for dialect '%s' ...",self.db_name, self.dialect)# all good - it's time to create the databaseif self.dialect == "postgresql":self.__conn.connection.connection.set_isolation_level(0)try:self.__conn.execute("CREATE DATABASE {}".format(self.db_name))self.__conn.connection.connection.set_isolation_level(1)except sqlalchemy.exc.OperationalError as se:logger.error("cannot create database: {}!".format(se))return Falseelif self.dialect != "sqlite":from sqlalchemy import textcreate_database_stmt = text("CREATE DATABASE {}".format(self.db_name))#self.__conn.execute("CREATE DATABASE {}".format(self.db_name))self.__conn.execute(create_database_stmt)logger.debug("success")return Truedef create_module(self, import_file):"""create a module object"""self.exec_sql_file(import_file)def ensure_user(self, db_url):url = make_url(db_url)if url.password is None:logger.error("database URL does not include a password")return Falseif url.drivername.lower() == "mysql":#sqlcmd = "CREATE USER IF NOT EXISTS '{}' IDENTIFIED BY '{}'".format(#            url.username, url.password)from sqlalchemy import textsqlcmd = text("CREATE USER IF NOT EXISTS '{}' IDENTIFIED BY '{}'".format(url.username, url.password))try:result = self.__conn.execute(sqlcmd)if result:logger.info("created user '%s'", url.username)except:logger.error("failed to create user '%s'", url.username)return Falseif url.username == 'root':logger.debug("skipping password change for root user")else:"""Query compatibility facts when changing a MySQL user password:- SET PASSWORD syntax has diverged between MySQL and MariaDB- ALTER USER syntax is not supported in MariaDB < 10.2"""# try MariaDB syntax first#sqlcmd = "SET PASSWORD FOR '{}' = PASSWORD('{}')".format(#            url.username, url.password)sqlcmd = text("SET PASSWORD FOR '{}' = PASSWORD('{}')".format(url.username, url.password))try:result = self.__conn.execute(sqlcmd)if result:logger.info("set password '%s%s%s' for '%s' (MariaDB)",url.password[0] if len(url.password) >= 1 else '',(len(url.password) - 2) * '*',url.password[-1] if len(url.password) >= 2 else '',url.username)except sqlalchemy.exc.ProgrammingError as se:try:if int(se.args[0].split(",")[0].split("(")[2]) == 1064:# syntax error!  OK, now try Oracle MySQL syntaxsqlcmd = "ALTER USER '{}' IDENTIFIED BY '{}'".format(url.username, url.password)result = self.__conn.execute(sqlcmd)if result:logger.info("set password '%s%s%s' for '%s' (MySQL)",url.password[0] if len(url.password) >= 1 else '',(len(url.password) - 2) * '*',url.password[-1] if len(url.password) >= 2 else '',url.username)except:logger.exception("failed to set password for '%s'", url.username)return Falseexcept:logger.exception("failed to set password for '%s'", url.username)return False#sqlcmd = "GRANT ALL ON {}.* TO '{}'".format(self.db_name, url.username)sqlcmd = text("GRANT ALL ON {}.* TO '{}'".format(self.db_name, url.username))try:result = self.__conn.execute(sqlcmd)if result:logger.info("granted access to user '%s' on DB '%s'",url.username, self.db_name)except:logger.exception("failed to grant access to '%s' on DB '%s'",url.username, self.db_name)return Falsesqlcmd = "FLUSH PRIVILEGES"try:result = self.__conn.execute(sqlcmd)logger.info("flushed privileges")except:logger.exception("failed to flush privileges")return Falseelif url.drivername.lower() == "postgresql":if not self.exists_role(url.username):logger.info("creating role %s", url.username)if not self.create_role(url.username, url.password):logger.error("failed to create role %s", url.username)self.create_role(url.username, url.password, update=True)sqlcmd = "GRANT ALL PRIVILEGES ON DATABASE {} TO {}".format(self.db_name, url.username)logger.info(sqlcmd)try:result = self.__conn.execute(sqlcmd)if result:logger.debug("... OK")except:logger.error("failed to grant ALL to '%s' on db '%s'",url.username, self.db_name)return Falsereturn Truedef create_role(self, role_name, role_password, update=False,role_options="NOCREATEDB NOCREATEROLE LOGIN"):"""create a role object (PostgreSQL secific)"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")if update:sqlcmd = "ALTER USER {} WITH PASSWORD '{}' {}".format(role_name, role_password, role_options)else:sqlcmd = "CREATE ROLE {} WITH {} PASSWORD '{}'".format(role_name, role_options, role_password)logger.info(sqlcmd)try:result = self.__conn.execute(sqlcmd)if result:logger.info("role '{}' with options '{}' created".format(role_name, role_options))except Exception as e:logger.exception(e)logger.error("creation of new role '%s' with options '%s' failed",role_name, role_options)return Falsereturn resultdef delete(self, table, filter_keys=None):"""delete a table object from a database"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")where_str = self.get_where(filter_keys)statement = "DELETE FROM {}{}".format(table, where_str)try:self.__conn.execute(statement)except sqlalchemy.exc.SQLAlchemyError as ex:logger.error("cannot execute query: {}".format(statement))logger.error(ex)return Falsereturn Truedef destroy(self):"""decontructor of a database object"""# TODO: do this only for SQLAlchemyif not self.__conn:returnself.__conn.close()def drop(self):"""drop a database object"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")if self.dialect != "sqlite":database_url = self.set_url_db(self.db_url, self.db_name)else:database_url = 'sqlite:///' + self.db_nametry:sqlalchemy_utils.drop_database(database_url)logger.debug("database '%s' dropped", self.db_name)return Trueexcept sqlalchemy.exc.NoSuchModuleError as me:logger.error("cannot check if database {} exists: {}".format(self.db_name, me))raise osdbError("cannot handle {} dialect".format(self.dialect)) from Nonedef drop_role(self, role_name):"""drop a role object (PostgreSQL specific)"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")return Falselogger.debug("Role '%s' will be dropped", role_name)sqlcmd = "DROP ROLE IF EXISTS {}".format(role_name)try:result = self.__conn.execute(sqlcmd)if result:logger.debug("Role '%s' dropped", role_name)except:logger.error("dropping role '%s' failed", role_name)return Falsereturndef entry_exists(self, table, constraints):"""check for existence of table constraints"""ret = self.find(table, "count(*)", constraints)if ret and ret.first()[0] != 0:return Truereturn Falsedef exec_sql_file(self, sql_file):"""deploy given sql file"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")with open(sql_file, 'r') as f:if sql_file.endswith("-migrate.sql"):try:sql = f.read()# the DELIMITER thingies are only useful to mysql shell clientsql = re.sub(r'DELIMITER .*\n', '', sql)sql = re.sub(r'\$\$', ';', sql)# DROP/CREATE PROCEDURE statements seem to only work separatelysql = re.sub(r'DROP PROCEDURE .*\n', '', sql)self.__conn.execute(sql)except sqlalchemy.exc.IntegrityError as ie:raise osdbError("cannot deploy {} file: {}".format(sql_file, ie)) from Noneelse:from sqlalchemy import textfor sql in f.read().split(";"):sql = sql.strip()if not sql:continuetry:self.__conn.execute(text(sql))except sqlalchemy.exc.IntegrityError as ie:raise osdbModuleAlreadyExistsError("cannot deploy {} file: {}".format(sql_file, ie)) from Nonedef exists(self, db=None):"""check for existence of a database object"""check_db = db if db is not None else self.db_name# TODO: do this only for SQLAlchemyif not self.__conn:return Falseif self.dialect != "sqlite":database_url = self.set_url_db(self.db_url, check_db)else:database_url = 'sqlite:///' + check_dblogger.debug("check database URL '{}'".format(database_url))try:if sqlalchemy_utils.database_exists(database_url):logger.debug("DB '{}' exists".format(check_db))return Trueexcept sqlalchemy.exc.NoSuchModuleError as me:logger.error("cannot check if database {} exists: {}".format(check_db, me))raise osdbError("cannot handle {} dialect".format(self.dialect)) from Nonelogger.debug("DB does not exist")return Falsedef exists_role(self, role_name=None):"""check for existence of a role object (PostgreSQL specific)"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")return Falseif role_name is None:role_name = 'opensips'filter_args = {'rolname': role_name}logger.debug("filter argument: '%s'", filter_args)role_count = self.__session.query(Roles).\filter_by(**filter_args).\count()logger.debug("Number of matching role instances: '%s'", role_count)if role_count >= 1:logger.debug("Role instance '%s' exists", role_name)return Trueelse:logger.debug("Role instance '%s' does not exist", role_name)return Falsedef find(self, table, fields, filter_keys):"""match fields in a given table"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")if not fields:fields = ['*']elif type(fields) != list:fields = [ fields ]where_str = self.get_where(filter_keys)statement = "SELECT {} FROM {}{}".format(", ".join(fields),table,where_str)try:result = self.__conn.execute(statement)except sqlalchemy.exc.SQLAlchemyError as ex:logger.error("cannot execute query: {}".format(statement))logger.error(ex)return Nonereturn resultdef get_dialect(url):"""extract database dialect from an url"""return url.split('://')[0]def get_where(self, filter_keys):"""construct a sql 'where clause' from given filter keys"""if filter_keys:where_str = ""for k, v in filter_keys.items():where_str += " AND {} = ".format(k)if type(v) == int:where_str += str(v)else:where_str += "'{}'".format(v.translate(str.maketrans({'\'': '\\\''})))if where_str != "":where_str = " WHERE " + where_str[5:]else:where_str = ""return where_strdef get_role(self, role_name="opensips"):"""get attibutes of a role object (PostgreSQL specific)"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")return False# query elements for the given rolerole_element = self.__session.query(Roles).\filter(Roles.rolname == role_name).all()# create a dictionary and output the key-value pairsfor row in role_element:#print ("role: ", row.rolname, "(password:", row.rolpassword, "canlogin:", row.rolcanlogin, ")")dict = self.row2dict(row)for key in sorted(dict, key=lambda k: dict[k], reverse=True):print (key + ": " + dict[key])logger.debug("role_elements: %s", dict)def grant_db_options(self, role_name, on_statement, privs="ALL PRIVILEGES"):"""assign attibutes to a role object (PostgreSQL specific)"""# TODO: is any other dialect using the "role" concept?if self.dialect != "postgresql":return False# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")sqlcmd = "GRANT {} {} TO {}".format(privs, on_statement, role_name)logger.info(sqlcmd)try:self.__conn.execute(sqlcmd)except Exception as e:logger.exception(e)logger.error("failed to grant '%s' '%s' to '%s'", privs, on_statement, role_name)return Falsereturn Truedef grant_public_schema(self, role_name):self.grant_db_options(role_name, "ON SCHEMA public")def grant_table_options(self, role, table, privs="ALL PRIVILEGES"):self.grant_db_options(role, "ON TABLE {}".format(table))def has_sqlalchemy():"""check for usability of the SQLAlchemy modules"""return sqlalchemy_availabledef has_dialect(dialect):"""check for support of a given database dialect via SQLAlchemy"""# TODO: do this only for SQLAlchemytry:sqlalchemy.create_engine('{}://'.format(dialect))except sqlalchemy.exc.NoSuchModuleError:return Falsereturn Truedef insert(self, table, keys):"""insert values into table"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")values = ""for v in keys.values():values += ", "if type(v) == int:values += velse:values += "'{}'".format(v.translate(str.maketrans({'\'': '\\\''})))statement = "INSERT INTO {} ({}) VALUES ({})".format(table, ", ".join(keys.keys()), values[2:])try:result = self.__conn.execute(statement)except sqlalchemy.exc.SQLAlchemyError as ex:logger.error("cannot execute query: {}".format(statement))logger.error(ex)return Falsereturn resultdef migrate(self, proc_suffix, migrate_scripts, old_db, new_db, tables=[]):"""migrate from source to destination database using SQL schema files@flavour: values should resemble: '2.4_to_3.0', '3.0_to_3.1'@sp_suffix: stored procedure name suffix, specific to each migration"""if self.dialect != "mysql":logger.error("Table data migration is only supported for MySQL!")returnproc_db_migrate = 'OSIPS_DB_MIGRATE_{}'.format(proc_suffix)proc_tb_migrate = 'OSIPS_TB_COPY_{}'.format(proc_suffix)self.connect(old_db)# separately drop DB/table migration stored procedures if already# present, since there are issues with multiple statements in 1 importtry:self.__conn.execute(sqlalchemy.sql.text("DROP PROCEDURE IF EXISTS {}".format(proc_db_migrate)).execution_options(autocommit=True))self.__conn.execute(sqlalchemy.sql.text("DROP PROCEDURE IF EXISTS {}".format(proc_tb_migrate)).execution_options(autocommit=True))except:logger.exception("Failed to drop migration stored procedures!")for ms in migrate_scripts:logger.debug("Importing {}...".format(ms))self.exec_sql_file(ms)if tables:for tb in tables:logger.info("Migrating {} data... ".format(tb))try:self.__conn.execute(sqlalchemy.sql.text("CALL {}.{}('{}', '{}', '{}')".format(old_db, proc_tb_migrate, old_db, new_db, tb)))except Exception as e:logger.exception(e)logger.error("Failed to migrate '{}' table data, ".format(tb) +"see above errors!")else:try:self.__conn.execute(sqlalchemy.sql.text("CALL {}.{}('{}', '{}')".format(old_db, proc_db_migrate, old_db, new_db)))except Exception as e:logger.exception(e)logger.error("Failed to migrate database!")print("Finished copying OpenSIPS table data " +"into database '{}'!".format(new_db))def row2dict(self, row):"""convert SQL table row to python dict"""dict = {}for column in row.__table__.columns:dict[column.name] = str(getattr(row, column.name))return dictdef update(self, table, update_keys, filter_keys=None):"""update table"""# TODO: do this only for SQLAlchemyif not self.__conn:raise osdbError("connection not available")update_str = ""for k, v in update_keys.items():update_str += ", {} = ".format(k)if type(v) == int:update_str += velse:update_str += "'{}'".format(v.translate(str.maketrans({'\'': '\\\''})))where_str = self.get_where(filter_keys)statement = "UPDATE {} SET {}{}".format(table,update_str[2:], where_str)try:result = self.__conn.execute(statement)except sqlalchemy.exc.SQLAlchemyError as ex:logger.error("cannot execute query: {}".format(statement))logger.error(ex)return Falsereturn result@staticmethoddef get_db_engine():if cfg.exists('database_admin_url'):engine = osdb.get_url_driver(cfg.get('database_admin_url'))elif cfg.exists('database_url'):engine = osdb.get_url_driver(cfg.get('database_url'))else:engine = "mysql"if engine not in SUPPORTED_BACKENDS:logger.error("bad database engine ({}), supported: {}".format(engine, " ".join(SUPPORTED_BACKENDS)))return Nonereturn engine@staticmethoddef get_db_host():if cfg.exists('database_admin_url'):return osdb.get_url_host(cfg.get('database_admin_url'))elif cfg.exists('database_url'):return osdb.get_url_host(cfg.get('database_url'))return "localhost"@staticmethoddef set_url_db(url, db):"""Force a given database @url string to include the given @db.Args:url (str): the URL to change the DB for.db (str): the name of the database to set.  If None, the databasepart will be removed from the URL."""at_idx = url.find('@')if at_idx < 0:logger.error("Bad database URL: {}, missing host part".format(url))return Nonedb_idx = url.find('/', at_idx)if db_idx < 0:if db is None:return urlreturn url + '/' + dbelse:if db is None:return url[:db_idx]return url[:db_idx+1] + db@staticmethoddef set_url_driver(url, driver):return driver + url[url.find(':'):]@staticmethoddef set_url_password(url, password):url = make_url(url)url.password = passwordreturn str(url)@staticmethoddef set_url_host(url, host):url = make_url(url)url.host = hostreturn str(url)@staticmethoddef get_url_driver(url, capitalize=False):if capitalize:driver = make_url(url).drivername.lower()capitalized = {'mysql': 'MySQL','postgresql': 'PostgreSQL','sqlite': 'SQLite','oracle': 'Oracle',}return capitalized.get(driver, driver.capitalize())else:return make_url(url).drivername.lower()@staticmethoddef get_url_user(url):return make_url(url).username@staticmethoddef get_url_pswd(url):return make_url(url).password@staticmethoddef get_url_host(url):return make_url(url).host

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339318.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL学习——影响选项文件处理的命令行选项和程序选项修改器

大多数支持选项文件的MySQL程序都处理以下选项。因为这些选项会影响选项文件的处理&#xff0c;所以必须在命令行上给出&#xff0c;而不是在选项文件中给出。为了正常工作&#xff0c;这些选项中的每一个都必须先于其他选项给出&#xff0c;但以下情况除外&#xff1a; -prin…

OpenCASCADE开发指南<十四>:OCCT建模类之BRepPrimAPI_MakePipe创建管道

1、OpenCasCade拓扑几何 在Open CASCADE Technology (OCCT) 中,除了基本三维几何体建模类BRepBuilderAPI外,还提供了复杂模型的建模类,常用的有如下几种,他们可以单独使用或相互组合,通过OCCT提供的融合函数进行组装。例如:BRepOffsetAPI_ThruSections、BRepOffsetAPI_Ma…

sqlite基本操作

简介 文章目录 简介1.数据库的安装2.数据库命令&#xff1a;API&#xff0c;创建表单代码 csprintf&#xff08;&#xff09;getchar和scanf&#xff08;&#xff09; 1.数据库的安装 sudo dpkg -i *.deb这个报错表明出现依赖问题 用这个命令后再试试sudo apt --fix-broken in…

Docker是什么?使用场景作用及Docker的安装和启动详解

目录 Docker是什么&#xff1f; Docker的发展 Docker的安装 Docker使用 Docker的运行机制 第一个Docker容器 进入Docker容器 客户机访问容器 Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker …

ChatGPT的基本原理是什么?又该如何提高其准确性?

在深入探索如何提升ChatGPT的准确性之前&#xff0c;让我们先来了解一下它的工作原理吧。ChatGPT是一种基于深度学习的自然语言生成模型&#xff0c;它通过预训练和微调两个关键步骤来学习和理解自然语言。 在预训练阶段&#xff0c;ChatGPT会接触到大规模的文本数据集&#x…

绘画参数配置及使用

绘画参数配置及使用 路径&#xff1a;站点后台-功能-AI绘画 进入参数配置 接口选择&#xff1a;多种接口自主选择&#xff08;需自己准备key&#xff09;&#xff0c;对应接口的key对话和绘画通用 存储空间&#xff1a; 位置在超管后台-存储空间 自主选择存储&#xff08;需…

冯喜运:6.3周一黄金原油行情分析及操作建议

【黄金消息面分析】&#xff1a;上周行情概述&#xff1a;现货黄金上周&#xff08;0527-0531&#xff09;反弹上探&#xff0c;5月27号开盘前本人曾提醒关注反弹&#xff0c;较当时上涨约30美元&#xff0c;最高至2364一线&#xff0c;其后震荡下跌。周线小幅收跌0.27%&#x…

微服务:Rabbitmq的WorkQueue模型的使用、默认消费方式(消息队列中间件)

文章目录 WorkQueue模型控制预取消息个数 WorkQueue模型 当然&#xff0c;一个队列&#xff0c;可以由多个消费者去监听。 来实现一下. 生产者&#xff1a; Testpublic void testWorkQueue() throws InterruptedException {// 队列名称String queueName "simple.queue…

通过提示工程将化学知识整合到大型语言模型中

在当今快速发展的人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;正成为科学研究的新兴工具。这些模型以其卓越的语言处理能力和零样本推理而闻名&#xff0c;为解决传统科学问题提供了全新的途径。然而&#xff0c;LLMs在特定科学领域的应用面临挑战&#…

力扣173题:二叉搜索树迭代器(含模拟面试)

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业…

蓝奏管理器iapp源码V3

蓝奏登录注册&#xff0c;简单管理文件夹等都没问题&#xff0c;就是上传接口需要有能力的人抓包进行修复一下&#xff08;我留了之前还能正常使用的接口&#xff0c;也是蓝奏官方的&#xff0c;所以参照一下就行。&#xff09;&#xff0c;这个应该也不是什么大问题&#xff0…

【自然语言处理】【Scaling Law】Observational Scaling Laws:跨不同模型构建Scaling Law

相关博客 【自然语言处理】【Scaling Law】Observational Scaling Laws&#xff1a;跨不同模型构建Scaling Law 【自然语言处理】【Scaling Law】语言模型物理学 第3.3部分&#xff1a;知识容量Scaling Laws 【自然语言处理】Transformer中的一种线性特征 【自然语言处理】【大…

Ansible04-Ansible Vars变量详解

目录 写在前面6 Ansible Vars 变量6.1 playbook中的变量6.1.1 playbook中定义变量的格式6.1.2 举例6.1.3 小tip 6.2 共有变量6.2.1 变量文件6.2.1.1 变量文件编写6.2.1.2 playbook编写6.2.1.3 运行测试 6.2.2 根据主机组使用变量6.2.2.1 groups_vars编写6.2.2.2 playbook编写6.…

第17篇:JTAG UART IP应用<四>

Q&#xff1a;如何通过JTAG UART发送命令控制开发板的外设比如LED&#xff1f; A&#xff1a;Quartus硬件工程以及Platform Designer系统在第一个Nios II工程--Hello_World的Quartus硬件工程基础上添加PIO&#xff0c;表示DE2-115开发板上的18个红色LED。 Nios II软件工程对应…

mysql中EXPLAIN详解

大家好。众所周知&#xff0c;MySQL 查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划&#xff0c;这个执行计划展示了接下来具体执行查询的方式。在日常工作过程中&#xff0c;我们可以使用EXPLAIN语句来查看某个查询语句的具体执行计划&#xff0c; 今天我们…

JMeter的基本使用

JMeter的基本使用三步骤&#xff1a;1.添加线程、2.添加请求、3.添加查询结果的内容 如果需要添加token请求头来验证&#xff0c;则需要再加上一步骤&#xff1a;添加请求头 1.线程 添加线程的方式 主要修改者三个属性值 Number of Threads&#xff1a;并发线程数 Ramp-up…

LabVIEW通过以太网控制PLC程序开发

在使用LabVIEW通过以太网控制PLC程序开发时&#xff0c;需要综合考虑硬件、软件和通信协议的协调工作。以下是详细步骤、注意事项、重点和难点分析&#xff0c;以及几种实现方式及其特点的概述。 实现步骤 确定硬件和软件环境&#xff1a; 确定PLC型号和品牌&#xff08;如西门…

Java 18新特性深度解析:提升开发效率与性能的革新工具

在Java的世界中&#xff0c;每一次更新都带来新的惊喜和挑战。Java 18作为长期支持版本&#xff0c;不仅延续了Java语言的稳定性和可靠性&#xff0c;还引入了一系列令人兴奋的新特性&#xff0c;旨在进一步提升开发者的生产力和应用程序的性能。本文将深入探讨Java 18中的关键…

【一刷《剑指Offer》】面试题 29:数组中出现次数超过一半的数字

力扣对应题目链接&#xff1a;169. 多数元素 - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;数组中出现次数超过一半的数字_牛客题霸_牛客网 (nowcoder.com) 核心考点 &#xff1a; 数组使用&#xff0c;简单算法的设计。 一、《剑指Offer》对应内容 二…

2024后端服务架构升级

文章目录 背景改造方案新架构图技术选型思考 服务拆分公共组件设计自部署算法服务排期计划 全球多活改造背景架构图分布式ID 背景 1、xx业务经过多轮的业务决策和调整&#xff0c;存在非常多技术包袱&#xff0c;带了不好的用户体验和极高的维护成本 2、多套机房部署&#xf…