blob: 2023ab0a024ae1db4357ff9664ac46615a37758e [file] [log] [blame]
Ivan Udovichenkoe25bced2018-08-07 12:52:46 +03001# -*- coding: utf-8 -*-
2'''
3PowerDNS MySQL interections
4==========================
5
6:depends: - MySQLdb Python module
7:configuration: See :py:mod:`salt.modules.mysql` for setup instructions.
8
9The mysql_query module is used to execute queries on MySQL databases.
10Its output may be stored in a file or in a grain.
11
12Sometimes you would need to store information about PowerDNS supermasters
13in tables to work with.
14Here is an example of making sure rows with specific data exist:
15
16 .. code-block:: yaml
17
18 use_supermaster_127.0.0.1:
19 powerdns_mysql.row_present:
20 - table: supermasters
21 - where_sql: ip="127.0.0.1"
22 - database: powerdns
23 - data:
24 ip: 127.0.0.1
25 nameserver: ns.localhost.local
26 account: master
27
28Comprehensive example could be found in file:
29powerdns/server/backends/mysql.sls
30'''
31
32import sys
33import salt.utils
34import salt.ext.six as six
35
36
37def __virtual__():
38 '''
39 Only load if the mysql module is available in __salt__
40 '''
41 return 'mysql.query' in __salt__
42
43
44def _get_mysql_error():
45 '''
46 Look in module context for a MySQL error. Eventually we should make a less
47 ugly way of doing this.
48 '''
49 return sys.modules[
50 __salt__['test.ping'].__module__
51 ].__context__.pop('mysql.error', None)
52
53
54def row_present(name,
55 database,
56 table,
57 data,
58 where_sql,
59 update=False,
60 **connection_args):
61 '''
62 Checks to make sure the given row exists. If row exists and update is True
63 then row will be updated with data. Otherwise it will leave existing
64 row unmodified and check it against data. If the existing data
65 doesn't match data check the state will fail. If the row doesn't
66 exist then it will insert data into the table. If more than one
67 row matches, then the state will fail.
68
69 name
70 Used only as an ID
71
72 database
73 The name of the database to execute the query on
74
75 table
76 The table name to check the data
77
78 data
79 The dictionary of key/value pairs to check against if
80 row exists, insert into the table if it doesn't
81
82 where_sql
83 The SQL to select the row to check
84
85 update
86 True will replace the existing row with data
87 When False and the row exists and data does not equal
88 the row data then the state will fail
89
90 connection_args
91 MySQL connection arguments to connect to MySQL server with
92 '''
93
94 ret = {'name': name,
95 'changes': {},
96 'result': True,
97 'comment': 'Database {0} is already present'.format(database)}
98
99 # check if database exists
100 if not __salt__['mysql.db_exists'](database, **connection_args):
101 err = _get_mysql_error()
102 if err is not None:
103 ret['comment'] = err
104 ret['result'] = False
105 return ret
106
107 ret['result'] = None
108 ret['comment'] = ('Database {0} is not present'
109 ).format(name)
110 return ret
111
112 try:
113 query = "SELECT * FROM `" + table + "` WHERE " + where_sql
114 select_result = __salt__['mysql.query'](database, query, **connection_args)
115
116 if select_result['rows returned'] > 1:
117 ret['result'] = False
118 ret['comment'] = 'More than one row matched the specified query'
119 elif select_result['rows returned'] == 1:
120 # create ordered dict of values returned by mysql.query function
121 old_data = salt.utils.odict.OrderedDict()
122 for num in xrange(0, len(select_result['columns'])):
123 old_data[select_result['columns'][num]] = select_result['results'][0][num]
124
125 for key, value in six.iteritems(data):
126 if key in old_data and old_data[key] != value:
127 if update:
128 if __opts__['test']:
129 ret['result'] = True
130 ret['comment'] = "Row will be update in " + table
131 else:
132 columns = []
133 for key, value in six.iteritems(data):
134 columns.append('`' + key + '`="' + value + '"')
135
136 query = "UPDATE `" + table + "` SET "
137 query += ",".join(columns)
138 query += " WHERE "
139 query += where_sql
140 update_result = __salt__['mysql.query'](database, query, **connection_args)
141
142 if update_result['rows affected'] == 1:
143 ret['result'] = True
144 ret['comment'] = "Row updated"
145 ret['changes']['old'] = old_data
146 ret['changes']['new'] = data
147 else:
148 ret['result'] = None
149 ret['comment'] = "Row update failed"
150 else:
151 ret['result'] = False
152 ret['comment'] = "Existing data does" + \
153 "not match desired state"
154 break
155
156 if ret['result'] is None:
157 ret['result'] = True
158 ret['comment'] = "Row exists"
159 else:
160 if __opts__['test']:
161 ret['result'] = True
162 ret['changes']['new'] = data
163 ret['comment'] = "Row will be inserted into " + table
164 else:
165 columns = []
166 values = []
167 for key, value in six.iteritems(data):
168 values.append('"' + value + '"')
169 columns.append("`" + key + "`")
170
171 query = "INSERT INTO `" + table + "` ("
172 query += ",".join(columns)
173 query += ") VALUES ("
174 query += ",".join(values)
175 query += ")"
176 insert_result = __salt__['mysql.query'](database, query, **connection_args)
177
178 if insert_result['rows affected'] == 1:
179 ret['result'] = True
180 ret['changes']['new'] = data
181 ret['comment'] = 'Inserted row'
182 else:
183 ret['result'] = False
184 ret['comment'] = "Unable to insert data"
185
186 except Exception as e:
187 ret['result'] = False
188 ret['comment'] = str(e)
189
190 return ret