Привет, Хабр! Меня зовут Роман Петров, занимаюсь разработкой продуктов для управления учетными данными в 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, вы можете оставить заявку на подключение и бесплатную консультацию.
