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