From 55e22a1f58c78371870a25aae9d14f62a096a42d Mon Sep 17 00:00:00 2001 From: Manisha Singhal Date: Tue, 10 Oct 2023 12:45:49 +0200 Subject: [PATCH] feat: Add Cloudinit support using iso image (#294) fixes #296 --- app/helpers/proxmox_vm_cloudinit_helper.rb | 73 +++++++++++++++++++ .../proxmox_vm_commands.rb | 1 + 2 files changed, 74 insertions(+) diff --git a/app/helpers/proxmox_vm_cloudinit_helper.rb b/app/helpers/proxmox_vm_cloudinit_helper.rb index 9d12c4ac3..afdceb87f 100644 --- a/app/helpers/proxmox_vm_cloudinit_helper.rb +++ b/app/helpers/proxmox_vm_cloudinit_helper.rb @@ -41,4 +41,77 @@ def parse_server_cloudinit(args) end cloudinit_h end + + def create_cloudinit_iso(vm_name, configs) + ssh = Fog::SSH.new(URI.parse(fog_credentials[:proxmox_url]).host, fog_credentials[:proxmox_username].split('@')[0], { password: fog_credentials[:proxmox_password] }) + files = [] + wd = create_temp_directory(ssh) + configs.each do |config| + begin + ssh.run([%({echo "#{config[1]}" >> "#{wd}/#{config[0]}"})]) + rescue ::Foreman::Exception => e + logger.error("Error writing to the file #{config[0]}: #{e}") + raise e + end + files.append(File.join(wd, config[0])) + end + raise Foreman::Exception, N_('ISO build failed') unless ssh.run(generate_iso_command(vm_name, files)) + delete_config_files(wd) + iso + end + + def generate_iso_command(vm_name, files) + iso = File.join(default_iso_path, "#{vm_name}_cloudinit.iso") + arguments = ["genisoimage", "-output #{iso}", '-volid', 'cidata', '-joliet', '-rock'] + logger.warn("iso image generation args: #{arguments}") + arguments.concat(files).join(' ') + end + + def create_temp_directory(ssh) + res = ssh.run([%({mktemp -d})]) + raise ::Foreman::Exception, "Could not create working directory to store cloudinit config data: #{res.first.stdout}." unless res.first.status.zero? + res.first.stdout.chomp + end + + def delete_config_files(working_dir) + ssh.run("rm -rf #{working_dir}") + rescue Foreman::Exception => e + logger.warn("Could not delete directory for config files: #{e}. Please delete it manually at #{working_dir}") + end + + def parse_cloudinit_config(args) + filenames = ["meta-data"] + config_data = ["instance-id: #{args[:name]}"] + user_data = args.delete(:user_data) + + return args if user_data == '' + + if user_data.include?('#network-config') && user_data.include?('#cloud-config') + config_data.concat(user_data.split('#network-config')) + filenames.append(['user-data', 'network-config']) + elsif user_data.include?('#network-config') && !user_data.include?('#cloud-config') + config_data.append(user_data.split('#network-config')[1]) + filenames.append("network-config") + elsif !user_data.include?('#network-config') && user_data.include?('#cloud-config') + config_data.append(user_data) + filenames.append("user-data") + end + + return args if config_data.length == 1 + + configs = filenames.zip(config_data).to_h + + iso = create_cloudinit_iso(args[:name], configs) + args.merge(attach_cloudinit_iso(args[:node_id], iso)) + end + + def attach_cloudinit_iso(node, iso) + storage = storages(node, 'iso')[0] + volume = storage.volumes.detect { |v| v.volid.include? File.basename(iso) } + { ide2: "#{volume.volid},media=cdrom" } + end + + def default_iso_path + "/var/lib/vz/template/iso" + end end diff --git a/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb b/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb index cfe5ddfa7..8287cb81f 100644 --- a/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +++ b/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb @@ -56,6 +56,7 @@ def create_vm(args = {}) end def compute_clone_attributes(args, container, type) + args = parse_cloudinit_config(args) if args[:user_data] parsed_args = parse_typed_vm(args, type) if container options = { :hostname => args[:name] }