Module: ProxmoxVMCloudinitHelper

Included in:
ProxmoxVMVolumesHelper
Defined in:
app/helpers/proxmox_vm_cloudinit_helper.rb

Overview

Convert a foreman form server hash into a fog-proxmox server attributes hash

Instance Method Summary collapse

Instance Method Details

#attach_cloudinit_iso(node, storage_id, iso) ⇒ Object

Raises:

  • (::Foreman::Exception)


140
141
142
143
144
145
146
147
148
149
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 140

def attach_cloudinit_iso(node, storage_id, iso)
  storage = client.nodes.get(node).storages.get(storage_id)
  volume = storage&.volumes&.all&.detect do |v|
    File.basename(v.volid) == File.basename(iso)
  end

  raise ::Foreman::Exception, "Could not find generated cloud-init ISO #{File.basename(iso)} on storage #{storage_id} for node #{node}" unless volume

  { ide2: "#{volume.volid},media=cdrom" }
end

#check_template_format(user_data) ⇒ Object



151
152
153
154
155
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 151

def check_template_format(user_data)
  YAML.safe_load(user_data)
rescue StandardError => e
  raise ::Foreman::Exception, "'User data kind' template provided could not be loaded, please check the format: #{e}"
end

#cloudinit_clone_args(args, vm_instance) ⇒ Object



86
87
88
89
90
91
92
93
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 86

def cloudinit_clone_args(args, vm_instance)
  return args unless args[:user_data]

  actual_node = vm_instance.node_id
  logger.info("Cloud-init ISO storage will be validated for cloned VM node #{actual_node} instead of selected node #{args[:node_id]}") if args[:node_id] != actual_node

  parse_cloudinit_config(args, vm_node: actual_node)
end

#create_cloudinit_iso(vm_name, configs) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 48

def create_cloudinit_iso(vm_name, configs)
  wd = create_temp_directory
  iso = File.join(wd, "#{vm_name.tr('.', '_')}_cloudinit.iso")
  files = []

  configs.each do |config|
    config_file = File.join(wd, config[0])
    File.write(config_file, config[1])
    files.append(config_file)
  end
  logger.debug("Generating cloud-init ISO at #{iso}")
  stdout, stderr, status = Open3.capture3(*generate_iso_command(iso, files))
  raise Foreman::Exception, N_("ISO build failed: #{stderr.presence || stdout}") unless status.success?

  yield iso
ensure
  delete_temp_dir(wd) if wd && Dir.exist?(wd)
end

#create_temp_directoryObject



74
75
76
77
78
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 74

def create_temp_directory
  Dir.mktmpdir
rescue StandardError => e
  raise ::Foreman::Exception, "Could not create working directory to store cloudinit config data: #{e}."
end

#delete_temp_dir(working_dir) ⇒ Object



80
81
82
83
84
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 80

def delete_temp_dir(working_dir)
  FileUtils.remove_entry(working_dir)
rescue StandardError => e
  logger.warn("Could not delete directory for config files: #{e}. Please delete it manually at #{working_dir}")
end

#generate_iso_command(iso_file, config_files) ⇒ Object



67
68
69
70
71
72
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 67

def generate_iso_command(iso_file, config_files)
  arguments = ["genisoimage", '-output', iso_file, '-volid', 'cidata', '-joliet', '-rock']
  arguments.concat(config_files)
  logger.debug("iso image generation args: #{arguments.join(' ')}")
  arguments
end

#parse_cloudinit_config(args, vm_node: ) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 95

def parse_cloudinit_config(args, vm_node: args[:node_id])
  filenames = ["meta-data"]
  config_data = ["instance-id: #{args[:name]}"]
  user_data = args.delete(:user_data)
  return args if user_data == ''
  check_template_format(user_data)

  if user_data.include?('#network-config') && user_data.include?('#cloud-config')
    config_data.concat(user_data.split('#network-config'))
    filenames.concat(['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

  create_cloudinit_iso(args[:name], configs) do |iso|
    storage = upload_iso(args[:node_id], args[:iso_upload_storage], iso, vm_node: vm_node)
    args.merge!(attach_cloudinit_iso(vm_node, storage, iso))
  end
end

#parse_server_cloudinit(args) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 30

def parse_server_cloudinit(args)
  cloudinit_h = {}
  cloudinit = args['cloudinit']
  unless ['none'].include? cloudinit
    volid = args['volid']
    storage = args['storage']
    cloudinit_volid = volid if volid
    cloudinit_volid ||= "#{storage}:cloudinit" if storage
    controller = args['controller']
    device = args['device']
    id = "#{controller}#{device}" if controller && device
    cloudinit_h.store(:id, id.to_sym) if id
    cloudinit_h.store(:volid, cloudinit_volid) if cloudinit_volid
    cloudinit_h.store(:media, 'cdrom')
  end
  cloudinit_h
end

#upload_iso(node, storage_id, iso, vm_node: node) ⇒ Object

Raises:

  • (::Foreman::Exception)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/helpers/proxmox_vm_cloudinit_helper.rb', line 122

def upload_iso(node, storage_id, iso, vm_node: node)
  raise ::Foreman::Exception, _('ISO upload storage must be selected') if storage_id.blank?

  iso_storages = storages(node, 'iso')
  storage = iso_storages.find { |s| s.identity == storage_id }
  raise ::Foreman::Exception, "Could not find selected ISO upload storage #{storage_id} for node #{node}" unless storage

  if node != vm_node
    vm_storage = storages(vm_node, 'iso').find { |s| s.identity == storage_id }
    raise ::Foreman::Exception, "Selected ISO upload storage #{storage_id} is not accessible from cloned VM node #{vm_node}" unless vm_storage

    storage = vm_storage
  end

  storage.upload_iso(iso)
  storage.identity
end