From 30c38eb814a8cc6f590facaf4a8313a4cdd7e0a4 Mon Sep 17 00:00:00 2001 From: Neil Hanlon Date: Fri, 30 May 2025 17:45:25 -0400 Subject: [PATCH 1/4] WIP: vmware backend for vagrant --- kiwi/defaults.py | 3 +- kiwi/storage/subformat/vagrant_base.py | 10 +- kiwi/storage/subformat/vagrant_vmware.py | 137 +++++++++++++++++++++++ 3 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 kiwi/storage/subformat/vagrant_vmware.py diff --git a/kiwi/defaults.py b/kiwi/defaults.py index 56a5531e7b3..067f71e4d5d 100644 --- a/kiwi/defaults.py +++ b/kiwi/defaults.py @@ -1341,7 +1341,8 @@ def get_disk_format_types(): """ return [ 'gce', 'qcow2', 'vmdk', 'ova', 'vmx', 'vhd', 'vhdx', - 'vhdfixed', 'vdi', 'vagrant.libvirt.box', 'vagrant.virtualbox.box' + 'vhdfixed', 'vdi', 'vagrant.libvirt.box', 'vagrant.virtualbox.box', + 'vagrant.vmware_desktop.box' ] @staticmethod diff --git a/kiwi/storage/subformat/vagrant_base.py b/kiwi/storage/subformat/vagrant_base.py index 3f15996ec57..7b7654a7884 100644 --- a/kiwi/storage/subformat/vagrant_base.py +++ b/kiwi/storage/subformat/vagrant_base.py @@ -33,6 +33,7 @@ KiwiFormatSetupError ) +VagrantConfigDict = Dict['str', xml_parse.vagrantconfig] class DiskFormatVagrantBase(DiskFormatBase): """ @@ -74,7 +75,8 @@ class DiskFormatVagrantBase(DiskFormatBase): * :meth:`get_additional_vagrant_config_settings` """ - def post_init(self, custom_args: Dict['str', xml_parse.vagrantconfig]): + + def post_init(self, custom_args: VagrantConfigDict): """ vagrant disk format post initialization method @@ -96,10 +98,10 @@ def post_init(self, custom_args: Dict['str', xml_parse.vagrantconfig]): raise KiwiFormatSetupError( 'no vagrantconfig provided' ) - self.vagrantconfig = custom_args['vagrantconfig'] - self.vagrant_post_init() + self.vagrantconfig = custom_args.pop('vagrantconfig') + self.vagrant_post_init(custom_args) - def vagrant_post_init(self) -> None: + def vagrant_post_init(self, custom_args: Dict['str', xml_parse.vagrantconfig]) -> None: """ Vagrant provider specific post initialization method diff --git a/kiwi/storage/subformat/vagrant_vmware.py b/kiwi/storage/subformat/vagrant_vmware.py new file mode 100644 index 00000000000..28fd5f97443 --- /dev/null +++ b/kiwi/storage/subformat/vagrant_vmware.py @@ -0,0 +1,137 @@ +# Copyright (c) 2019 SUSE Linux GmbH. All rights reserved. +# +# This file is part of kiwi. +# +# kiwi is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# kiwi is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with kiwi. If not, see +# + +import os +from textwrap import dedent +from typing import ( + List, Dict +) + +# project +from kiwi.storage.subformat.vagrant_base import DiskFormatVagrantBase, VagrantConfigDict +from kiwi.storage.subformat.vmdk import DiskFormatVmdk +from kiwi.command import Command + + +class DiskFormatVagrantVMware(DiskFormatVagrantBase): + """ + Base class for creating vagrant boxes. + + The documentation of the vagrant box format can be found here: + https://www.vagrantup.com/docs/boxes/format.html + In a nutshell, a vagrant box is a tar, tar.gz or zip archive of the + following: + + 1. ``metadata.json``: + A json file that contains the name of the provider + and arbitrary additional data (that vagrant doesn't care about). + 2. ``Vagrantfile``: + A Vagrantfile which defines the boxes' MAC address. It + can be also used to define other settings of the box, e.g. + the method via which the ``/vagrant/`` directory is shared. + This file is either automatically generated by KIWI or we use a file + that has been provided by the user (depends on the setting in + `vagrantconfig.embebbed_vagrantfile`) + 3. The actual virtual disk image: this is provider specific and + vagrant simply forwards it to your virtual machine provider. + + Required methods/variables that child classes must implement: + + * :meth:`vagrant_post_init` + + post initializing method that has to specify the vagrant + provider name in :attr:`provider` and the box name in + :attr:`image_format`. Note: new providers also needs to + be specified in the schema and the box name needs to be + registered to :func:`kiwi.defaults.Defaults.get_disk_format_types` + + * :meth:`create_box_img` + + Optional methods: + + * :meth:`get_additional_metadata` + * :meth:`get_additional_vagrant_config_settings` + + """ + + """ + **Create a vagrant box for the vmware provider** + """ + + def vagrant_post_init(self, custom_args: VagrantConfigDict) -> None: + self.image_format = 'vagrant.vmware_desktop.box' + self.provider = 'vmware_desktop' + self.options = self.get_qemu_option_list(custom_args) + + def create_box_img(self, temp_image_dir: str) -> List[str]: + """ + Creates the vmdk disk image for the vmware_desktop vagrant provider + + :param str temp_image_dir: + Path to the temporary directory used to build the box image + + :return: + A list of files relevant for the libvirt box to be + included in the vagrant box + + :rtype: list + """ + vmdk = DiskFormatVmdk( + self.xml_state, self.root_dir, self.target_dir, + custom_args=self.options + ) + vmdk.create_image_format() + box_image = os.sep.join([temp_image_dir, 'box.img']) + Command.run( + [ + 'mv', self.get_target_file_path_for_format(vmdk.image_format), + box_image + ] + ) + return [box_image] + + def get_additional_metadata(self) -> Dict: + """ + Provide box metadata needed to create the box in vagrant + + :return: + Returns a dictionary containing the virtual image format + and the size of the image. + + :rtype: dict + """ + return { + 'format': 'vmdk' + } + + def get_additional_vagrant_config_settings(self) -> str: + """ + Returns settings for the libvirt provider telling vagrant to use kvm. + + :return: + ruby code to be evaluated as string + + :rtype: str + """ + # return dedent(''' + # config.vm.synced_folder ".", "/vagrant", type: "rsync" + # config.vm.provider :libvirt do |libvirt| + # libvirt.driver = "kvm" + # end + # ''').strip() + return '' From 9b346828f11321e3505ceea2dffcce30882725bd Mon Sep 17 00:00:00 2001 From: Neil Hanlon Date: Fri, 30 May 2025 19:35:59 -0400 Subject: [PATCH 2/4] continue adding / setting up --- kiwi/schema/kiwi.rnc | 5 ++- kiwi/schema/kiwi.rng | 2 + kiwi/storage/subformat/__init__.py | 1 + .../subformat/template/vmware_settings.py | 25 +++++++++-- kiwi/storage/subformat/vagrant_vmware.py | 42 +------------------ kiwi/storage/subformat/vmdk.py | 2 +- 6 files changed, 29 insertions(+), 48 deletions(-) diff --git a/kiwi/schema/kiwi.rnc b/kiwi/schema/kiwi.rnc index e045d2a01e7..e998854ec22 100644 --- a/kiwi/schema/kiwi.rnc +++ b/kiwi/schema/kiwi.rnc @@ -2565,7 +2565,8 @@ div { "lsilogic" | "lsisas1068" | "legacyESX" | - "pvscsi" + "pvscsi" | + "sata" } k.vmdisk.id.attribute = ## The disk ID / device for the VM disk (vmdk only) @@ -3539,7 +3540,7 @@ div { ] k.vagrantconfig.provider.attribute = ## The vagrant provider for this box - attribute provider { "libvirt" | "virtualbox" } + attribute provider { "libvirt" | "virtualbox" | "vmware" } k.vagrantconfig.virtualbox_guest_additions_present.attribute = ## Guest additions are present in this box attribute virtualbox_guest_additions_present { xsd:boolean } diff --git a/kiwi/schema/kiwi.rng b/kiwi/schema/kiwi.rng index 654d70bc294..98e9ea777f4 100644 --- a/kiwi/schema/kiwi.rng +++ b/kiwi/schema/kiwi.rng @@ -3864,6 +3864,7 @@ by the VM (ova only) lsisas1068 legacyESX pvscsi + sata @@ -5316,6 +5317,7 @@ and setup the system disk. libvirt virtualbox + vmware diff --git a/kiwi/storage/subformat/__init__.py b/kiwi/storage/subformat/__init__.py index 75ca857c17b..eaaabc9aeb6 100644 --- a/kiwi/storage/subformat/__init__.py +++ b/kiwi/storage/subformat/__init__.py @@ -49,6 +49,7 @@ def new( 'ova': 'Ova', 'vagrant_libvirt': 'VagrantLibVirt', 'vagrant_virtualbox': 'VagrantVirtualBox', + 'vagrant_vmware': 'VagrantVMware', 'base': 'Base' } module_namespace: Optional[str] = None diff --git a/kiwi/storage/subformat/template/vmware_settings.py b/kiwi/storage/subformat/template/vmware_settings.py index 82b90c6aa29..ead28f6702c 100644 --- a/kiwi/storage/subformat/template/vmware_settings.py +++ b/kiwi/storage/subformat/template/vmware_settings.py @@ -37,12 +37,25 @@ def __init__(self): guestOS = "${guest_os}" ''').strip() + self.cr + self.pcie_bridge = dedent(''' + pciBridge0.functions = "8" + pciBridge0.pciSlotNumber = "17" + pciBridge0.present = "true" + pciBridge0.virtualDev = "pcieRootPort" + ''').strip() + self.cr + self.ide_disk = dedent(''' ide${disk_id}:0.present = "true" ide${disk_id}:0.fileName= "${vmdk_file}" ide${disk_id}:0.redo = "" ''').strip() + self.cr + self.sata_disk = dedent(''' + sata${disk_id}.present = "true" + sata${disk_id}:0.present = "true" + sata${disk_id}:0.fileName = "${vmdk_file}" + ''').strip() + self.cr + self.scsi_disk = dedent(''' scsi${disk_id}.present = "true" scsi${disk_id}.sharedBus = "none" @@ -130,6 +143,7 @@ def get_template( """ template_data = self.header template_data += self.defaults + template_data += self.pcie_bridge if memory_setup: template_data += self.memory @@ -137,10 +151,13 @@ def get_template( if cpu_setup: template_data += self.number_of_cpus - if disk_controller == 'ide': - template_data += self.ide_disk - else: - template_data += self.scsi_disk + controller_funcs = { + 'ide': self.ide_disk, + 'scsi': self.scsi_disk, + 'sata': self.sata_disk, + } + + template_data += controller_funcs.get(disk_controller, self.ide_disk) if network_setup: for nic_id, nic_setup in list(network_setup.items()): diff --git a/kiwi/storage/subformat/vagrant_vmware.py b/kiwi/storage/subformat/vagrant_vmware.py index 28fd5f97443..1b1818e62ce 100644 --- a/kiwi/storage/subformat/vagrant_vmware.py +++ b/kiwi/storage/subformat/vagrant_vmware.py @@ -29,46 +29,6 @@ class DiskFormatVagrantVMware(DiskFormatVagrantBase): - """ - Base class for creating vagrant boxes. - - The documentation of the vagrant box format can be found here: - https://www.vagrantup.com/docs/boxes/format.html - In a nutshell, a vagrant box is a tar, tar.gz or zip archive of the - following: - - 1. ``metadata.json``: - A json file that contains the name of the provider - and arbitrary additional data (that vagrant doesn't care about). - 2. ``Vagrantfile``: - A Vagrantfile which defines the boxes' MAC address. It - can be also used to define other settings of the box, e.g. - the method via which the ``/vagrant/`` directory is shared. - This file is either automatically generated by KIWI or we use a file - that has been provided by the user (depends on the setting in - `vagrantconfig.embebbed_vagrantfile`) - 3. The actual virtual disk image: this is provider specific and - vagrant simply forwards it to your virtual machine provider. - - Required methods/variables that child classes must implement: - - * :meth:`vagrant_post_init` - - post initializing method that has to specify the vagrant - provider name in :attr:`provider` and the box name in - :attr:`image_format`. Note: new providers also needs to - be specified in the schema and the box name needs to be - registered to :func:`kiwi.defaults.Defaults.get_disk_format_types` - - * :meth:`create_box_img` - - Optional methods: - - * :meth:`get_additional_metadata` - * :meth:`get_additional_vagrant_config_settings` - - """ - """ **Create a vagrant box for the vmware provider** """ @@ -86,7 +46,7 @@ def create_box_img(self, temp_image_dir: str) -> List[str]: Path to the temporary directory used to build the box image :return: - A list of files relevant for the libvirt box to be + A list of files relevant for the vmware box to be included in the vagrant box :rtype: list diff --git a/kiwi/storage/subformat/vmdk.py b/kiwi/storage/subformat/vmdk.py index ebd20ca3b03..7978706e587 100644 --- a/kiwi/storage/subformat/vmdk.py +++ b/kiwi/storage/subformat/vmdk.py @@ -146,7 +146,7 @@ def _create_vmware_settings_file(self): if disk_setup: disk_controller = disk_setup.get_controller() or disk_controller disk_id = disk_setup.get_id() - if not disk_controller == 'ide': + if disk_controller not in ['ide', 'sata']: template_record['scsi_controller_name'] = disk_controller if disk_id: template_record['disk_id'] = disk_id From 3c53643f1d29bf0f518dc4bb2b0828a54d7c4fc3 Mon Sep 17 00:00:00 2001 From: Neil Hanlon Date: Fri, 30 May 2025 20:06:27 -0400 Subject: [PATCH 3/4] fix disk controller logic h/t lumarel try and pull in the generated vmx file --- .../subformat/template/vmware_settings.py | 4 +-- kiwi/storage/subformat/vagrant_base.py | 13 ++++++++ kiwi/storage/subformat/vagrant_vmware.py | 33 +++++++++++-------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/kiwi/storage/subformat/template/vmware_settings.py b/kiwi/storage/subformat/template/vmware_settings.py index ead28f6702c..07d0850159b 100644 --- a/kiwi/storage/subformat/template/vmware_settings.py +++ b/kiwi/storage/subformat/template/vmware_settings.py @@ -153,11 +153,11 @@ def get_template( controller_funcs = { 'ide': self.ide_disk, - 'scsi': self.scsi_disk, 'sata': self.sata_disk, } - template_data += controller_funcs.get(disk_controller, self.ide_disk) + # All except ide and sata ontrollers are a form of scsi + template_data += controller_funcs.get(disk_controller, self.scsi_disk) if network_setup: for nic_id, nic_setup in list(network_setup.items()): diff --git a/kiwi/storage/subformat/vagrant_base.py b/kiwi/storage/subformat/vagrant_base.py index 7b7654a7884..790d0b63595 100644 --- a/kiwi/storage/subformat/vagrant_base.py +++ b/kiwi/storage/subformat/vagrant_base.py @@ -18,6 +18,7 @@ import json import os.path +from pathlib import PurePath from kiwi.utils.temporary import Temporary from typing import ( Dict, Optional, List @@ -223,6 +224,18 @@ def get_additional_vagrant_config_settings(self) -> str: """ return '' + def _stage_box_file(self, temp_dir: str, format: str) -> str: + file_path = self.get_target_file_path_for_format(format) + file_name = PurePath(file_path).name + box_file = os.sep.join([temp_dir, file_name]) + Command.run( + [ + 'mv', file_path, + box_file + ] + ) + return box_file + def _create_box_metadata(self): metadata = self.get_additional_metadata() or {} metadata['provider'] = self.provider diff --git a/kiwi/storage/subformat/vagrant_vmware.py b/kiwi/storage/subformat/vagrant_vmware.py index 1b1818e62ce..d11b77e63c0 100644 --- a/kiwi/storage/subformat/vagrant_vmware.py +++ b/kiwi/storage/subformat/vagrant_vmware.py @@ -17,15 +17,21 @@ # import os -from textwrap import dedent +# from textwrap import dedent from typing import ( List, Dict ) +from pathlib import PurePath # project -from kiwi.storage.subformat.vagrant_base import DiskFormatVagrantBase, VagrantConfigDict -from kiwi.storage.subformat.vmdk import DiskFormatVmdk from kiwi.command import Command +from kiwi.storage.subformat.vagrant_base import (DiskFormatVagrantBase, + VagrantConfigDict) +from kiwi.storage.subformat.vmdk import DiskFormatVmdk + +from kiwi.exceptions import ( + KiwiTemplateError +) class DiskFormatVagrantVMware(DiskFormatVagrantBase): @@ -55,15 +61,15 @@ def create_box_img(self, temp_image_dir: str) -> List[str]: self.xml_state, self.root_dir, self.target_dir, custom_args=self.options ) - vmdk.create_image_format() - box_image = os.sep.join([temp_image_dir, 'box.img']) - Command.run( - [ - 'mv', self.get_target_file_path_for_format(vmdk.image_format), - box_image - ] - ) - return [box_image] + try: + vmdk.create_image_format() + box_image = self._stage_box_file(temp_image_dir, vmdk.image_format) + settings_file = self._stage_box_file(temp_image_dir, 'vmx') + except KiwiTemplateError as e: + # TODO: handle + raise e + + return [box_image, settings_file] def get_additional_metadata(self) -> Dict: """ @@ -71,7 +77,6 @@ def get_additional_metadata(self) -> Dict: :return: Returns a dictionary containing the virtual image format - and the size of the image. :rtype: dict """ @@ -81,7 +86,7 @@ def get_additional_metadata(self) -> Dict: def get_additional_vagrant_config_settings(self) -> str: """ - Returns settings for the libvirt provider telling vagrant to use kvm. + Returns settings for the vmware provider :return: ruby code to be evaluated as string From ef721bcbdcb298fe6472a9064fd5e2ed7ad0d79f Mon Sep 17 00:00:00 2001 From: Neil Hanlon Date: Sat, 31 May 2025 23:03:16 -0400 Subject: [PATCH 4/4] linting is good probably --- kiwi/storage/subformat/vagrant_base.py | 3 ++- kiwi/storage/subformat/vagrant_libvirt.py | 6 ++++-- kiwi/storage/subformat/vagrant_virtualbox.py | 6 ++++-- kiwi/storage/subformat/vagrant_vmware.py | 9 +++------ 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/kiwi/storage/subformat/vagrant_base.py b/kiwi/storage/subformat/vagrant_base.py index 790d0b63595..d745326627d 100644 --- a/kiwi/storage/subformat/vagrant_base.py +++ b/kiwi/storage/subformat/vagrant_base.py @@ -36,6 +36,7 @@ VagrantConfigDict = Dict['str', xml_parse.vagrantconfig] + class DiskFormatVagrantBase(DiskFormatBase): """ Base class for creating vagrant boxes. @@ -102,7 +103,7 @@ def post_init(self, custom_args: VagrantConfigDict): self.vagrantconfig = custom_args.pop('vagrantconfig') self.vagrant_post_init(custom_args) - def vagrant_post_init(self, custom_args: Dict['str', xml_parse.vagrantconfig]) -> None: + def vagrant_post_init(self, custom_args: VagrantConfigDict = None) -> None: """ Vagrant provider specific post initialization method diff --git a/kiwi/storage/subformat/vagrant_libvirt.py b/kiwi/storage/subformat/vagrant_libvirt.py index 53433e43acc..aa4eaec85b8 100644 --- a/kiwi/storage/subformat/vagrant_libvirt.py +++ b/kiwi/storage/subformat/vagrant_libvirt.py @@ -22,7 +22,9 @@ ) # project -from kiwi.storage.subformat.vagrant_base import DiskFormatVagrantBase +from kiwi.storage.subformat.vagrant_base import ( + DiskFormatVagrantBase, VagrantConfigDict +) from kiwi.storage.subformat.qcow2 import DiskFormatQcow2 from kiwi.command import Command @@ -31,7 +33,7 @@ class DiskFormatVagrantLibVirt(DiskFormatVagrantBase): """ **Create a vagrant box for the libvirt provider** """ - def vagrant_post_init(self) -> None: + def vagrant_post_init(self, custom_args: VagrantConfigDict = None) -> None: self.image_format = 'vagrant.libvirt.box' self.provider = 'libvirt' diff --git a/kiwi/storage/subformat/vagrant_virtualbox.py b/kiwi/storage/subformat/vagrant_virtualbox.py index acf38f14bdb..56e804995aa 100644 --- a/kiwi/storage/subformat/vagrant_virtualbox.py +++ b/kiwi/storage/subformat/vagrant_virtualbox.py @@ -24,7 +24,9 @@ from kiwi.storage.subformat.template.virtualbox_ovf import ( VirtualboxOvfTemplate ) -from kiwi.storage.subformat.vagrant_base import DiskFormatVagrantBase +from kiwi.storage.subformat.vagrant_base import ( + DiskFormatVagrantBase, VagrantConfigDict +) from kiwi.storage.subformat.vmdk import DiskFormatVmdk from kiwi.command import Command @@ -34,7 +36,7 @@ class DiskFormatVagrantVirtualBox(DiskFormatVagrantBase): **Create a vagrant box for the virtualbox provider** """ - def vagrant_post_init(self) -> None: + def vagrant_post_init(self, custom_args: VagrantConfigDict = None) -> None: self.provider = 'virtualbox' self.image_format = 'vagrant.virtualbox.box' diff --git a/kiwi/storage/subformat/vagrant_vmware.py b/kiwi/storage/subformat/vagrant_vmware.py index d11b77e63c0..82f25f2df5b 100644 --- a/kiwi/storage/subformat/vagrant_vmware.py +++ b/kiwi/storage/subformat/vagrant_vmware.py @@ -16,15 +16,12 @@ # along with kiwi. If not, see # -import os # from textwrap import dedent from typing import ( List, Dict ) -from pathlib import PurePath # project -from kiwi.command import Command from kiwi.storage.subformat.vagrant_base import (DiskFormatVagrantBase, VagrantConfigDict) from kiwi.storage.subformat.vmdk import DiskFormatVmdk @@ -39,10 +36,10 @@ class DiskFormatVagrantVMware(DiskFormatVagrantBase): **Create a vagrant box for the vmware provider** """ - def vagrant_post_init(self, custom_args: VagrantConfigDict) -> None: + def vagrant_post_init(self, custom_args: VagrantConfigDict = None) -> None: self.image_format = 'vagrant.vmware_desktop.box' self.provider = 'vmware_desktop' - self.options = self.get_qemu_option_list(custom_args) + self.custom_args = custom_args def create_box_img(self, temp_image_dir: str) -> List[str]: """ @@ -59,7 +56,7 @@ def create_box_img(self, temp_image_dir: str) -> List[str]: """ vmdk = DiskFormatVmdk( self.xml_state, self.root_dir, self.target_dir, - custom_args=self.options + custom_args=self.custom_args ) try: vmdk.create_image_format()