opensipscli 0.3.5__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opensipscli-0.3.5 → opensipscli-0.4.0}/PKG-INFO +5 -6
- {opensipscli-0.3.5 → opensipscli-0.4.0}/README.md +2 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/bin/opensips-cli +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docker/Dockerfile +2 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/INSTALLATION.md +7 -8
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/database.md +4 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/user.md +2 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/__init__.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/args.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/cli.py +10 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/comm.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/config.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/db.py +48 -44
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/defaults.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/libs/__init__.py +1 -1
- opensipscli-0.4.0/opensipscli/libs/sqlalchemy_utils.py +166 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/logger.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/main.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/module.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/__init__.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/database.py +9 -3
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/diagnose.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/instance.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/mi.py +12 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/tls.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/trace.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/trap.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/modules/user.py +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/opensipscli/version.py +2 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/changelog +6 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/control +2 -2
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/rules +1 -1
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/redhat_fedora/opensips-cli.spec +8 -7
- {opensipscli-0.3.5 → opensipscli-0.4.0}/pyproject.toml +2 -3
- {opensipscli-0.3.5 → opensipscli-0.4.0}/setup.py +3 -4
- opensipscli-0.3.5/opensipscli/libs/sqlalchemy_utils.py +0 -244
- {opensipscli-0.3.5 → opensipscli-0.4.0}/.github/workflows/docker-push-image.yml +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/.github/workflows/docker-readme.yml +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/.github/workflows/pypi.yml +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/.gitignore +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/LICENSE +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/SECURITY.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docker/Makefile +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docker/docker.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docker/run.sh +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/diagnose.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/instance.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/mi.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/tls.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/trace.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/docs/modules/trap.md +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/etc/default.cfg +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/.gitignore +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/compat +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/copyright +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/source/format +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/packaging/debian/watch +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/setup.cfg +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/test/alltests.py +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/test/test-database.sh +0 -0
- {opensipscli-0.3.5 → opensipscli-0.4.0}/test/test.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensipscli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: OpenSIPS Command Line Interface
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenSIPS/opensips-cli
|
|
6
6
|
Project-URL: Source, https://github.com/OpenSIPS/opensips-cli
|
|
@@ -14,10 +14,9 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Requires-Python: >=3.8
|
|
17
|
-
Requires-Dist: mysqlclient<1.4.0rc1
|
|
18
17
|
Requires-Dist: opensips
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist: sqlalchemy
|
|
18
|
+
Requires-Dist: pymysql
|
|
19
|
+
Requires-Dist: sqlalchemy>=1.3.16
|
|
21
20
|
Description-Content-Type: text/markdown
|
|
22
21
|
|
|
23
22
|
# OpenSIPS CLI (Command Line Interface)
|
|
@@ -92,7 +91,7 @@ The OpenSIPSCLI object can receive a set of arguments/modifiers through the
|
|
|
92
91
|
```
|
|
93
92
|
from opensipscli import args
|
|
94
93
|
...
|
|
95
|
-
args = OpenSIPSCLIArgs(debug=True)
|
|
94
|
+
args = args.OpenSIPSCLIArgs(debug=True)
|
|
96
95
|
opensipscli = cli.OpenSIPSCLI(args)
|
|
97
96
|
...
|
|
98
97
|
```
|
|
@@ -100,7 +99,7 @@ opensipscli = cli.OpenSIPSCLI(args)
|
|
|
100
99
|
Custom settings can be provided through the arguments, i.e.:
|
|
101
100
|
```
|
|
102
101
|
# run commands over http
|
|
103
|
-
args = OpenSIPSCLIArgs(communication_type = "http",
|
|
102
|
+
args = args.OpenSIPSCLIArgs(communication_type = "http",
|
|
104
103
|
url="http://127.0.0.1:8080/mi")
|
|
105
104
|
...
|
|
106
105
|
```
|
|
@@ -70,7 +70,7 @@ The OpenSIPSCLI object can receive a set of arguments/modifiers through the
|
|
|
70
70
|
```
|
|
71
71
|
from opensipscli import args
|
|
72
72
|
...
|
|
73
|
-
args = OpenSIPSCLIArgs(debug=True)
|
|
73
|
+
args = args.OpenSIPSCLIArgs(debug=True)
|
|
74
74
|
opensipscli = cli.OpenSIPSCLI(args)
|
|
75
75
|
...
|
|
76
76
|
```
|
|
@@ -78,7 +78,7 @@ opensipscli = cli.OpenSIPSCLI(args)
|
|
|
78
78
|
Custom settings can be provided through the arguments, i.e.:
|
|
79
79
|
```
|
|
80
80
|
# run commands over http
|
|
81
|
-
args = OpenSIPSCLIArgs(communication_type = "http",
|
|
81
|
+
args = args.OpenSIPSCLIArgs(communication_type = "http",
|
|
82
82
|
url="http://127.0.0.1:8080/mi")
|
|
83
83
|
...
|
|
84
84
|
```
|
|
@@ -8,7 +8,7 @@ ENV DEBIAN_FRONTEND noninteractive
|
|
|
8
8
|
|
|
9
9
|
#install basic components
|
|
10
10
|
RUN apt-get -y update -qq && \
|
|
11
|
-
apt-get -y install git
|
|
11
|
+
apt-get -y install git
|
|
12
12
|
|
|
13
13
|
#add keyserver, repository
|
|
14
14
|
RUN git clone https://github.com/OpenSIPS/opensips-cli.git /usr/src/opensips-cli && \
|
|
@@ -16,7 +16,7 @@ RUN git clone https://github.com/OpenSIPS/opensips-cli.git /usr/src/opensips-cli
|
|
|
16
16
|
python3 -m pip install . && \
|
|
17
17
|
cd / && rm -rf /usr/src/opensips-cli
|
|
18
18
|
|
|
19
|
-
RUN apt-get purge -y git
|
|
19
|
+
RUN apt-get purge -y git && \
|
|
20
20
|
apt-get autoremove -y && \
|
|
21
21
|
apt-get clean
|
|
22
22
|
|
|
@@ -56,27 +56,26 @@ will vary on every supported operating system.
|
|
|
56
56
|
|
|
57
57
|
```
|
|
58
58
|
# required OS packages
|
|
59
|
-
sudo apt install python3 python3-pip python3-
|
|
60
|
-
python3-mysqldb python3-sqlalchemy python3-sqlalchemy-utils \
|
|
59
|
+
sudo apt install python3 python3-pip python3-pymysql python3-sqlalchemy \
|
|
61
60
|
python3-openssl
|
|
62
61
|
|
|
63
62
|
# alternatively, you can build the requirements from source
|
|
64
|
-
sudo pip3 install
|
|
63
|
+
sudo pip3 install PyMySQL sqlalchemy pyOpenSSL
|
|
65
64
|
```
|
|
66
65
|
|
|
67
66
|
#### Red Hat / CentOS
|
|
68
67
|
|
|
69
68
|
```
|
|
70
69
|
# required CentOS 7 packages
|
|
71
|
-
sudo yum install python36 python36-pip python36-
|
|
72
|
-
python36-
|
|
70
|
+
sudo yum install python36 python36-pip python36-PyMySQL \
|
|
71
|
+
python36-sqlalchemy python36-pyOpenSSL
|
|
73
72
|
|
|
74
73
|
# required CentOS 8 packages
|
|
75
|
-
sudo yum install python3 python3-pip python3-
|
|
76
|
-
python3-
|
|
74
|
+
sudo yum install python3 python3-pip python3-PyMySQL \
|
|
75
|
+
python3-sqlalchemy python3-pyOpenSSL
|
|
77
76
|
|
|
78
77
|
# alternatively, you can build the requirements from source
|
|
79
|
-
sudo pip3 install
|
|
78
|
+
sudo pip3 install PyMySQL sqlalchemy pyOpenSSL
|
|
80
79
|
```
|
|
81
80
|
|
|
82
81
|
### Download, Build & Install
|
|
@@ -132,8 +132,10 @@ opensips-cli -o database_schema_path=~/src/opensips-3.1/scripts \
|
|
|
132
132
|
|
|
133
133
|
## Dependencies
|
|
134
134
|
|
|
135
|
-
* [sqlalchemy
|
|
136
|
-
|
|
135
|
+
* [sqlalchemy](https://www.sqlalchemy.org/) - used to abstract the SQL
|
|
136
|
+
database regardless of the backend used
|
|
137
|
+
* [PyMySQL](https://github.com/PyMySQL/PyMySQL) - pure-Python MySQL/MariaDB
|
|
138
|
+
driver used by SQLAlchemy when connecting to MySQL backends
|
|
137
139
|
|
|
138
140
|
## Limitations
|
|
139
141
|
|
|
@@ -53,8 +53,8 @@ opensips-cli -x user delete username@domain.com
|
|
|
53
53
|
|
|
54
54
|
## Dependencies
|
|
55
55
|
|
|
56
|
-
* [sqlalchemy
|
|
57
|
-
|
|
56
|
+
* [sqlalchemy](https://www.sqlalchemy.org/) - used to abstract the database
|
|
57
|
+
manipulation, regardless of the backend used
|
|
58
58
|
|
|
59
59
|
## Limitations
|
|
60
60
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
##
|
|
3
3
|
## This file is part of OpenSIPS CLI
|
|
4
4
|
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
@@ -195,6 +195,7 @@ class OpenSIPSCLI(cmd.Cmd, object):
|
|
|
195
195
|
"""
|
|
196
196
|
preload a history file
|
|
197
197
|
"""
|
|
198
|
+
self.configure_completion_delims()
|
|
198
199
|
history_file = cfg.get('history_file')
|
|
199
200
|
logger.debug("using history file {}".format(history_file))
|
|
200
201
|
try:
|
|
@@ -210,6 +211,14 @@ class OpenSIPSCLI(cmd.Cmd, object):
|
|
|
210
211
|
if not self.registered_atexit:
|
|
211
212
|
atexit.register(self.history_write)
|
|
212
213
|
|
|
214
|
+
def configure_completion_delims(self):
|
|
215
|
+
"""
|
|
216
|
+
keep ':' inside tokens for command completion (e.g. "mi evi:")
|
|
217
|
+
"""
|
|
218
|
+
delims = readline.get_completer_delims()
|
|
219
|
+
if ':' in delims:
|
|
220
|
+
readline.set_completer_delims(delims.replace(':', ''))
|
|
221
|
+
|
|
213
222
|
def postcmd(self, stop, line):
|
|
214
223
|
"""
|
|
215
224
|
post command after switching instance
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
##
|
|
3
3
|
## This file is part of OpenSIPS CLI
|
|
4
4
|
## (see https://github.com/OpenSIPS/opensips-cli).
|
|
@@ -23,8 +23,12 @@ import re
|
|
|
23
23
|
|
|
24
24
|
try:
|
|
25
25
|
import sqlalchemy
|
|
26
|
-
from sqlalchemy
|
|
27
|
-
|
|
26
|
+
from sqlalchemy import Column, Date, Integer, String, Boolean, text
|
|
27
|
+
try:
|
|
28
|
+
from sqlalchemy.orm import declarative_base # SA 1.4+
|
|
29
|
+
except ImportError:
|
|
30
|
+
# keep this for Debian Bullseye, which still has SA 1.3 in stable
|
|
31
|
+
from sqlalchemy.ext.declarative import declarative_base # SA 1.3
|
|
28
32
|
from sqlalchemy.orm import sessionmaker, deferred
|
|
29
33
|
|
|
30
34
|
# for now, we use our own make_url(), since Alchemy API is highly unstable
|
|
@@ -32,13 +36,14 @@ try:
|
|
|
32
36
|
#from sqlalchemy.engine.url import make_url
|
|
33
37
|
|
|
34
38
|
sqlalchemy_available = True
|
|
35
|
-
logger.debug("SQLAlchemy version: ", sqlalchemy.__version__)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
logger.debug("SQLAlchemy version: %s", sqlalchemy.__version__)
|
|
40
|
+
# always use the vendored shim — the system sqlalchemy-utils package
|
|
41
|
+
# varies a lot across distros (0.36.x ships broken database_exists for
|
|
42
|
+
# PostgreSQL on Bullseye; 0.41.x is required for SA 2.0); the shim is
|
|
43
|
+
# tested against SA 1.3–2.0 and behaves consistently
|
|
44
|
+
from opensipscli.libs import sqlalchemy_utils
|
|
45
|
+
import pymysql
|
|
46
|
+
pymysql.install_as_MySQLdb()
|
|
42
47
|
except ImportError:
|
|
43
48
|
logger.info("sqlalchemy not available!")
|
|
44
49
|
sqlalchemy_available = False
|
|
@@ -205,14 +210,10 @@ class osdb(object):
|
|
|
205
210
|
|
|
206
211
|
# TODO: do this only for SQLAlchemy
|
|
207
212
|
try:
|
|
208
|
-
|
|
209
|
-
self.__engine = sqlalchemy.create_engine(db_url, isolation_level='AUTOCOMMIT')
|
|
210
|
-
else:
|
|
211
|
-
self.__engine = sqlalchemy.create_engine(db_url)
|
|
213
|
+
self.__engine = sqlalchemy.create_engine(db_url, isolation_level='AUTOCOMMIT')
|
|
212
214
|
|
|
213
215
|
logger.debug("connecting to %s", db_url)
|
|
214
|
-
self.__conn = self.__engine.connect()
|
|
215
|
-
execution_options(autocommit=True)
|
|
216
|
+
self.__conn = self.__engine.connect()
|
|
216
217
|
# connect the Session object to our engine
|
|
217
218
|
self.Session.configure(bind=self.__engine)
|
|
218
219
|
# instantiate the Session object
|
|
@@ -266,7 +267,7 @@ class osdb(object):
|
|
|
266
267
|
msg += " and password '********'"
|
|
267
268
|
msg += " on database '{}'".format(self.db_name)
|
|
268
269
|
try:
|
|
269
|
-
result = self.__conn.execute(sqlcmd)
|
|
270
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
270
271
|
if result:
|
|
271
272
|
logger.info( "{} was successful".format(msg))
|
|
272
273
|
except:
|
|
@@ -296,7 +297,7 @@ class osdb(object):
|
|
|
296
297
|
self.session = self.Session()
|
|
297
298
|
logger.debug("connected to database URL '%s'", self.db_url)
|
|
298
299
|
elif self.dialect != "sqlite":
|
|
299
|
-
self.__conn.execute("USE {}".format(self.db_name))
|
|
300
|
+
self.__conn.execute(text("USE {}".format(self.db_name)))
|
|
300
301
|
except Exception as e:
|
|
301
302
|
logger.error("failed to connect to %s", self.db_url)
|
|
302
303
|
logger.error(e)
|
|
@@ -319,15 +320,13 @@ class osdb(object):
|
|
|
319
320
|
|
|
320
321
|
# all good - it's time to create the database
|
|
321
322
|
if self.dialect == "postgresql":
|
|
322
|
-
self.__conn.connection.connection.set_isolation_level(0)
|
|
323
323
|
try:
|
|
324
|
-
self.__conn.execute("CREATE DATABASE {}".format(self.db_name))
|
|
325
|
-
self.__conn.connection.connection.set_isolation_level(1)
|
|
324
|
+
self.__conn.execute(text("CREATE DATABASE {}".format(self.db_name)))
|
|
326
325
|
except sqlalchemy.exc.OperationalError as se:
|
|
327
326
|
logger.error("cannot create database: {}!".format(se))
|
|
328
327
|
return False
|
|
329
328
|
elif self.dialect != "sqlite":
|
|
330
|
-
self.__conn.execute("CREATE DATABASE {}".format(self.db_name))
|
|
329
|
+
self.__conn.execute(text("CREATE DATABASE {}".format(self.db_name)))
|
|
331
330
|
|
|
332
331
|
logger.debug("success")
|
|
333
332
|
return True
|
|
@@ -344,11 +343,11 @@ class osdb(object):
|
|
|
344
343
|
logger.error("database URL does not include a password")
|
|
345
344
|
return False
|
|
346
345
|
|
|
347
|
-
if url.drivername.lower() == "mysql":
|
|
346
|
+
if url.drivername.lower().split('+')[0] == "mysql":
|
|
348
347
|
sqlcmd = "CREATE USER IF NOT EXISTS '{}' IDENTIFIED BY '{}'".format(
|
|
349
348
|
url.username, url.password)
|
|
350
349
|
try:
|
|
351
|
-
result = self.__conn.execute(sqlcmd)
|
|
350
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
352
351
|
if result:
|
|
353
352
|
logger.info("created user '%s'", url.username)
|
|
354
353
|
except:
|
|
@@ -368,7 +367,7 @@ class osdb(object):
|
|
|
368
367
|
sqlcmd = "SET PASSWORD FOR '{}' = PASSWORD('{}')".format(
|
|
369
368
|
url.username, url.password)
|
|
370
369
|
try:
|
|
371
|
-
result = self.__conn.execute(sqlcmd)
|
|
370
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
372
371
|
if result:
|
|
373
372
|
logger.info("set password '%s%s%s' for '%s' (MariaDB)",
|
|
374
373
|
url.password[0] if len(url.password) >= 1 else '',
|
|
@@ -381,7 +380,7 @@ class osdb(object):
|
|
|
381
380
|
# syntax error! OK, now try Oracle MySQL syntax
|
|
382
381
|
sqlcmd = "ALTER USER '{}' IDENTIFIED BY '{}'".format(
|
|
383
382
|
url.username, url.password)
|
|
384
|
-
result = self.__conn.execute(sqlcmd)
|
|
383
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
385
384
|
if result:
|
|
386
385
|
logger.info("set password '%s%s%s' for '%s' (MySQL)",
|
|
387
386
|
url.password[0] if len(url.password) >= 1 else '',
|
|
@@ -397,7 +396,7 @@ class osdb(object):
|
|
|
397
396
|
|
|
398
397
|
sqlcmd = "GRANT ALL ON {}.* TO '{}'".format(self.db_name, url.username)
|
|
399
398
|
try:
|
|
400
|
-
result = self.__conn.execute(sqlcmd)
|
|
399
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
401
400
|
if result:
|
|
402
401
|
logger.info("granted access to user '%s' on DB '%s'",
|
|
403
402
|
url.username, self.db_name)
|
|
@@ -408,13 +407,13 @@ class osdb(object):
|
|
|
408
407
|
|
|
409
408
|
sqlcmd = "FLUSH PRIVILEGES"
|
|
410
409
|
try:
|
|
411
|
-
result = self.__conn.execute(sqlcmd)
|
|
410
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
412
411
|
logger.info("flushed privileges")
|
|
413
412
|
except:
|
|
414
413
|
logger.exception("failed to flush privileges")
|
|
415
414
|
return False
|
|
416
415
|
|
|
417
|
-
elif url.drivername.lower() == "postgresql":
|
|
416
|
+
elif url.drivername.lower().split('+')[0] == "postgresql":
|
|
418
417
|
if not self.exists_role(url.username):
|
|
419
418
|
logger.info("creating role %s", url.username)
|
|
420
419
|
if not self.create_role(url.username, url.password):
|
|
@@ -427,7 +426,7 @@ class osdb(object):
|
|
|
427
426
|
logger.info(sqlcmd)
|
|
428
427
|
|
|
429
428
|
try:
|
|
430
|
-
result = self.__conn.execute(sqlcmd)
|
|
429
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
431
430
|
if result:
|
|
432
431
|
logger.debug("... OK")
|
|
433
432
|
except:
|
|
@@ -459,7 +458,7 @@ class osdb(object):
|
|
|
459
458
|
logger.info(sqlcmd)
|
|
460
459
|
|
|
461
460
|
try:
|
|
462
|
-
result = self.__conn.execute(sqlcmd)
|
|
461
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
463
462
|
if result:
|
|
464
463
|
logger.info("role '{}' with options '{}' created".
|
|
465
464
|
format(role_name, role_options))
|
|
@@ -481,7 +480,7 @@ class osdb(object):
|
|
|
481
480
|
where_str = self.get_where(filter_keys)
|
|
482
481
|
statement = "DELETE FROM {}{}".format(table, where_str)
|
|
483
482
|
try:
|
|
484
|
-
self.__conn.execute(statement)
|
|
483
|
+
self.__conn.execute(text(statement))
|
|
485
484
|
except sqlalchemy.exc.SQLAlchemyError as ex:
|
|
486
485
|
logger.error("cannot execute query: {}".format(statement))
|
|
487
486
|
logger.error(ex)
|
|
@@ -535,7 +534,7 @@ class osdb(object):
|
|
|
535
534
|
|
|
536
535
|
sqlcmd = "DROP ROLE IF EXISTS {}".format(role_name)
|
|
537
536
|
try:
|
|
538
|
-
result = self.__conn.execute(sqlcmd)
|
|
537
|
+
result = self.__conn.execute(text(sqlcmd))
|
|
539
538
|
if result:
|
|
540
539
|
logger.debug("Role '%s' dropped", role_name)
|
|
541
540
|
except:
|
|
@@ -572,7 +571,7 @@ class osdb(object):
|
|
|
572
571
|
# DROP/CREATE PROCEDURE statements seem to only work separately
|
|
573
572
|
sql = re.sub(r'DROP PROCEDURE .*\n', '', sql)
|
|
574
573
|
|
|
575
|
-
self.__conn.execute(sql)
|
|
574
|
+
self.__conn.execute(text(sql))
|
|
576
575
|
except sqlalchemy.exc.IntegrityError as ie:
|
|
577
576
|
raise osdbError("cannot deploy {} file: {}".
|
|
578
577
|
format(sql_file, ie)) from None
|
|
@@ -582,7 +581,7 @@ class osdb(object):
|
|
|
582
581
|
if not sql:
|
|
583
582
|
continue
|
|
584
583
|
try:
|
|
585
|
-
self.__conn.execute(sql)
|
|
584
|
+
self.__conn.execute(text(sql))
|
|
586
585
|
except sqlalchemy.exc.IntegrityError as ie:
|
|
587
586
|
raise osdbModuleAlreadyExistsError(
|
|
588
587
|
"cannot deploy {} file: {}".format(sql_file, ie)) from None
|
|
@@ -665,7 +664,7 @@ class osdb(object):
|
|
|
665
664
|
table,
|
|
666
665
|
where_str)
|
|
667
666
|
try:
|
|
668
|
-
result = self.__conn.execute(statement)
|
|
667
|
+
result = self.__conn.execute(text(statement))
|
|
669
668
|
except sqlalchemy.exc.SQLAlchemyError as ex:
|
|
670
669
|
logger.error("cannot execute query: {}".format(statement))
|
|
671
670
|
logger.error(ex)
|
|
@@ -676,7 +675,7 @@ class osdb(object):
|
|
|
676
675
|
"""
|
|
677
676
|
extract database dialect from an url
|
|
678
677
|
"""
|
|
679
|
-
return url.split('://')[0]
|
|
678
|
+
return url.split('://')[0].split('+')[0]
|
|
680
679
|
|
|
681
680
|
def get_where(self, filter_keys):
|
|
682
681
|
"""
|
|
@@ -738,7 +737,7 @@ class osdb(object):
|
|
|
738
737
|
logger.info(sqlcmd)
|
|
739
738
|
|
|
740
739
|
try:
|
|
741
|
-
self.__conn.execute(sqlcmd)
|
|
740
|
+
self.__conn.execute(text(sqlcmd))
|
|
742
741
|
except Exception as e:
|
|
743
742
|
logger.exception(e)
|
|
744
743
|
logger.error("failed to grant '%s' '%s' to '%s'", privs, on_statement, role_name)
|
|
@@ -767,6 +766,11 @@ class osdb(object):
|
|
|
767
766
|
sqlalchemy.create_engine('{}://'.format(dialect))
|
|
768
767
|
except sqlalchemy.exc.NoSuchModuleError:
|
|
769
768
|
return False
|
|
769
|
+
except Exception:
|
|
770
|
+
# dialect is known to SQLAlchemy but its default DBAPI is not
|
|
771
|
+
# installed (e.g. MySQLdb when only PyMySQL is available);
|
|
772
|
+
# a compatible driver is still usable via the +driver URL syntax
|
|
773
|
+
pass
|
|
770
774
|
return True
|
|
771
775
|
|
|
772
776
|
def insert(self, table, keys):
|
|
@@ -781,14 +785,14 @@ class osdb(object):
|
|
|
781
785
|
for v in keys.values():
|
|
782
786
|
values += ", "
|
|
783
787
|
if type(v) == int:
|
|
784
|
-
values += v
|
|
788
|
+
values += str(v)
|
|
785
789
|
else:
|
|
786
790
|
values += "'{}'".format(
|
|
787
791
|
v.translate(str.maketrans({'\'': '\\\''})))
|
|
788
792
|
statement = "INSERT INTO {} ({}) VALUES ({})".format(
|
|
789
793
|
table, ", ".join(keys.keys()), values[2:])
|
|
790
794
|
try:
|
|
791
|
-
result = self.__conn.execute(statement)
|
|
795
|
+
result = self.__conn.execute(text(statement))
|
|
792
796
|
except sqlalchemy.exc.SQLAlchemyError as ex:
|
|
793
797
|
logger.error("cannot execute query: {}".format(statement))
|
|
794
798
|
logger.error(ex)
|
|
@@ -873,7 +877,7 @@ class osdb(object):
|
|
|
873
877
|
for k, v in update_keys.items():
|
|
874
878
|
update_str += ", {} = ".format(k)
|
|
875
879
|
if type(v) == int:
|
|
876
|
-
update_str += v
|
|
880
|
+
update_str += str(v)
|
|
877
881
|
else:
|
|
878
882
|
update_str += "'{}'".format(
|
|
879
883
|
v.translate(str.maketrans({'\'': '\\\''})))
|
|
@@ -881,7 +885,7 @@ class osdb(object):
|
|
|
881
885
|
statement = "UPDATE {} SET {}{}".format(table,
|
|
882
886
|
update_str[2:], where_str)
|
|
883
887
|
try:
|
|
884
|
-
result = self.__conn.execute(statement)
|
|
888
|
+
result = self.__conn.execute(text(statement))
|
|
885
889
|
except sqlalchemy.exc.SQLAlchemyError as ex:
|
|
886
890
|
logger.error("cannot execute query: {}".format(statement))
|
|
887
891
|
logger.error(ex)
|
|
@@ -961,8 +965,8 @@ class osdb(object):
|
|
|
961
965
|
|
|
962
966
|
@staticmethod
|
|
963
967
|
def get_url_driver(url, capitalize=False):
|
|
968
|
+
driver = make_url(url).drivername.lower().split('+')[0]
|
|
964
969
|
if capitalize:
|
|
965
|
-
driver = make_url(url).drivername.lower()
|
|
966
970
|
capitalized = {
|
|
967
971
|
'mysql': 'MySQL',
|
|
968
972
|
'postgresql': 'PostgreSQL',
|
|
@@ -971,7 +975,7 @@ class osdb(object):
|
|
|
971
975
|
}
|
|
972
976
|
return capitalized.get(driver, driver.capitalize())
|
|
973
977
|
else:
|
|
974
|
-
return
|
|
978
|
+
return driver
|
|
975
979
|
|
|
976
980
|
|
|
977
981
|
@staticmethod
|