I want Ansible to install a .deb package from a url when the url changes every week (because of updates). There is an anchor to the dynamic download url on a static download page. Is there a trick to have ansible figure out the url?
E.g. on a certain page, there is a link called "Latest version" as so:
Latest version.
Can I get ansible to get URL_TO_DOWNLOAD_DEB?
Here is some made-up trick that hopefully illustrates what I mean. Obviously, this does not work.
- name: Find link to DEB package
regex_from_url:
url: http://some.download.page
regex: g/<a href="([^"]+)">Latest version<\/a>./
register: URL_TO_DOWNLOAD_DEB
- name: Install DEB package
apt:
deb: {{ URL_TO_DOWNLOAD_DEB }}
You can try something like this:
- name: Send GET request to target
shell: wget -O - http://some.download.page | grep '>Latest version<' | sed -n 's/.*href="\(.*\)".*/\1/p'
register: web
args:
warn: False
- name: Show download link
debug:
msg: "{{ web.stdout }}"
Related
I'm trying to run a shell command to install a package downloaded from my local Artifactory repository, as I don't have access to download it straight from the internet.
When I run the command directly on the node as such
rpm -ivh kubectl-1.1.1.x86_64.rpm --nodigest --nofiledigest
It works perfectly.
But then put in Ansible playbook as such
- name: Install Kubectl
shell: rpm -ivh kubectl-1.1.1.x86_64.rpm --nodigest --nofiledigest
Nothing happens.
It doesn't error.. It just doesn't install.
I've tried the command and ansible.builtin.shell module as well, but nothing works.
Is there a way to do this please?
There are different topics in your question.
Regarding
to install a package downloaded from my local Artifactory repository, as I don't have access to download it straight from the internet.
you can use different approaches.
1. Direct download
- name: Make sure package becomes installed from internal repository
yum:
name: https://{{ REPOSITORY_URL }}/artifactory/kube/kubectl-{{ KUBE_VERSION }}.x86_64.rpm
state: present
2. Configure local repository
The next one is to provide a .repo template file like
[KUBE]
name = Kubectl - $basearch
baseurl = https://{{ REPOSITORY_URL }}/artifactory/kube/
username = {{ API_USER }}
password = {{ API_KEY }}
sslverify = 1
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-KUBE
and to perform
- name: Make sure package becomes installed from internal repository
yum:
name: kubectl
state: present
This is possible because JFrog Artifactory can provide local RPM repositories if configured correctly. For more information you research the documentation there since it is almost only about proper configuration.
Regarding
Nothing happens. It doesn't error.. It just doesn't install.
you can use several task to split up your steps, make them idempotent and get an better insight how they are working.
3. shell, rpm and debug
- name: Make sure destination folder for package download (/opt/packages) exists
file:
path: "/opt/packages/"
state: directory
- name: Download RPM to remote hosts
get_url:
url: "https://{{ REPOSTORY_URL }}/artifactory/kube/kubectl-{{ KUBE_VERSION }}.x86_64.rpm"
dest: "/opt/packages/kubectl-{{ KUBE_VERSION }}.x86_64.rpm"
- name: Check package content
shell:
cmd: "rpm -qlp /opt/packages/kubectl-{{ KUBE_VERSION }}.x86_64.rpm"
register: rpm_qlp
- name: STDOUT rpm_qlp
debug:
msg: "{{ rpm_qlp.stdout.split('\n')[:-1] }}"
- name: Install RPM using 'command: rpm -ivh'
shell:
cmd: "rpm -ivh /opt/packages/kubectl-{{ KUBE_VERSION }}.x86_64.rpm"
register: rpm_ivh
- name: STDOUT rpm_ivh
debug:
msg: "{{ rpm_ivh.stdout.split('\n')[:-1] }}"
Depending on the RPM package, environment and configuration, all may just work good.
Try use command module and register the output i use it to install oci8 by pecl for oracle database on linux
This is my code currently -
- name: software
hosts: localhost
tasks:
- name: install packages
yum:
name:
- ansible
- docker
state: latest
become: yes
So when I run this I get the latest ansible and docker installed.
What I want is for the default value of state to remain latest, so if I just run the playbook the latest versions are downloaded, as it is now. However I want a way for me to override the state for one or both using environment variables(extra vars) when running my playbook from the command line.
So I can choose what version of ansible or docker to install.
Is there a way?
Although I do not think this is the best way to manage software version and that the I consider the following a bit ugly, here is a in-a-nutshell example to get you on track for your experimentation (untested, you may have to adapt a bit):
---
- name: software
hosts: localhost
vars:
ansible_raw_suffix: "-{{ ansible_yum_version | default('') }}"
ansible_suffix: "{{ ansible_yum_version is defined | ternary(ansible_raw_suffix, '') }}"
docker_raw_suffix: "-{{ docker_yum_version | default('') }}"
docker_suffix: "{{ docker_yum_version is defined | ternary(docker_raw_suffix, '') }}"
tasks:
- name: install packages
yum:
name:
- "ansible{{ ansible_suffix }}"
- "docker{{ docker_suffix }}"
state: "{{ yum_state | default('present') }}"
become: yes
With the above, you can:
install to the latest version if first time install
ansible-playbook software.yml
install a specific version of one or both softwares:
ansible-playbook software.yml -e ansible_yum_version=2.9.2 -e docker_yum_version=20.10.6
upgrade to the lastest version
ansible-playbook software.yml -e yum_state=latest
I will let you go on with this to add more features (e.g. allow downgrades) if you feel you still want to walk that path.
(See update2 below for my final word on this.)
I'd like to have ansible generate a list of missing packages by comparing the installed packages with a list of desired packages. (The list of missing packages would then be passed to yum for installation.) It seems like this can be done using some combination of package_facts and the "difference" filter, but I can't come up with the right syntax.
For example, the following gives information about installed packages:
- name: get the rpm package facts
package_facts:
- name: show them
debug: var=ansible_facts.packages
and lists can be compared like this:
- name: Find difference of two lists
gather_facts: no
hosts: localhost
vars:
missing: "{{ input1|difference(input2) }}"
input1:
- john
- mary
input2:
- john
- alice
tasks:
- name: 'Print them out'
debug: var=missing
How can I plug these two techniques together to make a list that's usable later by yum?
Update
Zigarn's answer below gets me very close to what I want (with the caveat that it's "set_fact:" instead of "set_facts:" and "package:" instead of "packages:"). Here's what I'm trying now:
- name: Retrieve the installed packages
package_facts:
- name: Get only list of name of installed packages
set_fact:
installed_packages: "{{ ansible_facts.packages.keys() }}"
- name: Specify list of desired packages
set_fact:
desired_packages:
- emacs
- antiword
- gimp
- junk
- otherstuff
- name: Compute missing packages
set_fact:
missing_packages: "{{ desired_packages | difference (installed_packages) }}"
- name: Print list of missing packages
debug: var=missing_packages
- name: Print list of installed packages
debug: var=installed_packages
- name: Install missing packages
ignore_errors: yes
yum:
skip_broken: yes
name: "{{ item }}"
state: present
loop: "{{ missing_packages }}"
This does indeed try to install missing packages, with the odd exception of "emacs" which isn't currently installed, but also isn't put into the missing_packages list by ansible. This might just be something odd about this particular package, so I'm going to go ahead and mark zigarn's post as the answer but continue to look into the problem.
Regarding why I'm doing it this way, two reasons: First, I'm using a loop and a list instead of passing all the packages to yum at once because I want to avoid the case where a typo in the list of desired packages, or a named package that's not in the repositories, makes yum fail to install any of the listed packages. "skip_broken" doesn't help in these cases. Second, I'm installing a lot of packages (thousands) and yum is very slow, so whittling down the list to just the missing packages can save an hour on the time it takes to update a computer.
Update2
After experimenting with this for a while, I've decided that I'm not confident that I understand the data structure returned by ansible_facts.packages and the way "difference" interprets it, so the following is what I'm going to stick with for now.
- name: Get list of installed packages without arch
command: "rpm -q --qf '%{NAME}\n' -a"
args:
warn: false
register: installed_packages_noarch
- name: Get list of installed packages with arch
command: "rpm -q --qf '%{NAME}.%{ARCH}\n' -a"
args:
warn: false
register: installed_packages_arch
- name: Combine the lists of packages with and without arch
set_fact:
installed_packages: "{{ installed_packages_noarch.stdout_lines + installed_packages_arch.stdout_lines }}"
- name: Install missing packages
ignore_errors: yes
yum:
skip_broken: yes
state: present
name: "{{ item }}"
loop: "{{ desired_packages | difference (installed_packages) }}"
vars:
desired_packages:
- emacs
- gimp
- somethingelse
- anotherthing
Note that I've fallen back to just using rpm to get the list of installed packages, and that I actually have it generate two lists and combine them: A list of bare package names, and a list of packagename.architecture. This lets me specify things like libsomething.i686 or libother.x86_64 in the list of desired packages in cases where I want packages of multiple architectures installed.
First, you need to know that you don't need to compute yourself the list of missing packages as the package module (or platform-specific module yum, apt, ...) is idempotent and will anyway check itself the missing packages to install them.
But, if you need it for another purpose, here is how to do it.
package_facts is populating the ansible_facts.packages variables with a specific structured documented in https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_facts_module.html#returned-facts
If you want to compare it with a simple list of package names, you need to only use the keys of the package_facts. Then you can get the difference between the two lists:
- name: Retrieve the installed packages
package_facts:
- name: Get only list of name of installed packages
set_fact:
installed_packages: "{{ ansible_facts.packages.keys() }}"
- name: Compute missing packages
set_fact:
missing_packages: "{{ package_list_you_want | difference (installed_packages) }}"
- name: Install missing packages
package:
name: "{{ missing_packages }}"
state: present
Does Joomla have a Link to the Latest Version? Wordpress for example has a link to https://wordpress.org/latest.zip , so I know I can always download that zip and get the latest version.
I am working on ansible script to automatically setup a server and install joomla and I need to keep it simple so I would like to spesify a link or api call or something to always get the latest joomla version zip.
Any help appreciated.
David
good question - it is certainly not as easy as wordpress.
You can download the latest version using ansible in the following way:
Download the XML file you mentioned to your local machine:
- name: download latest joomla packages list to local machine
tags: joomlanew
get_url:
url: https://update.joomla.org/core/sts/extension_sts.xml
dest: "{{ role_path }}/scripts/extension_sts.xml"
force: yes
delegate_to: 127.0.0.1
Run a python script to parse this XML to find the latest package zip file:
- name: find latest joomla version
tags: joomlanew
command: python ./latest_joomla.py
args:
chdir: "{{ role_path }}/scripts"
delegate_to: 127.0.0.1
register: xmlresp
You can then use {{ xml.resp.stdout }} in ansible as the input for your download code:
- name: download & unzip joomla
tags: joomlanew
unarchive:
src: "{{ xmlresp.stdout }}"
dest: /home/username/public_html/
remote_src: yes
mode: 0755
FYI the code in th python script to parse the XML would be:
from xml.dom import minidom
xmldoc = minidom.parse('extension_sts.xml')
itemlist = xmldoc.getElementsByTagName('downloadurl')
last=(len(itemlist))-1
print (itemlist[last].firstChild.data)
Ok after some digging I guess the best way to go after this is the way Joomla Core does it:
Get detailsurl out of Last entry out of the Joomla Core Update site (https://update.joomla.org/core/list.xml) - in this instance https://update.joomla.org/core/sts/extension_sts.xml
Get downloadurl out of Last entry out of the url you got in step 1 - in this instance - you need to make sure this is not a PATCH url but UPDATE
You can use this link to download the latest version: https://github.com/joomla/joomla-cms/archive/staging.zip
I am trying to write an ansible playbook which installs some RPMs for me after they have been copied to a known location by a Jenkins job. The problem is, I'm not sure how to get the name of the RPM to install without hard coding it.
Here is what I have now:
- hosts: localhost
roles:
- { role: some_role, artifacts: "{{ rpm_path }}/prefix_.*.rpm" }
In this case, rpm_path would be something like:
"/home/jenkins/workspace/rpm_install/artifacts"
The role that is called in this example handles the yum install part:
- name: Install RPMs
yum: name={{item}} state=present
with_items:
- "{{ artifacts }}"
I'd rather not have to hard code RPM names since they come from Jenkins and they are always different. But is there a way either through the yum module, or when I call the role where the regular expression or glob can be interpreted so the full path (rpm name included) is handed to yum?
You should use with_fileglob insted of with_items, something like
- name: Install RPMs
yum: name="{{item}}" state=present
with_fileglob:
- "{{ artifacts }}"