my ansible modules which are stock in pull request
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
15KB

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # Copyright: (c) 2017, Ansible Project
  4. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  5. from __future__ import absolute_import, division, print_function
  6. __metaclass__ = type
  7. DOCUMENTATION = '''
  8. ---
  9. module: proxysql_replication_hostgroups
  10. version_added: "2.3"
  11. author: "Ben Mildren (@bmildren)"
  12. short_description: Manages replication hostgroups using the proxysql admin
  13. interface.
  14. description:
  15. - Each row in mysql_replication_hostgroups represent a pair of
  16. writer_hostgroup and reader_hostgroup. ProxySQL will monitor the value of
  17. read_only for all the servers in specified hostgroups, and based on the
  18. value of read_only will assign the server to the writer or reader
  19. hostgroups.
  20. options:
  21. writer_hostgroup:
  22. description:
  23. - Id of the writer hostgroup.
  24. required: True
  25. reader_hostgroup:
  26. description:
  27. - Id of the reader hostgroup.
  28. required: True
  29. comment:
  30. description:
  31. - Text field that can be used for any purposed defined by the user.
  32. check_type:
  33. description:
  34. - Variable checked when executing a read only check. Requires proxysql >= 2.0.1. Otherwise it has no effect.
  35. choices: ["read_only", "super_read_only", "innodb_read_only"]
  36. default: read_only
  37. version_added: 2.9
  38. state:
  39. description:
  40. - When C(present) - adds the replication hostgroup, when C(absent) -
  41. removes the replication hostgroup.
  42. choices: [ "present", "absent" ]
  43. default: present
  44. extends_documentation_fragment:
  45. - proxysql.managing_config
  46. - proxysql.connectivity
  47. '''
  48. EXAMPLES = '''
  49. ---
  50. # This example adds a replication hostgroup, it saves the mysql server config
  51. # to disk, but avoids loading the mysql server config to runtime (this might be
  52. # because several replication hostgroup are being added and the user wants to
  53. # push the config to runtime in a single batch using the
  54. # M(proxysql_manage_config) module). It uses supplied credentials to connect
  55. # to the proxysql admin interface.
  56. - proxysql_replication_hostgroups:
  57. login_user: 'admin'
  58. login_password: 'admin'
  59. writer_hostgroup: 1
  60. reader_hostgroup: 2
  61. state: present
  62. load_to_runtime: False
  63. # This example removes a replication hostgroup, saves the mysql server config
  64. # to disk, and dynamically loads the mysql server config to runtime. It uses
  65. # credentials in a supplied config file to connect to the proxysql admin
  66. # interface.
  67. - proxysql_replication_hostgroups:
  68. config_file: '~/proxysql.cnf'
  69. writer_hostgroup: 3
  70. reader_hostgroup: 4
  71. state: absent
  72. '''
  73. RETURN = '''
  74. stdout:
  75. description: The replication hostgroup modified or removed from proxysql
  76. returned: On create/update will return the newly modified group, on delete
  77. it will return the deleted record.
  78. type: dict
  79. "sample": {
  80. "changed": true,
  81. "msg": "Added server to mysql_hosts",
  82. "repl_group": {
  83. "comment": "",
  84. "reader_hostgroup": "1",
  85. "writer_hostgroup": "2"
  86. },
  87. "state": "present"
  88. }
  89. '''
  90. ANSIBLE_METADATA = {'metadata_version': '1.1',
  91. 'status': ['stableinterface'],
  92. 'supported_by': 'community'}
  93. from ansible.module_utils.basic import AnsibleModule
  94. from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
  95. from ansible.module_utils._text import to_native
  96. from distutils.version import LooseVersion
  97. # ===========================================
  98. # proxysql module specific support methods.
  99. #
  100. def perform_checks(module):
  101. if module.params["login_port"] < 0 \
  102. or module.params["login_port"] > 65535:
  103. module.fail_json(
  104. msg="login_port must be a valid unix port number (0-65535)"
  105. )
  106. if not module.params["writer_hostgroup"] >= 0:
  107. module.fail_json(
  108. msg="writer_hostgroup must be a integer greater than or equal to 0"
  109. )
  110. if not module.params["reader_hostgroup"] == \
  111. module.params["writer_hostgroup"]:
  112. if not module.params["reader_hostgroup"] > 0:
  113. module.fail_json(
  114. msg=("writer_hostgroup must be a integer greater than" +
  115. " or equal to 0")
  116. )
  117. else:
  118. module.fail_json(
  119. msg="reader_hostgroup cannot equal writer_hostgroup"
  120. )
  121. if mysql_driver is None:
  122. module.fail_json(msg=mysql_driver_fail_msg)
  123. def save_config_to_disk(cursor):
  124. cursor.execute("SAVE MYSQL SERVERS TO DISK")
  125. return True
  126. def load_config_to_runtime(cursor):
  127. cursor.execute("LOAD MYSQL SERVERS TO RUNTIME")
  128. return True
  129. class ProxySQLReplicationHostgroup(object):
  130. def __init__(self, module, proxysql_version):
  131. self.state = module.params["state"]
  132. self.save_to_disk = module.params["save_to_disk"]
  133. self.load_to_runtime = module.params["load_to_runtime"]
  134. self.writer_hostgroup = module.params["writer_hostgroup"]
  135. self.reader_hostgroup = module.params["reader_hostgroup"]
  136. self.comment = module.params["comment"]
  137. self.check_type = module.params["check_type"]
  138. self.support_check_type = LooseVersion(
  139. proxysql_version) >= LooseVersion("2.0.1")
  140. def check_repl_group_config(self, cursor, keys):
  141. query_string = \
  142. """SELECT count(*) AS `repl_groups`
  143. FROM mysql_replication_hostgroups
  144. WHERE writer_hostgroup = %s
  145. AND reader_hostgroup = %s"""
  146. query_data = [self.writer_hostgroup,
  147. self.reader_hostgroup]
  148. if self.comment and not keys:
  149. query_string += "\n AND comment = %s"
  150. query_data.append(self.comment)
  151. if self.support_check_type:
  152. query_string += " AND check_type = %s"
  153. query_data.append(self.check_type)
  154. cursor.execute(query_string, query_data)
  155. check_count = cursor.fetchone()
  156. return (int(check_count['repl_groups']) > 0)
  157. def get_repl_group_config(self, cursor):
  158. query_string = \
  159. """SELECT *
  160. FROM mysql_replication_hostgroups
  161. WHERE writer_hostgroup = %s
  162. AND reader_hostgroup = %s"""
  163. query_data = [self.writer_hostgroup,
  164. self.reader_hostgroup]
  165. cursor.execute(query_string, query_data)
  166. repl_group = cursor.fetchone()
  167. return repl_group
  168. def create_repl_group_config(self, cursor):
  169. if self.support_check_type:
  170. query_string = \
  171. """INSERT INTO mysql_replication_hostgroups (
  172. writer_hostgroup,
  173. reader_hostgroup,
  174. comment,
  175. check_type)
  176. VALUES (%s, %s, %s, %s)"""
  177. query_data = [self.writer_hostgroup,
  178. self.reader_hostgroup,
  179. self.comment or '',
  180. self.check_type]
  181. else:
  182. query_string = \
  183. """INSERT INTO mysql_replication_hostgroups (
  184. writer_hostgroup,
  185. reader_hostgroup,
  186. comment)
  187. VALUES (%s, %s, %s)"""
  188. query_data = [self.writer_hostgroup,
  189. self.reader_hostgroup,
  190. self.comment or '']
  191. cursor.execute(query_string, query_data)
  192. return True
  193. def update_repl_group_config(self, cursor):
  194. if self.support_check_type:
  195. query_string = \
  196. """UPDATE mysql_replication_hostgroups
  197. SET comment = %s, check_type = %s
  198. WHERE writer_hostgroup = %s
  199. AND reader_hostgroup = %s"""
  200. query_data = [self.comment, self.check_type, self.writer_hostgroup, self.reader_hostgroup]
  201. else:
  202. query_string = \
  203. """UPDATE mysql_replication_hostgroups
  204. SET comment = %s
  205. WHERE writer_hostgroup = %s
  206. AND reader_hostgroup = %s"""
  207. query_data = [self.comment,
  208. self.writer_hostgroup,
  209. self.reader_hostgroup]
  210. cursor.execute(query_string, query_data)
  211. return True
  212. def delete_repl_group_config(self, cursor):
  213. query_string = \
  214. """DELETE FROM mysql_replication_hostgroups
  215. WHERE writer_hostgroup = %s
  216. AND reader_hostgroup = %s"""
  217. query_data = \
  218. [self.writer_hostgroup,
  219. self.reader_hostgroup]
  220. cursor.execute(query_string, query_data)
  221. return True
  222. def manage_config(self, cursor, state):
  223. if state:
  224. if self.save_to_disk:
  225. save_config_to_disk(cursor)
  226. if self.load_to_runtime:
  227. load_config_to_runtime(cursor)
  228. def create_repl_group(self, check_mode, result, cursor):
  229. if not check_mode:
  230. result['changed'] = \
  231. self.create_repl_group_config(cursor)
  232. result['msg'] = "Added server to mysql_hosts"
  233. result['repl_group'] = \
  234. self.get_repl_group_config(cursor)
  235. self.manage_config(cursor,
  236. result['changed'])
  237. else:
  238. result['changed'] = True
  239. result['msg'] = ("Repl group would have been added to" +
  240. " mysql_replication_hostgroups, however" +
  241. " check_mode is enabled.")
  242. def update_repl_group(self, check_mode, result, cursor):
  243. if not check_mode:
  244. result['changed'] = \
  245. self.update_repl_group_config(cursor)
  246. result['msg'] = "Updated server in mysql_hosts"
  247. result['repl_group'] = \
  248. self.get_repl_group_config(cursor)
  249. self.manage_config(cursor,
  250. result['changed'])
  251. else:
  252. result['changed'] = True
  253. result['msg'] = ("Repl group would have been updated in" +
  254. " mysql_replication_hostgroups, however" +
  255. " check_mode is enabled.")
  256. def delete_repl_group(self, check_mode, result, cursor):
  257. if not check_mode:
  258. result['repl_group'] = \
  259. self.get_repl_group_config(cursor)
  260. result['changed'] = \
  261. self.delete_repl_group_config(cursor)
  262. result['msg'] = "Deleted server from mysql_hosts"
  263. self.manage_config(cursor,
  264. result['changed'])
  265. else:
  266. result['changed'] = True
  267. result['msg'] = ("Repl group would have been deleted from" +
  268. " mysql_replication_hostgroups, however" +
  269. " check_mode is enabled.")
  270. # ===========================================
  271. # Module execution.
  272. #
  273. def main():
  274. module = AnsibleModule(
  275. argument_spec=dict(
  276. login_user=dict(default=None, type='str'),
  277. login_password=dict(default=None, no_log=True, type='str'),
  278. login_host=dict(default="127.0.0.1"),
  279. login_unix_socket=dict(default=None),
  280. login_port=dict(default=6032, type='int'),
  281. config_file=dict(default="", type='path'),
  282. writer_hostgroup=dict(required=True, type='int'),
  283. reader_hostgroup=dict(required=True, type='int'),
  284. comment=dict(type='str'),
  285. check_type=dict(default="read_only", type='str', choices=[
  286. "read_only",
  287. "super_read_only",
  288. "innodb_read_only"]),
  289. state=dict(default='present', choices=['present',
  290. 'absent']),
  291. save_to_disk=dict(default=True, type='bool'),
  292. load_to_runtime=dict(default=True, type='bool')
  293. ),
  294. supports_check_mode=True
  295. )
  296. perform_checks(module)
  297. login_user = module.params["login_user"]
  298. login_password = module.params["login_password"]
  299. config_file = module.params["config_file"]
  300. cursor = None
  301. try:
  302. cursor = mysql_connect(module,
  303. login_user,
  304. login_password,
  305. config_file,
  306. cursor_class=mysql_driver.cursors.DictCursor)
  307. except mysql_driver.Error as e:
  308. module.fail_json(
  309. msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
  310. )
  311. cursor.execute("select version();")
  312. raw_version = cursor.fetchone()
  313. proxysql_version = raw_version['version()']
  314. proxysql_repl_group = ProxySQLReplicationHostgroup(
  315. module, proxysql_version)
  316. result = {}
  317. result['state'] = proxysql_repl_group.state
  318. if proxysql_repl_group.state == "present":
  319. try:
  320. if not proxysql_repl_group.check_repl_group_config(cursor,
  321. keys=True):
  322. proxysql_repl_group.create_repl_group(module.check_mode,
  323. result,
  324. cursor)
  325. else:
  326. if not proxysql_repl_group.check_repl_group_config(cursor,
  327. keys=False):
  328. proxysql_repl_group.update_repl_group(module.check_mode,
  329. result,
  330. cursor)
  331. else:
  332. result['changed'] = False
  333. result['msg'] = ("The repl group already exists in" +
  334. " mysql_replication_hostgroups and" +
  335. " doesn't need to be updated.")
  336. result['repl_group'] = \
  337. proxysql_repl_group.get_repl_group_config(cursor)
  338. except mysql_driver.Error as e:
  339. module.fail_json(
  340. msg="unable to modify replication hostgroup.. %s" % to_native(e)
  341. )
  342. elif proxysql_repl_group.state == "absent":
  343. try:
  344. if proxysql_repl_group.check_repl_group_config(cursor,
  345. keys=True):
  346. proxysql_repl_group.delete_repl_group(module.check_mode,
  347. result,
  348. cursor)
  349. else:
  350. result['changed'] = False
  351. result['msg'] = ("The repl group is already absent from the" +
  352. " mysql_replication_hostgroups memory" +
  353. " configuration")
  354. except mysql_driver.Error as e:
  355. module.fail_json(
  356. msg="unable to delete replication hostgroup.. %s" % to_native(e)
  357. )
  358. module.exit_json(**result)
  359. if __name__ == '__main__':
  360. main()