saltext.mysql 1.0.0__py2.py3-none-any.whl
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.
- saltext/mysql/__init__.py +28 -0
- saltext/mysql/auth/__init__.py +0 -0
- saltext/mysql/auth/mysql.py +124 -0
- saltext/mysql/cache/__init__.py +0 -0
- saltext/mysql/cache/mysql_cache.py +357 -0
- saltext/mysql/modules/__init__.py +0 -0
- saltext/mysql/modules/mysql.py +3073 -0
- saltext/mysql/pillar/__init__.py +0 -0
- saltext/mysql/pillar/mysql.py +146 -0
- saltext/mysql/returners/__init__.py +0 -0
- saltext/mysql/returners/mysql.py +617 -0
- saltext/mysql/states/__init__.py +0 -0
- saltext/mysql/states/mysql_database.py +187 -0
- saltext/mysql/states/mysql_grants.py +276 -0
- saltext/mysql/states/mysql_query.py +385 -0
- saltext/mysql/states/mysql_user.py +291 -0
- saltext/mysql/version.py +1 -0
- saltext.mysql-1.0.0.dist-info/LICENSE +201 -0
- saltext.mysql-1.0.0.dist-info/METADATA +151 -0
- saltext.mysql-1.0.0.dist-info/NOTICE +10 -0
- saltext.mysql-1.0.0.dist-info/RECORD +24 -0
- saltext.mysql-1.0.0.dist-info/WHEEL +6 -0
- saltext.mysql-1.0.0.dist-info/entry_points.txt +2 -0
- saltext.mysql-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# pylint: disable=missing-module-docstring
|
|
2
|
+
import pathlib
|
|
3
|
+
|
|
4
|
+
PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent
|
|
5
|
+
try:
|
|
6
|
+
from .version import __version__
|
|
7
|
+
except ImportError: # pragma: no cover
|
|
8
|
+
__version__ = "0.0.0.not-installed"
|
|
9
|
+
try:
|
|
10
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
__version__ = version(__name__)
|
|
14
|
+
except PackageNotFoundError:
|
|
15
|
+
# package is not installed
|
|
16
|
+
pass
|
|
17
|
+
except ImportError:
|
|
18
|
+
try:
|
|
19
|
+
from pkg_resources import get_distribution, DistributionNotFound
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
__version__ = get_distribution(__name__).version
|
|
23
|
+
except DistributionNotFound:
|
|
24
|
+
# package is not installed
|
|
25
|
+
pass
|
|
26
|
+
except ImportError:
|
|
27
|
+
# pkg resources isn't even available?!
|
|
28
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provide authentication using MySQL.
|
|
3
|
+
|
|
4
|
+
When using MySQL as an authentication backend, you will need to create or
|
|
5
|
+
use an existing table that has a username and a password column.
|
|
6
|
+
|
|
7
|
+
To get started, create a simple table that holds just a username and
|
|
8
|
+
a password. The password field will hold a SHA256 checksum.
|
|
9
|
+
|
|
10
|
+
.. code-block:: sql
|
|
11
|
+
|
|
12
|
+
CREATE TABLE `users` (
|
|
13
|
+
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
14
|
+
`username` varchar(25) DEFAULT NULL,
|
|
15
|
+
`password` varchar(70) DEFAULT NULL,
|
|
16
|
+
PRIMARY KEY (`id`)
|
|
17
|
+
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
|
|
18
|
+
|
|
19
|
+
To create a user within MySQL, execute the following statement.
|
|
20
|
+
|
|
21
|
+
.. code-block:: sql
|
|
22
|
+
|
|
23
|
+
INSERT INTO users VALUES (NULL, 'diana', SHA2('secret', 256))
|
|
24
|
+
|
|
25
|
+
.. code-block:: yaml
|
|
26
|
+
|
|
27
|
+
mysql_auth:
|
|
28
|
+
hostname: localhost
|
|
29
|
+
database: SaltStack
|
|
30
|
+
username: root
|
|
31
|
+
password: letmein
|
|
32
|
+
auth_sql: 'SELECT username FROM users WHERE username = "{0}" AND password = SHA2("{1}", 256)'
|
|
33
|
+
|
|
34
|
+
The `auth_sql` contains the SQL that will validate a user to ensure they are
|
|
35
|
+
correctly authenticated. This is where you can specify other SQL queries to
|
|
36
|
+
authenticate users.
|
|
37
|
+
|
|
38
|
+
Enable MySQL authentication.
|
|
39
|
+
|
|
40
|
+
.. code-block:: yaml
|
|
41
|
+
|
|
42
|
+
external_auth:
|
|
43
|
+
mysql:
|
|
44
|
+
damian:
|
|
45
|
+
- test.*
|
|
46
|
+
|
|
47
|
+
:depends: - MySQL-python Python module
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import logging
|
|
51
|
+
|
|
52
|
+
log = logging.getLogger(__name__)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
# Trying to import MySQLdb
|
|
56
|
+
import MySQLdb
|
|
57
|
+
import MySQLdb.converters
|
|
58
|
+
import MySQLdb.cursors
|
|
59
|
+
from MySQLdb.connections import OperationalError
|
|
60
|
+
except ImportError:
|
|
61
|
+
try:
|
|
62
|
+
# MySQLdb import failed, try to import PyMySQL
|
|
63
|
+
import pymysql
|
|
64
|
+
|
|
65
|
+
pymysql.install_as_MySQLdb()
|
|
66
|
+
import MySQLdb
|
|
67
|
+
import MySQLdb.converters
|
|
68
|
+
import MySQLdb.cursors
|
|
69
|
+
from MySQLdb.err import OperationalError
|
|
70
|
+
except ImportError:
|
|
71
|
+
MySQLdb = None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def __virtual__():
|
|
75
|
+
"""
|
|
76
|
+
Confirm that a python mysql client is installed.
|
|
77
|
+
"""
|
|
78
|
+
return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else ""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def __get_connection_info():
|
|
82
|
+
"""
|
|
83
|
+
Grab MySQL Connection Details
|
|
84
|
+
"""
|
|
85
|
+
conn_info = {}
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
conn_info["hostname"] = __opts__["mysql_auth"]["hostname"]
|
|
89
|
+
conn_info["username"] = __opts__["mysql_auth"]["username"]
|
|
90
|
+
conn_info["password"] = __opts__["mysql_auth"]["password"]
|
|
91
|
+
conn_info["database"] = __opts__["mysql_auth"]["database"]
|
|
92
|
+
|
|
93
|
+
conn_info["auth_sql"] = __opts__["mysql_auth"]["auth_sql"]
|
|
94
|
+
except KeyError as e:
|
|
95
|
+
log.error("%s does not exist", e)
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
return conn_info
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def auth(username, password):
|
|
102
|
+
"""
|
|
103
|
+
Authenticate using a MySQL user table
|
|
104
|
+
"""
|
|
105
|
+
_info = __get_connection_info()
|
|
106
|
+
|
|
107
|
+
if _info is None:
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
conn = MySQLdb.connect(
|
|
112
|
+
_info["hostname"], _info["username"], _info["password"], _info["database"]
|
|
113
|
+
)
|
|
114
|
+
except OperationalError as e:
|
|
115
|
+
log.error(e)
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
cur = conn.cursor()
|
|
119
|
+
cur.execute(_info["auth_sql"].format(username, password))
|
|
120
|
+
|
|
121
|
+
if cur.rowcount == 1:
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Minion data cache plugin for MySQL database.
|
|
3
|
+
|
|
4
|
+
.. versionadded:: 2018.3.0
|
|
5
|
+
|
|
6
|
+
It is up to the system administrator to set up and configure the MySQL
|
|
7
|
+
infrastructure. All is needed for this plugin is a working MySQL server.
|
|
8
|
+
|
|
9
|
+
.. warning::
|
|
10
|
+
|
|
11
|
+
The mysql.database and mysql.table_name will be directly added into certain
|
|
12
|
+
queries. Salt treats these as trusted input.
|
|
13
|
+
|
|
14
|
+
The module requires the database (default ``salt_cache``) to exist but creates
|
|
15
|
+
its own table if needed. The keys are indexed using the ``bank`` and
|
|
16
|
+
``etcd_key`` columns.
|
|
17
|
+
|
|
18
|
+
To enable this cache plugin, the master will need the python client for
|
|
19
|
+
MySQL installed. This can be easily installed with pip:
|
|
20
|
+
|
|
21
|
+
.. code-block:: bash
|
|
22
|
+
|
|
23
|
+
pip install pymysql
|
|
24
|
+
|
|
25
|
+
Optionally, depending on the MySQL agent configuration, the following values
|
|
26
|
+
could be set in the master config. These are the defaults:
|
|
27
|
+
|
|
28
|
+
.. code-block:: yaml
|
|
29
|
+
|
|
30
|
+
mysql.host: 127.0.0.1
|
|
31
|
+
mysql.port: 2379
|
|
32
|
+
mysql.user: None
|
|
33
|
+
mysql.password: None
|
|
34
|
+
mysql.database: salt_cache
|
|
35
|
+
mysql.table_name: cache
|
|
36
|
+
# This may be enabled to create a fresh connection on every call
|
|
37
|
+
mysql.fresh_connection: false
|
|
38
|
+
|
|
39
|
+
To use the mysql as a minion data cache backend, set the master ``cache`` config
|
|
40
|
+
value to ``mysql``:
|
|
41
|
+
|
|
42
|
+
.. code-block:: yaml
|
|
43
|
+
|
|
44
|
+
cache: mysql
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
.. _`MySQL documentation`: https://github.com/coreos/mysql
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import copy
|
|
51
|
+
import logging
|
|
52
|
+
import time
|
|
53
|
+
|
|
54
|
+
import salt.payload
|
|
55
|
+
import salt.utils.stringutils
|
|
56
|
+
from salt.exceptions import SaltCacheError
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Trying to import MySQLdb
|
|
60
|
+
import MySQLdb
|
|
61
|
+
import MySQLdb.converters
|
|
62
|
+
import MySQLdb.cursors
|
|
63
|
+
from MySQLdb.connections import OperationalError
|
|
64
|
+
|
|
65
|
+
# Define the interface error as a subclass of exception
|
|
66
|
+
# It will never be thrown/used, it is defined to support the pymysql error below
|
|
67
|
+
class InterfaceError(Exception):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
except ImportError:
|
|
71
|
+
try:
|
|
72
|
+
# MySQLdb import failed, try to import PyMySQL
|
|
73
|
+
import pymysql
|
|
74
|
+
from pymysql.err import InterfaceError
|
|
75
|
+
|
|
76
|
+
pymysql.install_as_MySQLdb()
|
|
77
|
+
import MySQLdb
|
|
78
|
+
import MySQLdb.converters
|
|
79
|
+
import MySQLdb.cursors
|
|
80
|
+
from MySQLdb.err import OperationalError
|
|
81
|
+
except ImportError:
|
|
82
|
+
MySQLdb = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_DEFAULT_DATABASE_NAME = "salt_cache"
|
|
86
|
+
_DEFAULT_CACHE_TABLE_NAME = "cache"
|
|
87
|
+
_RECONNECT_INTERVAL_SEC = 0.050
|
|
88
|
+
|
|
89
|
+
log = logging.getLogger(__name__)
|
|
90
|
+
|
|
91
|
+
# Module properties
|
|
92
|
+
|
|
93
|
+
__virtualname__ = "mysql"
|
|
94
|
+
__func_alias__ = {"ls": "list"}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def __virtual__():
|
|
98
|
+
"""
|
|
99
|
+
Confirm that a python mysql client is installed.
|
|
100
|
+
"""
|
|
101
|
+
return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else ""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def force_reconnect():
|
|
105
|
+
"""
|
|
106
|
+
Force a reconnection to the MySQL database, by removing the client from
|
|
107
|
+
Salt's __context__.
|
|
108
|
+
"""
|
|
109
|
+
__context__.pop("mysql_client", None)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def run_query(conn, query, args=None, retries=3):
|
|
113
|
+
"""
|
|
114
|
+
Get a cursor and run a query. Reconnect up to ``retries`` times if
|
|
115
|
+
needed.
|
|
116
|
+
Returns: cursor, affected rows counter
|
|
117
|
+
Raises: SaltCacheError, AttributeError, OperationalError, InterfaceError
|
|
118
|
+
"""
|
|
119
|
+
if __context__.get("mysql_fresh_connection"):
|
|
120
|
+
# Create a new connection if configured
|
|
121
|
+
conn = MySQLdb.connect(**__context__["mysql_kwargs"])
|
|
122
|
+
__context__["mysql_client"] = conn
|
|
123
|
+
if conn is None:
|
|
124
|
+
conn = __context__.get("mysql_client")
|
|
125
|
+
try:
|
|
126
|
+
cur = conn.cursor()
|
|
127
|
+
|
|
128
|
+
if not args:
|
|
129
|
+
log.debug("Doing query: %s", query)
|
|
130
|
+
out = cur.execute(query)
|
|
131
|
+
else:
|
|
132
|
+
log.debug("Doing query: %s args: %s ", query, repr(args))
|
|
133
|
+
out = cur.execute(query, args)
|
|
134
|
+
|
|
135
|
+
return cur, out
|
|
136
|
+
except (AttributeError, OperationalError, InterfaceError) as e:
|
|
137
|
+
if retries == 0:
|
|
138
|
+
raise
|
|
139
|
+
# reconnect creating new client
|
|
140
|
+
time.sleep(_RECONNECT_INTERVAL_SEC)
|
|
141
|
+
if conn is None:
|
|
142
|
+
log.debug("mysql_cache: creating db connection")
|
|
143
|
+
else:
|
|
144
|
+
log.info("mysql_cache: recreating db connection due to: %r", e)
|
|
145
|
+
__context__["mysql_client"] = MySQLdb.connect(**__context__["mysql_kwargs"])
|
|
146
|
+
return run_query(
|
|
147
|
+
conn=__context__.get("mysql_client"),
|
|
148
|
+
query=query,
|
|
149
|
+
args=args,
|
|
150
|
+
retries=(retries - 1),
|
|
151
|
+
)
|
|
152
|
+
except Exception as e: # pylint: disable=broad-except
|
|
153
|
+
if len(query) > 150:
|
|
154
|
+
query = query[:150] + "<...>"
|
|
155
|
+
raise SaltCacheError(
|
|
156
|
+
"Error running {}{}: {}".format(query, f"- args: {args}" if args else "", e)
|
|
157
|
+
) from e
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _create_table():
|
|
161
|
+
"""
|
|
162
|
+
Create table if needed
|
|
163
|
+
"""
|
|
164
|
+
# Explicitly check if the table already exists as the library logs a
|
|
165
|
+
# warning on CREATE TABLE
|
|
166
|
+
query = """SELECT COUNT(TABLE_NAME) FROM information_schema.tables
|
|
167
|
+
WHERE table_schema = %s AND table_name = %s"""
|
|
168
|
+
cur, _ = run_query(
|
|
169
|
+
__context__.get("mysql_client"),
|
|
170
|
+
query,
|
|
171
|
+
args=(__context__["mysql_kwargs"]["db"], __context__["mysql_table_name"]),
|
|
172
|
+
)
|
|
173
|
+
r = cur.fetchone()
|
|
174
|
+
cur.close()
|
|
175
|
+
if r[0] == 1:
|
|
176
|
+
query = """
|
|
177
|
+
SELECT COUNT(TABLE_NAME)
|
|
178
|
+
FROM
|
|
179
|
+
information_schema.columns
|
|
180
|
+
WHERE
|
|
181
|
+
table_schema = %s
|
|
182
|
+
AND table_name = %s
|
|
183
|
+
AND column_name = 'last_update'
|
|
184
|
+
"""
|
|
185
|
+
cur, _ = run_query(
|
|
186
|
+
__context__["mysql_client"],
|
|
187
|
+
query,
|
|
188
|
+
args=(__context__["mysql_kwargs"]["db"], __context__["mysql_table_name"]),
|
|
189
|
+
)
|
|
190
|
+
r = cur.fetchone()
|
|
191
|
+
cur.close()
|
|
192
|
+
if r[0] == 1:
|
|
193
|
+
return
|
|
194
|
+
else:
|
|
195
|
+
query = """
|
|
196
|
+
ALTER TABLE {}.{}
|
|
197
|
+
ADD COLUMN last_update TIMESTAMP NOT NULL
|
|
198
|
+
DEFAULT CURRENT_TIMESTAMP
|
|
199
|
+
ON UPDATE CURRENT_TIMESTAMP
|
|
200
|
+
""".format(
|
|
201
|
+
__context__["mysql_kwargs"]["db"], __context__["mysql_table_name"]
|
|
202
|
+
)
|
|
203
|
+
cur, _ = run_query(__context__["mysql_client"], query)
|
|
204
|
+
cur.close()
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
query = """CREATE TABLE IF NOT EXISTS {} (
|
|
208
|
+
bank CHAR(255),
|
|
209
|
+
etcd_key CHAR(255),
|
|
210
|
+
data MEDIUMBLOB,
|
|
211
|
+
last_update TIMESTAMP NOT NULL
|
|
212
|
+
DEFAULT CURRENT_TIMESTAMP
|
|
213
|
+
ON UPDATE CURRENT_TIMESTAMP,
|
|
214
|
+
PRIMARY KEY(bank, etcd_key)
|
|
215
|
+
);""".format(
|
|
216
|
+
__context__["mysql_table_name"]
|
|
217
|
+
)
|
|
218
|
+
log.info("mysql_cache: creating table %s", __context__["mysql_table_name"])
|
|
219
|
+
cur, _ = run_query(__context__.get("mysql_client"), query)
|
|
220
|
+
cur.close()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _init_client():
|
|
224
|
+
"""Initialize connection and create table if needed"""
|
|
225
|
+
if __context__.get("mysql_client") is not None:
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
opts = copy.deepcopy(__opts__)
|
|
229
|
+
mysql_kwargs = {
|
|
230
|
+
"autocommit": True,
|
|
231
|
+
"host": opts.pop("mysql.host", "127.0.0.1"),
|
|
232
|
+
"user": opts.pop("mysql.user", None),
|
|
233
|
+
"passwd": opts.pop("mysql.password", None),
|
|
234
|
+
"db": opts.pop("mysql.database", _DEFAULT_DATABASE_NAME),
|
|
235
|
+
"port": opts.pop("mysql.port", 3306),
|
|
236
|
+
"unix_socket": opts.pop("mysql.unix_socket", None),
|
|
237
|
+
"connect_timeout": opts.pop("mysql.connect_timeout", None),
|
|
238
|
+
}
|
|
239
|
+
mysql_kwargs["autocommit"] = True
|
|
240
|
+
|
|
241
|
+
__context__["mysql_table_name"] = opts.pop("mysql.table_name", "salt")
|
|
242
|
+
__context__["mysql_fresh_connection"] = opts.pop("mysql.fresh_connection", False)
|
|
243
|
+
|
|
244
|
+
# Gather up any additional MySQL configuration options
|
|
245
|
+
for k in opts:
|
|
246
|
+
if k.startswith("mysql."):
|
|
247
|
+
_key = k.split(".")[1]
|
|
248
|
+
mysql_kwargs[_key] = opts.get(k)
|
|
249
|
+
|
|
250
|
+
# TODO: handle SSL connection parameters
|
|
251
|
+
|
|
252
|
+
for k, v in copy.deepcopy(mysql_kwargs).items():
|
|
253
|
+
if v is None:
|
|
254
|
+
mysql_kwargs.pop(k)
|
|
255
|
+
kwargs_copy = mysql_kwargs.copy()
|
|
256
|
+
kwargs_copy["passwd"] = "<hidden>"
|
|
257
|
+
log.info("mysql_cache: Setting up client with params: %r", kwargs_copy)
|
|
258
|
+
__context__["mysql_kwargs"] = mysql_kwargs
|
|
259
|
+
# The MySQL client is created later on by run_query
|
|
260
|
+
_create_table()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def store(bank, key, data):
|
|
264
|
+
"""
|
|
265
|
+
Store a key value.
|
|
266
|
+
"""
|
|
267
|
+
_init_client()
|
|
268
|
+
data = salt.payload.dumps(data)
|
|
269
|
+
query = "REPLACE INTO {} (bank, etcd_key, data) values(%s,%s,%s)".format(
|
|
270
|
+
__context__["mysql_table_name"]
|
|
271
|
+
)
|
|
272
|
+
args = (bank, key, data)
|
|
273
|
+
|
|
274
|
+
cur, cnt = run_query(__context__.get("mysql_client"), query, args=args)
|
|
275
|
+
cur.close()
|
|
276
|
+
if cnt not in (1, 2):
|
|
277
|
+
raise SaltCacheError(f"Error storing {bank} {key} returned {cnt}")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def fetch(bank, key):
|
|
281
|
+
"""
|
|
282
|
+
Fetch a key value.
|
|
283
|
+
"""
|
|
284
|
+
_init_client()
|
|
285
|
+
query = "SELECT data FROM {} WHERE bank=%s AND etcd_key=%s".format(
|
|
286
|
+
__context__["mysql_table_name"]
|
|
287
|
+
)
|
|
288
|
+
cur, _ = run_query(__context__.get("mysql_client"), query, args=(bank, key))
|
|
289
|
+
r = cur.fetchone()
|
|
290
|
+
cur.close()
|
|
291
|
+
if r is None:
|
|
292
|
+
return {}
|
|
293
|
+
return salt.payload.loads(r[0])
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def flush(bank, key=None):
|
|
297
|
+
"""
|
|
298
|
+
Remove the key from the cache bank with all the key content.
|
|
299
|
+
"""
|
|
300
|
+
_init_client()
|
|
301
|
+
query = "DELETE FROM {} WHERE bank=%s".format(__context__["mysql_table_name"])
|
|
302
|
+
if key is None:
|
|
303
|
+
data = (bank,)
|
|
304
|
+
else:
|
|
305
|
+
data = (bank, key)
|
|
306
|
+
query += " AND etcd_key=%s"
|
|
307
|
+
|
|
308
|
+
cur, _ = run_query(__context__.get("mysql_client"), query, args=data)
|
|
309
|
+
cur.close()
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def ls(bank):
|
|
313
|
+
"""
|
|
314
|
+
Return an iterable object containing all entries stored in the specified
|
|
315
|
+
bank.
|
|
316
|
+
"""
|
|
317
|
+
_init_client()
|
|
318
|
+
query = "SELECT etcd_key FROM {} WHERE bank=%s".format(__context__["mysql_table_name"])
|
|
319
|
+
cur, _ = run_query(__context__.get("mysql_client"), query, args=(bank,))
|
|
320
|
+
out = [row[0] for row in cur.fetchall()]
|
|
321
|
+
cur.close()
|
|
322
|
+
return out
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def contains(bank, key):
|
|
326
|
+
"""
|
|
327
|
+
Checks if the specified bank contains the specified key.
|
|
328
|
+
"""
|
|
329
|
+
_init_client()
|
|
330
|
+
if key is None:
|
|
331
|
+
data = (bank,)
|
|
332
|
+
query = "SELECT COUNT(data) FROM {} WHERE bank=%s".format(__context__["mysql_table_name"])
|
|
333
|
+
else:
|
|
334
|
+
data = (bank, key)
|
|
335
|
+
query = "SELECT COUNT(data) FROM {} WHERE bank=%s AND etcd_key=%s".format(
|
|
336
|
+
__context__["mysql_table_name"]
|
|
337
|
+
)
|
|
338
|
+
cur, _ = run_query(__context__.get("mysql_client"), query, args=data)
|
|
339
|
+
r = cur.fetchone()
|
|
340
|
+
cur.close()
|
|
341
|
+
return r[0] == 1
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def updated(bank, key):
|
|
345
|
+
"""
|
|
346
|
+
Return the integer Unix epoch update timestamp of the specified bank and
|
|
347
|
+
key.
|
|
348
|
+
"""
|
|
349
|
+
_init_client()
|
|
350
|
+
query = "SELECT UNIX_TIMESTAMP(last_update) FROM {} WHERE bank=%s " "AND etcd_key=%s".format(
|
|
351
|
+
__context__["mysql_table_name"]
|
|
352
|
+
)
|
|
353
|
+
data = (bank, key)
|
|
354
|
+
cur, _ = run_query(__context__.get("mysql_client"), query=query, args=data)
|
|
355
|
+
r = cur.fetchone()
|
|
356
|
+
cur.close()
|
|
357
|
+
return int(r[0]) if r else r
|
|
File without changes
|