I'm trying to perfrom a simple "SHOW DATABASES;" command with an ansible playbook and it gets stuck when executes the command.
I tried different options
- hosts: servers
become: true
vars_prompt:
- name: "db_passw"
prompt: "DB root password?"
tasks:
- name: Configure Database
shell: |
mysql -u root -p {{ db_passw }} < ~/sql_query.sql
Also
- hosts: servers
become: true
vars_prompt:
- name: "db_passw"
prompt: "DB root password?"
tasks:
- name: Configure Database
shell: |
mysql -u root -p {{ db_passw }} -e "SHOW DATABASES;"
And I also tried, just as a test, to put the password explicitly
- hosts: servers
become: true
vars_prompt:
- name: "db_passw"
prompt: "DB root password?"
tasks:
- name: Configure Database
shell: |
mysql -u root -p <root_passw> -e "SHOW DATABASES;"
But it always gets stuck at the same point. I tried to execute above's mysql commands in the remote machine shell and they work with no problems. I also tried to execute other commands before mysql's ones and they are being executed.
Is there any problem between ansible and mysql?
I know thta there is a MySQL module for Ansible, but it's functionality is too limited.
Thank you
Works for me. The play below
- hosts: dbserver
tasks:
- command: |
mysql -e "SHOW DATABASES;"
register: result
- debug:
var: result
gives:
TASK [debug]
ok: [dbserver] => {
"result": {
"changed": true,
"cmd": [
"mysql",
"-e",
"SHOW DATABASES;"
],
"delta": "0:00:00.010958",
"end": "2019-04-04 16:19:58.359100",
"failed": false,
"rc": 0,
"start": "2019-04-04 16:19:58.348142",
"stderr": "",
"stderr_lines": [],
"stdout":
...
It's not necessary to specify "-u root" when "become: true". I haven't set a MySQL password for root.
Related
I try to setup a MySQL DB with Ansible, however, I have trouble with changing the initial root password.
- name: Get temporary root password from install log
shell: cat /var/log/mysqld.log | grep "temporary password" | grep -oE '[^ ]+$'
register: tmp_root_password
- name: Set new password from temporary password
shell: 'mysql -e \"ALTER USER root IDENTIFIED BY("{{ mysql_root_password }}");\" --connect-expired-password -uroot -p"{{ tmp_root_password.stdout }}"'
Fails with the following error:
fatal: [mysqlhost.mydomain]: FAILED! => {"changed": true, "cmd": "mysql -e \\\"ALTER USER root IDENTIFIED BY(\" MyNewPassword\");\\\" --connect-expired-password -uroot -p\"MyTmpPassword\"", "delta": "0:00:00.003081", "end": "2021-11-28 08:40:52.000198", "msg": "non-zero return code", "rc": 1, "start": "2021-11-28 08:40:51.997117", "stderr": "/bin/sh: -c: line 0: syntax error near unexpected token `('\n/bin/sh: -c: line 0: `mysql -e \\\"ALTER USER root IDENTIFIED BY(\" MyNewPassword\");\\\" --connect-expired-password -uroot -p\"MyTmpPassword\"'", "stderr_lines": ["/bin/sh: -c: line 0: syntax error near unexpected token `('", "/bin/sh: -c: line 0: `mysql -e \\\"ALTER USER root IDENTIFIED BY(\" MyNewPassword\");\\\" --connect-expired-password -uroot -p\"MyTmpPassword\"'"], "stdout": "", "stdout_lines": []}
I've tried to set the root password based on the below guide, as well, without any luck.
https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_user_module.html#ansible-collections-community-mysql-mysql-user-module
Thanks!
The following is based the Ansible role I created for mysql/percona and is idempotent.
This is the playbook you could use, taken from the repo described above.
This sets the 'debian-sys-main' user as a root user of the database.
This also assumes you build MySQL for the first time, and not while already being active/installed.
---
- name: root | stat to check whether /root/.my.cnf exists
stat:
path: /root/.my.cnf
register: cnf_file
- block:
- name: root | place temporary cnf file
template:
src: temp_cnf.j2
dest: /etc/my.cnf
mode: '0644'
- name: root | start mysql to add the debian-sys-maint user
systemd:
name: mysql
state: started
enabled: true
- name: root | get temp root password
shell: >-
grep 'temporary password' /var/log/mysqld.log |
awk '{print $NF}' | tail -n 1
register: temp_root_pw
no_log: true
- name: root | set root password
shell: >-
mysqladmin -u root
--password="{{ temp_root_pw.stdout }}"
password "{{ mysql_root_password }}"
no_log: true
- name: root | set debian-sys-maint user and password
mysql_user:
name: debian-sys-maint
password: "{{ mysql_system_password }}"
priv: '*.*:ALL,GRANT'
update_password: always
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
login_user: root
login_password: "{{ mysql_root_password }}"
no_log: true
- name: root | copy root.cnf
template:
src: root.cnf.j2
dest: /etc/mysql/root.cnf
mode: '0600'
owner: root
group: root
- name: root | make symlink of file for root db access
file:
state: link
src: /etc/mysql/root.cnf
path: /root/.my.cnf
- name: root | delete anonymous connections
mysql_user:
name: ""
host_all: true
state: absent
no_log: true
- name: root | secure root user
mysql_user:
name: root
host: "{{ item }}"
no_log: true
loop:
- ::1
- 127.0.0.1
- localhost
- name: root | ensure test database is removed
mysql_db:
name: test
login_user: root
state: absent
- name: root | stop mysql again
systemd:
name: mysql
state: stopped
enabled: true
- name: root | remove mysqld log file
file:
path: /var/log/mysqld.log
state: absent
when: not cnf_file.stat.exists
The temp_cnf.j2:
[client]
socket=/var/run/mysqld/mysqld.sock
[mysqld]
server-id=1
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
And the root.cnf.j2
{{ ansible_managed | comment }}
# This file is symlinked to /root/.my.cnf to use passwordless login for the root user
[client]
socket = {{ mysqld.socket }}
user = debian-sys-maint
password = {{ percona_system_password }}
[mysql_upgrade]
socket = {{ mysqld.socket }}
user = debian-sys-maint
password = {{ percona_system_password }}
Some vars:
mysql_root_password: my_password
mysql_system_password: my_password
mysqld:
socket: /var/run/mysqld/mysqld.sock
Should work for CentOS 8, Rocky Linux and Oracle Linux as well.
Regarding the initial question for RHEL 7 and MySQL Server 8.0.21, I've found the following approach working in the mentioned environment.
- name: Delete all anonymous SQL user accounts
mysql_user:
user: ""
host_all: yes
state: absent
- name: Remove the SQL test database
mysql_db:
db: test
state: absent
- name: Change root user password on first run
mysql_user:
login_user: root
login_password: ''
name: root
password: "{{ SQL_ROOT_PASSWORD }}"
priv: "*.*:ALL,GRANT"
host: "{{ item }}"
with_items:
- "{{ ansible_hostname }}"
- "127.0.0.1"
- "::1"
- "localhost"
I'm trying to use ansible and docker to take a backup of my MySQL database. This is my ansible-task:
docker_container:
name: "{{ mysql_dump_container_name }}"
image: mysql:8.0.20
env:
MYSQL_ROOT_PASSWORD: "{{ mysql_password }}"
MYSQL_DATABASE: "{{ mysql_database }}"
ports:
- "3307:3307"
volumes:
- "{{ mysql_backup_dir }}:/backup"
entrypoint: "mysqldump -u {{ mysql_username }} -p{{ mysql_password }} --host {{ mysql_host }} {{ mysql_database }} > 1.sql "
detach: yes
restart_policy: "unless-stopped"
become: yes
I am getting the following error:
mysqldump: Couldn't find table: ">"
Entrypoint message is:
"entrypoint": [
"mysqldump -u root -proot --host wl.cs.net TEST > 1.sql"
],
What am I doing wrong?
Actually, you don't need to overwrite the entrypoint as the one that is already in the container will run your command. You just need to make sure that the > ends up at the right place. I do that by quoting the command.
You can do this:
docker_container:
name: "{{ mysql_dump_container_name }}"
image: "mysql:8.0.20"
volumes:
- "{{ mysql_backup_dir }}:/backup"
command: "/bin/sh -c 'mysqldump -u {{ mysql_username }} -p{{ mysql_password }} --host {{ mysql_host }} {{ mysql_database }} > /backup/1.sql'"
detach: false
cleanup: true
become: yes
Additional notes:
You do not need any port as the container is not listening for anything.
You don't need the env, as you aren't creating a database.
You probably want to write the dump to a file in /backup.
You probably don't want restart_policy: "unless-stopped" as it doesn't make sense to restart a failed backup, as it will probably fail again. (You need to find out why it failed, first)
You will probably want detach: false, so ansible will wait for the container to finish and then show failed if the backup failed.
You will probably want cleanup: true, so you don't need to clean up containers manually.
Links:
Documentation of the docker_container module
Redirecting command output in docker
I found this page from answer number 4 by #cobbzilla useful to my use case.
Just want to ask if Ansible is capable to have a function that will handle this command:
2>&1 >> /tmp/debug.log
I have already applied this solution to my yml files and I was looking if the command can be wrapped on a function so that it will show much cleaner.
My Sample Ansible yml:
- name: Deploy
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: perform 1st script
shell: bash -c "1st_script.sh 2>&1 >> /var/tmp/debug.log"
- name: perform 2nd script
shell: bash -c "2nd_script.sh 2>&1 >> /var/tmp/debug.log"
- name: perform 3rd script
shell: bash -c "3rd_script.sh 2>&1 >> /var/tmp/debug.log"
Preliminary note: I am answering your direct question below because this can be useful in other circumstances.
Meanwhile, in the ansible context, playing bash scripts and returning their output and error to a log file on the target is generally not a good idea. You will be left blind if something goes wrong, or if you want to analyze the output, as the module will return an empty stderr and stdout. You will have to rely on analyzing the log file later (and finding the correct output since you mix script output in there).
On an even wider level, you should use shell/command only when there is no other possibility to get the same job done using existing modules
If you don't mind writing some lines of python, a pretty easy and straight forward way to acheive your goal is to use a custom filter. The below example is quickNdirty. You will probably have to harden its code (escape command characters, check for specific errors...) but this should put you on track.
For the example, I am creating the filter in the filter_plugins folder adjacent to the demo playbook. If you need to distribute that filter across projects, search the ansible documentation to learn how to wrap that in a role or a collection.
In filter_plugins/shell_filters.py
def bash_n_log(command, log_file='/var/tmp/debug.log'):
"""Return a formatted string to play the script in bash and log its output"""
return f'bash -c "{command} 2>&1 >> {log_file}"'
class FilterModule(object):
"""collection of shell utility filters."""
def filters(self):
"""Return the filter list."""
return {
'bash_n_log': bash_n_log
}
Then the demo playbook.yml
- name: Custom shell filter demo
hosts: localhost
gather_facts: false
vars:
my_commands:
- echo Hello World
- ls -l /dev/null
tasks:
- name: Play my commands with my filter
shell: "{{ item | bash_n_log }}"
loop: "{{ my_commands }}"
- name: Same example with non default log file
shell: "{{ item | bash_n_log('/tmp/other.log') }}"
loop: "{{ my_commands }}"
- name: Get content of the log files
slurp:
path: "{{ item }}"
register: slurped_logs
loop:
- /var/tmp/debug.log
- /tmp/other.log
- name: Show log file content
debug:
msg: "{{ (item.content | b64decode).split('\n') }}"
loop: "{{ slurped_logs.results }}"
loop_control:
label: "{{ item.item }}"
gives:
$ ansible-playbook playbook.yml
PLAY [Custom shell filter demo] ********************************************************************************************************************************************************************************************************
TASK [Play my commands with my filter] *************************************************************************************************************************************************************************************************
changed: [localhost] => (item=echo Hello World)
changed: [localhost] => (item=ls -l /dev/null)
TASK [Same example with non default log file] ******************************************************************************************************************************************************************************************
changed: [localhost] => (item=echo Hello World)
changed: [localhost] => (item=ls -l /dev/null)
TASK [Get content of the log files] ****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=/var/tmp/debug.log)
ok: [localhost] => (item=/tmp/other.log)
TASK [Show log file content] ***********************************************************************************************************************************************************************************************************
ok: [localhost] => (item=/var/tmp/debug.log) => {
"msg": [
"Hello World",
"crw-rw-rw- 1 root root 1, 3 Mar 27 12:06 /dev/null",
""
]
}
ok: [localhost] => (item=/tmp/other.log) => {
"msg": [
"Hello World",
"crw-rw-rw- 1 root root 1, 3 Mar 27 12:06 /dev/null",
""
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I just tried and tested this simple solution worked, I just added vars section witch points the variable to a command
- name: Deploy
hosts: localhost
connection: local
gather_facts: false
vars:
logger: "2>&1 >> /var/tmp/debug.log"
tasks:
- name: perform 1st script
shell: bash -c "1st_script.sh {{ logger }}"
- name: perform 2nd script
shell: bash -c "2nd_script.sh {{ logger }}"
- name: perform 3rd script
shell: bash -c "3rd_script.sh {{ logger }}"
This question already has answers here:
Ansible Command module says that '|' is illegal character
(2 answers)
Closed 4 years ago.
I'm trying implement a solution I've found on stackoverflow for truncating all tables in a database.
It works when I run it on terminal command line but unfortunately it does not work on Ansible.
Problematic part is this -e \"truncate table $table\" How should I handle a variable in double quote ? I couldn't found any solution so far.
- set_fact: db_truncate_command="mysql -v -h {{ wp_db_host }} -u {{ wp_db_user }} -p{{ wp_db_password }} -Nse 'show tables' {{ wp_db_name }} | while read table; do mysql -v -h {{ wp_db_host }} -u {{ wp_db_user }} -p{{ wp_db_password }} -e \"truncate table $table\" {{ wp_db_name }}; done"
when: stat_db_file.stat.exists
- debug:
msg: "command {{ db_truncate_command }} "
- name: Truncate existing tables in db
command: >
{{ db_truncate_command }}
when: stat_db_file.stat.exists
The output I get:
TASK [mysql : set_fact] ***********************************************************************************************************************************************************
ok: [ansible.mydomain.com] => {"ansible_facts": {"db_truncate_command": "mysql -v -h 192.155.190.255 -u wp_user -pMyPassword -Nse 'show tables' ansible | while read table; do mysql -v -h 192.155.190.255 -u wp_user -pMyPassword -e \"truncate table $table\" ansible; done"}, "changed": false}
TASK [mysql : debug] **************************************************************************************************************************************************************
ok: [ansible.mydomain.com] => {
"msg": "command mysql -v -h 192.155.190.255 -u wp_user -pMyPassword -Nse 'show tables' ansible | while read table; do mysql -v -h 192.155.190.255 -u wp_user -pMyPassword -e \"truncate table $table\" ansible; done "
}
TASK [mysql : Truncate existing tables in db] *************************************************************************************************************************************
fatal: [ansible.mydomain.com]: FAILED! => {"changed": true, "cmd": ["mysql", "-v", "-h", "192.155.190.255", "-u", "wp_user", "-pMyPassword", "-Nse", "show tables", "ansible", "|", "while", "read", "table;", "do", "mysql", "-v", "-h", "192.155.190.255", "-u", "wp_user", "-pMyPassword", "-e", "truncate table $table", "ansible;", "done"], "delta": "0:00:00.005262", "end": "2018-02-24 10:33:22.808490", "msg": "non-zero return code", "rc": 1, "start": "2018-02-24 10:33:22.803228", "stderr": "mysql: [Warning] Using a password on the command line interface can be insecure.", "stderr_lines": ["mysql: [Warning] Using a password on the command line interface can be insecure."], "stdout": "mysql Ver 14.14 Distrib 5.7.21, for Linux (x86_64) using EditLine wrapper\n
Added output of fail message
Using shell instead of command works.
- name: Truncate existing tables in db
shell: >
{{ db_truncate_command }}
when: stat_db_file.stat.exists
I am executing this line in inno setup, but i am getting and exit code 2
;Setting root password default root (blank). ex : mypass4u#
Filename: "{app}\mysql\bin\mysqladmin.exe"; \
Parameters: "-u root -e ""update mysql.user set password=PASSWORD('mypass4u#') where user='root';"""; \
StatusMsg: "Setting password root"; \
Flags: runhidden;
I get the following message in the debug window
[11:56:54.387] -- Run entry -- [11:56:54.392] Run as: Current user
[11:56:54.396] Type: Exec
[11:56:54.400] Filename: C:\Program Files (x86)\Company\Myapp\mysql\bin\mysqladmin.exe
[11:56:54.405] Parameters: -u root -e "update mysql.user set
password=PASSWORD('mypass4u#') where user='root';"
[11:56:54.758] Process exit code: 2
What could be causing this error
I assume you wanted to use mysql.exe, not mysqladmin.exe.