How do you prevent a dpkg installation task to notify a changed state when it runs for the second time? - deb

There isn't a module for installing .deb packages directly. When you have to run dpkg as a command, it always mark the installation task as one that has changed. I'd some trouble configuring it correctly, so I'm posting here as a public notebook.
Here is the task to install with dpkg:
- name: Install old python
command: dpkg -i {{ temp_dir }}/{{ item }}
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb
The files where uploaded to {{temp_dir}} in another task.

The answer below still works, but newer ansible versions have the apt module. Mariusz Sawicki answer is the preferred one now. I've marked it as the accepted answer.
It'll work just with Ansible version 1.3, when changed_when parameter was added. It is a little clumsy, maybe someone can improve the solution. I didn't find the documentation of this "register" object.
- name: Install old python
command: dpkg --skip-same-version -i {{ temp_dir }}/{{ item }}
register: dpkg_result
changed_when: "dpkg_result.stdout.startswith('Selecting')"
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb
Here you can run the same task and it will just install the first time. After the first time, the packages won't be installed.
There were two modifications. One is the parameter --skip-same-version for preventing dpkg to reinstall the software. The other is the register and changed_when attributes. The first time dpkg runs, it prints to stdout a string starting with 'Selecting' and a change is notified. Later it will have a different output. I've tried a more readable condition, but couldn't make it work with a more sofisticated condition that uses "not" or searches for a substring.

In Ansible 1.6 (and newer), the apt module has a deb option:
- apt: deb=/tmp/mypackage.deb

You could use apt module with dpkg_options parameter:
- name: Install old python
apt: deb={{ temp_dir }}/{{ item }} dpkg_options="skip-same-version"
register: dpkg_result
changed_when: dpkg_result.stderr.find("already installed") == -1
with_items:
- python2.4-minimal_2.4.6-6+precise1_i386.deb
- python2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- libpython2.4_2.4.6-6+{{ ubuntu_release }}1_i386.deb
- python2.4-dev_2.4.6-6+{{ ubuntu_release }}1_i386.deb

Related

Salt can't parse files any more: Requisite declaration... is not formed as a single key dictionary

About two-month-old sls files are working no more. I've tried to put the minimal example below:
salt 'myserver.internal' state.highstate gave:
myserver.internal:
Data failed to compile:
----------
Requisite declaration dhparam in SLS nginx is not formed as a single key dictionary
----------
Requisite declaration /etc/nginx/sites-available/myapp.conf in SLS nginx is not formed as a single key dictionary
ERROR: Minions returned with non-zero exit code
with the following nginx.sls:
/etc/nginx/sites-available/myapp.conf:
file.managed:
- name: /etc/nginx/sites-available/myapp.conf
- source: salt://nginx-myapp.conf.jinja
- template: jinja
- require:
- dhparam
dhparam:
cmd:
- run
- name: "mkdir -p /etc/nginx/ssl/; openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048"
- unless: ls /etc/nginx/ssl/dhparam.pem
And there are tens of those errors when I run the whole configuration. Am I missing something? Maybe, some crucial dependency not installed/updated/broken? yamllint did not find any problems in my SLS files. Same files worked well on another server two months ago.
Versions:
salt-master 2016.11.6+ds-1
salt-minion 2015.8.8+ds-1
The system is Ubuntu Xenial 16.04.2 LTS on both master and minion.
The problem is the version of the minion. While I added repo key for saltstack, I forgot to add
deb http://repo.saltstack.com/apt/ubuntu/16.04/amd64/latest xenial main
to /etc/apt/sources.list.d/saltstack.list and run apt update before installing salt-minion.
When I corrected that, files started to work again.

Ansible package warning when using sudo

I was following this doc to install shiny package in RedHat 7.3. The command provided in the doc is:
$ sudo su - \
-c "R -e \"install.packages('shiny', repos='https://cran.rstudio.com/')\""
In Ansible, I wrote it like this:
- name: Installing Shiny Packages
shell: sudo su - -c "R -e \"install.packages('shiny', repos='https://cran.rstudio.com/')\""
#when: install_R|changed
I am getting a warning when I run my playbook:
TASK [Installing Shiny Packages] ***********************************************
[WARNING]: Consider using 'become', 'become_method', and 'become_user' rather
than running sudo
changed: [test]
Please let me know how to write this in ansible so that I can avoid the warning.
It is probably because of the outdated sudo usage from version 1.9. From the official Ansible documentation.
Before 1.9 Ansible mostly allowed the use of sudo and a limited use of su to allow a login/remote user to become a different user and execute tasks, create resources with the 2nd user’s permissions. As of 1.9, become supersedes the old sudo/su, while still being backwards compatible.
You can remove it by using the become module which allows you to 'become' another user, different from the user that logged into the machine (remote user). You need to set to true to activate privilege escalation.
name: Installing Shiny Packages
shell: R -e "install.packages('shiny', repos='https://cran.rstudio.com/')"
become: true

ansible mingw32-make out of memory

I try to automate a build via ansible on window7 Virtual Machine.
My build is base on cmake (version 3.7.1) and mingw32 (version 4.9.2) as compiler.
If I do the build directly in the VM by enter manually all command in the powershell, everything work find.
git clone --recursive somedepot
cd somedepot
cmake.bat .
mingw32-make all
Note : cmake.bat is the following script :
#cmake.exe -G"MinGW Makefile" %*
But when I try to do the same by ansible I got "cc1plus.exe: out of memory allocating 176080 bytes\r\nmingw32-make[2]" at the execution of mingw32-make all. But not on all build failed, I test my script before in a simple build and work well. It's when I wanted to go to the "real build" (which is more bigger) that the problem append.
Here my playbook :
- name: Some Build
hosts: win_build
tasks :
- name: Get src
win_command: git clone --recursive --branch "{{ tag_src }}" "{{ url_src }}" "{{ path_cmake }}"
- name: CMake
win_command: cmake.bat .
args:
chdir: "{{ path_cmake }}"
- name: Make
win_command: mingw32-make all
args:
chdir: "{{ path_cmake }}"
Thanks in advance.
I found the problem.
It was a bug in powershell3. I applied microsoft hotfix and everything work fine.

Ansible command to check the java version in different servers

I am writing a Test case using ansible.There are totally 9 servers in which I need to check whether the installed java version is 1.7.0 or not?
If it is less than 1.7.0 then test case should fail.
Can anyone help me to write this Test case as I am very new to ansible.
Thanks in advance
Ansible has a version_compare filter since 1.6. But since Ansible doesn't know about your Java version you first need to fetch it in a separate task and register the output, so you can compare it.
- name: Fetch Java version
shell: java -version 2>&1 | grep version | awk '{print $3}' | sed 's/"//g'
register: java_version
- assert:
that:
- java_version.stdout | version_compare('1.7', '>=')
On a sidenote, if your main use case for Ansible is to validate the server state you might want to have a look at using an infrastructure test tool instead: serverspec, goss, inspec, testinfra.
Altough in your question you havn't specified what have you tried, but still
You can run a commands like this
ansible your_host -m command -a 'java -version'
If you need to parse the output of java -version there is a very good script from Glenn Jackman here adapt it to your needs and use it.
If you are still looking for help, be more specific and show what you tried to do.
Since 2.0 you can make this
- name: Check if java is installed
command: java -version
become_user: '{{ global_vars.user_session }}' // your user session
register: java_result
ignore_errors: True
- debug:
msg: "Failed - Java is not installed"
when: java_result is failed
- debug:
msg: "Success - Java is installed"
when: java_result is success

Saltstack for "configure make install"

I'm getting my feet wet with SaltStack. I've made my first state (a Vim installer with a static configuration) and I'm working on my second one.
Unfortunately, there isn't an Ubuntu package for the application I'd like my state to install. I will have to build the application myself. Is there a "best practice" for doing "configure-make-install" type installations with Salt? Or should I just use cmd?
In particular, if I was doing it by hand, I would do something along the lines of:
wget -c http://example.com/foo-3.4.3.tar.gz
tar xzf foo-3.4.3.tar.gz
cd foo-3.4.3
./configure --prefix=$PREFIX && make && make install
There are state modules to abstract the first two lines, if you wish.
file.managed: http://docs.saltstack.com/ref/states/all/salt.states.file.html
archive.extracted: http://docs.saltstack.com/ref/states/all/salt.states.archive.html
But you could also just run the commands on the target minion(s).
install-foo:
cmd.run:
- name: |
cd /tmp
wget -c http://example.com/foo-3.4.3.tar.gz
tar xzf foo-3.4.3.tar.gz
cd foo-3.4.3
./configure --prefix=/usr/local
make
make install
- cwd: /tmp
- shell: /bin/bash
- timeout: 300
- unless: test -x /usr/local/bin/foo
Just make sure to include an unless argument to make the script idempotent.
Alternatively, distribute a bash script to the minion and execute. See:
How can I execute multiple commands using Salt Stack?
As for best practice? I would recommend using fpm to create a .deb or .rpm package and install that. At the very least, copy that tarball to the salt master and don't rely on external resources to be there three years from now.
Let's assume foo-3.4.3.tar.gz is checked into GitHub. Here is an approach that you might pursue in your state file:
git:
pkg.installed
https://github.com/nomen/foo.git:
git.latest:
- rev: master
- target: /tmp/foo
- user: nomen
- require:
- pkg: git
foo_deployed:
cmd.run:
- cwd: /tmp/foo
- user: nomen
- name: |
./configure --prefix=/usr/local
make
make install
- require:
- git: https://github.com/nomen/foo.git
Your configuration prefix location could be passed as a salt pillar. If the build process is more complicated, you may consider writing a custom state.

Resources