英文:
How to create a dictionary list of key: value items and append it to the original list in Ansible?
问题
It appears that you're working with Ansible and trying to manipulate a list of user data based on their "ClassType" to add a "path" value to each user. However, there are some issues in your playbook, and you want to achieve a specific output format.
Here's the corrected playbook snippet to achieve the desired output:
- name: If class is full time
  set_fact:
    path: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
  when: item.ClassType == "Full Time"
  loop: "{{ data_list }}"
- name: If employment type is part time
  set_fact:
    path: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
  when: item.ClassType == "Part Time"
  loop: "{{ data_list }}"
- name: If employment type is flexi
  set_fact:
    path: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
  when: item.ClassType == "Flexi"
  loop: "{{ data_list }}"
- name: Append to original userdata list
  set_fact:
    userdata: "{{ userdata + [{'ClassType': item.ClassType, 'FirstName': item.FirstName, 'LastName': item.LastName, 'path': path}] }}"
  loop: "{{ data_list }}"
This playbook will correctly set the "path" value based on "ClassType" and append the modified user data to the "userdata" list in the desired format.
英文:
I want to create a new dictionary list, 'path: value' to be looped for each user and appended under each item in the user list. The end result would look something like below:
        "userdata": [
            {
                "ClassType": "Full Time",
                "FirstName": "Grace",
                "LastName": "Higgins",
                "path": "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
            },
            {
                "ClassType": "Part Time",
                "FirstName": "Robert",
                "LastName": "Miller",
                "path": "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
            },
            {
                "ClassType": "Flexi",
                "FirstName": "Jeffrey",
                "LastName": "Keller",
                "path": "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
            }
        ]
The values for path is dependent on the value of the ClassType.
However, when I tried to append the new path: value dictionary list to the original userdata list, the 'path: item.path' is in the wrong layout and also has the wrong value:
TASK [Read_csv_creation : Append to original userdata list] *******************************************************************************************
ok: [localhost] => (item={'FirstName': '}) => {
    "ansible_facts": {
        "userdata": [
            {
                "ClassType": "Full Time",
                "FirstName": "Grace",
                "LastName": "Higgins"
            },
            {
                "ClassType": "Part Time",
                "FirstName": "Robert",
                "LastName": "Miller"
            },
            {
                "ClassType": "Flexi",
                "FirstName": "Jeffrey",
                "LastName": "Keller"
            },
            "path: item.path"
        ]
    },
How can I get the final output as shown below?
        "userdata": [
            {
                "ClassType": "Full Time",
                "FirstName": "Grace",
                "LastName": "Higgins",
                "path": "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
            },
            {
                "ClassType": "Part Time",
                "FirstName": "Robert",
                "LastName": "Miller",
                "path": "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
            },
            {
                "ClassType": "Flexi",
                "FirstName": "Jeffrey",
                "LastName": "Keller",
                "path": "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
            }
        ]
Below is my tasks file. The variable 'data_list' would be my original list.
    - name: Read input file
      read_csv:
        path: /var/lib/awx/projects/file/creation.csv
        key: FirstName 
        fieldnames: FirstName,LastName,ClassType
        delimiter: ','
      register: userdata
    - name: Initialise data list
      set_fact:
        data_list: []
    - name: Extract the list
      set_fact:
        data_list: "{{ data_list + [{ 'FirstName': item.FirstName, 'LastName': item.LastName, 'ClassType': item.ClassType }] }}"
      loop: "{{ userdata | community.general.json_query('dict.[*][0]') }}"
    - name: Set fact for data_list
      set_fact: 
        data_list: "{{ data_list[1:] }}"
    - name: If class is full time
      set_fact:
        path: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
      when: item.ClassType == "Full Time"  
      loop: "{{ data_list  }}"   
    - name: If employment type is part time
      set_fact:
        path: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
      when: item.ClassType == "Part Time"  
      loop: "{{ data_list  }}"   
    - name: If employment type is flexi
      set_fact:
        path: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
      when: item.ClassType == "Flexi"  
      loop: "{{ data_list  }}"   
    - name: Append to original userdata list
      set_fact:
        userdata: "{{ data_list + ['path: item.path'] }}"
      loop: "{{ data_list  }}" 
The job run output is as below. I have omitted some of it as it is very lengthy:
TASK [Read_csv_creation : Initialise data list] *******************************************************************************************************
ok: [localhost] => {
    "ansible_facts": {
        "data_list": []
    },
    "changed": false
}
TASK [Read_csv_creation : Read input file] ************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "dict": {
        " FirstName ": {
            "ClassType": " Employment Type ",
            "FirstName": " FirstName ",
            "LastName": " Last Name "
        },
        "Grace": {
            "ClassType": "Sub Contract",
            "FirstName": "Grace",
            "LastName": "Higgins"
        },
        "Robert": {
            "ClassType": "Contract",
            "FirstName": "Robert",
            "LastName": "Miller"
        },
        "Jeffrey": {
            "ClassType": "Full Time",
            "FirstName": "Jeffrey",
            "LastName": "Keller"
        }
    },
    "invocation": {
        "module_args": {
            "delimiter": ",",
            "dialect": "excel",
            "fieldnames": [
                "FirstName",
                "LastName",
                "ClassType"
            ],
            "key": "FirstName",
            "path": "/var/lib/awx/projects/file/creation.csv",
            "skipinitialspace": null,
            "strict": null,
            "unique": true
        }
    },
    "list": []
}
TASK [Read_csv_creation : Extract the list] ***********************************************************************************************************
ok: [localhost] => (item={'FirstName': ' FirstName ', 'LastName': ' Last Name ', 'ClassType': ' Class Type '}) => {
    "ansible_facts": {
        "data_list": [
            {
                "ClassType": " Class Type ",
                "FirstName": " FirstName ",
                "LastName": " Last Name "
            }
        ]
    },
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "ClassType": " Class Type ",
        "FirstName": " FirstName ",
        "LastName": " Last Name ",
    }
}
ok: [localhost] => (item={'FirstName': '}) => {
    "ansible_facts": {
        "data_list": [
            {
                "ClassType": " Class Type ",
                "FirstName": " FirstName ",
                "LastName": " Last Name ",
            }
        ]
    },
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "ClassType": "Class Time",
        "FirstName": "Grace",
        "LastName": "Higgins"
    }
}
TASK [Read_csv_creation : Set fact for data_list] *****************************************************************************************************
ok: [localhost] => {
    "ansible_facts": {
        "data_list": [
            {
                "ClassType": "Full Time",
                "FirstName": "Grace",
                "LastName": "Higgins"
            },
            {
                "ClassType": "Part Time",
                "FirstName": "Robert",
                "LastName": "Miller"
            },
            {
                "EmploymentType": "Flexi",
                "FirstName": "Jeffrey",
                "LastName": "Keller"
            }
        ]
    },
    "changed": false
}
TASK [Read_csv_creation : If class type is full time] *******************************************************************************************
ok: [localhost] => (item={'FirstName': 'Grace', 'LastName': 'Higgins', 'ClassType': 'Full Time'}) => {
    "ansible_facts": {
        "path": "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
    },
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "ClassType": "Full Time",
        "FirstName": "Grace",
        "LastName": "Higgins"
    }
}
skipping: [localhost] => (item={'FirstName': 'Robert', 'LastName': 'Miller', 'ClassType': 'Part Time'})  => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "EmploymentType": "Contract",
        "FirstName": "Robert",
        "LastName": "Miller"
    },
    "skip_reason": "Conditional result was False"
}
skipping: [localhost] => (item={'FirstName': .....
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        .......
        "ClassType": "Flexi"
    },
    "skip_reason": "Conditional result was False"
}
答案1
得分: 1
给定用于测试的文件 mre
shell> cat /tmp/creation.csv 
Grace,Higgins,Full Time
Robert,Miller,Part Time
Jeffrey,Keller,Flexi
读取 CSV 文件
    - read_csv:
        path: /tmp/creation.csv
        # key: FirstName 
        fieldnames: FirstName,LastName,ClassType
        delimiter: ','
      register: userdata
当你省略参数 key 时,你会得到列表而不会'提取'
  userdata.list:
  - ClassType: Full Time
    FirstName: Grace
    LastName: Higgins
  - ClassType: Part Time
    FirstName: Robert
    LastName: Miller
  - ClassType: Flexi
    FirstName: Jeffrey
    LastName: Keller
要添加属性 path,创建字典 class_path
  class_path:
    Full Time: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
    Part Time: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
    Flexi: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
从列表 map 属性 ClassType,提取 path,并创建哈希列表
  path: "{{ userdata.list|map(attribute='ClassType')|
                          map('extract', class_path)|
                          map('community.general.dict_kv', 'path') }}"
得到
  path:
  - path: OU=fulltime,OU=Test OU,DC=localdemo,DC=local
  - path: OU=parttime,OU=Test OU,DC=localdemo,DC=local
  - path: OU=flexi,OU=Test OU,DC=localdemo,DC=local
zip 列表并 combine 项目。你必须使用 set_facts 因为变量 userdata 已经在 registered vars 中使用过了
    - set_fact:
        userdata: "{{ userdata.list|zip(path)|map('combine') }}"
得到你想要的结果
  userdata:
  - ClassType: Full Time
    FirstName: Grace
    LastName: Higgins
    path: OU=fulltime,OU=Test OU,DC=localdemo,DC=local
  - ClassType: Part Time
    FirstName: Robert
    LastName: Miller
    path: OU=parttime,OU=Test OU,DC=localdemo,DC=local
  - ClassType: Flexi
    FirstName: Jeffrey
    LastName: Keller
    path: OU=flexi,OU=Test OU,DC=localdemo,DC=local
用于测试的完整 playbook 示例
- hosts: localhost
  vars:
    class_path:
      Full Time: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
      Part Time: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
      Flexi: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
    path: "{{ userdata.list|map(attribute='ClassType')|
                            map('extract', class_path)|
                            map('community.general.dict_kv', 'path') }}"
  tasks:
    - read_csv:
        path: /tmp/creation.csv
        # key: FirstName 
        fieldnames: FirstName,LastName,ClassType
        delimiter: ','
      register: userdata
    - debug:
        var: userdata.list
    - debug:
        var: path
    - set_fact:
        userdata: "{{ userdata.list|zip(path)|map('combine') }}"
    - debug:
        var: userdata
英文:
Given the file for mre testing
shell> cat /tmp/creation.csv 
Grace,Higgins,Full Time
Robert,Miller,Part Time
Jeffrey,Keller,Flexi
read the CSV file
    - read_csv:
        path: /tmp/creation.csv
        # key: FirstName 
        fieldnames: FirstName,LastName,ClassType
        delimiter: ','
      register: userdata
When you omit the parameter key you get the list without 'extracting'
  userdata.list:
  - ClassType: Full Time
    FirstName: Grace
    LastName: Higgins
  - ClassType: Part Time
    FirstName: Robert
    LastName: Miller
  - ClassType: Flexi
    FirstName: Jeffrey
    LastName: Keller
To add the attribute path create the dictionary class_path
  class_path:
    Full Time: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
    Part Time: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
    Flexi: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
From the list map the attribute ClassType, extract the path, and create the list of the hashes
  path: "{{ userdata.list|map(attribute='ClassType')|
                          map('extract', class_path)|
                          map('community.general.dict_kv', 'path') }}"
gives
  path:
  - path: OU=fulltime,OU=Test OU,DC=localdemo,DC=local
  - path: OU=parttime,OU=Test OU,DC=localdemo,DC=local
  - path: OU=flexi,OU=Test OU,DC=localdemo,DC=local
zip the lists and combine the items. You have to use set_facts because the variable userdata has already been used in registered vars
    - set_fact:
        userdata: "{{ userdata.list|zip(path)|map('combine') }}"
gives what you want
  userdata:
  - ClassType: Full Time
    FirstName: Grace
    LastName: Higgins
    path: OU=fulltime,OU=Test OU,DC=localdemo,DC=local
  - ClassType: Part Time
    FirstName: Robert
    LastName: Miller
    path: OU=parttime,OU=Test OU,DC=localdemo,DC=local
  - ClassType: Flexi
    FirstName: Jeffrey
    LastName: Keller
    path: OU=flexi,OU=Test OU,DC=localdemo,DC=local
<hr>
<sup>
Example of a complete playbook for testing
- hosts: localhost
  vars:
    class_path:
      Full Time: "OU=fulltime,OU=Test OU,DC=localdemo,DC=local"
      Part Time: "OU=parttime,OU=Test OU,DC=localdemo,DC=local"
      Flexi: "OU=flexi,OU=Test OU,DC=localdemo,DC=local"
    path: "{{ userdata.list|map(attribute='ClassType')|
                            map('extract', class_path)|
                            map('community.general.dict_kv', 'path') }}"
  tasks:
    - read_csv:
        path: /tmp/creation.csv
        # key: FirstName 
        fieldnames: FirstName,LastName,ClassType
        delimiter: ','
      register: userdata
    - debug:
        var: userdata.list
    - debug:
        var: path
    - set_fact:
        userdata: "{{ userdata.list|zip(path)|map('combine') }}"
    - debug:
        var: userdata
</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论