如何使用 Ansible 官方模块更高效地向 VMware VM 主机添加磁盘?

Posted

技术标签:

【中文标题】如何使用 Ansible 官方模块更高效地向 VMware VM 主机添加磁盘?【英文标题】:How to add disk to VMware's VM host more efficiently by using Ansible offical module? 【发布时间】:2020-03-06 10:00:08 【问题描述】:

环境

操作系统版本:CentOS 7.5 Ansible 版本:2.8.4 和 python 2.7.5 ESXi 和 VCenter 版本:6.5.2

目的

我已经完成了我的代码,但我想知道是否有更好的方法通过 Ansible 官方模块将磁盘添加到 VM 主机。

我的方法

vmware_guest_disk_facts 的返回消息

TASK [get VM info] *******************************************************************************************************
ok: [TFGH0001.TFGH.COM -> localhost] => changed=false 
  guest_disk_facts:
    '0':
      backing_datastore: VCENTER01_DS01
      backing_eagerlyscrub: false
      backing_filename: '[VCENTER01_DS01] TFGH0001/TFGH0001-000001.vmdk'
      backing_thinprovisioned: false
      backing_type: FlatVer2
      backing_uuid: 6000C294-d22d-06a9-c89b-119e13fffe33
      backing_writethrough: false
      capacity_in_bytes: 32212254720
      capacity_in_kb: 31457280
      controller_key: 1000
      key: 2000
      label: Hard disk 1
      summary: 31,457,280 KB
      unit_number: 0
    '1':
      backing_datastore: VCENTER01_DS01
      backing_eagerlyscrub: false
      backing_filename: '[VCENTER01_DS01] TFGH0001/TFGH0001_3.vmdk'
      backing_thinprovisioned: false
      backing_type: FlatVer2
      backing_uuid: 6000C293-ae37-30f6-b684-d5b2416ff2f8
      backing_writethrough: false
      capacity_in_bytes: 10737418240
      capacity_in_kb: 10485760
      controller_key: 1000
      key: 2001
      label: Hard disk 2
      summary: 10,485,760 KB
      unit_number: 1

我的剧本

---
  - hosts: all
    remote_user: kai
    become: yes
    become_user: root
    become_method: sudo
    gather_facts: true

    vars:
      vcenter_host: "VCENTER01"
      vcenter_username: "TFGH\\kaikudou"
      vcenter_password: xxxxxxxxx
      target_host: "TFGH0001"

    tasks:

    - name: get VM info
      vmware_guest_disk_facts:
        hostname: " vcenter_host "
        username: " vcenter_username "
        password: " vcenter_password "
        validate_certs: False
        datacenter: " vcenter_host "
        name: " target_host "
      delegate_to: localhost
      register: vm_disk_info

    # - name: show vm_disk_info
    #   debug:
    #     msg: " vm_disk_info.guest_disk_facts['0'].backing_datastore "

    - name: Set empty list to store varialbe
      set_fact:
        all_scsi_number_list: []  # A list to store all scsi device number
        scsi_0: []  # A list to store scsi 0's device for counting the quantity
        scsi_1: []  # A list to store scsi 1's device for counting the quantity
        scsi_2: []  # A list to store scsi 2's device for counting the quantity
        scsi_3: []  # A list to store scsi 3's device for counting the quantity
        all_unit_number_list: []  # A list to store the device number from scsi controller   

    - name: Set variable of datastore
      set_fact:
        host_datastore: " vm_disk_info.guest_disk_facts['0'].backing_datastore "

    - name: Store scsi_number into all_scsi_number_list
      set_fact:
        all_scsi_number_list: " all_scsi_number_list + [vm_disk_info.guest_disk_facts[item].controller_key] "
      loop: " vm_disk_info.guest_disk_facts.keys() "

    - name: Find out scsi_controller 0 and store into scsi_0
      set_fact:
        scsi_0 : " scsi_0 + [item] "
      loop: " all_scsi_number_list "
      when: item == 1000

    - name: Find out the scsi_controller 1 and store into scsi_1
      set_fact:
        scsi_1 : " scsi_1 + [item] "
      loop: " all_scsi_number_list "
      when: item == 1001

    - name: Find out the scsi_controller 2 and store into scsi_2
      set_fact:
        scsi_2 : " scsi_2 + [item] "
      loop: " all_scsi_number_list "
      when: item == 1002

    - name: Find out the scsi_controller 3 and store into scsi_3
      set_fact:
        scsi_3 : " scsi_3 + [item] "
      loop: " all_scsi_number_list "
      when: item == 1003

    - name: Calcualte the quantity of scsi_*
      set_fact:
        scsi_0_number: " scsi_0 | length "
        scsi_1_number: " scsi_1 | length "
        scsi_2_number: " scsi_2 | length "
        scsi_3_number: " scsi_3 | length "

    - name: Verify the scsi controller's number because snapshot will also cost the device so less than 7 to prevent
      set_fact:
        scsi_number: " item.src "
      loop:
        -  src: "0", when: " (scsi_0_number <= '6' and scsi_0_number != '0') or (scsi_0_number == '0') " 
        -  src: "1", when: " (scsi_1_number <= '6' and scsi_1_number != '0') or (scsi_1_number == '0') " 
        -  src: "2", when: " (scsi_2_number <= '6' and scsi_2_number != '0') or (scsi_2_number == '0') " 
        -  src: "3", when: " (scsi_3_number <= '6' and scsi_3_number != '0') or (scsi_3_number == '0') " 
      when: item.when

    # - name: Show scsi_number which we get
    #   debug:
    #     msg: " scsi_number "

    - name: Change the scsi_number we obtained to 4 digit number
      set_fact:
        scsi_digit_number: " item.src | int "
      loop:
        -  src: "1000", when: " scsi_number == '0' " 
        -  src: "1001", when: " scsi_number == '1' " 
        -  src: "1002", when: " scsi_number == '2' " 
        -  src: "1003", when: " scsi_number == '3' " 
      when: item.when

    # - name: Show scsi_digit_number which we get
    #   debug:
    #     msg: " scsi_digit_number "

    - name: Check the number of devices from the scci_number we obtained
      set_fact:
        all_unit_number_list: " all_unit_number_list + [vm_disk_info.guest_disk_facts[item].unit_number] "
      loop: " vm_disk_info.guest_disk_facts.keys() "
      when: vm_disk_info.guest_disk_facts[item].controller_key == scsi_digit_number | int

    # - name: Show all_unit_number_list which we get
    #   debug:
    #     msg: " all_unit_number_list | length | type_debug "

    - name: Find the max number in all_unit_number_list then plus 1 to add new disk
      set_fact:
        disk_number: " all_unit_number_list | max + 1 "
      ignore_errors: yes

    - name: If we have to add new scsi controller then the all_unit_number_list will be empty list, so we need to prevent it failed
      set_fact:
        disk_number: 0
      when: all_unit_number_list | length == 0

    - name: add disk
      vmware_guest_disk:
        hostname: " vcenter_host "
        username: " vcenter_username "
        password: " vcenter_password "
        validate_certs: False
        datacenter: " vcenter_host "
        name: " target_host "
        disk:
          - size_gb: 2
            type: thin
            state: present
            datastore: " host_datastore "
            # autoselect_datastore: True
            scsi_controller: " scsi_number | int "
            unit_number: " disk_number | int "
            scsi_type: 'paravirtual'

根据VMware offical document 1 VM 主机只能有 4 个 scsi 控制器,每个可以连接 15 个设备。所以我必须写很多条件来防止它。

奇怪的是,从VCenter添加硬盘时,不会触发硬盘数量超过7的问题。但是,Ansible的模块在unit_number超过7时无法增加硬盘,必须换另一个SCSI控制器。

除了这样做,还有其他更好的方法吗?或者我可以参考和研究的东西?

感谢您的帮助和建议!

【问题讨论】:

【参考方案1】:

我试着重新表述一下这个问题。这远非完美,因为它不会考虑编号中的“漏洞”(即从序列中删除的磁盘或控制器)。但我认为您当前的实施也没有。我的应该使用更少的变量分配,如果没有更多可用的插槽,它将优雅地失败。

从客户机获取磁盘信息(例如粘贴在 var 中)。 根据信息,循环配置控制器的数量。对于每个控制器,记录其密钥、数量、设备数量和最大配置的设备 ID。 从列表中提取具有可用插槽的第一个控制器 如果失败,则创建一个空的控制器信息,增加 scsi 设备号;如果达到最大值,则失败。

注意:我使用了 json_query,所以你需要在你的 ansible 控制器机器上pip(3) install jmespath 来运行示例。

剧本:

---
- name: My take to your vmware disk management question
  hosts: localhost
  gather_facts: false

  vars:
    # Info copied from your example.
    vcenter_host: "VCENTER01"
    vcenter_username: "TFGH\\kaikudou"
    vcenter_password: xxxxxxxxx
    target_host: "TFGH0001"

    # Max number of devices per scsi controller
    scsi_max_devices: 15
    # Max id for scsi controllers
    scsi_max_controller_id: 3

    # Calling the two following vars before getting facts will fail
    # but since we don't need to loop to get them they can be statically
    # declared in playbook vars
    scsi_controller_unique_keys: >-
      
        vm_disk_info.guest_disk_facts
        | dict2items
        | map(attribute='value.controller_key')
        | list
        | unique
        | sort
      
    host_datastore: " vm_disk_info.guest_disk_facts['0'].backing_datastore "

    # Your example data to play with (minified, single line)
    # To take from module call return IRL.
    vm_disk_info: "guest_disk_facts":"0":"backing_datastore":"VCENTER01_DS01","backing_eagerlyscrub":false,"backing_filename":"[VCENTER01_DS01] TCB0945/TFGH0001-000001.vmdk","backing_thinprovisioned":false,"backing_type":"FlatVer2","backing_uuid":"6000C294-d22d-06a9-c89b-119e13fffe33","backing_writethrough":false,"capacity_in_bytes":32212254720,"capacity_in_kb":31457280,"controller_key":1000,"key":2000,"label":"Hard disk 1","summary":"31,457,280 KB","unit_number":0,"1":"backing_datastore":"VCENTER01_DS01","backing_eagerlyscrub":false,"backing_filename":"[VCENTER01_DS01] TFGH0001/TFGH0001_3.vmdk","backing_thinprovisioned":false,"backing_type":"FlatVer2","backing_uuid":"6000C293-ae37-30f6-b684-d5b2416ff2f8","backing_writethrough":false,"capacity_in_bytes":10737418240,"capacity_in_kb":10485760,"controller_key":1000,"key":2001,"label":"Hard disk 2","summary":"10,485,760 KB","unit_number":1

  tasks:

    - name: Create a list holding all the info we need for each existing controller
      vars:
        scsi_controller_devices_query: >-
          [?to_string(value.controller_key)==' controller_key '].value.unit_number[]
        scsi_controller_devices: >-
          
            vm_disk_info.guest_disk_facts |
            dict2items |
            json_query(scsi_controller_devices_query)
          
        # Construct object directly as json so that we retain int type for further comparison usage.
        current_controller: >-
          
            "controller_number":  controller_number | int ,
            "controller_key":  controller_key | int ,
            "number_of_devices":  scsi_controller_devices | length | int ,
            "max_unit_number":  scsi_controller_devices | max | int ,
          
      set_fact:
        scsi_controllers_info: " scsi_controllers_info | default([]) + [current_controller] "
      loop: " scsi_controller_unique_keys "
      loop_control:
        loop_var: controller_key
        index_var: controller_number

    - block:

        # Note: This was already sorted when we got controllers list in our first loop
        - name: "Extract first controller having less than  scsi_max_devices  disks"
          set_fact:
            scsi_controller: >-
              
                (
                  scsi_controllers_info |
                  selectattr('number_of_devices', '<', scsi_max_devices) |
                  list
                ).0
              

      rescue:

        - name: Fail if we cannot add an other controller id
          # i.e.controllernumber of our last element in list is equal (or greater for tests) that scsi_max_controller_id
          fail:
            msg: All scsi controllers are full, disk cannot be added.
          when: scsi_controllers_info[-1].controller_number >= scsi_max_controller_id

        - name: Return an empty controller with incremented id
          set_fact:
            scsi_controller: >-
              
                "controller_number":  scsi_controllers_info[-1].controller_number + 1 | int ,
                "controller_key":  scsi_controllers_info[-1].controller_key + 1 | int ,
                "number_of_devices": 0,
                "max_unit_number": -1,
              


    - name: Show what the call to vmware_guest_disk would look like
      vars:
        vmware_guest_disk_params:
          hostname: " vcenter_host "
          username: " vcenter_username "
          password: " vcenter_password "
          validate_certs: False
          datacenter: " vcenter_host "
          name: " target_host "
          disk:
            - size_gb: 2
              type: thin
              state: present
              datastore: " host_datastore "
              # autoselect_datastore: True
              scsi_controller: " scsi_controller.controller_number "
              unit_number: " scsi_controller.max_unit_number + 1 "
              scsi_type: 'paravirtual'
      debug:
        msg: " vmware_guest_disk_params "

【讨论】:

如你所说,我的playbook确实有一个bug,就是如果unit_number的个数增加了,但是没有达到7,那么就会报错,但是我不能暂时想一个更好的解决方案。 非常感谢您的指导和建议。我以为我无法处理剧本中的太多消息。再次感谢您为我的学习指明方向。

以上是关于如何使用 Ansible 官方模块更高效地向 VMware VM 主机添加磁盘?的主要内容,如果未能解决你的问题,请参考以下文章

在 azure vm 上运行 ansible 的库错误

Ansible-基础

如何更高效的使用MVP以及官方MVP架构解析

如何高效地向Redis写入大量的数据

如何高效地向Redis写入大量的数据

Ansible5:常用模块