Как настроить автоматическую сборку образов ВМ для VMware Cloud Director — компактное руководство

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр! Меня зовут Роман Петров, занимаюсь разработкой продуктов для управления учетными данными в SberCloud. В рамках одного проекта мне потребовалось автоматизировать сборку виртуальных машин под VMware Cloud Director.

Можно было решить эту задачу с помощью инструментов VMware: Fusion, Workstation или Player, но они требуют покупки лицензий, а последний еще и установки не обновляемого пакета VMware VIX API в случае GNU\Linux. Я выбрал альтернативный путь и построил необходимый пайплайн на базе опенсорсного VirtualBox.

Под катом — кратко о том, как я это сделал.

Прежде чем мы начнем

Прежде чем переходить к делу, немного расскажу о том, почему вообще решил заняться этим вопросом, и познакомлю вас с программным стеком, который мы будем использовать. У меня был шаблон для Packer с операционной системой Ubuntu Server 18.04 и перечнем дополнительного ПО для предустановки. Изначально сборка этого образа проходила в среде Fusion. Для инсталляции операционной системы использовал файл ответов preseed.cfg. Он содержит сценарии ответов на все вопросы инсталлятора (для автоматизации процесса).

Я решил оставить Packer, но заменить Fusion на VirtualBox. Выбор в пользу open source решения был сделан по причине того, что оно распространяется на бесплатной основе, поддерживает большое количество хостовых ОС и экспорт образов в форматах OVF и OVA. Кроме того, VirtualBox предоставляет репозитории для GNU/Linux, позволяющие автоматически обновлять пакеты операционных систем. К слову, аналогичный репозиторий предлагают и разработчики Packer.

Начало работы

В качестве хоста для развертки окружения, в котором будет запускаться CI-пайплайн, выбрал виртуальную машину с Ubuntu Server 20.04. На неё установил утилиты Packer и VirtualBox. Не буду подробно рассказывать, как это сделать, так как все необходимые инструкции вы можете найти в официальной документации этих инструментов [раз, два].

Следующим шагом стала подготовка шаблона для Packer. Packer включает в себя сборщики для разных окружений, в том числе для VirtualBox — virtualbox-iso. Написал шаблон, запустил сборку образа ВМ. Осталось загрузить этот образ в библиотеку Cloud Director. Обнаружил, что Cloud Director отказывается его импортировать. Это основная трудность, которая заняла много времени.

В процессе поиска решения проблемы я наткнулся на статью иностранного коллеги. Он заметил, что из-за несовместимости в системных типах и поддержке аппаратного обеспечения (разная трактовка открытого стандарта), экспорт такого образа в VMware Cloud Director требует дополнительной настройки — какой именно, покажу далее.

Настраиваем шаблон Packer

В шаблон Packer внесем следующие изменения:

  • Настроим экспорт образа в формате OVF. Для этого в конфигурационном файле атрибуту format присвоим значение ovf. Эта операция позволит нам получить на выходе два отдельных файла: XML-документ с описанием ВМ и образ жесткого диска. Так, нам будет проще настраивать нашу систему в дальнейшем. Отмечу, что для своего кейса я не создаю дополнительные диски и не включаю iso-образы в состав виртуальной машины. Расширить дисковое пространство или добавить дополнительные накопители можно непосредственно при развертке виртуальной машины.

  • Укажем SCSI в качестве контроллера жестких дисков. Соответствующее значение (scsi) необходимо прописать в атрибуте hard_drive_interface. В VirtualBox будет эмулироваться контроллер LsiLogic, что является плюсом, поскольку контроллер этого типа поддерживается в Cloud Director.

  • В качестве прошивки выберем BIOS, а в качестве видеоконтроллера — vmsvga (VMware SVGA). Атрибуты, которые нам нужны, — это firmware и gfx_controller.

Финальный вариант шаблона для Packer — main.pkr.hcl — выглядит так:

source "virtualbox-iso" "ubuntu-18_04-amd64" {
  vm_name                  = "ubuntu-18.04-amd64"
  guest_os_type            = "Ubuntu_64"
  nested_virt              = false
  headless                 = true
  keep_registered          = false
  guest_additions_mode     = "disable"
  cpus                     = 2
  memory                   = 4096
  chipset                  = "piix3"
  firmware                 = "bios"
  rtc_time_base            = "UTC"
  disk_size                = 32768
  hard_drive_interface     = "scsi"
  hard_drive_discard       = true
  hard_drive_nonrotational = true
  nic_type                 = "82540EM"
  gfx_controller           = "vmsvga"
  gfx_vram_size            = "16"
  gfx_accelerate_3d        = false
  sound                    = "none"
  iso_url                  = "http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-18.04.5-server-amd64.iso"
  iso_checksum             = "sha256:8c5fc24894394035402f66f3824beb7234b757dd2b5531379cb310cedfdf0996"
  boot_command             = ["<esc><wait>",
                              "<esc><wait>",
                              "<enter><wait>",
                              "/install/vmlinuz auto=true priority=critical fb=false initrd=/install/initrd.gz grub-installer/bootdev=/dev/sda preseed/file=/floppy/preseed.cfg -- <enter>"]
  floppy_files             = ["./preseed/preseed.cfg"]
  shutdown_command         = "echo '${var.password}' | sudo -S shutdown -P now"
  post_shutdown_delay      = "1m"
  output_directory         = "./output"
  format                   = "ovf"
  bundle_iso               = false
  communicator             = "ssh"
  ssh_username             = var.username
  ssh_password             = var.password
  ssh_timeout              = "20m"
}



build {
  sources = ["sources.virtualbox-iso.ubuntu-18_04-amd64"]

  # ssh
  provisioner "shell" {
    inline           = ["sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config",
                        "sudo systemctl stop sshd",
                        "sudo rm /etc/ssh/ssh_host_*"]
  }

  # ./packer-manifest.json
  post-processor "manifest" {
      output = "packer-manifest.json"
  }
}

Более подробно о том, для чего нужны остальные атрибуты, вы можете узнать в документации на сборщик virtualbox-iso. Здесь я дополнительно хочу выделить лишь один из них — headless, отвечающий за сборку в фоновом режиме. Этот флажок стоит установить в состояние false, чтобы включить графический интерфейс. Это упростит отладку.

Теперь, если мы запустим Packer с нашими настройками, он сгенерирует три файла:

  • Образ жесткого диска: ubuntu-18.04-amd64-disk001.vmdk;

  • XML-файл с описанием виртуальной машины: ubuntu-18.04-amd64.ovf;

  • JSON-файл со списком созданных артефактов: packer-manifest.json.

Последний файл записан благодаря блоку:

# ./packer-manifest.json
post-processor "manifest" {
    output = "packer-manifest.json"
}

Файл packer-manifest.json будет иметь следующее содержимое:

{
  "builds": [
    {
      "name": "ubuntu-18_04-amd64",
      "builder_type": "virtualbox-iso",
      "build_time": 1629043596,
      "files": [
        {
          "name": "output/ubuntu-18.04-amd64-disk001.vmdk",
          "size": 916132352
        },
        {
          "name": "output/ubuntu-18.04-amd64.ovf",
          "size": 6771
        }
      ],
      "artifact_id": "VM",
      "packer_run_uuid": "b5fbae2b-59e7-fd6f-4154-cae16befd459",
      "custom_data": null
    }
  ],
  "last_run_uuid": "b5fbae2b-59e7-fd6f-4154-cae16befd459"
}

Этот файл поможет нам найти результат сборки образа ВМ (ubuntu-18.04-amd64-disk001.vmdk и ubuntu-18.04-amd64.ovf).

Редактируем .ovf

На предыдущем этапе мы настроили экспорт образа ВМ в Open Virtualization Format. Следующим шагом будет редактировать файл *.ovf.

Я написал специальный скрипт на Python 3.8 (он включен в дистрибутив Ubuntu Server 20.04) и «распарсил» packer-manifest.json, чтобы извлечь путь до файла .ovf. Вот часть, решающая эту задачу:

import json

with open("packer-manifest.json", "r") as json_file:
    data = json.load(json_file)
    for i in data["builds"]:
        for j in i["files"]:
            if ".ovf" in j["name"]:
                ovf_in = j["name"]
                ovf_out = ovf_in.replace(".ovf", "-vmware.ovf")

Переменная ovf_in хранит путь до файла .ovf, а ovf_out — путь до нового файла .ovf, в котором мы сохраним изменения. Чтобы эти изменения внести, использую модуль xml.etree.ElementTree.

При редактировании XML очень помогла утилита XMLStarlet. Она покажет, какие элементы дерева входят в состав файла .ovf.

Вывод утилиты XMLStarlet
> xmlstarlet el ./output/ubuntu-18.04-amd64.ovf
Envelope
Envelope/References
Envelope/References/File
Envelope/DiskSection
Envelope/DiskSection/Info
Envelope/DiskSection/Disk
Envelope/NetworkSection
Envelope/NetworkSection/Info
Envelope/NetworkSection/Network
Envelope/NetworkSection/Network/Description
Envelope/VirtualSystem
Envelope/VirtualSystem/Info
Envelope/VirtualSystem/OperatingSystemSection
Envelope/VirtualSystem/OperatingSystemSection/Info
Envelope/VirtualSystem/OperatingSystemSection/Description
Envelope/VirtualSystem/OperatingSystemSection/vbox:OSType
Envelope/VirtualSystem/VirtualHardwareSection
Envelope/VirtualSystem/VirtualHardwareSection/Info
Envelope/VirtualSystem/VirtualHardwareSection/System
Envelope/VirtualSystem/VirtualHardwareSection/System/vssd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/System/vssd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/System/vssd:VirtualSystemIdentifier
Envelope/VirtualSystem/VirtualHardwareSection/System/vssd:VirtualSystemType
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:VirtualQuantity
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:AllocationUnits
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:VirtualQuantity
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Address
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceSubType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Address
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceSubType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Address
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceSubType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:AddressOnParent
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Description
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:HostResource
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Parent
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/VirtualHardwareSection/Item
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:AutomaticAllocation
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Caption
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:Connection
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ElementName
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:InstanceID
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceSubType
Envelope/VirtualSystem/VirtualHardwareSection/Item/rasd:ResourceType
Envelope/VirtualSystem/vbox:Machine
Envelope/VirtualSystem/vbox:Machine/ovf:Info
Envelope/VirtualSystem/vbox:Machine/Hardware
Envelope/VirtualSystem/vbox:Machine/Hardware/CPU
Envelope/VirtualSystem/vbox:Machine/Hardware/CPU/PAE
Envelope/VirtualSystem/vbox:Machine/Hardware/CPU/LongMode
Envelope/VirtualSystem/vbox:Machine/Hardware/CPU/X2APIC
Envelope/VirtualSystem/vbox:Machine/Hardware/CPU/HardwareVirtExLargePages
Envelope/VirtualSystem/vbox:Machine/Hardware/Memory
Envelope/VirtualSystem/vbox:Machine/Hardware/Boot
Envelope/VirtualSystem/vbox:Machine/Hardware/Boot/Order
Envelope/VirtualSystem/vbox:Machine/Hardware/Boot/Order
Envelope/VirtualSystem/vbox:Machine/Hardware/Boot/Order
Envelope/VirtualSystem/vbox:Machine/Hardware/Boot/Order
Envelope/VirtualSystem/vbox:Machine/Hardware/Display
Envelope/VirtualSystem/vbox:Machine/Hardware/VideoCapture
Envelope/VirtualSystem/vbox:Machine/Hardware/RemoteDisplay
Envelope/VirtualSystem/vbox:Machine/Hardware/RemoteDisplay/VRDEProperties
Envelope/VirtualSystem/vbox:Machine/Hardware/RemoteDisplay/VRDEProperties/Property
Envelope/VirtualSystem/vbox:Machine/Hardware/RemoteDisplay/VRDEProperties/Property
Envelope/VirtualSystem/vbox:Machine/Hardware/BIOS
Envelope/VirtualSystem/vbox:Machine/Hardware/BIOS/IOAPIC
Envelope/VirtualSystem/vbox:Machine/Hardware/BIOS/SmbiosUuidLittleEndian
Envelope/VirtualSystem/vbox:Machine/Hardware/Network
Envelope/VirtualSystem/vbox:Machine/Hardware/Network/Adapter
Envelope/VirtualSystem/vbox:Machine/Hardware/Network/Adapter/NAT
Envelope/VirtualSystem/vbox:Machine/Hardware/AudioAdapter
Envelope/VirtualSystem/vbox:Machine/Hardware/RTC
Envelope/VirtualSystem/vbox:Machine/Hardware/Clipboard
Envelope/VirtualSystem/vbox:Machine/StorageControllers
Envelope/VirtualSystem/vbox:Machine/StorageControllers/StorageController
Envelope/VirtualSystem/vbox:Machine/StorageControllers/StorageController
Envelope/VirtualSystem/vbox:Machine/StorageControllers/StorageController/AttachedDevice
Envelope/VirtualSystem/vbox:Machine/StorageControllers/StorageController/AttachedDevice/Image

Вывод утилиты XMLStarlet представлен на языке XPath. Как вы можете заметить, пространство имен определено не для каждого элемента — в их названии отсутствует часть перед символом двоеточия. Важно помнить, что при работе с XML в Python это приведет к неоднозначной трактовке атрибутов.

Строки, которые мы будем править, пропишу ниже в синтаксисе XPath с указанием пространства имен.

Удаляем элементы, содержащие упоминания сети и сетевых адаптеров, при разворачивании виртуальной машины из шаблона всегда можно указать новые адаптеры:

ovf:Envelope/ovf:NetworkSection
ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:Hardware/ovf:Network

Из элементов ниже я удалил только те, в значении которых содержат слово Ethernet:

ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item/rasd:Caption

Исключаем элементы с упоминанием IDE-контроллеров. IDE нам не нужно, так как мы будем работать со SCSI. В первом случае удаляем значения, где упоминается ideController, а во втором — только строки с атрибутом name="IDE Controller".

ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item/rasd:Caption
ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:StorageControllers/ovf:StorageController

Удаляем аудиоконтроллер. Он нам также не понадобится, так как практически никогда не используется на серверах.

ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:Hardware/ovf:AudioAdapter

Для оперативной памяти заменяем единицы измерения. Меняем их с MegaBytes на byte*2^20. Этот нюанс связан с тем, что разные производители программного обеспечения порой трактуют Open Virtualization Format по-разному:

ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item/rasd:AllocationUnits

Меняем тип системы. Вместо virtualbox-2.2 прописываем vmx-16, то есть меняем систему виртуализации с VirtualBox на VMware.

ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:System/vssd:VirtualSystemType.

Здесь дополнительно отмечу, что для работы с модулем xml.etree.ElementTree необходимо зарегистрировать используемые пространства имен функцией xml.etree.ElementTree.register_namespace. Их можно найти в исходном файле .ova по значению атрибутов xmlns.

import xml.etree.ElementTree as ET

ET.register_namespace('', "http://schemas.dmtf.org/ovf/envelope/1")
ET.register_namespace('ovf', "http://schemas.dmtf.org/ovf/envelope/1")
ET.register_namespace('rasd', "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
ET.register_namespace('vssd', "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData")
ET.register_namespace('xsi', "http://www.w3.org/2001/XMLSchema-instance")
ET.register_namespace('vbox', "http://www.virtualbox.org/ovf/machine")

По итогу мы получаем финальный скрипт ovf-patch.py:

#!/usr/bin/python3

import json
import xml.etree.ElementTree as ET

ET.register_namespace('', "http://schemas.dmtf.org/ovf/envelope/1")
ET.register_namespace('ovf', "http://schemas.dmtf.org/ovf/envelope/1")
ET.register_namespace('rasd', "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
ET.register_namespace('vssd', "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData")
ET.register_namespace('xsi', "http://www.w3.org/2001/XMLSchema-instance")
ET.register_namespace('vbox', "http://www.virtualbox.org/ovf/machine")

with open("packer-manifest.json", "r") as json_file:
    data = json.load(json_file)
    for i in data["builds"]:
        for j in i["files"]:
            if ".ovf" in j["name"]:
                ovf_in = j["name"]
                ovf_out = ovf_in.replace(".ovf", "-vmware.ovf")

    tree = ET.parse(ovf_in)
    root = tree.getroot()

    # ovf:Envelope/ovf:NetworkSection
    for i in root.findall("./{*}NetworkSection"):
        root.remove(i)

    # ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection
    for i in root.findall("./{*}VirtualSystem/{*}VirtualHardwareSection"):
        # ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item
        for i_i in i.findall("./{*}Item"):
            # ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item/rasd:AllocationUnits
            for i_i_i in i_i.findall("./{*}AllocationUnits"):
                if i_i_i.text == "MegaBytes":
                    i_i_i.text = "byte * 2^20"
            # ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item/rasd:Caption
            for i_i_i in i_i.findall("./{*}Caption"):
                if "ideController" in i_i_i.text:
                    i.remove(i_i)
                if "Ethernet" in i_i_i.text:
                    i.remove(i_i)
        # ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:System/vssd:VirtualSystemType
        for i_i in i.findall("{*}System/{*}VirtualSystemType"):
            i_i.text = "vmx-16"

    # ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:Hardware
    for i in root.findall("./{*}VirtualSystem/{*}Machine/{*}Hardware"):
        # ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:Hardware/ovf:AudioAdapter
        for i_i in i.findall("./{*}AudioAdapter"):
            i.remove(i_i)
        # ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:Hardware/ovf:Network
        for i_i in i.findall("./{*}Network"):
            i.remove(i_i)

    # ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:StorageControllers
    for i in root.findall("./{*}VirtualSystem/{*}Machine/{*}StorageControllers"):
        # ovf:Envelope/ovf:VirtualSystem/vbox:Machine/ovf:StorageControllers/ovf:StorageController
        for i_i in i.findall("./{*}StorageController[@name='IDE Controller']"):
            i.remove(i_i)

    tree.write(ovf_out)
    print(ovf_out)

После запуска скрипта в выходном каталоге появятся файлы:

  • Образ жесткого диска: ubuntu-18.04-amd64-disk001.vmdk;

  • XML-файл с описанием ВМ: ubuntu-18.04-amd64.ovf;

  • XML-файл с описанием ВМ, готовый к импорту в Cloud Director: ubuntu-18.04-amd64-vmware.ovf.

В качестве примера привожу исходный файл ubuntu-18.04-amd64.ovf:

ubuntu-18.04-amd64.ovf
<?xml version="1.0"?>
<Envelope ovf:version="1.0" xml:lang="en-US" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vbox="http://www.virtualbox.org/ovf/machine">
  <References>
    <File ovf:id="file1" ovf:href="ubuntu-18.04-amd64-disk001.vmdk"/>
  </References>
  <DiskSection>
    <Info>List of the virtual disks used in the package</Info>
    <Disk ovf:capacity="34359738368" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" vbox:uuid="a5659548-990a-4853-a8ab-bb9a168df0fc"/>
  </DiskSection>
  <NetworkSection>
    <Info>Logical networks used in the package</Info>
    <Network ovf:name="NAT">
      <Description>Logical network used by this appliance.</Description>
    </Network>
  </NetworkSection>
  <VirtualSystem ovf:id="ubuntu-18.04-amd64">
    <Info>A virtual machine</Info>
    <OperatingSystemSection ovf:id="94">
      <Info>The kind of installed guest operating system</Info>
      <Description>Ubuntu_64</Description>
      <vbox:OSType ovf:required="false">Ubuntu_64</vbox:OSType>
    </OperatingSystemSection>
    <VirtualHardwareSection>
      <Info>Virtual hardware requirements for a virtual machine</Info>
      <System>
        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
        <vssd:InstanceID>0</vssd:InstanceID>
        <vssd:VirtualSystemIdentifier>ubuntu-18.04-amd64</vssd:VirtualSystemIdentifier>
        <vssd:VirtualSystemType>virtualbox-2.2</vssd:VirtualSystemType>
      </System>
      <Item>
        <rasd:Caption>2 virtual CPU</rasd:Caption>
        <rasd:Description>Number of virtual CPUs</rasd:Description>
        <rasd:ElementName>2 virtual CPU</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
      </Item>
      <Item>
        <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
        <rasd:Caption>4096 MB of memory</rasd:Caption>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>4096 MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>4096</rasd:VirtualQuantity>
      </Item>
      <Item>
        <rasd:Address>0</rasd:Address>
        <rasd:Caption>ideController0</rasd:Caption>
        <rasd:Description>IDE Controller</rasd:Description>
        <rasd:ElementName>ideController0</rasd:ElementName>
        <rasd:InstanceID>3</rasd:InstanceID>
        <rasd:ResourceSubType>PIIX4</rasd:ResourceSubType>
        <rasd:ResourceType>5</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:Address>1</rasd:Address>
        <rasd:Caption>ideController1</rasd:Caption>
        <rasd:Description>IDE Controller</rasd:Description>
        <rasd:ElementName>ideController1</rasd:ElementName>
        <rasd:InstanceID>4</rasd:InstanceID>
        <rasd:ResourceSubType>PIIX4</rasd:ResourceSubType>
        <rasd:ResourceType>5</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:Address>0</rasd:Address>
        <rasd:Caption>scsiController0</rasd:Caption>
        <rasd:Description>SCSI Controller</rasd:Description>
        <rasd:ElementName>scsiController0</rasd:ElementName>
        <rasd:InstanceID>5</rasd:InstanceID>
        <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
        <rasd:ResourceType>6</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:AddressOnParent>0</rasd:AddressOnParent>
        <rasd:Caption>disk1</rasd:Caption>
        <rasd:Description>Disk Image</rasd:Description>
        <rasd:ElementName>disk1</rasd:ElementName>
        <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
        <rasd:InstanceID>6</rasd:InstanceID>
        <rasd:Parent>5</rasd:Parent>
        <rasd:ResourceType>17</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
        <rasd:Caption>Ethernet adapter on 'NAT'</rasd:Caption>
        <rasd:Connection>NAT</rasd:Connection>
        <rasd:ElementName>Ethernet adapter on 'NAT'</rasd:ElementName>
        <rasd:InstanceID>7</rasd:InstanceID>
        <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
      </Item>
    </VirtualHardwareSection>
    <vbox:Machine ovf:required="false" version="1.16-linux" uuid="{37db6186-f653-47f2-ba71-1b325b4aa806}" name="ubuntu-18.04-amd64" OSType="Ubuntu_64" snapshotFolder="Snapshots" lastStateChange="2021-08-15T18:03:51Z">
      <ovf:Info>Complete VirtualBox machine configuration in VirtualBox format</ovf:Info>
      <Hardware>
        <CPU count="2">
          <PAE enabled="true"/>
          <LongMode enabled="true"/>
          <X2APIC enabled="true"/>
          <HardwareVirtExLargePages enabled="false"/>
        </CPU>
        <Memory RAMSize="4096"/>
        <Boot>
          <Order position="1" device="HardDisk"/>
          <Order position="2" device="DVD"/>
          <Order position="3" device="None"/>
          <Order position="4" device="None"/>
        </Boot>
        <Display controller="VMSVGA" VRAMSize="16"/>
        <VideoCapture screens="1" file="." fps="25"/>
        <RemoteDisplay enabled="true">
          <VRDEProperties>
            <Property name="TCP/Address" value="127.0.0.1"/>
            <Property name="TCP/Ports" value="5968"/>
          </VRDEProperties>
        </RemoteDisplay>
        <BIOS>
          <IOAPIC enabled="true"/>
          <SmbiosUuidLittleEndian enabled="true"/>
        </BIOS>
        <Network>
          <Adapter slot="0" enabled="true" MACAddress="0800272C8B78" type="82540EM">
            <NAT/>
          </Adapter>
        </Network>
        <AudioAdapter driver="Pulse" enabledIn="false" enabledOut="false"/>
        <RTC localOrUTC="UTC"/>
        <Clipboard/>
      </Hardware>
      <StorageControllers>
        <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true"/>
        <StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true">
          <AttachedDevice nonrotational="true" discard="true" type="HardDisk" hotpluggable="false" port="0" device="0">
            <Image uuid="{a5659548-990a-4853-a8ab-bb9a168df0fc}"/>
          </AttachedDevice>
        </StorageController>
      </StorageControllers>
    </vbox:Machine>
  </VirtualSystem>
</Envelope>

И файл с внесенными изменениями ubuntu-18.04-amd64-vmware.ovf:

ubuntu-18.04-amd64-vmware.ovf
<ovf:Envelope xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vbox="http://www.virtualbox.org/ovf/machine" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ovf:version="1.0" xml:lang="en-US">
  <ovf:References>
    <ovf:File ovf:id="file1" ovf:href="ubuntu-18.04-amd64-disk001.vmdk" />
  </ovf:References>
  <ovf:DiskSection>
    <ovf:Info>List of the virtual disks used in the package</ovf:Info>
    <ovf:Disk ovf:capacity="34359738368" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" vbox:uuid="a5659548-990a-4853-a8ab-bb9a168df0fc" />
  </ovf:DiskSection>
  <ovf:VirtualSystem ovf:id="ubuntu-18.04-amd64">
    <ovf:Info>A virtual machine</ovf:Info>
    <ovf:OperatingSystemSection ovf:id="94">
      <ovf:Info>The kind of installed guest operating system</ovf:Info>
      <ovf:Description>Ubuntu_64</ovf:Description>
      <vbox:OSType ovf:required="false">Ubuntu_64</vbox:OSType>
    </ovf:OperatingSystemSection>
    <ovf:VirtualHardwareSection>
      <ovf:Info>Virtual hardware requirements for a virtual machine</ovf:Info>
      <ovf:System>
        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
        <vssd:InstanceID>0</vssd:InstanceID>
        <vssd:VirtualSystemIdentifier>ubuntu-18.04-amd64</vssd:VirtualSystemIdentifier>
        <vssd:VirtualSystemType>vmx-16</vssd:VirtualSystemType>
      </ovf:System>
      <ovf:Item>
        <rasd:Caption>2 virtual CPU</rasd:Caption>
        <rasd:Description>Number of virtual CPUs</rasd:Description>
        <rasd:ElementName>2 virtual CPU</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
      </ovf:Item>
      <ovf:Item>
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Caption>4096 MB of memory</rasd:Caption>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>4096 MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>4096</rasd:VirtualQuantity>
      </ovf:Item>
      <ovf:Item>
        <rasd:Address>0</rasd:Address>
        <rasd:Caption>scsiController0</rasd:Caption>
        <rasd:Description>SCSI Controller</rasd:Description>
        <rasd:ElementName>scsiController0</rasd:ElementName>
        <rasd:InstanceID>5</rasd:InstanceID>
        <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
        <rasd:ResourceType>6</rasd:ResourceType>
      </ovf:Item>
      <ovf:Item>
        <rasd:AddressOnParent>0</rasd:AddressOnParent>
        <rasd:Caption>disk1</rasd:Caption>
        <rasd:Description>Disk Image</rasd:Description>
        <rasd:ElementName>disk1</rasd:ElementName>
        <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
        <rasd:InstanceID>6</rasd:InstanceID>
        <rasd:Parent>5</rasd:Parent>
        <rasd:ResourceType>17</rasd:ResourceType>
      </ovf:Item>
      </ovf:VirtualHardwareSection>
    <vbox:Machine ovf:required="false" version="1.16-linux" uuid="{37db6186-f653-47f2-ba71-1b325b4aa806}" name="ubuntu-18.04-amd64" OSType="Ubuntu_64" snapshotFolder="Snapshots" lastStateChange="2021-08-15T18:03:51Z">
      <ovf:Info>Complete VirtualBox machine configuration in VirtualBox format</ovf:Info>
      <ovf:Hardware>
        <ovf:CPU count="2">
          <ovf:PAE enabled="true" />
          <ovf:LongMode enabled="true" />
          <ovf:X2APIC enabled="true" />
          <ovf:HardwareVirtExLargePages enabled="false" />
        </ovf:CPU>
        <ovf:Memory RAMSize="4096" />
        <ovf:Boot>
          <ovf:Order position="1" device="HardDisk" />
          <ovf:Order position="2" device="DVD" />
          <ovf:Order position="3" device="None" />
          <ovf:Order position="4" device="None" />
        </ovf:Boot>
        <ovf:Display controller="VMSVGA" VRAMSize="16" />
        <ovf:VideoCapture screens="1" file="." fps="25" />
        <ovf:RemoteDisplay enabled="true">
          <ovf:VRDEProperties>
            <ovf:Property name="TCP/Address" value="127.0.0.1" />
            <ovf:Property name="TCP/Ports" value="5968" />
          </ovf:VRDEProperties>
        </ovf:RemoteDisplay>
        <ovf:BIOS>
          <ovf:IOAPIC enabled="true" />
          <ovf:SmbiosUuidLittleEndian enabled="true" />
        </ovf:BIOS>
        <ovf:RTC localOrUTC="UTC" />
        <ovf:Clipboard />
      </ovf:Hardware>
      <ovf:StorageControllers>
        <ovf:StorageController name="SCSI Controller" type="LsiLogic" PortCount="16" useHostIOCache="false" Bootable="true">
          <ovf:AttachedDevice nonrotational="true" discard="true" type="HardDisk" hotpluggable="false" port="0" device="0">
            <ovf:Image uuid="{a5659548-990a-4853-a8ab-bb9a168df0fc}" />
          </ovf:AttachedDevice>
        </ovf:StorageController>
      </ovf:StorageControllers>
    </vbox:Machine>
  </ovf:VirtualSystem>
</ovf:Envelope>

Импортируем образ

Для импорта образа ВМ в библиотеку можно воспользоваться инструментами OVF Tool или vcd-cli. Это — инструменты, разработанные VMware. Они хорошо документированы и работают стабильно. В дальнейшем из библиотеки Cloud Director можно разворачивать ВМ из созданного шаблона вручную или с инструментами автоматизации (например, Terraform), добавив по требованию сетевые контроллеры, дополнительные диски и так далее.

Таким образом, мне удалось подготовить образ VirtualBox к загрузке в VMware Cloud Director. Описал шаги для реализации полноценного CI-пайплайна.

Если хотите использовать виртуальный ЦОД на базе VMware, вы можете оставить заявку на подключение и бесплатную консультацию.

Источник: https://habr.com/ru/company/sbercloud/blog/581362/


Интересные статьи

Интересные статьи

Добрый день. Я занимаюсь переводом уроков к Vulkan API с английского языка на русский (ссылка на оригинальный тьюториал — vulkan-tutorial.com). Сегодня я бы хотела поделиться с вами п...
Хочу поделиться опытом автоматизации экспорта заказов из Aliexpress в несколько CRM. Приведенные примеры написаны на PHP, но библиотеки для работы с Aliexpress есть и для...
* скорее всего, я что-нибудь да упустил, но уверен, в комментариях мне это подскажут Эту статью я пишу для своих личных нужд. Планируется, что она будет содержать в себе ответы на все вопросы,...
Модульность является одним из ключевых принципов разработки программного обеспечения с 1960-х годов. Применение этого принципа приносит в программирование много полезного. Модульность способствуе...
Итак, у вас есть приложение с автоматическими возобновляемыми подписками. Оно прекрасно работает, пользователи безудержно оформляют премиум подписки и пишут хвалебные отзывы. Красота! Всем при...