如何在Ansible中对版本号进行排序

7
我正在编写一个Ansible playbook,其中我想在需要升级软件时备份数据库。为此,我希望比较可用的最高版本号和已安装版本号。如果最新版本高于已安装版本,则备份数据库。
然而问题在于,我无法找到一种好的方法在Ansible中对版本号进行排序。标准的sort过滤器按字符串而非数字/版本进行排序。
这是我目前正在做的事情:
- name: Get package version
  yum:
    list: package
  register: software_version

- name: Read which version is installed and available
  set_fact:
    software_version_installed: "{{ software_version | json_query(\"results[?yumstate=='installed'].version\") | sort | last }}"
    software_version_available: "{{ software_version | json_query(\"results[?yumstate=='available'].version\") | sort | last }}"

- name: Backup old database file on remote host
  copy:
    src: "{{ software.database_path }}"
    dest: "{{ software.database_path }}_{{ ansible_date_time.date }}_v{{ software_version_installed }}"
    remote_src: yes
  when: software_version_installed is version(software_version_available, "<")

上述playbook的工作是可靠的,只要版本号不超过10(例如1.2.3,但不是1.10.1),因为它是按照字符串排序的。当版本号需要排序例如1.2.3和1.10.1时,它将把1.2.3作为最新版本。
为了展示此问题:
- name: Read which version is installed and available
  set_fact:
    software_versions: [ "2.5.0", "2.9.0", "2.10.0", "2.11.0" ]

- name: Debug
  debug:
    var: software_versions | sort

TASK [grafana : Debug]**********************************
ok: [localhost] => {
    "software_versions | sort": [
        "2.10.0",
        "2.11.0",
        "2.5.0",
        "2.9.0"
    ]
}

有没有人知道在Ansible中排序版本号的好方法?

4个回答

5

问:有人知道在Ansible中对版本号进行排序的好方法吗?

答:使用filter_plugin插件。例如,可以使用以下过滤器:

shell> cat filter_plugins/version_sort.py
from distutils.version import LooseVersion

def version_sort(l):
    return sorted(l, key=LooseVersion)

class FilterModule(object):

    def filters(self):
        return {
            'version_sort' : version_sort
            }

with the playbook

shell> cat test-versions.yml
- name: Sort versions
  hosts: localhost

  vars:
    versions:
      - "0.1.0"
      - "0.1.5"
      - "0.11.11"
      - "0.9.11"
      - "0.9.3"
      - "0.10.2"
      - "0.6.1"
      - "0.6.0"
      - "0.11.0"
      - "0.6.5"
    
  tasks:
    - debug:
        msg: "{{ versions|version_sort }}"

提供

    "msg": [
        "0.1.0", 
        "0.1.5", 
        "0.6.0", 
        "0.6.1", 
        "0.6.5", 
        "0.9.3", 
        "0.9.11", 
        "0.10.2", 
        "0.11.0", 
        "0.11.11"
    ]

为了方便起见,筛选器可以在Github上找到ansible-plugins


版本比较可以迭代列表并比较项目。看下面的例子:

shell> cat test-versions.yml
- hosts: localhost
    vars:
      version_installed: "1.10.1"
      versions:
        - "1.1.3"
        - "1.2.3"
        - "1.7.5"
        - "1.10.7"
    tasks:
      - debug: msg="{{ item }} is newer than {{ version_installed }}"
        loop: "{{ versions }}"
        when: item is version(version_installed, '>')

shell> ansible-playbook test-versions.yml | grep msg
"msg": "1.10.7 is newer than 1.10.1"

2
这不是我要找的东西。版本过滤器运作得很好,但排序并不是那样工作的。排序将变量视为字符串。 - ReDNaX
1
@ReDNaX。Ansible如何区分“版本”列表和“字符串”列表? - Vladimir Botka

5

3
现在有另一种解决方法。不是对版本进行排序,而是将当前版本与所有可用版本进行比较。
  • 我首先将更新变量设置为false
  • 接下来,我将已安装的版本与每个可用版本进行比较
  • 如果已安装的版本大于当前版本,则将更新变量设置为true

只有当更新变量为true时,才会执行执行备份任务。

- name: Get package version
  yum:
    list: package
  register: software_version

- name: Read which version is installed and available
  set_fact:
    software_update: false
    software_version_installed: "{{ software_version | json_query(\"results[?yumstate=='installed'].version\") | last }}"
    software_version_available: "{{ software_version | json_query(\"results[?yumstate=='available'].version\") }}"

- name: Check if upgrade is needed
  set_fact:
    software_update: true
  when: software_version_installed is version(item, "<")
  with_items: "{{ software_version_available }}"

- name: Backup old database file on remote host
  copy:
    src: "{{ software.database_path }}"
    dest: "{{ software.database_path }}_{{ ansible_date_time.date }}_v{{ software_version_installed }}"
    remote_src: yes
  when: software_update

2

目前被接受的答案提供了一个非常好的解决方案,使用了filter_plugin。不幸的是,它使用的Python包distutils似乎已经被弃用。一些搜索结果让我找到了packaging包,它提供了类似的版本类。这里是一个更新后的filter_plugin,它不使用distutils

from packaging.version import Version

def version_sort(l):
    return sorted(l, key=Version)

class FilterModule(object):

    def filters(self):
        return {
            'version_sort': version_sort
        }

对我们来说,它的运行情况很好,但我不想承诺在每种情况下其行为都会完全相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接